2. 스마트 컨트랙트는 무엇인가?
1990년대 암호 작성자 Nick Szabo가 용어 정의: “a set of promises, specified in digital form,
including protocols within which the parties perform on the other promises”.
이 책에서는 스마트 컨트랙트를 ‘이더리움 네트워크 프로토콜의 이더리움 가상 머신(이하 EVM)
문맥에서 결정적으로 수행되는 불가변적인 컴퓨터 프로그램’이라고 지칭한다.
3. 스마트 컨트랙트는 무엇인가?
1990년대 암호 작성자 Nick Szabo가 용어 정의: “a set of promises, specified in digital form,
including protocols within which the parties perform on the other promises”.
이 책에서는 스마트 컨트랙트를 ‘이더리움 네트워크 프로토콜의 이더리움 가상 머신(이하 EVM)
문맥에서 결정적으로 수행되는 불가변적인 컴퓨터 프로그램’이라고 지칭한다.
• EVM 문맥(EVM context) : 스마트 컨트랙트가 접근 가능한 한정적인 정보를 말한다(자기 자신의 state만 접근 가능, 가장 최근
의 몇몇 블록의 정보만 접근 가능).
• 결정적 : 스마트 컨트랙트의 실행 전과 후의 이더리움의 state는 모든 인스턴스에서 동일하다.
• 불가변성 : 이미 배포된 스마트 컨트랙트의 코드는 변경할 수 없다.
4. 스마트 컨트랙트의 Life Cycle
1. 스마트 컨트랙트는 일반적으로 Solidity같은 스마트 컨트랙트용 언어로 작성하고 EVM에서 실행
가능한 수준의 바이트 코드로 컴파일 한다.
2. 컴파일된 바이트 코드는 ‘contract creation transaction’이라는 특수한 트랜잭션을 통해서 이더리
움 네트워크에 배포된다(일반적인 트랜잭션과 달리, 받는 주소 to가 ‘0’로 되어있다).
3. 배포된 스마트 컨트랙트는 스스로 실행되는 것이 아니라 다른 EOA가 발생시킨 트랜잭션에 의해
서 실행되고, 오류가 없으면 state를 변화시키고 실행을 마친다(오류가 있거나 gas가 부족하면
gas만 소모한 상태로 state를 롤백한다).
4. 스마트 컨트랙트는 불가변적이나 SELFDESTRUCT라는 opcode를 통해서 삭제할 수 있다(가스를 환
불해줌으로써 이 작업을 장려한다). 그러나 트랜잭션의 기록이 지워지는 것이 아니라 스마트 컨
트랙트의 코드와 state만 삭제한다.
5. 스마트 컨트랙트 지원 언어
LLL : Lisp과 비슷한 syntax의 함수형 프로그래밍 언어로, 최초로 Smart Contract 언어로 사용된 언
어지만 현재는 거의 안쓰이고 있다.
Serpent : 절차형 프로그래밍 언어로 Vitalik Buterin이 처음 만든 언어다.
Solidity : Javascript, 그리고 java 와 비슷한 syntax의 절차형 프로그래밍 언어다. 현재 가장 많이 쓰
이는 스마트 컨트랙트용 언어다.
Vyper : Serpent와 비슷하지만 syntax는 Python에 가까운 언어로, Serpent 보다 Python에 더 유사한
순수-함수형 프로그래밍 언어다.
6. 스마트 컨트랙트 빌딩(with Solidity)
Solidity 설치
Solidity는 지속적으로 업데이트가 진행중이며, 버전에 따라서 호환 가능한 컨트랙트가 다르기 때
문에 항상 최신 버전의 컴파일러를 사용하길 권장
( https://solidity.readthedocs.io/en/latest/installing-solidity.html )
apt를 이용한 Solidity compiler 설치 방법:
버전 확인:
7. 스마트 컨트랙트 빌딩(with Solidity)
Application Binary Interface(ABI)
바이트 코드가 EVM이 코드를 실행하기 위한 기계어라고 한다면, ABI는 사용자가 읽을 수 있는 포
맷으로 컨트랙트의 자료 구조와 함수 구조를 표현한 인터페이스다(기계어와 사용자 레벨의 언어
사이의 인터페이스).
ABI를 통해서 유저는 호출하려는 스마트 컨트랙트의 함수의 인수와 반환값을 파악하여 올바른
유형의 트랜잭션을 호출할 수 있도록 가독성을 제공한다.
그림1] Faucet.sol의 바이트 코드
그림2] Faucet.sol의 ABI(Json format)
8. 스마트 컨트랙트 빌딩(with Solidity)
Application Binary Interface(ABI)
바이트 코드가 EVM이 코드를 실행하기 위한 기계어라고 한다면, ABI는 사용자가 읽을 수 있는 포
맷으로 컨트랙트의 자료 구조와 함수 구조를 표현한 인터페이스다(기계어와 사용자 레벨의 언어
사이의 인터페이스).
ABI를 통해서 유저는 호출하려는 스마트 컨트랙트의 함수의 인수와 반환값을 파악하여 올바른
유형의 트랜잭션을 호출할 수 있도록 가독성을 제공한다.
그림1] Faucet.sol의 바이트 코드
그림2] Faucet.sol의 ABI(Json format)
Machine code
ABI(Human-
readable)
9. Solidity 데이터 타입
boolean (bool)
integer (int, uint)
fixed point (fixed, ufixed)
address : 20-byte의 이더리움 주소를 나타내는 자료형으로, 주로 특정 주소에 송금을 하거나 다
른 데이터에 mapping하는 용도로 사용한다.
byte array (fixed, dynamic)
enum
arrays
struct
mapping : Key/Value를 매핑하는 해시 테이블 자료형( mapping(KEY_TYPE => VALUE_TYPE) NAME;
time units
ether units : 이더리움 네트워크에서 사용되는 화폐 단위를 나타내는 자료형
10. Solidity Pre-defined Variables and Functions
msg.sender : 현재 스마트 컨트랙트를 호출한 계좌의 주소(EOA, CA 모두 가능)
msg.value : 현재 트랜잭션에 동봉된 ETH(wei 단위)의 값
msg.gas : 현재 실행 단계에서 남아있는 gas의 값(최근 버전에서는 gasleft()로 대체됨)
msg.data : 현재 트랜잭션의 동봉된 데이터
msg.sig : data의 첫 번째 4-bytes로, 어떤 함수를 호출했는지에 대한 지칭
tx.gasprice : 현재 트랜잭션의 gas price
tx.origin : 현재 트랜잭션을 호출한 EOA의 주소(불완전)
block.blockhash(blockNumber) : blockNumber에 해당하는 블록의 해시 값(가장 최근의 256개의 블
록의 정보만 접근 가능)
block.coinbase : 현재 블록의 보상을 받는 채굴자의 주소
block.difficulty : 현재 블록의 난이도
block.gaslimit : 현재 블록의 gas limit
11. Solidity Pre-defined Variables and Functions
block.number : 현재 블록의 번호(높이)
block.timestamp : 현재 블록이 채굴된 시간(Unix time)
address.balance : 해당 주소의 잔고(wei 단위)
address.transfer(amount) : amount(wei) 만큼의 금액을 해당 주소로 송금
address.send(amount) : transfer와 동일한 기능이지만 에러에 대해서 예외 처리가 없다
address.call(payload) :
address.callcode(payload) : ‘low-level interface for sending a message to a contract’
address.delegatecall() :
12. Solidity Pre-defined Variables and Functions
예시]
그림3] Solidity 코드 예시
address.call을 호출한 경우에는 msg.sender는 contract D고,
contract E의 address public sender는 D의 주소를 갖는다.
13. Solidity Pre-defined Variables and Functions
예시]
그림3] Solidity 코드 예시
Callcode을 호출한 경우에는 contract D의 address public
sender가 D의 주소를 갖는다.
15. Solidity Pre-defined Variables and Functions
예시]
그림3] Solidity 코드 예시
C.Foo를 호출한 경우에는 msg.sender는 contract C의
주소를 나타내고 마찬가지로 contract E의 storage는
바꾸지 않는다.
①
②
16. Solidity Pre-defined Variables and Functions
msg.sender : 현재 스마트 컨트랙트를 호출한 계좌의 주소(EOA, CA 모두 가능)
msg.value : 현재 트랜잭션에 동봉된 ETH(wei 단위)의 값
msg.gas : 현재 실행 단계에서 남아있는 gas의 값(최근 버전에서는 gasleft()로 대체됨)
msg.data : 현재 트랜잭션의 동봉된 데이터
msg.sig : data의 첫 번째 4-bytes로, 어떤 함수를 호출했는지에 대한 지칭
tx.gasprice : 현재 트랜잭션의 gas price
tx.origin : 현재 트랜잭션을 호출한 EOA의 주소(불완전)
block.blockhash(blockNumber) : blockNumber에 해당하는 블록의 해시 값(가장 최근의 256개의 블
록의 정보만 접근 가능)
block.coinbase : 현재 블록의 보상을 받는 채굴자의 주소
block.difficulty : 현재 블록의 난이도
block.gaslimit : 현재 블록의 gas limit
17. 각종 키워드
함수 키워드
함수는 다음과 같은 syntax로 정의되어 있다:
fucntion FunctionName([parameters]) {public|private |internal |external} [pure |constant |view |payable]
18. 각종 키워드
함수 키워드
함수는 다음과 같은 syntax로 정의되어 있다:
fucntion FunctionName([parameters]) {public|private |internal |external} [pure |constant |view |payable]
FunctionName : 함수의 이름
Prameters : 함수에 넘겨줄 인자들의 이름과 데이터 타입
19. 각종 키워드
함수 키워드
함수는 다음과 같은 syntax로 정의되어 있다:
fucntion FunctionName([parameters]) {public|private |internal |external} [pure |constant |view |payable]
함수의 가시성을 나타내는 키워드
• public : 기본값으로, 어느 컨트랙트나 EOA 모두 호출할 수 있는 함수
• external : public과 동일하나 컨트랙트 내에서는 호출이 불가능한 함수(EOA만 호출 가능)
• Internal : 오직 상속받은 컨트랙트에서만 호출 가능한 함수
• Private : 그 어떤 컨트랙트에서도 호출 불가능한
20. 각종 키워드
함수 키워드
함수는 다음과 같은 syntax로 정의되어 있다:
fucntion FunctionName([parameters]) {public|private |internal |external} [pure |constant |view |payable]
함수가 state에 접근하는 권한을 명시하는 키워드
• constant & view : 컨트랙트의 state를 변경(write)할 수 없는 함수
• pure : 컨트랙트의 state를 읽거나 쓰는것이 불가능한 함수
• payable : ETH의 전송이 가능한 함수
21. Constructor and Destructor
예시]
0.4.21 이전 버전에서는 컨트랙트와 동일한 이름으로 생성자를 명시했고, 0.4.22 버전 이후부터는
‘constructor’라는 키워드로 생성자를 명시한다.
function destroy는 일반적인 소멸자의 예시로, 컨트랙트를 배포했던 owner만 소멸자를 호출할 수
있도록 구현하였다(선택적, 권장사항).
그림4] Constructro 와 destructor 예시
22. Modifier
예시]
modifier는 함수 선언에 추가하여 함수에 적용되는 추가적인 조건을 만드는 데 사용되는 키워드
다. 위의 예시는 이전 페이지의 ‘require(msg.sender == owner);’ 조건을 modifier로 적용한 코드다.
modifier 내부의 ‘_;‘는 ‘placeholder’라는 syntax로, modifier가 적용되는 함수의 내용이 위치하는 자
리입니다.
그림5] modifier 예시
23. 상속
예시]
Solidity는 컨트랙트간 상속을 지원하여 모듈화, 확장성, 그리고 재사용성을 얻는다.
위의 예시에서 contract mortal은 contract owned의 modifier인 onlyowner를 사용할 수 있다.
그림6] 상속의 예시(owned->mortal 상속)
그림7] 상속의 예시(owned->mortal->Faucet 상속)
24. Event 발생
트랜잭션이 수행되면, 트랜잭션 수행중에 일어난 정보들의 로그들을 포함하는 ‘receipt’를 생성
한다. Event는 이런 로그들 중, 특정한 로그에 대하여 ‘감시’ 하다가 이벤트를 감지하면 사용자 인
터페이스에 보고하는 오브젝트다.
그림8] event와 emit 예시
25. Event Catch
이 페이지에서는 이전 페이지에서 setup 해둔 event의 결과를 truffle을 이용해서 받아본다.
그림9] truffle 실행화면, 스마트 컨트랙트 배포
26. Event Catch
이 페이지에서는 이전 페이지에서 setup 해둔 event의 결과를 truffle을 이용해서 받아본다.
withdraw 함수를 실행시키고, withdraw 함수 내부의 event Withdrawal의 결과를 response로 받은
결과
그림10] truffle 실행화면, 트랜잭션 생성 및 response 확인
27. 다른 컨트랙트 호출
새로운 인스턴스 생성
가장 안전하게 다른 컨트랙트를 호출하는 방법은 호출하려는 다른 컨트랙트를 자신이 직접 생성
하는 것이다.
호출하려는 다른 컨트랙트를 import하고 해당 컨트랙트의 인스턴스를 새로 만드는 방법으로, 밑
의 예시와 같은 방법으로 사용 가능하다.
종이 지갑 : 키를 장기 보관하기 위해서 직접 종이에 인쇄한 형태의 지갑이다.
그림11] Faucet.sol 컨트랙트 인스턴스를 새로 생성하는 코드
28. 다른 컨트랙트 호출
주소 참조
직접 새 인스턴스를 생성하는 방법보다는 위험하지만, 현존하는 컨트랙트의 주소를 사용하여 직
접 호출하는 방법이 있다.
아래의 예시와 같이, 호출하려는 함수의 컨트랙트 주소(_f)를 이용해서 외부 컨트랙트 함수인
withdraw를 호출할 수 있다.
그림12] 주소 _f 를 이용한 컨트랙트 함수 호출
29. Gas Consideration
스마트 컨트랙트를 수행하는 데는 gas가 소모되며, 가스가 부족한 경우(Out-of-Gas) 자원만 소모
하고, 수행 결과는 롤백 되므로 주의해야 한다. 다음은 가스 소모를 위해서 고려할 사항들이다.
• 동적 배열 사용 피하기
• 외부 컨트랙트 호출 피하기
• 가스 소모량 예측하기( 코드 참조 :
https://github.com/ethereumbook/ethereumbook/blob/develop/code/truffle/FaucetEvents/gas_estimates.js)
그림13] 외부 함수 코드 예시
그림13] gas 소모량 측정하는 코드