2

该怎么猜智能合约上的随机数?

 2 years ago
source link: https://learnblockchain.cn/article/3441
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

该怎么猜智能合约上的随机数?

该怎么猜智能合约上的随机数?

在链上创建随机数是一项复杂的任务。事实上,有一些方法可以做到这一点,但总的来说,强烈建议在链下进行,因为几乎所有用于熵的输入都是公开的,或者在某种程度上可以被操纵。

幸运的是,这个挑战要求我们猜测链上创建的一个“随机”数字。这是怎么回事?

1642406464128
猜随机数字挑战智能合约代码

合约的第一行是一个uint8变量,answer。记住uint8变量最多包含256个可能的整数:0到255。

这个变量在构造函数中被分配给两个输入的 keccak256 哈希:包含我们部署交易的那个区块的前一个区块的blockhash (block.blockhash(block.number - 1), of type bytes32)和我们的区块被挖的时间戳*(now, of type uint256)*。

请记住,这个合约使用的是编译器版本^0.4.21,从那以后,一些语法发生了变化:block.blockhash()现在是blockhash(),now现在是block.timestamp。

正如我们在这行中看到的,keccak256函数(一个 bytes32 固定大小的字节数组)随后被显式转换为uint8并赋值给我们的变量。

这看起来很随机,对吧?我们应该如何猜出0到255之间的一个数字,它来自于对某个区块的哈希函数和时间戳。

其实很简单。因为区块链上的所有东西都是公开的。

我们的目标是在函数下guess,我们必须调用它并发送一个uint8 + 1 以太(我们已经在部署上发送了一个),然后如果我们的uint8等于answer变量,合约将发送我们2个以太,耗尽余额,因此isComplete()函数将返回到 true。

有多种与合约交互的方式,但我决定通过另一个合约来实现。这不是最简单的方法,在这种情况下,甚至没有必要,但绝对是我们可以利用的方法。

以下是我为解决这个问题所编写的代码:

// SPDX-License-Identifier: No License
pragma solidity ^0.8.0;interface IGuessTheRandomNumberChallenge {
  function guess(uint8) external payable;
}contract GuessTheRandomNumberSolver {  IGuessTheRandomNumberChallenge public _interface;
  bytes32 public previousBlockHash = 0x66bcdb5e320c9e0c04a9fdeaa15de33a4c8a040db342f4f955fa54f170dba9ce;
  uint public previousTimestamp = 1641520092;  constructor(address _interfaceAddress) {
    require(_interfaceAddress != address(0), "Address can not be Zero");
    _interface = IGuessTheRandomNumberChallenge(_interfaceAddress);
  }  function solve() public payable {
    uint8 answer = uint8(uint256(keccak256(abi.encodePacked(previousBlockHash, previousTimestamp))));
    _interface.guess{value: 1 ether}(answer);
  }  function getBalance() public view returns(uint){
    return address(this).balance;
  }  function withdraw() public {
    payable(msg.sender).transfer(address(this).balance);
  }  receive() external payable {}
}

编译器版本之后,首先看到的是一个接口。我们可以使用它们通过代码与其他合约交互。它基本上是一个带有一些规则的简单合约:

  • 它们不能从其他合约继承,但可以从其他接口继承。
  • 所有声明的函数必须是外部的。
  • 它们不能声明构造函数。
  • 它们不能声明状态变量。
  • 它们不能声明修饰符。

因为我们只需要调用' guess '函数,所以它是我们在接口中声明的唯一一个函数。

然后,在我们的GuessTheRandomNumberSolver合约中,我们将声明一个_interface变量,并通过构造函数分配挑战的地址(在CTE中部署它时获得的的地址)。

这就是我们现在在已部署的挑战中调用函数所需要的一切,我们继续收集信息,以重新创建与它一起部署的random number。

这些都可以在etherscan中找到,我们只需要寻找我们挑战的地址。

Blockhash(block.number - 1):要得到这个,可以转到内部Internal Txns标签,然后单击显示Contract Creation的同一行上的区块号码。在我的例子中,区块是#11766860:

1642406633774

现在,我们可以看到很多关于那个区块的信息,但我们需要访问前一个,所以继续寻找它。在我的例子中,它是#11766859。

下面我们可以看到hash。这是我们需要的第一个信息。

Block.timestamp:回到我们的区块,你会在第二行看到时间戳。这是一种人类可读的格式,我们需要Unix Timestamp格式。那是什么? 它是自1970年1月1日以来所经过的秒数。这是衡量时间的标准方法。

为了将这个人类可读的时间戳转换为Unix时间,我使用了一个非常方便的站点epochconverter。有了这个数字,我们终于有了最后一块拼图,我们可以来解决这个挑战。

回到GuessTheRandomNumberSolver合约,让我们创建一个solve函数,我们将调用它来联系我们的挑战合约。

为了提高可读性,我还创建了两个新变量:

  • bytes32 public previousBlockHash
  • uint public previousTimestamp.

创建它们,但要赋予它们挑战的价值。

然后,在我们的solve函数中,我们将创建uint8 answer变量,并将其赋值:

uint8(uint256(keccak256(abi.encodePacked(previousBlockHash, previousTimestamp))))

语法和格式的变化是因为我们使用的是^0.8.0版本的编译器,而挑战是使用^0.4.21版本。

现在我们已经将答案赋给了变量,我们只需要通过接口调用挑战。这就是下一行要做的:

\_interface.guess{value: 1 ether}(answer)

我假设你正在使用remix,所以继续,通过Injected Web3环境连接到metamask钱包,并部署合约,指定自己的挑战地址来分配给自己的界面。

现在,在将值输入为1的情况下,继续调用guess函数。

我已经添加了更多的函数:

  • getBalance()
  • withdraw()
  • receive()

这是因为挑战是msg.sender将是我们的GuessTheRandomNumberSolver合约,而不是我们的EOA -所以我们需要接收2个以太,并能够将它们发送到我们的EOA。

1642406811475

Source:https://betterprogramming.pub/capture-the-ether-guess-the-random-number-2ebb8c9c0347

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

Layer 2道友 - 欢迎对Layer 2感兴趣的区块链技术爱好者、研究分析人与Gavin(微信: chinadefi)联系,共同探讨Layer 2带来的落地机遇。敬请关注我们的微信公众号 “去中心化金融社区”


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK