tx-pool: initial
This commit is contained in:
parent
cdd3dbefb1
commit
69f3e85e37
@ -0,0 +1,76 @@
|
||||
var bn = require('bn.js');
|
||||
|
||||
function TXPool() {
|
||||
if (!(this instanceof TXPool))
|
||||
return new TXPool();
|
||||
|
||||
this._all = {};
|
||||
this._unspent = {};
|
||||
this._orphans = {};
|
||||
}
|
||||
module.exports = TXPool;
|
||||
|
||||
TXPool.prototype.add = function add(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
|
||||
// Do not add TX two times
|
||||
if (this._all[hash])
|
||||
return;
|
||||
this._all[hash] = tx;
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
for (var i = 0; i < tx.inputs.length; i++) {
|
||||
var input = tx.inputs[i];
|
||||
var key = input.out.hash + '/' + input.out.index;
|
||||
var unspent = this._unspent[key];
|
||||
|
||||
if (unspent) {
|
||||
// Add TX to inputs and spend money
|
||||
tx.input(unspent.tx, unspent.index);
|
||||
delete this._unspent[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Double-spend?!
|
||||
if (this._orphans[key])
|
||||
continue;
|
||||
|
||||
// Add orphan, if no parent transaction is yet known
|
||||
this._orphans[key] = { tx: tx, index: i };
|
||||
}
|
||||
|
||||
// Add unspent outputs or fullfill orphans
|
||||
for (var i = 0; i < tx.outputs.length; i++) {
|
||||
var out = tx.outputs[i];
|
||||
|
||||
var key = hash + '/' + i;
|
||||
var orphan = this._orphans[key];
|
||||
if (!orphan) {
|
||||
this._unspent[key] = { tx: tx, index: i };
|
||||
continue;
|
||||
}
|
||||
delete this._orphans[key];
|
||||
|
||||
// Add input to orphan
|
||||
orphan.tx.input(tx, orphan.index);
|
||||
}
|
||||
};
|
||||
|
||||
TXPool.prototype.unspent = function unspent(wallet) {
|
||||
return Object.keys(this._unspent).map(function(key) {
|
||||
return this._unspent[key];
|
||||
}, this).filter(function(item) {
|
||||
return wallet.own(item.tx, item.index);
|
||||
});
|
||||
};
|
||||
|
||||
TXPool.prototype.balance = function balance(wallet) {
|
||||
var acc = new bn(0);
|
||||
var unspent = this.unspent(wallet);
|
||||
if (unspent.length === 0)
|
||||
return acc;
|
||||
|
||||
return unspent.reduce(function(acc, item) {
|
||||
return acc.iadd(item.tx.outputs[item.index].value);
|
||||
}, acc);
|
||||
};
|
||||
@ -49,6 +49,8 @@ TX.prototype.render = function render() {
|
||||
TX.prototype.input = function input(i, index) {
|
||||
if (i instanceof TX)
|
||||
i = { tx: i, index: index };
|
||||
else if (typeof i === 'string' || Array.isArray(i))
|
||||
i = { hash: i, index: index };
|
||||
|
||||
var hash;
|
||||
if (i.tx)
|
||||
@ -89,6 +91,8 @@ TX.prototype.input = function input(i, index) {
|
||||
};
|
||||
|
||||
TX.prototype.out = function out(output, value) {
|
||||
if (output instanceof bcoin.wallet)
|
||||
output = output.getAddress();
|
||||
if (typeof output === 'string') {
|
||||
output = {
|
||||
address: output,
|
||||
|
||||
@ -18,6 +18,7 @@ function Wallet(options, passphrase) {
|
||||
options = {};
|
||||
|
||||
this.compressed = true;
|
||||
this.tx = new bcoin.txPool();
|
||||
this.key = null;
|
||||
|
||||
if (options.passphrase) {
|
||||
@ -94,10 +95,13 @@ Wallet.prototype.validateAddress = function validateAddress(addr) {
|
||||
};
|
||||
Wallet.validateAddress = Wallet.prototype.validateAddress;
|
||||
|
||||
Wallet.prototype.own = function own(tx) {
|
||||
Wallet.prototype.own = function own(tx, index) {
|
||||
var hash = this.getHash();
|
||||
var key = this.getPublicKey();
|
||||
var outputs = tx.outputs.filter(function(output) {
|
||||
var outputs = tx.outputs.filter(function(output, i) {
|
||||
if (index && index !== i)
|
||||
return false;
|
||||
|
||||
var s = output.script;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
@ -143,3 +147,15 @@ Wallet.prototype.sign = function sign(tx, type) {
|
||||
|
||||
return inputs.length;
|
||||
};
|
||||
|
||||
Wallet.prototype.addTX = function addTX(tx) {
|
||||
this.tx.add(tx);
|
||||
};
|
||||
|
||||
Wallet.prototype.unspent = function unspent() {
|
||||
return this.tx.unspent(this);
|
||||
};
|
||||
|
||||
Wallet.prototype.balance = function balance() {
|
||||
return this.tx.balance(this);
|
||||
};
|
||||
|
||||
@ -70,4 +70,28 @@ describe('Wallet', function() {
|
||||
w.sign(tx);
|
||||
assert(tx.verify());
|
||||
});
|
||||
|
||||
it('should have TX pool', function() {
|
||||
var w = bcoin.wallet();
|
||||
|
||||
// Coinbase
|
||||
var t1 = bcoin.tx().out(w, 50000).out(w, 1000);
|
||||
var t2 = bcoin.tx().input(t1.hash(), 0)
|
||||
.out(w, 24000)
|
||||
.out(w, 24000);
|
||||
var t3 = bcoin.tx().input(t1.hash(), 1)
|
||||
.input(t2.hash(), 0)
|
||||
.out(w, 23000);
|
||||
var t4 = bcoin.tx().input(t2.hash(), 1)
|
||||
.input(t3.hash(), 0)
|
||||
.out(w, 22000);
|
||||
w.addTX(t4);
|
||||
assert.equal(w.balance().toString(10), '22000');
|
||||
w.addTX(t1);
|
||||
assert.equal(w.balance().toString(10), '73000');
|
||||
w.addTX(t2);
|
||||
assert.equal(w.balance().toString(10), '47000');
|
||||
w.addTX(t3);
|
||||
assert.equal(w.balance().toString(10), '22000');
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user