我希望我的智能合约在调用合约时返回7或8个 UNIQUE 随机数,范围从1到100。获得这种结果的最佳方法是什么?
答案 0 :(得分:4)
如果您尝试使用以太坊区块链构建轮盘,彩票和纸牌游戏,由于以太坊区块链是确定性的,这给那些选择编写自己的伪随机数生成器(PRNG)的人带来了一定的困难。
如果您使用诸如block.coinbase,block.difficulty,block.timestamp等的块变量作为熵的来源,那么所有这些块变量都可以被矿工操纵,因此不能用作矿工的来源。由于矿工的激励而产生熵。由于块变量显然是在同一块内共享的,因此您可以轻松地使用内部消息来产生相同的结果。
其他方法类似于使用当前或过去的块的哈希 或过去区块的区块哈希结合私有种子。在这些情况下使用block.blockhash(block.number)函数。但是,在EVM中执行事务时,由于明显的原因,尚不知道正在创建的块的块哈希,并且EVM总是产生零。如果我们尝试使用前一个区块的区块哈希来进行尝试,则攻击者可以使用相同的代码制定漏洞利用合同,以便通过内部消息调用目标合同。这两个合同的“随机”数将相同。
即使我们将区块哈希与私有种子结合在一起,本质上是透明的,也绝不能使用区块链以明文形式存储秘密,从合同存储中提取私有变量指针的值并提供它是很简单的作为对漏洞利用的论证。
借助Oraclize之类的外部Oracle,智能合约可以从Web API请求数据,例如货币汇率,天气预报和股票价格(例如random.org)。这种方法的主要缺点是它是集中式的。 Oraclize守护程序会篡改结果吗?我们可以信任random.org吗?
我们也可以使用BTCRelay代替Oraclize,BTCRelay是以太坊和比特币区块链之间的桥梁。通过使用BTCRelay,以太坊区块链中的智能合约可以请求将来的比特币区块哈希并将其用作熵的来源。
Signidice是一种基于加密签名的算法,可用于仅涉及两方(玩家和房屋)的智能合约中的随机数生成。该算法的工作原理如下:
公开展示方法包括两个阶段:
{@ 3}}是一种更好的落实披露方法。提交-披露可以与将来的哈希结合使用,以使其更加安全。
这几乎涵盖了所有使用以太坊生成随机数的方法。
答案 1 :(得分:2)
就像拉加夫所说,区块链上的随机数很难。网络的公共性质使得很难生成无法预先计算的数字。
话虽如此,最好的解决方案之一是使用从外部(读取:基于非区块链的)源获取随机数的预言机。看看this指南。 Ethtroll Dapp是一个很好的例子,因此请看一下代码here。他们使用Oraclize从Random.org获取随机数。使用Oracle的问题是集中化因素。如果按照我上面描述的方式设置Dapp,那么您将受雇于两个不同的集中式服务部门(Oraclize和Random.org)的工作人员。尽管某人不太可能操纵这两种资源中的任何一种,但人们会为获得潜在的经济利益而采取不合理的行为。
答案 2 :(得分:2)
使用块哈希或类似的随机种子方法存在许多问题。如果攻击者在您签订合同之前就知道了哈希,则他们可以使用该信息来获取您想要做的任何恶意利用。甲骨文可以在这里提供帮助,但是它们是失败的主要根源,必须能够证明它们是随机的。
您需要一个可以执行以下操作的oracle网络:
这时,以下示例显示了如何解决#1的问题。您可以通过从足够数量的支持Chainlink VRF的节点中拉出来解决#2。
要获得确切的实现,请see this answer from a similar question.
您将希望使用带有您生成的种子的函数来向节点发出请求:
function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
bytes32 _requestId = requestRandomness(keyHash, fee, seed);
emit RequestRandomness(_requestId, keyHash, seed);
return _requestId;
}
返回值时,将其修改为100并加1。如果需要7或8个随机数,则需要调用7或8次。
function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
uint256 d6Result = randomness.mod(100).add(1);
emit RequestRandomnessFulfilled(requestId, randomness);
}
答案 3 :(得分:0)
我有一个集思广益的想法,也许可以帮助别人。
这是一种简化的“提交-披露”方法,只有一名参与者。 每个随机世代都需要一个标题。 标题应该是标准的,并且易于审核。
首先我在smartContract上提交(“爱丽丝的彩票”)。如果标题重复(校验哈希),它将被拒绝。 并且需要至少等待至少1个额外的区块确认才能显示,因此这2个区块应来自不同的矿工,以确保矿工不会攻击此智能合约。
然后执行Reveal(“ Alberto的彩票”)。 魔术发生在这里;随机源将是提交块的十分之一,msg.sender,block.blockhash和block.blockhash(commitBlockNumber + 1),因为没有人可以预测未来的哈希值,也没有哪个矿工会发现它[您可以添加coinbase或时间戳也可以获取更多随机值]。 另外,您还可以检查commitBlockNumber和commitBlockNumber + 1的时间戳是否太近或太远,这可能表明某个矿工正在试图强行阻止某个块,因此您可以拒绝此彩票。
当然,如果您可以通过类似(“ Alice's Lottery”)||的提交观看太多的tx (“ A Alice's Lottery”),您可以检查该彩票是否被欺骗。 您也可以使用2个以上的“间隔”块来完成此操作