- Published on
Capture the Ether (RareSkills Repo) - Guess the Secret Number - Solution
- Authors
- Name
- Marco Besier, Ph.D.
- @marcobesier
Capture the Ether (RareSkills Repo) - Guess the Secret Number - Solution
Contract
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract GuessTheSecretNumber {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
constructor() payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable returns (bool) {
require(msg.value == 1 ether);
if (keccak256(abi.encodePacked(n)) == answerHash) {
(bool ok,) = msg.sender.call{value: 2 ether}("");
require(ok, "Failed to Send 2 ether");
}
return true;
}
}
Solution
Note that n
is of data type uint8
, i.e., the number we're looking for must be an unsigned integer between 0 and 255. Since the set of possible solutions is so small, we can easily brute-force the solution either on-chain or off-chain.
The code below demonstrates how to solve this challenge on-chain:
GetSecretNumber.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract GuessTheSecretNumber {
...
}
contract ExploitContract {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
function Exploiter() public view returns (uint8) {
uint8 n;
for (uint8 i; i < 255; i++) {
if (keccak256(abi.encodePacked(i)) == answerHash) {
n = i;
}
}
return n;
}
}
GetSecretNumber.t.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/GuessSecretNumber.sol";
contract GuessSecretNumberTest is Test {
ExploitContract exploitContract;
GuessTheSecretNumber guessTheSecretNumber;
function setUp() public {
// Deploy "GuessTheSecretNumber" contract and deposit one ether into it
guessTheSecretNumber = (new GuessTheSecretNumber){value: 1 ether}();
// Deploy "ExploitContract"
exploitContract = new ExploitContract();
}
function testFindSecretNumber() public {
uint8 secretNumber = exploitContract.Exploiter();
_checkSolved(secretNumber);
}
function _checkSolved(uint8 _secretNumber) internal {
assertTrue(guessTheSecretNumber.guess{value: 1 ether}(_secretNumber), "Wrong Number");
assertTrue(guessTheSecretNumber.isComplete(), "Challenge Incomplete");
}
receive() external payable {}
}