Solidity 튜토리얼 4: truffle + zeppelin-solidity를 이용한 크라우드 세일 계약 III

Yoonjae Yoo
DNEXT
Published in
8 min readApr 16, 2018

--

Solidity 튜토리얼 3: truffle + zeppelin-solidity을 이용한 크라우드 세일 계약 II에서 이어집니다.

여기서 쓰인 예제 코드들은 실제로 작동하는 코드이지만, 취약점이 존재할 가능성이 있습니다. 따라서 실제 크라우드 세일에서 사용하지 말아야 합니다. 사용했을 때 발생하는 문제에 대해서는 책임을 지지 않습니다. 계약의 보안성을 높이기 위해서는 스마트 계약 전문 Audit 업체들에게 서비스를 받는 것을 추천합니다.

사전 등록을 위한 계약 작성

사전 등록을 위한 계약의 이름은 DNextTokenWhitelist.sol 로 명명합니다.

pragma solidity ^0.4.21;contract DNextTokenWhitelist {
}

우선 계약 배포자를 명시하고 생성자에서 배포자를 설정해줍니다.

address owner;function DNextTokenWhitelist() public {
owner = msg.sender;
}

그리고 우리는 화이트리스트가 저장될 상태 변수가 필요합니다.

mapping(bytes32 => bool) whitelist;

여기서 whitelist 변수 자료형(type)인 mapping(bytes32 => bool)을 이해해봅시다. 우선, mapping 자료형은 키, 값 형태로 구성된 자료의 집합입니다. 이 때, 키는 bytes32 자료형(32바이트의 데이터)의 데이터를 키로 가지고, bool 자료형(true 혹은 false)의 데이터를 값으로 가집니다.

자료형을 이렇게 설계한 이유는, 특정 이더리움 주소의 해시값(bytes32 자료형)에 대해 해당 주소가 화이트리스트에 등록되어 있는지 여부(bool 자료형)을 나타내기 위함입니다. 이더리움 주소 자체를 저장하지 않고 해시값을 저장하는 이유는 참여자들의 프라이버시를 보호하기 위해서입니다. 블록체인은 누구나 볼 수 있는 공개된 데이터베이스이기 때문에, 특정 주소가 크라우드 세일에 참여하기 위해 화이트리스트에 등록되었다는 사실 또한 영속적으로 기록되게 됩니다.

다음은 화이트리스트에 등록하는 함수입니다.

function register() external {
whitelist[keccak256(msg.sender)] = true;
}

우선 주목해야할 것은, external 키워드입니다. external로 정의된 함수는 외부 소유 계정(EOA: Externally Owned Account)¹만이 실행시킬 수 있습니다. 위에서 봤던 public 또한 한 종류인데, 상태 변수 또는 함수의 가시성(visibility), 즉 상태 변수 혹은 함수가 얼마나 많이 공개될 것인가를 나타냅니다. 종류는 아래와 같이 네가지가 있습니다.

  • external : 계약의 ABI(Application Binary Interface)²에 포함되는 것으로써 해당 외부에만 공개됨.
  • public : 해당 계약 내부와 외부 모두에 공개됨.
  • internal : 해당 계약과 해당 계약을 상속한 하위 계약에게만 공개됨.
  • private : 오로지 해당 계약 계정에게만 공개됨.

register() 함수는 external로 정의했기 때문에 사전 등록 참여자들이 거래(transaction)를 발생시켜 우리의 계약과 상호작용할 수 있는 것입니다. 만약 public이라면 오로지 다른 계약 계정도 해당 함수를 실행할 수 있습니다.

함수의 내용은, 이 함수를 실행시키는 참여자의 이더리움 주소에 해당하는 msg.sender 의 해시값을 keccak256() 함수를 통해 구한 뒤, 해당 해시값에 해당하는 whitelist 변수의 값을 true로 만들어줍니다. 이를 통해 우리의 화이트리스트 계약 계정 내부에 있는 저장소(storage)에 해당하는 상태 변수에 영속적으로 저장이 되지만, 참여자의 주소값 그 자체가 저장되는 것이 아니고 해시값만이 저장되므로 참여자의 프라이버시를 보장할 수 있게 되었습니다.

우리의 요구조건에 따라 등록된 화이트리스트 항목은 등록 해제할 수도 있어야 합니다.

function unregister() external {
whitelist[keccak256(msg.sender)] = false;
}

다른 내용은 register 함수와 같지만, 해시값에 해당하는 값을 true 대신 false로 바꿔준다는 차이점이 있습니다.

이렇게 등록/등록 해제된 화이트리스트 내용은 아래의 함수를 통해 참조됩니다.

function isRegistered(address anAddress) public view returns (bool registered) {
return whitelist[keccak256(anAddress)];
}

우선 isRegistered() 함수의 public 키워드 바로 뒤에 붙은 view 키워드에 주목합시다. view 키워드는 해당 함수가 블록체인의 상태를 변경시키지 않음을 의미하고 구체적으로는 아래에 나열된 사항 중 어떤 것도 하지 않는다는 것을 의미합니다.

  • 상태 변수 값 변경
  • 이벤트 발생
  • 다른 계약 생성
  • selfdestruct 사용(해당 계약 계정을 삭제하고 모든 잔액을 지정된 주소로 이동)
  • 이더 전송
  • view 혹은 pure로 선언되지 않은 어떠한 함수라도 호출
  • 로우레벨 호출
  • 특정 OPCODE를 포함한 인라인 어셈블리 사용

이와 비슷한 것으로는 pure가 있습니다. pureview로 선언된 함수는 EOA 입장에서 다른 함수들와 다르게 거래를 생성하지 않고도 실행될 수 있습니다. 즉, 거래가 생성되고 전파되어 블록에 기록되는 시간(최소 15초 정도)를 기다리지 않고도 바로 결과를 확인할 수 있습니다.

pure 함수는 아래에 나열된 사항 중 어떤 것도 하지 않는다는 것을 의미합니다.

  • 상태 변수 읽기
  • this.balance 혹은 <주소>.balance 접근
  • block, tx, msg 중 하나의 멤버 변수에 접근(예외: msg.sigmsg.data는 제외)
  • pure로 정의되어 있지 않은 어떠한 함수라도 호출
  • 특정 OPCODE를 포함한 인라인 어셈블리 사용

isRegistered() 함수의 역할은 첫번째 인자인 anAddress의 주소값이 화이트리스트에 등록되어 있는지 여부를 확인하는 것입니다. 이를 위해 등록/등록 해제를 했을 때처럼 주소값의 해시값을 구해서 whitelist 변수에서 해당 해시값에 해당하는 값이 true 인지 여부를 반환합니다.

함수의 view 키워드 뒤에 returns (bool registered)라는 새로운 키워드가 나타나는데, 이것의 의미는 ‘이 함수는 bool 자료형의 registered 라는 이름의 반환값을 가지고 있다’입니다. 함수 내에서 return whitelist[keccak256(anAddress)를 통해 bool 값을 반환하고 있으므로 함수의 정의와 일치합니다.

isRegistered() 함수는 public 함수이므로 EOA가 실행할 수는 없습니다. 즉, 다른 계약 계정이 실행해야 하는데 그 계약은 바로 이후에 우리가 살펴볼 토큰 판매 계약입니다.

이렇게 완성된 사전 등록 계약의 전체 소스 코드는 아래와 같습니다.

pragma solidity ^0.4.21;

contract DNextTokenWhitelist {
address owner;
mapping(bytes32 => bool) whitelist;

function DNextTokenWhitelist() public {
owner = msg.sender;
}

function register() external {
whitelist[keccak256(msg.sender)] = true;
}

function unregister() external {
whitelist[keccak256(msg.sender)] = false;
}

function isRegistered(address anAddress) public view returns (bool registered) {
return whitelist[keccak256(anAddress)];
}
}

다음 포스트에서는 세번째이자 가장 중요한 스마트 계약인, 토큰 판매를 위한 스마트 계약 작성에 대해 다룰 예정입니다.

스마트 계약과 관련한 도움이 필요하다면 DNext에서 운영하고 있는 블록체인 전문 교육 기관인 DNext Campus를 언제든 방문해주시기 바랍니다. 블록체인, 이더리움, 스마트 계약에 관련된 주제로 정규 교육 과정을 운영중에 있습니다.

또한 저희 DNext에서는 토큰 발행 및 판매와 관련한 전문 컨설팅 서비스를 제공하고 있습니다. 도움이 필요하신 분들은 support@dnext.co로 문의주시기 바랍니다.

  1. 외부 소유 계정: 계약 계정과 대비되는 개념으로 코드가 포함되어 있지 않고 일반 사용자들이 개인키(private key)를 통해 제어하는 일반 계정.
  2. ABI: 계약과 상호작용을 하기 위해 필요한 일종의 설명서. EOA가 거래(transaction)을 통해 계약의 함수를 실행시키기 위해서 필수적인 정보.

--

--