From b0317be4d53e1f285a32e4a835e246230947267b Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:43:47 +1000 Subject: [PATCH 01/29] tests: formatting --- test/transaction.js | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index e9bbec6..c9ffab0 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -44,15 +44,15 @@ describe('Transaction', function() { assert.equal(buffer.toString('hex'), serializedTx) }) - it('decodes version correctly', function(){ + it('decodes version correctly', function() { assert.equal(tx.version, 1) }) - it('decodes locktime correctly', function(){ + it('decodes locktime correctly', function() { assert.equal(tx.locktime, 0) }) - it('decodes inputs correctly', function(){ + it('decodes inputs correctly', function() { assert.equal(tx.ins.length, 1) var input = tx.ins[0] @@ -65,7 +65,7 @@ describe('Transaction', function() { "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") }) - it('decodes outputs correctly', function(){ + it('decodes outputs correctly', function() { assert.equal(tx.outs.length, 1) var output = tx.outs[0] @@ -74,7 +74,7 @@ describe('Transaction', function() { assert.deepEqual(output.script, Address.fromBase58Check('n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9').toOutputScript()) }) - it('assigns hash to deserialized object', function(){ + it('assigns hash to deserialized object', function() { var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c" assert.equal(tx.hash, hashHex) }) @@ -107,18 +107,18 @@ describe('Transaction', function() { tx = new Transaction() }) - describe('addInput', function(){ - it('allows a Transaction object to be passed in', function(){ + describe('addInput', function() { + it('allows a Transaction object to be passed in', function() { tx.addInput(prevTx, 0) verifyTransactionIn() }) - it('allows a Transaction hash to be passed in', function(){ + it('allows a Transaction hash to be passed in', function() { tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0) verifyTransactionIn() }) - it('allows a TransactionIn object to be passed in', function(){ + it('allows a TransactionIn object to be passed in', function() { var txCopy = tx.clone() txCopy.addInput(prevTx, 0) var transactionIn = txCopy.ins[0] @@ -127,7 +127,7 @@ describe('Transaction', function() { verifyTransactionIn() }) - it('allows a string in the form of txhash:index to be passed in', function(){ + it('allows a string in the form of txhash:index to be passed in', function() { tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0") verifyTransactionIn() }) @@ -145,23 +145,23 @@ describe('Transaction', function() { } }) - describe('addOutput', function(){ - it('allows an address and a value to be passed in', function(){ + describe('addOutput', function() { + it('allows an address and a value to be passed in', function() { tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000) verifyTransactionOut() }) - it('allows an Address object and value to be passed in', function(){ + it('allows an Address object and value to be passed in', function() { tx.addOutput(Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'), 40000) verifyTransactionOut() }) - it('allows a string in the form of address:index to be passed in', function(){ + it('allows a string in the form of address:index to be passed in', function() { tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") verifyTransactionOut() }) - it('allows a TransactionOut object to be passed in', function(){ + it('allows a TransactionOut object to be passed in', function() { var txCopy = tx.clone() txCopy.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") var transactionOut = txCopy.outs[0] @@ -170,7 +170,7 @@ describe('Transaction', function() { verifyTransactionOut() }) - it('supports alternative networks', function(){ + it('supports alternative networks', function() { var addr = 'mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR' tx.addOutput(addr, 40000) @@ -179,7 +179,7 @@ describe('Transaction', function() { assert.equal(tx.outs[0].address.toString(), addr) }) - function verifyTransactionOut(){ + function verifyTransactionOut() { assert.equal(tx.outs.length, 1) var output = tx.outs[0] @@ -188,8 +188,8 @@ describe('Transaction', function() { } }) - describe('sign', function(){ - it('works', function(){ + describe('sign', function() { + it('works', function() { tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0") tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") tx.addOutput("1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd:50000") @@ -204,14 +204,14 @@ describe('Transaction', function() { }) }) - describe('validateInput', function(){ + describe('validateInput', function() { var validTx beforeEach(function() { validTx = Transaction.fromHex(fixtureTx2Hex) }) - it('returns true for valid signature', function(){ + it('returns true for valid signature', function() { var key = ECKey.fromWIF('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb') var script = prevTx.outs[0].script var sig = new Buffer(validTx.ins[0].script.chunks[0]) @@ -220,23 +220,23 @@ describe('Transaction', function() { }) }) - describe('estimateFee', function(){ - it('works for fixture tx 1', function(){ + describe('estimateFee', function() { + it('works for fixture tx 1', function() { var tx = Transaction.fromHex(fixtureTx1Hex) assert.equal(tx.estimateFee(), 20000) }) - it('works for fixture big tx', function(){ + it('works for fixture big tx', function() { var tx = Transaction.fromHex(fixtureTxBigHex) assert.equal(tx.estimateFee(), 60000) }) - it('allow feePerKb to be passed in as an argument', function(){ + it('allow feePerKb to be passed in as an argument', function() { var tx = Transaction.fromHex(fixtureTx2Hex) assert.equal(tx.estimateFee(10000), 10000) }) - it('allow feePerKb to be set to 0', function(){ + it('allow feePerKb to be set to 0', function() { var tx = Transaction.fromHex(fixtureTx2Hex) assert.equal(tx.estimateFee(0), 0) }) From 8b5647b0b96777c610f976380919e5f25b9b0179 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 16 May 2014 17:12:39 +1000 Subject: [PATCH 02/29] Transaction: remove TxIn/TxOut exports --- src/index.js | 4 +--- src/transaction.js | 6 +----- src/wallet.js | 2 +- test/bitcoin.core.js | 2 +- test/transaction.js | 2 +- test/wallet.js | 2 +- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index 5af8210..fc46f9b 100644 --- a/src/index.js +++ b/src/index.js @@ -15,9 +15,7 @@ module.exports = { HDNode: require('./hdnode'), Script: require('./script'), scripts: require('./scripts'), - Transaction: T.Transaction, - TransactionIn: T.TransactionIn, - TransactionOut: T.TransactionOut, + Transaction: require('./transaction'), networks: require('./networks'), Wallet: require('./wallet') } diff --git a/src/transaction.js b/src/transaction.js index 6ff1c23..7308d1b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -416,8 +416,4 @@ TransactionOut.prototype.clone = function() { }) } -module.exports = { - Transaction: Transaction, - TransactionIn: TransactionIn, - TransactionOut: TransactionOut -} +module.exports = Transaction diff --git a/src/wallet.js b/src/wallet.js index aa9355c..b4acd3f 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -4,7 +4,7 @@ var rng = require('secure-random') var Address = require('./address') var HDNode = require('./hdnode') -var Transaction = require('./transaction').Transaction +var Transaction = require('./transaction') function Wallet(seed, network) { network = network || networks.bitcoin diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 4cd8238..b058385 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -6,7 +6,7 @@ var networks = require('../src/networks') var Address = require('../src/address') var BigInteger = require('bigi') var ECKey = require('../src/eckey') -var Transaction = require('../src/transaction').Transaction +var Transaction = require('../src/transaction') var Script = require('../src/script') var base58_encode_decode = require("./fixtures/core/base58_encode_decode.json") diff --git a/test/transaction.js b/test/transaction.js index c9ffab0..8341fec 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -4,7 +4,7 @@ var scripts = require('../src/scripts') var Address = require('../src/address') var ECKey = require('../src/eckey') -var Transaction = require('../src/transaction').Transaction +var Transaction = require('../src/transaction') var Script = require('../src/script') var fixtureTxes = require('./fixtures/mainnet_tx') diff --git a/test/wallet.js b/test/wallet.js index 0d857ad..88b7dcd 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -6,7 +6,7 @@ var scripts = require('../src/scripts') var Address = require('../src/address') var HDNode = require('../src/hdnode') -var Transaction = require('../src/transaction').Transaction +var Transaction = require('../src/transaction') var Wallet = require('../src/wallet') var fixtureTxes = require('./fixtures/mainnet_tx') From 40f0c9116262d8098374649815a37924f6b20d4c Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 16 May 2014 17:20:45 +1000 Subject: [PATCH 03/29] Transaction: use TxIn/TxOut consistently --- src/transaction.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 7308d1b..7403696 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -293,14 +293,14 @@ Transaction.fromBuffer = function(buffer) { var script = readSlice(scriptLen) var sequence = readUInt32() - ins.push({ + ins.push(new TransactionIn({ outpoint: { hash: hash.toString('hex'), - index: vout, + index: vout }, script: Script.fromBuffer(script), sequence: sequence - }) + })) } var voutLen = readVarInt() @@ -310,10 +310,10 @@ Transaction.fromBuffer = function(buffer) { var scriptLen = readVarInt() var script = readSlice(scriptLen) - outs.push({ + outs.push(new TransactionOut({ value: value, script: Script.fromBuffer(script) - }) + })) } var locktime = readUInt32() From 1f2becbb455c4e79df1399685898a40396a1f7c4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 19 May 2014 08:49:41 +1000 Subject: [PATCH 04/29] Transaction: move all constants to top --- src/transaction.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 7403696..a4d18a8 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -12,6 +12,10 @@ var ECKey = require('./eckey') var Script = require('./script') var DEFAULT_SEQUENCE = 0xffffffff +var SIGHASH_ALL = 0x01 +var SIGHASH_NONE = 0x02 +var SIGHASH_SINGLE = 0x03 +var SIGHASH_ANYONECANPAY = 0x80 function Transaction(doc) { if (!(this instanceof Transaction)) { return new Transaction(doc) } @@ -179,11 +183,6 @@ Transaction.prototype.toHex = function() { return this.toBuffer().toString('hex') } -var SIGHASH_ALL = 0x01 -var SIGHASH_NONE = 0x02 -var SIGHASH_SINGLE = 0x03 -var SIGHASH_ANYONECANPAY = 0x80 - /** * Hash transaction for signing a specific input. * From a6b9dd9473ca04c11f15804fd1eefa39f36c0566 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 19 May 2014 09:31:16 +1000 Subject: [PATCH 05/29] Transaction: remove hash:index notation --- src/transaction.js | 31 +++++++------------------------ src/wallet.js | 5 +++-- test/transaction.js | 18 ++++-------------- 3 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index a4d18a8..c75b69b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -29,15 +29,15 @@ function Transaction(doc) { if (doc.version) this.version = doc.version; if (doc.locktime) this.locktime = doc.locktime; if (doc.ins && doc.ins.length) { - doc.ins.forEach(function(input) { - this.addInput(new TransactionIn(input)) - }, this) + this.ins = doc.ins.map(function(input) { + return new TransactionIn(input) + }) } if (doc.outs && doc.outs.length) { - doc.outs.forEach(function(output) { - this.addOutput(new TransactionOut(output)) - }, this) + this.outs = doc.outs.map(function(output) { + return new TransactionOut(output) + }) } this.hash = this.hash || this.getHash() @@ -52,7 +52,6 @@ function Transaction(doc) { * - An existing TransactionIn object * - A transaction and an index * - A transaction hash and an index - * - A single string argument of the form txhash:index * * Note that this method does not sign the created input. */ @@ -62,15 +61,7 @@ Transaction.prototype.addInput = function (tx, outIndex) { return } - var hash - if (arguments[0].length > 65) { - var args = arguments[0].split(':') - hash = args[0] - outIndex = parseInt(args[1]) - - } else { - hash = typeof tx === "string" ? tx : tx.hash - } + var hash = typeof tx === "string" ? tx : tx.hash this.ins.push(new TransactionIn({ outpoint: { @@ -88,9 +79,7 @@ Transaction.prototype.addInput = function (tx, outIndex) { * * i) An existing TransactionOut object * ii) An address object or a string address, and a value - * iii) An address:value string * - * FIXME: This is a bit convoluted */ Transaction.prototype.addOutput = function (address, value) { if (arguments[0] instanceof TransactionOut) { @@ -99,12 +88,6 @@ Transaction.prototype.addOutput = function (address, value) { } if (typeof address === 'string') { - if (arguments[0].indexOf(':') >= 0) { - var args = arguments[0].split(':') - address = args[0] - value = parseInt(args[1]) - } - address = Address.fromBase58Check(address) } diff --git a/src/wallet.js b/src/wallet.js index b4acd3f..657f722 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -190,11 +190,12 @@ function Wallet(seed, network) { for (var i = 0; i < utxos.length; ++i) { var utxo = utxos[i] - tx.addInput(utxo.receive) - accum += utxo.value + var outpoint = utxo.receive.split(':') + tx.addInput(outpoint[0], parseInt(outpoint[1])) var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee + accum += utxo.value subTotal = value + fee if (accum >= subTotal) { var change = accum - subTotal diff --git a/test/transaction.js b/test/transaction.js index 8341fec..e76d39c 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -127,11 +127,6 @@ describe('Transaction', function() { verifyTransactionIn() }) - it('allows a string in the form of txhash:index to be passed in', function() { - tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0") - verifyTransactionIn() - }) - function verifyTransactionIn() { assert.equal(tx.ins.length, 1) @@ -156,14 +151,9 @@ describe('Transaction', function() { verifyTransactionOut() }) - it('allows a string in the form of address:index to be passed in', function() { - tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") - verifyTransactionOut() - }) - it('allows a TransactionOut object to be passed in', function() { var txCopy = tx.clone() - txCopy.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") + txCopy.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000) var transactionOut = txCopy.outs[0] tx.addOutput(transactionOut) @@ -190,9 +180,9 @@ describe('Transaction', function() { describe('sign', function() { it('works', function() { - tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57:0") - tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3:40000") - tx.addOutput("1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd:50000") + tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0) + tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000) + tx.addOutput("1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd", 50000) var key = ECKey.fromWIF('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb') tx.sign(0, key) From 5bd636cab73b12a7e773611adb30ccb3cac8768f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 19 May 2014 14:14:07 +1000 Subject: [PATCH 06/29] Transaction: remove TxIn/TxOut from API --- src/transaction.js | 24 ++++++------------------ test/transaction.js | 18 ------------------ 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index c75b69b..a99dbd2 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -49,18 +49,12 @@ function Transaction(doc) { * * Can be called with any of: * - * - An existing TransactionIn object * - A transaction and an index * - A transaction hash and an index * * Note that this method does not sign the created input. */ Transaction.prototype.addInput = function (tx, outIndex) { - if (arguments[0] instanceof TransactionIn) { - this.ins.push(arguments[0]) - return - } - var hash = typeof tx === "string" ? tx : tx.hash this.ins.push(new TransactionIn({ @@ -77,16 +71,10 @@ Transaction.prototype.addInput = function (tx, outIndex) { * * Can be called with: * - * i) An existing TransactionOut object - * ii) An address object or a string address, and a value - * + * - An address object and a value + * - A base58 address string and a value */ Transaction.prototype.addOutput = function (address, value) { - if (arguments[0] instanceof TransactionOut) { - this.outs.push(arguments[0]) - return - } - if (typeof address === 'string') { address = Address.fromBase58Check(address) } @@ -222,12 +210,12 @@ Transaction.prototype.clone = function () { newTx.version = this.version newTx.locktime = this.locktime - this.ins.forEach(function(txin) { - newTx.addInput(txin.clone()) + newTx.ins = this.ins.map(function(txin) { + return new TransactionIn(txin) }) - this.outs.forEach(function(txout) { - newTx.addOutput(txout.clone()) + newTx.outs = this.outs.map(function(txout) { + return new TransactionOut(txout) }) return newTx diff --git a/test/transaction.js b/test/transaction.js index e76d39c..a00fa2e 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -118,15 +118,6 @@ describe('Transaction', function() { verifyTransactionIn() }) - it('allows a TransactionIn object to be passed in', function() { - var txCopy = tx.clone() - txCopy.addInput(prevTx, 0) - var transactionIn = txCopy.ins[0] - - tx.addInput(transactionIn) - verifyTransactionIn() - }) - function verifyTransactionIn() { assert.equal(tx.ins.length, 1) @@ -151,15 +142,6 @@ describe('Transaction', function() { verifyTransactionOut() }) - it('allows a TransactionOut object to be passed in', function() { - var txCopy = tx.clone() - txCopy.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000) - var transactionOut = txCopy.outs[0] - - tx.addOutput(transactionOut) - verifyTransactionOut() - }) - it('supports alternative networks', function() { var addr = 'mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR' From 867465a03f666f72787cf6235838ab32bef532db Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 19 May 2014 14:55:54 +1000 Subject: [PATCH 07/29] Transaction: support non-addressable output scripts --- src/transaction.js | 25 +++++++++++++++++++------ test/transaction.js | 5 +++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index a99dbd2..a30308b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -71,18 +71,31 @@ Transaction.prototype.addInput = function (tx, outIndex) { * * Can be called with: * - * - An address object and a value * - A base58 address string and a value + * - An Address object and a value + * - A scriptPubKey Script and a value */ -Transaction.prototype.addOutput = function (address, value) { - if (typeof address === 'string') { - address = Address.fromBase58Check(address) +Transaction.prototype.addOutput = function(scriptPubKey, value) { + // Attempt to get a valid address if it's a base58 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = Address.fromBase58Check(scriptPubKey) + } + + // TODO: remove me + var addressString + + // Attempt to get a valid script if it's an Address object + if (scriptPubKey instanceof Address) { + var address = scriptPubKey + + addressString = address.toBase58Check() + scriptPubKey = address.toOutputScript() } this.outs.push(new TransactionOut({ + script: scriptPubKey, value: value, - script: address.toOutputScript(), - address: address // TODO: Remove me + address: addressString })) } diff --git a/test/transaction.js b/test/transaction.js index a00fa2e..54d1e7c 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -142,6 +142,11 @@ describe('Transaction', function() { verifyTransactionOut() }) + it('allows a scriptPubKey and a value to be passed in', function() { + tx.addOutput(Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3').toOutputScript(), 40000) + verifyTransactionOut() + }) + it('supports alternative networks', function() { var addr = 'mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR' From bdc7131d0eb06b246e0df24acb7e9a64a0c88369 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 20 May 2014 14:07:22 +1000 Subject: [PATCH 08/29] Transaction: renames getHash to getId In turn also removes the inherent calculation of tx.hash after deserialization. --- src/transaction.js | 17 +++++++++++------ src/wallet.js | 6 +++--- test/transaction.js | 15 ++++++++++----- test/wallet.js | 3 +-- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index a30308b..4479577 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -25,7 +25,6 @@ function Transaction(doc) { this.outs = [] if (doc) { - if (doc.hash) this.hash = doc.hash; if (doc.version) this.version = doc.version; if (doc.locktime) this.locktime = doc.locktime; if (doc.ins && doc.ins.length) { @@ -39,8 +38,6 @@ function Transaction(doc) { return new TransactionOut(output) }) } - - this.hash = this.hash || this.getHash() } } @@ -54,8 +51,16 @@ function Transaction(doc) { * * Note that this method does not sign the created input. */ -Transaction.prototype.addInput = function (tx, outIndex) { - var hash = typeof tx === "string" ? tx : tx.hash +Transaction.prototype.addInput = function(tx, outIndex) { + var hash + + if (typeof tx === 'string') { + hash = tx + + } else { + assert(tx instanceof Transaction, 'Unexpected input: ' + tx) + hash = tx.getId() + } this.ins.push(new TransactionIn({ outpoint: { @@ -209,7 +214,7 @@ Transaction.prototype.hashForSignature = function(prevOutScript, inIndex, hashTy return crypto.hash256(buffer) } -Transaction.prototype.getHash = function () { +Transaction.prototype.getId = function () { var buffer = crypto.hash256(this.toBuffer()) // Big-endian is used for TxHash diff --git a/src/wallet.js b/src/wallet.js index 657f722..87293bb 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -146,9 +146,9 @@ function Wallet(seed, network) { } function processTx(tx, isPending) { - var txhash = tx.getHash() + var txid = tx.getId() - tx.outs.forEach(function(txOut, i){ + tx.outs.forEach(function(txOut, i) { var address try { @@ -158,7 +158,7 @@ function Wallet(seed, network) { } if (isMyAddress(address)) { - var output = txhash + ':' + i + var output = txid + ':' + i me.outputs[output] = { receive: output, diff --git a/test/transaction.js b/test/transaction.js index 54d1e7c..78a5ca4 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -74,11 +74,6 @@ describe('Transaction', function() { assert.deepEqual(output.script, Address.fromBase58Check('n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9').toOutputScript()) }) - it('assigns hash to deserialized object', function() { - var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c" - assert.equal(tx.hash, hashHex) - }) - it('decodes large inputs correctly', function() { // transaction has only 1 input var tx = new Transaction() @@ -251,5 +246,15 @@ describe('Transaction', function() { assert.equal(tx.toHex(), expected) }) }) + + describe('getId', function() { + it('returns the expected txid', function() { + var tx = new Transaction() + tx.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0) + tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1) + + assert.equal(tx.getId(), '7c3275f1212fd1a2add614f47a1f1f7b6d9570a97cb88e0e2664ab1752976e9f') + }) + }) }) diff --git a/test/wallet.js b/test/wallet.js index 88b7dcd..cc8b760 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -303,7 +303,6 @@ describe('Wallet', function() { function outputCount(){ return Object.keys(wallet.outputs).length } - }) describe("when tx ins outpoint contains a known txhash:i", function(){ @@ -340,7 +339,7 @@ describe('Wallet', function() { function verifyOutputAdded(index, pending) { var txOut = tx.outs[index] - var key = tx.getHash() + ":" + index + var key = tx.getId() + ":" + index var output = wallet.outputs[key] assert.equal(output.receive, key) assert.equal(output.value, txOut.value) From 2f56e63491a103ea5b57b6769945dfc8ff799063 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 00:04:02 +1000 Subject: [PATCH 09/29] Wallet: avoid transaction inspection when signing --- src/wallet.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/wallet.js b/src/wallet.js index 87293bb..88d1695 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -183,12 +183,14 @@ function Wallet(seed, network) { var utxos = getCandidateOutputs(value) var accum = 0 var subTotal = value + var addresses = [] var tx = new Transaction() tx.addOutput(to, value) for (var i = 0; i < utxos.length; ++i) { var utxo = utxos[i] + addresses.push(utxo.address) var outpoint = utxo.receive.split(':') tx.addInput(outpoint[0], parseInt(outpoint[1])) @@ -210,7 +212,7 @@ function Wallet(seed, network) { assert(accum >= subTotal, 'Not enough funds (incl. fee): ' + accum + ' < ' + subTotal) - this.sign(tx) + this.signWith(tx, addresses) return tx } @@ -240,13 +242,15 @@ function Wallet(seed, network) { return me.changeAddresses[me.changeAddresses.length - 1] } - this.sign = function(tx) { - tx.ins.forEach(function(inp,i) { - var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index] - if (output) { - tx.sign(i, me.getPrivateKeyForAddress(output.address)) - } + this.signWith = function(tx, addresses) { + assert.equal(tx.ins.length, addresses.length, 'Number of addresses must match number of transaction inputs') + + addresses.forEach(function(address, i) { + var key = me.getPrivateKeyForAddress(address) + + tx.sign(i, key) }) + return tx } From 5551c3881235ac712c7b54edd7126926165d4a41 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 00:03:17 +1000 Subject: [PATCH 10/29] Transaction: use hash Buffer instead of hex string --- src/transaction.js | 29 +++++++++++------------------ src/wallet.js | 10 ++++++++-- test/bitcoin.core.js | 9 +++++++-- test/transaction.js | 4 ++-- test/wallet.js | 25 ++++++++++++++++--------- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 4479577..90899b9 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -55,11 +55,16 @@ Transaction.prototype.addInput = function(tx, outIndex) { var hash if (typeof tx === 'string') { - hash = tx + hash = new Buffer(tx, 'hex') + assert.equal(hash.length, 32, 'Invalid TX hash') + + // TxHash hex is big-endian, we need little-endian + Array.prototype.reverse.call(hash) } else { - assert(tx instanceof Transaction, 'Unexpected input: ' + tx) - hash = tx.getId() + assert(tx instanceof Transaction, 'Expected Transaction, got ' + tx) + hash = crypto.hash256(tx.toBuffer()) + } this.ins.push(new TransactionIn({ @@ -142,13 +147,8 @@ Transaction.prototype.toBuffer = function () { writeUInt32(this.version) writeVarInt(this.ins.length) - this.ins.forEach(function(txin) { - var hash = new Buffer(txin.outpoint.hash, 'hex') // FIXME: Performance: convert on tx.addInput instead - - // TxHash hex is big-endian, we need little-endian - Array.prototype.reverse.call(hash) - - writeSlice(hash) + this.ins.forEach(function(txin, i) { + writeSlice(txin.outpoint.hash) writeUInt32(txin.outpoint.index) writeVarInt(txin.script.buffer.length) writeSlice(txin.script.buffer) @@ -240,9 +240,6 @@ Transaction.prototype.clone = function () { } Transaction.fromBuffer = function(buffer) { - // Copy because we mutate (reverse TxOutHashs) - buffer = new Buffer(buffer) - var offset = 0 function readSlice(n) { offset += n @@ -272,10 +269,6 @@ Transaction.fromBuffer = function(buffer) { for (var i = 0; i < vinLen; ++i) { var hash = readSlice(32) - - // TxHash is little-endian, we want big-endian hex - Array.prototype.reverse.call(hash) - var vout = readUInt32() var scriptLen = readVarInt() var script = readSlice(scriptLen) @@ -283,7 +276,7 @@ Transaction.fromBuffer = function(buffer) { ins.push(new TransactionIn({ outpoint: { - hash: hash.toString('hex'), + hash: hash, index: vout }, script: Script.fromBuffer(script), diff --git a/src/wallet.js b/src/wallet.js index 88d1695..4410b36 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -169,9 +169,15 @@ function Wallet(seed, network) { } }) - tx.ins.forEach(function(txIn, i){ + tx.ins.forEach(function(txIn, i) { var op = txIn.outpoint - var output = op.hash + ':' + op.index + + // copy and convert to big-endian hex + var txinHash = new Buffer(op.hash) + Array.prototype.reverse.call(txinHash) + txinHash = txinHash.toString('hex') + + var output = txinHash + ':' + op.index if(me.outputs[output]) delete me.outputs[output] }) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index b058385..48f594c 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -147,7 +147,12 @@ describe('Bitcoin-core', function() { var prevOutIndex = input[1] // var prevOutScriptPubKey = input[2] // TODO: we don't have a ASM parser - assert.equal(txin.outpoint.hash, prevOutHash) + var actualHash = txin.outpoint.hash + + // Test data is big-endian + Array.prototype.reverse.call(actualHash) + + assert.equal(actualHash.toString('hex'), prevOutHash) // we read UInt32, not Int32 assert.equal(txin.outpoint.index & 0xffffffff, prevOutIndex) @@ -184,7 +189,7 @@ describe('Bitcoin-core', function() { } if (actualHash != undefined) { - // BigEndian test data + // Test data is big-endian Array.prototype.reverse.call(actualHash) assert.equal(actualHash.toString('hex'), expectedHash) diff --git a/test/transaction.js b/test/transaction.js index 78a5ca4..9bacec3 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -59,7 +59,7 @@ describe('Transaction', function() { assert.equal(input.sequence, 4294967295) assert.equal(input.outpoint.index, 0) - assert.equal(input.outpoint.hash, "69d02fc05c4e0ddc87e796eee42693c244a3112fffe1f762c3fb61ffcb304634") + assert.equal(input.outpoint.hash.toString('hex'), "344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069") assert.equal(b2h(input.script.buffer), "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") @@ -120,7 +120,7 @@ describe('Transaction', function() { assert.equal(input.sequence, 4294967295) assert.equal(input.outpoint.index, 0) - assert.equal(input.outpoint.hash, "0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57") + assert.equal(input.outpoint.hash.toString('hex'), "576bc3c3285dbdccd8c3cbd8c03e10d7f77a5c839c744f34c3eb00511059b80c") assert.equal(input.script, Script.EMPTY) } diff --git a/test/wallet.js b/test/wallet.js index cc8b760..7562bc5 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -14,7 +14,15 @@ var fixtureTx1Hex = fixtureTxes.prevTx var fixtureTx2Hex = fixtureTxes.tx function fakeTxHash(i) { - return "efefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" + i + var hash = new Buffer(32) + hash.fill(i) + return hash +} + +function fakeTxId(i) { + var hash = fakeTxHash(i) + Array.prototype.reverse.call(hash) + return hash.toString('hex') } describe('Wallet', function() { @@ -263,12 +271,11 @@ describe('Wallet', function() { }) describe('processConfirmedTx', function(){ - it('does not fail on scripts with no corresponding Address', function() { var pubKey = wallet.getPrivateKey(0).pub var script = scripts.pubKeyOutput(pubKey) var tx2 = new Transaction() - tx2.addInput(fakeTxHash(1), 0) + tx2.addInput(fakeTxId(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 @@ -365,19 +372,19 @@ describe('Wallet', function() { // set up 3 utxo utxo = [ { - "hash": fakeTxHash(1), + "hash": fakeTxId(1), "outputIndex": 0, "address" : address1, "value": 400000 // not enough for value }, { - "hash": fakeTxHash(2), + "hash": fakeTxId(2), "outputIndex": 1, "address" : address1, "value": 500000 // enough for only value }, { - "hash": fakeTxHash(3), + "hash": fakeTxId(3), "outputIndex": 0, "address" : address2, "value": 520000 // enough for value and fee @@ -415,7 +422,7 @@ describe('Wallet', function() { it('ignores pending outputs', function(){ utxo.push( { - "hash": fakeTxHash(4), + "hash": fakeTxId(4), "outputIndex": 0, "address" : address2, "value": 530000, @@ -437,7 +444,7 @@ describe('Wallet', function() { var address = wallet.generateAddress() wallet.setUnspentOutputs([{ - hash: fakeTxHash(0), + hash: fakeTxId(0), outputIndex: 0, address: address, value: value @@ -459,7 +466,7 @@ describe('Wallet', function() { var address = wallet.generateAddress() wallet.setUnspentOutputs([{ - hash: fakeTxHash(0), + hash: fakeTxId(0), outputIndex: 0, address: address, value: value From f85792ba22d016478c5ec731301f6f66a1f06ede Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 21 May 2014 11:38:03 +1000 Subject: [PATCH 11/29] Transaction: remove address from txOut --- src/transaction.js | 9 +-------- test/transaction.js | 7 ++++--- test/wallet.js | 24 ++++++++++++++++++------ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 90899b9..f7935d9 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -91,21 +91,16 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) { scriptPubKey = Address.fromBase58Check(scriptPubKey) } - // TODO: remove me - var addressString - // Attempt to get a valid script if it's an Address object if (scriptPubKey instanceof Address) { var address = scriptPubKey - addressString = address.toBase58Check() scriptPubKey = address.toOutputScript() } this.outs.push(new TransactionOut({ script: scriptPubKey, value: value, - address: addressString })) } @@ -386,14 +381,12 @@ TransactionIn.prototype.clone = function () { function TransactionOut(data) { this.script = data.script this.value = data.value - this.address = data.address } TransactionOut.prototype.clone = function() { return new TransactionOut({ script: this.script, - value: this.value, - address: this.address + value: this.value }) } diff --git a/test/transaction.js b/test/transaction.js index 9bacec3..f849ade 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -143,12 +143,13 @@ describe('Transaction', function() { }) it('supports alternative networks', function() { - var addr = 'mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR' + var address = Address.fromBase58Check('mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR') + var script = address.toOutputScript() - tx.addOutput(addr, 40000) + tx.addOutput(address, 40000) verifyTransactionOut() - assert.equal(tx.outs[0].address.toString(), addr) + assert.deepEqual(tx.outs[0].script, script) }) function verifyTransactionOut() { diff --git a/test/wallet.js b/test/wallet.js index 7562bc5..ffd75c7 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -455,7 +455,9 @@ describe('Wallet', function() { var tx = wallet.createTx(to, toValue) assert.equal(tx.outs.length, 1) - assert.equal(tx.outs[0].address.toString(), to) + + var outAddress = Address.fromOutputScript(tx.outs[0].script, networks.testnet) + assert.equal(outAddress.toString(), to) assert.equal(tx.outs[0].value, toValue) }) }) @@ -480,10 +482,14 @@ describe('Wallet', function() { var tx = wallet.createTx(to, toValue, fee, changeAddress) assert.equal(tx.outs.length, 2) - assert.equal(tx.outs[0].address.toString(), to) + + var outAddress0 = Address.fromOutputScript(tx.outs[0].script, networks.testnet) + var outAddress1 = Address.fromOutputScript(tx.outs[1].script, networks.testnet) + + assert.equal(outAddress0.toString(), to) assert.equal(tx.outs[0].value, toValue) - assert.equal(tx.outs[1].address.toString(), changeAddress) + assert.equal(outAddress1.toString(), changeAddress) assert.equal(tx.outs[1].value, value - (toValue + fee)) }) }) @@ -494,7 +500,9 @@ describe('Wallet', function() { assert.equal(tx.outs.length, 1) var out = tx.outs[0] - assert.equal(out.address, to) + var outAddress = Address.fromOutputScript(out.script) + + assert.equal(outAddress.toString(), to) assert.equal(out.value, value) }) @@ -507,7 +515,9 @@ describe('Wallet', function() { assert.equal(tx.outs.length, 2) var out = tx.outs[1] - assert.equal(out.address, wallet.changeAddresses[1]) + var outAddress = Address.fromOutputScript(out.script) + + assert.equal(outAddress.toString(), wallet.changeAddresses[1]) assert.equal(out.value, 15000) }) @@ -519,7 +529,9 @@ describe('Wallet', function() { assert.equal(wallet.changeAddresses.length, 1) var out = tx.outs[1] - assert.equal(out.address, wallet.changeAddresses[0]) + var outAddress = Address.fromOutputScript(out.script) + + assert.equal(outAddress.toString(), wallet.changeAddresses[0]) assert.equal(out.value, 15000) }) From 7f9711ef9bc78ae9c5386f515d5a40a9af164290 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 21 May 2014 11:41:25 +1000 Subject: [PATCH 12/29] Transaction: restrict Transaction constructor --- src/transaction.js | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index f7935d9..e5ee989 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -17,28 +17,11 @@ var SIGHASH_NONE = 0x02 var SIGHASH_SINGLE = 0x03 var SIGHASH_ANYONECANPAY = 0x80 -function Transaction(doc) { - if (!(this instanceof Transaction)) { return new Transaction(doc) } +function Transaction() { this.version = 1 this.locktime = 0 this.ins = [] this.outs = [] - - if (doc) { - if (doc.version) this.version = doc.version; - if (doc.locktime) this.locktime = doc.locktime; - if (doc.ins && doc.ins.length) { - this.ins = doc.ins.map(function(input) { - return new TransactionIn(input) - }) - } - - if (doc.outs && doc.outs.length) { - this.outs = doc.outs.map(function(output) { - return new TransactionOut(output) - }) - } - } } /** @@ -256,12 +239,10 @@ Transaction.fromBuffer = function(buffer) { return vi.number } - var ins = [] - var outs = [] + var tx = new Transaction() + tx.version = readUInt32() - var version = readUInt32() var vinLen = readVarInt() - for (var i = 0; i < vinLen; ++i) { var hash = readSlice(32) var vout = readUInt32() @@ -269,7 +250,7 @@ Transaction.fromBuffer = function(buffer) { var script = readSlice(scriptLen) var sequence = readUInt32() - ins.push(new TransactionIn({ + tx.ins.push(new TransactionIn({ outpoint: { hash: hash, index: vout @@ -280,27 +261,21 @@ Transaction.fromBuffer = function(buffer) { } var voutLen = readVarInt() - for (i = 0; i < voutLen; ++i) { var value = readUInt64() var scriptLen = readVarInt() var script = readSlice(scriptLen) - outs.push(new TransactionOut({ + tx.outs.push(new TransactionOut({ value: value, script: Script.fromBuffer(script) })) } - var locktime = readUInt32() + tx.locktime = readUInt32() assert.equal(offset, buffer.length, 'Invalid transaction') - return new Transaction({ - version: version, - ins: ins, - outs: outs, - locktime: locktime - }) + return tx } Transaction.fromHex = function(hex) { From 3b6f0bb9b35f1726135cbaaaf048bc6c090b0bac Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 21 May 2014 11:42:57 +1000 Subject: [PATCH 13/29] Transaction: fix test name --- test/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/transaction.js b/test/transaction.js index f849ade..1e2d7d9 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -16,7 +16,7 @@ function b2h(b) { return new Buffer(b).toString('hex') } function h2b(h) { return new Buffer(h, 'hex') } describe('Transaction', function() { - describe('deserialize', function() { + describe('fromBuffer', function() { var tx, serializedTx beforeEach(function() { serializedTx = [ From 6ac38034831353563537eaa803149d074ff965d0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 21 May 2014 11:48:30 +1000 Subject: [PATCH 14/29] Transaction: move test to proper section --- test/transaction.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index 1e2d7d9..dc88684 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -16,6 +16,16 @@ function b2h(b) { return new Buffer(b).toString('hex') } function h2b(h) { return new Buffer(h, 'hex') } describe('Transaction', function() { + describe('toBuffer', function() { + it('matches the expected output', function() { + var expected = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d600000000fd1b0100483045022100e5be20d440b2bbbc886161f9095fa6d0bca749a4e41d30064f30eb97adc7a1f5022061af132890d8e4e90fedff5e9365aeeb77021afd8ef1d5c114d575512e9a130a0147304402205054e38e9d7b5c10481b6b4991fde5704cd94d49e344406e3c2ce4d18a43bf8e022051d7ba8479865b53a48bee0cce86e89a25633af5b2918aa276859489e232f51c014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0101000000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000' + + var actual = Transaction.fromHex(expected).toHex() + + assert.equal(actual, expected) + }) + }) + describe('fromBuffer', function() { var tx, serializedTx beforeEach(function() { @@ -30,13 +40,6 @@ describe('Transaction', function() { tx = Transaction.fromHex(serializedTx) }) - it('returns the original after serialized again', function() { - var actual = tx.toBuffer() - var expected = serializedTx - - assert.equal(b2h(actual), expected) - }) - it('does not mutate the input buffer', function() { var buffer = new Buffer(serializedTx, 'hex') Transaction.fromBuffer(buffer) From 66636f56bbd6a987497e80898de9a49e4715de93 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 21 May 2014 11:48:54 +1000 Subject: [PATCH 15/29] Transaction: test hex formatting --- test/transaction.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index dc88684..354b15e 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -13,7 +13,6 @@ var fixtureTx2Hex = fixtureTxes.tx var fixtureTxBigHex = fixtureTxes.bigTx function b2h(b) { return new Buffer(b).toString('hex') } -function h2b(h) { return new Buffer(h, 'hex') } describe('Transaction', function() { describe('toBuffer', function() { @@ -29,14 +28,7 @@ describe('Transaction', function() { describe('fromBuffer', function() { var tx, serializedTx beforeEach(function() { - serializedTx = [ - '0100000001344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e78', - '7dc0d4e5cc02fd069000000004a493046022100ef89701f460e8660c8', - '0808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c0', - '72f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c2', - '3901ffffffff0100f2052a010000001976a914dd40dedd8f7e3746662', - '4c4dacc6362d8e7be23dd88ac00000000' - ].join('') + serializedTx = '0100000001344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069000000004a493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901ffffffff0100f2052a010000001976a914dd40dedd8f7e37466624c4dacc6362d8e7be23dd88ac00000000' tx = Transaction.fromHex(serializedTx) }) @@ -64,8 +56,7 @@ describe('Transaction', function() { assert.equal(input.outpoint.index, 0) assert.equal(input.outpoint.hash.toString('hex'), "344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069") - assert.equal(b2h(input.script.buffer), - "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") + assert.equal(b2h(input.script.buffer), "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") }) it('decodes outputs correctly', function() { From a2d581dec508977d923b0df2cd05dd9d70d2003f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 24 May 2014 15:29:23 +1000 Subject: [PATCH 16/29] Transaction: rename script -> prevOutScript --- src/transaction.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index e5ee989..9871d48 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -312,15 +312,15 @@ Transaction.prototype.setInputScript = function(index, script) { this.ins[index].script = script } -// FIXME: should probably be validateInput(index, pub) -Transaction.prototype.validateInput = function(index, script, pub, DERsig) { +// FIXME: could be validateInput(index, prevTxOut, pub) +Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, DERsig) { var type = DERsig.readUInt8(DERsig.length - 1) DERsig = DERsig.slice(0, -1) - var hash = this.hashForSignature(script, index, type) - var sig = ecdsa.parseSig(DERsig) + var hash = this.hashForSignature(prevOutScript, index, type) + var signature = ecdsa.parseSig(DERsig) - return pub.verify(hash, sig) + return pubKey.verify(hash, signature) } Transaction.feePerKb = 20000 From 8e5fdb78a897d2d64d62eb14b457ce7d8c86ae3a Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:25:41 +1000 Subject: [PATCH 17/29] Transaction: rename type to hashType --- src/transaction.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 9871d48..6bbf613 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -285,26 +285,26 @@ Transaction.fromHex = function(hex) { /** * Signs a pubKeyHash output at some index with the given key */ -Transaction.prototype.sign = function(index, key, type) { +Transaction.prototype.sign = function(index, key, hashType) { var prevOutScript = key.pub.getAddress().toOutputScript() - var signature = this.signInput(index, prevOutScript, key, type) + var signature = this.signInput(index, prevOutScript, key, hashType) // FIXME: Assumed prior TX was pay-to-pubkey-hash var scriptSig = scripts.pubKeyHashInput(signature, key.pub) this.setInputScript(index, scriptSig) } -Transaction.prototype.signInput = function(index, prevOutScript, key, type) { - type = type || SIGHASH_ALL +Transaction.prototype.signInput = function(index, prevOutScript, key, hashType) { + hashType = hashType || SIGHASH_ALL assert(key instanceof ECKey, 'Invalid private key') - var hash = this.hashForSignature(prevOutScript, index, type) + var hash = this.hashForSignature(prevOutScript, index, hashType) var signature = key.sign(hash) var DERencoded = ecdsa.serializeSig(signature) return Buffer.concat([ new Buffer(DERencoded), - new Buffer([type]) + new Buffer([hashType]) ]) } From 3b3d19974cf7048e84cb44776499f068e1b5d012 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:28:38 +1000 Subject: [PATCH 18/29] tests: avoid b2h where toHex exists --- test/transaction.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index 354b15e..0acf048 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -12,8 +12,6 @@ var fixtureTx1Hex = fixtureTxes.prevTx var fixtureTx2Hex = fixtureTxes.tx var fixtureTxBigHex = fixtureTxes.bigTx -function b2h(b) { return new Buffer(b).toString('hex') } - describe('Transaction', function() { describe('toBuffer', function() { it('matches the expected output', function() { @@ -56,7 +54,7 @@ describe('Transaction', function() { assert.equal(input.outpoint.index, 0) assert.equal(input.outpoint.hash.toString('hex'), "344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069") - assert.equal(b2h(input.script.buffer), "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") + assert.equal(input.script.toHex(), "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") }) it('decodes outputs correctly', function() { @@ -151,7 +149,7 @@ describe('Transaction', function() { var output = tx.outs[0] assert.equal(output.value, 40000) - assert.equal(b2h(output.script.buffer), "76a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac") + assert.equal(output.script.toHex(), "76a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac") } }) From 4f995fcae1c0dc216d30aaddab5c51c95e13ee3b Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:29:57 +1000 Subject: [PATCH 19/29] Transaction: tests should show unboxing of parameters --- test/transaction.js | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index 0acf048..bcb3179 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -95,13 +95,15 @@ describe('Transaction', function() { }) describe('addInput', function() { - it('allows a Transaction object to be passed in', function() { - tx.addInput(prevTx, 0) + it('accepts a transaction hash', function() { + var prevTxHash = prevTx.getId() + + tx.addInput(prevTxHash, 0) verifyTransactionIn() }) - it('allows a Transaction hash to be passed in', function() { - tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0) + it('accepts a Transaction object', function() { + tx.addInput(prevTx, 0) verifyTransactionIn() }) @@ -119,29 +121,25 @@ describe('Transaction', function() { }) describe('addOutput', function() { - it('allows an address and a value to be passed in', function() { - tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 40000) - verifyTransactionOut() - }) + it('accepts an address string', function() { + var dest = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' - it('allows an Address object and value to be passed in', function() { - tx.addOutput(Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'), 40000) + tx.addOutput(dest, 40000) verifyTransactionOut() }) - it('allows a scriptPubKey and a value to be passed in', function() { - tx.addOutput(Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3').toOutputScript(), 40000) + it('accepts an Address', function() { + var dest = Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3') + + tx.addOutput(dest, 40000) verifyTransactionOut() }) - it('supports alternative networks', function() { - var address = Address.fromBase58Check('mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR') - var script = address.toOutputScript() + it('accepts a scriptPubKey', function() { + var dest = Address.fromBase58Check('15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3').toOutputScript() - tx.addOutput(address, 40000) + tx.addOutput(dest, 40000) verifyTransactionOut() - - assert.deepEqual(tx.outs[0].script, script) }) function verifyTransactionOut() { From 009fcb9b82f653aa575a064c2db5a1ef9ff52203 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:44:02 +1000 Subject: [PATCH 20/29] Transaction: now returns index of added input/output --- src/transaction.js | 14 ++++++++------ test/transaction.js | 12 ++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 6bbf613..c195da3 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -34,7 +34,7 @@ function Transaction() { * * Note that this method does not sign the created input. */ -Transaction.prototype.addInput = function(tx, outIndex) { +Transaction.prototype.addInput = function(tx, index) { var hash if (typeof tx === 'string') { @@ -50,13 +50,15 @@ Transaction.prototype.addInput = function(tx, outIndex) { } - this.ins.push(new TransactionIn({ + assert.equal(typeof index, 'number', 'Expected number index, got ' + index) + + return (this.ins.push(new TransactionIn({ outpoint: { hash: hash, - index: outIndex + index: index }, script: Script.EMPTY - })) + })) - 1) } /** @@ -81,10 +83,10 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) { scriptPubKey = address.toOutputScript() } - this.outs.push(new TransactionOut({ + return (this.outs.push(new TransactionOut({ script: scriptPubKey, value: value, - })) + })) - 1) } Transaction.prototype.toBuffer = function () { diff --git a/test/transaction.js b/test/transaction.js index bcb3179..b2c8b92 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -107,6 +107,11 @@ describe('Transaction', function() { verifyTransactionIn() }) + it('returns an index', function() { + assert.equal(tx.addInput(prevTx, 0), 0) + assert.equal(tx.addInput(prevTx, 0), 1) + }) + function verifyTransactionIn() { assert.equal(tx.ins.length, 1) @@ -142,6 +147,13 @@ describe('Transaction', function() { verifyTransactionOut() }) + it('returns an index', function() { + var dest = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' + + assert.equal(tx.addOutput(dest, 40000), 0) + assert.equal(tx.addOutput(dest, 40000), 1) + }) + function verifyTransactionOut() { assert.equal(tx.outs.length, 1) From d56746358878acc1c51fb19821941601a15de36e Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 17:00:40 +1000 Subject: [PATCH 21/29] Transaction: remove estimateFee This is a wallet abstraction. --- src/transaction.js | 12 ------------ src/wallet.js | 7 +++++-- test/transaction.js | 22 ---------------------- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index c195da3..8132bd2 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -325,18 +325,6 @@ Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, DER return pubKey.verify(hash, signature) } -Transaction.feePerKb = 20000 -Transaction.prototype.estimateFee = function(feePerKb){ - var uncompressedInSize = 180 - var outSize = 34 - var fixedPadding = 34 - - if(feePerKb == undefined) feePerKb = Transaction.feePerKb; - var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding - - return feePerKb * Math.ceil(size / 1000) -} - function TransactionIn(data) { assert(data.outpoint && data.script, 'Invalid TxIn parameters') this.outpoint = data.outpoint diff --git a/src/wallet.js b/src/wallet.js index 4410b36..4915d68 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -237,10 +237,13 @@ function Wallet(seed, network) { return sortByValueDesc } - function estimateFeePadChangeOutput(tx){ + var feePerKb = 20000 + function estimateFeePadChangeOutput(tx) { var tmpTx = tx.clone() tmpTx.addOutput(getChangeAddress(), 0) - return tmpTx.estimateFee() + + var byteSize = tmpTx.toBuffer().length + return feePerKb * Math.ceil(byteSize / 1000) } function getChangeAddress() { diff --git a/test/transaction.js b/test/transaction.js index b2c8b92..8cf2366 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -194,28 +194,6 @@ describe('Transaction', function() { assert.equal(validTx.validateInput(0, script, key.pub, sig), true) }) }) - - describe('estimateFee', function() { - it('works for fixture tx 1', function() { - var tx = Transaction.fromHex(fixtureTx1Hex) - assert.equal(tx.estimateFee(), 20000) - }) - - it('works for fixture big tx', function() { - var tx = Transaction.fromHex(fixtureTxBigHex) - assert.equal(tx.estimateFee(), 60000) - }) - - it('allow feePerKb to be passed in as an argument', function() { - var tx = Transaction.fromHex(fixtureTx2Hex) - assert.equal(tx.estimateFee(10000), 10000) - }) - - it('allow feePerKb to be set to 0', function() { - var tx = Transaction.fromHex(fixtureTx2Hex) - assert.equal(tx.estimateFee(0), 0) - }) - }) }) describe('signInput', function() { From a17208a549f4bfc014b96a5f2bbd83162983cae9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 01:08:52 +1000 Subject: [PATCH 22/29] Transaction: rename key to privKey and standardize type check --- src/transaction.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 8132bd2..f780df9 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -287,21 +287,21 @@ Transaction.fromHex = function(hex) { /** * Signs a pubKeyHash output at some index with the given key */ -Transaction.prototype.sign = function(index, key, hashType) { - var prevOutScript = key.pub.getAddress().toOutputScript() - var signature = this.signInput(index, prevOutScript, key, hashType) +Transaction.prototype.sign = function(index, privKey, hashType) { + var prevOutScript = privKey.pub.getAddress().toOutputScript() + var signature = this.signInput(index, prevOutScript, privKey, hashType) // FIXME: Assumed prior TX was pay-to-pubkey-hash - var scriptSig = scripts.pubKeyHashInput(signature, key.pub) + var scriptSig = scripts.pubKeyHashInput(signature, privKey.pub) this.setInputScript(index, scriptSig) } -Transaction.prototype.signInput = function(index, prevOutScript, key, hashType) { +Transaction.prototype.signInput = function(index, prevOutScript, privKey, hashType) { hashType = hashType || SIGHASH_ALL - assert(key instanceof ECKey, 'Invalid private key') + assert(privKey instanceof ECKey, 'Expected ECKey, got ' + privKey) var hash = this.hashForSignature(prevOutScript, index, hashType) - var signature = key.sign(hash) + var signature = privKey.sign(hash) var DERencoded = ecdsa.serializeSig(signature) return Buffer.concat([ From b5268465dbce454a7824ad35d11551d65ed05f18 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 01:39:59 +1000 Subject: [PATCH 23/29] Transaction: remove TxIn/TxOut clone --- src/transaction.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index f780df9..b55c345 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -332,27 +332,9 @@ function TransactionIn(data) { this.sequence = data.sequence == undefined ? DEFAULT_SEQUENCE : data.sequence } -TransactionIn.prototype.clone = function () { - return new TransactionIn({ - outpoint: { - hash: this.outpoint.hash, - index: this.outpoint.index - }, - script: this.script, - sequence: this.sequence - }) -} - function TransactionOut(data) { this.script = data.script this.value = data.value } -TransactionOut.prototype.clone = function() { - return new TransactionOut({ - script: this.script, - value: this.value - }) -} - module.exports = Transaction From 203d6c711662c1b3de953238abb905ee3278ef0c Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 15:28:11 +1000 Subject: [PATCH 24/29] Transaction: add test for clone --- test/transaction.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/transaction.js b/test/transaction.js index 8cf2366..0233713 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -237,5 +237,22 @@ describe('Transaction', function() { assert.equal(tx.getId(), '7c3275f1212fd1a2add614f47a1f1f7b6d9570a97cb88e0e2664ab1752976e9f') }) }) + + describe('clone', function() { + it('creates a new object', function() { + var txA = new Transaction() + txA.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0) + txA.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1000) + + var txB = txA.clone() + + // Enforce value equality + assert.deepEqual(txA, txB) + + // Enforce reference inequality + assert.notEqual(txA.ins[0], txB.ins[0]) + assert.notEqual(txA.outs[0], txB.outs[0]) + }) + }) }) From d8fdd50950820a6d479379d0625dbe57c4585957 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sun, 15 Jun 2014 15:28:20 +1000 Subject: [PATCH 25/29] Transaction: remove TxIn/TxOut --- src/transaction.js | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index b55c345..d4e9634 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -52,13 +52,14 @@ Transaction.prototype.addInput = function(tx, index) { assert.equal(typeof index, 'number', 'Expected number index, got ' + index) - return (this.ins.push(new TransactionIn({ + return (this.ins.push({ outpoint: { hash: hash, index: index }, - script: Script.EMPTY - })) - 1) + script: Script.EMPTY, + sequence: DEFAULT_SEQUENCE + }) - 1) } /** @@ -83,10 +84,10 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) { scriptPubKey = address.toOutputScript() } - return (this.outs.push(new TransactionOut({ + return (this.outs.push({ script: scriptPubKey, value: value, - })) - 1) + }) - 1) } Transaction.prototype.toBuffer = function () { @@ -209,11 +210,18 @@ Transaction.prototype.clone = function () { newTx.locktime = this.locktime newTx.ins = this.ins.map(function(txin) { - return new TransactionIn(txin) + return { + outpoint: txin.outpoint, + script: txin.script, + sequence: txin.sequence + } }) newTx.outs = this.outs.map(function(txout) { - return new TransactionOut(txout) + return { + script: txout.script, + value: txout.value + } }) return newTx @@ -252,14 +260,14 @@ Transaction.fromBuffer = function(buffer) { var script = readSlice(scriptLen) var sequence = readUInt32() - tx.ins.push(new TransactionIn({ + tx.ins.push({ outpoint: { hash: hash, index: vout }, script: Script.fromBuffer(script), sequence: sequence - })) + }) } var voutLen = readVarInt() @@ -268,10 +276,10 @@ Transaction.fromBuffer = function(buffer) { var scriptLen = readVarInt() var script = readSlice(scriptLen) - tx.outs.push(new TransactionOut({ + tx.outs.push({ value: value, script: Script.fromBuffer(script) - })) + }) } tx.locktime = readUInt32() @@ -325,16 +333,4 @@ Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, DER return pubKey.verify(hash, signature) } -function TransactionIn(data) { - assert(data.outpoint && data.script, 'Invalid TxIn parameters') - this.outpoint = data.outpoint - this.script = data.script - this.sequence = data.sequence == undefined ? DEFAULT_SEQUENCE : data.sequence -} - -function TransactionOut(data) { - this.script = data.script - this.value = data.value -} - module.exports = Transaction From 2a267b62e60377010df22c8e0286c1fa100a458d Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 16 Jun 2014 01:27:05 +1000 Subject: [PATCH 26/29] jshint: remove unused variables --- src/index.js | 2 -- src/transaction.js | 2 +- src/wallet.js | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index fc46f9b..2b9ae4b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,3 @@ -var T = require('./transaction') - module.exports = { Address: require('./address'), base58: require('./base58'), diff --git a/src/transaction.js b/src/transaction.js index d4e9634..7c85424 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -128,7 +128,7 @@ Transaction.prototype.toBuffer = function () { writeUInt32(this.version) writeVarInt(this.ins.length) - this.ins.forEach(function(txin, i) { + this.ins.forEach(function(txin) { writeSlice(txin.outpoint.hash) writeUInt32(txin.outpoint.index) writeVarInt(txin.script.buffer.length) diff --git a/src/wallet.js b/src/wallet.js index 4915d68..77756f3 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -169,7 +169,7 @@ function Wallet(seed, network) { } }) - tx.ins.forEach(function(txIn, i) { + tx.ins.forEach(function(txIn) { var op = txIn.outpoint // copy and convert to big-endian hex From 569e0d4ff156e7094141888b5e977c63cba9ce83 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 16 Jun 2014 13:58:16 +1000 Subject: [PATCH 27/29] Wallet: fix processConfirmedTx tests These tests were still passing despite being incorrect. --- test/wallet.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/wallet.js b/test/wallet.js index ffd75c7..7d9aa8c 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -313,34 +313,36 @@ describe('Wallet', function() { }) describe("when tx ins outpoint contains a known txhash:i", function(){ + var spendTx beforeEach(function(){ wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input wallet.processConfirmedTx(tx) - tx = Transaction.fromHex(fixtureTx2Hex) + spendTx = Transaction.fromHex(fixtureTx2Hex) }) it("does not add to wallet.outputs", function(){ - var outputs = wallet.outputs - wallet.processConfirmedTx(tx) - assert.deepEqual(wallet.outputs, outputs) + wallet.processConfirmedTx(spendTx) + assert.deepEqual(wallet.outputs, {}) }) it("deletes corresponding 'output'", function(){ - wallet.processConfirmedTx(tx) + var txIn = spendTx.ins[0] + var txInId = new Buffer(txIn.outpoint.hash) + Array.prototype.reverse.call(txInId) + txInId = txInId.toString('hex') - var txIn = tx.ins[0] - var key = txIn.outpoint.hash + ":" + txIn.outpoint.index - var output = wallet.outputs[key] + var expected = txInId + ':' + txIn.outpoint.index + assert(expected in wallet.outputs) - assert.equal(output, undefined) + wallet.processConfirmedTx(spendTx) + assert(!(expected in wallet.outputs)) }) }) it("does nothing when none of the involved addresses belong to the wallet", function(){ - var outputs = wallet.outputs wallet.processConfirmedTx(tx) - assert.deepEqual(wallet.outputs, outputs) + assert.deepEqual(wallet.outputs, {}) }) }) From c0e5393595849d096136bd3f4774a34e2ce74e3a Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 16 Jun 2014 14:08:43 +1000 Subject: [PATCH 28/29] Transaction: remove .outpoint object --- src/transaction.js | 21 ++++++++------------- src/wallet.js | 10 ++++------ test/bitcoin.core.js | 4 ++-- test/transaction.js | 8 ++++---- test/wallet.js | 20 +++++++++++++------- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 7c85424..51cfd00 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,5 +1,3 @@ -// FIXME: To all ye that enter here, be weary of Buffers, Arrays and Hex interchanging between the outpoints - var assert = require('assert') var bufferutils = require('./bufferutils') var crypto = require('./crypto') @@ -53,10 +51,8 @@ Transaction.prototype.addInput = function(tx, index) { assert.equal(typeof index, 'number', 'Expected number index, got ' + index) return (this.ins.push({ - outpoint: { - hash: hash, - index: index - }, + hash: hash, + index: index, script: Script.EMPTY, sequence: DEFAULT_SEQUENCE }) - 1) @@ -129,8 +125,8 @@ Transaction.prototype.toBuffer = function () { writeVarInt(this.ins.length) this.ins.forEach(function(txin) { - writeSlice(txin.outpoint.hash) - writeUInt32(txin.outpoint.index) + writeSlice(txin.hash) + writeUInt32(txin.index) writeVarInt(txin.script.buffer.length) writeSlice(txin.script.buffer) writeUInt32(txin.sequence) @@ -211,7 +207,8 @@ Transaction.prototype.clone = function () { newTx.ins = this.ins.map(function(txin) { return { - outpoint: txin.outpoint, + hash: txin.hash, + index: txin.index, script: txin.script, sequence: txin.sequence } @@ -261,10 +258,8 @@ Transaction.fromBuffer = function(buffer) { var sequence = readUInt32() tx.ins.push({ - outpoint: { - hash: hash, - index: vout - }, + hash: hash, + index: vout, script: Script.fromBuffer(script), sequence: sequence }) diff --git a/src/wallet.js b/src/wallet.js index 77756f3..1501456 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -170,14 +170,12 @@ function Wallet(seed, network) { }) tx.ins.forEach(function(txIn) { - var op = txIn.outpoint - // copy and convert to big-endian hex - var txinHash = new Buffer(op.hash) - Array.prototype.reverse.call(txinHash) - txinHash = txinHash.toString('hex') + var txinId = new Buffer(txIn.hash) + Array.prototype.reverse.call(txinId) + txinId = txinId.toString('hex') - var output = txinHash + ':' + op.index + var output = txinId + ':' + txIn.index if(me.outputs[output]) delete me.outputs[output] }) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 48f594c..af52885 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -147,7 +147,7 @@ describe('Bitcoin-core', function() { var prevOutIndex = input[1] // var prevOutScriptPubKey = input[2] // TODO: we don't have a ASM parser - var actualHash = txin.outpoint.hash + var actualHash = txin.hash // Test data is big-endian Array.prototype.reverse.call(actualHash) @@ -155,7 +155,7 @@ describe('Bitcoin-core', function() { assert.equal(actualHash.toString('hex'), prevOutHash) // we read UInt32, not Int32 - assert.equal(txin.outpoint.index & 0xffffffff, prevOutIndex) + assert.equal(txin.index & 0xffffffff, prevOutIndex) }) }) }) diff --git a/test/transaction.js b/test/transaction.js index 0233713..38f031f 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -51,8 +51,8 @@ describe('Transaction', function() { var input = tx.ins[0] assert.equal(input.sequence, 4294967295) - assert.equal(input.outpoint.index, 0) - assert.equal(input.outpoint.hash.toString('hex'), "344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069") + assert.equal(input.index, 0) + assert.equal(input.hash.toString('hex'), "344630cbff61fbc362f7e1ff2f11a344c29326e4ee96e787dc0d4e5cc02fd069") assert.equal(input.script.toHex(), "493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901") }) @@ -118,8 +118,8 @@ describe('Transaction', function() { var input = tx.ins[0] assert.equal(input.sequence, 4294967295) - assert.equal(input.outpoint.index, 0) - assert.equal(input.outpoint.hash.toString('hex'), "576bc3c3285dbdccd8c3cbd8c03e10d7f77a5c839c744f34c3eb00511059b80c") + assert.equal(input.index, 0) + assert.equal(input.hash.toString('hex'), "576bc3c3285dbdccd8c3cbd8c03e10d7f77a5c839c744f34c3eb00511059b80c") assert.equal(input.script, Script.EMPTY) } diff --git a/test/wallet.js b/test/wallet.js index 7d9aa8c..ad94d03 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -328,11 +328,11 @@ describe('Wallet', function() { it("deletes corresponding 'output'", function(){ var txIn = spendTx.ins[0] - var txInId = new Buffer(txIn.outpoint.hash) + var txInId = new Buffer(txIn.hash) Array.prototype.reverse.call(txInId) txInId = txInId.toString('hex') - var expected = txInId + ':' + txIn.outpoint.index + var expected = txInId + ':' + txIn.index assert(expected in wallet.outputs) wallet.processConfirmedTx(spendTx) @@ -400,7 +400,8 @@ describe('Wallet', function() { var tx = wallet.createTx(to, value) assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) + assert.deepEqual(tx.ins[0].hash, fakeTxHash(3)) + assert.equal(tx.ins[0].index, 0) }) it('allows fee to be specified', function(){ @@ -408,8 +409,11 @@ describe('Wallet', function() { var tx = wallet.createTx(to, value, fee) assert.equal(tx.ins.length, 2) - assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) - assert.deepEqual(tx.ins[1].outpoint, { hash: fakeTxHash(2), index: 1 }) + + 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) }) it('allows fee to be set to zero', function(){ @@ -418,7 +422,8 @@ describe('Wallet', function() { var tx = wallet.createTx(to, value, fee) assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) + assert.deepEqual(tx.ins[0].hash, fakeTxHash(3)) + assert.equal(tx.ins[0].index, 0) }) it('ignores pending outputs', function(){ @@ -436,7 +441,8 @@ describe('Wallet', function() { var tx = wallet.createTx(to, value) assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].outpoint, { hash: fakeTxHash(3), index: 0 }) + assert.deepEqual(tx.ins[0].hash, fakeTxHash(3)) + assert.equal(tx.ins[0].index, 0) }) }) From c5252fc509ac5dfb9e79248a5bbfabced42f8763 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 16 Jun 2014 15:44:27 +1000 Subject: [PATCH 29/29] Transaction: amend confusing exception message --- src/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 51cfd00..65c0a59 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -37,13 +37,13 @@ Transaction.prototype.addInput = function(tx, index) { if (typeof tx === 'string') { hash = new Buffer(tx, 'hex') - assert.equal(hash.length, 32, 'Invalid TX hash') + assert.equal(hash.length, 32, 'Expected Transaction or string, got ' + tx) // TxHash hex is big-endian, we need little-endian Array.prototype.reverse.call(hash) } else { - assert(tx instanceof Transaction, 'Expected Transaction, got ' + tx) + assert(tx instanceof Transaction, 'Expected Transaction or string, got ' + tx) hash = crypto.hash256(tx.toBuffer()) }