Skip to content

Commit 51a9460

Browse files
committed
Use async verifier for heavy share hashes
1 parent 0cbf8c8 commit 51a9460

6 files changed

Lines changed: 292 additions & 68 deletions

File tree

lib/coins/core/factories.js

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,31 @@ const pow = {
795795
const hashes = ctx.runtime.powHash.kawpow_light(ctx.convertedBlob, Buffer.from(ctx.nonce, "hex"), ctx.blockTemplate.height);
796796
return hashes[1].equals(Buffer.from(ctx.mixhash, "hex")) ? hashes[0] : false;
797797
}),
798-
ethash: createHashPowFactory(null, function hashBuff(ctx) {
798+
ethash: createHashPowFactory({
799+
verifyInput(ctx) {
800+
return buildVerifyInput(ctx.algo, ctx.convertedBlob, {
801+
height: ctx.blockTemplate.height,
802+
nonce: ctx.nonce
803+
});
804+
}
805+
}, function hashBuff(ctx) {
799806
return ctx.runtime.powHash.ethash(ctx.convertedBlob, Buffer.from(ctx.nonce, "hex"), ctx.blockTemplate.height);
800807
}),
801-
etchash: createHashPowFactory(null, function hashBuff(ctx) {
808+
etchash: createHashPowFactory({
809+
verifyInput(ctx) {
810+
return buildVerifyInput(ctx.algo, ctx.convertedBlob, {
811+
height: ctx.blockTemplate.height,
812+
nonce: ctx.nonce
813+
});
814+
}
815+
}, function hashBuff(ctx) {
802816
return ctx.runtime.powHash.etchash(ctx.convertedBlob, Buffer.from(ctx.nonce, "hex"), ctx.blockTemplate.height);
803817
}),
804-
autolykos2: createHashPowFactory(null, function hashBuff(ctx) {
818+
autolykos2: createHashPowFactory({
819+
verifyInput(ctx) {
820+
return buildVerifyInput(ctx.algo, ctx.convertedBlob, { height: ctx.blockTemplate.height });
821+
}
822+
}, function hashBuff(ctx) {
805823
return ctx.runtime.powHash.autolykos2_hashes(ctx.convertedBlob, ctx.blockTemplate.height);
806824
}),
807825
astrobwt: createHashPowFactory({ variant: 0 }, function hashBuff(ctx) {
@@ -1064,6 +1082,29 @@ function rejectSpecialShare(ctx) {
10641082
return true;
10651083
}
10661084

1085+
function callSlowHashBuffAsync(ctx, convertedBlob, verifyContext, callback) {
1086+
if (typeof ctx.coinFuncs.slowHashBuffAsync === "function") {
1087+
return ctx.coinFuncs.slowHashBuffAsync(convertedBlob, ctx.blockTemplate, ctx.miner && ctx.miner.payout, callback, verifyContext);
1088+
}
1089+
if (typeof ctx.coinFuncs.isHashVerifierEnabled === "function" && ctx.coinFuncs.isHashVerifierEnabled()) {
1090+
return callback(false, "missing-async-hash-helper");
1091+
}
1092+
try {
1093+
return callback(ctx.coinFuncs.slowHashBuff(
1094+
convertedBlob,
1095+
ctx.blockTemplate,
1096+
verifyContext && verifyContext.nonce,
1097+
verifyContext && verifyContext.mixhash
1098+
));
1099+
} catch (_error) {
1100+
return callback(false, "local-hash-error");
1101+
}
1102+
}
1103+
1104+
function validHashBuffers(hashes, count) {
1105+
return hashes instanceof Array && hashes.length >= count && hashes.slice(0, count).every(Buffer.isBuffer);
1106+
}
1107+
10671108
function verifyXtmCShare(ctx) {
10681109
const header = Buffer.concat([ctx.bigIntToBuffer(BigInt(`0x${ctx.params.nonce}`), { endian: "big", size: 8 }), ctx.blockTemplate.buffer]);
10691110
const syntheticResult = typeof ctx.getBlockSubmitTestResultBuffer === "function" ? ctx.getBlockSubmitTestResultBuffer() : null;
@@ -1133,6 +1174,7 @@ function verifyRavenShare(ctx) {
11331174
return true;
11341175
}
11351176
ctx.verifySlowHashWithRetry(convertedBlob, { mixhash: ctx.params.mixhash, nonce: ctx.params.nonce }, function onHash(hash) {
1177+
if (hash === null) return ctx.processShareCB(null);
11361178
if (!hash) return rejectSpecialShare(ctx);
11371179
const resultBuff = Buffer.from(hash, "hex");
11381180
if (!resultBuff.equals(quickResult)) return rejectSpecialShare(ctx);
@@ -1157,9 +1199,11 @@ function verifyEthShare(ctx) {
11571199
);
11581200
return true;
11591201
}
1160-
const hashes = ctx.coinFuncs.slowHashBuff(Buffer.from(ctx.blockTemplate.hash, "hex"), ctx.blockTemplate, ctx.params.nonce);
1161-
const resultBuff = hashes[0];
1162-
ctx.verifyShareCB(ctx.hashEthBuffDiff(resultBuff), resultBuff, ["0x" + ctx.params.nonce, "0x" + ctx.blockTemplate.hash, "0x" + hashes[1].toString("hex")], false, true);
1202+
callSlowHashBuffAsync(ctx, Buffer.from(ctx.blockTemplate.hash, "hex"), { nonce: ctx.params.nonce }, function onEthHash(hashes) {
1203+
if (!validHashBuffers(hashes, 2)) return rejectSpecialShare(ctx);
1204+
const resultBuff = hashes[0];
1205+
ctx.verifyShareCB(ctx.hashEthBuffDiff(resultBuff), resultBuff, ["0x" + ctx.params.nonce, "0x" + ctx.blockTemplate.hash, "0x" + hashes[1].toString("hex")], false, true);
1206+
});
11631207
return true;
11641208
}
11651209

@@ -1170,8 +1214,10 @@ function verifyErgShare(ctx) {
11701214
ctx.verifyShareCB(ctx.hashEthBuffDiff(syntheticResult), syntheticResult, ctx.params.nonce, false, true);
11711215
return true;
11721216
}
1173-
const hashes = ctx.coinFuncs.slowHashBuff(Buffer.concat([Buffer.from(ctx.blockTemplate.hash, "hex"), Buffer.from(ctx.params.nonce, "hex")]), ctx.blockTemplate);
1174-
ctx.verifyShareCB(ctx.hashEthBuffDiff(hashes[1]), null, ctx.params.nonce, false, true);
1217+
callSlowHashBuffAsync(ctx, Buffer.concat([Buffer.from(ctx.blockTemplate.hash, "hex"), Buffer.from(ctx.params.nonce, "hex")]), null, function onErgHash(hashes) {
1218+
if (!validHashBuffers(hashes, 2)) return rejectSpecialShare(ctx);
1219+
ctx.verifyShareCB(ctx.hashEthBuffDiff(hashes[1]), null, ctx.params.nonce, false, true);
1220+
});
11751221
return true;
11761222
}
11771223

lib/coins/index.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,23 @@ let shareVerifyQueueErrorTime = [];
4444
let shareVerifyQueueErrorCount = [];
4545

4646
function hasVerifyShareHosts() {
47-
return global.config.verify_shares_host instanceof Array && global.config.verify_shares_host.length > 0;
47+
return global.config && global.config.verify_shares_host instanceof Array && global.config.verify_shares_host.length > 0;
48+
}
49+
50+
function isHexString(value) {
51+
return typeof value === "string" && value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
52+
}
53+
54+
function verifierResultToBuffer(value) {
55+
if (value === null || value === false) return value;
56+
if (Buffer.isBuffer(value)) return value;
57+
if (!isHexString(value)) return false;
58+
return Buffer.from(value, "hex");
59+
}
60+
61+
function verifierResultToBuffers(value) {
62+
if (Array.isArray(value)) return value.map(verifierResultToBuffer);
63+
return verifierResultToBuffer(value);
4864
}
4965

5066
function decrementMinerAddressVerify(miner_address) {
@@ -647,6 +663,7 @@ function Coin(data) {
647663
return result === false ? false : result.toString("hex");
648664
};
649665

666+
// KAWPOW finalizer is kept local because it is a cheap special-verifier precheck.
650667
this.kawpowQuickHash = function kawpowQuickHash(convertedBlob, nonce, mixhash) {
651668
return powHash.kawpow(convertedBlob, Buffer.from(nonce, "hex"), Buffer.from(mixhash, "hex"));
652669
};
@@ -714,6 +731,31 @@ function Coin(data) {
714731
});
715732
};
716733

734+
this.slowHashBuffAsync = function slowHashBuffAsync(convertedBlob, blockTemplate, miner_address, cb, verifyContext) {
735+
if (typeof miner_address === "function") {
736+
verifyContext = cb;
737+
cb = miner_address;
738+
miner_address = "";
739+
}
740+
const nonce = verifyContext && verifyContext.nonce;
741+
const mixhash = verifyContext && verifyContext.mixhash;
742+
743+
if (!hasVerifyShareHosts()) {
744+
try {
745+
return cb(this.slowHashBuff(convertedBlob, blockTemplate, nonce, mixhash));
746+
} catch (_error) {
747+
return cb(false, "local-hash-error");
748+
}
749+
}
750+
751+
return this.slowHashAsync(convertedBlob, blockTemplate, miner_address || "", function onRemoteHash(hash, errorKind) {
752+
return cb(verifierResultToBuffers(hash), errorKind);
753+
}, verifyContext);
754+
};
755+
756+
this.isHashVerifierEnabled = hasVerifyShareHosts;
757+
758+
// C29 proof helpers are kept local because they are cheap special-verifier checks.
717759
this.c29 = function c29(header, ring, port) {
718760
const profile = resolveProfile(port, undefined, this);
719761
if (!profile || !profile.pow || typeof profile.pow.c29 !== "function") return powHash.c29s(header, ring);

lib/pool/share_blocks.js

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -95,37 +95,53 @@ module.exports = function createShareBlockHelpers(deps) {
9595
return rows.map((row) => "\n" + row.label.padEnd(labelWidth) + ": " + formatDiff(row.value).padStart(valueWidth)).join("");
9696
}
9797

98-
function getLocalBlockCheck(blockTemplate, blockData, resultBuff, job) {
99-
if (!Buffer.isBuffer(blockData)) return null;
98+
function getLocalBlockCheck(blockTemplate, blockData, resultBuff, job, miner, callback) {
99+
if (!Buffer.isBuffer(blockData)) return callback(null);
100+
let convertedBlob;
100101
try {
101-
const convertedBlob = global.coinFuncs.convertBlob(blockData, blockTemplate.port);
102-
const buff = convertedBlob ? global.coinFuncs.slowHashBuff(convertedBlob, blockTemplate) : null;
103-
if (!Buffer.isBuffer(buff)) return null;
102+
convertedBlob = global.coinFuncs.convertBlob(blockData, blockTemplate.port);
103+
} catch (_error) {
104+
return callback(null);
105+
}
106+
if (!convertedBlob) return callback(null);
107+
108+
function finish(buff) {
109+
if (!Buffer.isBuffer(buff)) return callback(null);
104110
let diff = hashBuffDiff(buff);
105111
if (job && (job.coin === "ETH" || job.coin === "ETC" || job.coin === "ERG")) diff = hashEthBuffDiff(buff);
106112
else if (job && (job.coin === "RVN" || job.coin === "XNA")) diff = hashRavenBuffDiff(buff);
107-
return { diff, matchesSubmit: !Buffer.isBuffer(resultBuff) || buff.equals(resultBuff) };
113+
return callback({ diff, matchesSubmit: !Buffer.isBuffer(resultBuff) || buff.equals(resultBuff) });
114+
}
115+
116+
if (typeof global.coinFuncs.slowHashBuffAsync === "function") {
117+
return global.coinFuncs.slowHashBuffAsync(convertedBlob, blockTemplate, miner && miner.payout, finish);
118+
}
119+
if (typeof global.coinFuncs.isHashVerifierEnabled === "function" && global.coinFuncs.isHashVerifierEnabled()) return callback(null);
120+
121+
try {
122+
return finish(global.coinFuncs.slowHashBuff(convertedBlob, blockTemplate));
108123
} catch (_error) {
109-
return null;
124+
return callback(null);
110125
}
111126
}
112127

113-
function getBlockSubmitDiffMessage(blockTemplate, blockData, resultBuff, hashDiff, requiredDiff, job) {
114-
const localCheck = getLocalBlockCheck(blockTemplate, blockData, resultBuff, job);
128+
function getBlockSubmitDiffMessage(blockTemplate, blockData, resultBuff, hashDiff, requiredDiff, job, miner, callback) {
115129
const diffRows = [
116130
{ label: "Submitted share difficulty", value: hashDiff },
117131
{ label: "Required block difficulty", value: requiredDiff }
118132
];
119-
if (localCheck !== null) {
120-
const localDiff = localCheck.diff;
121-
diffRows.push({ label: "Locally verified difficulty", value: localDiff });
122-
let message = formatDiffRows(diffRows);
123-
if (!localCheck.matchesSubmit || (requiredDiff !== undefined && requiredDiff !== null && requiredDiff !== "" && !ge(localDiff, requiredDiff))) {
124-
message += "\nLocal check says this was not block level; no action is needed for this submit failure.";
133+
return getLocalBlockCheck(blockTemplate, blockData, resultBuff, job, miner, function onLocalCheck(localCheck) {
134+
if (localCheck !== null) {
135+
const localDiff = localCheck.diff;
136+
diffRows.push({ label: "Locally verified difficulty", value: localDiff });
137+
let message = formatDiffRows(diffRows);
138+
if (!localCheck.matchesSubmit || (requiredDiff !== undefined && requiredDiff !== null && requiredDiff !== "" && !ge(localDiff, requiredDiff))) {
139+
message += "\nLocal check says this was not block level; no action is needed for this submit failure.";
140+
}
141+
return callback(message);
125142
}
126-
return message;
127-
}
128-
return formatDiffRows(diffRows);
143+
return callback(formatDiffRows(diffRows));
144+
});
129145
}
130146

131147
function submitBlock(miner, job, blockTemplate, blockData, resultBuff, hashDiff, isTrustedShare, isParentBlock, portUsedToSubmit, submitBlockCB, submitParams, submitRetryCount) {
@@ -153,11 +169,30 @@ module.exports = function createShareBlockHelpers(deps) {
153169
}
154170
}
155171

156-
function shouldNotifySubmitFailure() {
157-
if (!(isParentBlock && isTrustedShare && !shouldSuppressBlockSubmitFailureEmail())) return true;
158-
const convertedBlob = global.coinFuncs.convertBlob(blockData, blockTemplate.port);
159-
const buff = global.coinFuncs.slowHashBuff(convertedBlob, blockTemplate);
160-
return Buffer.isBuffer(buff) && Buffer.isBuffer(resultBuff) && buff.equals(resultBuff);
172+
function shouldNotifySubmitFailure(callback) {
173+
if (!(isParentBlock && isTrustedShare && !shouldSuppressBlockSubmitFailureEmail())) return callback(true);
174+
let convertedBlob;
175+
try {
176+
convertedBlob = global.coinFuncs.convertBlob(blockData, blockTemplate.port);
177+
} catch (_error) {
178+
return callback(false);
179+
}
180+
if (!convertedBlob) return callback(false);
181+
182+
function finish(buff) {
183+
return callback(Buffer.isBuffer(buff) && Buffer.isBuffer(resultBuff) && buff.equals(resultBuff));
184+
}
185+
186+
if (typeof global.coinFuncs.slowHashBuffAsync === "function") {
187+
return global.coinFuncs.slowHashBuffAsync(convertedBlob, blockTemplate, miner && miner.payout, finish);
188+
}
189+
if (typeof global.coinFuncs.isHashVerifierEnabled === "function" && global.coinFuncs.isHashVerifierEnabled()) return callback(false);
190+
191+
try {
192+
return finish(global.coinFuncs.slowHashBuff(convertedBlob, blockTemplate));
193+
} catch (_error) {
194+
return callback(false);
195+
}
161196
}
162197

163198
function shouldRetryXmrSubmitFailure(isDisplaySubmitPort) {
@@ -183,7 +218,9 @@ module.exports = function createShareBlockHelpers(deps) {
183218
global.coinFuncs.getPortLastBlockHeader(blockTemplate.port, function (err, body) {
184219
if (err !== null) return console.error(getThreadName() + formatPoolEvent("Header fetch failed", { chain: formatCoinPort(blockTemplate.coin, blockTemplate.port) }));
185220
if (blockTemplate.height == body.height + 1) {
186-
global.support.sendAdminFyi("pool:block-submit:" + reportCoinPort, "FYI: Can't submit " + reportCoinPort + " block to deamon", "The pool server: " + global.config.hostname + " can't submit block to deamon on " + reportCoinPort + getDiffMessage() + "\nInput: " + blockDataStr + "\n" + getThreadName() + "Error submitting " + reportCoinPort + " block at " + reportHeight + " height from " + miner.logString + ", isTrustedShare: " + isTrustedShare + " error ): " + JSON.stringify(rpcResult));
221+
getDiffMessage(function onDiffMessage(diffMessage) {
222+
global.support.sendAdminFyi("pool:block-submit:" + reportCoinPort, "FYI: Can't submit " + reportCoinPort + " block to deamon", "The pool server: " + global.config.hostname + " can't submit block to deamon on " + reportCoinPort + diffMessage + "\nInput: " + blockDataStr + "\n" + getThreadName() + "Error submitting " + reportCoinPort + " block at " + reportHeight + " height from " + miner.logString + ", isTrustedShare: " + isTrustedShare + " error ): " + JSON.stringify(rpcResult));
223+
});
187224
}
188225
}, true);
189226
}, 2 * 1000);
@@ -204,8 +241,8 @@ module.exports = function createShareBlockHelpers(deps) {
204241
const replyFn = function (rpcResult, rpcStatus, port, nextSubmitBlockCB, isDisplaySubmitPortOverride) {
205242
const { activeHeight, isDisplaySubmitPort, reportCoinPort, reportDiff, reportHeight, reportPort } = buildSubmitReport(port, isDisplaySubmitPortOverride);
206243
const requiredBlockDiff = isMainPort && !isDisplaySubmitPort && blockTemplate.xmr_difficulty ? blockTemplate.xmr_difficulty : reportDiff;
207-
const getDiffMessage = function () {
208-
return getBlockSubmitDiffMessage(blockTemplate, blockData, resultBuff, hashDiff, requiredBlockDiff, job);
244+
const getDiffMessage = function (callback) {
245+
return getBlockSubmitDiffMessage(blockTemplate, blockData, resultBuff, hashDiff, requiredBlockDiff, job, miner, callback);
209246
};
210247
const blockDataStr = Buffer.isBuffer(blockData) ? blockData.toString("hex") : JSON.stringify(blockData);
211248

@@ -222,24 +259,24 @@ module.exports = function createShareBlockHelpers(deps) {
222259
}) + ", block hex: \n" + blockDataStr);
223260
return retrySubmit(port, nextSubmitBlockCB, SUBMIT_RETRY_DELAY_MS);
224261
}
225-
const isNotifyAdmin = shouldNotifySubmitFailure();
226-
console.error(getThreadName() + formatPoolEvent("Block submit failed", {
227-
chain: reportCoinPort,
228-
height: reportHeight,
229-
activeHeight,
230-
miner: miner.logString,
231-
trusted: isTrustedShare,
232-
valid: isNotifyAdmin,
233-
rpcStatus,
234-
error: rpcResult
235-
}) + ", block hex: \n" + blockDataStr);
262+
return shouldNotifySubmitFailure(function onSubmitFailureNotify(isNotifyAdmin) {
263+
console.error(getThreadName() + formatPoolEvent("Block submit failed", {
264+
chain: reportCoinPort,
265+
height: reportHeight,
266+
activeHeight,
267+
miner: miner.logString,
268+
trusted: isTrustedShare,
269+
valid: isNotifyAdmin,
270+
rpcStatus,
271+
error: rpcResult
272+
}) + ", block hex: \n" + blockDataStr);
236273

237-
if (isNotifyAdmin && !shouldSuppressBlockSubmitFailureEmail()) {
238-
sendSubmitFailureEmail(reportCoinPort, reportHeight, blockDataStr, rpcResult, getDiffMessage);
239-
}
240-
resetShareTrust();
241-
if (nextSubmitBlockCB) return nextSubmitBlockCB(false);
242-
return;
274+
if (isNotifyAdmin && !shouldSuppressBlockSubmitFailureEmail()) {
275+
sendSubmitFailureEmail(reportCoinPort, reportHeight, blockDataStr, rpcResult, getDiffMessage);
276+
}
277+
resetShareTrust();
278+
if (nextSubmitBlockCB) return nextSubmitBlockCB(false);
279+
});
243280
}
244281

245282
if (poolSettings.acceptSubmittedBlock({
@@ -307,7 +344,10 @@ module.exports = function createShareBlockHelpers(deps) {
307344
}) + ", block hex: \n" + blockDataStr);
308345
if (!shouldSuppressBlockSubmitFailureEmail()) {
309346
const submitCoinPort = formatCoinPort(blockTemplate.coin, blockTemplate.port);
310-
global.support.sendAdminFyi("pool:block-submit-rpc:" + submitCoinPort, "FYI: Can't submit block to deamon on " + submitCoinPort, "Input: " + blockDataStr + "\nThe pool server: " + global.config.hostname + " can't submit block to deamon on " + submitCoinPort + getDiffMessage() + "\nRPC Error. Please check logs for details");
347+
return getDiffMessage(function onRpcErrorDiff(diffMessage) {
348+
global.support.sendAdminFyi("pool:block-submit-rpc:" + submitCoinPort, "FYI: Can't submit block to deamon on " + submitCoinPort, "Input: " + blockDataStr + "\nThe pool server: " + global.config.hostname + " can't submit block to deamon on " + submitCoinPort + diffMessage + "\nRPC Error. Please check logs for details");
349+
if (nextSubmitBlockCB) return nextSubmitBlockCB(false);
350+
});
311351
}
312352
if (nextSubmitBlockCB) return nextSubmitBlockCB(false);
313353
};

0 commit comments

Comments
 (0)