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.lock = data.lock || 0;
|
||||||
this.ts = data.ts || 0;
|
this.ts = data.ts || 0;
|
||||||
this.block = null;
|
this.block = null;
|
||||||
this.funds = new bn(0);
|
|
||||||
|
|
||||||
this._hash = null;
|
this._hash = null;
|
||||||
this._raw = data._raw || 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;
|
ex.script = input.script.length ? input.script : ex.script;
|
||||||
} else {
|
} else {
|
||||||
this.inputs.push(input);
|
this.inputs.push(input);
|
||||||
if (input.out.tx)
|
|
||||||
this.funds.iadd(input.out.tx.outputs[input.out.index].value);
|
|
||||||
index = this.inputs.length - 1;
|
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() {
|
TX.prototype.toJSON = function toJSON() {
|
||||||
// Compact representation
|
// Compact representation
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var bcoin = require('../bcoin');
|
var bcoin = require('../bcoin');
|
||||||
var hash = require('hash.js');
|
var hash = require('hash.js');
|
||||||
|
var bn = require('bn.js');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
@ -39,6 +40,11 @@ function Wallet(options, passphrase) {
|
|||||||
|
|
||||||
this.prefix = 'bt/' + this.getAddress() + '/';
|
this.prefix = 'bt/' + this.getAddress() + '/';
|
||||||
this.tx = new bcoin.txPool(this);
|
this.tx = new bcoin.txPool(this);
|
||||||
|
|
||||||
|
// Just a constants, actually
|
||||||
|
this.fee = 10000;
|
||||||
|
this.dust = 5460;
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
util.inherits(Wallet, EventEmitter);
|
util.inherits(Wallet, EventEmitter);
|
||||||
@ -241,6 +247,76 @@ Wallet.prototype.balance = function balance() {
|
|||||||
return this.tx.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() {
|
Wallet.prototype.toJSON = function toJSON() {
|
||||||
return {
|
return {
|
||||||
v: 1,
|
v: 1,
|
||||||
|
|||||||
@ -130,4 +130,23 @@ describe('Wallet', function() {
|
|||||||
return tx.hash('hex') === f1.hash('hex');
|
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