난수 생성은 스마트 컨트랙트 보안에서 중요한 요소입니다. 블록체인 내부에서 난수를 생성하려 하면 조작 가능성이 높아 보안에 취약할 수 있습니다. 이를 해결하기 위해 외부 오라클을 사용해 보안성을 높일 수 있습니다. 


외부 오라클을 사용하는 이유

  1. 예측 가능성 방지: 블록 해시나 타임스탬프를 사용하는 난수는 공격자가 예측할 수 있습니다. 
  2. 조작 방지: 블록 생성자가 블록 해시 등을 이용해 결과를 조작할 가능성이 있습니다. 
  3. 공정성과 신뢰성: 외부 오라클은 난수를 암호학적으로 생성하여 공정성을 보장합니다. 

Chainlink VRF 를 활용한 난수 생성

 

Chainlink VRF(Verifiable Random Function)는 보안성과 검증 가능성을 갖춘 난수를 제공하는 오라클 서비스입니다. 

VRF 를 사용하기 위해서는 아래의 조건들을 먼저 확인해야 합니다. 

  • 네트워크: Chainlink VRF 는 다양한 네트워크에서 사용 가능 (Ethereum, Polygon 등)
  • LINK 토큰: 오라클 수수료를 지불하기 위해 필요
  • VRF Coordinator: VRF 요청 및 응답을 처리
  • Subscription: LINK 토큰을 충전하고 오라클과 상호작용

아래는 Chainlink VRF 를 사용해 난수를 생성하는 스마트 컨트랙트의 예입니다. 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

// Chainlink VRF
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract RandomNumberGenerator is VRFConsumerBaseV2 {
    VRFCoordinatorV2Interface COORDINATOR;

    // VRF 설정
    uint64 s_subscriptionId; // Subscription ID
    address vrfCoordinator = 0xYourCoordinatorAddress; // 네트워크별 VRF Coordinator 주소
    bytes32 keyHash = 0xYourKeyHash; // 네트워크별 Key Hash
    uint32 callbackGasLimit = 100000; // Callback 가스 한도
    uint16 requestConfirmations = 3; // 요청 대기 확인 블록 수
    uint32 numWords = 1; // 요청할 난수 개수

    uint256[] public s_randomWords; // 생성된 난수 저장
    uint256 public s_requestId; // 요청 ID
    address s_owner;

    constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId = subscriptionId;
        s_owner = msg.sender;
    }

    // 난수 요청 함수
    function requestRandomWords() external onlyOwner {
        // VRF 요청
        s_requestId = COORDINATOR.requestRandomWords(
            keyHash,
            s_subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
    }

    // VRF 응답 처리
    function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
        require(requestId == s_requestId, "Invalid requestId");
        s_randomWords = randomWords;
    }

    modifier onlyOwner() {
        require(msg.sender == s_owner, "Caller is not the owner");
        _;
    }

    // 난수 반환 함수
    function getRandomNumber() external view returns (uint256) {
        require(s_randomWords.length > 0, "No random number generated yet");
        return s_randomWords[0];
    }
}

 

코드 설명

  1. VRFCoordinatorV2Interface 및 VRFConsumerBaseV2
    • Chainlink VRF 의 인터페이스 및 기본 컨트랙트를 상속하여 VRF 요청 및 응답 처리를 간소화.
  2. Subscription ID
    • Chainlink VRF 를 사용하려면 Subscription ID가 필요하며, 이를 생성 후 등록.
  3. requestRandomWords()
    • VRF Coordinator 에 난수를 요청.
    • keyHash 와 subscriptionId를 이용하여 요청을 확인하고 난수를 생성.
  4. fulfillRandomWords()
    • VRF Coordinator 가 호출하는 콜백 함수.
    • Chainlink 에서 생성된 난수를 전달받아 s_randomWords 배열에 저장.
  5. getRandomNumber()
    • 생성된 난수를 반환.
    • 난수가 요청되기 전에 호출되지 않도록 확인.

보안 고려사항

  1. LINK 잔액 관리: LINK 잔액이 부족하면 난수 요청이 실패합니다. 잔액을 정기적으로 확인하고 충전해야 합니다. 
  2. 콜백 가스 한도 설정: callbackGasLimit 은 응답 처리에 필요한 충분한 가스를 설정해야 합니다. 부족하면 요청이 실패할 수 있습니다. 
  3. 중복 요청 방지: 동일한 요청 ID로 중복 요청이 발생하지 않도록 검증 로직을 추가해야 합니다. 
  4. 의존성 확인: 외부 오라클 의존성이 있으므로 네트워크 상태를 확인하고, 오라클 장애 시 대안을 마련해야 합니다. 

레퍼런스

Chainlink VRF Documentation

Chainlink VRF Example Code

Subscription Management

+ Recent posts