이더리움에서는 랜덤함수를 제공하지 않는다. 생성되는 블록의 번호나 해시값으로 난수를 생성하면 채굴자는 자신에게 유리한 블록만 채굴하는 경우가 발생할 수 있다. 또한 Don't trust, just verify
라는 말이 있듯이 블록체인에서는 정해진 상태 값과 인풋 값이 있을 때 그 결과값 또한 예측한 그대로 똑같은 값만 내뱉어야 한다. 난수 생성은 이와 정반대의 특성을 갖고 있기 때문에 블록체인에서 난수를 만든다는 것은 쉬운 일이 아니다.
체인링크에서는 온체인에서 검증 가능한 난수를 무작위로 생성할 수 있는 기능인 VRF(Verifiable Random Function)을 제공한다. 컨트랙트에서 VRF Coordinator로 난수 생성을 요청하면(온체인으로 동작) VRF Coordinator는 VRF Node로 요청을 전달한다. VRF Node는 blockhash
와 nonce
값을 이용해 난수를 생성(오프체인으로 동작)하고 다시 VRF Coordinator로 결과를 보낸다. 마지막으로 결과값을 받은 VRF Coordinator는 생성된 난수를 검증하는 절차를 거친다. 사용자는 VRF를 구독하고 미리 LINK토큰을 입금해놓아야 하며, LINK토큰을 비용으로 지불해야 한다는 단점이 있다.
예제에서 사용한 keccak256
은 암호화 해시 알고리즘이다. 입력값이 한 글자만 바뀌어도 결과가 완전히 달라진다. 사용자가 많은 Dapp의 경우 악의적인 노드가 자신에게 유리한 방향으로 악용할 수 있다는 단점이 있다. 하지만 예제처럼 작은 Dapp의 경우 그럴 일이 없으니 마음 놓고 사용한 것 같다.
keccak256
을 사용할 때는 주로 abi.encodePacked
를 통해 bytes 값을 얻어서 인자로 넣어준다. 예전에는 keccak256
함수도 여러 인자를 받을 수 있었다. 하지만 현재는 여러 개의 인자를 넣어주려면 인코딩 해서 넣어줘야 한다. 인코딩 할 때는 여러 방법이 있지만 여기선 abi.encodePacked
를 사용했다.
인코딩의 한 종류인 abi.encode
는 length 값을 포함해서 각 인자당 32bytes로 고정된 결과값을 리턴하기 때문에 복잡해보이는 반면, abi.encodePacked
는 전달받은 인자를 length 정보를 제외하고 인코딩해주기 때문에 조금 더 간결한 결과값을 얻을 수 있다.
FIRST_WORD
와 string으로 만든 tokenId
를 인자로 넣어줬다. 굳이 FIRST_WORD
를 하드코딩해서 넣어 줄 필요는 없지만 예제에서는 램덤 소스로 사용했다. 이후 배열의 길이에 맞게 나눈 나머지를 구하고 이를 배열 index 값으로 활용했다. 나머지 값을 index로 사용하면 배열의 길이를 초과한 숫자가 나오지 않기 때문.