Wallet: cleanup createTx control flow
Unknowingly this also revealed a subtle bug in the previous implementation which allowed the creation of transactions even when no UTXOs existed.
This commit is contained in:
parent
13f95d4ecf
commit
50e9a09a8c
2 changed files with 35 additions and 32 deletions
|
@ -179,34 +179,40 @@ function Wallet(seed, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createTx = function(to, value, fixedFee, changeAddress) {
|
this.createTx = function(to, value, fixedFee, changeAddress) {
|
||||||
checkDust(value)
|
if (isDust(value)) throw new Error("Value must be above dust threshold")
|
||||||
|
|
||||||
|
var utxos = getCandidateOutputs(value)
|
||||||
|
var accum = 0
|
||||||
|
var subTotal = value
|
||||||
|
|
||||||
var tx = new Transaction()
|
var tx = new Transaction()
|
||||||
tx.addOutput(to, value)
|
tx.addOutput(to, value)
|
||||||
|
|
||||||
var utxo = getCandidateOutputs(value)
|
for (var i = 0; i < utxos.length; ++i) {
|
||||||
var totalInValue = 0
|
var utxo = utxos[i]
|
||||||
for(var i=0; i<utxo.length; i++){
|
|
||||||
var output = utxo[i]
|
|
||||||
tx.addInput(output.receive)
|
|
||||||
|
|
||||||
totalInValue += output.value
|
tx.addInput(utxo.receive)
|
||||||
if(totalInValue < value) continue
|
accum += utxo.value
|
||||||
|
|
||||||
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
|
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
|
||||||
if(totalInValue < value + fee) continue
|
|
||||||
|
|
||||||
var change = totalInValue - value - fee
|
subTotal = value + fee
|
||||||
if(change > 0 && !isDust(change)) {
|
if (accum >= subTotal) {
|
||||||
tx.addOutput(changeAddress || getChangeAddress(), change)
|
var change = accum - subTotal
|
||||||
|
|
||||||
|
if (!isDust(change)) {
|
||||||
|
tx.addOutput(changeAddress || getChangeAddress(), change)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkInsufficientFund(totalInValue, value, fee)
|
if (accum < subTotal) {
|
||||||
|
throw new Error('Not enough funds: ' + accum + ' < ' + subTotal)
|
||||||
|
}
|
||||||
|
|
||||||
this.sign(tx)
|
this.sign(tx)
|
||||||
|
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +238,6 @@ function Wallet(seed, options) {
|
||||||
return amount <= me.dustThreshold
|
return amount <= me.dustThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDust(value){
|
|
||||||
if (isNullOrUndefined(value) || isDust(value)) {
|
|
||||||
throw new Error("Value must be above dust threshold")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCandidateOutputs(value){
|
function getCandidateOutputs(value){
|
||||||
var unspent = []
|
var unspent = []
|
||||||
for (var key in me.outputs){
|
for (var key in me.outputs){
|
||||||
|
@ -263,13 +263,6 @@ function Wallet(seed, options) {
|
||||||
return me.changeAddresses[me.changeAddresses.length - 1]
|
return me.changeAddresses[me.changeAddresses.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkInsufficientFund(totalInValue, value, fee) {
|
|
||||||
if(totalInValue < value + fee) {
|
|
||||||
throw new Error('Not enough money to send funds including transaction fee. Have: ' +
|
|
||||||
totalInValue + ', needed: ' + (value + fee))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sign = function(tx) {
|
this.sign = function(tx) {
|
||||||
tx.ins.forEach(function(inp,i) {
|
tx.ins.forEach(function(inp,i) {
|
||||||
var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index]
|
var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index]
|
||||||
|
|
|
@ -492,13 +492,23 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('testnet', function(){
|
describe('testnet', function(){
|
||||||
it('should create transaction', function(){
|
it('should create transaction', function(){
|
||||||
var to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue'
|
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, {network: 'testnet'})
|
||||||
var tx = wallet.createTx(to, value)
|
var address = wallet.generateAddress()
|
||||||
|
|
||||||
|
wallet.setUnspentOutputs([{
|
||||||
|
hash: fakeTxHash(0),
|
||||||
|
outputIndex: 0,
|
||||||
|
address: address,
|
||||||
|
value: value
|
||||||
|
}])
|
||||||
|
|
||||||
|
var to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue'
|
||||||
|
var toValue = value - 20000
|
||||||
|
|
||||||
|
var tx = wallet.createTx(to, toValue)
|
||||||
assert.equal(tx.outs.length, 1)
|
assert.equal(tx.outs.length, 1)
|
||||||
assert.equal(tx.outs[0].address.toString(), to)
|
assert.equal(tx.outs[0].address.toString(), to)
|
||||||
assert.equal(tx.outs[0].value, value)
|
assert.equal(tx.outs[0].value, toValue)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -605,7 +615,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
wallet.createTx(to, value)
|
wallet.createTx(to, value)
|
||||||
}, /Not enough money to send funds including transaction fee. Have: 1420000, needed: 1420001/)
|
}, /Not enough funds: 1420000 < 1420001/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue