From 72b7adc800f1768e49eb40efc2b4000f6ee5a962 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 8 Feb 2016 18:06:39 -0800 Subject: [PATCH] get bcoin working in the browser again. --- lib/bcoin.js | 37 ++++++++++++++++++++++-------- lib/bcoin/chain.js | 38 +++++++++++++++++++++++++++---- lib/bcoin/hd.js | 24 ++++---------------- lib/bcoin/miner.js | 2 -- lib/bcoin/pool.js | 42 ++++++++++++++++++++++------------ lib/bcoin/ramdisk.js | 54 ++++++++++++++++++++++++++++++++++++++++++++ lib/bcoin/utils.js | 54 +++++++++++++++++++++++++++++++++----------- 7 files changed, 188 insertions(+), 63 deletions(-) create mode 100644 lib/bcoin/ramdisk.js diff --git a/lib/bcoin.js b/lib/bcoin.js index 9ead06b7..f4941502 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -10,16 +10,41 @@ var bn = require('bn.js'); var hash = require('hash.js'); var async = require('async'); +bcoin.isBrowser = + (typeof process !== 'undefined' && process.browser) + || typeof window !== 'undefined'; + if (process.env.BCOIN_DEBUG) { bcoin.debug = process.env.BCOIN_DEBUG; if (bcoin.debug === '0' || bcoin.debug === '1') bcoin.debug = +bcoin.debug === 1; } +if (!bcoin.isBrowser) { + bcoin.fs = require('f' + 's'); + bcoin.crypto = require('cry' + 'pto'); + bcoin.net = require('n' + 'et'); +} + +bcoin.elliptic = elliptic; +bcoin.bn = bn; +bcoin.hash = hash; +bcoin.async = async; + bcoin.ecdsa = elliptic.ec('secp256k1'); + +if (bcoin.ecdsa.signature) + throw new Error; + +if (bcoin.ecdsa.keypair) + throw new Error; + +bcoin.ecdsa.signature = require('elliptic/lib/elliptic/ec/signature'); +bcoin.ecdsa.keypair = require('elliptic/lib/elliptic/ec/key'); + bcoin.utils = require('./bcoin/utils'); -bcoin.bloom = require('./bcoin/bloom'); bcoin.protocol = require('./bcoin/protocol'); +bcoin.bloom = require('./bcoin/bloom'); bcoin.script = require('./bcoin/script'); bcoin.input = require('./bcoin/input'); bcoin.output = require('./bcoin/output'); @@ -27,6 +52,7 @@ bcoin.coin = require('./bcoin/coin'); bcoin.tx = require('./bcoin/tx'); bcoin.txPool = require('./bcoin/tx-pool'); bcoin.block = require('./bcoin/block'); +bcoin.ramdisk = require('./bcoin/ramdisk'); bcoin.chain = require('./bcoin/chain'); bcoin.keypair = require('./bcoin/keypair'); bcoin.address = require('./bcoin/address'); @@ -37,12 +63,3 @@ bcoin.hd = require('./bcoin/hd'); bcoin.miner = require('./bcoin/miner'); bcoin.protocol.network.set(process.env.BCOIN_NETWORK || 'main'); - -bcoin.bn = bn; -bcoin.elliptic = elliptic; -bcoin.utils.assert(!bcoin.ecdsa.signature); -bcoin.ecdsa.signature = require('elliptic/lib/elliptic/ec/signature'); -bcoin.utils.assert(!bcoin.ecdsa.keypair); -bcoin.ecdsa.keypair = require('elliptic/lib/elliptic/ec/key'); -bcoin.hash = hash; -bcoin.async = async; diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 1c90793e..4f1a6d6a 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -9,11 +9,11 @@ var EventEmitter = require('events').EventEmitter; var bcoin = require('../bcoin'); var bn = require('bn.js'); -var fs = require('fs'); var constants = bcoin.protocol.constants; var network = bcoin.protocol.network; var utils = bcoin.utils; var assert = utils.assert; +var fs = bcoin.fs; /** * Chain @@ -764,6 +764,12 @@ function ChainDB(chain, options) { } ChainDB.prototype._init = function _init() { + if (!bcoin.fs) { + utils.debug('`fs` module not available. Falling back to ramdisk.'); + this.ramdisk = bcoin.ramdisk(new Buffer([]), 40 * 1024 * 1024); + return; + } + if (+process.env.BCOIN_FRESH === 1) { try { fs.unlinkSync(this.file); @@ -1054,6 +1060,9 @@ ChainDB.prototype._readSync = function _readSync(size, offset) { if (offset < 0 || offset == null) return; + if (!bcoin.fs) + return this.ramdisk.read(size, offset); + data = this._malloc(size); try { @@ -1081,8 +1090,13 @@ ChainDB.prototype._readAsync = function _readAsync(size, offset, callback) { var index = 0; var data, bytes; + callback = utils.asyncify(callback); + if (offset < 0 || offset == null) - return false; + return callback(); + + if (!bcoin.fs) + return callback(null, this.ramdisk.read(size, offset)); data = this._malloc(size); @@ -1116,6 +1130,12 @@ ChainDB.prototype._writeSync = function _writeSync(data, offset) { if (offset < 0 || offset == null) return false; + if (!bcoin.fs) { + this.size += added; + this.ramdisk.write(data, offset); + return; + } + try { while (bytes = fs.writeSync(this.fd, data, index, size, offset)) { index += bytes; @@ -1139,10 +1159,18 @@ ChainDB.prototype._writeAsync = function _writeAsync(data, offset, callback) { var size = data.length; var index = 0; - if (offset < 0 || offset == null) - return false; + callback = utils.asyncify(callback); - self.size += added; + if (offset < 0 || offset == null) + return callback(null, false); + + if (!bcoin.fs) { + this.size += added; + this.ramdisk.write(data, offset); + return callback(null, true); + } + + this.size += added; (function next() { fs.write(self.fd, data, index, size, offset, function(err, bytes) { diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 4a464a53..71c1ede9 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -558,7 +558,7 @@ HDPrivateKey.prototype._seed = function _seed(seed) { throw new Error('entropy not in range'); } - var hash = sha512hmac(seed, 'Bitcoin seed'); + var hash = utils.sha512hmac(seed, 'Bitcoin seed'); return { version: null, @@ -671,7 +671,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { ? [0].concat(this.privateKey).concat(array32(index)) : data = [].concat(this.publicKey).concat(array32(index)); - hash = sha512hmac(data, this.chainCode); + hash = utils.sha512hmac(data, this.chainCode); leftPart = new bn(hash.slice(0, 32)); chainCode = hash.slice(32, 64); @@ -973,7 +973,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { throw new Error('invalid path'); data = [].concat(this.publicKey).concat(array32(index)); - hash = sha512hmac(data, this.chainCode); + hash = utils.sha512hmac(data, this.chainCode); leftPart = new bn(hash.slice(0, 32)); chainCode = hash.slice(32, 64); @@ -1077,20 +1077,6 @@ HDPrivateKey.fromSecret = function fromSecret(privateKey) { * Helpers */ -var isBrowser = (typeof process !== 'undefined' && process.browser) - || typeof window !== 'undefined'; - -function sha512hmac(data, salt) { - if (isBrowser) { - var hmac = hash.hmac(hash.sha512, utils.toArray(salt)); - return hmac.update(utils.toArray(data)).digest(); - } - var crypto = require('crypto'); - var hmac = crypto.createHmac('sha512', new Buffer(salt)); - var h = hmac.update(new Buffer(data)).digest(); - return Array.prototype.slice.call(h); -} - function array32(data) { var b = []; utils.writeU32BE(b, data, 0); @@ -1141,12 +1127,12 @@ function pbkdf2(key, salt, iterations, dkLen) { block1[salt.length + 2] = i >> 8 & 0xff; block1[salt.length + 3] = i >> 0 & 0xff; - U = sha512hmac(block1, key); + U = utils.sha512hmac(block1, key); utils.copy(U.slice(0, hLen), T, 0); for (j = 1; j < iterations; j++) { - U = sha512hmac(U, key); + U = utils.sha512hmac(U, key); for (k = 0; k < hLen; k++) T[k] ^= U[k]; diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index 2794a92e..24db9ae2 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -13,8 +13,6 @@ var bn = require('bn.js'); var inherits = require('inherits'); var EventEmitter = require('events').EventEmitter; -var crypto = require('crypto'); - /** * Miner */ diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index e2c5ff7a..29d21c2b 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -236,6 +236,11 @@ Pool.prototype._init = function _init() { } }; +Pool.prototype.startServer = function startServer() { + bcoin.net.createServer(function(socket) { + }); +}; + Pool.prototype._startTimer = function _startTimer() { var self = this; @@ -309,8 +314,9 @@ Pool.prototype.createConnection = function createConnection(peer, options) { if (this._createSocket) { socket = this._createSocket(addr.port, addr.host); } else { - net = require('net'); - socket = net.connect(addr.port, addr.host); + if (!bcoin.net) + throw new Error('Please include a `createSocket` callback.'); + socket = bcoin.net.connect(addr.port, addr.host); } utils.debug( @@ -719,13 +725,14 @@ Pool.prototype.loadMempool = function loadMempool() { }); }; -Pool.prototype._createPeer = function _createPeer(priority) { +Pool.prototype._createPeer = function _createPeer(priority, socket) { var self = this; var peer = new bcoin.peer(this, this.createConnection, { startHeight: this.options.startHeight, relay: this.options.relay, - priority: priority + priority: priority, + socket: socket }); peer.on('error', function(err) { @@ -801,23 +808,30 @@ Pool.prototype._handleTX = function _handleTX(tx, peer) { this.emit('watched', tx, peer); }; -Pool.prototype._addPeer = function _addPeer() { +Pool.prototype._addPeer = function _addPeer(socket) { var self = this; var peer; - if (this.destroyed) - return; + if (!socket) { + if (this.destroyed) + return; - if (this.peers.block.length + this.peers.pending.length >= this.size) - return; + if (this.peers.block.length + this.peers.pending.length >= this.size) + return; - if (!this.getSeed()) { - setTimeout(this._addPeer.bind(this), 5000); - return; + if (!this.getSeed()) { + setTimeout(this._addPeer.bind(this), 5000); + return; + } + + peer = this._createPeer(false); + } else { + if (this.destroyed) + return socket.destroy(); + + peer = this._createPeer(false, socket); } - peer = this._createPeer(false); - this.peers.pending.push(peer); this.peers.all.push(peer); diff --git a/lib/bcoin/ramdisk.js b/lib/bcoin/ramdisk.js new file mode 100644 index 00000000..9876837c --- /dev/null +++ b/lib/bcoin/ramdisk.js @@ -0,0 +1,54 @@ +function Ramdisk(fileData, size) { + if (!(this instanceof Ramdisk)) + return new Ramdisk(fileData, size); + + if (size < fileData.length) + size = fileData.length + (fileData.length / 2 | 0); + + this.size = fileData.length; + this.heap = new Buffer(size); + + fileData.copy(this.heap, 0, 0, fileData.length); +} + +Ramdisk.prototype.brk = function brk() { + var heap = new Buffer(this.heap.length + (this.heap.length / 2 | 0)); + utils.debug('brk1(%d, %d)', this.heap.length, heap.length); + this.heap.copy(heap, 0, 0, this.heap.length); + utils.debug('brk2(%d, %d)', this.heap.length, heap.length); + this.heap = heap; +}; + +Ramdisk.prototype.write = function write(data, offset) { + var added = Math.max(0, (offset + data.length) - this.size); + + while (offset + data.length > this.heap.length) + this.brk(); + + data.copy(this.heap, offset, 0, data.length); + + this.size += added; + + return data.length; +}; + +Ramdisk.prototype.truncate = function truncate(size) { + assert(size <= this.size); + this.size = size; +}; + +Ramdisk.prototype.read = function read(size, offset) { + var data, ret; + + if (offset + size > this.size) + return; + + data = this.heap.slice(offset, offset + size); + ret = new Buffer(size); + + data.copy(ret, 0, 0, data.length); + + return ret; +}; + +module.exports = Ramdisk; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index ede01f45..b9274113 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -10,7 +10,6 @@ var bcoin = require('../bcoin'); var bn = require('bn.js'); var hash = require('hash.js'); var util = require('util'); -var crypto = require('crypto'); /** * Utils @@ -151,21 +150,31 @@ utils.isBase58 = function isBase58(msg) { }; utils.ripemd160 = function ripemd160(data, enc) { - // return hash.ripemd160().update(data, enc).digest(); - var hash; + var result; + + if (!bcoin.crypto) + return hash.ripemd160().update(data, enc).digest(); + if (Array.isArray(data)) data = new Buffer(data); - hash = crypto.createHash('ripemd160').update(data, enc).digest(); - return utils.toArray(hash); + + result = bcoin.crypto.createHash('ripemd160').update(data, enc).digest(); + + return utils.toArray(result); }; utils.sha1 = function sha1(data, enc) { - // return hash.sha1().update(data, enc).digest(); - var hash; + var result; + + if (!bcoin.crypto) + return hash.sha1().update(data, enc).digest(); + if (Array.isArray(data)) data = new Buffer(data); - hash = crypto.createHash('sha1').update(data, enc).digest(); - return utils.toArray(hash); + + result = bcoin.crypto.createHash('sha1').update(data, enc).digest(); + + return utils.toArray(result); }; utils.ripesha = function ripesha(data, enc) { @@ -177,18 +186,37 @@ utils.checksum = function checksum(data, enc) { }; utils.sha256 = function sha256(data, enc) { - // return hash.sha256().update(data, enc).digest(); - var hash; + var result; + + if (!bcoin.crypto) + return hash.sha256().update(data, enc).digest(); + if (Array.isArray(data)) data = new Buffer(data); - hash = crypto.createHash('sha256').update(data, enc).digest(); - return utils.toArray(hash); + + result = bcoin.crypto.createHash('sha256').update(data, enc).digest(); + + return utils.toArray(result); }; utils.dsha256 = function dsha256(data, enc) { return utils.sha256(utils.sha256(data, enc)); }; +utils.sha512hmac = function sha512hmac(data, salt) { + var hmac, result; + + if (!bcoin.crypto) { + hmac = hash.hmac(hash.sha512, utils.toArray(salt)); + return hmac.update(utils.toArray(data)).digest(); + } + + hmac = bcoin.crypto.createHmac('sha512', new Buffer(salt)); + result = hmac.update(new Buffer(data)).digest(); + + return utils.toArray(result); +}; + utils.writeAscii = function writeAscii(dst, str, off) { var i = 0; var c;