├── .gitignore ├── JSMiner Final Report.pdf ├── README.md ├── index.html ├── index.js ├── npm-debug.log ├── package.json └── public ├── glminer.js ├── miner.js ├── sha256.js ├── shader-fs.js ├── shader-vs.js ├── util.js └── work-manager.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /JSMiner Final Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howardchung/jsminer/8972f809da689f4a73212ef9d6423d09729ce735/JSMiner Final Report.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsminer 2 | An experiment with in-browser distributed bitcoin mining. 3 | 4 | * Howard Chung 5 | * Kent Jiang 6 | 7 | https://github.com/howardc93/jsminer 8 | 9 | About 10 | ---- 11 | 12 | Bitcoin is a digital cryptocurrency made up of processed data blocks used for online and brick-and-mortar purchases. Because bitcoins are limited and their value is determined by market forces, bitcoins are also traded like stocks on various exchanges. 13 | The purpose of Bitcoin mining is to create new bitcoins as well as ensure that all participants have a consistent view of the bitcoin data. 14 | 15 | JSMiner is an attempt to engage in bitcoin mining in a distributed manner--namely, through visitors' web browsers. 16 | Traditionally, bitcoin mining has been done through standalone programs, which generally require administrator permissions to run. 17 | By moving the computation to the browser, a visitor merely needs to visit a web page in order to begin mining. 18 | 19 | Implementation 20 | ---- 21 | JSMiner consists of two components: 22 | * The first is a client application, consisting of HTML and JavaScript. 23 | * The second is a Node.js server which coordinates the work done by the clients, and serves as a "bridge" between the clients mining in the browser and the bitcoin network. 24 | 25 | In this implementation, bitcoin mining is done through a pool. Due to the currently high difficulty of mining, "solo" mining without a pool can take years in order to find a block. 26 | By mining in a pool, the variance is reduced. 27 | The pool assigns work (shares) that are easier than the work required to find a block. 28 | When someone in the pool is successful in mining a "real" block, the pool distributes rewards among participants based on the number of shares they submitted. 29 | 30 | The server connects to a mining pool using the Stratum TCP protocol. This allows the pool to "push" new work. 31 | For example, when a miner anywhere in the world finds a block, the pool needs to notify all participants of this change and assign new work. 32 | When our server receives this "push" from the pool, it recalculates the block header that clients should hash. 33 | 34 | The server has three HTTP endpoints: 35 | * /: The root of the site serves the UI, and allows users to begin mining 36 | * /work: This returns a JSON-encoded job that the client should use to begin mining 37 | * /submit: This endpoint allows the client to submit a successful nonce to the server. 38 | 39 | Clients, on connection to the server, get the current block header from the server and begin hashing. 40 | Each client selects a random nonce to start hashing with, reducing the possibility of clients doing "overlapping" work. 41 | Each time the client fails, it increments the nonce by 1 and tries the hash again. 42 | Thus, the client can attempt 4.2 billion hashes (the nonce is a 32-bit integer) before getting new work from the server. 43 | When a client successfully finds a nonce that produces a low enough hash (lower than the target value), it uses the /submit endpoint to send the nonce to the server. 44 | 45 | The server then forwards this work to the pool, which credits it toward the user's earnings. 46 | 47 | Difficulties 48 | ---- 49 | Connecting this application to the live bitcoin network proved challenging. 50 | The client-side hashing example we began with used the old "getwork" protocol, which relies on polling the server for new work (once a second). 51 | We had to research the Stratum protocol and write the code to convert the Stratum data into a block for the client to mine. 52 | 53 | In addition, it was difficult to test actual share submission, since bitcoin mining on CPUs is so slow that it would take a very long time in order to find a valid share. 54 | 55 | Performance 56 | ---- 57 | By using WebWorkers, the JavaScript miner achieved speeds of approximately 80Khash/s, considerably slower than a C-implemented miner. 58 | 59 | Extensions 60 | ---- 61 | At this stage, bitcoin mining on CPUs is largely inefficient. 62 | While possibly profitable for users if run on "free" electricity (from an institution or similar), it has largely been superseded by superior technologies. 63 | ASICs (Application-Specific Integrated Circuits) have proven to be much more efficient in terms of hashes per watt, and currently make up the bulk of the bitcoin mining hash power. 64 | 65 | A further optimization would be the use of WebSockets in order to allow the server to push new work to clients. While the Stratum protocol takes care of the pool pushing work to the server, the server and client currently do not have a two-way connection. This would lead to slightly improved efficiency, since any work done by clients before the server is able to notify them becomes "stale" and is thus wasted. 66 | 67 | Another interesting direction to go in would be to try to utilize the GPU using WebGL. Traditionally, GPUs have proved to be far more efficient at the SHA256-hashing required in bitcoin mining. However, the point is likely moot as both CPUs and GPUs have been superseded by ASICs. This idea might be useful if applied to "altcoins" such as Litecoin, Darkcoin, and others designed to be "ASIC-proof", through high memory bandwidth requirements. 68 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JSMiner 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
Mine Method: 17 | Javascript 18 | WebWorker 19 | WebGL 20 |
WebGL Threads: 21 | 24 |

25 | 26 |
27 |
Total Hashes: 28 |
Hash/s: 29 | 32 |
Golden Ticket: 33 |
34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var port = process.env.PORT || 5000; 4 | var worker_name = '13XHeLLVeFtqef7WD4BDL3fQRqpVTUdG3i'; 5 | var curr_block = {}; 6 | var createHash = require('sha.js'); 7 | var sha256 = createHash('sha256'); 8 | var ba = require('binascii'); 9 | app.listen(port); 10 | app.use('/public', express.static(__dirname + '/public')); 11 | app.get('/', function(req, res) { 12 | res.sendFile("index.html", { 13 | "root": __dirname 14 | }); 15 | }); 16 | app.get('/work', function(req, res) { 17 | console.log("client requested work!"); 18 | //send constructed block to client 19 | //client mines block 20 | //when client succeeds, client hits /submit 21 | res.json({ 22 | result: curr_block 23 | }); 24 | }); 25 | app.get('/submit', function(req, res) { 26 | console.log("client submitted work!"); 27 | //endpoint for clients to submit work 28 | //server forwards to mining pool 29 | //req.query.nonce has the solution 30 | /* 31 | params[0] = Worker Name 32 | params[1] = Job ID 33 | params[2] = ExtraNonce 2 34 | params[3] = nTime 35 | params[4] = nonce 36 | */ 37 | client.stratumSubmit(worker_name, curr_block.job_id, curr_block.extranonce2, curr_block.ntime, req.query.nonce); 38 | res.json({ 39 | error: null 40 | }); 41 | }); 42 | //communicate with bitcoin network via stratum tcp protocol 43 | var Client = require('stratum').Client; 44 | var client = Client.create(); 45 | client.on('mining.error', function(msg, socket) { 46 | console.log(msg); 47 | }); 48 | // the client is a one-way communication, it receives data from the server after issuing commands 49 | client.on('mining', function(data, socket, type) { 50 | // type will be either 'broadcast' or 'result' 51 | //console.log('Mining data (%s): %s', type, JSON.stringify(data)); 52 | // you can issue more commands to the socket, it's the exact same socket as "client" variable 53 | // in this example 54 | // the socket (client) got some fields like: 55 | // client.name = name of the worker 56 | // client.authorized = if the current connection is authorized or not 57 | // client.id = an UUID ([U]niversal [U]nique [ID]entifier) that you can safely rely on it's uniqueness 58 | // client.subscription = the subscription data from the server 59 | if (data.method === "notify") { 60 | //console.log(JSON.stringify(data)); 61 | /* 62 | params[0] = Job ID. This is included when miners submit a results so work can be matched with proper transactions. 63 | params[1] = Hash of previous block. Used to build the header. 64 | params[2] = Coinbase (part 1). The miner inserts ExtraNonce1 and ExtraNonce2 after this section of the coinbase. 65 | params[3] = Coinbase (part 2). The miner appends this after the first part of the coinbase and the two ExtraNonce values. 66 | params[4][] = List of merkle branches. The coinbase transaction is hashed against the merkle branches to build the final merkle root. 67 | params[5] = Bitcoin block version, used in the block header. 68 | params[6] = nBit, the encoded network difficulty. Used in the block header. 69 | params[7] = nTime, the current time. nTime rolling should be supported, but should not increase faster than actual time. 70 | params[8] = Clean Jobs. If true, miners should abort their current work and immediately use the new job. If false, they can still use the current job, but should move to the new one after exhausting the current nonce 71 | */ 72 | //build curr_block 73 | curr_block.job_id = data.params[0]; 74 | //TODO: generate a block in getwork format for client to mine 75 | //generate an extranonce2 of the correct size. typically the client would increment this but we already have 2^32 possible nonces per worker 76 | //therefore each client has 4 billion hashes to try before needing new work 77 | //ExtraNonce2_size: This is how many bytes should be used for the ExtraNonce2 counter. 78 | //ExtraNonce2 is a hexadecimal counter, and should be padded to fill up the number of bytes identified as the ExtraNonce2_size. 79 | //static extranonce of 4 bytes in hex, could be incremented by the client miners if desired 80 | curr_block.extranonce2 = "00000000"; 81 | curr_block.ntime = data.params[7]; 82 | //construct the block header using double sha256, coinbase, etc. 83 | //To produce coinbase, we just concatenate Coinb1 + Extranonce1 + Extranonce2 + Coinb2 together. That's all! 84 | var coinbase = data.params[2] + curr_block.extranonce1 + curr_block.extranonce2 + data.params[3]; 85 | console.log("coinbase: %s", coinbase); 86 | //double sha hash the coinbase 87 | var coinbase_hash_bin = sha256.update(sha256.update(coinbase, "hex").digest()).digest(); 88 | //compute the merkle root 89 | var merkle_branches = data.params[4]; 90 | var merkle_root = coinbase_hash_bin; 91 | for (var i = 0; i < merkle_branches.length; i++) { 92 | merkle_root = sha256.update(sha256.update(merkle_root + ba.unhexlify(merkle_branches[i])).digest()).digest(); 93 | } 94 | merkle_root = merkle_root.toString('hex'); 95 | console.log("merkle root: %s", merkle_root); 96 | var version = data.params[5]; 97 | var prevhash = data.params[1]; 98 | var ntime = data.params[7]; 99 | var nbits = data.params[6]; 100 | var header = version + prevhash + merkle_root + ntime + nbits + '00000000' + '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000'; 101 | curr_block.data = header; 102 | //big-endian difficulty 1 target 103 | curr_block.target = "00000000ffff0000000000000000000000000000000000000000000000000000"; 104 | //we could use websockets to push new work to clients when curr_block updates, but we don't have to implement this 105 | //or they can just poll occasionally to get new work 106 | console.log("computed block!"); 107 | console.log(JSON.stringify(curr_block)); 108 | } 109 | else if (data.result) { 110 | /* 111 | result[0] = "mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f" - Unique string used for the subscription 112 | result[1] = ExtraNonce1, used for building the coinbase. 113 | result[2] = Extranonce2_size, the number of bytes that the miner users for its ExtraNonce2 counter 114 | */ 115 | //place subscription data in the current block 116 | curr_block.extranonce1 = data.result[1]; 117 | curr_block.extranonce2_size = data.result[2]; 118 | console.log('got subscription data'); 119 | console.log(JSON.stringify(data)); 120 | } 121 | if (!socket.authorized) { 122 | console.log('Asking for authorization'); 123 | //any password can be used 124 | socket.stratumAuthorize(worker_name, 'x'); 125 | } 126 | else { 127 | console.log("authorized"); 128 | } 129 | }); 130 | client.connect({ 131 | //host: 'stratum.mining.eligius.st', 132 | //port: 3334 133 | host: 'stratum.bitcoin.cz', 134 | port: 3333 135 | }).then(function(socket) { 136 | // deferred, this can be chained if needed, no callback hell 137 | // "socket" refers to the current client, in this case, the 'client' 138 | // variable 139 | console.log('Connected! subscribing'); 140 | socket.stratumSubscribe(); 141 | }); -------------------------------------------------------------------------------- /npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ 'node', '/usr/local/bin/npm', 'start' ] 3 | 2 info using npm@2.7.3 4 | 3 info using node@v0.12.1 5 | 4 verbose stack Error: missing script: start 6 | 4 verbose stack at run (/usr/local/lib/node_modules/npm/lib/run-script.js:165:19) 7 | 4 verbose stack at /usr/local/lib/node_modules/npm/lib/run-script.js:81:5 8 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:52:40 9 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:376:33 10 | 4 verbose stack at checkBinReferences_ (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:341:59) 11 | 4 verbose stack at final (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:373:17) 12 | 4 verbose stack at then (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:127:33) 13 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:332:40 14 | 4 verbose stack at evalmachine.:336:14 15 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:102:5 16 | 5 verbose cwd /home/bitnami/jsminer 17 | 6 error Linux 3.13.0-24-generic 18 | 7 error argv "node" "/usr/local/bin/npm" "start" 19 | 8 error node v0.12.1 20 | 9 error npm v2.7.3 21 | 10 error missing script: start 22 | 11 error If you need help, you may report this error at: 23 | 11 error 24 | 12 verbose exit [ 1, true ] 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsminer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "binascii": "0.0.1", 17 | "express": "^4.12.3", 18 | "sha.js": "^2.4.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/glminer.js: -------------------------------------------------------------------------------- 1 | var vShaderQuellcode; 2 | var fShaderQuellcode; 3 | 4 | var dataLoc; 5 | var hash1Loc; 6 | var midstateLoc; 7 | var targetLoc; 8 | var nonceLoc; 9 | 10 | var maxNonce = 0xFFFFFFFF; 11 | var maxCnt = 0; 12 | var reportPeriod = 1000; 13 | var useTimeout = true; 14 | var TotalHashes = 0; 15 | var gl; 16 | var canvas; 17 | var debug = false; 18 | var buf; 19 | 20 | var width; 21 | var height; 22 | 23 | function throwOnGLError(err, funcName, args) { 24 | throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to" + funcName; 25 | }; 26 | 27 | function meinWebGLStart(threads) { 28 | canvas = document.createElement('canvas'); 29 | if (debug || true) document.body.appendChild(canvas) 30 | canvas.height = 1; 31 | canvas.width = threads; 32 | 33 | var names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; 34 | for (var i=0; i= 0; r--) { 210 | if (buf[i + e] & 1 << r) { 211 | var b = (3 - e) * (nonces_per_pixel / 4) + r; 212 | job.nonce = nonce + i * (nonces_per_pixel / 4) + b; 213 | submit_nonce(); 214 | } 215 | } 216 | } 217 | 218 | job.golden_ticket = null; 219 | } 220 | } 221 | } 222 | 223 | if (nonce >= maxNonce) { 224 | cb(null); 225 | break; 226 | } 227 | 228 | nonce+= threads; 229 | TotalHashes += threads; 230 | 231 | if (t < (new Date()).getTime()) { 232 | t = (new Date()).getTime() + reportPeriod; 233 | job.total_hashes = TotalHashes; 234 | callback(job); 235 | TotalHashes = 0; 236 | } 237 | 238 | if (useTimeout && ++curCnt > maxCnt) { 239 | curCnt = 0; 240 | job.nonce = nonce; 241 | job.t = t; 242 | var c = function() { 243 | next_run(job, callback); 244 | } 245 | window.setTimeout(c, 1); 246 | return; 247 | } 248 | } 249 | } 250 | var intMessage = function(event) { 251 | if (!event.data || !event.data.run) { 252 | run = false; 253 | console.log("worker: forced quit!"); 254 | return; 255 | } 256 | }; 257 | 258 | var mine = function(job, callback) { 259 | 260 | gl.uniform2fv(dataLoc, job.data); 261 | gl.uniform2fv(hash1Loc, job.hash1); 262 | gl.uniform2fv(midstateLoc, job.midstate); 263 | gl.uniform2fv(targetLoc, job.target); 264 | 265 | width = canvas.width; 266 | height = canvas.height; 267 | 268 | buf = new Uint8Array(width * height * 4); 269 | 270 | next_run(job, callback); 271 | return intMessage; 272 | } 273 | 274 | var is_golden_hash = function(hash, target) { 275 | var u1 = derMiner.Util.ToUInt32(hash); 276 | var u2 = derMiner.Util.ToUInt32(target[6]); 277 | 278 | console.log("worker: checking " + u1 + " <= " + u2); 279 | return (u1 <= u2); 280 | } 281 | 282 | return mine(job, callback); 283 | }; 284 | -------------------------------------------------------------------------------- /public/miner.js: -------------------------------------------------------------------------------- 1 | 2 | var console = { log: function(m) { 3 | postMessage({ golden_ticket: false, print: m}); 4 | }}; 5 | 6 | var TotalHashes = 0; 7 | var useTimeout = false; 8 | try { 9 | importScripts('sha256.js'); 10 | importScripts('util.js'); 11 | } catch (e) { 12 | useTimeout = true; 13 | } 14 | 15 | var reportPeriod = 1000; 16 | var maxNonce = 0xFFFFFFFF; 17 | var maxCnt = 5; 18 | var run = true; 19 | 20 | function scanhash(job, progress_report, cb) { 21 | //var midstate = job.midstate; 22 | var data = job.data; 23 | //var hash1 = job.hash1; 24 | var target = job.target; 25 | 26 | var t = job.t === undefined ? 0 : job.t; 27 | var nonce = job.nonce === undefined ? 0 : job.nonce; 28 | var curCnt = 0; 29 | 30 | if (!run || nonce > 0xFFFFFFFF) return; 31 | 32 | while(run) { 33 | 34 | data[3] = nonce; 35 | /* 36 | sha256.reset(); 37 | var h1 = sha256.update(midstate, data).state; 38 | 39 | var h2 = hash1; 40 | for (var i=0; i<8; i++) h2[i] = h1[i]; 41 | sha256.reset(); 42 | 43 | var hash = sha256.update(h2).state; 44 | */ 45 | sha256.reset(); 46 | var hash = sha256.update(data).state; 47 | 48 | // console.log(derMiner.Util.toPoolString(hash)); 49 | if (is_golden_hash(hash, target)) { 50 | job.nonce = nonce; 51 | 52 | var r = []; 53 | for (var i = 0; i < job.half.length; i++) 54 | r.push(job.half[i]); 55 | for (var i = 0; i < job.data.length; i++) 56 | r.push(job.data[i]); 57 | 58 | var ret = derMiner.Util.toPoolString(r); 59 | cb(ret); 60 | job.golden_ticket = null; 61 | } 62 | 63 | if (nonce >= maxNonce) { 64 | cb(null); 65 | break; 66 | } 67 | 68 | TotalHashes++; 69 | nonce++; 70 | 71 | if (t < (new Date()).getTime()) { 72 | t = (new Date()).getTime() + reportPeriod; 73 | progress_report(); 74 | } 75 | 76 | if (useTimeout && ++curCnt > maxCnt) { 77 | curCnt = 0; 78 | job.nonce = nonce; 79 | job.t = t; 80 | var c = function() { 81 | scanhash(job, progress_report, cb); 82 | } 83 | window.setTimeout(c, 1); 84 | return; 85 | } 86 | } 87 | 88 | return; 89 | } 90 | 91 | function is_golden_hash(hash, target) 92 | { 93 | if (hash[7] == 0x00000000) { 94 | var u1 = derMiner.Util.ToUInt32(hash[6]); 95 | var u2 = derMiner.Util.ToUInt32(target[6]); 96 | 97 | console.log("worker: checking " + u1 + " <= " + u2); 98 | return (u1 <= u2); 99 | } 100 | return false; 101 | } 102 | 103 | ///// Web Worker ///// 104 | 105 | onmessage = function(event) { 106 | 107 | if (!event.data || !event.data.run) { 108 | run = false; 109 | console.log("worker: forced quit!"); 110 | return; 111 | } 112 | 113 | var job = event.data; 114 | job.golden_ticket = false; 115 | 116 | sendProgressUpdate(job); 117 | 118 | var result = function (golden_ticket) { 119 | job.golden_ticket = golden_ticket; 120 | postMessage(job); 121 | }; 122 | 123 | scanhash(event.data, function() { sendProgressUpdate(job); }, result); 124 | }; 125 | 126 | function sendProgressUpdate(job) 127 | { 128 | job.total_hashes = TotalHashes; 129 | 130 | postMessage(job); 131 | TotalHashes = 0; 132 | } -------------------------------------------------------------------------------- /public/sha256.js: -------------------------------------------------------------------------------- 1 | Sha256 = function(init, data) { 2 | 3 | var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 4 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 5 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 6 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 7 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 8 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 9 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 10 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; 11 | 12 | var H = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; 13 | 14 | var add = function (x, y) { 15 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 16 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 17 | return ((0xFFFF & msw) << 16) | (lsw & 0xFFFF); 18 | }; 19 | 20 | var add_all = function() { 21 | var sum = arguments[0]; 22 | for (var i = 1; i < arguments.length; i++) 23 | sum = add(sum, arguments[i]); 24 | return sum; 25 | }; 26 | 27 | var set_state = function(target, source) { 28 | for (var i = 0; i < 8; i++) 29 | target[i] = source[i]; 30 | }; 31 | 32 | var extend_work = function(work, w) { 33 | for (var i = 0; i < 16; i++) 34 | work[i] = w[i]; 35 | w = work; 36 | 37 | for (var i = 16; i < 64; i++) { 38 | var s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ shr(w[i - 15], 3); 39 | var s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ shr(w[i - 2], 10); 40 | w[i] = add_all(w[i-16], s0, w[i-7], s1); 41 | } 42 | return w; 43 | }; 44 | 45 | var rotr = function(x, n) { 46 | return (x >>> n) | (x << (32 - n)); 47 | }; 48 | 49 | var shr = function(x, n) { 50 | return (x >>> n); 51 | }; 52 | 53 | this.state = [0,0,0,0,0,0,0,0]; 54 | set_state(this.state, H); 55 | 56 | this.work = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 59 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 60 | 61 | this.hex = function() { 62 | return derMiner.Util.uint32_array_to_hex(this.state); 63 | }; 64 | 65 | this.reset = function() { 66 | set_state(this.state, H); 67 | return this; 68 | }; 69 | 70 | this.update = function(init, data) { 71 | if (!data) { data = init; init = null; } 72 | if (typeof(init) == 'string') 73 | init = derMiner.Util.hex_to_uint32_array(init); 74 | if (init) set_state(this.state, init); 75 | if (typeof(data) == 'string') 76 | data = derMiner.Util.hex_to_uint32_array(data); 77 | 78 | var w = extend_work(this.work, data); 79 | var s = this.state; 80 | 81 | var a = s[0], b = s[1], c = s[2], d = s[3], 82 | e = s[4], f = s[5], g = s[6], h = s[7]; 83 | 84 | for (var i = 0; i < 64; i++) { 85 | var s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); 86 | var maj = (a & b) ^ (a & c) ^ (b & c); 87 | var t2 = add(s0, maj); 88 | var s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); 89 | var ch = (e & f) ^ ((~e) & g); 90 | var t1 = add_all(h, s1, ch, K[i], w[i]); 91 | 92 | h = g; g = f; f = e; 93 | e = add(d, t1); 94 | d = c; c = b; b = a; 95 | a = add(t1, t2); 96 | } 97 | 98 | 99 | s[0] = add(s[0], a); 100 | s[1] = add(s[1], b); 101 | s[2] = add(s[2], c); 102 | s[3] = add(s[3], d); 103 | s[4] = add(s[4], e); 104 | s[5] = add(s[5], f); 105 | s[6] = add(s[6], g); 106 | s[7] = add(s[7], h); 107 | 108 | return this; 109 | }; 110 | 111 | if (init) this.update(init, data); 112 | }; 113 | 114 | sha256 = new Sha256(); -------------------------------------------------------------------------------- /public/shader-fs.js: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | /* 5 | 32-bit integers are represented by a vec2. GLSL 2 integers may only have up 6 | to 16-bit precision (in portable code), and they are likely to be implemented 7 | with floats anyway. Instead we use float-pairs, with 16-bit in each (although 8 | floats fit 24-bit precision). A vec4 is also used instead of two vec2, where 9 | possible. 10 | */ 11 | 12 | uniform vec2 data[16]; /* Second part of data */ 13 | uniform vec2 hash1[16]; /* Second part of hash1 */ 14 | uniform vec2 midstate[8]; 15 | uniform vec2 target[8]; 16 | uniform vec2 nonce_base; 17 | uniform vec2 H[8]; 18 | uniform vec2 K[64]; 19 | 20 | /* Note: N is the width of the buffer and should only be between 1 and 2048 or 21 | so. Preferably less -- around 128 or 256. */ 22 | uniform float N; 23 | 24 | #define Ox10000 65536.0 25 | #define Ox8000 32768.0 26 | 27 | vec4 toRGBA(vec2 arg) { 28 | float V = float(arg.x); 29 | float R = floor(V / pow(2.0, 8.0)); 30 | V -= R * pow(2.0, 8.0); 31 | float G = V; 32 | V = float(arg.y); 33 | float B = floor(V / pow(2.0, 8.0)); 34 | V -= B * pow(2.0, 8.0); 35 | float A = V; 36 | return vec4(R/255., G/255., B/255., A/255.); 37 | } 38 | 39 | vec4 toRGBA(float V) { 40 | float R = V / pow(2.0, 24.0); 41 | V -= floor(R) * pow(2.0, 24.0); 42 | float G = V / pow(2.0, 16.0); 43 | V -= floor(G) * pow(2.0, 16.0); 44 | float B = V / pow(2.0, 8.0); 45 | V -= floor(B) * pow(2.0, 8.0);; 46 | float A = V; 47 | return vec4(R/255., G/255., B/255., A/255.); 48 | } 49 | 50 | vec2 safe_add (vec2 a, vec2 b) 51 | { 52 | vec2 ret; 53 | ret.x = a.x + b.x; 54 | ret.y = a.y + b.y; 55 | if (ret.y >= float(Ox10000)) { 56 | ret.y -= float(Ox10000); 57 | ret.x += 1.0; 58 | } 59 | if (ret.x >= float(Ox10000)) { 60 | ret.x -= float(Ox10000); 61 | } 62 | return ret; 63 | } 64 | /* Note: shift should be a power of two, e.g. to shift 3 steps, use 2^3. */ 65 | vec2 sftr (vec2 a, float shift) 66 | { 67 | vec2 ret = a / shift; 68 | ret = vec2 (floor (ret.x), floor (ret.y) + fract (ret.x) * float (Ox10000)); 69 | return ret; 70 | } 71 | 72 | /* For rotr (>>) use division with appropriate power of 2. */ 73 | /* Note: shift should be a power of two, e.g. to rotate 3 steps, use 2^3. */ 74 | vec2 rotr (vec2 a, float shift) 75 | { 76 | vec2 ret = a / shift; 77 | ret = floor (ret) + fract (ret.yx) * float (Ox10000); 78 | return ret; 79 | } 80 | 81 | float axor16 (float a, float b) 82 | { 83 | float ret = float(0); 84 | float fact = float (Ox8000); 85 | const int maxi = 16; 86 | float v1, v2; 87 | 88 | for (int i=0; i < maxi; i++) 89 | { 90 | v1 = step(fact, a); 91 | v2 = step(fact, b); 92 | ret += (v1*(1.-v2) + v2*(1.-v1)) * fact; 93 | if (v1 == 1.) a -= fact; 94 | if (v2 == 1.) b -= fact; 95 | fact /= 2.0; 96 | } 97 | return ret; 98 | } 99 | 100 | float aand16 (float a, float b) 101 | { 102 | float ret = float(0); 103 | float fact = float (Ox8000); 104 | const int maxi = 16; 105 | float v1, v2; 106 | 107 | for (int i=0; i < maxi; i++) 108 | { 109 | v1 = step(fact, a); 110 | v2 = step(fact, b); 111 | ret += (v1*v2) * fact; 112 | if (v1 == 1.) a -= fact; 113 | if (v2 == 1.) b -= fact; 114 | fact /= 2.0; 115 | } 116 | return ret; 117 | } 118 | 119 | float xor16 (float a, float b) 120 | { 121 | float ret = float(0); 122 | float fact = float (Ox8000); 123 | const int maxi = 16; 124 | 125 | for (int i=0; i < maxi; i++) 126 | { 127 | if ((max(a,b) >= fact) && (min(a,b) < fact)) 128 | ret += fact; 129 | 130 | if (a >= fact) 131 | a -= fact; 132 | if (b >= fact) 133 | b -= fact; 134 | 135 | fact /= 2.0; 136 | } 137 | return ret; 138 | } 139 | 140 | vec2 xor (vec2 a, vec2 b) 141 | { 142 | return vec2 (xor16 (a.x, b.x), xor16 (a.y, b.y)); 143 | } 144 | 145 | float and16 (float a, float b) 146 | { 147 | float ret = float(0); 148 | float fact = float (Ox8000); 149 | const int maxi = 16; 150 | 151 | for (int i=0; i < maxi; i++) 152 | { 153 | if (min(a, b) >= fact) 154 | ret += fact; 155 | 156 | if (a >= fact) 157 | a -= fact; 158 | if (b >= fact) 159 | b -= fact; 160 | 161 | fact /= 2.0; 162 | } 163 | return ret; 164 | } 165 | 166 | vec2 and (vec2 a, vec2 b) 167 | { 168 | return vec2 (and16 (a.x, b.x), and16 (a.y, b.y)); 169 | } 170 | 171 | /* Logical complement ("not") */ 172 | vec2 cpl (vec2 a) 173 | { 174 | return vec2 (float (Ox10000), float (Ox10000)) - a - vec2(1.0, 1.0); 175 | } 176 | #define POW_2_01 2.0 177 | #define POW_2_02 4.0 178 | #define POW_2_03 8.0 179 | #define POW_2_06 64.0 180 | #define POW_2_07 128.0 181 | #define POW_2_09 512.0 182 | #define POW_2_10 1024.0 183 | #define POW_2_11 2048.0 184 | #define POW_2_13 8192.0 185 | 186 | vec2 blend (vec2 m16, vec2 m15, vec2 m07, vec2 m02) 187 | { 188 | vec2 s0 = xor (rotr (m15 , POW_2_07), xor (rotr (m15.yx, POW_2_02), sftr (m15, POW_2_03))); 189 | vec2 s1 = xor (rotr (m02.yx, POW_2_01), xor (rotr (m02.yx, POW_2_03), sftr (m02, POW_2_10))); 190 | return safe_add (safe_add (m16, s0), safe_add (m07, s1)); 191 | } 192 | 193 | vec2 e0 (vec2 a) 194 | { 195 | return xor (rotr (a, POW_2_02), xor (rotr (a, POW_2_13), rotr (a.yx, POW_2_06))); 196 | } 197 | 198 | vec2 e1 (vec2 a) 199 | { 200 | return xor (rotr (a, POW_2_06), xor (rotr (a, POW_2_11), rotr (a.yx, POW_2_09))); 201 | } 202 | 203 | vec2 ch (vec2 a, vec2 b, vec2 c) 204 | { 205 | return xor (and (a, b), and (cpl (a), c)); 206 | } 207 | 208 | vec2 maj (vec2 a, vec2 b, vec2 c) 209 | { 210 | return xor (xor (and (a, b), and (a, c)), and (b, c)); 211 | } 212 | 213 | void main () 214 | { 215 | const int nonces_per_pixel = 1; 216 | 217 | vec2 ret = vec2 (0., 0.); 218 | vec2 nonce; 219 | vec2 w[64]; 220 | vec2 hash[16]; 221 | vec2 tmp[8]; //state 222 | vec2 udata[16]; 223 | float carry; 224 | 225 | vec2 a, b, c, d, e, f, g, h; 226 | vec2 t1, t2; 227 | vec2 _s0,_maj,_t2,_s1,_ch, _t1; 228 | float x_off = floor(float(gl_FragCoord.x)); 229 | float nonce_off = x_off * float(nonces_per_pixel); 230 | 231 | for (int i=0; i<16; i++) { 232 | udata[i] = data[i]; 233 | } 234 | 235 | for (int n = 0; n < nonces_per_pixel; n++) { 236 | nonce = safe_add(vec2 (0., nonce_off + float(n)), nonce_base); 237 | 238 | udata[3] = nonce; 239 | 240 | // Reset 241 | for (int i=0; i<8; i++) { 242 | tmp[i] = H[i]; 243 | } 244 | // TODO: check if needed 245 | /*for (int i = 0; i < 64; i++) { 246 | w[i] = vec2(0., 0.); 247 | }*/ 248 | 249 | 250 | // update(midstate, udata) 251 | // set state(midstate) 252 | for (int i=0; i<8; i++) { 253 | tmp[i] = midstate[i]; 254 | } 255 | // extend work (w, udata) 256 | for (int i=0; i<16; i++) { 257 | w[i] = udata[i]; 258 | } 259 | 260 | for (int i = 16; i < 64; ++i) { 261 | w[i] = blend(w[i-16], w[i-15], w[i-7], w[i-2]); 262 | } 263 | 264 | // var s = this.state; 265 | 266 | a = tmp[0]; 267 | b = tmp[1]; 268 | c = tmp[2]; 269 | d = tmp[3]; 270 | e = tmp[4]; 271 | f = tmp[5]; 272 | g = tmp[6]; 273 | h = tmp[7]; 274 | 275 | for (int i = 0; i < 64; i++) { 276 | _s0 = e0(a); 277 | _maj = maj(a,b,c); 278 | _t2 = safe_add(_s0, _maj); 279 | _s1 = e1(e); 280 | _ch = ch(e, f, g); 281 | _t1 = safe_add(safe_add(safe_add(safe_add(h, _s1), _ch), K[i]), w[i]); 282 | 283 | h = g; g = f; f = e; 284 | e = safe_add(d, _t1); 285 | d = c; c = b; b = a; 286 | a = safe_add(_t1, _t2); 287 | } 288 | 289 | tmp[0] = safe_add(a, tmp[0]); 290 | tmp[1] = safe_add(b, tmp[1]); 291 | tmp[2] = safe_add(c, tmp[2]); 292 | tmp[3] = safe_add(d, tmp[3]); 293 | tmp[4] = safe_add(e, tmp[4]); 294 | tmp[5] = safe_add(f, tmp[5]); 295 | tmp[6] = safe_add(g, tmp[6]); 296 | tmp[7] = safe_add(h, tmp[7]); 297 | 298 | for (int i = 0; i < 8; i++) { 299 | hash[i] = tmp[i]; 300 | } 301 | for (int i = 8; i < 16; i++) { 302 | hash[i] = hash1[i]; 303 | } 304 | 305 | // Reset 306 | for (int i=0; i<8; i++) { 307 | tmp[i] = H[i]; 308 | } 309 | // TODO: check if needed 310 | /* for (int i = 0; i < 64; i++) { 311 | w[i] = vec2(0., 0.); 312 | } */ 313 | 314 | // update(hash) 315 | // extend work (w, hash) 316 | for (int i=0; i<16; i++) { 317 | w[i] = hash[i]; 318 | } 319 | for (int i = 16; i < 64; ++i) { 320 | w[i] = blend(w[i-16], w[i-15], w[i-7], w[i-2]); 321 | } 322 | 323 | // var s = this.state; 324 | a = tmp[0]; 325 | b = tmp[1]; 326 | c = tmp[2]; 327 | d = tmp[3]; 328 | e = tmp[4]; 329 | f = tmp[5]; 330 | g = tmp[6]; 331 | h = tmp[7]; 332 | 333 | for (int i = 0; i < 64; i++) { 334 | _s0 = e0(a); 335 | _maj = maj(a,b,c); 336 | _t2 = safe_add(_s0, _maj); 337 | _s1 = e1(e); 338 | _ch = ch(e, f, g); 339 | _t1 = safe_add(safe_add(safe_add(safe_add(h, _s1), _ch), K[i]), w[i]); 340 | 341 | h = g; g = f; f = e; 342 | e = safe_add(d, _t1); 343 | d = c; c = b; b = a; 344 | a = safe_add(_t1, _t2); 345 | } 346 | 347 | // tmp[0] = safe_add(a, tmp[0]); 348 | // tmp[1] = safe_add(b, tmp[1]); 349 | // tmp[2] = safe_add(c, tmp[2]); 350 | // tmp[3] = safe_add(d, tmp[3]); 351 | // tmp[4] = safe_add(e, tmp[4]); 352 | // tmp[5] = safe_add(f, tmp[5]); 353 | // tmp[6] = safe_add(g, tmp[6]); 354 | tmp[7] = safe_add(h, tmp[7]); 355 | 356 | if (nonces_per_pixel != 1 && tmp[7].x == 0. && tmp[7].y == 0.) { 357 | if (n <= 15) { 358 | ret = safe_add(ret, vec2(0., pow(2.0, float(n)))); 359 | } else { 360 | ret = safe_add(ret, vec2(pow(2.0, float(n - 16)), 0.)); 361 | } 362 | // } else { 363 | // gl_FragColor = vec4(1., 1., 1., 1.); 364 | } 365 | 366 | /* TODO: Compare with target. */ 367 | } 368 | if (nonces_per_pixel == 1) { 369 | gl_FragColor = toRGBA( tmp[7]); 370 | } else { 371 | gl_FragColor = toRGBA(ret); 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /public/shader-vs.js: -------------------------------------------------------------------------------- 1 | attribute vec2 vPos; 2 | void main(void) { 3 | gl_Position = vec4(vPos, 0., 1.); 4 | } 5 | -------------------------------------------------------------------------------- /public/util.js: -------------------------------------------------------------------------------- 1 | if (typeof(derMiner) == 'undefined') 2 | var derMiner = {}; 3 | 4 | derMiner.Util = { 5 | hex_to_uint32_array: function(hex) { 6 | var arr = []; 7 | for (var i = 0, l = hex.length; i < l; i += 8) { 8 | arr.push((parseInt(hex.substring(i, i+8), 16))); 9 | } 10 | return arr; 11 | }, 12 | hex_to_uint16_array: function(hex) { 13 | var arr = []; 14 | for (var i = 0, l = hex.length; i < l; i += 4) { 15 | arr.push((parseInt(hex.substring(i, i+4), 16))); 16 | } 17 | return arr; 18 | }, 19 | uint32_array_to_hex: function(arr) { 20 | var hex = ''; 21 | for (var i = 0; i < arr.length; i++) { 22 | hex += derMiner.Util.byte_to_hex(arr[i] >>> 24); 23 | hex += derMiner.Util.byte_to_hex(arr[i] >>> 16); 24 | hex += derMiner.Util.byte_to_hex(arr[i] >>> 8); 25 | hex += derMiner.Util.byte_to_hex(arr[i] ); 26 | } 27 | return hex; 28 | }, 29 | uint16_array_to_hex: function(arr) { 30 | var hex = ''; 31 | for (var i = 0; i < arr.length; i++) { 32 | hex += derMiner.Util.byte_to_hex(arr[i] >>> 8); 33 | hex += derMiner.Util.byte_to_hex(arr[i] ); 34 | } 35 | return hex; 36 | }, 37 | to_uint16_array: function(w) { 38 | return [(w & 0xffff0000) >> 16, (w & 0x0000ffff) ]; 39 | }, 40 | byte_to_hex: function(b) { 41 | var tab = '0123456789abcdef'; 42 | b = b & 0xff; 43 | return tab.charAt(b / 16) + 44 | tab.charAt(b % 16); 45 | }, 46 | reverseBytesInWord: function(w) { 47 | return ((w << 24) & 0xff000000) | 48 | ((w << 8) & 0x00ff0000) | 49 | ((w >>> 8) & 0x0000ff00) | 50 | ((w >>> 24) & 0x000000ff); 51 | }, 52 | reverseBytesInInt: function(w) { 53 | return ((w << 8) & 0x0000ff00 | 54 | (w >> 8) & 0x000000ff); 55 | }, 56 | reverseBytesInWords: function(words) { 57 | var reversed = []; 58 | for(var i = 0; i < words.length; i++) 59 | reversed.push(derMiner.Util.reverseBytesInWord(words[i])); 60 | return reversed; 61 | }, 62 | reverseBytesInInts: function(words) { 63 | var reversed = []; 64 | for(var i = 0; i < words.length-1; i+=2) { 65 | reversed.push(derMiner.Util.reverseBytesInInt(words[i+1])); 66 | reversed.push(derMiner.Util.reverseBytesInInt(words[i])); 67 | } 68 | return reversed; 69 | }, 70 | fromPoolString: function(hex, gl) { 71 | return gl 72 | ? derMiner.Util.reverseBytesInInts(derMiner.Util.hex_to_uint16_array(hex)) 73 | : derMiner.Util.reverseBytesInWords(derMiner.Util.hex_to_uint32_array(hex)); 74 | }, 75 | toPoolString: function(data, gl) { 76 | return gl 77 | ? derMiner.Util.uint16_array_to_hex(derMiner.Util.reverseBytesInInts(data)) 78 | : derMiner.Util.uint32_array_to_hex(derMiner.Util.reverseBytesInWords(data)); 79 | }, 80 | ToUInt32: function (x) { 81 | return x >>> 0; 82 | } 83 | }; -------------------------------------------------------------------------------- /public/work-manager.js: -------------------------------------------------------------------------------- 1 | var console = window.console ? window.console : { 2 | log: function() {} 3 | }; 4 | var worker = null; 5 | var testmode = false; 6 | var repeat_to = null; 7 | var use_to = 0; // 5; 8 | var no_cache = false; 9 | var init = false; 10 | var start = null; 11 | var id = 1; 12 | // use this in case we can directly connect to a given pool 13 | // var _url = 'http://' + g_user + ':' + g_password + '@' + g_url + ':' + g_port; 14 | var _url = '/work'; 15 | 16 | function readScript(n) { 17 | var xhr = new XMLHttpRequest(); 18 | xhr.open("GET", n, false); 19 | xhr.send(null); 20 | var x = xhr.responseText; 21 | return x; 22 | }; 23 | 24 | function onError(data) { 25 | $('#info').val(data.status + " " + data.responseText); 26 | } 27 | 28 | function onSuccess(jsonresp) { 29 | if (worker) { 30 | try { 31 | worker.postMessage({ 32 | run: false 33 | }); 34 | worker.terminate(); 35 | } 36 | catch (e) {} 37 | } 38 | id = Number(jsonresp.id) + 1; 39 | var response = jsonresp.result; 40 | var data = JSON.stringify(response); 41 | $('#info').val(data); 42 | var type = $('[type=radio]'); 43 | if (type.length == 0) type = [{ 44 | checked: true 45 | }, { 46 | checked: false 47 | }, { 48 | checked: false 49 | }] 50 | var job = {}; 51 | var gl = type[2].checked 52 | job.run = true; 53 | job.work = data; 54 | //job.midstate = derMiner.Util.fromPoolString(response.midstate, gl); 55 | //job.half = derMiner.Util.fromPoolString(response.data.substr(0, 128), gl); 56 | job.data = derMiner.Util.fromPoolString(response.data.substr(128, 256), gl); 57 | //job.hash1 = derMiner.Util.fromPoolString(response.hash1, gl); 58 | job.target = derMiner.Util.fromPoolString(response.target, gl); 59 | var t = derMiner.Util.ToUInt32(derMiner.Util.fromPoolString(response.target, false)[6]); 60 | var d = (4273753909.69051265) / t; 61 | $('#target').val(t + "/" + d.toFixed(3)); 62 | if (testmode) { 63 | job.nonce = derMiner.Util.fromPoolString("204e2e35")[0]; 64 | } 65 | else { 66 | job.nonce = Math.floor(Math.random() * 0xFFFFFFFF); 67 | } 68 | job.hexdata = response.data; 69 | if (type[2].checked) { 70 | var postMessage = function(m) { 71 | onWorkerMessage({ 72 | data: m 73 | }); 74 | } 75 | var th = $('#threads')[0].value; 76 | if (!init) meinWebGLStart(th); 77 | worker = { 78 | postMessage: function(m) { 79 | worker.intMessage({ 80 | data: m 81 | }) 82 | }, 83 | intMessage: glminer(job, postMessage) 84 | }; 85 | } 86 | else if (type[0].checked) { 87 | var postMessage = function(m) { 88 | onWorkerMessage({ 89 | data: m 90 | }); 91 | } 92 | worker = { 93 | postMessage: function(m) { 94 | worker.intMessage({ 95 | data: m 96 | }); 97 | }, 98 | intMessage: function() {} 99 | }; 100 | var m = readScript('/public/miner.js'); 101 | var s = '(function() {' + m + ';\n' + 'onmessage({ data: job });' + ' worker.intMessage = onmessage; })'; 102 | var run = eval(s); 103 | run(); 104 | } 105 | else { 106 | worker = new Worker("/public/miner.js"); 107 | worker.onmessage = onWorkerMessage; 108 | worker.onerror = onWorkerError; 109 | worker.postMessage(job); 110 | } 111 | init = true; 112 | } 113 | 114 | function begin_mining() { 115 | var tm = $('#testmode'); 116 | testmode = tm.length > 0 && tm[0].checked; 117 | //testmode = true; 118 | start = (new Date()).getTime(); 119 | if (testmode) { 120 | var dd = '{"midstate":"eae773ad01907880889ac5629af0c35438376e8c4ae77906301c65fa89c2779c","data":"0000000109a78d37203813d08b45854d51470fcdb588d6dfabbe946e92ad207e0000000038a8ae02f7471575aa120d0c85a10c886a1398ad821fadf5124c37200cb677854e0603871d07fff800000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000","hash1":"00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000","target":"0000000000000000000000000000000000000000000000000000f8ff07000000", "sol" : "31952e35"}'; // near match with nonce = 0 121 | onSuccess({ 122 | result: JSON.parse(dd) 123 | }); 124 | } 125 | else { 126 | $.get(_url, onSuccess, "text json"); 127 | /* 128 | if (use_to) { 129 | var enqueuMiner = function() { 130 | get_work(); 131 | repeat_to = window.setTimeout(enqueuMiner, use_to * 1000); 132 | }; 133 | repeat_to = window.setTimeout(enqueuMiner, 1000); 134 | } else { 135 | get_work(true); 136 | long_poll(); 137 | } 138 | */ 139 | } 140 | } 141 | var long_poll_suc = null; 142 | 143 | function long_poll() { 144 | var done = function(resp) { 145 | if (resp.result || long_poll_suc) { 146 | long_poll_suc = true; 147 | if (resp.result) onSuccess(resp); 148 | long_poll(); 149 | } 150 | else if (long_poll_suc === null) { 151 | console.log('Stop polling!!!!'); 152 | long_poll_suc = false; 153 | window.setInterval(get_work, 3 * 60 * 1000); 154 | } 155 | }; 156 | $.ajax({ 157 | url: _url + "/long-polling" + (no_cache ? "?cache=0&ts=" + (new Date().getTime()) : ''), 158 | data: '{ "method": "long-poll", "id": "' + id + ' ", "params": [] }', 159 | type: "POST", 160 | headers: {}, 161 | success: done, 162 | error: done, 163 | dataType: "json" 164 | }); 165 | } 166 | 167 | function get_work() { 168 | $.post(_url + (no_cache ? "?cache=0&ts=" + (new Date().getTime()) : ''), '{ "method": "getwork", "id": "' + id + '", "params": [] }', onSuccess, "text json"); 169 | } 170 | 171 | function onWorkerMessage(event) { 172 | var job = event.data; 173 | if (job.print) console.log('worker:' + job.print); 174 | if (job.golden_ticket) { 175 | if (job.nonce) console.log("nonce: " + job.nonce); 176 | $('#golden-ticket').val(job.golden_ticket); 177 | $.get("/submit?nonce=" + job.nonce); 178 | if (repeat_to) { 179 | window.clearTimeout(repeat_to); 180 | } 181 | } 182 | if (!job.total_hashes) job.total_hashes = 1; 183 | var total_time = ((new Date().getTime()) - start) / 1000; 184 | var total_hashed = job.total_hashes + Number($('#total-hashes').val()); 185 | var hashes_per_second = total_hashed / (total_time + 1); 186 | $('#total-hashes').val(total_hashed); 187 | var old = Number($('#hashes-per-second').val()); 188 | if (old == "NaN" || old == "Infinity") old = 0; 189 | $('#hashes-per-second').val(Math.round((old + hashes_per_second) / 2)); 190 | } 191 | 192 | function onWorkerError(event) { 193 | throw event.data; 194 | } 195 | window.onload = function() { 196 | onl(); 197 | // try { 198 | var d = document.createElement('div'); 199 | d.setAttribute('style', 'display:none'); 200 | var add = false; 201 | var arr = ["total-hashes", "hashes-per-second", "golden-ticket", "info"]; 202 | for (var i = 0; i < arr.length; i++) { 203 | var n = arr[i]; 204 | var l = document.getElementById(n); 205 | if (!l) { 206 | var e = document.createElement('input'); 207 | d.appendChild(e); 208 | add = true; 209 | } 210 | else { 211 | l.value = ""; 212 | } 213 | } 214 | if (add) { 215 | document.body.appendChild(d); 216 | } 217 | // } catch (e) { 218 | // console.log("manager:" + e); 219 | // } 220 | } --------------------------------------------------------------------------------