add browser example.
This commit is contained in:
parent
a9dce51b3a
commit
dc436d10e2
282
browser/index.html
Normal file
282
browser/index.html
Normal file
@ -0,0 +1,282 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>bcoin</title>
|
||||
<style>
|
||||
html { height: 100%; }
|
||||
body { height: 90%; padding: 20px; }
|
||||
h1 {
|
||||
font: 3em monospace;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
small {
|
||||
margin: 10px;
|
||||
width: 50%;
|
||||
display: block;
|
||||
}
|
||||
.log {
|
||||
padding: 5px;
|
||||
margin-left: 10px;
|
||||
overflow-y: scroll;
|
||||
border: 1px solid black;
|
||||
white-space: pre-wrap;
|
||||
height: 40%;
|
||||
width: 40%;
|
||||
font: 1em monospace;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.wallet {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
.send {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
#newaddr{
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.tx {
|
||||
float: right;
|
||||
font: 1em monospace;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.floating {
|
||||
font: 1em monospace;
|
||||
white-space: pre-wrap;
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding: 5px;
|
||||
background: white;
|
||||
border: 1px solid black;
|
||||
width: 40%;
|
||||
height: 30%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -20%;
|
||||
margin-top: -15%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<script src="/bcoin.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>BCoin, the browser full node</h1>
|
||||
<small>Welcome. Your machine is currently validating the segnet4 blockchain.
|
||||
The blocks and wallet are stored on your local disk with indexed DB. You are
|
||||
connecting to the actual bitcoin P2P network via a websocket->tcp proxy.
|
||||
Enjoy. (See the <a href="https://github.com/bcoin-org/bcoin">bcoin repo</a> for
|
||||
more bitcoin magic).</small>
|
||||
<div class="tx">
|
||||
Last 20 Blocks/TXs:
|
||||
<div id="tx"></div>
|
||||
</div>
|
||||
<div id="log" class="log"></div>
|
||||
<div id="wallet" class="wallet"></div>
|
||||
<form id="send" class="send" action="#">
|
||||
<input type="text" name="address" id="address" placeholder="Address">
|
||||
<input type="text" name="amount" id="amount" placeholder="Amount (BTC)">
|
||||
<input type="submit" value="Send">
|
||||
</form>
|
||||
<input type="button" id="newaddr" value="New Address">
|
||||
<div id="floating" class="floating"></div>
|
||||
<script>
|
||||
;(function() {
|
||||
var utils = bcoin.utils;
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
var log = document.getElementById('log');
|
||||
var wdiv = document.getElementById('wallet');
|
||||
var tdiv = document.getElementById('tx');
|
||||
var floating = document.getElementById('floating');
|
||||
var send = document.getElementById('send');
|
||||
var newaddr = document.getElementById('newaddr');
|
||||
var items = [];
|
||||
var scrollback = 0;
|
||||
var logger, node;
|
||||
|
||||
body.onmouseup = function() {
|
||||
floating.style.display = 'none';
|
||||
};
|
||||
|
||||
floating.onmouseup = function(ev) {
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
function show(obj) {
|
||||
floating.innerHTML = escape(utils.inspectify(obj, false));
|
||||
floating.style.display = 'block';
|
||||
}
|
||||
|
||||
logger = {
|
||||
debug: function(args) {
|
||||
if (++scrollback > 1000) {
|
||||
log.innerHTML = '';
|
||||
scrollback = 1;
|
||||
}
|
||||
var msg = utils.format(args, false);
|
||||
log.innerHTML += '<span style="color:blue;">' + utils.now() + '</span> ';
|
||||
log.innerHTML += escape(msg) + '\n';
|
||||
log.scrollTop = log.scrollHeight;
|
||||
},
|
||||
error: function(err) {
|
||||
if (++scrollback > 1000) {
|
||||
log.innerHTML = '';
|
||||
scrollback = 1;
|
||||
}
|
||||
var msg = (err.message + '').replace(/^ *Error: */, '');
|
||||
log.innerHTML += '<span style="color:blue;">' + utils.now() + '</span> ';
|
||||
log.innerHTML += '<span style="color:red;">[Error]</span> ';
|
||||
log.innerHTML += escape(msg) + '\n';
|
||||
log.scrollTop = log.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
send.onsubmit = function(ev) {
|
||||
var value = document.getElementById('amount').value;
|
||||
var address = document.getElementById('address').value;
|
||||
value = utils.satoshi(value);
|
||||
node.wallet.createTX({}, [{address: address,value:value}], function(err, tx) {
|
||||
if (err)
|
||||
return bcoin.error(err);
|
||||
node.wallet.sign(tx, function(err) {
|
||||
if (err)
|
||||
return bcoin.error(err);
|
||||
node.sendTX(tx, function(err) {
|
||||
if (err)
|
||||
return bcoin.error(err);
|
||||
show(tx);
|
||||
});
|
||||
});
|
||||
});
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
newaddr.onmouseup = function() {
|
||||
node.wallet.createReceive(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
formatWallet(node.wallet);
|
||||
});
|
||||
};
|
||||
|
||||
function kb(size) {
|
||||
size /= 1000;
|
||||
return size.toFixed(2) + 'kb';
|
||||
}
|
||||
|
||||
function create(html) {
|
||||
var el = document.createElement('div');
|
||||
el.innerHTML = html;
|
||||
return el.firstChild;
|
||||
}
|
||||
|
||||
function escape(html, encode) {
|
||||
return html
|
||||
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function addItem(tx) {
|
||||
var el;
|
||||
|
||||
if (items.length === 20) {
|
||||
el = items.shift();
|
||||
tdiv.removeChild(el);
|
||||
}
|
||||
|
||||
el = create('<a style="display:block;" href="#'
|
||||
+ tx.rhash + '">' + tx.rhash + ' (' + tx.height
|
||||
+ ' - ' + kb(tx.getSize()) + ')</a>');
|
||||
tdiv.appendChild(el);
|
||||
|
||||
el.onmouseup = function(ev) {
|
||||
show(tx);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
items.push(el);
|
||||
}
|
||||
|
||||
function formatWallet(wallet) {
|
||||
var html = '';
|
||||
var key = wallet.master.toJSON();
|
||||
html += '<b>Wallet</b><br>';
|
||||
html += 'Current Address (p2wpkh): <b>' + wallet.getAddress() + '</b><br>';
|
||||
html += 'Current Address (p2wpkh behind p2sh): <b>' + wallet.getProgramAddress() + '</b><br>';
|
||||
html += 'Extended Private Key: <b>' + key.xprivkey + '</b><br>';
|
||||
html += 'Mnemonic: <b>' + key.phrase + '</b><br>';
|
||||
wallet.getBalance(function(err, balance) {
|
||||
if (balance) {
|
||||
html += 'Confirmed Balance: <b>' + utils.btc(balance.confirmed) + '</b><br>';
|
||||
html += 'Unconfirmed Balance: <b>' + utils.btc(balance.unconfirmed) + '</b><br>';
|
||||
html += 'Balance: <b>' + utils.btc(balance.total) + '</b><br>';
|
||||
}
|
||||
wallet.getHistory(function(err, txs) {
|
||||
html += 'TXs:\n';
|
||||
wdiv.innerHTML = html;
|
||||
if (txs) {
|
||||
txs.forEach(function(tx) {
|
||||
var el = create('<a style="display:block;" href="#' + tx.rhash + '">' + tx.rhash + '</a>');
|
||||
wdiv.appendChild(el);
|
||||
el.onclick = function(ev) {
|
||||
show(tx);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bcoin.set({
|
||||
network: 'segnet4',
|
||||
debug: true,
|
||||
// db: 'memory',
|
||||
logger: logger,
|
||||
useWorkers: true
|
||||
});
|
||||
|
||||
node = new bcoin.fullnode();
|
||||
|
||||
node.on('error', function(err) {
|
||||
bcoin.error(err);
|
||||
});
|
||||
|
||||
node.on('block', addItem);
|
||||
node.mempool.on('tx', addItem);
|
||||
|
||||
node.open(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
node.startSync();
|
||||
|
||||
formatWallet(node.wallet);
|
||||
|
||||
node.wallet.on('update', function() {
|
||||
formatWallet(node.wallet);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
136
browser/proxysocket.js
Normal file
136
browser/proxysocket.js
Normal file
@ -0,0 +1,136 @@
|
||||
var bcoin = require('../lib/bcoin/env');
|
||||
var utils = require('../lib/bcoin/utils');
|
||||
var BufferWriter = require('../lib/bcoin/writer');
|
||||
var assert = utils.assert;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var IOClient = require('socket.io-client');
|
||||
|
||||
function ProxySocket(uri) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof ProxySocket))
|
||||
return new ProxySocket(uri);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.info = null;
|
||||
|
||||
this.socket = new IOClient(uri, { reconnection: false });
|
||||
this.sendBuffer = [];
|
||||
this.snonce = null;
|
||||
|
||||
this.closed = false;
|
||||
|
||||
this.socket.on('info', function(info) {
|
||||
if (self.closed)
|
||||
return;
|
||||
|
||||
self.info = info;
|
||||
|
||||
if (info.pow) {
|
||||
self.snonce = new Buffer(info.snonce, 'hex');
|
||||
self.target = new Buffer(info.target, 'hex');
|
||||
}
|
||||
|
||||
self.emit('info', info);
|
||||
});
|
||||
|
||||
this.socket.on('error', function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
this.socket.on('tcp connect', function() {
|
||||
if (self.closed)
|
||||
return;
|
||||
self.emit('connect');
|
||||
});
|
||||
|
||||
this.socket.on('tcp data', function(data) {
|
||||
self.emit('data', new Buffer(data, 'hex'));
|
||||
});
|
||||
|
||||
this.socket.on('tcp close', function(data) {
|
||||
if (self.closed)
|
||||
return;
|
||||
self.closed = true;
|
||||
self.emit('close');
|
||||
});
|
||||
|
||||
this.socket.on('tcp error', function(err) {
|
||||
self.emit('error', new Error(err));
|
||||
});
|
||||
|
||||
this.socket.on('close', function() {
|
||||
if (self.closed)
|
||||
return;
|
||||
self.closed = true;
|
||||
self.emit('close');
|
||||
});
|
||||
}
|
||||
|
||||
utils.inherits(ProxySocket, EventEmitter);
|
||||
|
||||
ProxySocket.prototype.connect = function connect(port, host) {
|
||||
var nonce = 0;
|
||||
var i, pow;
|
||||
|
||||
if (this.closed) {
|
||||
this.sendBuffer.length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.info)
|
||||
return this.once('info', connect.bind(this, port, host));
|
||||
|
||||
if (this.info.pow) {
|
||||
bcoin.debug(
|
||||
'Solving proof of work to create socket (%d, %s) -- please wait.',
|
||||
port, host);
|
||||
|
||||
pow = new BufferWriter();
|
||||
pow.writeU32(nonce);
|
||||
pow.writeBytes(this.snonce);
|
||||
pow.writeU32(port);
|
||||
pow.writeString(host, 'ascii');
|
||||
pow = pow.render();
|
||||
|
||||
do {
|
||||
nonce++;
|
||||
assert(nonce <= 0xffffffff, 'Could not create socket.');
|
||||
pow.writeUInt32LE(nonce, 0, true);
|
||||
} while (utils.cmp(utils.dsha256(pow), this.target) >= 0);
|
||||
|
||||
bcoin.debug('Solved proof of work: %d', nonce);
|
||||
}
|
||||
|
||||
this.socket.emit('tcp connect', port, host, nonce);
|
||||
|
||||
for (i = 0; i < this.sendBuffer.length; i++)
|
||||
this.write(this.sendBuffer[i]);
|
||||
|
||||
this.sendBuffer.length = 0;
|
||||
};
|
||||
|
||||
ProxySocket.prototype.write = function write(data) {
|
||||
if (!this.info) {
|
||||
this.sendBuffer.push(data);
|
||||
return true;
|
||||
}
|
||||
this.socket.emit('tcp data', data.toString('hex'));
|
||||
return true;
|
||||
};
|
||||
|
||||
ProxySocket.prototype.destroy = function destroy() {
|
||||
if (this.closed)
|
||||
return;
|
||||
this.closed = true;
|
||||
this.socket.disconnect();
|
||||
};
|
||||
|
||||
ProxySocket.connect = function connect(uri, port, host) {
|
||||
var socket = new ProxySocket(uri);
|
||||
socket.connect(port, host);
|
||||
return socket;
|
||||
};
|
||||
|
||||
module.exports = ProxySocket;
|
||||
31
browser/server.js
Normal file
31
browser/server.js
Normal file
@ -0,0 +1,31 @@
|
||||
var HTTPBase = require('../lib/bcoin/http/base');
|
||||
var fs = require('fs');
|
||||
|
||||
var server = new HTTPBase();
|
||||
var proxy = require('./wsproxy')({
|
||||
pow: process.argv.indexOf('--pow') !== -1
|
||||
});
|
||||
|
||||
var index = fs.readFileSync(__dirname + '/index.html');
|
||||
var bcoin = fs.readFileSync(__dirname + '/bcoin.js');
|
||||
var worker = fs.readFileSync(__dirname + '/../lib/bcoin/worker.js');
|
||||
|
||||
server.get('/favicon.ico', function(req, res, next, send) {
|
||||
send(404, '', 'text');
|
||||
});
|
||||
|
||||
server.get('/', function(req, res, next, send) {
|
||||
send(200, index, 'html');
|
||||
});
|
||||
|
||||
server.get('/bcoin.js', function(req, res, next, send) {
|
||||
send(200, bcoin, 'js');
|
||||
});
|
||||
|
||||
server.get('/bcoin-worker.js', function(req, res, next, send) {
|
||||
send(200, worker, 'js');
|
||||
});
|
||||
|
||||
proxy.attach(server.server);
|
||||
|
||||
server.listen(8080);
|
||||
197
browser/setimmediate.js
Normal file
197
browser/setimmediate.js
Normal file
@ -0,0 +1,197 @@
|
||||
/*!
|
||||
* Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
(function (global, undefined) {
|
||||
"use strict";
|
||||
|
||||
if (global.setImmediate) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nextHandle = 1; // Spec says greater than zero
|
||||
var tasksByHandle = {};
|
||||
var currentlyRunningATask = false;
|
||||
var doc = global.document;
|
||||
var setImmediate;
|
||||
|
||||
function addFromSetImmediateArguments(args) {
|
||||
tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args);
|
||||
return nextHandle++;
|
||||
}
|
||||
|
||||
// This function accepts the same arguments as setImmediate, but
|
||||
// returns a function that requires no arguments.
|
||||
function partiallyApplied(handler) {
|
||||
var args = [].slice.call(arguments, 1);
|
||||
return function() {
|
||||
if (typeof handler === "function") {
|
||||
handler.apply(undefined, args);
|
||||
} else {
|
||||
(new Function("" + handler))();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function runIfPresent(handle) {
|
||||
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
|
||||
// So if we're currently running a task, we'll need to delay this invocation.
|
||||
if (currentlyRunningATask) {
|
||||
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
|
||||
// "too much recursion" error.
|
||||
setTimeout(partiallyApplied(runIfPresent, handle), 0);
|
||||
} else {
|
||||
var task = tasksByHandle[handle];
|
||||
if (task) {
|
||||
currentlyRunningATask = true;
|
||||
try {
|
||||
task();
|
||||
} finally {
|
||||
clearImmediate(handle);
|
||||
currentlyRunningATask = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearImmediate(handle) {
|
||||
delete tasksByHandle[handle];
|
||||
}
|
||||
|
||||
function installNextTickImplementation() {
|
||||
setImmediate = function() {
|
||||
var handle = addFromSetImmediateArguments(arguments);
|
||||
process.nextTick(partiallyApplied(runIfPresent, handle));
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function canUsePostMessage() {
|
||||
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
|
||||
// where `global.postMessage` means something completely different and can't be used for this purpose.
|
||||
if (global.postMessage && !global.importScripts) {
|
||||
var postMessageIsAsynchronous = true;
|
||||
var oldOnMessage = global.onmessage;
|
||||
global.onmessage = function() {
|
||||
postMessageIsAsynchronous = false;
|
||||
};
|
||||
global.postMessage("", "*");
|
||||
global.onmessage = oldOnMessage;
|
||||
return postMessageIsAsynchronous;
|
||||
}
|
||||
}
|
||||
|
||||
function installPostMessageImplementation() {
|
||||
// Installs an event handler on `global` for the `message` event: see
|
||||
// * https://developer.mozilla.org/en/DOM/window.postMessage
|
||||
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
|
||||
|
||||
var messagePrefix = "setImmediate$" + Math.random() + "$";
|
||||
var onGlobalMessage = function(event) {
|
||||
if (event.source === global &&
|
||||
typeof event.data === "string" &&
|
||||
event.data.indexOf(messagePrefix) === 0) {
|
||||
runIfPresent(+event.data.slice(messagePrefix.length));
|
||||
}
|
||||
};
|
||||
|
||||
if (global.addEventListener) {
|
||||
global.addEventListener("message", onGlobalMessage, false);
|
||||
} else {
|
||||
global.attachEvent("onmessage", onGlobalMessage);
|
||||
}
|
||||
|
||||
setImmediate = function() {
|
||||
var handle = addFromSetImmediateArguments(arguments);
|
||||
global.postMessage(messagePrefix + handle, "*");
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installMessageChannelImplementation() {
|
||||
var channel = new MessageChannel();
|
||||
channel.port1.onmessage = function(event) {
|
||||
var handle = event.data;
|
||||
runIfPresent(handle);
|
||||
};
|
||||
|
||||
setImmediate = function() {
|
||||
var handle = addFromSetImmediateArguments(arguments);
|
||||
channel.port2.postMessage(handle);
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installReadyStateChangeImplementation() {
|
||||
var html = doc.documentElement;
|
||||
setImmediate = function() {
|
||||
var handle = addFromSetImmediateArguments(arguments);
|
||||
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
|
||||
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
|
||||
var script = doc.createElement("script");
|
||||
script.onreadystatechange = function () {
|
||||
runIfPresent(handle);
|
||||
script.onreadystatechange = null;
|
||||
html.removeChild(script);
|
||||
script = null;
|
||||
};
|
||||
html.appendChild(script);
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installSetTimeoutImplementation() {
|
||||
setImmediate = function() {
|
||||
var handle = addFromSetImmediateArguments(arguments);
|
||||
setTimeout(partiallyApplied(runIfPresent, handle), 0);
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
|
||||
var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
|
||||
attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
|
||||
|
||||
// Don't get fooled by e.g. browserify environments.
|
||||
if ({}.toString.call(global.process) === "[object process]") {
|
||||
// For Node.js before 0.9
|
||||
installNextTickImplementation();
|
||||
|
||||
} else if (canUsePostMessage()) {
|
||||
// For non-IE10 modern browsers
|
||||
installPostMessageImplementation();
|
||||
|
||||
} else if (global.MessageChannel) {
|
||||
// For web workers, where supported
|
||||
installMessageChannelImplementation();
|
||||
|
||||
} else if (doc && "onreadystatechange" in doc.createElement("script")) {
|
||||
// For IE 6–8
|
||||
installReadyStateChangeImplementation();
|
||||
|
||||
} else {
|
||||
// For older browsers
|
||||
installSetTimeoutImplementation();
|
||||
}
|
||||
|
||||
attachTo.setImmediate = setImmediate;
|
||||
attachTo.clearImmediate = clearImmediate;
|
||||
}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));
|
||||
132
browser/wsproxy.js
Normal file
132
browser/wsproxy.js
Normal file
@ -0,0 +1,132 @@
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
var IOServer = require('socket.io');
|
||||
var utils = require('../lib/bcoin/utils');
|
||||
var IP = require('../lib/bcoin/ip');
|
||||
var network = require('../lib/bcoin/protocol/network');
|
||||
var BufferWriter = require('../lib/bcoin/writer');
|
||||
var ports = [];
|
||||
var i, type;
|
||||
|
||||
for (i = 0; i < network.types.length; i++) {
|
||||
type = network.types[i];
|
||||
ports.push(network[type].port);
|
||||
}
|
||||
|
||||
module.exports = function wsproxy(options) {
|
||||
var target, io;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
target = new Buffer(
|
||||
'0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
'hex');
|
||||
|
||||
io = new IOServer();
|
||||
|
||||
io.on('error', function(err) {
|
||||
utils.error(err.stack + '');
|
||||
});
|
||||
|
||||
io.on('connection', function(ws) {
|
||||
var snonce = utils.nonce().toArrayLike(Buffer, 'be', 8);
|
||||
var socket, pow;
|
||||
|
||||
ws.emit('info', {
|
||||
pow: !!options.pow,
|
||||
snonce: snonce.toString('hex'),
|
||||
target: target.toString('hex')
|
||||
});
|
||||
|
||||
ws.on('error', function(err) {
|
||||
utils.error(err.stack + '');
|
||||
});
|
||||
|
||||
ws.on('tcp connect', function(port, host, nonce) {
|
||||
if (socket)
|
||||
return;
|
||||
|
||||
if (options.pow) {
|
||||
if (!utils.isNumber(port)
|
||||
|| typeof host !== 'string'
|
||||
|| !utils.isNumber(nonce)) {
|
||||
utils.error('Client did not solve proof of work.');
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
pow = new BufferWriter();
|
||||
pow.writeU32(nonce);
|
||||
pow.writeBytes(snonce);
|
||||
pow.writeU32(port);
|
||||
pow.writeString(host, 'ascii');
|
||||
pow = pow.render();
|
||||
|
||||
if (utils.cmp(utils.dsha256(pow), target) >= 0) {
|
||||
utils.error('Client did not solve proof of work.');
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (IP.isPrivate(host)) {
|
||||
utils.error('Client is trying to connect to a private ip.');
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ports.indexOf(port) === -1) {
|
||||
utils.error('Client is trying to connect to a non-bitcoin port.');
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
socket = net.connect(port, host);
|
||||
utils.error('Connecting to %s:%d.', host, port);
|
||||
} catch (e) {
|
||||
utils.error('Closing %s:%d.', host, port);
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
socket.on('connect', function() {
|
||||
ws.emit('tcp connect');
|
||||
});
|
||||
|
||||
socket.on('data', function(data) {
|
||||
ws.emit('tcp data', data.toString('hex'));
|
||||
});
|
||||
|
||||
socket.on('error', function(err) {
|
||||
ws.emit('tcp error', err.message);
|
||||
});
|
||||
|
||||
socket.on('close', function() {
|
||||
utils.error('Closing %s:%d.', host, port);
|
||||
ws.emit('tcp close');
|
||||
ws.disconnect();
|
||||
});
|
||||
|
||||
ws.on('tcp data', function(data) {
|
||||
socket.write(new Buffer(data, 'hex'));
|
||||
});
|
||||
|
||||
ws.on('disconnect', function() {
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
socket.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return io;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user