Skip to content

Instantly share code, notes, and snippets.

@ohac
Last active October 31, 2017 13:54
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 ohac/d2375c17f55f0f81e051af824acd2c86 to your computer and use it in GitHub Desktop.
Save ohac/d2375c17f55f0f81e051af824acd2c86 to your computer and use it in GitHub Desktop.
worker_all.js
foo.html
foo.js
foo.wasm
#!/bin/bash
emcc foo.c -O3 -s WASM=1 -s EXPORTED_FUNCTIONS="['_sha256d_str']" -o foo.html
0.0.0.0:8080 {
root ./
proxy /proxy localhost:8088 {
websocket
max_fails 1
}
ext .html
}
#!/bin/bash
cat em.js foo.js worker.js > worker_all.js
var Module = {
print: function() {
return function(text) {
if (arguments.length > 1) {
text = Array.prototype.slice.call(arguments).join(' ');
}
console.log(text);
};
},
printErr: function(text) {
if (arguments.length > 1) {
text = Array.prototype.slice.call(arguments).join(' ');
}
console.error(text);
},
};
const char* sha256d_str(const char *datastr) {
static char rv[64 + 1];
#if 0
unsigned char buff[256]; // TODO
unsigned char buff2[32];
size_t len = strlen(datastr) / 2;
hex2bin(buff, datastr, len);
sha256d(buff2, buff, len);
bin2hex(rv, buff2, 32);
#else
rv[0] = 'a';
rv[1] = 0;
#endif
return rv;
}
<!doctype html>
<html lang="en">
<head>
<title>Web Miner</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<main role="main" class="container">
<h1>Web Miner</h1>
<div class="form-group">
<label for="host">Hostname</label>
<input id="host" type="text" class="form-control" placeholder="Hostname" aria-label="Hostname" aria-describedby="basic-addon1">
</div>
<div class="form-group">
<label for="port">Port number</label>
<input id="port" type="text" class="form-control" placeholder="Port number" aria-label="Port number" aria-describedby="basic-addon1">
</div>
<div class="form-group">
<label for="username">Username</label>
<input id="username" type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password">
</div>
<div class="form-group">
<label for="algo">Algorithm</label>
<select id="algo" class="form-control">
<option>yescrypt</option>
</select>
</div>
<div class="form-group">
<label for="threads">Threads</label>
<select id="threads" class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
</select>
</div>
<div class="form-group">
<button id="start" type="button" class="btn btn-primary">Start</button>
</div>
<div id="connected" class="alert alert-info" role="alert" style="display: none">
Connected
</div>
<div id="disconnected" class="alert alert-danger" role="alert" style="display: none">
Disconnected
</div>
<div id="message" class="alert alert-info" role="alert" style="display: none">
Got message
</div>
<div id="error" class="alert alert-danger" role="alert" style="display: none">
Error
</div>
<div id="result"></div>
</main>
<script src="//code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<script src="/miner.js" type="text/javascript"></script>
</body>
</html>
$(function(){
if (true) { // debug code
var worker = new Worker('worker_all.js');
worker.onmessage = function(e) {
var result = e.data;
console.log('recv from worker: ' + result);
}
setTimeout(function(){
var work = {};
work['jobid'] = '7b61';
work['clean'] = false;
work['prevhash'] = '1e924c35bc128651ad5618755c3ce078' +
'e20896b652575e3411106f740000000b';
work['diff'] = 0.2;
work['coinb1'] = '010000000100000000000000000000000000000000000000' +
'00000000000000000000000000ffffffff270332c80f062f' +
'503253482f04d7e2f55908';
work['coinb2'] = '0d2f6e6f64655374726174756d2f0000000001806e877401' +
'0000001976a9143321f2d17da1a0f064ccaa8fea08d50b46' +
'c38ed888ac00000000';
work['xnonce1'] = '07ffb0ec';
work['merkles'] = [];
work['version'] = '00000002';
work['nbits'] = '1d1c8031';
work['xnonce2len'] = 4;
work['xnonce2'] = '00000000';
work['ntime'] = '59f5e2d7';
work['nonce'] = 0x1b00; // expected nonce: 0x1b8a
worker.postMessage(work);
}, 1000); // wait for main of foo.c
}
$('#start').click(function(){
var auth = false;
const workers = [];
var ws = new WebSocket('ws://localhost:8080/proxy');
ws.onopen = function(ev) {
console.log('open');
$('.alert').hide();
$('#connected').show();
var msg = {"id": 0, "method": "proxy.connect", "params": []};
msg.params[0] = $('#host').val();
msg.params[1] = $('#port').val();
ws.send(JSON.stringify(msg) + "\n");
auth = false;
msg = {"id": 1, "method": "mining.subscribe", "params": []};
var user_agent = 'webminer/0.1';
var session_id = null;
msg.params[0] = user_agent;
if (session_id) {
msg.params[1] = session_id;
}
ws.send(JSON.stringify(msg) + "\n");
};
ws.onclose = function(ev) {
console.log('close');
$('.alert').hide();
$('#disconnected').show();
};
var work = {};
ws.onmessage = function(ev) {
console.log('message: ' + ev.data);
$('.alert').hide();
$('#message').show();
var doauth = false;
var json = JSON.parse(ev.data);
var result = json.result;
if (result) {
var res0 = result[0];
if (json.id == 1) {
// for bunnymining.work
var res00 = res0[0];
if (res00 == 'mining.notify') {
var sessionid = res0[1];
var xnonce1 = result[1];
var xnonce2len = result[2];
work['sessionid'] = sessionid;
work['xnonce1'] = xnonce1;
work['xnonce2len'] = xnonce2len;
console.log('mining.mining.notify 1: ' + work);
doauth = true;
}
// for jp.lapool.me
var res000 = res00[0];
if (res000 == 'mining.set_difficulty') {
var xnonce1 = result[1];
var xnonce2len = result[2];
work['xnonce1'] = xnonce1;
work['xnonce2len'] = xnonce2len;
console.log('mining.mining.notify 1: ' + work);
doauth = true;
}
}
}
if (json.id == 4) {
console.log('yay!?');
}
var method = json.method;
var params = json.params;
if (json.id == null) {
if (method == 'mining.set_difficulty') {
var diff = params[0];
console.log('mining.set_difficulty: ' + diff);
work['diff'] = diff;
}
else if (method == 'mining.notify') {
work['jobid'] = params[0];
work['prevhash'] = params[1];
work['coinb1'] = params[2];
work['coinb2'] = params[3];
work['merkles'] = params[4];
work['version'] = params[5];
work['nbits'] = params[6];
work['ntime'] = params[7];
work['clean'] = params[8];
console.log('mining.notify 2: ' + work);
for (var i = 0; i < $('#threads').val(); i++) {
var worker = workers[i];
if (worker) {
worker.terminate();
}
worker = new Worker('worker_all.js');
workers[i] = worker;
worker.onmessage = function(e) {
var result = e.data;
console.log('recv from worker: ' + result);
var xnonce2 = result[0];
var nonce = result[1];
var username = $('#username').val();
var msg = {"id": 4, "method": "mining.submit",
"params": [username, work.jobid, xnonce2, work.ntime, nonce]
};
ws.send(JSON.stringify(msg) + "\n");
work['nonce'] = parseInt(nonce, 16) + 1;
console.log('restart nonce', work['nonce']);
worker.postMessage($.extend({}, work));
}
}
setTimeout(function(){
for (var i = 0; i < $('#threads').val(); i++) {
work['nonce'] = 0x10000000 * i;
console.log('start nonce', work['nonce']);
worker.postMessage($.extend({}, work));
}
}, 100); // TODO wait for main of foo.c
}
}
if (!auth && doauth) {
auth = true;
msg = {"id": 2, "method": "mining.authorize", "params": []};
msg.params[0] = $('#username').val();
msg.params[1] = $('#password').val();
ws.send(JSON.stringify(msg) + "\n");
}
};
ws.onerror = function(ev) {
console.log('error');
$('.alert').hide();
$('#error').show();
for (var i = 0; i < workers.length; i++) {
var worker = workers[i];
if (worker) {
worker.postMessage('stop');
workers[i] = null;
}
}
};
return false;
});
});
var miner = function(work){
var sha256d_str2 = Module.cwrap('sha256d_str2', 'string',
['string', 'string', 'string', 'string', 'string']);
var miner_thread2 = Module.cwrap('miner_thread2', 'string',
['string', 'string', 'number']);
console.log('worker: running');
if (work.jobid) {
var xnonce2 = '';
for (var i = 0; i < work.xnonce2len; i++) {
xnonce2 += '00';
}
var merklestr = '';
for (var i = 0; i < work.merkles.length; i++) {
merklestr += work.merkles[i];
}
var merkleroot = sha256d_str2(
work.coinb1,
work.xnonce1,
xnonce2,
work.coinb2,
merklestr);
console.log('worker: merkleroot = ' + merkleroot);
// work.sessionid
// work.diff
// work.jobid
// work.clean
var nonce = '00000000';
var blockheader0 =
work.version +
work.prevhash +
merkleroot +
work.ntime +
work.nbits;
var blockheader = blockheader0 + nonce;
console.log('worker: blockheader = ' + blockheader);
var nonce_and_hash = miner_thread2(blockheader, work.diff.toString(),
work.nonce);
console.log('worker: found? ' + nonce_and_hash);
postMessage([xnonce2, nonce_and_hash.split(',')[0]]);
}
};
self.addEventListener('message', (message) => {
var data = message.data;
console.log(data);
if (data == 'stop') {
console.log('worker: stop');
self.close();
}
var doit = false;
if (data == 'debug') { doit = true; }
if (data.jobid) { doit = true; }
if (doit) {
miner(data);
}
});
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"github.com/gorilla/websocket"
"log"
"net"
"net/http"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type Command struct {
Id int `json:"id"`
Method string `json:"method"`
Params []string `json:"params"`
}
func proxysender(c *websocket.Conn, conn *net.TCPConn, cmdq chan string) {
mt := websocket.TextMessage
for {
cmd := <-cmdq
fmt.Println("get from command queue:", cmd)
if cmd == "stop" {
break
}
err := c.WriteMessage(mt, []byte(cmd))
if err != nil {
fmt.Println("ws:", err)
break
}
var command Command
json.Unmarshal([]byte(cmd), &command)
method := command.Method
if method == "mining.subscribe" || method == "mining.authorize" || method == "mining.submit" {
fmt.Println("write to pool:", cmd)
conn.Write([]byte(cmd))
}
}
conn.Close()
}
func proxyreceiver(conn *net.TCPConn, cmdq chan string) {
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
t := scanner.Text()
fmt.Println("from pool:", t)
cmdq <- t
}
if err := scanner.Err(); err != nil {
fmt.Println("from pool:", err)
return
}
cmdq <- "stop"
}
func proxyconnect(host, port string) (conn *net.TCPConn) {
dest, err := net.ResolveTCPAddr("tcp", host+":"+port)
if err != nil {
fmt.Println("resolve:", err)
return
}
src := new(net.TCPAddr)
var err2 error
conn, err2 = net.DialTCP("tcp", src, dest)
if err2 != nil {
fmt.Println("dial:", err2)
return
}
fmt.Println("dial ok!")
return
}
func proxymain(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
cmdq := make(chan string, 10)
for {
_, jsonstr, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
cmdq <- "stop"
break
}
log.Printf("recv: %s", jsonstr)
var command Command
json.Unmarshal(jsonstr, &command)
fmt.Println(command)
if command.Method == "proxy.connect" && len(command.Params) == 2 {
host := command.Params[0]
port := command.Params[1]
conn := proxyconnect(host, port)
if conn != nil {
go proxysender(c, conn, cmdq)
go proxyreceiver(conn, cmdq)
}
} else {
cmdq <- string(jsonstr)
}
}
}
func main() {
flag.Parse()
log.SetFlags(0)
http.HandleFunc("/proxy", proxymain)
addr := flag.String("addr", "localhost:8088", "http service address")
log.Fatal(http.ListenAndServe(*addr, nil))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment