Merge pull request #16 from mCaptcha/feat-incremental

feat: incremental proof of work computing fn
This commit is contained in:
Aravinth Manivannan 2023-10-28 20:52:18 +00:00 committed by GitHub
commit b0111bde6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 4 deletions

View file

@ -4,7 +4,7 @@
* Copyright © 2021 Aravinth Manivnanan <realaravinth@batsense.net>.
*
* Use of this source code is governed by Apache 2.0 or MIT license.
* You shoud have received a copy of MIT and Apache 2.0 along with
* You should have received a copy of MIT and Apache 2.0 along with
* this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/
@ -51,7 +51,7 @@ export const digest = async (message: string): Promise<number[]> => {
* Calculate difficulty of a hash
*
* @param {number[]} hash - hash for which difficulty should be calculated
* @returns {BigInt} - diffuclty of the given hash
* @returns {BigInt} - difficulty of the given hash
*/
export const score = (hash: number[]): BigInt => {
let sum = BigInt(0);
@ -75,7 +75,7 @@ export type WasmWork = {
};
/**
* Generate Proof-of-Work(PoW) according to the algorithim used in mCaptcha
* Generate Proof-of-Work(PoW) according to the algorithm used in mCaptcha
*
* @param {string} salt - salt used in PoW computation. Will be provided in PoW requirement
* @param {string} phrase - challenge phrase used in PoW computation. Will be provided in PoW requirement
@ -105,3 +105,46 @@ export const generate_work = async (
};
return work;
};
/**
* Generate Proof-of-Work(PoW) according to the algorithm used in mCaptcha incrementally
*
* @param {string} salt - salt used in PoW computation. Will be provided in PoW requirement
* @param {string} phrase - challenge phrase used in PoW computation. Will be provided in PoW requirement
* @param {number} difficulty - target difficulty for which PoW should be generated. Will be provided in PoW requirement
* @param {number} step - notify progress with nonce after 'n' number of steps
* @param {(nonce: number) => void} fn - callback function to notify progress
*
* @returns {Promise<WasmWork>} - proof-of-work
**/
export const stepped_generate_work = async (
salt: string,
phrase: string,
difficulty: number,
step: number,
fn: (nonce: number) => void,
): Promise<WasmWork> => {
const serialized_phrase = decoder.decode(serialize(phrase));
const base = salt + serialized_phrase;
let nonce = 0;
let result: BigInt = BigInt(0);
const difficulty_new: BigInt = U128_MAX - U128_MAX / BigInt(difficulty);
let count = 0;
while (result < difficulty_new) {
if (count < step) {
nonce += 1;
const hash = await digest(base + nonce.toString());
result = score(hash);
count+=1;
} else {
fn(nonce);
count = 0;
}
}
const work: WasmWork = {
result: result.toString(),
nonce,
};
return work;
};

View file

@ -1,4 +1,4 @@
import { digest, score, generate_work } from "./index";
import { digest, score, generate_work, stepped_generate_work } from "./index";
import { DATA, DIFFICULTY, SALT } from "./test-data";
("use strict");
@ -30,3 +30,33 @@ test("Digest works", async () => {
}
}
});
test("Incremental proof generation works", async () => {
for (let i = 0; i < DATA.length; i++) {
const d = DATA[i];
let last_nonce = 0;
const step = 1000;
let fnExecuted = false;
const fn = (n: number): void => {
expect(last_nonce + step).toBe(n);
last_nonce = n;
fnExecuted = true;
};
try {
const proof = await stepped_generate_work(
SALT,
d.phrase,
DIFFICULTY,
1000,
fn
);
expect(proof.nonce).toBe(d.pow.nonce);
expect(proof.result).toBe(`${d.pow.result}`);
} catch (error) {
console.log(`${d.pow.nonce}${error}`);
throw error;
}
expect(fnExecuted).toBeTruthy();
}
});