【发布时间】:2022-01-13 20:44:02
【问题描述】:
我正在构建一个彩票智能合约,用户可以在其中购买一张彩票,该彩票具有从 1 到 25 的 5 个生成数字,而没有重复的数字。
一切都很好,除了 30% 的时间,Metamask 说“交易用完了气体”,因为这个功能。
下面的函数生成一个由 1 到 25 之间的 5 个数字组成的数组:
/// @dev Generate 5 numbers ( 1 <= x >= 25) without duplicate numbers
/// @param _lotteryCount used for the random generator number function
/// @return uint256[5] return an array of 5 generated uint
function generateRandomTicketNumbers(uint256 _lotteryCount) internal view returns (uint8[5] memory) {
uint8[5] memory numbers;
uint256 generatedNumber;
// Execute 5 times (to generate 5 numbers)
for (uint256 i = 0; i < 5; i++) {
// Check duplicate
bool readyToAdd = false;
uint256 maxRetry = 5;
uint256 retry = 0;
// Generate a new number while it is a duplicate, up to 5 times (to prevent errors and infinite loops)
while (!readyToAdd && retry <= maxRetry) {
generatedNumber = (uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, i, retry, _lotteryCount))) % 25).add(1);
bool isDuplicate = false;
// Look in all already generated numbers array if the new generated number is already there.
for (uint256 j = 0; j < numbers.length; j++) {
if (numbers[j] == generatedNumber) {
isDuplicate = true;
break;
}
}
readyToAdd = !isDuplicate;
retry++;
}
// Throw if we hit maximum retry : generated a duplicate 5 times in a row.
require(retry < maxRetry, 'Error generating random ticket numbers. Max retry.');
numbers[i] = uint8(generatedNumber);
}
return numbers;
}
我不确定 Metamask 如何估算交易的 gas,但我猜它在本地运行交易,查看它使用了多少 gas,然后将这个数量用于实际交易。
如果这是正确的,那就可以解释为什么 30% 的时间都失败了。有时,该函数需要多次重试才能在“while”循环中生成一个数字,而有时,“while”循环只执行一次。
我可以强制函数在每个“for”和“while”循环中运行最大次数,但我相信有比浪费气体更好的解决方案。
你知道我该如何解决这个问题吗?
谢谢!
【问题讨论】:
-
Fisher-Yates shuffle 只需 5 次调用随机数生成器即可完成任务。
-
谢谢,这正是我想要的!
标签: algorithm ethereum solidity smartcontracts