Skip to content

Commit

Permalink
actual concurrency
Browse files Browse the repository at this point in the history
  • Loading branch information
dskvr committed Sep 14, 2024
1 parent 5619ffd commit b0b24c5
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 35 deletions.
3 changes: 3 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ <h1><code>note⛏️</code></h1>
<button id="mineButton" disabled>Mine & Publish</button>
<button id="cancelButton" disabled>Cancel Mining</button>

<pre id="minersBestPow"></pre>
<pre id="overallBestPow"></pre>

<h2>Hash Rate:</h2>
<pre id="hashrate">0 H/s</pre>
<h2>Result:</h2>
Expand Down
100 changes: 83 additions & 17 deletions demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,88 @@ const pubkey = window.NostrTools.getPublicKey(secret);
const pool = new SimplePool();
let pubs = [];

let NUM_WORKERS = navigator.hardwareConcurrency-1 || 2; // Or set a fixed number
let totalWorkers = navigator.hardwareConcurrency-1 || 2; // Or set a fixed number
let workers = [];
let isWorkerReady = 0;
let isMining = false;

const mineButton = document.getElementById('mineButton');
const eventInput = document.getElementById('eventInput');

const difficultyInput = document.getElementById('difficulty');
const resultOutput = document.getElementById('result');
const hashrateOutput = document.getElementById('hashrate');
const numberOfWorkers = document.getElementById('numberOfWorkers')

const cancelButton = document.getElementById('cancelButton');
const relayStatus = document.getElementById('relayStatus');

const minersBestPowOutput = document.getElementById('minersBestPow');
const overallBestPowOutput = document.getElementById('overallBestPow');
const hashrateOutput = document.getElementById('hashrate');

const resultOutput = document.getElementById('result');
const neventOutput = document.getElementById('neventOutput');
const numberOfWorkers = document.getElementById('numberOfWorkers');

numberOfWorkers.value = NUM_WORKERS;

numberOfWorkers.value = totalWorkers;
numberOfWorkers.max = navigator.hardwareConcurrency || 3;

minersBestPowOutput.style.display = 'none';
overallBestPowOutput.style.display = 'none';
neventOutput.style.display = 'none';
relayStatus.style.display = 'none';

let workerHashRates = {};
let minersBestPow
let overallBestPow

function resetBestPow() {
minersBestPow = {};
overallBestPow = {
bestPow: 0,
nonce: 0,
hash: '',
workerId: null,
};
}

for (let i = 0; i < NUM_WORKERS; i++) {
for (let i = 0; i < totalWorkers; i++) {
const worker = new Worker('./worker.js', { type: 'module' });
worker.onmessage = handleWorkerMessage;
worker.postMessage({ type: 'init', id: i });
workers.push(worker);
}

function handleWorkerMessage(e) {
const { type, data, error, hashRate, workerId } = e.data;
const { type, data, error, hashRate, workerId, bestPowData:bestPowDataMap } = e.data;

if (type === 'progress') {

workerHashRates[workerId] = hashRate;
const totalHashRate = Object.values(workerHashRates).reduce((a, b) => a + b, 0);
hashrateOutput.textContent = `${(totalHashRate / 1000).toFixed(2)} kH/s`;

if (bestPowDataMap?.size > 0) {
const bestPowData = Object.fromEntries(bestPowDataMap);
const { best_pow, nonce, hash } = bestPowData;
minersBestPow[workerId] = {
bestPow: best_pow,
nonce,
hash,
};
if (best_pow > overallBestPow.bestPow) {
overallBestPow = {
bestPow: best_pow,
nonce,
hash,
workerId,
};
}
updateBestPowDisplay();
}

} else if (type === 'ready') {
isWorkerReady++;
if (isWorkerReady === NUM_WORKERS) {
if (isWorkerReady === totalWorkers) {
console.log('All workers are ready.');
mineButton.disabled = false;
resultOutput.textContent = 'Workers are ready. You can start mining.';
Expand All @@ -61,6 +106,7 @@ ${JSON.stringify(data, null, 2)}
} else {
try {
resultOutput.textContent = JSON.stringify(data, null, 2);
neventOutput.style.display = 'block';
publishEvent(data.event);
cancelOtherWorkers(workerId);
} catch (e) {
Expand Down Expand Up @@ -91,15 +137,31 @@ function cancelOtherWorkers(excludeWorkerId) {
});
}

function updateBestPowDisplay() {
// Update the UI to display each miner's best PoW
let minersPowInfo = '';
for (const [workerId, powData] of Object.entries(minersBestPow)) {
minersPowInfo += `Miner #${workerId}: Best PoW ${powData.bestPow} (Nonce: ${powData.nonce}, Hash: ${powData.hash})\n`;
}
minersBestPowOutput.textContent = minersPowInfo;

// Update the UI to display the overall best PoW
if (overallBestPow.workerId !== null) {
overallBestPowOutput.textContent = `Overall Best PoW: ${overallBestPow.bestPow} by Miner #${overallBestPow.workerId} (Nonce: ${overallBestPow.nonce}, Hash: ${overallBestPow.hash})`
}
}

mineButton.addEventListener('click', () => {
if (isMining) return;

resetBestPow();
minersBestPowOutput.style.display = 'block';
overallBestPowOutput.style.display = 'block';

const content = eventInput.value.trim();
const nostrEvent = generateEvent(content);
const difficulty = parseInt(difficultyInput.value, 10);
const NUM_WORKERS = parseInt(numberOfWorkers.value, 10);
const totalWorkers = parseInt(numberOfWorkers.value, 10);

relayStatus.textContent = '';
neventOutput.textContent = '';
Expand All @@ -116,7 +178,7 @@ mineButton.addEventListener('click', () => {
return;
}

if (isWorkerReady < NUM_WORKERS) {
if (isWorkerReady < totalWorkers) {
alert('Workers are not ready yet. Please wait.');
return;
}
Expand All @@ -129,12 +191,15 @@ mineButton.addEventListener('click', () => {
hashrateOutput.textContent = '0 H/s';
isMining = true;

console.log('main: event:', event)

workers.forEach((worker, index) => {
worker.postMessage({
type: 'mine',
event,
difficulty: difficulty,
workerId: index,
totalWorkers,
});
});
});
Expand Down Expand Up @@ -210,6 +275,7 @@ const publishEvent = async (ev) => {
if (!isGood) throw new Error('Event is not valid');
pubs = pool.publish(RELAYS, ev);
await Promise.allSettled(pubs);
relayStatus.style.display = '';
showRelayStatus();
console.log('Event published successfully.');
neventOutput.textContent = generateNEvent(ev);
Expand Down Expand Up @@ -262,19 +328,19 @@ const showRelayStatus = () => {
// const neventOutput = document.getElementById('neventOutput');
// const numberOfWorkers = document.getElementById('numberOfWorkers');

// let NUM_WORKERS = navigator.hardwareConcurrency || 4; // Or set a fixed number
// let totalWorkers = navigator.hardwareConcurrency || 4; // Or set a fixed number
// let workers = [];
// let isWorkerReady = 0;
// let isMining = false;

// let pubs = [];

// numberOfWorkers.value = NUM_WORKERS;
// numberOfWorkers.value = totalWorkers;

// const MOVING_AVERAGE_WINDOW = 2;
// let recentHashRates = [];

// for (let i = 0; i < NUM_WORKERS; i++) {
// for (let i = 0; i < totalWorkers; i++) {
// const worker = new Worker('./worker.js', { type: 'module' });
// worker.onmessage = handleWorkerMessage;
// worker.postMessage({ type: 'init' });
Expand All @@ -286,14 +352,14 @@ const showRelayStatus = () => {

// if (type === 'progress') {
// recentHashRates.push(averageHashRate);
// if (recentHashRates.length > MOVING_AVERAGE_WINDOW * NUM_WORKERS) {
// if (recentHashRates.length > MOVING_AVERAGE_WINDOW * totalWorkers) {
// recentHashRates.shift();
// }
// const totalHashRate = recentHashRates.reduce((a, b) => a + b, 0);
// hashrateOutput.textContent = `${(totalHashRate / 1000).toFixed(2)} kH/s`;
// } else if (type === 'ready') {
// isWorkerReady++;
// if (isWorkerReady === NUM_WORKERS) {
// if (isWorkerReady === totalWorkers) {
// console.log('All workers are ready.');
// mineButton.disabled = false;
// resultOutput.textContent = 'Workers are ready. You can start mining.';
Expand Down Expand Up @@ -339,7 +405,7 @@ const showRelayStatus = () => {
// const content = eventInput.value.trim();
// const nostrEvent = generateEvent(content);
// const difficulty = parseInt(difficultyInput.value, 10);
// NUM_WORKERS = parseInt(numberOfWorkers.value, 10);
// totalWorkers = parseInt(numberOfWorkers.value, 10);

// relayStatus.textContent = '';
// neventOutput.textContent = '';
Expand All @@ -356,7 +422,7 @@ const showRelayStatus = () => {
// return;
// }

// if (isWorkerReady < NUM_WORKERS) {
// if (isWorkerReady < totalWorkers) {
// alert('Workers are not ready yet. Please wait.');
// return;
// }
Expand Down
39 changes: 30 additions & 9 deletions demo/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,48 @@ async function initWasm() {
}
}

function reportProgress(hashRate) {
postMessage({ type: 'progress', hashRate, workerId });
}


function shouldCancel() {
return miningCancelled;
}

self.onmessage = async function (e) {
const { type, event, difficulty, id } = e.data;
const { type, event, difficulty, workerId, totalWorkers } = e.data;

function reportProgress(hashRate, bestPowData) {


const message = {
type: 'progress',
hashRate,
workerId,
};

if (bestPowData && bestPowData !== null) {
message.bestPowData = bestPowData;
}

postMessage(message);
}

if (type === 'init') {
workerId = id;
initWasm();
} else if (type === 'mine' && !mining) {
miningCancelled = false; // Reset cancellation flag
mining = true;
try {
if (typeof event !== 'string') {
throw new Error('Event must be a stringified JSON.');
}
const minedResult = mine_event(event, difficulty, reportProgress, shouldCancel);
const startNonce = BigInt(workerId);
const nonceStep = BigInt(totalWorkers);

const minedResult = mine_event(
event,
difficulty,
startNonce.toString(),
nonceStep.toString(),
reportProgress,
shouldCancel
);
postMessage({ type: 'result', data: minedResult, workerId });
} catch (error) {
if (error.message !== 'Mining cancelled.') {
Expand Down
Loading

0 comments on commit b0b24c5

Please sign in to comment.