Random Generation in Blockchain

Random Generation with SCORE

Joonyoung Choi
B!ock.Chain
8 min readMay 13, 2019

--

이번 글은 ICON의 스마트컨트랙트인 SCORE 를 활용하여 블록체인 상에서 랜덤 함수를 구현하는 과정을 정리한 것으로, SCORE 작성 방법에 대해서는 다루지 않습니다.

아직 SCORE 작성 방법이 익숙하지 않으신 분들은 SCORE 작성 방법에 대해 간단한 샘플을 작성하며 배우는 How to SCORE 시리즈를 먼저 보시고 오실 것을 권장합니다.

실습 환경 : 운영체제[macOS Mojave], 파이썬[3.6.5]

How to SCORE 시리즈는 총 7개의 게시글로 구성되어 있습니다.

Table of Contents

Random

  • Random Number
  • Conditions for Random Generation Function

How to implement

  • Hash
  • Block Timestamp

Hands-on Exercise

  • Deterministic
  • Unpredictable
  • Dice Roll

Random

간단한 ‘주사위 굴리기’ 부터 보다 복잡한 게임들까지 랜덤함수는 다방면에서 활용되고 있습니다.

블록체인은 서로 다른 환경의 노드에서도 동일한 입력을 바탕으로한 랜덤 함수의 실행 결과가 같아야 합의가 이루어 질 수 있습니다.

기존의 프로그래밍 언어에서 제공하는 랜덤 함수들은 노드의 환경에 영향을 받아 동일한 입력에도 동일한 결과가 도출되지 않습니다.

Random Number

Conditions for Random Generation Function

  • Deterministic

동일한 조건에서 실행된 랜덤 함수는 동일한 결과를 반환해야 합니다.

  • Unpredictable

랜덤 함수의 실행 결과는 예측 불가능해야 합니다.

How to implement

위키백과의 설명을 따르자면 의사난수를 생성하기 위해서는 변하는 입력함수 두 가지가 필요합니다.

Hash

Hash 함수의 가장 기본적인 성질두 해시 값이 다르다면 원래의 데이터도 다르다는 것입니다. 또한 해시함수에 입력되는 데이터가 아주 조금이라도 수정된다 하더라도 출력되는 결과는 크게 변화하는 성질(산사태 효과 : Avalanche Effect)을 가지고 있습니다.

이러한 특징을 가진 Hash 함수를 활용하여 동일한 입력에 대해 동일한 결과가 도출되는 결정론적인 랜덤 생성 함수를 구현할 수 있습니다.

Block Timestamp

현재 블록의 타임스탬프로 블록이 생성될 때마다 변화합니다. 따라서 블록의 타임스탬프 혹은 이와 유사하게 블록 생성될 때 마다 변하는 블록의 높이를 예측 불가능한 랜덤 생성 함수를 위한 변하는 입력으로 사용할 수 있습니다.

Hands-on Exercise

Hash 함수와 Block Timestamp를 활용하여 앞서 살펴 본 두가지 조건들을 만족하는 결정론적이고, 예측 불가능한 랜덤 생성 함수를 구현해 봅시다.

Deterministic

ICON에서 Utility Function으로 제공하는 Hash 함수인 sha3_256을 활용하여 동일한 입력에는 동일한 결과가 출력되며, 작은 입력의 변화에도 출력되는 결과가 크게 달라지는 함수를 구현할 수 있습니다.

sha3_256(data: bytes)와 같이 bytes를 입력으로 하여 함수의 실행 결과로 bytes 데이터를 받아 사용할 수 있습니다.

    @external(readonly=True)
def getRandom(self) -> int:
input_data = f'{self.block.timestamp}'.encode()
hash = sha3_256(input_data)
return hash

작성한 getRandom 메소드는 sha3_256을 활용하여 동일한 입력에 대해 결정론적으로 동일한 출력을 반환하도록 구현되었습니다.

Hash 함수와 블록의 타임스탬프를 활용하여 결정론적인 랜덤 생성 함수를 작성하였지만, 사실 입력 값인 블록의 타임스탬프가 특정 상황에서는 어느정도 예측 가능하여 예측 불가능한 함수로 활용하기엔 아직 부족한 것 같습니다.

Unpredictable

작성한 getRandom 메소드를 호출하는 사용자로부터 호출할 때 마다 임의의 데이터를 입력 받아 활용하도록 랜덤 생성 함수를 수정해 봅시다.

    @external(readonly=True)
def getRandom(self, data: str) -> int:
input_data = f'{self.block.timestamp}, {data}'.encode()
hash = sha3_256(input_data)
return int.from_bytes(hash, 'big)

사용자에 의해 메소드가 호출될 때 입력되는 data: str를 통해 랜덤 생성 함수의 결과를 이전보다 예측하기 어렵도록 보완하였습니다.

int.from_bytes(bytes, byteorder, *, signed=False)를 통해 bytes타입의 데이터를 int 타입으로 변환하여 반환합니다.

이외에도 여러 구현 방식을 통해 랜덤 생성 함수의 결과가 더욱 예측하기 어렵도록 보완할 수 있습니다.

Dice Roll

사용자로부터 메소드가 호출될 때 마다 data: str를 입력받도록 구현한 랜덤 생성 함수를 활용하여 0 ~ 5의 수를 무작위로 반환하는 간단한 주사위를 구현해 봅시다.

    def _get_random(self, data: str):
input_data = f'{self.block.timestamp}, {data}'.encode()
hash = sha3_256(input_data)
return int.from_bytes(hash, 'big')
@external(readonly=True)
def diceRoll(self, data: str) -> int:
return self._get_random(data) % 6
간단한 랜덤 생성 함수를 구현한 SCORE 샘플

랜덤 생성 함수를 구현한 SCORE를 배포하고 ICONex를 통해 생성한 함수를 호출하며 결과를 확인해봅시다.

입력하는 데이터는 a -> b -> a 순으로 기대하는 결과는 메소드 호출 시마다 0 ~ 5 사이의 무작위로 뽑힌 숫자가 출력되기를 기대합니다.

첫번째 a 입력과 마지막 a 입력의 경우에도 블록의 타임스탬프 값이 달라지므로, 서로 다른 결과가 출력될 수 있어야 합니다.

세번의 메소드 호출에 따른 결과는 확률적으로 같을 수 있습니다.

처음 문자 a 를 입력값으로 하여 diceRoll 메소드를 호출한 결과 : 4
두번째로 문자 b 를 입력값으로 하여 diceRoll 메소드를 호출한 결과 : 1
마지막으로 다시 한번 문자 a 를 입력값으로 하여 diceRoll 메소드를 호출한 결과 : 5

ICON 과 관련된 추가적인 질문사항이 있는 경우, ICON 의 공식 개발자포럼 혹은 페이스북 그룹에 질문하시면 답변을 얻으실 수 있습니다.

아이콘 공식 개발자포럼

Dive into ICON 페이스북 그룹

ICON Developers 유튜브 채널

--

--

Joonyoung Choi
B!ock.Chain

Blockchain / Java / Python / Developer / Researcher