이더리움 스마트 계약(3)—블록체인에 은행 만들기

1편에서 구성했던 개발환경과 2편에 논의 되었던 개념을 바탕으로 이더리움 블록체인 위에 은행을 만들어보자.

1편 이더리움 스마트 계약(1) — 개발환경구성

2편 이더리움 스마트 계약(2) — 분산자율조직개념


설계

은행은 예금을 받고 대출을 하여 그 사이에 예대마진을 가져가는 비즈니스 모델을 가지고 있다. 스마트 계약을 작성하기 위해서 이러한 업무의 핵심을 짚은 뒤 이를 묘사하는 일련의 상태와 행위로 추상화 하는 과정을 거쳐야 한다.

은행의 핵심 비즈니스

  • 예금업무
  • 대출업무

여기서는 대출업무는 배제하고, 예금업무만을 수행하는 은행을 가정한다.

예금업무를 추상화하면 다음과 같다.

예금업무 주요특성

  1. (상태)총예금액
  2. (상태)고객별예금액
  3. (행위)은행으로 예금
  4. (행위)은행에서 출금

2가지의 상태를 가지고 2개의 행위를 하는 단순화된 은행을 가정했다. 이를 자율분산조직개념을 통해 나타내면 다음과 같다.

분산화된 자율 은행(decentralized autonomous bank)

스마트 계약을 통해 어떠한 업무를 블록체인을 통해 구현하고자 할 때에는 업무를 수행하는 주체의 특징을 명확히 한정 하는 것이 매우 중요하다. 또한 이를 컨트랙트 객체로 구체화 하는 과정에서 업무의 핵심을 담는 상태와 행위를 식별해 낼 수 있어야 한다.


코드구현

앞에서 설계된 자율분산조직의 상태는 변수로, 행위는 함수로 나타낼 수 있다.

  • 라인 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를 사용해보자. [솔리디티 테스트 할 수 있는 다양한 방법]

browser solidity링크

Bank.sol의 소스 내용을 브라우저 솔리디티에 붙여 넣으면 실시간으로 컴파일을 하게 된다. 우측에 Bytecode와 Interface필드를 메모장에 복사해 놓자.

1편 개별환경구성에서 만들었던 execute_geth.sh를 실행해 1번 노드를 구동시키고, attach_geth.sh를 실행해 콘솔창을 연다.

앞으로 콘솔을 통해 진행되는 배포 과정을 요약하면 다음과 같다.

  1. 외부 어카운트 2개를 생성
  2. 마이닝을 통해 외부 계정1번에 잔액을 쌓음
  3. 외부계정 1번으로 컨트랙트 계정을 생성하는 트랜잭션 작성
  4. 생성된 트랜잭션을 마이닝
  5. 마이닝 결과 생성된 컨트랙트 주소를 메모

콘솔 실행 과정에서 eth, personal, miner등의 geth javascript API가 사용되었다. 각 API에 대한 상세한 설명은 공식문서 참조.

  • 라인40 : 이 줄을 실행하고 1번 노드의 표준출력을 살펴보면 외부계정 주소와 동일한 형식의 컨트랙트 계정 주소가 생성된 것을 확인할 수 있다. 메모장에 복사 해 놓자. (혹은 eth.getTransactionReceipt(“트랜잭션해시”)명령을 통해 조회 할 수 있다)

은행에 입금, 조회하기

이번 단계를 진행하기 위해서 앞에서 생성된 ‘콘트랙스 주소’와 브라우저 솔리디티에서 복사한 ‘인터페이스 정보’가 필요하다.

수록된 코드는 다음 과정으로 요약할 수 있다.

  1. 콘솔에 배포된 컨트랙트와 커뮤니케이션 하는 객체 생성
  2. 배포된 컨트렉트에 외부 계정이 예금
  3. 마이닝
  4. 예금 내역을 조회
  • 라인 28~29 : 이 줄을 통해서 배포된 컨트랙트의 잔액 정보를 확인할 수 있다. getBalance명령은 콘트렉트 주소 뿐 아니라, 외부계정 주소의 잔액도 확인하는 데에도 쓰인다.
  • 라인 33 : call을 통해 처음 생성한 1번 계정의 잔액을 조회한다.

한계점 및 주의사항

Bank.sol 테스트 코드로써, 실제 배포해서 사용할 경우 심각한 문제를 일으키게 된다. 이는 다음과 같은 문제 때문이다.

  1. 예금에 대한 출금은 예금을 한 ‘당사자’만 할 수 있어야 하는데, 현재 코드에서는 그렇지 않다. 출금 함수 초입에 조건문이나 modifier등을 통해서 출금 가능한 계정을 제한해야 한다.
  2. value를 0으로 한 허위 예금 트랜잭션이 다량 생성될 수 있다. 최소 입금 조건을 설정해서 이러한 공격을 방지해야 한다.
  3. call.value을 위처럼 작성할 경우 reentrant공격을 당하게 된다. 이를 방지하기 위해서는 Checks-Effects-Iterations패턴을 적용한 시큐어 코딩이 필요하다.
  4. 기타 여러 보안 조치 부재

제시한 문제를 해결하는 Bank.sol을 만들어 보는 것도 solidity를 공부하는 데에 의미있을 것이다.

그렇지만 공식 문서에는 코드 자체로 문제가 없더라도 이더리움 플랫폼 자체에 대한 발견되지 않은 취약점이 존재할 수 있으므로, 신중해야 한다는 내용이 포함되어 있다. 얼마전에 발생한 옵코드 Dos공격이 대표적인 예다. (설명)