-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathminer.cpp
270 lines (245 loc) · 8.31 KB
/
miner.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// Aquachain CPU Miner
// Copyright (C) 2020 aerth <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <aquahash.h> // for argon2_context, arg...
#include <gmp.h> // for mpz_init, mpz_cmp
#include <stdint.h> // for uint8_t, uint32_t
#include <stdio.h> // for printf
#include <stdlib.h> // for malloc, exit, EXIT_...
#include <chrono> // for milliseconds
#include <cstring> // for memcpy
#include <random> // for mt19937_64, random_...
#include <thread> // for thread, sleep_for
//#include <utility> // for move
#include <vector> // for vector
#include "aqua.hpp" // for mpz_fromBytesNoInit
#include "miner.hpp" // for Miner
#include "spdlog/common.h" // for debug
#include "spdlog/logger.h" // for logger
#include "spdlog/sinks/ansicolor_sink-inl.h" // for ansicolor_sink::pri...
#include "spdlog/sinks/stdout_color_sinks-inl.h" // for stderr_color_mt
#ifdef DEBUG
#include <iostream>
#include <string>
#endif
#ifdef SCHEDPOL
#include <sched.h>
//#include <sys/sysctl.h>
#include <sys/types.h>
#endif
using std::move;
using std::thread;
using std::vector;
int aquahash_version(void *output, const void *input, uint32_t mem) {
argon2_context context;
context.out = static_cast<uint8_t *>(output);
context.outlen = static_cast<uint32_t>(HASH_LEN);
context.pwd = const_cast<uint8_t *>(static_cast<const uint8_t *>(input));
context.pwdlen = static_cast<uint32_t>(HASH_INPUT_LEN);
context.salt = nullptr;
context.saltlen = 0;
context.secret = nullptr;
context.secretlen = 0;
context.ad = nullptr;
context.adlen = 0;
context.allocate_cbk = nullptr;
context.free_cbk = nullptr;
context.flags = ARGON2_DEFAULT_FLAGS;
context.m_cost = mem;
context.lanes = 1;
context.threads = 1;
context.t_cost = 1;
context.version = ARGON2_VERSION_13;
return argon2_ctx(&context, Argon2_id);
}
#define handle_error_en(en, msg) \
do { \
errno = en; \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
#ifdef AFFINE
static void affine_to_cpu_mask(int id, unsigned long mask, int num_cpus) {
cpu_set_t set;
CPU_ZERO(&set);
for (uint8_t i = 0; i < num_cpus; i++) {
// cpu mask
if (mask & (1UL << i)) {
CPU_SET(i, &set);
}
}
if (id == -1) {
// process affinity
sched_setaffinity(0, sizeof(&set), &set);
} else {
// thread only
// pthread_setaffinity_np(, sizeof(&set), &set);
}
}
#endif
void Miner::minerThread(uint8_t thread_id) {
static const bool verbose = this->verbose;
logger->debug("thread {} started\n", thread_id);
#ifdef SCHEDPOL
sched_param sch;
const sched_param wantsch = {20};
int policy;
int s;
s = pthread_getschedparam(pthread_self(), &policy, &sch);
if (s != 0) {
logger->error("Can't get thread priority!");
handle_error_en(s, "pthread_attr_getschedpolicy #1");
}
logger->info("Thread {} was at priority {} ({})", thread_id,
sch.sched_priority, policy);
s = pthread_setschedparam(pthread_self(), SCHED_RR, &wantsch);
if (s != 0) {
logger->error("Can't set thread priority!");
handle_error_en(s, "pthread_attr_setschedpolicy");
}
s = pthread_getschedparam(pthread_self(), &policy, &sch);
if (s != 0) {
handle_error_en(s, "pthread_attr_getschedpolicy #2");
}
logger->info("Thread {} is now at priority {} ({})", thread_id,
sch.sched_priority, policy);
#endif
logger->info("Binding thread {} to cpu {} (mask {})", thread_id,
thread_id % num_cpus, (1 << ((thread_id - 1) % num_cpus)));
#ifdef AFFINE
affine_to_cpu_mask(thread_id, 1UL << ((thread_id - 1) % num_cpus), num_cpus);
#endif
// create new WorkPacket to store work variables
WorkPacket *work = new WorkPacket();
// random nonce
std::random_device engine;
std::mt19937_64 prng;
uint64_t n = std::random_device{}();
prng.seed(n);
memcpy(&work->buf[32], &n, 4);
prng.seed(n);
memcpy(&work->buf[36], &n, 4);
// initialize variables
mpz_t mpz_result;
mpz_init(mpz_result);
uint64_t nonce_int = 0;
uint64_t tries = 0;
uint64_t triesHashes = 0;
// so all the threads dont report at the same time
uint64_t reportTriesMod = static_cast<uint64_t>(thread_id + (1 * 1000));
// starting nonce
memcpy(&nonce_int, &work->buf[32], 8);
logger->info("Thread {} starting nonce: {}", thread_id, nonce_int);
memcpy(&work->buf[32], &nonce_int, 8);
// miner loop
while (true) {
// roll nonce
memcpy(&nonce_int, &work->buf[32], 8);
nonce_int++;
memcpy(&work->buf[32], &nonce_int, 8);
#ifdef NONCEDEBUG
printf("NEWNONCE:");
print_hex(&work->buf[32], 8);
#endif
if (tries % 100000 == 0) {
// see if we got new work
if (!this->getCurrentWork(work, thread_id)) {
logger->info("getCurrentWork failed");
logger->debug("getCurrentWork({}, {})...", work->inputStr, thread_id);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
continue;
}
tries = 0;
}
// report hashrate every 10k hashes (per thread)
if (triesHashes % 20000 == reportTriesMod) {
this->numTries += triesHashes;
triesHashes = 0;
}
tries++;
// Aquahash Version Switch (See Aquachain HF)
//
// TODO: move this to aquahash_version()
uint32_t mem = 1;
if (work->version == '2') {
} else if (work->version == '3') {
mem = 16;
} else if (work->version == '4') {
mem = 32;
} else if (work->version == 0 || work->version == '0') {
printf("thread %d going to sleep for 1 sec (no work yet)\n", thread_id);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
continue;
} else if (benching && work->version == '!') {
break;
} else {
printf("thread %d going to sleep for 1 sec (no work: '%c')\n", thread_id,
work->version);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
continue;
}
// hash it
//
// TODO: accept work->version char as 3rd arg
if (ARGON2_OK != aquahash_version(work->output, work->buf, mem)) {
printf("argon2 failed\n");
exit(111);
}
triesHashes++;
// TODO: memcmp
mpz_fromBytesNoInit(work->output, HASH_LEN, mpz_result);
if (mpz_cmp(mpz_result, work->target) > 0) {
// reloop
continue;
}
#ifdef DEBUG
printf("thread %d mining version %c (input=%s)\n", thread_id, work->version,
work->inputStr);
printf("input from thread %d: ", thread_id);
print_hex(work->buf, 40);
printf("\n");
printf("output from thread %d: ", thread_id);
print_hex(work->output, 32);
printf("\n");
printf("nonce from thread %d:", thread_id);
print_hex(&work->buf[32], 8);
printf("\n");
printf("diff target from thread %d:", thread_id);
std::string diff = mpzToString(work->difficulty);
std::cout << diff << std::endl;
#endif
logger->info("thread {} found new solution", thread_id);
// std::thread(submitwork, work, poolUrl).detach();
CURL *tmpsubmitcurl = curl_easy_init();
this->initcurl(tmpsubmitcurl, 2);
bool poolret = submitwork(work, poolUrl, verbose, tmpsubmitcurl);
curl_easy_cleanup(tmpsubmitcurl);
if (!poolret) {
logger->warn("submitwork failed, this miner thread sleeping 2s");
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
continue;
}
if (solomining) {
work->version = 0; // pauses
logger->info(
"mined a block. sleeping 1 second for getwork thread to catch up");
continue;
}
}
// std::this_thread::sleep_for(std::chrono::milliseconds(60));
// if invalid diff, increase nonce
// roll nonce
// (*(uint64_t *)&work->buf[32])++;
}