Merge pull request #219 from weilu/utxo

Wallet fee & utxo fixups
This commit is contained in:
Daniel Cousens 2014-06-21 23:50:27 +10:00
commit f4940ccd48
2 changed files with 83 additions and 29 deletions

View file

@ -77,20 +77,21 @@ function Wallet(seed, network) {
utxo.forEach(function(uo){
validateUnspentOutput(uo)
var o = unspentOutputToOutput(uo)
outputs[o.receive] = o
outputs[o.from] = o
})
this.outputs = outputs
}
function outputToUnspentOutput(output){
var hashAndIndex = output.receive.split(":")
var hashAndIndex = output.from.split(":")
return {
hash: hashAndIndex[0],
outputIndex: parseInt(hashAndIndex[1]),
address: output.address,
value: output.value
value: output.value,
pending: output.pending
}
}
@ -98,7 +99,7 @@ function Wallet(seed, network) {
var hash = o.hash
var key = hash + ":" + o.outputIndex
return {
receive: key,
from: key,
address: o.address,
value: o.value,
pending: o.pending
@ -158,7 +159,7 @@ function Wallet(seed, network) {
var output = txid + ':' + i
me.outputs[output] = {
receive: output,
from: output,
value: txOut.value,
address: address,
pending: isPending
@ -174,7 +175,13 @@ function Wallet(seed, network) {
var output = txinId + ':' + txIn.index
if(me.outputs[output]) delete me.outputs[output]
if (!(output in me.outputs)) return
if (isPending) {
return me.outputs[output].pending = true
}
delete me.outputs[output]
})
}
@ -193,7 +200,7 @@ function Wallet(seed, network) {
var utxo = utxos[i]
addresses.push(utxo.address)
var outpoint = utxo.receive.split(':')
var outpoint = utxo.from.split(':')
tx.addInput(outpoint[0], parseInt(outpoint[1]))
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
@ -234,7 +241,7 @@ function Wallet(seed, network) {
function estimateFeePadChangeOutput(tx) {
var tmpTx = tx.clone()
tmpTx.addOutput(getChangeAddress(), 0)
tmpTx.addOutput(getChangeAddress(), network.dustSoftThreshold || 0)
return network.estimateFee(tmpTx)
}

View file

@ -175,7 +175,8 @@ describe('Wallet', function() {
"hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7",
"outputIndex": 0,
"address" : "1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv",
"value": 20000
"value": 20000,
"pending": true
}
expectedOutputKey = expectedUtxo.hash + ":" + expectedUtxo.outputIndex
})
@ -183,9 +184,10 @@ describe('Wallet', function() {
function addUtxoToOutput(utxo){
var key = utxo.hash + ":" + utxo.outputIndex
wallet.outputs[key] = {
receive: key,
from: key,
address: utxo.address,
value: utxo.value
value: utxo.value,
pending: utxo.pending
}
}
@ -262,12 +264,35 @@ describe('Wallet', function() {
})
describe("processPendingTx", function(){
it("sets the pending flag on output", function(){
it("incoming: sets the pending flag on output", function(){
wallet.addresses = [addresses[0]]
wallet.processPendingTx(tx)
verifyOutputAdded(0, true)
})
describe("when tx ins outpoint contains a known txhash:i", function(){
var spendTx
beforeEach(function(){
wallet.addresses = [addresses[0]]
wallet.processConfirmedTx(tx)
spendTx = Transaction.fromHex(fixtureTx2Hex)
})
it("outgoing: sets the pending flag on output instead of deleting it", function(){
var txIn = spendTx.ins[0]
var txInId = new Buffer(txIn.hash)
Array.prototype.reverse.call(txInId)
txInId = txInId.toString('hex')
var key = txInId + ':' + txIn.index
assert(!wallet.outputs[key].pending)
wallet.processPendingTx(spendTx)
assert(wallet.outputs[key].pending)
})
})
})
describe('processConfirmedTx', function(){
@ -350,7 +375,7 @@ describe('Wallet', function() {
var txOut = tx.outs[index]
var key = tx.getId() + ":" + index
var output = wallet.outputs[key]
assert.equal(output.receive, key)
assert.equal(output.from, key)
assert.equal(output.value, txOut.value)
assert.equal(output.pending, pending)
@ -395,25 +420,12 @@ describe('Wallet', function() {
wallet.setUnspentOutputs(utxo)
})
describe('choosing utxo', function(){
it('calculates fees', function(){
var tx = wallet.createTx(to, value)
assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
assert.equal(tx.ins[0].index, 0)
})
describe('transaction fee', function(){
it('allows fee to be specified', function(){
var fee = 30000
var tx = wallet.createTx(to, value, fee)
assert.equal(tx.ins.length, 2)
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
assert.equal(tx.ins[0].index, 0)
assert.deepEqual(tx.ins[1].hash, fakeTxHash(2))
assert.equal(tx.ins[1].index, 1)
assert.equal(getFee(wallet, tx), fee)
})
it('allows fee to be set to zero', function(){
@ -421,6 +433,41 @@ describe('Wallet', function() {
var fee = 0
var tx = wallet.createTx(to, value, fee)
assert.equal(getFee(wallet, tx), fee)
})
it('does not overestimate fees when network has dustSoftThreshold', function(){
var wallet = new Wallet(seed, networks.litecoin)
var address = wallet.generateAddress()
wallet.setUnspentOutputs([{
hash: fakeTxId(0),
outputIndex: 0,
address: address,
value: 500000
}])
value = 200000
var tx = wallet.createTx(address, value)
assert.equal(getFee(wallet, tx), 100000)
})
function getFee(wallet, tx) {
var inputValue = tx.ins.reduce(function(memo, input){
var id = Array.prototype.reverse.call(input.hash).toString('hex')
return memo + wallet.outputs[id + ':' + input.index].value
}, 0)
return tx.outs.reduce(function(memo, output){
return memo - output.value
}, inputValue)
}
})
describe('choosing utxo', function(){
it('takes fees into account', function(){
var tx = wallet.createTx(to, value)
assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
assert.equal(tx.ins[0].index, 0)
@ -446,7 +493,7 @@ describe('Wallet', function() {
})
})
describe(networks.testnet, function(){
describe('works for testnet', function(){
it('should create transaction', function(){
var wallet = new Wallet(seed, networks.testnet)
var address = wallet.generateAddress()