From 7e98123ccdebcc483421aa44766462ef856a08f1 Mon Sep 17 00:00:00 2001 From: Wei Lu Date: Wed, 18 Jun 2014 14:29:02 +0800 Subject: [PATCH 1/4] wallet.getUnspentOutputs includes the pending field --- src/wallet.js | 3 ++- test/wallet.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wallet.js b/src/wallet.js index a77cd027d..48b9d8d3a 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -90,7 +90,8 @@ function Wallet(seed, network) { hash: hashAndIndex[0], outputIndex: parseInt(hashAndIndex[1]), address: output.address, - value: output.value + value: output.value, + pending: output.pending } } diff --git a/test/wallet.js b/test/wallet.js index eec0ac0f6..356c590ef 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -175,7 +175,8 @@ describe('Wallet', function() { "hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7", "outputIndex": 0, "address" : "1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv", - "value": 20000 + "value": 20000, + "pending": true } expectedOutputKey = expectedUtxo.hash + ":" + expectedUtxo.outputIndex }) @@ -185,7 +186,8 @@ describe('Wallet', function() { wallet.outputs[key] = { receive: key, address: utxo.address, - value: utxo.value + value: utxo.value, + pending: utxo.pending } } From 7e31668b693b37e0a4ee5f31eb2f65e2b59c8e96 Mon Sep 17 00:00:00 2001 From: Wei Lu Date: Wed, 18 Jun 2014 14:34:53 +0800 Subject: [PATCH 2/4] wallet: rename utxo.receive to utxo.from --- src/wallet.js | 10 +++++----- test/wallet.js | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wallet.js b/src/wallet.js index 48b9d8d3a..78be58463 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -77,14 +77,14 @@ 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], @@ -99,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 @@ -159,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 @@ -194,7 +194,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 diff --git a/test/wallet.js b/test/wallet.js index 356c590ef..a1f090588 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -184,7 +184,7 @@ 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, pending: utxo.pending @@ -352,7 +352,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) From 79a17d67ecfdb2ac3554d3fec80deab9ad0030c5 Mon Sep 17 00:00:00 2001 From: Wei Lu Date: Wed, 18 Jun 2014 21:16:17 +0800 Subject: [PATCH 3/4] wallet: do not overestimate fees when network has dustSoftThreshold --- src/wallet.js | 2 +- test/wallet.js | 54 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/wallet.js b/src/wallet.js index 78be58463..d3c3fbe14 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -235,7 +235,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) } diff --git a/test/wallet.js b/test/wallet.js index a1f090588..58a02834b 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -397,25 +397,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(){ @@ -423,6 +410,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) @@ -448,7 +470,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() From 82b1d8fbdcd5ffee0b500125aece6730a5daa5ba Mon Sep 17 00:00:00 2001 From: Wei Lu Date: Sat, 21 Jun 2014 14:26:38 +0800 Subject: [PATCH 4/4] wallet: do not delete pending incoming tx from outputs --- src/wallet.js | 8 +++++++- test/wallet.js | 25 ++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/wallet.js b/src/wallet.js index d3c3fbe14..65651f0bc 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -175,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] }) } diff --git a/test/wallet.js b/test/wallet.js index 58a02834b..456838ef1 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -264,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(){