wallet: .fill()

This commit is contained in:
Fedor Indutny 2014-05-11 01:43:20 +04:00
parent 7f8e5e08af
commit 2c36c8692a
3 changed files with 124 additions and 3 deletions

View File

@ -18,7 +18,6 @@ function TX(data, block) {
this.lock = data.lock || 0;
this.ts = data.ts || 0;
this.block = null;
this.funds = new bn(0);
this._hash = null;
this._raw = data._raw || null;
@ -94,8 +93,6 @@ TX.prototype._input = function _input(i, index) {
ex.script = input.script.length ? input.script : ex.script;
} else {
this.inputs.push(input);
if (input.out.tx)
this.funds.iadd(input.out.tx.outputs[input.out.index].value);
index = this.inputs.length - 1;
}
@ -250,6 +247,35 @@ TX.prototype.inputAddrs = function inputAddrs() {
});
};
TX.prototype.funds = function funds(side) {
if (side === 'in') {
var inputs = this.inputs.filter(function(input) {
return input.out.tx;
});
var acc = new bn(0);
if (inputs.length === 0)
return acc;
inputs.reduce(function(acc, input) {
return acc.iadd(input.out.tx.outputs[input.out.index].value);
}, acc);
return acc;
}
// Output
var acc = new bn(0);
if (this.outputs.length === 0)
return acc;
this.outputs.reduce(function(acc, output) {
return acc.iadd(output.value);
}, acc);
return acc;
};
TX.prototype.toJSON = function toJSON() {
// Compact representation
return {

View File

@ -1,6 +1,7 @@
var assert = require('assert');
var bcoin = require('../bcoin');
var hash = require('hash.js');
var bn = require('bn.js');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var utils = bcoin.utils;
@ -39,6 +40,11 @@ function Wallet(options, passphrase) {
this.prefix = 'bt/' + this.getAddress() + '/';
this.tx = new bcoin.txPool(this);
// Just a constants, actually
this.fee = 10000;
this.dust = 5460;
this._init();
}
util.inherits(Wallet, EventEmitter);
@ -241,6 +247,76 @@ Wallet.prototype.balance = function balance() {
return this.tx.balance();
};
Wallet.prototype.fill = function fill(tx, cb) {
// NOTE: tx should be prefilled with all outputs
var cost = tx.funds('out');
// Use initial fee for starters
var fee = 1;
// total = cost + fee
var total = cost.add(new bn(this.fee));
var lastAdded = -1;
function addInput(unspent, i) {
// Add new inputs until TX will have enough funds to cover both
// minimum post cost and fee
tx.input(unspent);
lastAdded = i;
return tx.funds('in').cmp(total) < 0;
}
// Transfer `total` funds maximum
var unspent = this.unspent();
unspent.every(addInput, this);
// Add dummy output (for `left`) to calculate maximum TX size
tx.out(this, new bn(0));
// Change fee value if it is more than 1024 bytes
// (10000 satoshi for every 1024 bytes)
do {
// Calculate maximum possible size after signing
var byteSize = tx.maxSize();
var addFee = Math.ceil(byteSize / 1024) - fee;
total.iadd(new bn(addFee * this.fee));
fee += addFee;
// Failed to get enough funds, add more inputs
if (tx.funds('in').cmp(total) < 0)
unspent.slice(lastAdded + 1).every(addInput, this);
} while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length);
// Still failing to get enough funds, notify caller
if (tx.funds('in').cmp(total) < 0) {
var err = new Error('Not enough funds');
err.minBalance = total;
return cb(err);
}
// How much money is left after sending outputs
var left = tx.funds('in').sub(total);
// Not enough money, transfer everything to owner
if (left.cmpn(this.dust) < 0) {
// NOTE: that this output is either `postCost` or one of the `dust` values
tx.outputs[tx.outputs.length - 2].value.iadd(left);
left = new bn(0);
}
// Change or remove last output if there is some money left
if (left.cmpn(0) === 0)
tx.outputs.pop();
else
tx.outputs[tx.outputs.length - 1].value = left;
// Sign transaction
this.sign(tx);
return tx;
};
Wallet.prototype.toJSON = function toJSON() {
return {
v: 1,

View File

@ -130,4 +130,23 @@ describe('Wallet', function() {
return tx.hash('hex') === f1.hash('hex');
}));
});
it('should fill tx with inputs', function() {
var w1 = bcoin.wallet();
var w2 = bcoin.wallet();
// Coinbase
var t1 = bcoin.tx().out(w1, 5460).out(w1, 5460).out(w1, 5460).out(w1, 5460);
// Fake TX should temporarly change output
w1.addTX(t1);
// Create new transaction
var t2 = bcoin.tx().out(w2, 5460);
w1.sign(t2);
w1.fill(t2);
assert.equal(t2.funds('in').toString(10), 16380);
assert.equal(t2.funds('out').toString(10), 6380);
});
});