난수 생성은 스마트 컨트랙트 보안에서 중요한 요소입니다. 블록체인 내부에서 난수를 생성하려 하면 조작 가능성이 높아 보안에 취약할 수 있습니다. 이를 해결하기 위해 외부 오라클을 사용해 보안성을 높일 수 있습니다.
외부 오라클을 사용하는 이유
- 예측 가능성 방지: 블록 해시나 타임스탬프를 사용하는 난수는 공격자가 예측할 수 있습니다.
- 조작 방지: 블록 생성자가 블록 해시 등을 이용해 결과를 조작할 가능성이 있습니다.
- 공정성과 신뢰성: 외부 오라클은 난수를 암호학적으로 생성하여 공정성을 보장합니다.
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];
}
}
코드 설명
- VRFCoordinatorV2Interface 및 VRFConsumerBaseV2
- Chainlink VRF 의 인터페이스 및 기본 컨트랙트를 상속하여 VRF 요청 및 응답 처리를 간소화.
- Subscription ID
- Chainlink VRF 를 사용하려면 Subscription ID가 필요하며, 이를 생성 후 등록.
- requestRandomWords()
- VRF Coordinator 에 난수를 요청.
- keyHash 와 subscriptionId를 이용하여 요청을 확인하고 난수를 생성.
- fulfillRandomWords()
- VRF Coordinator 가 호출하는 콜백 함수.
- Chainlink 에서 생성된 난수를 전달받아 s_randomWords 배열에 저장.
- getRandomNumber()
- 생성된 난수를 반환.
- 난수가 요청되기 전에 호출되지 않도록 확인.
보안 고려사항
- LINK 잔액 관리: LINK 잔액이 부족하면 난수 요청이 실패합니다. 잔액을 정기적으로 확인하고 충전해야 합니다.
- 콜백 가스 한도 설정: callbackGasLimit 은 응답 처리에 필요한 충분한 가스를 설정해야 합니다. 부족하면 요청이 실패할 수 있습니다.
- 중복 요청 방지: 동일한 요청 ID로 중복 요청이 발생하지 않도록 검증 로직을 추가해야 합니다.
- 의존성 확인: 외부 오라클 의존성이 있으므로 네트워크 상태를 확인하고, 오라클 장애 시 대안을 마련해야 합니다.
레퍼런스
'Solidity' 카테고리의 다른 글
| Solidity 개발 시, 가스비 절감을 위한 최적화 기법 (1) | 2025.01.03 |
|---|---|
| Ethereum 에서 사용할 수 있는 주요 오라클 서비스 (2) | 2025.01.03 |
| delegate call 이란? (1) | 2025.01.03 |
| Solidity에서의 예외처리: require vs. assert (0) | 2025.01.02 |
| OpenZeppelin PullPayment 의 사용 사례 (0) | 2025.01.02 |