Puzzle #2
AddressGame
Author
0xb49bf876be26435b6fae1ef42c3c82c5867fa149
chainlight.io

Write-up for AddressGame

Overview

This challenge was inspired by The Password Game.

For this challenge, you need to generate a vanity contract address that satisfies:

  • The address contains box(seed, 0) % 2 vowel hexadecimal digits (0xA and 0xE).
  • The address contains box(seed, 1) % 3 consonant hexadecimal digits (0xB, 0xC, 0xD, and 0xF).
  • All number digits in the address ([0x0, 0x9]) sum to 25 + seed % 50.

Solving the puzzle

To generate the vanity address, you can write your own miner that satisfies the conditions above.

For example, we made the following changes to profanity2:

profanity2.patch
1
diff --git a/profanity.cl b/profanity.cl
2
index fe187fd..ddd865c 100644
3
--- a/profanity.cl
4
+++ b/profanity.cl
5
@@ -768,18 +768,35 @@ __kernel void profanity_score_range(__global mp_number * const pInverse, __globa
6
__global const uchar * const hash = pInverse[id].d;
7
int score = 0;
8
9
+ int c[16];
10
+ for (int i = 0; i < 16; i++) {
11
+ c[i] = 0;
12
+ }
13
+
14
+ score = 40;
15
+
16
for (int i = 0; i < 20; ++i) {
17
const uchar first = (hash[i] & 0xF0) >> 4;
18
const uchar second = (hash[i] & 0x0F);
19
+ c[first]++;
20
+ c[second]++;
21
+ }
22
23
- if (first >= data1[0] && first <= data2[0]) {
24
- ++score;
25
+ {
26
+ if (((c[0xa] + c[0xe]) & 1) != 0) score = 0;
27
+ if (((c[0xb] + c[0xc] + c[0xd] + c[0xf]) % 3) != 0) score = 0;
28
}
29
30
- if (second >= data1[0] && second <= data2[0]) {
31
- ++score;
32
+ {
33
+ int _sum = 0;
34
+ for (int i = 1; i < 10; i++) {
35
+ _sum += c[i] * i;
36
}
37
+ if (_sum != 25 + 35) {
38
+ score = 0;
39
}
40
+ }
41
+

While box returns uint256, you can mine for uint64 instead since the modulo fits within 64 bits: log215203777108537674021<64\log_{2}15203777108537674021<64.

Solve script

Check out our solve test below for more details.

The solution below is for our address.

SoliditySolidity's logo.Solve.t.sol
1
// SPDX-License-Identifier: UNLICENSED
2
pragma solidity ^0.8.13;
3
4
import {Test, console2} from "forge-std/Test.sol";
5
import {IBox, AddressGame} from "../src/Challenge.sol";
6
7
contract MyBox is IBox {
8
function isSolved() external view returns (bool) {
9
return true;
10
}
11
}
12
13
contract ChallengeTest is Test {
14
AddressGame public challenge;
15
16
function setUp() public {
17
challenge = new AddressGame();
18
}
19
20
function test_Solve() public {
21
// Solution: see profanity2.patch and https://github.com/1inch/profanity2
22
address ATTACKER = 0xB49bf876BE26435b6fae1Ef42C3c82c5867Fa149;
23
address DEPLOYER = vm.createWallet(0x000039ecc4b345efae348b37e89fef3678e6ed97715a2a3b053be81f528bdad2).addr;
24
25
uint256 seed = challenge.generate(ATTACKER);
26
console2.log(seed, challenge.box(seed, 0) % 2, challenge.box(seed, 1) % 3);
27
28
vm.startPrank(DEPLOYER);
29
uint256 solution = uint256(uint160(address(new MyBox())));
30
assertEq(challenge.verify(seed, solution), true);
31
}
32
}
Waterfall