diff --git a/src/wallet.js b/src/wallet.js index 8c7ab56..ec19147 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -135,31 +135,35 @@ var Wallet = function (seed, options) { return value == undefined } - // Processes a transaction object - // If "verified" is true, then we trust the transaction as "final" - this.processTx = function(tx, verified) { + this.processTx = function(tx) { var txhash = convert.bytesToHex(tx.getHash()) - for (var i = 0; i < tx.outs.length; i++) { - if (this.addresses.indexOf(tx.outs[i].address.toString()) >= 0) { - me.outputs[txhash+':'+i] = { - output: txhash+':'+i, - value: tx.outs[i].value, - address: tx.outs[i].address.toString(), - timestamp: new Date().getTime() / 1000, - pending: true + + tx.outs.forEach(function(txOut, i){ + var address = txOut.address.toString() + if (isMyAddress(address)) { + var output = txhash+':'+i + me.outputs[output] = { + output: output, + value: txOut.value, + address: address, + scriptPubKey: convert.bytesToHex(txOut.script.buffer) //TODO: txOut.scriptPubKey() + // timestamp: new Date().getTime() / 1000, + // pending: true } } - } - for (var i = 0; i < tx.ins.length; i++) { - var op = tx.ins[i].outpoint + }) + + tx.ins.forEach(function(txIn, i){ + var op = txIn.outpoint var o = me.outputs[op.hash+':'+op.index] if (o) { o.spend = txhash+':'+i - o.spendpending = true - o.timestamp = new Date().getTime() / 1000 + // o.spendpending = true + // o.timestamp = new Date().getTime() / 1000 } - } + }) } + // Processes an output from an external source of the form // { output: txhash:index, value: integer, address: address } // Excellent compatibility with SX and pybitcointools @@ -276,6 +280,18 @@ var Wallet = function (seed, options) { throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.') } } + + function isReceiveAddress(address){ + return me.addresses.indexOf(address) > -1 + } + + function isChangeAddress(address){ + return me.changeAddresses.indexOf(address) > -1 + } + + function isMyAddress(address) { + return isReceiveAddress(address) || isChangeAddress(address) + } }; module.exports = Wallet; diff --git a/test/fixtures/mainnet_tx.json b/test/fixtures/mainnet_tx.json new file mode 100644 index 0000000..f42bd7b --- /dev/null +++ b/test/fixtures/mainnet_tx.json @@ -0,0 +1,4 @@ +{ + "prevTx": "0100000001e0214ebebb0fd3414d3fdc0dbf3b0f4b247a296cafc984558622c3041b0fcc9b010000008b48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8ffffffff03a0860100000000001976a91400ea3576c8fcb0bc8392f10e23a3425ae24efea888ac40420f00000000001976a91477890e8ec967c5fd4316c489d171fd80cf86997188acf07cd210000000001976a9146fb93c557ee62b109370fd9003e456917401cbfa88ac00000000", + "tx": "0100000001576bc3c3285dbdccd8c3cbd8c03e10d7f77a5c839c744f34c3eb00511059b80c000000006b483045022100a82a31607b837c1ae510ae3338d1d3c7cbd57c15e322ab6e5dc927d49bffa66302205f0db6c90f1fae3c8db4ebfa753d7da1b2343d653ce0331aa94ed375e6ba366c0121020497bfc87c3e97e801414fed6a0db4b8c2e01c46e2cf9dff59b406b52224a76bffffffff02409c0000000000001976a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac50c30000000000001976a91477890e8ec967c5fd4316c489d171fd80cf86997188ac00000000" +} diff --git a/test/wallet.js b/test/wallet.js index 37aa4f7..a7161e2 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -1,10 +1,15 @@ var Wallet = require('../src/wallet.js') var HDNode = require('../src/hdwallet.js') +var Transaction = require('../src/transaction.js').Transaction var convert = require('../src/convert.js') var assert = require('assert') var SHA256 = require('crypto-js/sha256') var Crypto = require('crypto-js') +var fixtureTxes = require('./fixtures/mainnet_tx') +var fixtureTx1Hex = fixtureTxes.prevTx +var fixtureTx2Hex = fixtureTxes.tx + describe('Wallet', function() { var seed, wallet; beforeEach(function(){ @@ -234,6 +239,81 @@ describe('Wallet', function() { }) }) + describe('processTx', function(){ + var tx; + + beforeEach(function(){ + tx = Transaction.deserialize(fixtureTx1Hex) + }) + + 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 = [tx.outs[0].address.toString()] + + wallet.processTx(tx) + + assert.equal(outputCount(), totalOuts + 1) + verifyOutputAdded(0) + }) + + it("works for change address", function(){ + var totalOuts = outputCount() + wallet.changeAddresses = [tx.outs[1].address.toString()] + + 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 = convert.bytesToHex(tx.getHash()) + ":" + index + var output = wallet.outputs[key] + assert.equal(output.output, key) + assert.equal(output.value, txOut.value) + assert.equal(output.address, txOut.address) + assert.equal(output.scriptPubKey, convert.bytesToHex(txOut.script.buffer)) + } + }) + + describe("when tx ins outpoint contains a known txhash:i, the corresponding 'output' gets updated", function(){ + beforeEach(function(){ + wallet.addresses = [tx.outs[0].address.toString()] // the address fixtureTx2 is spending from + wallet.processTx(tx) + + tx = Transaction.deserialize(fixtureTx2Hex) + }) + + it("does not add to wallet.outputs", function(){ + var outputs = wallet.outputs + wallet.processTx(tx) + 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, convert.bytesToHex(tx.getHash()) + ':' + 0) + }) + }) + + it("does nothing when none of the involved addresses belong to the wallet", function(){ + var outputs = wallet.outputs + wallet.processTx(tx) + assert.deepEqual(wallet.outputs, outputs) + }) + }) + function assertEqual(obj1, obj2){ assert.equal(obj1.toString(), obj2.toString()) }