이더리움 스마트 계약(3) — 블록체인에 은행 만들기
은행은 예금을 받고 대출을 하여 그 사이에 예대마진을 가져가는 비즈니스 모델을 가지고 있다. 스마트 계약을 작성하기 위해서 이러한 업무의 핵심을 짚은 뒤 이를 묘사하는 일련의 상태와 행위로 추상화 하는 과정을 거쳐야 한다.
여기서는 대출업무는 배제하고, 예금업무만을 수행하는 은행을 가정한다.
예금업무를 추상화하면 다음과 같다.
- (상태)총예금액
- (상태)고객별예금액
- (행위)은행으로 예금
- (행위)은행에서 출금
2가지의 상태를 가지고 2개의 행위를 하는 단순화된 은행을 가정했다. 이를 자율분산조직개념을 통해 나타내면 다음과 같다.
스마트 계약을 통해 어떠한 업무를 블록체인을 통해 구현하고자 할 때에는 업무를 수행하는 주체의 특징을 명확히 한정 하는 것이 매우 중요하다. 또한 이를 컨트랙트 객체로 구체화 하는 과정에서 업무의 핵심을 담는 상태와 행위를 식별해 낼 수 있어야 한다.
앞에서 설계된 자율분산조직의 상태는 변수로, 행위는 함수로 나타낼 수 있다.
- 라인 1 : 버전 프래그마. 향후 컴파일러의 업그레이드로 인해 소스가 다르게 해석되는 것을 방지한다.
- 라인3 : Bank라는 이름의 컨트랙트 객체를 선언하고 있다.
- 라인4 : uint타입의 ‘총예금액’ 상태를 담을 수 있는 totalDeposit변수를 선언하고 있다.
- 라인5 : address타입을 키(key)로, uint타입을 값(value)으로 받는 mapping타입의 balanceOf변수를 선언하고 있다. 이 변수를 통해 계정별로 구분된 잔액 상태를 담을 수 있다.
- 라인7~10 : deposit이란 이름의 함수를 선언하고 payable 변환자를 통해 이 함수 호출을 통해 이더를 받을 수 있도록 했다. msg.sender를 이용해 발생된 트랜잭션의 프로퍼티 중 보낸사람(송신자)의 정보를 키(key)로, 트랜잭션에 담긴 이더리움 액수(msg.value)를 값(value)로 매핑하고 있다. 이어서 라인 9에서는 그렇게 전송된 액수를 총 예금에 더한다.
- 라인 12~15 : 출금 코드는 입금 코드와 비슷하다. 다만 라인15를 통해 콘트랙트 어카운트에 쌓인 잔액을 msg.sender에게 보내고 있다. “주소형.call”에 대한 설명은 다음 링크를 참조. address.call.value
- 라인 18~20 : 총 예금을 조회하는 함수. constant로 리턴하는 함수는 블록체인 상에 트렌잭션을 일으키지 않고도 컨트랙트 상태를 조회할 수 있다. message와 call의 차이점
- 라인 22~24 : 계정 별 잔액을 조회
솔리디티 공식독에 보면 기본적인 예제에서 시작되어 타입, 조건문, 분기문 등 문법적인 내용이 잘 정리되어 있다.
솔리디티 코드가 바이트 코드로 변환되는 것을 컴파일이라 하고, 컴파일된 바이트 코드가 블록체인 네트워크에 자리 잡는 것을 배포라 한다. Bank.sol을 여러 방법으로 컴파일 및 배포 테스트를 할 수 있는데, 여기서는 browser solidity를 사용해보자. [솔리디티 테스트 할 수 있는 다양한 방법]
Bank.sol의 소스 내용을 브라우저 솔리디티에 붙여 넣으면 실시간으로 컴파일을 하게 된다. 우측에 Bytecode와 Interface필드를 메모장에 복사해 놓자.
1편 개별환경구성에서 만들었던 execute_geth.sh를 실행해 1번 노드를 구동시키고, attach_geth.sh를 실행해 콘솔창을 연다.
앞으로 콘솔을 통해 진행되는 배포 과정을 요약하면 다음과 같다.
- 외부 어카운트 2개를 생성
- 마이닝을 통해 외부 계정1번에 잔액을 쌓음
- 외부계정 1번으로 컨트랙트 계정을 생성하는 트랜잭션 작성
- 생성된 트랜잭션을 마이닝
- 마이닝 결과 생성된 컨트랙트 주소를 메모
콘솔 실행 과정에서 eth, personal, miner등의 geth javascript API가 사용되었다. 각 API에 대한 상세한 설명은 공식문서 참조.
- 라인40 : 이 줄을 실행하고 1번 노드의 표준출력을 살펴보면 외부계정 주소와 동일한 형식의 컨트랙트 계정 주소가 생성된 것을 확인할 수 있다. 메모장에 복사 해 놓자. (혹은 eth.getTransactionReceipt(“트랜잭션해시”)명령을 통해 조회 할 수 있다)
이번 단계를 진행하기 위해서 앞에서 생성된 ‘콘트랙스 주소’와 브라우저 솔리디티에서 복사한 ‘인터페이스 정보’가 필요하다.
수록된 코드는 다음 과정으로 요약할 수 있다.
- 콘솔에 배포된 컨트랙트와 커뮤니케이션 하는 객체 생성
- 배포된 컨트렉트에 외부 계정이 예금
- 마이닝
- 예금 내역을 조회
- 라인 28~29 : 이 줄을 통해서 배포된 컨트랙트의 잔액 정보를 확인할 수 있다. getBalance명령은 콘트렉트 주소 뿐 아니라, 외부계정 주소의 잔액도 확인하는 데에도 쓰인다.
- 라인 33 : call을 통해 처음 생성한 1번 계정의 잔액을 조회한다.
Bank.sol 테스트 코드로써, 실제 배포해서 사용할 경우 심각한 문제를 일으키게 된다. 이는 다음과 같은 문제 때문이다.
- 예금에 대한 출금은 예금을 한 ‘당사자’만 할 수 있어야 하는데, 현재 코드에서는 그렇지 않다. 출금 함수 초입에 조건문이나 modifier등을 통해서 출금 가능한 계정을 제한해야 한다.
- value를 0으로 한 허위 예금 트랜잭션이 다량 생성될 수 있다. 최소 입금 조건을 설정해서 이러한 공격을 방지해야 한다.
- call.value을 위처럼 작성할 경우 reentrant공격을 당하게 된다. 이를 방지하기 위해서는 Checks-Effects-Iterations패턴을 적용한 시큐어 코딩이 필요하다.
- 기타 여러 보안 조치 부재
제시한 문제를 해결하는 Bank.sol을 만들어 보는 것도 solidity를 공부하는 데에 의미있을 것이다.
그렇지만 공식 문서에는 코드 자체로 문제가 없더라도 이더리움 플랫폼 자체에 대한 발견되지 않은 취약점이 존재할 수 있으므로, 신중해야 한다는 내용이 포함되어 있다. 얼마전에 발생한 옵코드 Dos공격이 대표적인 예다. (설명)
Originally published at medium.com on December 3, 2016.