wallet: .fill()
This commit is contained in:
parent
7f8e5e08af
commit
2c36c8692a
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user