From dc436d10e2b8d874423ba2609be589b9f6180d02 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 5 Jun 2016 20:10:44 -0700 Subject: [PATCH] add browser example. --- browser/index.html | 282 ++++++++++++++++++++++++++++++++++++++++ browser/proxysocket.js | 136 +++++++++++++++++++ browser/server.js | 31 +++++ browser/setimmediate.js | 197 ++++++++++++++++++++++++++++ browser/wsproxy.js | 132 +++++++++++++++++++ 5 files changed, 778 insertions(+) create mode 100644 browser/index.html create mode 100644 browser/proxysocket.js create mode 100644 browser/server.js create mode 100644 browser/setimmediate.js create mode 100644 browser/wsproxy.js diff --git a/browser/index.html b/browser/index.html new file mode 100644 index 00000000..5679d1c9 --- /dev/null +++ b/browser/index.html @@ -0,0 +1,282 @@ + + + +bcoin + + + + +

BCoin, the browser full node

+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 bcoin repo for +more bitcoin magic). +
+ Last 20 Blocks/TXs: +
+
+
+
+
+ + + +
+ +
+ + + diff --git a/browser/proxysocket.js b/browser/proxysocket.js new file mode 100644 index 00000000..32f14e64 --- /dev/null +++ b/browser/proxysocket.js @@ -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; diff --git a/browser/server.js b/browser/server.js new file mode 100644 index 00000000..00905918 --- /dev/null +++ b/browser/server.js @@ -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); diff --git a/browser/setimmediate.js b/browser/setimmediate.js new file mode 100644 index 00000000..d5c57e6d --- /dev/null +++ b/browser/setimmediate.js @@ -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