wip
This commit is contained in:
parent
c5d7d7f5f2
commit
4328d7c3ec
@ -31,10 +31,7 @@ function Chain(options) {
|
||||
hashes: preload.hashes.slice(),
|
||||
ts: preload.ts.slice()
|
||||
};
|
||||
this.request = {
|
||||
map: {},
|
||||
count: 0
|
||||
};
|
||||
this.request = new utils.RequestCache();
|
||||
|
||||
if (this.index.hashes.length === 0)
|
||||
this.add(new bcoin.block(constants.genesis));
|
||||
@ -122,14 +119,7 @@ Chain.prototype.add = function add(block) {
|
||||
this._bloomBlock(block);
|
||||
|
||||
// Fullfill request
|
||||
if (this.request.map[hash]) {
|
||||
var req = this.request.map[hash];
|
||||
delete this.request.map[hash];
|
||||
this.request.count--;
|
||||
req.forEach(function(cb) {
|
||||
cb(block);
|
||||
});
|
||||
}
|
||||
this.request.fullfill(hash, block);
|
||||
|
||||
if (!this.orphan.map[hash])
|
||||
break;
|
||||
@ -199,13 +189,8 @@ Chain.prototype.get = function get(hash, cb) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (this.request.map[hash]) {
|
||||
this.request.map[hash].push(cb);
|
||||
} else {
|
||||
this.request.map[hash] = [ cb ];
|
||||
this.request.count++;
|
||||
if (this.request.add(hash, cb))
|
||||
this.emit('missing', hash);
|
||||
}
|
||||
};
|
||||
|
||||
Chain.prototype.isFull = function isFull() {
|
||||
|
||||
@ -93,7 +93,7 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
items = [ items ];
|
||||
|
||||
var self = this;
|
||||
items.forEach(function(item) {
|
||||
var result = items.map(function(item) {
|
||||
var key = item.hash('hex');
|
||||
var old = this._broadcast.map[key];
|
||||
if (old)
|
||||
@ -101,21 +101,32 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
|
||||
// Auto-cleanup broadcast map after timeout
|
||||
var entry = {
|
||||
e: new EventEmitter(),
|
||||
timeout: setTimeout(function() {
|
||||
entry.e.emit('timeout');
|
||||
delete self._broadcast.map[key];
|
||||
}, this._broadcast.timout),
|
||||
value: item
|
||||
};
|
||||
|
||||
this._broadcast.map[key] = entry;
|
||||
|
||||
return entry.e;
|
||||
}, this);
|
||||
|
||||
this._write(this.framer.inv(items));
|
||||
this._write(this.framer.inv(items.map(function(item) {
|
||||
return {
|
||||
type: item.type,
|
||||
hash: item.hash()
|
||||
};
|
||||
})));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Peer.prototype.updateWatch = function updateWatch() {
|
||||
if (this.ack)
|
||||
this._write(this.framer.filterLoad(this.bloom, 'pubkeyOnly'));
|
||||
this._write(this.framer.filterLoad(this.bloom, 'none'));
|
||||
};
|
||||
|
||||
Peer.prototype.destroy = function destroy() {
|
||||
@ -224,6 +235,8 @@ Peer.prototype._onPacket = function onPacket(packet) {
|
||||
if (this._res(cmd, payload)) {
|
||||
return;
|
||||
} else {
|
||||
if (cmd !== 'merkleblock')
|
||||
console.log(cmd);
|
||||
this.emit(cmd, payload);
|
||||
}
|
||||
};
|
||||
@ -244,8 +257,9 @@ Peer.prototype._handleGetData = function handleGetData(items) {
|
||||
if (!this._broadcast.map[hash])
|
||||
return;
|
||||
|
||||
var entry = this._broadcast.map[hash].value;
|
||||
this._write(entry.render(this.framer));
|
||||
var entry = this._broadcast.map[hash];
|
||||
this._write(entry.value.render());
|
||||
entry.e.emit('request');
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
@ -13,8 +13,9 @@ function Pool(options) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.options = options || {};
|
||||
this.size = options.size || 8;
|
||||
this.size = options.size || 16;
|
||||
this.parallel = options.parallel || 2000;
|
||||
this.redundancy = 2;
|
||||
this.load = {
|
||||
timeout: options.loadTimeout || 10000,
|
||||
window: options.loadWindow || 250,
|
||||
@ -27,7 +28,7 @@ function Pool(options) {
|
||||
this.requestTimeout = options.requestTimeout || 10000;
|
||||
this.chain = new bcoin.chain();
|
||||
this.watchMap = {};
|
||||
this.bloom = new bcoin.bloom(8 * 10 * 1024,
|
||||
this.bloom = new bcoin.bloom(8 * 1024,
|
||||
10,
|
||||
(Math.random() * 0xffffffff) | 0),
|
||||
this.peers = {
|
||||
@ -54,12 +55,13 @@ function Pool(options) {
|
||||
minDepth: options.minValidateDepth || 0,
|
||||
|
||||
// getTx map
|
||||
map: {},
|
||||
map: {}
|
||||
};
|
||||
|
||||
// Validation cache
|
||||
reqs: new bcoin.utils.RequestCache(),
|
||||
cache: [],
|
||||
cacheSize: 1000
|
||||
// Currently broadcasted TXs
|
||||
this.tx = {
|
||||
list: [],
|
||||
timeout: options.txTimeout || 60000
|
||||
};
|
||||
|
||||
this.createConnection = options.createConnection;
|
||||
@ -211,6 +213,12 @@ Pool.prototype._addPeer = function _addPeer() {
|
||||
}
|
||||
|
||||
peer.updateWatch();
|
||||
|
||||
self.tx.list.forEach(function(entry) {
|
||||
peer.broadcast(entry.tx)[0].once('request', function() {
|
||||
entry.e.emit('ack');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
peer.on('merkleblock', function(block) {
|
||||
@ -287,6 +295,12 @@ Pool.prototype.unwatch = function unwatch(id) {
|
||||
};
|
||||
|
||||
Pool.prototype.search = function search(id, range) {
|
||||
// Optional id argument
|
||||
if (typeof id === 'object' && !Array.isArray(id) ||
|
||||
typeof id === 'number') {
|
||||
range = id;
|
||||
id = null;
|
||||
}
|
||||
if (typeof id === 'string')
|
||||
id = utils.toArray(id, 'hex');
|
||||
|
||||
@ -307,7 +321,8 @@ Pool.prototype.search = function search(id, range) {
|
||||
var hashes = this.chain.hashesInRange(range.start, range.end);
|
||||
var waiting = hashes.length;
|
||||
|
||||
this.watch(id);
|
||||
if (id)
|
||||
this.watch(id);
|
||||
|
||||
hashes.slice().reverse().forEach(function(hash) {
|
||||
// Get the block that is in index
|
||||
@ -318,7 +333,8 @@ Pool.prototype.search = function search(id, range) {
|
||||
waiting--;
|
||||
e.emit('progress', hashes.length - waiting, hashes.length);
|
||||
if (waiting === 0) {
|
||||
self.unwatch(id);
|
||||
if (id)
|
||||
self.unwatch(id);
|
||||
e.emit('end');
|
||||
}
|
||||
});
|
||||
@ -407,14 +423,18 @@ Pool.prototype._doRequests = function _doRequests() {
|
||||
if (above && below && this.load.hiReached)
|
||||
this._load();
|
||||
|
||||
// Split list between nodes
|
||||
// Split list between peers
|
||||
var red = this.redundancy;
|
||||
var count = this.peers.block.length;
|
||||
var split = Math.ceil(items.length / count);
|
||||
for (var i = 0, off = 0; i < count; i++, off += split) {
|
||||
var peer = this.peers.block[i];
|
||||
peer.getData(items.slice(off, off + split).map(function(item) {
|
||||
return item.start(peer);
|
||||
}));
|
||||
var split = Math.ceil(items.length * red / count);
|
||||
for (var i = 0, off = 0; i < count; i += red, off += split) {
|
||||
var req = items.slice(off, off + split).map(function(item) {
|
||||
return item.start(this.peers.block[i]);
|
||||
}, this);
|
||||
|
||||
for (var j = 0; j < red && i + j < count; j++) {
|
||||
this.peers.block[i + j].getData(req);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -478,21 +498,28 @@ Pool.prototype.getTx = function getTx(hash, range, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
Pool.prototype._addValidateCache = function addValidateCache(tx, result) {
|
||||
this.validate.cache.push({
|
||||
hash: tx.hash('hex'),
|
||||
result: result
|
||||
});
|
||||
if (this.validate.cache.length > this.validate.cacheSize)
|
||||
this.validate.cache = this.validate.cache.slice(-this.validate.cacheSize);
|
||||
};
|
||||
Pool.prototype.sendTx = function sendTx(tx) {
|
||||
var e = new EventEmitter();
|
||||
|
||||
Pool.prototype._probeValidateCache = function probeValidateCache(tx) {
|
||||
for (var i = 0; i < this.validate.cache.length; i++) {
|
||||
var entry = this.validate.cache[i];
|
||||
if (entry.hash === tx.hash('hex'))
|
||||
return entry.result;
|
||||
}
|
||||
var self = this;
|
||||
var entry = {
|
||||
tx: tx,
|
||||
e: e,
|
||||
timer: setTimeout(function() {
|
||||
var i = self.tx.list.indexOf(entry);
|
||||
if (i !== -1)
|
||||
self.tx.list.splice(i, 1);
|
||||
}, this.tx.timeout)
|
||||
};
|
||||
this.tx.list.push(entry);
|
||||
|
||||
this.peers.block.forEach(function(peer) {
|
||||
peer.broadcast(tx)[0].once('request', function() {
|
||||
e.emit('ack');
|
||||
});
|
||||
});
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
function LoadRequest(pool, type, hash, cb) {
|
||||
|
||||
@ -22,12 +22,12 @@ function TX(data) {
|
||||
|
||||
if (data.inputs) {
|
||||
data.inputs.forEach(function(input) {
|
||||
this.input(input);
|
||||
this.input(input, null, this === data);
|
||||
}, this);
|
||||
}
|
||||
if (data.outputs) {
|
||||
data.outputs.forEach(function(out) {
|
||||
this.out(out);
|
||||
this.out(out, null, this === data);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
@ -38,21 +38,15 @@ TX.prototype.clone = function clone() {
|
||||
};
|
||||
|
||||
TX.prototype.hash = function hash(enc) {
|
||||
if (!this._hash) {
|
||||
// First, obtain the raw TX data
|
||||
this.render();
|
||||
|
||||
// Hash it
|
||||
this._hash = utils.dsha256(this._raw);
|
||||
}
|
||||
return enc === 'hex' ? utils.toHex(this._hash) : this._hash;
|
||||
var h = utils.dsha256(this.render());
|
||||
return enc === 'hex' ? utils.toHex(h) : h;
|
||||
};
|
||||
|
||||
TX.prototype.render = function render() {
|
||||
return bcoin.protocol.framer.tx(this);
|
||||
};
|
||||
|
||||
TX.prototype.input = function input(i, index) {
|
||||
TX.prototype.input = function input(i, index, clone) {
|
||||
if (i instanceof TX)
|
||||
i = { tx: i, index: index };
|
||||
|
||||
@ -70,14 +64,22 @@ TX.prototype.input = function input(i, index) {
|
||||
hash: hash,
|
||||
index: i.out ? i.out.index : i.index,
|
||||
},
|
||||
script: bcoin.script.decode(i.script),
|
||||
script: clone ? i.script.slice() : bcoin.script.decode(i.script),
|
||||
seq: i.seq === undefined ? 0xffffffff : i.seq
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
TX.prototype.out = function out(output, value) {
|
||||
TX.prototype.inputTx = function inputTx(i, tx) {
|
||||
if (!(tx instanceof TX))
|
||||
tx = new TX(tx);
|
||||
|
||||
assert(i <= this.inputs.length);
|
||||
this.inputs[i].out.tx = tx;
|
||||
};
|
||||
|
||||
TX.prototype.out = function out(output, value, clone) {
|
||||
if (typeof output === 'string') {
|
||||
output = {
|
||||
address: output,
|
||||
@ -85,7 +87,8 @@ TX.prototype.out = function out(output, value) {
|
||||
};
|
||||
}
|
||||
|
||||
var script = bcoin.script.decode(output.script);
|
||||
var script = clone ? output.script.slice() :
|
||||
bcoin.script.decode(output.script);
|
||||
|
||||
// Default script if given address
|
||||
if (output.address) {
|
||||
|
||||
@ -290,6 +290,7 @@ utils.nextTick = function nextTick(fn) {
|
||||
|
||||
function RequestCache() {
|
||||
this.map = {};
|
||||
this.count = 0;
|
||||
};
|
||||
utils.RequestCache = RequestCache;
|
||||
|
||||
@ -299,6 +300,7 @@ RequestCache.prototype.add = function add(id, cb) {
|
||||
return false;
|
||||
} else {
|
||||
this.map[id] = [ cb ];
|
||||
this.count++;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -309,6 +311,7 @@ RequestCache.prototype.fullfill = function fullfill(id, err, data) {
|
||||
|
||||
var cbs = this.map[id];
|
||||
delete this.map[id];
|
||||
this.count--;
|
||||
cbs.forEach(function(cb) {
|
||||
cb(err, data);
|
||||
});
|
||||
|
||||
@ -1,15 +1,45 @@
|
||||
var assert = require('assert');
|
||||
var bcoin = require('../bcoin');
|
||||
var hash = require('hash.js');
|
||||
var utils = bcoin.utils;
|
||||
|
||||
function Wallet() {
|
||||
function Wallet(options, passphrase) {
|
||||
if (!(this instanceof Wallet))
|
||||
return new Wallet();
|
||||
return new Wallet(options, passphrase);
|
||||
|
||||
this.key = bcoin.ecdsa.genKeyPair();
|
||||
// bcoin.wallet('scope', 'password')
|
||||
if (typeof options === 'string' && typeof passphrase === 'string') {
|
||||
options = {
|
||||
scope: options,
|
||||
passphrase: passphrase
|
||||
};
|
||||
}
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.key = null;
|
||||
|
||||
if (options.passphrase) {
|
||||
this.key = bcoin.ecdsa.genKeyPair({
|
||||
pers: options.scope,
|
||||
entropy: hash.sha256().update(options.passphrase).digest()
|
||||
});
|
||||
} else if (options.priv) {
|
||||
this.key = bcoin.ecdsa.keyPair(options.priv);
|
||||
} else {
|
||||
this.key = bcoin.ecdsa.genKeyPair();
|
||||
}
|
||||
}
|
||||
module.exports = Wallet;
|
||||
|
||||
Wallet.prototype.getPrivateKey = function getPrivateKey() {
|
||||
return this.key.getPrivate().toArray();
|
||||
};
|
||||
|
||||
Wallet.prototype.getPublicKey = function getPublicKey() {
|
||||
return this.key.getPublic('array');
|
||||
};
|
||||
|
||||
Wallet.prototype.getHash = function getHash() {
|
||||
var pub = this.key.getPublic('array');
|
||||
return utils.ripesha(pub);
|
||||
@ -52,14 +82,21 @@ Wallet.prototype.validateAddress = function validateAddress(addr) {
|
||||
Wallet.validateAddress = Wallet.prototype.validateAddress;
|
||||
|
||||
Wallet.prototype.own = function own(tx) {
|
||||
return tx.outputs.some(function(output) {
|
||||
return output.script.length === 5 &&
|
||||
output.script[0] === 'dup' &&
|
||||
output.script[1] === 'hash160' &&
|
||||
utils.isEqual(output.script[2], this.getHash()) &&
|
||||
output.script[3] === 'eqverify' &&
|
||||
output.script[4] === 'checksig';
|
||||
var outputs = tx.outputs.filter(function(output) {
|
||||
if (output.script.length < 5)
|
||||
return false;
|
||||
|
||||
var s = output.script.slice(-5);
|
||||
return s[0] === 'dup' &&
|
||||
s[1] === 'hash160' &&
|
||||
utils.isEqual(s[2], this.getHash()) &&
|
||||
s[3] === 'eqverify' &&
|
||||
s[4] === 'checksig';
|
||||
}, this);
|
||||
if (outputs.length === 0)
|
||||
return false;
|
||||
|
||||
return outputs;
|
||||
};
|
||||
|
||||
Wallet.prototype.sign = function sign(tx, type) {
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"dependencies": {
|
||||
"async": "^0.8.0",
|
||||
"bn.js": "^0.3.0",
|
||||
"elliptic": "^0.8.0",
|
||||
"elliptic": "^0.9.0",
|
||||
"hash.js": "^0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -48,8 +48,7 @@ describe('TX', function() {
|
||||
|
||||
it('should be verifiable', function() {
|
||||
var tx = bcoin.tx(parser.parseTx(bcoin.utils.toArray(raw, 'hex')));
|
||||
tx.inputs[0].out.tx =
|
||||
bcoin.tx(parser.parseTx(bcoin.utils.toArray(inp, 'hex')));
|
||||
tx.inputTx(0, bcoin.tx(parser.parseTx(bcoin.utils.toArray(inp, 'hex'))));
|
||||
|
||||
assert(tx.verify());
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
var assert = require('assert');
|
||||
var bn = require('bn.js');
|
||||
var bcoin = require('../');
|
||||
|
||||
describe('Wallet', function() {
|
||||
@ -25,9 +26,15 @@ describe('Wallet', function() {
|
||||
outputs: [{
|
||||
value: 5460 * 2,
|
||||
address: w.getAddress()
|
||||
}, {
|
||||
value: 5460 * 2,
|
||||
address: w.getAddress() + 'x'
|
||||
}]
|
||||
});
|
||||
assert(w.own(src));
|
||||
assert.equal(w.own(src).reduce(function(acc, out) {
|
||||
return acc.iadd(out.value);
|
||||
}, new bn(0)).toString(10), 5460 * 2);
|
||||
|
||||
var tx = bcoin.tx()
|
||||
.input(src, 0)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user