2. 목차
스마트 컨트랙트의 개념
Metamask와 Testnet을 활용한 스마트 컨트랙트 배포
ERC20 토큰 개발
Solidity 개발환경 및 디버깅 방법
Solidity 문법
3. 스마트 컨트랙트
스마트 컨트랙트는 특정 계약을 스스로 수립, 검증, 이행하기 위한 컴퓨터 프로토콜
1996년 닉 자보( Nick Szabo : 컴퓨터과학자, 법학자, 암호학자)에 의해서 개념이 정립
“a set of promises, specified in digital form, including protocols within which the parties
perform on these promises.”
조건이 충족되면 사람의 개입 없이 자동으로 실행되는 ‘자동화된 거래규약’
이더리움에서 스마트 컨트랙트는 이더리움의 상태를 변경할수 있는 프로그램 코드로서 블록
에 포함되어 각 노드에 전파되고, EVM에서 작동되어 상태전이를 발생
블록헤더의 데이타뿐만 아니라 특정 값이나 발신자 및 수신되는 메시지의 데이타를 조작하는
등 이더리움의 상태변화와 데이타 저장등이 가능한 프로그램 코드
새로운 스마트 컨트랙트를 생성하거나, 특정 스마트 컨트랙트상의 함수를 실행하거나, 이더
를 전송하는 방식중의 하나로 실행
사용자 어카운트(EOA)에 의해서 발생한 트랜잭션이나 다른 컨트랙트에 의해서만 실행
스마트 컨트랙트간의 호출은 메시지라는 특별한 구조체를 사용하여 호출
5. EVM(Ethereum Virtual Machine) 구조
비휘발성 (non-volatile)
storage : 상태(state)가 저장
code : 스마트 컨트랙트의 컴파일된 바이트 코드가 저장
volatile (휘발성)
stack : OP 코드를 실행하기위한 스택영역
args : 컨트랙트 호출시에 넘어오는 인자를 저장
memory : word 단위로 아이템을 저장하는 바이트 배열
6. EVM(Ethereum Virtual Machine) 특징
임시저장소와 영구저장소를 구분하여, 임시저장소에 저장한 값은 해당 인스턴스에서만 유효
하고, 영구저장소에 저장한 값은 해당 컨트랙트 전체에 유효
EVM에서 바이트 코드를 실행하기 위해서는 다음의 3가지 요소가 필요
컨테이너에 값을 Push, Pop하기 위한 스택
바이트 배열을 담을수 있는 메모리
영속적으로 값을 저장하기 위한 저장소. 현재 저장소로는 레벨DB를 사용
4,8바이트 워드단위는 크기가 너무작아, 32바이트 워드 단위를 지원
32바이트 워드크기 등 이더리움에서 요구하는 VM기능과 명세를 지원하기 위해 단순화된
자체 VM을 개발
메모리 크기가 가변적이고 스택의 크기에 제한이 없음
반복호출횟수를 1024로 제한
13. 함수 호출시 InputData
4바이트의 Function Selector + 32바이트*n 의 함수 arguments
Function Selector = keccak256(function prototype) 의 앞 4바이트
Function name
Parameters
Return Type은 Function selector에 사용되지않음
14. 최소요건을 갖춘 토큰
pragma solidity ^0.4.16;
contract MinimumViableToken {
mapping (address => uint256) public balanceOf;
function MinimumViableToken(uint256 initialSupply) public {
balanceOf[msg.sender] = initialSupply;
}
function transfer(address _to, uint256 _value) public {
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
}
16. ERC20 Token
// Balances for each account
mapping(address => uint256) balances;
// Owner of account approves the transfer of an amount to another account
mapping(address => mapping (address => uint256)) allowed;
// 현재까지 공급된 토큰수
function totalSupply() constant returns (uint256 supply) {}
// _owner가 보유한 토큰잔액을 반환
function balanceOf(address _owner) constant returns (uint256 balance) {}
// 수신자(_to) 로 해당금액(_value)를 송금. 송금이 성공하면 TRUE를 반환하고, 실패하면 FALSE를 반환.
function transfer(address _to, uint256 _value) returns (bool success) {}
// 송신자(_from)주소에서 수신자(_to) 주소로 해당금액(_value)을 송금. 송금이 성공하면 TRUE,
// 실패하면 FALSE를 반환. transferFrom이 성공하려면 먼저 approve 인터페이스를 사용하여
// 일정금액을 인출할수 있도록 허락하여야 함.
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
// 송신자(msg.sender)가 보유한 토큰에서 일정금액(_value)만큼의 토큰을 인출할수 있는 권한을
// 수신자(_spender)에게 부여.
function approve(address _spender, uint256 _value) returns (bool success) {}
// 토큰 소유자(_owner)가 토큰 수신자(_spender)에게 인출을 허락한 토큰이 얼마인지를 반환.
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
https://theethereum.wiki/w/index.php/ERC20_Token_Standard
17. Debugging Smart Contract in Remix
EVM Execution Structure
Program Counter
- 다음 차례에 실행할 EVM 명령어의 위치
Program
- EVM이 실행할 스마트 컨트랙트의 EVM 명령어 목록을 보관
Stack
- 연산에 필요한 데이타를 저장하는 공간.
- 32바이트 크기의 값들이 저장되며, 최대 1024개가 저장.
Storage
- 블록체인에 영구적으로 기록하기 위한 저장공간.
- 키/값을 매핑하기위한 구조이며, 256비트 크기를 사용.
Memory
- 함수를 호출하거나 메모리 연산을 수행할때 임시로 사용되는 공간.
- 데이타를 읽을때는 256비트 단위, 쓸때는 8비트단위나 256비트 단위로도 가능.
Log
- 스마트 컨트랙트가 실행될때 부가적인 정보를 저장하기 위한 공간.
Call Data
- 이더리움에 트랜잭션을 요청했을때 전송되는 데이타들이 저장되는 공간.
20. Debugging Smart Contract in Remix
[assignment]
1. Instruction
- 현재 실행중인 OPCODE 를 보여줌
2. Solidity Locals
- 로칼변수를 보여줌
3. Stack
- 연산과정에서의 스택의 내용을 보여줌
21. Debugging Smart Contract in Remix
[memoryAlloc]
1. Memory
- 현재 메모리 상태를 보여줌
- 단위는 32바이트 단위로 값이 할당됨
22. Debugging Smart Contract in Remix
[storageAlloc]
1. Solidity States
- 상태변수의 정보를 보여줌
- 상태변수는 블록에 영구적으로 저장
2. Storage completely loaded
- 상태변수를 [key:value]의 형태로 보여줌
23. Layout of the Solidity source file
컴파일러 버전 지정
외부 소스화일 가져오기
주석
pragma solidity ^0.4.0;
import "./someothercontract.sol";
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
24. Structure of a Contract
상태 변수 : contract storage 영역에 영구적으로 저장되는 state variables
함수 : contract의 실행단위
function name(a1, a2, a3) option returns (b1, b2, b3){ … }
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData; // State variable
// ...
}
pragma solidity ^0.4.16;
contract Simple {
function arithmetics(uint _a, uint _b) public pure
returns (uint o_sum, uint o_product)
{
o_sum = _a + _b;
o_product = _a * _b;
}
}
25. 함수의 option 필드
가시성
external : Contract 외부에서만 호출가능
public : Contract 내부와 외부 모두에서 호출가능
internal : 함수를 선언한 contract와 함수를 선언한 contract를 상속받은 contract에서 호출가능
private : 함수를 선언한 contract 내부에서만 호출가능
constant : 함수가 컨트랙트의 상태를 수정하지않음을 보장하며, gas를 소모하지 않음
payable : 컨트랙트가 자신의 함수를 통해 다른 지갑이나 컨트랙트에서 이더를 송금받음
26. Structure of a Contract
이벤트 : EVM 로깅 기능을 위한 인터페이스
struct type
pragma solidity ^0.4.0;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
function bid() public payable {
// ...
HighestBidIncreased(msg.sender, msg.value); // Triggering event
}
}
pragma solidity ^0.4.0;
contract Ballot {
struct Voter { // Struct
uint weight;
bool voted;
address delegate;
uint vote;
}
}
Enum type
pragma solidity ^0.4.0;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
27. Data type
Boolean : bool (true or false)
Integers : int/uint, int8/uint8, … , int256/uint256
Fixed point numbers : ufixed0x8, ufixed0x16, …, ufixed0x256
현재 solidit에서는 선언은 할수있지만, 값을 할당할수는 없음.
Address : address
멤버 : balance, transfer, send, call, callcode, delegatecall
정적 바이트 배열 : byte, byte1, byte2, byte3, … , byte32
멤버 : .length
동적 바이트 배열 : bytes, string
멤버 : .length, .push
28. address type member
<address>.balance : wei 단위의 address의 잔액
<address>.transfer(uint256 amount)
addres로 amount의 wei를 전송, 2300 gas 사용, 실패시 exception을 반환하고 revert 됨
일반적인 경우 transfer 사용
<address>.send(uint256 amount) returns (bool)
addres로 amount의 wei를 전송, 2300 gas 사용, 실패시 false를 반환하고, contract 실행이 중지되지 않음
<address>.call(…) returns (bool)
임의의 contract에 있는 함수를 호출시 사용하며 EVM의 CALL을 호출
<address>.delegatecall(…) returns (bool)
임의의 contract에 있는 함수를 호출시 사용하며 EVM의 DELEGATECALL을 호출
호출된 함수에서 현재 contrac의 storage, balance등을 사용할수 있음
다른 contract에 있는 코드를 라이브러리로 호출할때 사용
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10)
x.transfer(10);
29. Function call in another contract
pragma solidity ^0.4.17;
contract SomeContract {
event callMeMaybeEvent(address _from);
function callMeMaybe() payable public {
callMeMaybeEvent(this);
}
}
contract ThatCallsSomeContract {
function callTheOtherContract(address _contractAddress) public {
require(_contractAddress.call(bytes4(keccak256("callMeMaybe()"))));
require(_contractAddress.delegatecall(bytes4(keccak256("callMeMaybe()"))));
SomeLib.calledSomeLibFun();
}
}
library SomeLib {
event calledSomeLib(address _from);
function calledSomeLibFun() public {
calledSomeLib(this);
}
}
33. Mapping
key와 value를 매핑하기 위해 사용
mapping(_KeyType => _ValueType) name
_KeyType : 매핑, 동적크기 배열, 컨트랙트, enum 타입을 제외한 모든 타입 가능
_ValueType : 매핑을 포함한 모든 타입 가능
pragma solidity ^0.4.0;
contract MappingExample {
mapping(address => uint) public balances;
function update(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}
contract MappingUser {
function f() public returns (uint) {
MappingExample m = new MappingExample();
m.update(100);
return m.balances(this);
}
}
34. 이더리움 단위
이더 통화 단위
wei, finney, Szabo, ether 단위를 사용하여 단위계산 가능
“2 ether == 2000 finney” 의 결과는 “true”
시간 단위
seconds, minutes, hours, days, weeks, years
1 == 1 seconds, 1 minutes == 60 seconds, 1 hours == 60 minutes, 1 days == 24 hours
1 weeks == 7 days, 1 years == 365 days
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) {
// ...
}
}
35. 특수 변수 및 함수
블록과 거래 속성
block.blockhash(uint blocknumber) returns (bytes32)
: 해당 블록의 hash 값을 반환. 현재 블록포함 최근의 256 블록에 대해서만 작동
block.coinbase (address) : 현재 블록 채굴자의 주소
block.difficulty (uint) : 현재 블록의 채굴 난이도
block.gaslimit (uint) : 현재 블록의 gaslimit를 반환
block.number (uint) : 현재 블록번호
block.timestamp (uint) : 현재 블록의 유닉스 타임스탬프를 반환
gasleft() returns (uint256) : 남아있는 gas
msg.data (bytes) : 메시지에 있는 데이터 전체
msg.gas (uint) : 메시지의 남아있는 gas, 0.4.21 버전에서 gasleft로 교체
msg.sender (address) : 메시지를 보낸 송신자의 주소, 즉 현재 스마트 컨트랙트를 실행시킨 사용자의 주소
msg.sig (bytes4) : calldata의 첫번째 4바이트 function selector
msg.value (uint) : wei 단위의 메시지의 송금액
now (uint) : 현재 블록의 타임스탬프, block.timestamp와 동일
tx.gasprice (uint) : 트랜잭션의 gasprice
tx.origin (address) : 트랜잭션을 보낸 송신자의 주소
36. 특수 변수 및 함수
수학함수와 암호화 함수
addmod(uint x, uint y, uint k) returns (uint) : (x +y) % k 를 계산
mulmod(uint x, uint y, uint k) returns (uint) : (x * y) % k 를 계산
keccak256(…) returns (bytes32) : 입력값에 대한 keccak256 hash 값을 반환
sha256(…) returns (bytes32) : 입력값에 대한 SHA256 hash 값을 반환
sha3(…) returns (bytes32) : 입력값에 대한 sha3 hash 값을 반환, keccak256과 동일
ripemd160(…) returns (bytes20) : 입력값에 대한 RIPEMD-160 hash 값을 반환
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
타원곡선 전자서명의 공개키와 관련된 주소를 반환, 에러 발생시 0을 반환
컨트랙트 관련 함수
this : 현재 컨트랙트
selfdestruct(address recipient) : 현재 컨트랙트를 파괴하고 남아있는 잔액을 recipient로 전송
suicide(address recipient) : selfdesctuct와 동일
37. 함수호출 타입
Internal function call
contract 내부에서 직접 또는 재귀호출 방식으로 호출되는 방식
External function call
this.func2(8), cont.func2(2) : cont는 contract의 인스턴스
jump가 아닌 Message call 방식으로 호출되며, Constructor에서는 external call을 할수없음
external call을 통해 이더를 전송가능하며, value()로 이더를 지정하고, gas()로 gas 지정
이더전송을 위해서는 해당함수의 상태 변경성을 payable로 지정해야함
pragma solidity ^0.4.16;
contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
}
pragma solidity ^0.4.0;
contract DataFeed {
function data() payable returns (uint dataReturn) { return 42; }
}
contract DataConsumer {
DataFeed feed;
function feedSet(address addr) { feed = DataFeed(addr); }
function feedCall() { feed.data.value(10).gas(800)(); }
}
38. 에러 핸들링
solidity에서는 exception을 사용하여 에러를 다루고, 예외가 발생하면 변경이전 상태로 되돌아감
revert() : 실행을 종료. 지금까지의 변경사항을 revert하고 미사용 gas를 반환
assert(bool condition) : 조건이 false이면 실행을 종료. 내부오류 확인에 사용
require(bool condition) : 조건이 false이면 실행을 종료. 입력값,컨트랙트 상태변수, 다른함수의 반환
값이 유효한지 검사. 외부적인 오류에서 사용
pragma solidity ^0.4.0;
contract TheSharer {
function sendHalfSum(address addrTo) payable returns (uint balance) {
require(msg.value % 2 == 0); // Allow even numbers only
uint balanceBefore = this.balance;
addrTo.transfer(msg.value / 2);
// Because transfer will throw an exception if it fails and
// is unable to call back here, we should have no way to
// = have half of all the money still.
assert(this.balance == balanceBefore - msg.value / 2);
return this.balance;
}
}
39. 가시성
external
contract 외부에서만 호출가능
f()가 external 인 경우 : f() 호출 불가능, this.f() 호출가능
public
contract 내부와 외부 모두에서 호출가능
상태변수를 public으로 선언하면 상태변수를 조회하는 getter 함수를 자동으로 생성
getter 함수의 이름은 상태변수의 이름과 동일
internal
함수나 상태변수를 선언한 contract와 contract를 상속받은 contract에서 호출 및 사용가능
private
함수나 상태변수를 선언한 contract 내부에서만 호출가능
40. 가시성 예제// This code does not compile properly
pragma solidity ^0.4.0;
contract cont1 {
uint private data;
function func(uint x) private returns(uint y) { return x + 1; }
function dataSet(uint x) { data = x; }
function dataGet() public returns(uint) { return data; }
function compute(uint x, uint y) internal returns (uint) { return x+y; }
}
contract cont2 {
function dataRead() {
cont1 z = new cont1();
uint local = z.func(7); // error: member "func" is not visible
z.dataSet(3);
local = z.dataGet();
local = z.compute(3, 5); // error: member "compute" is not visible
}
}
contract cont3 is cont1 {
function g() {
cont1 z = new cont1();
uint val = compute(3, 5); // access to internal member (from derived to parent contract)
}
}
41. Getter 함수
pragma solidity ^0.4.0;
contract cont1 {
uint public data = 42;
}
contract contractCaller {
cont1 c = new cont1();
function func() {
uint local = c.data();
}
}
pragma solidity ^0.4.0;
contract cont1 {
uint public littleNumber;
function func() {
littleNumber = 3; // internal access
uint val = this.littleNumber(); // external access
}
}
42. 함수 제어자(Function modifier)
함수 정의부 끝에 해당 함수의 작동방식을 바꾸도록 지정
가장 일반적인 사용은 함수 실행전 require 체크를 넣는것
contract owned {
function owned() public { owner = msg.sender; }
address owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
}
contract mortal is owned {
function close() public onlyOwner {
selfdestruct(owner);
}
}
43. view 함수
해당 함수가 상태를 변경하지 않음을 보장
Getter 함수가 view 함수의 대표적인 예
상태를 변경하는 예
상태변수를 변경
이벤트를 전송
다른 컨트랙트를 생성
selfdestruct가 호출
call 함수를 통해 이더를 전송
view나 pure로 선언되지 않은 함수를 호출
low-level 함수를 호출
상태변경 opcode를 실행하는 인라인 어셈블리 호출
pragma solidity ^0.4.16;
contract cont {
function func(uint x, uint y) view returns (uint) {
return x * (y + 42) + now;
}
}
44. pure 함수
해당 함수가 상태를 변경하지 않는것 뿐만 아니라 상태를 읽지도 않음을 보장
상태를 읽는 예
상태변수를 읽음
<address>.balance나 this.balance를 억세스
msg, tx, block의 멤버를 억세스 (msg.sig와 msg.data는 제외)
pure로 선언되지 않은 함수를 호출
상태를 읽는 opcode가 포함된 인라인 어셈블리 호출
pragma solidity ^0.4.16;
contract cont {
function func(uint x, uint y) pure returns (uint) {
return x * (y + 42);
}
}
45. fallback 함수
컨트랙트에 선언된 이름이 없는 함수로 컨트랙트는 단 하나의 폴백 함수만 정의할수 있음
매개변수를 선언할수 없고, 값도 반환할수 없음
컨트랙트에서 다른 컨트랙트로 send나 transfer를 호출하여 이더를 전송하면 호출됨
이더리움 어카운트 주소의 call 함수를 호출할때 함수이름을 지정하지 않아도 폴백함수가 호
출되고, 존재하지 않는 함수를 호출해도 폴백함수가 호출됨
pragma solidity ^0.4.24;
contract Fallback{
uint public counter = 1;
function () public payable {
if(msg.value <= 0) {
revert();
}
counter++;
}
}
46. 이벤트
EVM 로깅기능을 편하게 사용하기 위한 방법
Dapp의 사용자 인터페이스에서 JavaScript 콜백함수를 사용하여 이벤트를 수신할수 있음
EVM은 5개의 로깅함수를 지원 : log0, log1, log2, log3, log4, logi
Solidity에서 이벤트를 선언하면 이벤트가 호출될때 매개변수에 맞는 log함수가 호출
이벤트 선언시 매개변수에 indexed 속성을 지정하면 해당 매개변수로 필터링할수 있음
이벤트 사용 유스 케이스
트랜잭션의 결과값을 확인하고 싶을때
컨트랙트에 이더 송금등의 트랜잭션 이력을 기록하고 싶을때
스토리지 영역은 32바이트당 20,000 가스사용, 로그 영역은 32바이트당 96 가스 사용
트랜잭션을 생성한 어플리케이션을 트리거하기 위해
Dapp이 모니터링하는 이벤트가 발생하면 이더리움은 해당 어플리케이션의 이벤트 처리할수를 호출
47. 이벤트
pragma solidity ^0.4.10;
contract Cont {
function func() {
bytes32 _id = 0x420042;
log3(
bytes32(msg.sender),
bytes32(msg.value),
bytes32(/*hexadecimal equivalent to
kecak256("Deposit(address,hash256,uint256)")*/),
_id
);
}
}
pragma solidity ^0.4.0;
contract ClientReceipt {
event Deposit(
address indexed _from,
bytes32 indexed _id,
uint _value
);
function deposit(bytes32 _id) public payable {
// Events are emitted using `emit`, followed by
// the name of the event and the arguments
// (if any) in parentheses. Any such invocation
// (even deeply nested) can be detected from
// the JavaScript API by filtering for `Deposit`.
Deposit(msg.sender, _id, msg.value);
}
}
48. 이벤트
var abi = /* abi as generated by the compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */);
var event = clientReceipt.Deposit();
// watch for changes
event.watch(function(error, result){
// result will contain various information
// including the argumets given to the `Deposit`
// call.
if (!error)
console.log(result);
});
// Or pass a callback to start watching immediately
var event = clientReceipt.Deposit(function(error, result) {
if (!error)
console.log(result);
});
49. 상속pragma solidity ^0.4.16;
contract owned {
function owned() { owner = msg.sender; }
address owner;
}
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Config {
function lookup(uint id) public returns (address adr);
}
contract NameReg {
function register(bytes32 name) public;
function unregister() public;
}
contract named is owned, mortal {
function named(bytes32 name) {
Config config =
Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
}
function kill() public {
if (msg.sender == owner) {
Config config =
Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).unregister();
mortal.kill();
}
}
}
contract PriceFeed is owned, mortal, named("GoldFeed") {
function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo;
}
function get() public view returns(uint r) { return info; }
uint info;
}
50. selfdestruct를 이용한 컨트랙트 제거의 위험성
pragma solidity ^0.4.0;
contract owned {
function owned() public { owner = msg.sender; }
address owner;
}
contract mortal is owned {
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public { /* do cleanup 1 */ mortal.kill(); }
}
contract Base2 is mortal {
function kill() public { /* do cleanup 2 */ mortal.kill(); }
}
contract Final is Base1, Base2 {
}
51. super로 안전하게 컨트랙트 제거하기
pragma solidity ^0.4.0;
contract owned {
function owned() public { owner = msg.sender; }
address owner;
}
contract mortal is owned {
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public { /* do cleanup 1 */ super.kill(); }
}
contract Base2 is mortal {
function kill() public { /* do cleanup 2 */ super.kill(); }
}
contract Final is Base1, Base2 {
}
상속 그래프를 사용하여 안전하게 초기화 수행
Final의 상속그래프
Final, Base2, Base1, mortal, owned
52. 추상 컨트랙트
구현되지 않은 함수를 가지고 있는 컨트랙트
new를 이용하여 인스턴스를 생성하지 못함
베이스 컨트랙트 역할을 수행하며, 추상 컨트랙트를 상속받은 컨트랙트는 함수를 구현해야함
pragma solidity ^0.4.0;
contract Felinae {
function utterance() returns (bytes32);
}
contract Cheetah is Felinae {
function utterance() returns (bytes32) { return "sneaky cat"; }
}
53. 인터페이스
인터페이스는 추상 컨트랙트와 유사하지만 다음과 같은 추가적인 제약이 있음
다른 컨트랙트나 인터페이스를 상속받을수 없음
생성자, 상태변수, struct, enum 을 정의할수 없음
pragma solidity ^0.4.11;
interface Token {
function transfer(address recipient, uint amount) public;
}
54. 라이브러리
라이브러리의 목적은 블록체인에 배포된 컨트랙트를 재사용하기 위함
라이브러리를 호출할때는 EVM의 delegatecall을 호출하여 코드를 재사용
delegatecall을 호출할경우 실행 컨텍스트가 변경되지않고 호출하는 컨텍스트에서 함수실행
라이브러리 함수에서 this 를 호출할경우 호출한 컨텍스트를 가리킴
라이브러리는 컨트랙트에 비해 다음의 제약이 있음
상태 변수를 정의할수 없음
상속하거나 상속을 받을수 없음
이더를 전송받을수 없음
55. 라이브러리
pragma solidity ^0.4.16;
library Set {
struct Data { mapping(uint => bool) flags; }
function insert(Data storage self, uint value)
public returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public view returns (bool)
{
return self.flags[value];
}
}
contract C {
Set.Data knownValues;
function register(uint value) public {
require(Set.insert(knownValues, value));
}
}
56. using A for B
A의 라이브러리 함수들을 기존의 B 타입에 추가
pragma solidity ^0.4.16;
library Set {
struct Data { mapping(uint => bool) flags; }
function insert(Data storage self, uint value)
public returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public view returns (bool)
{
return self.flags[value];
}
}
contract C {
using Set for Set.Data;
Set.Data knownValues;
function register(uint value) public {
// require(Set.insert(knownValues, value));
require(knownValues.insert(value));
}
}