您不能创建真正的随机数,但能够生成伪随机数。区块链是一种确定性系统,因此我们必须确保每个节点必须提供相同的随机数。
确定性非常重要,因为无论智能合约代码在何处执行,都会每次和任何地方产生相同的结果。
在确定性系统中获取真正的随机数是不可能的,因为使用全局变量是可预测或可以被某种方式操纵。
例如,时间戳漏洞很常见。通常,块的时间戳通过
block.timestamp
访问,但这个时间戳可以被矿工操纵,导致影响依赖于时间戳的某些函数的结果。时间戳用作彩票游戏中选择下一个获胜者的随机源。因此,矿工可能会以某种方式修改时间戳,从而增加成为下一个获胜者的机会。
为了获得真正的随机数,我们必须从区块链外部寻找。我们需要使用oracle服务来获取真正的随机数。如果您的智能合约中没有真正的随机数,则您的智能合约可能会被黑客攻击。您可以阅读2个案例的详细信息:
https://www.reddit.com/r/ethereum/comments/74d3dc/smartbillions_lottery_contract_just_got_hacked/
https://hrishiolickel.medium.com/why-smart-contracts-fail-undiscovered-bugs-and-what-we-can-do-about-them-119aa2843007
由于Solidity发展非常快,其他答案已经过时。虽然这个答案有一天也会过时,但目前您可以像这样实现一个伪随机数生成器:
uint counter =1;
function random() private view returns (uint) {
counter++;
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, players,counter)));
}
这将返回非常大的数字。但我们使用模运算符。
random() % players.length
这将返回一个介于0和players.length之间的数字。我们编写一个函数来实现:
function pickWinner() public {
uint index=random()%players.length;
}
使用Chainlink获取随机数
在部署此合约后,您需要向该合约发送Link代币,然后点击getRandomNumber
。等待大约一分钟,然后点击result
。
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBase.sol";
contract Test is VRFConsumerBase {
bytes32 public keyHash;
uint256 public fee;
uint256 public ticketPrice;
uint256 public result;
constructor(
address _ChainlinkVRFCoordinator,
address _ChainlinkLINKToken,
bytes32 _ChainlinkKeyHash,
uint256 _ticketPrice
) VRFConsumerBase(_ChainlinkVRFCoordinator, _ChainlinkLINKToken) {
keyHash = _ChainlinkKeyHash;
fee = 0.1 * 10 ** 18;
ticketPrice = _ticketPrice;
}
function getRandomNumber() public payable returns (bytes32 requestId) {
require(
LINK.balanceOf(address(this)) >= fee,
"YOU HAVE TO SEND LINK TOKEN TO THIS CONTRACT"
);
return requestRandomness(keyHash, fee);
}
function fulfillRandomness(
bytes32 requestId,
uint256 randomness
) internal override {
result = randomness;
}
receive() external payable {}
}
我回答了这个问题:
使用Chainlink VRF获取随机数。