diff --git a/src/base58.js b/src/base58.js index 47e8020d3..2cbb0aed6 100644 --- a/src/base58.js +++ b/src/base58.js @@ -1,11 +1,9 @@ - // https://en.bitcoin.it/wiki/Base58Check_encoding var BigInteger = require('./jsbn/jsbn'); var Crypto = require('crypto-js'); -var SHA256 = Crypto.SHA256; -var WordArray = Crypto.lib.WordArray; var convert = require('./convert'); +var SHA256 = Crypto.SHA256; var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; var base = BigInteger.valueOf(58); @@ -38,7 +36,7 @@ module.exports.encode = function (input) { } return chars.reverse().join(''); -}, +} // decode a base58 string into a byte array // input should be a base58 encoded string @@ -90,10 +88,13 @@ module.exports.checkDecode = function(input) { var bytes = module.exports.decode(input), front = bytes.slice(0,bytes.length-4), back = bytes.slice(bytes.length-4); + var checksum = getChecksum(front) + if (""+checksum != ""+back) { throw new Error("Checksum failed"); } + var o = front.slice(1); o.version = front[0]; return o; @@ -105,4 +106,3 @@ function getChecksum(bytes) { } module.exports.getChecksum = getChecksum - diff --git a/src/convert.js b/src/convert.js index 27154bedd..f042de03a 100644 --- a/src/convert.js +++ b/src/convert.js @@ -53,8 +53,8 @@ exports.base64ToBytes = function(base64) { // Remove non-base-64 characters base64 = base64.replace(/[^A-Z0-9+\/]/ig, ''); - var bytes = [] - , imod4 = 0 + var bytes = []; + var imod4 = 0; for (var i = 0; i < base64.length; imod4 = ++i % 4) { if (!imod4) continue diff --git a/src/ecdsa.js b/src/ecdsa.js index c35eac4ca..668555ae0 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -248,7 +248,7 @@ var ECDSA = { var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); var beta = alpha.modPow(P_OVER_FOUR, p); - var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); +// var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); // If beta is even, but y isn't or vice versa, then convert it, // otherwise we're done and y == beta. var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta); diff --git a/src/eckey.js b/src/eckey.js index 23722d3d6..dc4cc1725 100644 --- a/src/eckey.js +++ b/src/eckey.js @@ -63,7 +63,7 @@ ECKey.prototype.getPub = function(compressed) { * @deprecated Reserved keyword, factory pattern. Use toHex, toBytes, etc. */ ECKey.prototype['export'] = function(format) { - format || (format = 'hex') + var format = format || 'hex' return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]() } @@ -77,7 +77,7 @@ ECKey.version_bytes = { } ECKey.prototype.toWif = function(version) { - var version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion; return base58.checkEncode(this.toBytes(), ECKey.version_bytes[version]) } @@ -147,7 +147,7 @@ ECPubKey.prototype.multiply = function(key) { } ECPubKey.prototype['export'] = function(format) { - format || (format = 'hex') + var format = format || 'hex'; return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]() } @@ -165,7 +165,7 @@ ECPubKey.prototype.toBin = function() { } ECPubKey.prototype.toWif = function(version) { - var version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion; return base58.checkEncode(this.toBytes(), version) } @@ -173,7 +173,7 @@ ECPubKey.prototype.toWif = function(version) { ECPubKey.prototype.toString = ECPubKey.prototype.toHex ECPubKey.prototype.getAddress = function(version) { - var version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion; return new Address(util.sha256ripe160(this.toBytes()), version); } diff --git a/src/opcode.js b/src/opcode.js index 209830bfd..0db260438 100644 --- a/src/opcode.js +++ b/src/opcode.js @@ -1,4 +1,4 @@ -Opcode = { +var Opcode = { map: { // push value OP_0 : 0, @@ -144,4 +144,4 @@ for(var i in Opcode.map) { Opcode.reverseMap[Opcode.map[i]] = i } -module.exports = Opcode +module.exports = Opcode; diff --git a/src/transaction.js b/src/transaction.js index 6a4978c81..20d03dd8e 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -5,7 +5,6 @@ var convert = require('./convert'); var ECKey = require('./eckey').ECKey; var ECDSA = require('./ecdsa'); var Address = require('./address'); -var Message = require('./message'); var SHA256 = require('crypto-js/sha256'); var Transaction = function (doc) { @@ -14,27 +13,29 @@ var Transaction = function (doc) { this.locktime = 0; this.ins = []; this.outs = []; - this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF + this.defaultSequence = [255, 255, 255, 255]; // 0xFFFFFFFF if (doc) { if (typeof doc == "string" || Array.isArray(doc)) { - doc = Transaction.deserialize(doc) + doc = Transaction.deserialize(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) { - for (var i = 0; i < doc.ins.length; i++) { - this.addInput(new TransactionIn(doc.ins[i])); - } + doc.ins.forEach(function(input) { + this.addInput(new TransactionIn(input)); + }, this); } + if (doc.outs && doc.outs.length) { - for (var i = 0; i < doc.outs.length; i++) { - this.addOutput(new TransactionOut(doc.outs[i])); - } + doc.outs.forEach(function(output) { + this.addOutput(new TransactionOut(output)); + }, this); } - this.hash = this.hash || this.getHash() + this.hash = this.hash || this.getHash(); } }; @@ -59,8 +60,9 @@ Transaction.prototype.addInput = function (tx, outIndex) { return this.addInput(args[0], args[1]); } else { - var hash = typeof tx === "string" ? tx : tx.hash - var hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash + var hash = typeof tx === "string" ? tx : tx.hash; + hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash; + this.ins.push(new TransactionIn({ outpoint: { hash: hash, @@ -87,11 +89,13 @@ Transaction.prototype.addOutput = function (address, value) { this.outs.push(arguments[0]); return; } + if (arguments[0].indexOf(':') >= 0) { var args = arguments[0].split(':'); address = args[0]; value = parseInt(args[1]); } + this.outs.push(new TransactionOut({ value: value, script: Script.createOutputScript(address) @@ -107,30 +111,33 @@ Transaction.prototype.addOutput = function (address, value) { */ Transaction.prototype.serialize = function () { var buffer = []; - buffer = buffer.concat(convert.numToBytes(parseInt(this.version),4)); + buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4)); buffer = buffer.concat(convert.numToVarInt(this.ins.length)); - for (var i = 0; i < this.ins.length; i++) { - var txin = this.ins[i]; + this.ins.forEach(function(txin) { // Why do blockchain.info, blockexplorer.com, sx and just about everybody // else use little-endian hashes? No idea... buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse()); - buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index),4)); + buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4)); + var scriptBytes = txin.script.buffer; buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)); buffer = buffer.concat(scriptBytes); buffer = buffer.concat(txin.sequence); - } + }); + buffer = buffer.concat(convert.numToVarInt(this.outs.length)); - for (var i = 0; i < this.outs.length; i++) { - var txout = this.outs[i]; + + this.outs.forEach(function(txout) { buffer = buffer.concat(convert.numToBytes(txout.value,8)); + var scriptBytes = txout.script.buffer; buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)); buffer = buffer.concat(scriptBytes); - } - buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime),4)); + }); + + buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4)); return buffer; }; @@ -139,7 +146,7 @@ Transaction.prototype.serializeHex = function() { return convert.bytesToHex(this.serialize()); } -var OP_CODESEPARATOR = 171; +//var OP_CODESEPARATOR = 171; var SIGHASH_ALL = 1; var SIGHASH_NONE = 2; @@ -167,9 +174,9 @@ function (connectedScript, inIndex, hashType) });*/ // Blank out other inputs' signatures - for (var i = 0; i < txTmp.ins.length; i++) { - txTmp.ins[i].script = new Script(); - } + txTmp.ins.forEach(function(txin) { + txin.script = new Script(); + }); txTmp.ins[inIndex].script = connectedScript; @@ -178,9 +185,12 @@ function (connectedScript, inIndex, hashType) txTmp.outs = []; // Let the others update at will - for (var i = 0; i < txTmp.ins.length; i++) - if (i != inIndex) + txTmp.ins.forEach(function(txin, i) { + if (i != inIndex) { txTmp.ins[i].sequence = 0; + } + }); + } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { // TODO: Implement } @@ -192,9 +202,9 @@ function (connectedScript, inIndex, hashType) var buffer = txTmp.serialize(); - buffer = buffer.concat(convert.numToBytes(parseInt(hashType),4)); - + buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4)); buffer = convert.bytesToWordArray(buffer); + return convert.wordArrayToBytes(SHA256(SHA256(buffer))); }; @@ -217,14 +227,15 @@ Transaction.prototype.clone = function () var newTx = new Transaction(); newTx.version = this.version; newTx.locktime = this.locktime; - for (var i = 0; i < this.ins.length; i++) { - var txin = this.ins[i].clone(); - newTx.addInput(txin); - } - for (var i = 0; i < this.outs.length; i++) { - var txout = this.outs[i].clone(); - newTx.addOutput(txout); - } + + this.ins.forEach(function(txin) { + newTx.addInput(txin.clone()); + }); + + this.outs.forEach(function(txout) { + newTx.addOutput(txout.clone()); + }); + return newTx; }; @@ -238,7 +249,7 @@ Transaction.deserialize = function(buffer) { } var pos = 0; var readAsInt = function(bytes) { - if (bytes == 0) return 0; + if (bytes === 0) return 0; pos++; return buffer[pos-1] + readAsInt(bytes-1) * 256; } @@ -263,7 +274,9 @@ Transaction.deserialize = function(buffer) { } obj.version = readAsInt(4); var ins = readVarInt(); - for (var i = 0; i < ins; i++) { + var i; + + for (i = 0; i < ins; i++) { obj.ins.push({ outpoint: { hash: convert.bytesToHex(readBytes(32).reverse()), @@ -274,12 +287,14 @@ Transaction.deserialize = function(buffer) { }); } var outs = readVarInt(); - for (var i = 0; i < outs; i++) { + + for (i = 0; i < outs; i++) { obj.outs.push({ value: convert.bytesToNum(readBytes(8)), script: new Script(readVarString()) }); } + obj.locktime = readAsInt(4); return new Transaction(obj); @@ -292,7 +307,7 @@ 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'), @@ -306,6 +321,7 @@ Transaction.prototype.sign = function(index, key, type) { // Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...] Transaction.prototype.signWithKeys = function(keys, outputs, type) { type = type || SIGHASH_ALL; + var addrdata = keys.map(function(key) { key = new ECKey(key); return { @@ -313,18 +329,24 @@ Transaction.prototype.signWithKeys = function(keys, outputs, type) { address: key.getAddress().toString() } }); + var hmap = {}; - for (var o in outputs) { - hmap[outputs[o].output] = outputs[o]; - } + outputs.forEach(function(o) { + hmap[o.output] = o; + }); + for (var i = 0; i < this.ins.length; i++) { - var outpoint = this.ins[i].outpoint.hash+':'+this.ins[i].outpoint.index, - histItem = hmap[outpoint]; + var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index; + var histItem = hmap[outpoint]; + if (!histItem) continue; + var thisInputAddrdata = addrdata.filter(function(a) { return a.address == histItem.address; }); - if (thisInputAddrdata.length == 0) continue; + + if (thisInputAddrdata.length === 0) continue; + this.sign(i,thisInputAddrdata[0].key); } } @@ -344,7 +366,7 @@ Transaction.prototype.p2shsign = function(index, script, key, type) { Transaction.prototype.multisign = Transaction.prototype.p2shsign; -Transaction.prototype.applyMultisigs = function(index, script, sigs, type) { +Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) { this.ins[index].script = Script.createMultiSigInputScript(sigs, script); } @@ -411,6 +433,8 @@ TransactionOut.prototype.clone = function () return newTxout; }; -module.exports.Transaction = Transaction; -module.exports.TransactionIn = TransactionIn; -module.exports.TransactionOut = TransactionOut; +module.exports = { + Transaction: Transaction, + TransactionIn: TransactionIn, + TransactionOut: TransactionOut +}