Merge pull request #209 from weilu/pending-utxo

Allow marking utxo as pending
This commit is contained in:
Daniel Cousens 2014-06-14 10:06:03 +10:00
commit a98391572a
2 changed files with 105 additions and 94 deletions

View file

@ -68,7 +68,7 @@ function Wallet(seed, network) {
for(var key in this.outputs){ for(var key in this.outputs){
var output = this.outputs[key] var output = this.outputs[key]
if(!output.spend) utxo.push(outputToUnspentOutput(output)) utxo.push(outputToUnspentOutput(output))
} }
return utxo return utxo
@ -103,7 +103,8 @@ function Wallet(seed, network) {
return { return {
receive: key, receive: key,
address: o.address, address: o.address,
value: o.value value: o.value,
pending: o.pending
} }
} }
@ -136,7 +137,15 @@ function Wallet(seed, network) {
return value == undefined return value == undefined
} }
this.processTx = function(tx) { this.processPendingTx = function(tx){
processTx(tx, true)
}
this.processConfirmedTx = function(tx){
processTx(tx, false)
}
function processTx(tx, isPending) {
var txhash = tx.getHash() var txhash = tx.getHash()
tx.outs.forEach(function(txOut, i){ tx.outs.forEach(function(txOut, i){
@ -155,17 +164,16 @@ function Wallet(seed, network) {
receive: output, receive: output,
value: txOut.value, value: txOut.value,
address: address, address: address,
pending: isPending
} }
} }
}) })
tx.ins.forEach(function(txIn, i){ tx.ins.forEach(function(txIn, i){
var op = txIn.outpoint var op = txIn.outpoint
var output = op.hash + ':' + op.index
var o = me.outputs[op.hash + ':' + op.index] if(me.outputs[output]) delete me.outputs[output]
if (o) {
o.spend = txhash + ':' + i
}
}) })
} }
@ -210,7 +218,7 @@ function Wallet(seed, network) {
for (var key in me.outputs) { for (var key in me.outputs) {
var output = me.outputs[key] var output = me.outputs[key]
if (!output.spend) unspent.push(output) if (!output.pending) unspent.push(output)
} }
var sortByValueDesc = unspent.sort(function(o1, o2){ var sortByValueDesc = unspent.sort(function(o1, o2){

View file

@ -195,14 +195,6 @@ describe('Wallet', function() {
assert.equal(wallet.getBalance(), 40000) assert.equal(wallet.getBalance(), 40000)
}) })
it('excludes spent outputs', function(){
addUtxoToOutput(expectedUtxo)
addUtxoToOutput(utxo1)
wallet.outputs[utxo1.hash + ':' + utxo1.outputIndex].spend = "sometxn:m"
assert.equal(wallet.getBalance(), 20000)
})
}) })
describe('getUnspentOutputs', function(){ describe('getUnspentOutputs', function(){
@ -213,11 +205,6 @@ describe('Wallet', function() {
it('parses wallet outputs to the expect format', function(){ it('parses wallet outputs to the expect format', function(){
assert.deepEqual(wallet.getUnspentOutputs(), [expectedUtxo]) assert.deepEqual(wallet.getUnspentOutputs(), [expectedUtxo])
}) })
it('excludes spent outputs', function(){
wallet.outputs[expectedOutputKey].spend = "sometxn:m"
assert.deepEqual(wallet.getUnspentOutputs(), [])
})
}) })
describe('setUnspentOutputs', function(){ describe('setUnspentOutputs', function(){
@ -252,7 +239,7 @@ describe('Wallet', function() {
}) })
}) })
describe('processTx', function(){ describe('Process transaction', function(){
var addresses var addresses
var tx var tx
@ -266,90 +253,105 @@ describe('Wallet', function() {
tx = Transaction.fromHex(fixtureTx1Hex) tx = Transaction.fromHex(fixtureTx1Hex)
}) })
it('does not fail on scripts with no corresponding Address', function() { describe("processPendingTx", function(){
var pubKey = wallet.getPrivateKey(0).pub it("sets the pending flag on output", function(){
var script = Script.createPubKeyScriptPubKey(pubKey)
var tx2 = new Transaction()
tx2.addInput(fakeTxHash(1), 0)
// FIXME: Transaction doesn't support custom ScriptPubKeys... yet
// So for now, we hijack the script with our own, and undefine the cached address
tx2.addOutput(addresses[0], 10000)
tx2.outs[0].script = script
tx2.outs[0].address = undefined
wallet.processTx(tx2)
})
describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.outputs", function(){
it("works for receive address", function(){
var totalOuts = outputCount()
wallet.addresses = [addresses[0]] wallet.addresses = [addresses[0]]
wallet.processTx(tx) wallet.processPendingTx(tx)
assert.equal(outputCount(), totalOuts + 1) verifyOutputAdded(0, true)
verifyOutputAdded(0)
}) })
it("works for change address", function(){
var totalOuts = outputCount()
wallet.changeAddresses = [addresses[1]]
wallet.processTx(tx)
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(1)
})
function outputCount(){
return Object.keys(wallet.outputs).length
}
function verifyOutputAdded(index) {
var txOut = tx.outs[index]
var key = tx.getHash() + ":" + index
var output = wallet.outputs[key]
assert.equal(output.receive, key)
assert.equal(output.value, txOut.value)
var txOutAddress = Address.fromScriptPubKey(txOut.script).toString()
assert.equal(output.address, txOutAddress)
}
}) })
describe("when tx ins outpoint contains a known txhash:i, the corresponding 'output' gets updated", function(){ describe('processConfirmedTx', function(){
beforeEach(function(){
wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input
wallet.processTx(tx)
tx = Transaction.fromHex(fixtureTx2Hex) it('does not fail on scripts with no corresponding Address', function() {
var pubKey = wallet.getPrivateKey(0).pub
var script = Script.createPubKeyScriptPubKey(pubKey)
var tx2 = new Transaction()
tx2.addInput(fakeTxHash(1), 0)
// FIXME: Transaction doesn't support custom ScriptPubKeys... yet
// So for now, we hijack the script with our own, and undefine the cached address
tx2.addOutput(addresses[0], 10000)
tx2.outs[0].script = script
tx2.outs[0].address = undefined
wallet.processConfirmedTx(tx2)
}) })
it("does not add to wallet.outputs", function(){ describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.outputs", function(){
it("works for receive address", function(){
var totalOuts = outputCount()
wallet.addresses = [addresses[0]]
wallet.processConfirmedTx(tx)
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(0, false)
})
it("works for change address", function(){
var totalOuts = outputCount()
wallet.changeAddresses = [addresses[1]]
wallet.processConfirmedTx(tx)
assert.equal(outputCount(), totalOuts + 1)
verifyOutputAdded(1, false)
})
function outputCount(){
return Object.keys(wallet.outputs).length
}
})
describe("when tx ins outpoint contains a known txhash:i", function(){
beforeEach(function(){
wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input
wallet.processConfirmedTx(tx)
tx = Transaction.fromHex(fixtureTx2Hex)
})
it("does not add to wallet.outputs", function(){
var outputs = wallet.outputs
wallet.processConfirmedTx(tx)
assert.deepEqual(wallet.outputs, outputs)
})
it("deletes corresponding 'output'", function(){
wallet.processConfirmedTx(tx)
var txIn = tx.ins[0]
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
var output = wallet.outputs[key]
assert.equal(output, undefined)
})
})
it("does nothing when none of the involved addresses belong to the wallet", function(){
var outputs = wallet.outputs var outputs = wallet.outputs
wallet.processTx(tx) wallet.processConfirmedTx(tx)
assert.deepEqual(wallet.outputs, outputs) assert.deepEqual(wallet.outputs, outputs)
}) })
it("sets spend with the transaction hash and input index", function(){
wallet.processTx(tx)
var txIn = tx.ins[0]
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
var output = wallet.outputs[key]
assert.equal(output.spend, tx.getHash() + ':' + 0)
})
}) })
it("does nothing when none of the involved addresses belong to the wallet", function(){ function verifyOutputAdded(index, pending) {
var outputs = wallet.outputs var txOut = tx.outs[index]
wallet.processTx(tx) var key = tx.getHash() + ":" + index
assert.deepEqual(wallet.outputs, outputs) var output = wallet.outputs[key]
}) assert.equal(output.receive, key)
assert.equal(output.value, txOut.value)
assert.equal(output.pending, pending)
var txOutAddress = Address.fromScriptPubKey(txOut.script).toString()
assert.equal(output.address, txOutAddress)
}
}) })
describe('createTx', function(){ describe('createTx', function(){
var to, value var to, value
var address1, address2 var address1, address2
@ -412,23 +414,24 @@ describe('Wallet', function() {
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
}) })
it('ignores spent outputs', function(){ it('ignores pending outputs', function(){
utxo.push( utxo.push(
{ {
"hash": fakeTxHash(4), "hash": fakeTxHash(4),
"outputIndex": 0, "outputIndex": 0,
"address" : address2, "address" : address2,
"value": 530000 // enough but spent before createTx "value": 530000,
"pending": true
} }
) )
wallet.setUnspentOutputs(utxo) wallet.setUnspentOutputs(utxo)
wallet.outputs[fakeTxHash(4) + ":" + 0].spend = fakeTxHash(5) + ":" + 0
var tx = wallet.createTx(to, value) var tx = wallet.createTx(to, value)
assert.equal(tx.ins.length, 1) assert.equal(tx.ins.length, 1)
assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 })
}) })
}) })
describe(networks.testnet, function(){ describe(networks.testnet, function(){