├── LICENSE ├── README.md ├── bench.sh ├── build.sh └── main.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vectorized 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Function Selector Miner 2 | 3 | Simple and fast Solidity function selector miner (CPU based). 4 | 5 | Uses AVX2 instructions and multithreading to compute hashes in parallel. 6 | 7 | Should be able to mine most selectors under a minute with a core i7-12700F. 8 | 9 | ## Requirements 10 | 11 | C++ compiler with OpenMP support. 12 | 13 | ## Building 14 | 15 | ``` 16 | g++ main.cpp -O3 -march=native -DNDEBUG -fopenmp -o function_selector_miner 17 | ``` 18 | 19 | ## Running 20 | 21 | ``` 22 | ./function_selector_miner 23 | ``` 24 | 25 | For example: 26 | 27 | ``` 28 | ./function_selector_miner "someFunction" "(uint256,address)" "0x12345678" 29 | ``` 30 | -------------------------------------------------------------------------------- /bench.sh: -------------------------------------------------------------------------------- 1 | time ./function_selector_miner "someFunction" "(uint256,address)" "0x12345678" -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | g++ main.cpp -O3 -march=native -DNDEBUG -fopenmp -o function_selector_miner; -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | inline char *writeDecimal(char *out, uint64_t x) 11 | { 12 | char buff[64], *end = buff + 32, *p = end; 13 | do { *(--p) = (x % 10) + 48; } while (x /= 10); 14 | memcpy(out, p, 32); 15 | return out + (end - p); 16 | } 17 | 18 | inline std::string functionSelectorToHex(uint32_t x) 19 | { 20 | std::stringstream ss; 21 | ss << "0x" << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << x; 22 | return ss.str(); 23 | } 24 | 25 | #if defined (__AVX2__) 26 | #include 27 | 28 | struct V 29 | { 30 | __m256i v; 31 | inline V() {} 32 | inline V(const __m256i &v): v(v) {} 33 | inline V(uint64_t v0, uint64_t v1, uint64_t v2, uint64_t v3) 34 | { 35 | v = _mm256_set_epi64x(v3, v2, v1, v0); 36 | } 37 | }; 38 | 39 | inline V operator ^ (const V &a, const V &b) 40 | { 41 | return V(_mm256_xor_si256(a.v, b.v)); 42 | } 43 | 44 | inline V operator ^ (const V &a, uint64_t b) 45 | { 46 | return V(_mm256_xor_si256(a.v, _mm256_set1_epi64x(b))); 47 | } 48 | 49 | inline V operator | (const V &a, const V &b) 50 | { 51 | return V(_mm256_or_si256(a.v, b.v)); 52 | } 53 | 54 | inline V operator << (const V &a, uint64_t i) 55 | { 56 | return V(_mm256_sll_epi64(a.v, _mm_set1_epi64x(i))); 57 | } 58 | 59 | inline V operator >> (const V &a, uint64_t i) 60 | { 61 | return V(_mm256_srl_epi64(a.v, _mm_set1_epi64x(i))); 62 | } 63 | 64 | inline V operator & (const V &a, const V &b) 65 | { 66 | return V(_mm256_and_si256(a.v, b.v)); 67 | } 68 | 69 | inline V operator ~ (const V &a) 70 | { 71 | return V(_mm256_xor_si256(a.v, _mm256_set1_epi64x(0xffffffffffffffffull))); 72 | } 73 | #endif 74 | 75 | #define ROL(X, S) (((X) << S) | ((X) >> (64 - S))) 76 | 77 | #if defined (__AVX512F__) && defined (__AVX512VL__) 78 | #undef ROL 79 | #define ROL(X, S) V(_mm256_rol_epi64((X).v, S)) 80 | #endif 81 | 82 | #define THETA_(M, N, O) t = b[M] ^ ROL(b[N], 1); \ 83 | a[O + 0] = a[O + 0] ^ t; a[O + 5] = a[O + 5] ^ t; a[O + 10] = a[O + 10] ^ t; \ 84 | a[O + 15] = a[O + 15] ^ t; a[O + 20] = a[O + 20] ^ t; 85 | 86 | #define THETA() \ 87 | b[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; \ 88 | b[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; \ 89 | b[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; \ 90 | b[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; \ 91 | b[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; \ 92 | THETA_(4, 1, 0); THETA_(0, 2, 1); THETA_(1, 3, 2); THETA_(2, 4, 3); THETA_(3, 0, 4); 93 | 94 | #define RHO_PI_(M, N) t = b[0]; b[0] = a[M]; a[M] = ROL(t, N); 95 | 96 | #define RHO_PI() t = a[1]; b[0] = a[10]; a[10] = ROL(t, 1); \ 97 | RHO_PI_(7, 3); RHO_PI_(11, 6); RHO_PI_(17, 10); RHO_PI_(18, 15); RHO_PI_(3, 21); RHO_PI_(5, 28); \ 98 | RHO_PI_(16, 36); RHO_PI_(8, 45); RHO_PI_(21, 55); RHO_PI_(24, 2); RHO_PI_(4, 14); RHO_PI_(15, 27); \ 99 | RHO_PI_(23, 41); RHO_PI_(19, 56); RHO_PI_(13, 8); RHO_PI_(12, 25); RHO_PI_(2, 43); RHO_PI_(20, 62); \ 100 | RHO_PI_(14, 18); RHO_PI_(22, 39); RHO_PI_(9, 61); RHO_PI_(6, 20); RHO_PI_(1, 44); 101 | 102 | #define CHI_(N) \ 103 | b[0] = a[N + 0]; b[1] = a[N + 1]; b[2] = a[N + 2]; b[3] = a[N + 3]; b[4] = a[N + 4]; \ 104 | a[N + 0] = b[0] ^ ((~b[1]) & b[2]); \ 105 | a[N + 1] = b[1] ^ ((~b[2]) & b[3]); \ 106 | a[N + 2] = b[2] ^ ((~b[3]) & b[4]); \ 107 | a[N + 3] = b[3] ^ ((~b[4]) & b[0]); \ 108 | a[N + 4] = b[4] ^ ((~b[0]) & b[1]); 109 | 110 | #define CHI() CHI_(0); CHI_(5); CHI_(10); CHI_(15); CHI_(20); 111 | 112 | #define IOTA(X) a[0] = a[0] ^ X; 113 | 114 | #define ITER(X) THETA(); RHO_PI(); CHI(); IOTA(X); 115 | 116 | #define ITERS() \ 117 | ITER(0x0000000000000001); ITER(0x0000000000008082); \ 118 | ITER(0x800000000000808a); ITER(0x8000000080008000); \ 119 | ITER(0x000000000000808b); ITER(0x0000000080000001); \ 120 | ITER(0x8000000080008081); ITER(0x8000000000008009); \ 121 | ITER(0x000000000000008a); ITER(0x0000000000000088); \ 122 | ITER(0x0000000080008009); ITER(0x000000008000000a); \ 123 | ITER(0x000000008000808b); ITER(0x800000000000008b); \ 124 | ITER(0x8000000000008089); ITER(0x8000000000008003); \ 125 | ITER(0x8000000000008002); ITER(0x8000000000000080); \ 126 | ITER(0x000000000000800a); ITER(0x800000008000000a); \ 127 | ITER(0x8000000080008081); ITER(0x8000000000008080); \ 128 | ITER(0x0000000080000001); ITER(0x8000000080008008); 129 | 130 | #if defined (__AVX2__) 131 | #define COMPUTE_SELECTORS(C0, C1, C2, C3, SPONGE) \ 132 | { \ 133 | V *a = SPONGE, b[5], t; \ 134 | ITERS() \ 135 | memcpy(&C0, ((uint64_t *)&(a[0].v)) + 0, 4); \ 136 | memcpy(&C1, ((uint64_t *)&(a[0].v)) + 1, 4); \ 137 | memcpy(&C2, ((uint64_t *)&(a[0].v)) + 2, 4); \ 138 | memcpy(&C3, ((uint64_t *)&(a[0].v)) + 3, 4); \ 139 | } 140 | #else 141 | #define COMPUTE_SELECTORS(C, SPONGE) \ 142 | { \ 143 | uint64_t *a = SPONGE, b[5], t; \ 144 | ITERS() \ 145 | memcpy(&C, a, 4); \ 146 | } 147 | #endif 148 | 149 | inline uint32_t normalizeEndianess(uint32_t x) 150 | { 151 | union { 152 | uint32_t i; 153 | char c[4]; 154 | } bint = {0x01020304}; 155 | if (bint.c[0] == 1) return x; 156 | x = ((x >> 8) & 0x00FF00FF) | ((x & 0x00FF00FF) << 8); 157 | return (x >> 16) | (x << 16); 158 | } 159 | 160 | struct SmallString 161 | { 162 | char data[128]; 163 | uint64_t length; 164 | 165 | inline SmallString(const char *s) 166 | { 167 | length = strlen(s); 168 | if (length < 128) strcpy(data, s); 169 | } 170 | }; 171 | 172 | inline char *fillSponge(char *sponge, const SmallString &s) 173 | { 174 | memcpy(sponge, s.data, s.length); 175 | return sponge + s.length; 176 | } 177 | 178 | inline char *fillSponge(char *sponge, const SmallString &functionName, uint64_t nonce, const SmallString &functionParams) 179 | { 180 | char *o = sponge; 181 | o = fillSponge(o, functionName); 182 | o = writeDecimal(o, nonce); 183 | o = fillSponge(o, functionParams); 184 | const char *end = sponge + 200; 185 | for (char *c = o; c < end; ++c) *c = 0; 186 | sponge[135] = 0x80u; 187 | return o; 188 | } 189 | 190 | int main(int argc, char * argv[]) 191 | { 192 | if (argc < 4) { 193 | std::cout << "Usage: \n" << std::flush; 194 | return -1; 195 | } 196 | 197 | const uint32_t selector = normalizeEndianess(std::stol(argv[3], nullptr, 16)); 198 | const SmallString functionName(argv[1]); 199 | const SmallString functionParams(argv[2]); 200 | 201 | if (functionName.length + functionParams.length >= 115) { 202 | std::cout << "Total length of and must be under 115 bytes."; 203 | return -1; 204 | } 205 | 206 | if (sizeof(char) != 1 || sizeof(uint64_t) != 8) { 207 | std::cout << "Incompatible architecture\n"; 208 | return -1; 209 | } 210 | 211 | std::cout << "Function name: " << argv[1] << "\n"; 212 | std::cout << "Function params: " << argv[2] << "\n"; 213 | std::cout << "Target selector: " << functionSelectorToHex(normalizeEndianess(selector)) << "\n"; 214 | 215 | bool go = true; 216 | 217 | const uint64_t numThreads = omp_get_max_threads(); 218 | const uint64_t end = 0xfffffffff0000000ull; 219 | 220 | std::cout << "Starting mining with " << numThreads << " threads...\n"; 221 | 222 | union Sponge { 223 | uint64_t uint64s[25]; 224 | char chars[200]; 225 | }; 226 | 227 | #pragma omp parallel for 228 | for (uint64_t t = 0; t < numThreads; ++t) { 229 | #if defined (__AVX2__) 230 | #define STEP 4 231 | #else 232 | #define STEP 1 233 | #endif 234 | uint64_t i = 0; 235 | for (uint64_t nonce = t * STEP; nonce < end && go; nonce += numThreads * STEP) { 236 | #define CHECK_SELECTOR(I) \ 237 | if (c##I == selector) { \ 238 | *fillSponge(s##I.chars, functionName, nonce + I, functionParams) = 0x00u; \ 239 | std::cout << "Function found: " << s##I.chars << "\n"; \ 240 | go = false; \ 241 | } 242 | 243 | #if defined (__AVX2__) 244 | Sponge s0, s1, s2, s3; 245 | *fillSponge(s0.chars, functionName, nonce + 0, functionParams) = 0x01u; 246 | *fillSponge(s1.chars, functionName, nonce + 1, functionParams) = 0x01u; 247 | *fillSponge(s2.chars, functionName, nonce + 2, functionParams) = 0x01u; 248 | *fillSponge(s3.chars, functionName, nonce + 3, functionParams) = 0x01u; 249 | V sponge[25]; 250 | #define SET_SPONGE_(I) sponge[I] = V(s0.uint64s[I], s1.uint64s[I], s2.uint64s[I], s3.uint64s[I]); 251 | #define SET_SPONGE(I) SET_SPONGE_(I + 0) SET_SPONGE_(I + 1) SET_SPONGE_(I + 2) SET_SPONGE_(I + 3) SET_SPONGE_(I + 4) 252 | SET_SPONGE(0) SET_SPONGE(5) SET_SPONGE(10) SET_SPONGE(15) SET_SPONGE(20) 253 | uint32_t c0, c1, c2, c3; 254 | COMPUTE_SELECTORS(c0, c1, c2, c3, sponge); 255 | CHECK_SELECTOR(0) CHECK_SELECTOR(1) CHECK_SELECTOR(2) CHECK_SELECTOR(3) 256 | #else 257 | Sponge s0; 258 | *fillSponge(s0.chars, functionName, nonce, functionParams) = 0x01u; 259 | uint32_t c0; 260 | COMPUTE_SELECTORS(c0, s0.uint64s); 261 | CHECK_SELECTOR(0) 262 | #endif 263 | if (t == 0) if ((++i & 0x3fffff) == 0) std::cout << nonce << " hashes done.\n"; 264 | } 265 | } 266 | return 0; 267 | } 268 | --------------------------------------------------------------------------------