Puzzle #7
Mini Mutant
Author
0x230d31eec85f4063a405b0f95bde509c0d0a8b5d
brocke.eth
SoliditySolidity's logo.Puzzle
Curtacallsverify()
1
// SPDX-License-Identifier: UNLICENSED
2
pragma solidity ^0.8.17;
3
4
import "memmove/Mapping.sol";
5
import "solvm/Evm.sol";
6
7
struct Solution {
8
uint8 stop;
9
uint8 ctxSetting;
10
bytes code;
11
}
12
13
contract MiniMutant is IPuzzle {
14
using EvmLib for Evm;
15
using MappingLib for Mapping;
16
17
function name() external pure returns (string memory) {
18
return "Mini Mutant";
19
}
20
21
function generate(address _seed) external returns (uint256) {
22
return uint256(keccak256(abi.encodePacked(_seed)));
23
}
24
25
function verify(uint256 _start, uint256 _solution) external returns (bool) {
26
27
Solution memory sol = Solution({
28
stop: 0,
29
ctxSetting: 0,
30
code: abi.encodePacked(_solution)
31
});
32
33
address origin = tx.origin;
34
address caller = msg.sender;
35
address execution_address = address(this);
36
bytes memory calld = abi.encodePacked(_start);
37
38
while (sol.stop == 0) {
39
if (sol.ctxSetting == 0) {
40
origin = tx.origin;
41
caller = msg.sender;
42
execution_address = address(this);
43
} else if (sol.ctxSetting == 1) {
44
origin = msg.sender;
45
caller = address(this);
46
}
47
48
(bool succ, bytes memory ret) = this.run(origin, caller, execution_address, calld, sol.code);
49
require(succ, "Script failed");
50
require(ret.length > 2, "Bad return length");
51
sol.stop = uint8(ret[0]);
52
sol.ctxSetting = uint8(ret[1]);
53
sol.code = slice(ret, 2, ret.length - 2);
54
}
55
56
(bool succ, bytes memory ret) = this.run(origin, caller, execution_address, calld, sol.code);
57
require(succ, "Script failed");
58
uint256 result = abi.decode(ret, (uint256));
59
require(result == _start, "Fail");
60
return true;
61
}
62
63
function run(
64
address origin,
65
address caller,
66
address execution_address,
67
bytes calldata calld,
68
bytes calldata executionCode
69
) external returns (bool, bytes memory) {
70
uint256 fee;
71
uint256 id;
72
assembly("memory-safe") {
73
fee := basefee()
74
id := chainid()
75
}
76
EvmContext memory ctx = EvmContext({
77
origin: origin,
78
caller: caller,
79
execution_address: execution_address,
80
callvalue: 0,
81
coinbase: block.coinbase,
82
timestamp: block.timestamp,
83
number: block.number,
84
gaslimit: block.gaslimit,
85
difficulty: block.difficulty,
86
chainid: id,
87
basefee: fee,
88
balances: MappingLib.newMapping(1),
89
calld: calld
90
});
91
Evm evm = EvmLib.newEvm(ctx);
92
93
// NOTE: No frame switching opcodes are supported (i.e. Call, Create, etc.)
94
return evm.evaluate(executionCode);
95
}
96
97
function slice(
98
bytes memory _bytes,
99
uint256 _start,
100
uint256 _length
101
)
102
internal
103
pure
104
returns (bytes memory)
105
{
106
require(_length + 31 >= _length, "slice_overflow");
107
require(_bytes.length >= _start + _length, "slice_outOfBounds");
108
109
bytes memory tempBytes;
110
111
assembly {
112
switch iszero(_length)
113
case 0 {
114
// Get a location of some free memory and store it in tempBytes as
115
// Solidity does for memory variables.
116
tempBytes := mload(0x40)
117
118
// The first word of the slice result is potentially a partial
119
// word read from the original array. To read it, we calculate
120
// the length of that partial word and start copying that many
121
// bytes into the array. The first word we copy will start with
122
// data we don't care about, but the last `lengthmod` bytes will
123
// land at the beginning of the contents of the new array. When
124
// we're done copying, we overwrite the full first word with
125
// the actual length of the slice.
126
let lengthmod := and(_length, 31)
127
128
// The multiplication in the next line is necessary
129
// because when slicing multiples of 32 bytes (lengthmod == 0)
130
// the following copy loop was copying the origin's length
131
// and then ending prematurely not copying everything it should.
132
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
133
let end := add(mc, _length)
134
135
for {
136
// The multiplication in the next line has the same exact purpose
137
// as the one above.
138
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
139
} lt(mc, end) {
140
mc := add(mc, 0x20)
141
cc := add(cc, 0x20)
142
} {
143
mstore(mc, mload(cc))
144
}
145
146
mstore(tempBytes, _length)
147
148
//update free-memory pointer
149
//allocating the array padded to 32 bytes like the compiler does now
150
mstore(0x40, and(add(mc, 31), not(31)))
151
}
152
//if we want a zero-length slice let's just return a zero-length array
153
default {
154
tempBytes := mload(0x40)
155
//zero out the 32 bytes slice we are about to return
156
//we need to do it because Solidity does not garbage collect
157
mstore(tempBytes, 0)
158
159
mstore(0x40, add(tempBytes, 0x20))
160
}
161
}
162
163
return tempBytes;
164
}
165
}
166
167
interface IPuzzle {
168
/// @notice Returns the puzzle's name.
169
/// @return The puzzle's name.
170
function name() external pure returns (string memory);
171
172
/// @notice Generates the puzzle's starting position based on a seed.
173
/// @dev The seed is intended to be `msg.sender` of some wrapper function or
174
/// call.
175
/// @param _seed The seed to use to generate the puzzle.
176
/// @return The puzzle's starting position.
177
function generate(address _seed) external returns (uint256);
178
179
/// @notice Verifies that a solution is valid for the puzzle.
180
/// @dev `_start` is intended to be an output from {IPuzzle-generate}.
181
/// @param _start The puzzle's starting position.
182
/// @param _solution The solution to the puzzle.
183
/// @return Whether the solution is valid.
184
function verify(uint256 _start, uint256 _solution) external returns (bool);
185
}
186
First Blood
chainlight.io
00:19:24
50
Time Left

Solve locally (WIP)

  1. Clone GitHub repo + install deps
git clone https://github.com/waterfall-mkt/curta-puzzles.git && cd curta-puzzles && forge install
  1. Set RPC_URL_MAINNET in .env
.env
RPC_URL_MAINNET=""
  1. Write solution + run script
forge script <PATH_TO_PUZZLE> -f mainnet -vvv
This is still WIP.
Waterfall