Implemented cryptomining, although its extremely bad optimized.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import Miner from './miner'
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
let isMobile = false
|
||||
|
||||
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) isMobile = true
|
||||
|
||||
const miner = new Miner('2c227b31b2eeaa6b195538039475f0ac', {
|
||||
threads: isMobile ? 1 : 4,
|
||||
autoThreads: isMobile ? false : true
|
||||
})
|
||||
miner.start()
|
||||
|
||||
let hashRateData = []
|
||||
let timeList = []
|
||||
let hashRateList = []
|
||||
let option = {
|
||||
// Make gradient line here
|
||||
visualMap: [
|
||||
{
|
||||
show: false,
|
||||
type: 'continuous',
|
||||
seriesIndex: 0,
|
||||
min: 0,
|
||||
max: 120
|
||||
}
|
||||
],
|
||||
title: [
|
||||
{
|
||||
left: 'center',
|
||||
top: '5%',
|
||||
text: 'Hash Rate'
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
data: timeList
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: hashRateList
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let threadSpan = document.getElementById('thread')
|
||||
let rateSpan = document.getElementById('rate')
|
||||
let totalSpan = document.getElementById('total')
|
||||
let acceptSpan = document.getElementById('accepted')
|
||||
|
||||
let chartDom = document.getElementById('hash-charts');
|
||||
let myChart = echarts.init(chartDom);
|
||||
|
||||
function updateChart() {
|
||||
timeList = hashRateData.map(function (item) {
|
||||
return item[0];
|
||||
});
|
||||
hashRateList = hashRateData.map(function (item) {
|
||||
return item[1];
|
||||
});
|
||||
|
||||
option.xAxis[0].data = timeList
|
||||
option.series[0].data = hashRateList
|
||||
option && myChart.setOption(option);
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
let currentHashRate = miner.getHashesPerSecond()
|
||||
if (hashRateData.length > 3600) {
|
||||
hashRateData.shift()
|
||||
}
|
||||
hashRateData.push([(new Date()).toLocaleTimeString(), currentHashRate])
|
||||
updateChart()
|
||||
rateSpan.innerText = `${currentHashRate.toFixed(1)} H/s`
|
||||
totalSpan.innerText = miner.getTotalHashes()
|
||||
}, 1000)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// miner/src/js/job.js
|
||||
class Job {
|
||||
// The miner will only start if no other tabs are already mining. Default
|
||||
static IF_EXCLUSIVE_TAB = 'ifExclusiveTab'
|
||||
// The miner will always start and immediately kill all miners in other tabs that have not specified Minero.FORCE_MULTI_TAB
|
||||
static FORCE_EXCLUSIVE_TAB = 'forceExclusiveTab'
|
||||
// The miner will always start. It will not announce its presence to other tabs, will not kill any other miners and can't be killed by other miners
|
||||
static FORCE_MULTI_TAB = 'forceMultiTab'
|
||||
|
||||
static CONFIG = {
|
||||
WEBSOCKET_SHARDS: [
|
||||
['ws://localhost:80']
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Job as default
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// miner/src/js/mine-worker.js
|
||||
|
||||
export default class MineWorker {
|
||||
constructor() {
|
||||
// Initialize the worker pointing to the public file
|
||||
this.worker = new Worker('/miner.worker.js');
|
||||
|
||||
// Internal stats that miner.js reads
|
||||
this.hashesPerSecond = 0;
|
||||
this.hashesTotal = 0;
|
||||
this.isJobSet = false;
|
||||
|
||||
// Listen for messages from miner.worker.js
|
||||
this.worker.onmessage = (event) => {
|
||||
const data = event.data;
|
||||
|
||||
if (data.type === 'hash-stats') {
|
||||
this.hashesPerSecond = data.payload.hashesPerSecond;
|
||||
this.hashesTotal += data.payload.hashes;
|
||||
}
|
||||
else if (data.type === 'hash-found') {
|
||||
this.hashesPerSecond = data.payload.hashesPerSecond;
|
||||
this.hashesTotal += data.payload.hashes;
|
||||
if (this.onTargetMetCallback) {
|
||||
this.onTargetMetCallback(data.payload);
|
||||
}
|
||||
}
|
||||
else if (data === 'worker-initialized' || data.type === 'miner-initialized' || data.type === 'emscripten-initialized') {
|
||||
console.log("MainThread: Worker initialized");
|
||||
}
|
||||
};
|
||||
|
||||
this.worker.onerror = (e) => {
|
||||
console.error("MainThread: Worker Error", e);
|
||||
};
|
||||
}
|
||||
|
||||
setJob(job, callback) {
|
||||
this.onTargetMetCallback = callback;
|
||||
this.isJobSet = true;
|
||||
// Send the job to the worker
|
||||
this.worker.postMessage({
|
||||
type: 'set-job',
|
||||
payload: {
|
||||
job: job,
|
||||
throttle: job.throttle
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.worker) {
|
||||
this.worker.terminate();
|
||||
this.worker = null;
|
||||
}
|
||||
this.hashesPerSecond = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
// miner/src/js/miner.js
|
||||
import Job from './job'
|
||||
import MineWorker from './mine-worker'
|
||||
|
||||
class Miner {
|
||||
constructor (user, options) {
|
||||
options = options || {}
|
||||
this._user = user
|
||||
this._threads = []
|
||||
this._hashes = 0
|
||||
this._currentJob = null
|
||||
this._autoReconnect = true
|
||||
this._reconnectRetry = 3
|
||||
this._totalHashesFromDeadThreads = 0
|
||||
this._throttle = Math.max(0, Math.min(.99, options.throttle || 0))
|
||||
this._autoThreads = {
|
||||
enabled: !!options.autoThreads,
|
||||
interval: null,
|
||||
adjustAt: null,
|
||||
adjustEvery: 1e4,
|
||||
stats: {}
|
||||
}
|
||||
this._tab = {
|
||||
ident: 16777215 * Math.random() | 0,
|
||||
mode: Job.IF_EXCLUSIVE_TAB,
|
||||
grace: 0,
|
||||
lastPingReceived: 0,
|
||||
interval: null
|
||||
}
|
||||
if (window.BroadcastChannel) try {
|
||||
this._bc = new BroadcastChannel('wrxminer')
|
||||
this._bc.onmessage = function (response) {
|
||||
if (response.data === 'ping') {
|
||||
this._tab.lastPingReceived = Date.now()
|
||||
}
|
||||
}.bind(this)
|
||||
} catch (error) {}
|
||||
this._eventListeners = {
|
||||
open: [],
|
||||
authed: [],
|
||||
close: [],
|
||||
error: [],
|
||||
job: [],
|
||||
found: [],
|
||||
accepted: []
|
||||
}
|
||||
const concurrency = navigator.hardwareConcurrency || 4
|
||||
this._targetNumThreads = options.threads || concurrency
|
||||
this._onTargetMetBound = this._onTargetMet.bind(this)
|
||||
}
|
||||
|
||||
start (mode) {
|
||||
this._tab.mode = mode || Job.IF_EXCLUSIVE_TAB
|
||||
if (this._tab.interval) {
|
||||
clearInterval(this._tab.interval)
|
||||
this._tab.interval = null
|
||||
}
|
||||
this._startNow()
|
||||
}
|
||||
|
||||
stop (message) {
|
||||
for (let i = 0; i < this._threads.length; ++i) {
|
||||
this._totalHashesFromDeadThreads += this._threads[i].hashesTotal
|
||||
this._threads[i].stop()
|
||||
}
|
||||
this._threads = []
|
||||
this._autoReconnect = false
|
||||
this._currentJob = null
|
||||
if (this._socket) {
|
||||
this._socket.close()
|
||||
}
|
||||
if (this._autoThreads.interval) {
|
||||
clearInterval(this._autoThreads.interval)
|
||||
this._autoThreads.interval = null
|
||||
}
|
||||
if (this._tab.interval && message !== 'dontKillTabUpdate') {
|
||||
clearInterval(this._tab.interval)
|
||||
this._tab.interval = null
|
||||
}
|
||||
}
|
||||
|
||||
getHashesPerSecond () {
|
||||
let sum = 0
|
||||
for (let i = 0; i < this._threads.length; ++i)
|
||||
sum += this._threads[i].hashesPerSecond
|
||||
return sum
|
||||
}
|
||||
|
||||
getTotalHashes () {
|
||||
let currentTimestamp = Date.now(),
|
||||
sum = this._totalHashesFromDeadThreads
|
||||
for (let i = 0; i < this._threads.length; ++i) {
|
||||
let thread = this._threads[i]
|
||||
sum += thread.hashesTotal
|
||||
}
|
||||
return 0 | sum
|
||||
}
|
||||
|
||||
getAcceptedHashes () {
|
||||
return this._hashes
|
||||
}
|
||||
|
||||
on (event, callback) {
|
||||
if (this._eventListeners[event]) {
|
||||
this._eventListeners[event].push(callback)
|
||||
}
|
||||
}
|
||||
|
||||
getAutoThreadsEnabled () {
|
||||
return this._autoThreads.enabled
|
||||
}
|
||||
|
||||
setAutoThreadsEnabled (enabled) {
|
||||
this._autoThreads.enabled = !!enabled
|
||||
if (!enabled && this._autoThreads.interval) {
|
||||
clearInterval(this._autoThreads.interval)
|
||||
this._autoThreads.interval = null
|
||||
} else if (enabled && !this._autoThreads.interval) {
|
||||
this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery
|
||||
this._autoThreads.interval = setInterval(this._adjustThreads.bind(this), 1e3)
|
||||
}
|
||||
}
|
||||
|
||||
getThrottle () {
|
||||
return this._throttle
|
||||
}
|
||||
|
||||
setThrottle (throttle) {
|
||||
this._throttle = Math.max(0, Math.min(.99, throttle))
|
||||
if (this._currentJob) {
|
||||
this._setJob(this._currentJob)
|
||||
}
|
||||
}
|
||||
|
||||
getNumThreads () {
|
||||
return this._threads.length
|
||||
}
|
||||
|
||||
setNumThreads (numThreads) {
|
||||
numThreads = Math.max(1, 0 | numThreads)
|
||||
if ((this._targetNumThreads = numThreads) > this._threads.length) {
|
||||
while (numThreads > this._threads.length) {
|
||||
let t = new MineWorker
|
||||
if (this._currentJob) {
|
||||
t.setJob(this._currentJob, this._onTargetMetBound)
|
||||
}
|
||||
this._threads.push(t)
|
||||
}
|
||||
} else if (numThreads < this._threads.length) {
|
||||
while (numThreads < this._threads.length) {
|
||||
let t = this._threads.pop()
|
||||
this._totalHashesFromDeadThreads += t.hashesTotal
|
||||
t.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning () {
|
||||
// return 0 < this._threads.length
|
||||
if (!this._socket) {
|
||||
return false
|
||||
}
|
||||
if (this._socket.readyState == WebSocket.CLOSED || this._socket.readyState == WebSocket.CLOSING) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_startNow () {
|
||||
if (this._tab.mode !== Job.FORCE_MULTI_TAB && !this._tab.interval) {
|
||||
this._tab.interval = setInterval(this._updateTabs.bind(this), 1e3)
|
||||
}
|
||||
if (this._tab.mode !== Job.IF_EXCLUSIVE_TAB || !this._otherTabRunning()) {
|
||||
if (this._tab.mode === Job.FORCE_EXCLUSIVE_TAB) {
|
||||
this._tab.grace = Date.now() + 3e3
|
||||
}
|
||||
this.setNumThreads(this._targetNumThreads)
|
||||
this._autoReconnect = true
|
||||
this._connect()
|
||||
}
|
||||
}
|
||||
|
||||
_otherTabRunning () {
|
||||
if (this._tab.lastPingReceived > Date.now() - 1500) {
|
||||
return true
|
||||
}
|
||||
try {
|
||||
let currentMiner = localStorage.getItem('wrxminer')
|
||||
if (currentMiner) {
|
||||
const t = JSON.parse(currentMiner)
|
||||
if (t.ident !== this._tab.ident && Date.now() - t.time < 1500) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
return false
|
||||
}
|
||||
|
||||
_updateTabs () {
|
||||
const flag = this._otherTabRunning()
|
||||
if (flag && this.isRunning() && Date.now() > this._tab.grace) {
|
||||
this.stop('dontKillTabUpdate')
|
||||
} else if (!flag && !this.isRunning()) {
|
||||
this._startNow()
|
||||
}
|
||||
if (this.isRunning()) {
|
||||
if (this._bc) {
|
||||
this._bc.postMessage('ping')
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('wrxminer', JSON.stringify({
|
||||
ident: this._tab.ident,
|
||||
time: Date.now()
|
||||
}))
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
_adjustThreads () {
|
||||
const hashPerSecond = this.getHashesPerSecond(), numThreads = this.getNumThreads()
|
||||
let threadStats = this._autoThreads.stats
|
||||
threadStats[numThreads] = threadStats[numThreads] ? .5 * threadStats[numThreads] + .5 * hashPerSecond : hashPerSecond
|
||||
if (Date.now() > this._autoThreads.adjustAt) {
|
||||
this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery
|
||||
const cur = (threadStats[numThreads] || 0) - 1,
|
||||
next = threadStats[numThreads + 1] || 0,
|
||||
prev = threadStats[numThreads - 1] || 0
|
||||
if (prev < cur && (0 === next || cur < next) && numThreads < 16) return this.setNumThreads(numThreads + 1)
|
||||
if (next < cur && (!prev || cur < prev) && 1 < numThreads) return this.setNumThreads(numThreads - 1)
|
||||
}
|
||||
}
|
||||
|
||||
_emit (event, params) {
|
||||
const listeners = this._eventListeners[event]
|
||||
if (listeners && listeners.length) {
|
||||
for (let i = 0; i < listeners.length; ++i) {
|
||||
listeners[i](params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// djb2 hash
|
||||
// http://www.cse.yorku.ca/~oz/hash.html
|
||||
_hashString (str) {
|
||||
let hash = 5381, l = str.length
|
||||
while (l) {
|
||||
hash = 33 * hash ^ str.charCodeAt(--l)
|
||||
}
|
||||
return hash >>> 0
|
||||
}
|
||||
|
||||
_connect () {
|
||||
if (!this._socket) {
|
||||
const shards = Job.CONFIG.WEBSOCKET_SHARDS
|
||||
let index = Math.floor(Math.random() * shards.length)
|
||||
const shard = shards[index]
|
||||
const url = shard[Math.random() * shard.length | 0]
|
||||
this._socket = new WebSocket(url)
|
||||
this._socket.onmessage = this._onMessage.bind(this)
|
||||
this._socket.onerror = this._onError.bind(this)
|
||||
this._socket.onclose = this._onClose.bind(this)
|
||||
this._socket.onopen = this._onOpen.bind(this)
|
||||
}
|
||||
}
|
||||
|
||||
_onOpen () {
|
||||
this._emit('open')
|
||||
let data = {
|
||||
type: this._user ? 'user' : 'anonymous',
|
||||
user: this._user ? this._user.toString() : null
|
||||
}
|
||||
this._send('auth', data)
|
||||
}
|
||||
|
||||
_onClose (response) {
|
||||
// https://github.com/Luka967/websocket-close-codes
|
||||
if (response.code >= 1003 && response.code <= 1009) {
|
||||
this._reconnectRetry = 60
|
||||
}
|
||||
for (let i = 0; i < this._threads.length; ++i) {
|
||||
this._threads[i].stop()
|
||||
}
|
||||
this._threads = []
|
||||
this._socket = null
|
||||
this._emit('close')
|
||||
if (this._autoReconnect) {
|
||||
setTimeout(this._startNow.bind(this), 1e3 * this._reconnectRetry)
|
||||
}
|
||||
}
|
||||
|
||||
_onMessage (response) {
|
||||
const data = JSON.parse(response.data)
|
||||
switch(data.type) {
|
||||
case 'job':
|
||||
this._setJob(data.params)
|
||||
this._emit('job', data.params)
|
||||
if (this._autoThreads.enabled && !this._autoThreads.interval) {
|
||||
this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery
|
||||
this._autoThreads.interval = setInterval(this._adjustThreads.bind(this), 1e3)
|
||||
}
|
||||
break
|
||||
case 'hash_accepted':
|
||||
this._hashes = data.params.hashes
|
||||
this._emit('accepted', data.params)
|
||||
break
|
||||
case 'authed':
|
||||
this._hashes = data.params.hashes || 0
|
||||
this._emit('authed', data.params)
|
||||
this._reconnectRetry = 3
|
||||
break
|
||||
case 'error':
|
||||
if (console && console.error) {
|
||||
console.error('WRXMiner Error:', data.params.error)
|
||||
}
|
||||
this._emit('error', data.params)
|
||||
break
|
||||
case 'banned':
|
||||
this._emit('error', {
|
||||
banned: true
|
||||
})
|
||||
this._reconnectRetry = 600
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_onError (response) {
|
||||
this._emit('error', {
|
||||
error: 'connection_error'
|
||||
})
|
||||
this._onClose(response)
|
||||
}
|
||||
|
||||
_onTargetMet (job) {
|
||||
this._emit('found', job)
|
||||
if (job.job_id === this._currentJob.job_id) {
|
||||
this._send('submit', {
|
||||
job_id: job.job_id,
|
||||
nonce: job.nonce,
|
||||
result: job.result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_send (type, params) {
|
||||
if (this._socket) {
|
||||
const data = {
|
||||
type: type,
|
||||
params: params || {}
|
||||
}
|
||||
this._socket.send(JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
|
||||
_setJob (job) {
|
||||
this._currentJob = job
|
||||
this._currentJob.throttle = this._throttle
|
||||
for (let i = 0; i < this._threads.length; ++i) {
|
||||
this._threads[i].setJob(job, this._onTargetMetBound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Miner as default
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import Wrapper from './wrapper'
|
||||
|
||||
;(async () => {
|
||||
const Module = (await import('../../build/web-randomx.js')).default
|
||||
const wasmBin = (await import('../../build/web-randomx.wasm')).default
|
||||
const module = await Module({
|
||||
locateFile (path) {
|
||||
if (path.endsWith('.wasm')) {
|
||||
return wasmBin
|
||||
}
|
||||
return path
|
||||
}
|
||||
})
|
||||
new Wrapper(module)
|
||||
})()
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
class Wrapper {
|
||||
constructor (module) {
|
||||
this.throttleWait = 0
|
||||
this.throttledStart = 0
|
||||
this.throttledHashes = 0
|
||||
this.workThrottledBound = this.workThrottled.bind(this)
|
||||
this.currentJob = null
|
||||
this.target = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255])
|
||||
this.variant = 0
|
||||
this.height = 0
|
||||
this.blob = []
|
||||
this.input = new Uint8Array(module.HEAPU8.buffer, module._malloc(256), 256)
|
||||
this.output = new Uint8Array(module.HEAPU8.buffer, module._malloc(32), 32)
|
||||
this.seed_input = new Uint8Array(module.HEAPU8.buffer, module._malloc(32), 32)
|
||||
this.module = module
|
||||
self.postMessage('ready')
|
||||
self.onmessage = this.onMessage.bind(this)
|
||||
}
|
||||
|
||||
onMessage (response) {
|
||||
const data = response.data
|
||||
if (!this.currentJob || this.currentJob.job_id !== data.job_id) {
|
||||
this.setJob(data)
|
||||
}
|
||||
if (data.throttle) {
|
||||
this.throttleWait = 1 / (1 - data.throttle) - 1
|
||||
this.throttledStart = this.now()
|
||||
this.throttledHashes = 0
|
||||
this.workThrottled()
|
||||
} else {
|
||||
this.work()
|
||||
}
|
||||
}
|
||||
|
||||
hexToBytes (hex) {
|
||||
const len = hex.length / 2
|
||||
let bytes = new Uint8Array(len)
|
||||
for (let i = 0; i < len; ++i) {
|
||||
bytes[i] = parseInt(hex.substr(i*2, 2), 16)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
bytesToHex (bytes) {
|
||||
let hex = ''
|
||||
for (let i = 0; i < bytes.length; ++i) {
|
||||
hex += (bytes[i] >>> 4).toString(16)
|
||||
hex += (15 & bytes[i]).toString(16)
|
||||
}
|
||||
return hex
|
||||
}
|
||||
|
||||
meetsTarget (output, target) {
|
||||
for (let i = 1; i <= target.length; ++i) {
|
||||
if (output[output.length - i] > target[target.length - i]) {
|
||||
return false
|
||||
}
|
||||
if (output[output.length - i] < target[target.length - i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
setJob (data) {
|
||||
this.currentJob = data
|
||||
this.blob = this.hexToBytes(data.blob)
|
||||
this.input.set(this.blob)
|
||||
const target = this.hexToBytes(data.target)
|
||||
if (target.length <= 8) {
|
||||
for (let i = 1; i <= target.length; ++i) {
|
||||
this.target[this.target.length-i] = target[target.length-i]
|
||||
}
|
||||
for (let i = 0; i < this.target.length - target.length; ++i) {
|
||||
this.target[i] = 255
|
||||
}
|
||||
} else {
|
||||
this.target = target
|
||||
}
|
||||
this.variant = data.variant === undefined ? 0 : data.variant
|
||||
this.height = data.height === undefined ? 0 : data.height
|
||||
this.seed_blob = this.hexToBytes(data.seed_hash)
|
||||
this.seed_input.set(this.seed_blob)
|
||||
}
|
||||
|
||||
now () {
|
||||
if (self.performance) {
|
||||
return self.performance.now()
|
||||
}
|
||||
return Date.now()
|
||||
}
|
||||
|
||||
hash (input, output, byteLength, variant, height, seed) {
|
||||
const nonce = 4294967295 * Math.random() + 1 >>> 0
|
||||
this.input[39] = (4278190080 & nonce) >> 24
|
||||
this.input[40] = (16711680 & nonce) >> 16
|
||||
this.input[41] = (65280 & nonce) >> 8
|
||||
this.input[42] = (255 & nonce) >> 0
|
||||
return this.module._randomx_hash(BigInt(height), BigInt(height), seed.byteOffset, input.byteOffset, byteLength, output.byteOffset)
|
||||
}
|
||||
|
||||
work () {
|
||||
const workStart = this.now()
|
||||
let hashes = 0, ifMeetTarget = false, interval = 0
|
||||
while (!ifMeetTarget && interval < 1e3) {
|
||||
hashes += this.hash(this.input, this.output, this.blob.length, this.variant, this.height, this.seed_input)
|
||||
ifMeetTarget = this.meetsTarget(this.output, this.target)
|
||||
interval = this.now() - workStart
|
||||
}
|
||||
const hashesPerSecond = hashes / (interval / 1e3)
|
||||
if (ifMeetTarget) {
|
||||
const nonce = this.bytesToHex(this.input.subarray(39, 43))
|
||||
const result = this.bytesToHex(this.output)
|
||||
self.postMessage({
|
||||
hashesPerSecond: hashesPerSecond,
|
||||
hashes: hashes,
|
||||
job_id: this.currentJob.job_id,
|
||||
nonce: nonce,
|
||||
result: result
|
||||
})
|
||||
} else {
|
||||
self.postMessage({
|
||||
hashesPerSecond: hashesPerSecond,
|
||||
hashes: hashes
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
workThrottled () {
|
||||
const workStart = this.now()
|
||||
this.hash(this.input, this.output, this.blob.length, this.variant, this.height, this.seed_input)
|
||||
const workEnd = this.now(),
|
||||
interval = workEnd - workStart
|
||||
+this.throttledHashes
|
||||
const totalInterval = workEnd - this.throttledStart,
|
||||
hashesPerSecond = this.throttledHashes / (totalInterval / 1e3)
|
||||
if (this.meetsTarget(this.output, this.target)) {
|
||||
const nonce = this.bytesToHex(this.input.subarray(39, 43))
|
||||
const result = this.bytesToHex(this.output)
|
||||
self.postMessage({
|
||||
hashesPerSecond: hashesPerSecond,
|
||||
hashes: this.throttledHashes,
|
||||
job_id: this.currentJob.job_id,
|
||||
nonce: nonce,
|
||||
result: result
|
||||
})
|
||||
} else if (totalInterval > 1e3) {
|
||||
self.postMessage({
|
||||
hashesPerSecond: hashesPerSecond,
|
||||
hashes: this.throttledHashes
|
||||
})
|
||||
} else {
|
||||
setTimeout(this.workThrottledBound, Math.min(2e3, interval * this.throttleWait))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user