diff --git a/src/transaction.js b/src/transaction.js index 3abdd80..ac841fa 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -2,7 +2,6 @@ var BigInteger = require('./jsbn/jsbn'); var Script = require('./script'); var util = require('./util'); var convert = require('./convert'); -var Wallet = require('./wallet'); var ECKey = require('./eckey').ECKey; var ECDSA = require('./ecdsa'); var Address = require('./address'); @@ -15,8 +14,7 @@ var Transaction = function (doc) { this.locktime = 0; this.ins = []; this.outs = []; - this.timestamp = null; - this.block = null; + this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF if (doc) { if (typeof doc == "string" || Array.isArray(doc)) { @@ -35,27 +33,11 @@ var Transaction = function (doc) { this.addOutput(new TransactionOut(doc.outs[i])); } } - if (doc.timestamp) this.timestamp = doc.timestamp; - if (doc.block) this.block = doc.block; this.hash = this.hash || this.getHash() } }; -/** - * Turn transaction data into Transaction objects. - * - * Takes an array of plain JavaScript objects containing transaction data and - * returns an array of Transaction objects. - */ -Transaction.objectify = function (txs) { - var objs = []; - for (var i = 0; i < txs.length; i++) { - objs.push(new Transaction(txs[i])); - } - return objs; -}; - /** * Create a new txin. * @@ -85,7 +67,7 @@ Transaction.prototype.addInput = function (tx, outIndex) { index: outIndex }, script: new Script(), - sequence: 4294967295 + sequence: this.defaultSequence })); } }; @@ -138,7 +120,7 @@ Transaction.prototype.serialize = function () { var scriptBytes = txin.script.buffer; buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)); buffer = buffer.concat(scriptBytes); - buffer = buffer.concat(convert.numToBytes(parseInt(txin.sequence),4)); + buffer = buffer.concat(txin.sequence); } buffer = buffer.concat(convert.numToVarInt(this.outs.length)); for (var i = 0; i < this.outs.length; i++) { @@ -191,23 +173,6 @@ function (connectedScript, inIndex, hashType) txTmp.ins[inIndex].script = connectedScript; - // Blank out some of the outputs - if ((hashType & 0x1f) == SIGHASH_NONE) { - txTmp.outs = []; - - // Let the others update at will - for (var i = 0; i < txTmp.ins.length; i++) - if (i != inIndex) - txTmp.ins[i].sequence = 0; - } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { - // TODO: Implement - } - - // Blank out other inputs completely, not recommended for open transactions - if (hashType & SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]]; - } - var buffer = txTmp.serialize(); buffer = buffer.concat(convert.numToBytes(parseInt(hashType),4)); @@ -246,169 +211,6 @@ Transaction.prototype.clone = function () return newTx; }; -/** - * Analyze how this transaction affects a wallet. - * - * Returns an object with properties 'impact', 'type' and 'addr'. - * - * 'impact' is an object, see Transaction#calcImpact. - * - * 'type' can be one of the following: - * - * recv: - * This is an incoming transaction, the wallet received money. - * 'addr' contains the first address in the wallet that receives money - * from this transaction. - * - * self: - * This is an internal transaction, money was sent within the wallet. - * 'addr' is undefined. - * - * sent: - * This is an outgoing transaction, money was sent out from the wallet. - * 'addr' contains the first external address, i.e. the recipient. - * - * other: - * This method was unable to detect what the transaction does. Either it - */ -Transaction.prototype.analyze = function (wallet) { - if (!(wallet instanceof Wallet)) return null; - - var allFromMe = true, - allToMe = true, - firstRecvHash = null, - firstMeRecvHash = null, - firstSendHash = null; - - for (var i = this.outs.length-1; i >= 0; i--) { - var txout = this.outs[i]; - var hash = txout.script.simpleOutPubKeyHash(); - if (!wallet.hasHash(hash)) { - allToMe = false; - } else { - firstMeRecvHash = hash; - } - firstRecvHash = hash; - } - for (var i = this.ins.length-1; i >= 0; i--) { - var txin = this.ins[i]; - firstSendHash = txin.script.simpleInPubKeyHash(); - if (!wallet.hasHash(firstSendHash)) { - allFromMe = false; - break; - } - } - - var impact = this.calcImpact(wallet); - - var analysis = {}; - - analysis.impact = impact; - - if (impact.sign > 0 && impact.value > 0) { - analysis.type = 'recv'; - analysis.addr = new Address(firstMeRecvHash); - } else if (allFromMe && allToMe) { - analysis.type = 'self'; - } else if (allFromMe) { - analysis.type = 'sent'; - // TODO: Right now, firstRecvHash is the first output, which - if the - // transaction was not generated by this library could be the - // change address. - analysis.addr = new Address(firstRecvHash); - } else { - analysis.type = "other"; - } - - return analysis; -}; - -/** - * Get a human-readable version of the data returned by Transaction#analyze. - * - * This is merely a convenience function. Clients should consider implementing - * this themselves based on their UI, I18N, etc. - */ -Transaction.prototype.getDescription = function (wallet) { - var analysis = this.analyze(wallet); - - if (!analysis) return ""; - - switch (analysis.type) { - case 'recv': - return "Received with "+analysis.addr; - break; - - case 'sent': - return "Payment to "+analysis.addr; - break; - - case 'self': - return "Payment to yourself"; - break; - - case 'other': - default: - return ""; - } -}; - -/** - * Get the total amount of a transaction's outputs. - */ -Transaction.prototype.getTotalOutValue = function () { - return this.outs.reduce(function(t,o) { return t + o.value },0); -}; - - /** - * Old name for Transaction#getTotalOutValue. - * - * @deprecated - */ - Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; - -/** - * Calculates the impact a transaction has on this wallet. - * - * Based on the its public keys, the wallet will calculate the - * credit or debit of this transaction. - * - * It will return an object with two properties: - * - sign: 1 or -1 depending on sign of the calculated impact. - * - value: amount of calculated impact - * - * @returns Object Impact on wallet - */ -Transaction.prototype.calcImpact = function (wallet) { - if (!(wallet instanceof Wallet)) return 0; - - // Calculate credit to us from all outputs - var valueOut = this.outs.filter(function(o) { - return wallet.hasHash(convert.bytesToHex(o.script.simpleOutPubKeyHash())); - }) - .reduce(function(t,o) { return t+o.value },0); - - var valueIn = this.ins.filter(function(i) { - return wallet.hasHash(convert.bytesToHex(i.script.simpleInPubKeyHash())) - && wallet.txIndex[i.outpoint.hash]; - }) - .reduce(function(t,i) { - return t + wallet.txIndex[i.outpoint.hash].outs[i.outpoint.index].value - },0); - - if (valueOut > valueIn) { - return { - sign: 1, - value: valueOut - valueIn - }; - } else { - return { - sign: -1, - value: valueIn - valueOut - }; - } -}; - /** * Converts a serialized transaction into a transaction object */ @@ -451,7 +253,7 @@ Transaction.deserialize = function(buffer) { index: readAsInt(4) }, script: new Script(readVarString()), - sequence: readAsInt(4) + sequence: readBytes(4) }); } var outs = readVarInt(); @@ -473,6 +275,9 @@ Transaction.deserialize = function(buffer) { Transaction.prototype.sign = function(index, key, type) { type = type || SIGHASH_ALL; key = new ECKey(key); + + // TODO: getPub is slow, sha256ripe160 probably is too. + // This could be sped up a lot by providing these as inputs. var pub = key.getPub().export('bytes'), hash160 = util.sha256ripe160(pub), script = Script.createOutputScript(new Address(hash160)), @@ -533,7 +338,6 @@ Transaction.prototype.validateSig = function(index, script, sig, pub) { convert.coerceToBytes(pub)); } - var TransactionIn = function (data) { if (typeof data == "string") this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } @@ -549,7 +353,7 @@ var TransactionIn = function (data) { else this.script = new Script(data.script) - this.sequence = data.sequence || 4294967295; + this.sequence = data.sequence || this.defaultSequence }; TransactionIn.prototype.clone = function () { diff --git a/test/transaction.js b/test/transaction.js index 89b69b8..8a67b78 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -36,7 +36,7 @@ describe('Transaction', function() { assert.equal(tx.ins.length, 1) var input = tx.ins[0] - assert.equal(input.sequence, 4294967295) + assert.deepEqual(input.sequence, [255, 255, 255, 255]) assert.equal(input.outpoint.index, 0) assert.equal(input.outpoint.hash, "69d02fc05c4e0ddc87e796eee42693c244a3112fffe1f762c3fb61ffcb304634") @@ -98,7 +98,7 @@ describe('Transaction', function() { assert.equal(tx.ins.length, 1) var input = tx.ins[0] - assert.equal(input.sequence, 4294967295) + assert.deepEqual(input.sequence, [255, 255, 255, 255]) assert.equal(input.outpoint.index, 0) assert.equal(input.outpoint.hash, "0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57")