From 93fe1b4c787d2736e91fced48f586597a053597b Mon Sep 17 00:00:00 2001 From: Wei Lu Date: Mon, 31 Mar 2014 11:47:47 +0800 Subject: [PATCH] cosmetic standardization [closes #56] --- src/address.js | 3 +- src/base58.js | 95 ++++---- src/convert.js | 96 ++++---- src/ecdsa.js | 267 +++++++++++---------- src/eckey.js | 49 ++-- src/hdwallet.js | 316 ++++++++++++------------- src/index.js | 48 ++-- src/message.js | 4 +- src/network.js | 2 +- src/opcode.js | 260 ++++++++++----------- src/script.js | 411 ++++++++++++++++----------------- src/transaction.js | 549 ++++++++++++++++++++++---------------------- src/util.js | 10 - src/wallet.js | 524 +++++++++++++++++++++--------------------- test/address.js | 181 ++++++++------- test/base58.js | 73 +++--- test/convert.js | 255 ++++++++++---------- test/ec.js | 31 ++- test/eckey.js | 349 ++++++++++++++-------------- test/hdwallet.js | 435 +++++++++++++++++------------------ test/jsbn.js | 57 +++-- test/misc.js | 94 ++++---- test/script.js | 25 +- test/transaction.js | 3 +- test/wallet.js | 69 +++--- 25 files changed, 2078 insertions(+), 2128 deletions(-) diff --git a/src/address.js b/src/address.js index aa133c8..a952d3d 100644 --- a/src/address.js +++ b/src/address.js @@ -4,8 +4,9 @@ var error = require('./util').error var mainnet = require('./network').mainnet.addressVersion function Address(bytes, version) { - if (!(this instanceof Address)) + if (!(this instanceof Address)) { return new Address(bytes, version) + } if (bytes instanceof Address) { this.hash = bytes.hash diff --git a/src/base58.js b/src/base58.js index db0a13c..0b38586 100644 --- a/src/base58.js +++ b/src/base58.js @@ -1,109 +1,108 @@ // https://en.bitcoin.it/wiki/Base58Check_encoding -var BigInteger = require('./jsbn/jsbn'); -var Crypto = require('crypto-js'); -var convert = require('./convert'); -var SHA256 = Crypto.SHA256; +var BigInteger = require('./jsbn/jsbn') +var Crypto = require('crypto-js') +var convert = require('./convert') +var SHA256 = Crypto.SHA256 -var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; -var base = BigInteger.valueOf(58); +var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +var base = BigInteger.valueOf(58) -var positions = {}; -for (var i=0 ; i < alphabet.length ; ++i) { - positions[alphabet[i]] = i; +var positions = {} +for (var i=0; i= 0) { - var mod = bi.mod(base); - chars.push(alphabet[mod.intValue()]); - bi = bi.subtract(mod).divide(base); + var mod = bi.mod(base) + chars.push(alphabet[mod.intValue()]) + bi = bi.subtract(mod).divide(base) } - chars.push(alphabet[bi.intValue()]); + chars.push(alphabet[bi.intValue()]) // Convert leading zeros too. - for (var i = 0; i < input.length; i++) { + for (var i=0; i 0) { - bytes.unshift(0); + bytes.unshift(0) } - return bytes; + return bytes } function checkEncode(input, vbyte) { - vbyte = vbyte || 0; + vbyte = vbyte || 0 - var front = [vbyte].concat(input); - return encode(front.concat(getChecksum(front))); + var front = [vbyte].concat(input) + return encode(front.concat(getChecksum(front))) } function checkDecode(input) { var bytes = decode(input), - front = bytes.slice(0,bytes.length-4), - back = bytes.slice(bytes.length-4); + front = bytes.slice(0, bytes.length-4), + back = bytes.slice(bytes.length-4) - var checksum = getChecksum(front); + var checksum = getChecksum(front) if ("" + checksum != "" + back) { - throw new Error("Checksum failed"); + throw new Error("Checksum failed") } - var o = front.slice(1); - o.version = front[0]; - return o; + var o = front.slice(1) + o.version = front[0] + return o } function getChecksum(bytes) { var wordArray = convert.bytesToWordArray(bytes) - return convert.hexToBytes(SHA256(SHA256(wordArray)).toString()).slice(0,4); + return convert.hexToBytes(SHA256(SHA256(wordArray)).toString()).slice(0, 4) } module.exports = { diff --git a/src/convert.js b/src/convert.js index ceb978a..c917d61 100644 --- a/src/convert.js +++ b/src/convert.js @@ -1,74 +1,62 @@ -var Crypto = require('crypto-js'); -var WordArray = Crypto.lib.WordArray; -var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var Crypto = require('crypto-js') +var WordArray = Crypto.lib.WordArray +var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' function lpad(str, padString, length) { - while (str.length < length) str = padString + str; - return str; + while (str.length < length) str = padString + str + return str } -/** - * Convert a byte array to a hex string - */ function bytesToHex(bytes) { return bytes.map(function(x) { return lpad(x.toString(16), '0', 2) - }).join(''); -}; + }).join('') +} -/** - * Convert a hex string to a byte array - */ function hexToBytes(hex) { return hex.match(/../g).map(function(x) { return parseInt(x,16) - }); + }) } -/** - * Convert a byte array to a base-64 string - */ function bytesToBase64(bytes) { var base64 = [] for (var i = 0; i < bytes.length; i += 3) { - var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] for (var j = 0; j < 4; j++) { if (i * 8 + j * 6 <= bytes.length * 8) { - base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); + base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)) } else { - base64.push('='); + base64.push('=') } } } - return base64.join(''); + return base64.join('') } -/** - * Convert a base-64 string to a byte array - */ function base64ToBytes(base64) { // Remove non-base-64 characters - base64 = base64.replace(/[^A-Z0-9+\/]/ig, ''); + base64 = base64.replace(/[^A-Z0-9+\/]/ig, '') - var bytes = []; - var imod4 = 0; + var bytes = [] + var imod4 = 0 for (var i = 0; i < base64.length; imod4 = ++i % 4) { if (!imod4) continue - bytes.push( - ( - (base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << - (imod4 * 2) + bytes.push( + ( + (base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << + (imod4 * 2) ) | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)) - ); + ) } - return bytes; + return bytes } /** @@ -76,48 +64,48 @@ function base64ToBytes(base64) { */ function coerceToBytes(input) { if (typeof input != 'string') return input - return hexToBytes(input); + return hexToBytes(input) } function binToBytes(bin) { return bin.match(/......../g).map(function(x) { return parseInt(x,2) - }); + }) } function bytesToBin(bytes) { return bytes.map(function(x) { return lpad(x.toString(2), '0', 8) - }).join(''); + }).join('') } function bytesToString(bytes) { return bytes.map(function(x){ return String.fromCharCode(x) - }).join(''); + }).join('') } function stringToBytes(string) { return string.split('').map(function(x) { return x.charCodeAt(0) - }); + }) } /** * Create a byte array representing a number with the given length */ function numToBytes(num, bytes) { - if (bytes === undefined) bytes = 8; - if (bytes === 0) return []; - return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1)); + if (bytes === undefined) bytes = 8 + if (bytes === 0) return [] + return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1)) } /** * Convert a byte array to the number that it represents */ function bytesToNum(bytes) { - if (bytes.length === 0) return 0; - return bytes[0] + 256 * bytesToNum(bytes.slice(1)); + if (bytes.length === 0) return 0 + return bytes[0] + 256 * bytesToNum(bytes.slice(1)) } /** @@ -128,10 +116,10 @@ function bytesToNum(bytes) { * Returns a byte array. */ function numToVarInt(num) { - if (num < 253) return [num]; - if (num < 65536) return [253].concat(numToBytes(num, 2)); - if (num < 4294967296) return [254].concat(numToBytes(num, 4)); - return [255].concat(numToBytes(num, 8)); + if (num < 253) return [num] + if (num < 65536) return [253].concat(numToBytes(num, 2)) + if (num < 4294967296) return [254].concat(numToBytes(num, 4)) + return [255].concat(numToBytes(num, 8)) } /** @@ -148,7 +136,7 @@ function varIntToNum(bytes) { prefix < 253 ? bytes.slice(0, 1) : prefix === 253 ? bytes.slice(1, 3) : prefix === 254 ? bytes.slice(1, 5) - : bytes.slice(1, 9) + : bytes.slice(1, 9) return { bytes: prefix < 253 ? viBytes : bytes.slice(0, viBytes.length + 1), @@ -157,19 +145,19 @@ function varIntToNum(bytes) { } function bytesToWords(bytes) { - var words = []; + var words = [] for (var i = 0, b = 0; i < bytes.length; i++, b += 8) { - words[b >>> 5] |= bytes[i] << (24 - b % 32); + words[b >>> 5] |= bytes[i] << (24 - b % 32) } - return words; + return words } function wordsToBytes(words) { - var bytes = []; + var bytes = [] for (var b = 0; b < words.length * 32; b += 8) { - bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF) } - return bytes; + return bytes } function bytesToWordArray(bytes) { diff --git a/src/ecdsa.js b/src/ecdsa.js index f55dc7c..1fc14b2 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -1,133 +1,129 @@ -var sec = require('./jsbn/sec'); -var rng = require('secure-random'); -var BigInteger = require('./jsbn/jsbn'); +var sec = require('./jsbn/sec') +var rng = require('secure-random') +var BigInteger = require('./jsbn/jsbn') var convert = require('./convert') -var HmacSHA256 = require('crypto-js/hmac-sha256'); +var HmacSHA256 = require('crypto-js/hmac-sha256') +var ECPointFp = require('./jsbn/ec').ECPointFp +var ecparams = sec("secp256k1") +var P_OVER_FOUR = null -var ECPointFp = require('./jsbn/ec').ECPointFp; - -var ecparams = sec("secp256k1"); -var P_OVER_FOUR = null; - -function implShamirsTrick(P, k, Q, l) -{ - var m = Math.max(k.bitLength(), l.bitLength()); - var Z = P.add2D(Q); - var R = P.curve.getInfinity(); +function implShamirsTrick(P, k, Q, l) { + var m = Math.max(k.bitLength(), l.bitLength()) + var Z = P.add2D(Q) + var R = P.curve.getInfinity() for (var i = m - 1; i >= 0; --i) { - R = R.twice2D(); + R = R.twice2D() - R.z = BigInteger.ONE; + R.z = BigInteger.ONE if (k.testBit(i)) { if (l.testBit(i)) { - R = R.add2D(Z); + R = R.add2D(Z) } else { - R = R.add2D(P); + R = R.add2D(P) } } else { if (l.testBit(i)) { - R = R.add2D(Q); + R = R.add2D(Q) } } } - return R; -}; + return R +} function deterministicGenerateK(hash,key) { - var vArr = []; - var kArr = []; - for (var i = 0;i < 32;i++) vArr.push(1); - for (var i = 0;i < 32;i++) kArr.push(0); - var v = convert.bytesToWordArray(vArr) - var k = convert.bytesToWordArray(kArr) - - k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k) - v = HmacSHA256(v, k) - vArr = convert.wordArrayToBytes(v) - k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k) - v = HmacSHA256(v,k) - v = HmacSHA256(v,k) - vArr = convert.wordArrayToBytes(v) - return BigInteger.fromByteArrayUnsigned(vArr); + var vArr = [] + var kArr = [] + for (var i = 0;i < 32;i++) vArr.push(1) + for (var i = 0;i < 32;i++) kArr.push(0) + var v = convert.bytesToWordArray(vArr) + var k = convert.bytesToWordArray(kArr) + + k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k) + v = HmacSHA256(v, k) + vArr = convert.wordArrayToBytes(v) + k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k) + v = HmacSHA256(v,k) + v = HmacSHA256(v,k) + vArr = convert.wordArrayToBytes(v) + return BigInteger.fromByteArrayUnsigned(vArr) } var ECDSA = { getBigRandom: function (limit) { - return new BigInteger(limit.bitLength(), rng) - .mod(limit.subtract(BigInteger.ONE)) - .add(BigInteger.ONE) - ; + return new BigInteger(limit.bitLength(), rng). + mod(limit.subtract(BigInteger.ONE)). + add(BigInteger.ONE) }, sign: function (hash, priv) { - var d = priv; - var n = ecparams.getN(); - var e = BigInteger.fromByteArrayUnsigned(hash); + var d = priv + var n = ecparams.getN() + var e = BigInteger.fromByteArrayUnsigned(hash) var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned()) - var G = ecparams.getG(); - var Q = G.multiply(k); - var r = Q.getX().toBigInteger().mod(n); + var G = ecparams.getG() + var Q = G.multiply(k) + var r = Q.getX().toBigInteger().mod(n) - var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); + var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) - return ECDSA.serializeSig(r, s); + return ECDSA.serializeSig(r, s) }, verify: function (hash, sig, pubkey) { - var r,s; + var r,s if (Array.isArray(sig)) { - var obj = ECDSA.parseSig(sig); - r = obj.r; - s = obj.s; + var obj = ECDSA.parseSig(sig) + r = obj.r + s = obj.s } else if ("object" === typeof sig && sig.r && sig.s) { - r = sig.r; - s = sig.s; + r = sig.r + s = sig.s } else { - throw new Error("Invalid value for signature"); + throw new Error("Invalid value for signature") } - var Q; + var Q if (pubkey instanceof ECPointFp) { - Q = pubkey; + Q = pubkey } else if (Array.isArray(pubkey)) { - Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); + Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey) } else { - throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp"); + throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp") } - var e = BigInteger.fromByteArrayUnsigned(hash); + var e = BigInteger.fromByteArrayUnsigned(hash) - return ECDSA.verifyRaw(e, r, s, Q); + return ECDSA.verifyRaw(e, r, s, Q) }, verifyRaw: function (e, r, s, Q) { - var n = ecparams.getN(); - var G = ecparams.getG(); + var n = ecparams.getN() + var G = ecparams.getG() - if (r.compareTo(BigInteger.ONE) < 0 || - r.compareTo(n) >= 0) - return false; + if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) { + return false + } - if (s.compareTo(BigInteger.ONE) < 0 || - s.compareTo(n) >= 0) - return false; + if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) { + return false + } - var c = s.modInverse(n); + var c = s.modInverse(n) - var u1 = e.multiply(c).mod(n); - var u2 = r.multiply(c).mod(n); + var u1 = e.multiply(c).mod(n) + var u2 = r.multiply(c).mod(n) // TODO(!!!): For some reason Shamir's trick isn't working with // signed message verification!? Probably an implementation // error! - //var point = implShamirsTrick(G, u1, Q, u2); - var point = G.multiply(u1).add(Q.multiply(u2)); + //var point = implShamirsTrick(G, u1, Q, u2) + var point = G.multiply(u1).add(Q.multiply(u2)) - var v = point.getX().toBigInteger().mod(n); + var v = point.getX().toBigInteger().mod(n) - return v.equals(r); + return v.equals(r) }, /** @@ -136,22 +132,22 @@ var ECDSA = { * Takes two BigIntegers representing r and s and returns a byte array. */ serializeSig: function (r, s) { - var rBa = r.toByteArraySigned(); - var sBa = s.toByteArraySigned(); + var rBa = r.toByteArraySigned() + var sBa = s.toByteArraySigned() - var sequence = []; + var sequence = [] sequence.push(0x02); // INTEGER - sequence.push(rBa.length); - sequence = sequence.concat(rBa); + sequence.push(rBa.length) + sequence = sequence.concat(rBa) sequence.push(0x02); // INTEGER - sequence.push(sBa.length); - sequence = sequence.concat(sBa); + sequence.push(sBa.length) + sequence = sequence.concat(sBa) - sequence.unshift(sequence.length); + sequence.unshift(sequence.length) sequence.unshift(0x30); // SEQUENCE - return sequence; + return sequence }, /** @@ -165,48 +161,51 @@ var ECDSA = { * } */ parseSig: function (sig) { - var cursor; - if (sig[0] != 0x30) - throw new Error("Signature not a valid DERSequence"); + var cursor + if (sig[0] != 0x30) { + throw new Error("Signature not a valid DERSequence") + } - cursor = 2; - if (sig[cursor] != 0x02) - throw new Error("First element in signature must be a DERInteger");; - var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + cursor = 2 + if (sig[cursor] != 0x02) { + throw new Error("First element in signature must be a DERInteger") + } + var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]) - cursor += 2+sig[cursor+1]; - if (sig[cursor] != 0x02) - throw new Error("Second element in signature must be a DERInteger"); - var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + cursor += 2+sig[cursor+1] + if (sig[cursor] != 0x02) { + throw new Error("Second element in signature must be a DERInteger") + } + var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]) - cursor += 2+sig[cursor+1]; + cursor += 2+sig[cursor+1] //if (cursor != sig.length) - // throw new Error("Extra bytes in signature"); + // throw new Error("Extra bytes in signature") - var r = BigInteger.fromByteArrayUnsigned(rBa); - var s = BigInteger.fromByteArrayUnsigned(sBa); + var r = BigInteger.fromByteArrayUnsigned(rBa) + var s = BigInteger.fromByteArrayUnsigned(sBa) - return {r: r, s: s}; + return {r: r, s: s} }, parseSigCompact: function (sig) { if (sig.length !== 65) { - throw new Error("Signature has the wrong length"); + throw new Error("Signature has the wrong length") } // Signature is prefixed with a type byte storing three bits of // information. - var i = sig[0] - 27; + var i = sig[0] - 27 if (i < 0 || i > 7) { - throw new Error("Invalid signature type"); + throw new Error("Invalid signature type") } - var n = ecparams.getN(); - var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); - var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n); + var n = ecparams.getN() + var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n) + var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n) - return {r: r, s: s, i: i}; + return {r: r, s: s, i: i} }, /** @@ -219,57 +218,55 @@ var ECDSA = { */ recoverPubKey: function (r, s, hash, i) { // The recovery parameter i has two bits. - i = i & 3; + i = i & 3 // The less significant bit specifies whether the y coordinate // of the compressed point is even or not. - var isYEven = i & 1; + var isYEven = i & 1 // The more significant bit specifies whether we should use the // first or second candidate key. - var isSecondKey = i >> 1; + var isSecondKey = i >> 1 - var n = ecparams.getN(); - var G = ecparams.getG(); - var curve = ecparams.getCurve(); - var p = curve.getQ(); - var a = curve.getA().toBigInteger(); - var b = curve.getB().toBigInteger(); + var n = ecparams.getN() + var G = ecparams.getG() + var curve = ecparams.getCurve() + var p = curve.getQ() + var a = curve.getA().toBigInteger() + var b = curve.getB().toBigInteger() // We precalculate (p + 1) / 4 where p is if the field order if (!P_OVER_FOUR) { - P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)); + P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)) } // 1.1 Compute x - var x = isSecondKey ? r.add(n) : r; + var x = isSecondKey ? r.add(n) : r // 1.3 Convert x to point - var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); - var beta = alpha.modPow(P_OVER_FOUR, p); + 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); + var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta) // 1.4 Check that nR is at infinity - var R = new ECPointFp(curve, - curve.fromBigInteger(x), - curve.fromBigInteger(y)); - R.validate(); + var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + R.validate() // 1.5 Compute e from M - var e = BigInteger.fromByteArrayUnsigned(hash); - var eNeg = BigInteger.ZERO.subtract(e).mod(n); + var e = BigInteger.fromByteArrayUnsigned(hash) + var eNeg = BigInteger.ZERO.subtract(e).mod(n) // 1.6 Compute Q = r^-1 (sR - eG) - var rInv = r.modInverse(n); - var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); + var rInv = r.modInverse(n) + var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv) - Q.validate(); + Q.validate() if (!ECDSA.verifyRaw(e, r, s, Q)) { - throw new Error("Pubkey recovery unsuccessful"); + throw new Error("Pubkey recovery unsuccessful") } return Q @@ -297,6 +294,6 @@ var ECDSA = { throw new Error("Unable to find valid recovery factor") } -}; +} -module.exports = ECDSA; +module.exports = ECDSA diff --git a/src/eckey.js b/src/eckey.js index 7803202..8467c60 100644 --- a/src/eckey.js +++ b/src/eckey.js @@ -8,7 +8,6 @@ var ECPointFp = require('./jsbn/ec').ECPointFp var sec = require('./jsbn/sec') var Network = require('./network') var util = require('./util') - var ecparams = sec("secp256k1") // input can be nothing, array of bytes, hex string, or base58 string @@ -61,12 +60,12 @@ ECKey.prototype.import = function (input, compressed) { } ECKey.prototype.getPub = function(compressed) { - if (compressed === undefined) compressed = this.compressed - return ECPubKey(ecparams.getG().multiply(this.priv), compressed) + if (compressed === undefined) compressed = this.compressed + return ECPubKey(ecparams.getG().multiply(this.priv), compressed) } ECKey.prototype.toBin = function() { - return convert.bytesToString(this.toBytes()) + return convert.bytesToString(this.toBytes()) } ECKey.version_bytes = { @@ -75,41 +74,41 @@ ECKey.version_bytes = { } ECKey.prototype.toWif = function(version) { - version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion - return base58.checkEncode(this.toBytes(), ECKey.version_bytes[version]) + return base58.checkEncode(this.toBytes(), ECKey.version_bytes[version]) } ECKey.prototype.toHex = function() { - return convert.bytesToHex(this.toBytes()) + return convert.bytesToHex(this.toBytes()) } ECKey.prototype.toBytes = function() { - var bytes = this.priv.toByteArrayUnsigned(); - if (this.compressed) bytes.push(1) - return bytes + var bytes = this.priv.toByteArrayUnsigned() + if (this.compressed) bytes.push(1) + return bytes } ECKey.prototype.toBase64 = function() { - return convert.bytesToBase64(this.toBytes()) + return convert.bytesToBase64(this.toBytes()) } ECKey.prototype.toString = ECKey.prototype.toHex ECKey.prototype.getAddress = function(version) { - return this.getPub().getAddress(version) + return this.getPub().getAddress(version) } ECKey.prototype.add = function(key) { - return ECKey(this.priv.add(ECKey(key).priv), this.compressed) + return ECKey(this.priv.add(ECKey(key).priv), this.compressed) } ECKey.prototype.multiply = function(key) { - return ECKey(this.priv.multiply(ECKey(key).priv), this.compressed) + return ECKey(this.priv.multiply(ECKey(key).priv), this.compressed) } ECKey.prototype.sign = function(hash) { - return ecdsa.sign(hash, this.priv); + return ecdsa.sign(hash, this.priv) } ECKey.prototype.verify = function(hash, sig) { @@ -145,38 +144,38 @@ ECPubKey.prototype.import = function(input, compressed) { } ECPubKey.prototype.add = function(key) { - return ECPubKey(this.pub.add(ECPubKey(key).pub), this.compressed) + return ECPubKey(this.pub.add(ECPubKey(key).pub), this.compressed) } ECPubKey.prototype.multiply = function(key) { - return ECPubKey(this.pub.multiply(ECKey(key).priv), this.compressed) + return ECPubKey(this.pub.multiply(ECKey(key).priv), this.compressed) } ECPubKey.prototype.toBytes = function(compressed) { - if (compressed === undefined) compressed = this.compressed - return this.pub.getEncoded(compressed) + if (compressed === undefined) compressed = this.compressed + return this.pub.getEncoded(compressed) } ECPubKey.prototype.toHex = function(compressed) { - return convert.bytesToHex(this.toBytes(compressed)) + return convert.bytesToHex(this.toBytes(compressed)) } ECPubKey.prototype.toBin = function(compressed) { - return convert.bytesToString(this.toBytes(compressed)) + return convert.bytesToString(this.toBytes(compressed)) } ECPubKey.prototype.toWif = function(version) { - version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion - return base58.checkEncode(this.toBytes(), version) + return base58.checkEncode(this.toBytes(), version) } ECPubKey.prototype.toString = ECPubKey.prototype.toHex ECPubKey.prototype.getAddress = function(version) { - version = version || Network.mainnet.addressVersion; + version = version || Network.mainnet.addressVersion - return new Address(util.sha256ripe160(this.toBytes()), version); + return new Address(util.sha256ripe160(this.toBytes()), version) } ECPubKey.prototype.verify = function(hash, sig) { diff --git a/src/hdwallet.js b/src/hdwallet.js index 601466f..b47d4a9 100644 --- a/src/hdwallet.js +++ b/src/hdwallet.js @@ -3,7 +3,7 @@ var base58 = require('./base58.js') var assert = require('assert') var format = require('util').format var util = require('./util.js') -var Crypto = require('crypto-js'); +var Crypto = require('crypto-js') var HmacSHA512 = Crypto.HmacSHA512 var HMAC= Crypto.algo.HMAC var ECKey = require('./eckey.js').ECKey @@ -12,234 +12,234 @@ var Address = require('./address.js') var Network = require('./network') var HDWallet = module.exports = function(seed, network) { - if (seed === undefined) return - - var seedWords = convert.bytesToWordArray(seed) - var I = convert.wordArrayToBytes(HmacSHA512(seedWords, 'Bitcoin seed')) - this.chaincode = I.slice(32) - this.network = network || 'mainnet' - if(!Network.hasOwnProperty(this.network)) { - throw new Error("Unknown network: " + this.network) - } + if (seed === undefined) return; + + var seedWords = convert.bytesToWordArray(seed) + var I = convert.wordArrayToBytes(HmacSHA512(seedWords, 'Bitcoin seed')) + this.chaincode = I.slice(32) + this.network = network || 'mainnet' + if(!Network.hasOwnProperty(this.network)) { + throw new Error("Unknown network: " + this.network) + } - this.priv = new ECKey(I.slice(0, 32).concat([1]), true) - this.pub = this.priv.getPub() - this.index = 0 - this.depth = 0 + this.priv = new ECKey(I.slice(0, 32).concat([1]), true) + this.pub = this.priv.getPub() + this.index = 0 + this.depth = 0 } HDWallet.HIGHEST_BIT = 0x80000000 HDWallet.LENGTH = 78 function arrayEqual(a, b) { - return !(a < b || a > b) + return !(a < b || a > b) } -HDWallet.getChecksum = base58.getChecksum; +HDWallet.getChecksum = base58.getChecksum HDWallet.fromSeedHex = function(hex, network) { - return new HDWallet(convert.hexToBytes(hex), network) + return new HDWallet(convert.hexToBytes(hex), network) } HDWallet.fromSeedString = function(string, network) { - return new HDWallet(convert.stringToBytes(string), network) + return new HDWallet(convert.stringToBytes(string), network) } HDWallet.fromBase58 = function(input) { - var buffer = base58.decode(input) + var buffer = base58.decode(input) - if (buffer.length == HDWallet.LENGTH + 4) { - var expectedChecksum = buffer.slice(HDWallet.LENGTH, HDWallet.LENGTH + 4) - buffer = buffer.slice(0, HDWallet.LENGTH) - var actualChecksum = HDWallet.getChecksum(buffer) + if (buffer.length == HDWallet.LENGTH + 4) { + var expectedChecksum = buffer.slice(HDWallet.LENGTH, HDWallet.LENGTH + 4) + buffer = buffer.slice(0, HDWallet.LENGTH) + var actualChecksum = HDWallet.getChecksum(buffer) - if (!arrayEqual(expectedChecksum, actualChecksum)) { - throw new Error('Checksum mismatch') - } + if (!arrayEqual(expectedChecksum, actualChecksum)) { + throw new Error('Checksum mismatch') } + } - return HDWallet.fromBytes(buffer) + return HDWallet.fromBytes(buffer) } HDWallet.fromHex = function(input) { - return HDWallet.fromBytes(convert.hexToBytes(input)) + return HDWallet.fromBytes(convert.hexToBytes(input)) } HDWallet.fromBytes = function(input) { - // This 78 byte structure can be encoded like other Bitcoin data in Base58. (+32 bits checksum) - if (input.length != HDWallet.LENGTH) { - throw new Error(format('Invalid input length, %s. Expected %s.', input.length, HDWallet.LENGTH)) + // This 78 byte structure can be encoded like other Bitcoin data in Base58. (+32 bits checksum) + if (input.length != HDWallet.LENGTH) { + throw new Error(format('Invalid input length, %s. Expected %s.', input.length, HDWallet.LENGTH)) + } + + var hd = new HDWallet() + + // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private + // testnet: 0x043587CF public, 0x04358394 private) + var versionBytes = input.slice(0, 4) + var versionWord = convert.bytesToWords(versionBytes)[0] + var type + + for(var name in Network) { + var network = Network[name] + for(var t in network.hdVersions) { + if (versionWord != network.hdVersions[t]) continue + type = t + hd.network = name } + } - var hd = new HDWallet() - - // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; - // testnet: 0x043587CF public, 0x04358394 private) - var versionBytes = input.slice(0, 4) - var versionWord = convert.bytesToWords(versionBytes)[0] - var type - - for(var name in Network) { - var network = Network[name] - for(var t in network.hdVersions) { - if (versionWord != network.hdVersions[t]) continue - type = t - hd.network = name - } - } + if (!hd.network) { + throw new Error(format('Could not find version %s', convert.bytesToHex(versionBytes))) + } - if (!hd.network) { - throw new Error(format('Could not find version %s', convert.bytesToHex(versionBytes))) - } + // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... + hd.depth = input[4] - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... - hd.depth = input[4] + // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) + hd.parentFingerprint = input.slice(5, 9) + assert((hd.depth === 0) == arrayEqual(hd.parentFingerprint, [0, 0, 0, 0])) - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - hd.parentFingerprint = input.slice(5, 9) - assert((hd.depth === 0) == arrayEqual(hd.parentFingerprint, [0, 0, 0, 0])) + // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. + // This is encoded in MSB order. (0x00000000 if master key) + hd.index = convert.bytesToNum(input.slice(9, 13).reverse()) + assert(hd.depth > 0 || hd.index === 0) - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in MSB order. (0x00000000 if master key) - hd.index = convert.bytesToNum(input.slice(9, 13).reverse()) - assert(hd.depth > 0 || hd.index === 0) + // 32 bytes: the chain code + hd.chaincode = input.slice(13, 45) - // 32 bytes: the chain code - hd.chaincode = input.slice(13, 45) + // 33 bytes: the public key or private key data (0x02 + X or 0x03 + X for + // public keys, 0x00 + k for private keys) + if (type == 'priv') { + hd.priv = new ECKey(input.slice(46, 78).concat([1]), true) + hd.pub = hd.priv.getPub() + } else { + hd.pub = new ECPubKey(input.slice(45, 78), true) + } - // 33 bytes: the public key or private key data (0x02 + X or 0x03 + X for - // public keys, 0x00 + k for private keys) - if (type == 'priv') { - hd.priv = new ECKey(input.slice(46, 78).concat([1]), true) - hd.pub = hd.priv.getPub() - } else { - hd.pub = new ECPubKey(input.slice(45, 78), true) - } - - return hd + return hd } HDWallet.prototype.getIdentifier = function() { - return util.sha256ripe160(this.pub.toBytes()) + return util.sha256ripe160(this.pub.toBytes()) } HDWallet.prototype.getFingerprint = function() { - return this.getIdentifier().slice(0, 4) + return this.getIdentifier().slice(0, 4) } HDWallet.prototype.getAddress = function() { - return new Address(util.sha256ripe160(this.pub.toBytes()), this.getKeyVersion()) + return new Address(util.sha256ripe160(this.pub.toBytes()), this.getKeyVersion()) } HDWallet.prototype.toBytes = function(priv) { - var buffer = [] - - // Version - // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, - // 0x04358394 private) - var version = Network[this.network].hdVersions[priv ? 'priv' : 'pub'] - var vBytes = convert.wordsToBytes([version]) - - buffer = buffer.concat(vBytes) - assert.equal(buffer.length, 4) - - // Depth - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... - buffer.push(this.depth) - assert.equal(buffer.length, 4 + 1) - - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - buffer = buffer.concat(this.depth ? this.parentFingerprint : [0, 0, 0, 0]) - assert.equal(buffer.length, 4 + 1 + 4) - - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in MSB order. (0x00000000 if master key) - buffer = buffer.concat(convert.numToBytes(this.index, 4).reverse()) - assert.equal(buffer.length, 4 + 1 + 4 + 4) - - // 32 bytes: the chain code - buffer = buffer.concat(this.chaincode) - assert.equal(buffer.length, 4 + 1 + 4 + 4 + 32) - - // 33 bytes: the public key or private key data - // (0x02 + X or 0x03 + X for public keys, 0x00 + k for private keys) - if (priv) { - assert(this.priv, 'Cannot serialize to private without private key') - buffer.push(0) - buffer = buffer.concat(this.priv.toBytes().slice(0, 32)) - } else { - buffer = buffer.concat(this.pub.toBytes(true)) - } + var buffer = [] + + // Version + // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, + // 0x04358394 private) + var version = Network[this.network].hdVersions[priv ? 'priv' : 'pub'] + var vBytes = convert.wordsToBytes([version]) + + buffer = buffer.concat(vBytes) + assert.equal(buffer.length, 4) - return buffer + // Depth + // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... + buffer.push(this.depth) + assert.equal(buffer.length, 4 + 1) + + // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) + buffer = buffer.concat(this.depth ? this.parentFingerprint : [0, 0, 0, 0]) + assert.equal(buffer.length, 4 + 1 + 4) + + // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. + // This is encoded in MSB order. (0x00000000 if master key) + buffer = buffer.concat(convert.numToBytes(this.index, 4).reverse()) + assert.equal(buffer.length, 4 + 1 + 4 + 4) + + // 32 bytes: the chain code + buffer = buffer.concat(this.chaincode) + assert.equal(buffer.length, 4 + 1 + 4 + 4 + 32) + + // 33 bytes: the public key or private key data + // (0x02 + X or 0x03 + X for public keys, 0x00 + k for private keys) + if (priv) { + assert(this.priv, 'Cannot serialize to private without private key') + buffer.push(0) + buffer = buffer.concat(this.priv.toBytes().slice(0, 32)) + } else { + buffer = buffer.concat(this.pub.toBytes(true)) + } + + return buffer } HDWallet.prototype.toHex = function(priv) { - var bytes = this.toBytes(priv) - return convert.bytesToHex(bytes) + var bytes = this.toBytes(priv) + return convert.bytesToHex(bytes) } HDWallet.prototype.toBase58 = function(priv) { - var buffer = this.toBytes(priv) + var buffer = this.toBytes(priv) , checksum = HDWallet.getChecksum(buffer) - buffer = buffer.concat(checksum) - return base58.encode(buffer) + buffer = buffer.concat(checksum) + return base58.encode(buffer) } HDWallet.prototype.derive = function(i) { - var I + var I , iBytes = convert.numToBytes(i, 4).reverse() , cPar = this.chaincode , usePriv = i >= HDWallet.HIGHEST_BIT , SHA512 = Crypto.algo.SHA512 - if (usePriv) { - assert(this.priv, 'Private derive on public key') - - // If 1, private derivation is used: - // let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:] - var kPar = this.priv.toBytes().slice(0, 32) - I = HmacFromBytesToBytes(SHA512, [0].concat(kPar, iBytes), cPar) - } else { - // If 0, public derivation is used: - // let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i) - var KPar = this.pub.toBytes(true) - I = HmacFromBytesToBytes(SHA512, KPar.concat(iBytes), cPar) - } - - // Split I = IL || IR into two 32-byte sequences, IL and IR. - var IL = I.slice(0, 32) + if (usePriv) { + assert(this.priv, 'Private derive on public key') + + // If 1, private derivation is used: + // let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:] + var kPar = this.priv.toBytes().slice(0, 32) + I = HmacFromBytesToBytes(SHA512, [0].concat(kPar, iBytes), cPar) + } else { + // If 0, public derivation is used: + // let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i) + var KPar = this.pub.toBytes(true) + I = HmacFromBytesToBytes(SHA512, KPar.concat(iBytes), cPar) + } + + // Split I = IL || IR into two 32-byte sequences, IL and IR. + var IL = I.slice(0, 32) , IR = I.slice(32) - var hd = new HDWallet() - hd.network = this.network - - if (this.priv) { - // ki = IL + kpar (mod n). - hd.priv = this.priv.add(new ECKey(IL.concat([1]))) - hd.priv.compressed = true - hd.priv.version = this.getKeyVersion() - hd.pub = hd.priv.getPub() - } else { - // Ki = (IL + kpar)*G = IL*G + Kpar - hd.pub = this.pub.add(new ECKey(IL.concat([1]), true).getPub()) - } - - // ci = IR. - hd.chaincode = IR - hd.parentFingerprint = this.getFingerprint() - hd.depth = this.depth + 1 - hd.index = i - hd.pub.compressed = true - return hd + var hd = new HDWallet() + hd.network = this.network + + if (this.priv) { + // ki = IL + kpar (mod n). + hd.priv = this.priv.add(new ECKey(IL.concat([1]))) + hd.priv.compressed = true + hd.priv.version = this.getKeyVersion() + hd.pub = hd.priv.getPub() + } else { + // Ki = (IL + kpar)*G = IL*G + Kpar + hd.pub = this.pub.add(new ECKey(IL.concat([1]), true).getPub()) + } + + // ci = IR. + hd.chaincode = IR + hd.parentFingerprint = this.getFingerprint() + hd.depth = this.depth + 1 + hd.index = i + hd.pub.compressed = true + return hd } HDWallet.prototype.derivePrivate = function(index) { - return this.derive(index + HDWallet.HIGHEST_BIT) + return this.derive(index + HDWallet.HIGHEST_BIT) } HDWallet.prototype.getKeyVersion = function() { - return Network[this.network].addressVersion + return Network[this.network].addressVersion } HDWallet.prototype.toString = HDWallet.prototype.toBase58 diff --git a/src/index.js b/src/index.js index 23f281e..c45c919 100644 --- a/src/index.js +++ b/src/index.js @@ -1,29 +1,25 @@ -var Key = require('./eckey'); +var Key = require('./eckey') +var T = require('./transaction') module.exports = { - Address: require('./address'), - Key: Key.ECKey, - ECKey: Key.ECKey, - ECPubKey: Key.ECPubKey, - Message: require('./message'), - BigInteger: require('./jsbn/jsbn'), - Crypto: require('crypto-js'), //should we expose this at all? - Script: require('./script'), - Opcode: require('./opcode'), - Transaction: require('./transaction').Transaction, - Util: require('./util'), - TransactionIn: require('./transaction').TransactionIn, - TransactionOut: require('./transaction').TransactionOut, - ECPointFp: require('./jsbn/ec').ECPointFp, - Wallet: require('./wallet'), - network: require('./network'), - - ecdsa: require('./ecdsa'), - HDWallet: require('./hdwallet.js'), - - // base58 encoding/decoding to bytes - base58: require('./base58'), - - // conversions - convert: require('./convert') + Address: require('./address'), + Key: Key.ECKey, + ECKey: Key.ECKey, + ECPubKey: Key.ECPubKey, + Message: require('./message'), + BigInteger: require('./jsbn/jsbn'), + Crypto: require('crypto-js'), //should we expose this at all? + Script: require('./script'), + Opcode: require('./opcode'), + Transaction: T.Transaction, + Util: require('./util'), + TransactionIn: T.TransactionIn, + TransactionOut: T.TransactionOut, + ECPointFp: require('./jsbn/ec').ECPointFp, + Wallet: require('./wallet'), + network: require('./network'), + ecdsa: require('./ecdsa'), + HDWallet: require('./hdwallet.js'), + base58: require('./base58'), + convert: require('./convert') } diff --git a/src/message.js b/src/message.js index 32b719a..a9ed312 100644 --- a/src/message.js +++ b/src/message.js @@ -38,8 +38,8 @@ function sign(key, message) { var sBa = obj.s.toByteArrayUnsigned() // Pad to 32 bytes per value - while (rBa.length < 32) rBa.unshift(0) - while (sBa.length < 32) sBa.unshift(0) + while (rBa.length < 32) rBa.unshift(0); + while (sBa.length < 32) sBa.unshift(0); sig = [i].concat(rBa, sBa) diff --git a/src/network.js b/src/network.js index 78e7617..6621a2c 100644 --- a/src/network.js +++ b/src/network.js @@ -15,5 +15,5 @@ module.exports = { priv: 0x04358394 } } -}; +} diff --git a/src/opcode.js b/src/opcode.js index 0db2604..69049c7 100644 --- a/src/opcode.js +++ b/src/opcode.js @@ -1,147 +1,147 @@ var Opcode = { - map: { - // push value - OP_0 : 0, - OP_FALSE : 0, - OP_PUSHDATA1 : 76, - OP_PUSHDATA2 : 77, - OP_PUSHDATA4 : 78, - OP_1NEGATE : 79, - OP_RESERVED : 80, - OP_1 : 81, - OP_TRUE : 81, - OP_2 : 82, - OP_3 : 83, - OP_4 : 84, - OP_5 : 85, - OP_6 : 86, - OP_7 : 87, - OP_8 : 88, - OP_9 : 89, - OP_10 : 90, - OP_11 : 91, - OP_12 : 92, - OP_13 : 93, - OP_14 : 94, - OP_15 : 95, - OP_16 : 96, + map: { + // push value + OP_0 : 0, + OP_FALSE : 0, + OP_PUSHDATA1 : 76, + OP_PUSHDATA2 : 77, + OP_PUSHDATA4 : 78, + OP_1NEGATE : 79, + OP_RESERVED : 80, + OP_1 : 81, + OP_TRUE : 81, + OP_2 : 82, + OP_3 : 83, + OP_4 : 84, + OP_5 : 85, + OP_6 : 86, + OP_7 : 87, + OP_8 : 88, + OP_9 : 89, + OP_10 : 90, + OP_11 : 91, + OP_12 : 92, + OP_13 : 93, + OP_14 : 94, + OP_15 : 95, + OP_16 : 96, - // control - OP_NOP : 97, - OP_VER : 98, - OP_IF : 99, - OP_NOTIF : 100, - OP_VERIF : 101, - OP_VERNOTIF : 102, - OP_ELSE : 103, - OP_ENDIF : 104, - OP_VERIFY : 105, - OP_RETURN : 106, + // control + OP_NOP : 97, + OP_VER : 98, + OP_IF : 99, + OP_NOTIF : 100, + OP_VERIF : 101, + OP_VERNOTIF : 102, + OP_ELSE : 103, + OP_ENDIF : 104, + OP_VERIFY : 105, + OP_RETURN : 106, - // stack ops - OP_TOALTSTACK : 107, - OP_FROMALTSTACK : 108, - OP_2DROP : 109, - OP_2DUP : 110, - OP_3DUP : 111, - OP_2OVER : 112, - OP_2ROT : 113, - OP_2SWAP : 114, - OP_IFDUP : 115, - OP_DEPTH : 116, - OP_DROP : 117, - OP_DUP : 118, - OP_NIP : 119, - OP_OVER : 120, - OP_PICK : 121, - OP_ROLL : 122, - OP_ROT : 123, - OP_SWAP : 124, - OP_TUCK : 125, + // stack ops + OP_TOALTSTACK : 107, + OP_FROMALTSTACK : 108, + OP_2DROP : 109, + OP_2DUP : 110, + OP_3DUP : 111, + OP_2OVER : 112, + OP_2ROT : 113, + OP_2SWAP : 114, + OP_IFDUP : 115, + OP_DEPTH : 116, + OP_DROP : 117, + OP_DUP : 118, + OP_NIP : 119, + OP_OVER : 120, + OP_PICK : 121, + OP_ROLL : 122, + OP_ROT : 123, + OP_SWAP : 124, + OP_TUCK : 125, - // splice ops - OP_CAT : 126, - OP_SUBSTR : 127, - OP_LEFT : 128, - OP_RIGHT : 129, - OP_SIZE : 130, + // splice ops + OP_CAT : 126, + OP_SUBSTR : 127, + OP_LEFT : 128, + OP_RIGHT : 129, + OP_SIZE : 130, - // bit logic - OP_INVERT : 131, - OP_AND : 132, - OP_OR : 133, - OP_XOR : 134, - OP_EQUAL : 135, - OP_EQUALVERIFY : 136, - OP_RESERVED1 : 137, - OP_RESERVED2 : 138, + // bit logic + OP_INVERT : 131, + OP_AND : 132, + OP_OR : 133, + OP_XOR : 134, + OP_EQUAL : 135, + OP_EQUALVERIFY : 136, + OP_RESERVED1 : 137, + OP_RESERVED2 : 138, - // numeric - OP_1ADD : 139, - OP_1SUB : 140, - OP_2MUL : 141, - OP_2DIV : 142, - OP_NEGATE : 143, - OP_ABS : 144, - OP_NOT : 145, - OP_0NOTEQUAL : 146, + // numeric + OP_1ADD : 139, + OP_1SUB : 140, + OP_2MUL : 141, + OP_2DIV : 142, + OP_NEGATE : 143, + OP_ABS : 144, + OP_NOT : 145, + OP_0NOTEQUAL : 146, - OP_ADD : 147, - OP_SUB : 148, - OP_MUL : 149, - OP_DIV : 150, - OP_MOD : 151, - OP_LSHIFT : 152, - OP_RSHIFT : 153, + OP_ADD : 147, + OP_SUB : 148, + OP_MUL : 149, + OP_DIV : 150, + OP_MOD : 151, + OP_LSHIFT : 152, + OP_RSHIFT : 153, - OP_BOOLAND : 154, - OP_BOOLOR : 155, - OP_NUMEQUAL : 156, - OP_NUMEQUALVERIFY : 157, - OP_NUMNOTEQUAL : 158, - OP_LESSTHAN : 159, - OP_GREATERTHAN : 160, - OP_LESSTHANOREQUAL : 161, - OP_GREATERTHANOREQUAL : 162, - OP_MIN : 163, - OP_MAX : 164, + OP_BOOLAND : 154, + OP_BOOLOR : 155, + OP_NUMEQUAL : 156, + OP_NUMEQUALVERIFY : 157, + OP_NUMNOTEQUAL : 158, + OP_LESSTHAN : 159, + OP_GREATERTHAN : 160, + OP_LESSTHANOREQUAL : 161, + OP_GREATERTHANOREQUAL : 162, + OP_MIN : 163, + OP_MAX : 164, - OP_WITHIN : 165, + OP_WITHIN : 165, - // crypto - OP_RIPEMD160 : 166, - OP_SHA1 : 167, - OP_SHA256 : 168, - OP_HASH160 : 169, - OP_HASH256 : 170, - OP_CODESEPARATOR : 171, - OP_CHECKSIG : 172, - OP_CHECKSIGVERIFY : 173, - OP_CHECKMULTISIG : 174, - OP_CHECKMULTISIGVERIFY : 175, + // crypto + OP_RIPEMD160 : 166, + OP_SHA1 : 167, + OP_SHA256 : 168, + OP_HASH160 : 169, + OP_HASH256 : 170, + OP_CODESEPARATOR : 171, + OP_CHECKSIG : 172, + OP_CHECKSIGVERIFY : 173, + OP_CHECKMULTISIG : 174, + OP_CHECKMULTISIGVERIFY : 175, - // expansion - OP_NOP1 : 176, - OP_NOP2 : 177, - OP_NOP3 : 178, - OP_NOP4 : 179, - OP_NOP5 : 180, - OP_NOP6 : 181, - OP_NOP7 : 182, - OP_NOP8 : 183, - OP_NOP9 : 184, - OP_NOP10 : 185, + // expansion + OP_NOP1 : 176, + OP_NOP2 : 177, + OP_NOP3 : 178, + OP_NOP4 : 179, + OP_NOP5 : 180, + OP_NOP6 : 181, + OP_NOP7 : 182, + OP_NOP8 : 183, + OP_NOP9 : 184, + OP_NOP10 : 185, - // template matching params - OP_PUBKEYHASH : 253, - OP_PUBKEY : 254, - OP_INVALIDOPCODE : 255 - }, - reverseMap: [] + // template matching params + OP_PUBKEYHASH : 253, + OP_PUBKEY : 254, + OP_INVALIDOPCODE : 255 + }, + reverseMap: [] } for(var i in Opcode.map) { - Opcode.reverseMap[Opcode.map[i]] = i + Opcode.reverseMap[Opcode.map[i]] = i } -module.exports = Opcode; +module.exports = Opcode diff --git a/src/script.js b/src/script.js index 85c4454..726bd16 100644 --- a/src/script.js +++ b/src/script.js @@ -1,46 +1,46 @@ -var Opcode = require('./opcode'); -var util = require('./util'); -var convert = require('./convert'); -var Address = require('./address'); -var network = require('./network'); +var Opcode = require('./opcode') +var util = require('./util') +var convert = require('./convert') +var Address = require('./address') +var network = require('./network') var Script = function(data) { - this.buffer = data || []; - if(!Array.isArray(this.buffer)) { - throw new Error('expect Script to be initialized with Array, but got ' + data) - } - this.parse(); -}; + this.buffer = data || [] + if(!Array.isArray(this.buffer)) { + throw new Error('expect Script to be initialized with Array, but got ' + data) + } + this.parse() +} Script.fromHex = function(data) { - return new Script(convert.hexToBytes(data)) -}; + return new Script(convert.hexToBytes(data)) +} Script.fromPubKey = function(str) { - var script = new Script(); - var s = str.split(' '); - for (var i in s) { - if (Opcode.map.hasOwnProperty(s[i])) { - script.writeOp(Opcode.map[s[i]]); - } else { - script.writeBytes(convert.hexToBytes(s[i])); - } + var script = new Script() + var s = str.split(' ') + for (var i in s) { + if (Opcode.map.hasOwnProperty(s[i])) { + script.writeOp(Opcode.map[s[i]]) + } else { + script.writeBytes(convert.hexToBytes(s[i])) } - return script; -}; + } + return script +} Script.fromScriptSig = function(str) { - var script = new Script(); - var s = str.split(' '); - for (var i in s) { - if (Opcode.map.hasOwnProperty(s[i])) { - script.writeOp(Opcode.map[s[i]]); - } else { - script.writeBytes(convert.hexToBytes(s[i])); - } + var script = new Script() + var s = str.split(' ') + for (var i in s) { + if (Opcode.map.hasOwnProperty(s[i])) { + script.writeOp(Opcode.map[s[i]]) + } else { + script.writeBytes(convert.hexToBytes(s[i])) } - return script; -}; + } + return script +} /** * Update the parsed script representation. @@ -54,47 +54,47 @@ Script.fromScriptSig = function(str) { * the script buffer manually, you should update the chunks using this method. */ Script.prototype.parse = function() { - var self = this; + var self = this + + this.chunks = [] - this.chunks = []; + // Cursor + var i = 0 - // Cursor - var i = 0; + // Read n bytes and store result as a chunk + function readChunk(n) { + self.chunks.push(self.buffer.slice(i, i + n)) + i += n + } - // Read n bytes and store result as a chunk - function readChunk(n) { - self.chunks.push(self.buffer.slice(i, i + n)); - i += n; + while (i < this.buffer.length) { + var opcode = this.buffer[i++] + if (opcode >= 0xF0) { + // Two byte opcode + opcode = (opcode << 8) | this.buffer[i++] } - while (i < this.buffer.length) { - var opcode = this.buffer[i++]; - if (opcode >= 0xF0) { - // Two byte opcode - opcode = (opcode << 8) | this.buffer[i++]; - } - - var len; - if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { - // Read some bytes of data, opcode value is the length of data - readChunk(opcode); - } else if (opcode == Opcode.map.OP_PUSHDATA1) { - len = this.buffer[i++]; - readChunk(len); - } else if (opcode == Opcode.map.OP_PUSHDATA2) { - len = (this.buffer[i++] << 8) | this.buffer[i++]; - readChunk(len); - } else if (opcode == Opcode.map.OP_PUSHDATA4) { - len = (this.buffer[i++] << 24) | - (this.buffer[i++] << 16) | - (this.buffer[i++] << 8) | - this.buffer[i++]; - readChunk(len); - } else { - this.chunks.push(opcode); - } + var len + if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { + // Read some bytes of data, opcode value is the length of data + readChunk(opcode) + } else if (opcode == Opcode.map.OP_PUSHDATA1) { + len = this.buffer[i++] + readChunk(len) + } else if (opcode == Opcode.map.OP_PUSHDATA2) { + len = (this.buffer[i++] << 8) | this.buffer[i++] + readChunk(len) + } else if (opcode == Opcode.map.OP_PUSHDATA4) { + len = (this.buffer[i++] << 24) | + (this.buffer[i++] << 16) | + (this.buffer[i++] << 8) | + this.buffer[i++] + readChunk(len) + } else { + this.chunks.push(opcode) } -}; + } +} /** * Compare the script to known templates of scriptPubKey. @@ -115,21 +115,21 @@ Script.prototype.parse = function() { * Any other script (no template matched). */ Script.prototype.getOutType = function() { - if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL && - this.chunks[0] == Opcode.map.OP_HASH160 && - this.chunks.length == 3) { - // Transfer to M-OF-N - return 'P2SH'; - } else if (this.chunks.length == 5 && - this.chunks[0] == Opcode.map.OP_DUP && - this.chunks[1] == Opcode.map.OP_HASH160 && - this.chunks[3] == Opcode.map.OP_EQUALVERIFY && - this.chunks[4] == Opcode.map.OP_CHECKSIG) { - // Transfer to Bitcoin address - return 'Pubkey'; - } else { - return 'Strange'; - } + if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL && + this.chunks[0] == Opcode.map.OP_HASH160 && + this.chunks.length == 3) { + // Transfer to M-OF-N + return 'P2SH' + } else if (this.chunks.length == 5 && + this.chunks[0] == Opcode.map.OP_DUP && + this.chunks[1] == Opcode.map.OP_HASH160 && + this.chunks[3] == Opcode.map.OP_EQUALVERIFY && + this.chunks[4] == Opcode.map.OP_CHECKSIG) { + // Transfer to Bitcoin address + return 'Pubkey' + } else { + return 'Strange' + } } /** @@ -137,37 +137,37 @@ Script.prototype.getOutType = function() { * Assumes strange scripts are P2SH */ Script.prototype.toScriptHash = function() { - var outType = this.getOutType(); + var outType = this.getOutType() - if (outType == 'Pubkey') { - return this.chunks[2] - } - - if (outType == 'P2SH') { - return util.sha256ripe160(this.buffer) - } + if (outType == 'Pubkey') { + return this.chunks[2] + } + if (outType == 'P2SH') { return util.sha256ripe160(this.buffer) + } + + return util.sha256ripe160(this.buffer) } //TODO: support testnet Script.prototype.getToAddress = function() { - var outType = this.getOutType(); + var outType = this.getOutType() - if (outType == 'Pubkey') { - return new Address(this.chunks[2]) - } - - if (outType == 'P2SH') { - return new Address(this.chunks[1], 5) - } + if (outType == 'Pubkey') { + return new Address(this.chunks[2]) + } + if (outType == 'P2SH') { return new Address(this.chunks[1], 5) + } + + return new Address(this.chunks[1], 5) } //TODO: support testnet Script.prototype.getFromAddress = function(){ - return new Address(this.simpleInHash()); + return new Address(this.simpleInHash()) } /** @@ -196,24 +196,24 @@ Script.prototype.getFromAddress = function(){ * Any other script (no template matched). */ Script.prototype.getInType = function() { - if (this.chunks.length == 1 && - Array.isArray(this.chunks[0])) { - // Direct IP to IP transactions only have the signature in their scriptSig. - // TODO: We could also check that the length of the data is correct. - return 'Pubkey'; - } else if (this.chunks.length == 2 && - Array.isArray(this.chunks[0]) && - Array.isArray(this.chunks[1])) { - return 'Address'; - } else if (this.chunks[0] == Opcode.map.OP_0 && - this.chunks.slice(1).reduce(function(t, chunk, i) { - return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1); - }, true)) { - return 'Multisig'; - } else { - return 'Strange'; - } -}; + if (this.chunks.length == 1 && + Array.isArray(this.chunks[0])) { + // Direct IP to IP transactions only have the signature in their scriptSig. + // TODO: We could also check that the length of the data is correct. + return 'Pubkey' + } else if (this.chunks.length == 2 && + Array.isArray(this.chunks[0]) && + Array.isArray(this.chunks[1])) { + return 'Address' + } else if (this.chunks[0] == Opcode.map.OP_0 && + this.chunks.slice(1).reduce(function(t, chunk, i) { + return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1) + }, true)) { + return 'Multisig' + } else { + return 'Strange' + } +} /** * Returns the affected public key for this input. @@ -229,17 +229,17 @@ Script.prototype.getInType = function() { * @deprecated */ Script.prototype.simpleInPubKey = function() { - switch (this.getInType()) { + switch (this.getInType()) { case 'Address': - return this.chunks[1]; + return this.chunks[1] case 'Pubkey': - // TODO: Theoretically, we could recover the pubkey from the sig here. - // See https://bitcointalk.org/?topic=6430.0 - throw new Error('Script does not contain pubkey'); + // TODO: Theoretically, we could recover the pubkey from the sig here. + // See https://bitcointalk.org/?topic=6430.0 + throw new Error('Script does not contain pubkey') default: - throw new Error('Encountered non-standard scriptSig'); - } -}; + throw new Error('Encountered non-standard scriptSig') + } +} /** * Returns the affected address hash for this input. @@ -257,133 +257,130 @@ Script.prototype.simpleInPubKey = function() { * This method is useful for indexing transactions. */ Script.prototype.simpleInHash = function() { - return util.sha256ripe160(this.simpleInPubKey()); -}; + return util.sha256ripe160(this.simpleInPubKey()) +} /** * Old name for Script#simpleInHash. * * @deprecated */ -Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; +Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash /** * Add an op code to the script. */ Script.prototype.writeOp = function(opcode) { - this.buffer.push(opcode); - this.chunks.push(opcode); -}; + this.buffer.push(opcode) + this.chunks.push(opcode) +} /** * Add a data chunk to the script. */ Script.prototype.writeBytes = function(data) { - if (data.length < Opcode.map.OP_PUSHDATA1) { - this.buffer.push(data.length); - } else if (data.length <= 0xff) { - this.buffer.push(Opcode.map.OP_PUSHDATA1); - this.buffer.push(data.length); - } else if (data.length <= 0xffff) { - this.buffer.push(Opcode.map.OP_PUSHDATA2); - this.buffer.push(data.length & 0xff); - this.buffer.push((data.length >>> 8) & 0xff); - } else { - this.buffer.push(Opcode.map.OP_PUSHDATA4); - this.buffer.push(data.length & 0xff); - this.buffer.push((data.length >>> 8) & 0xff); - this.buffer.push((data.length >>> 16) & 0xff); - this.buffer.push((data.length >>> 24) & 0xff); - } - this.buffer = this.buffer.concat(data); - this.chunks.push(data); -}; + if (data.length < Opcode.map.OP_PUSHDATA1) { + this.buffer.push(data.length) + } else if (data.length <= 0xff) { + this.buffer.push(Opcode.map.OP_PUSHDATA1) + this.buffer.push(data.length) + } else if (data.length <= 0xffff) { + this.buffer.push(Opcode.map.OP_PUSHDATA2) + this.buffer.push(data.length & 0xff) + this.buffer.push((data.length >>> 8) & 0xff) + } else { + this.buffer.push(Opcode.map.OP_PUSHDATA4) + this.buffer.push(data.length & 0xff) + this.buffer.push((data.length >>> 8) & 0xff) + this.buffer.push((data.length >>> 16) & 0xff) + this.buffer.push((data.length >>> 24) & 0xff) + } + this.buffer = this.buffer.concat(data) + this.chunks.push(data) +} /** * Create an output for an address */ Script.createOutputScript = function(address) { - var script = new Script(); - address = new Address(address); - if (address.version == network.mainnet.p2shVersion || address.version == network.testnet.p2shVersion) { - // Standard pay-to-script-hash - script.writeOp(Opcode.map.OP_HASH160); - script.writeBytes(address.hash); - script.writeOp(Opcode.map.OP_EQUAL); - } - else { - // Standard pay-to-pubkey-hash - script.writeOp(Opcode.map.OP_DUP); - script.writeOp(Opcode.map.OP_HASH160); - script.writeBytes(address.hash); - script.writeOp(Opcode.map.OP_EQUALVERIFY); - script.writeOp(Opcode.map.OP_CHECKSIG); - } - return script; -}; + var script = new Script() + address = new Address(address) + if (address.version == network.mainnet.p2shVersion || + address.version == network.testnet.p2shVersion) { + // Standard pay-to-script-hash + script.writeOp(Opcode.map.OP_HASH160) + script.writeBytes(address.hash) + script.writeOp(Opcode.map.OP_EQUAL) + } + else { + // Standard pay-to-pubkey-hash + script.writeOp(Opcode.map.OP_DUP) + script.writeOp(Opcode.map.OP_HASH160) + script.writeBytes(address.hash) + script.writeOp(Opcode.map.OP_EQUALVERIFY) + script.writeOp(Opcode.map.OP_CHECKSIG) + } + return script +} /** * Extract pubkeys from a multisig script */ Script.prototype.extractPubkeys = function() { - return this.chunks.filter(function(chunk) { - return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33) - }); + return this.chunks.filter(function(chunk) { + return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33) + }) } /** * Create an m-of-n output script */ Script.createMultiSigOutputScript = function(m, pubkeys) { - var script = new Script(); - - pubkeys = pubkeys.sort(); - - script.writeOp(Opcode.map.OP_1 + m - 1); - - for (var i = 0; i < pubkeys.length; ++i) { - script.writeBytes(pubkeys[i]); - } - - script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1); + var script = new Script() + pubkeys = pubkeys.sort() - script.writeOp(Opcode.map.OP_CHECKMULTISIG); + script.writeOp(Opcode.map.OP_1 + m - 1) + for (var i = 0; i < pubkeys.length; ++i) { + script.writeBytes(pubkeys[i]) + } + script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1) + script.writeOp(Opcode.map.OP_CHECKMULTISIG) - return script; -}; + return script +} /** * Create a standard payToPubKeyHash input. */ Script.createInputScript = function(signature, pubKey) { - var script = new Script(); - script.writeBytes(signature); - script.writeBytes(pubKey); - return script; -}; + var script = new Script() + script.writeBytes(signature) + script.writeBytes(pubKey) + return script +} /** * Create a multisig input */ Script.createMultiSigInputScript = function(signatures, script) { - script = new Script(script); - var k = script.chunks[0][0]; - - //Not enough sigs - if (signatures.length < k) return false; - - var inScript = new Script(); - inScript.writeOp(Opcode.map.OP_0); - signatures.map(function(sig) { - inScript.writeBytes(sig) - }); - inScript.writeBytes(script.buffer); - return inScript; + script = new Script(script) + var k = script.chunks[0][0] + + //Not enough sigs + if (signatures.length < k) return false; + + var inScript = new Script() + inScript.writeOp(Opcode.map.OP_0) + signatures.map(function(sig) { + inScript.writeBytes(sig) + }) + inScript.writeBytes(script.buffer) + return inScript } Script.prototype.clone = function() { - return new Script(this.buffer); -}; + return new Script(this.buffer) +} -module.exports = Script; +module.exports = Script diff --git a/src/transaction.js b/src/transaction.js index 577606f..55e9954 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,43 +1,43 @@ -var BigInteger = require('./jsbn/jsbn'); -var Script = require('./script'); -var util = require('./util'); -var convert = require('./convert'); -var ECKey = require('./eckey').ECKey; -var ECDSA = require('./ecdsa'); -var Address = require('./address'); -var SHA256 = require('crypto-js/sha256'); +var BigInteger = require('./jsbn/jsbn') +var Script = require('./script') +var util = require('./util') +var convert = require('./convert') +var ECKey = require('./eckey').ECKey +var ECDSA = require('./ecdsa') +var Address = require('./address') +var SHA256 = require('crypto-js/sha256') var Transaction = function (doc) { - if (!(this instanceof Transaction)) { return new Transaction(doc); } - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; - this.defaultSequence = [255, 255, 255, 255]; // 0xFFFFFFFF - - if (doc) { - if (typeof doc == "string" || Array.isArray(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) { - doc.ins.forEach(function(input) { - this.addInput(new TransactionIn(input)); - }, this); - } - - if (doc.outs && doc.outs.length) { - doc.outs.forEach(function(output) { - this.addOutput(new TransactionOut(output)); - }, this); - } - - this.hash = this.hash || this.getHash(); + if (!(this instanceof Transaction)) { return new Transaction(doc) } + this.version = 1 + this.locktime = 0 + this.ins = [] + this.outs = [] + this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF + + if (doc) { + if (typeof doc == "string" || Array.isArray(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) { + doc.ins.forEach(function(input) { + this.addInput(new TransactionIn(input)) + }, this) + } + + if (doc.outs && doc.outs.length) { + doc.outs.forEach(function(output) { + this.addOutput(new TransactionOut(output)) + }, this) + } + + this.hash = this.hash || this.getHash() + } +} /** * Create a new txin. @@ -52,27 +52,27 @@ var Transaction = function (doc) { * 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]); - } - else if (arguments[0].length > 65) { - var args = arguments[0].split(':'); - return this.addInput(args[0], args[1]); - } - else { - var hash = typeof tx === "string" ? tx : tx.hash; - hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash; - - this.ins.push(new TransactionIn({ - outpoint: { - hash: hash, - index: outIndex - }, - script: new Script(), - sequence: this.defaultSequence - })); - } -}; + if (arguments[0] instanceof TransactionIn) { + this.ins.push(arguments[0]) + } + else if (arguments[0].length > 65) { + var args = arguments[0].split(':') + return this.addInput(args[0], args[1]) + } + else { + var hash = typeof tx === "string" ? tx : tx.hash + hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash + + this.ins.push(new TransactionIn({ + outpoint: { + hash: hash, + index: outIndex + }, + script: new Script(), + sequence: this.defaultSequence + })) + } +} /** * Create a new txout. @@ -85,22 +85,22 @@ Transaction.prototype.addInput = function (tx, outIndex) { * */ Transaction.prototype.addOutput = function (address, value) { - if (arguments[0] instanceof TransactionOut) { - this.outs.push(arguments[0]); - return; - } + if (arguments[0] instanceof TransactionOut) { + this.outs.push(arguments[0]) + return + } - if (arguments[0].indexOf(':') >= 0) { - var args = arguments[0].split(':'); - address = args[0]; - value = parseInt(args[1]); - } + 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) - })); -}; + this.outs.push(new TransactionOut({ + value: value, + script: Script.createOutputScript(address) + })) +} /** * Serialize this transaction. @@ -110,48 +110,48 @@ Transaction.prototype.addOutput = function (address, value) { * be hashed to get the transaction's standard Bitcoin hash. */ Transaction.prototype.serialize = function () { - var buffer = []; - buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4)); - buffer = buffer.concat(convert.numToVarInt(this.ins.length)); + var buffer = [] + buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4)) + buffer = buffer.concat(convert.numToVarInt(this.ins.length)) - 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()); + 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); - }); + 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)); + buffer = buffer.concat(convert.numToVarInt(this.outs.length)) - this.outs.forEach(function(txout) { - buffer = buffer.concat(convert.numToBytes(txout.value,8)); + 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); - }); + 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; -}; + return buffer +} Transaction.prototype.serializeHex = function() { - return convert.bytesToHex(this.serialize()); + return convert.bytesToHex(this.serialize()) } -//var OP_CODESEPARATOR = 171; +//var OP_CODESEPARATOR = 171 -var SIGHASH_ALL = 1; -var SIGHASH_NONE = 2; -var SIGHASH_SINGLE = 3; -var SIGHASH_ANYONECANPAY = 80; +var SIGHASH_ALL = 1 +var SIGHASH_NONE = 2 +var SIGHASH_SINGLE = 3 +var SIGHASH_ANYONECANPAY = 80 /** * Hash transaction for signing a specific input. @@ -162,34 +162,34 @@ var SIGHASH_ANYONECANPAY = 80; * used to sign the transaction input in question. */ Transaction.prototype.hashTransactionForSignature = -function (connectedScript, inIndex, hashType) + function (connectedScript, inIndex, hashType) { - var txTmp = this.clone(); + var txTmp = this.clone() // In case concatenating two scripts ends up with two codeseparators, // or an extra one at the end, this prevents all those possible // incompatibilities. /*scriptCode = scriptCode.filter(function (val) { - return val !== OP_CODESEPARATOR; - });*/ + return val !== OP_CODESEPARATOR + });*/ // Blank out other inputs' signatures txTmp.ins.forEach(function(txin) { - txin.script = new Script(); - }); + txin.script = new Script() + }) - txTmp.ins[inIndex].script = connectedScript; + txTmp.ins[inIndex].script = connectedScript // Blank out some of the outputs if ((hashType & 0x1f) == SIGHASH_NONE) { - txTmp.outs = []; + txTmp.outs = [] // Let the others update at will txTmp.ins.forEach(function(txin, i) { if (i != inIndex) { - txTmp.ins[i].sequence = 0; + txTmp.ins[i].sequence = 0 } - }); + }) } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { // TODO: Implement @@ -197,16 +197,16 @@ function (connectedScript, inIndex, hashType) // Blank out other inputs completely, not recommended for open transactions if (hashType & SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]]; + txTmp.ins = [txTmp.ins[inIndex]] } - var buffer = txTmp.serialize(); + var buffer = txTmp.serialize() - buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4)); - buffer = convert.bytesToWordArray(buffer); + buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4)) + buffer = convert.bytesToWordArray(buffer) - return convert.wordArrayToBytes(SHA256(SHA256(buffer))); -}; + return convert.wordArrayToBytes(SHA256(SHA256(buffer))) +} /** * Calculate and return the transaction's hash. @@ -215,166 +215,157 @@ function (connectedScript, inIndex, hashType) */ Transaction.prototype.getHash = function () { - var buffer = convert.bytesToWordArray(this.serialize()); - return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse(); -}; + var buffer = convert.bytesToWordArray(this.serialize()) + return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse() +} -/** - * Create a copy of this transaction object. - */ Transaction.prototype.clone = function () { - var newTx = new Transaction(); - newTx.version = this.version; - newTx.locktime = this.locktime; + var newTx = new Transaction() + newTx.version = this.version + newTx.locktime = this.locktime this.ins.forEach(function(txin) { - newTx.addInput(txin.clone()); - }); + newTx.addInput(txin.clone()) + }) this.outs.forEach(function(txout) { - newTx.addOutput(txout.clone()); - }); - - return newTx; -}; + newTx.addOutput(txout.clone()) + }) -/** - * Converts a serialized transaction into a transaction object - */ + return newTx +} Transaction.deserialize = function(buffer) { - if (typeof buffer == "string") { - buffer = convert.hexToBytes(buffer) - } - var pos = 0; - var readAsInt = function(bytes) { - if (bytes === 0) return 0; - pos++; - return buffer[pos-1] + readAsInt(bytes-1) * 256; - } - var readVarInt = function() { - var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read - var result = convert.varIntToNum(bytes) + if (typeof buffer == "string") { + buffer = convert.hexToBytes(buffer) + } + var pos = 0 + var readAsInt = function(bytes) { + if (bytes === 0) return 0; + pos++; + return buffer[pos-1] + readAsInt(bytes-1) * 256 + } + var readVarInt = function() { + var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read + var result = convert.varIntToNum(bytes) - pos += result.bytes.length - return result.number - } - var readBytes = function(bytes) { - pos += bytes; - return buffer.slice(pos - bytes, pos); - } - var readVarString = function() { - var size = readVarInt(); - return readBytes(size); - } - var obj = { - ins: [], - outs: [] - } - obj.version = readAsInt(4); - var ins = readVarInt(); - var i; - - for (i = 0; i < ins; i++) { - obj.ins.push({ - outpoint: { - hash: convert.bytesToHex(readBytes(32).reverse()), - index: readAsInt(4) - }, - script: new Script(readVarString()), - sequence: readBytes(4) - }); - } - var outs = readVarInt(); + pos += result.bytes.length + return result.number + } + var readBytes = function(bytes) { + pos += bytes + return buffer.slice(pos - bytes, pos) + } + var readVarString = function() { + var size = readVarInt() + return readBytes(size) + } + var obj = { + ins: [], + outs: [] + } + obj.version = readAsInt(4) + var ins = readVarInt() + var i + + for (i = 0; i < ins; i++) { + obj.ins.push({ + outpoint: { + hash: convert.bytesToHex(readBytes(32).reverse()), + index: readAsInt(4) + }, + script: new Script(readVarString()), + sequence: readBytes(4) + }) + } + var outs = readVarInt() - for (i = 0; i < outs; i++) { - obj.outs.push({ - value: convert.bytesToNum(readBytes(8)), - script: new Script(readVarString()) - }); - } + for (i = 0; i < outs; i++) { + obj.outs.push({ + value: convert.bytesToNum(readBytes(8)), + script: new Script(readVarString()) + }) + } - obj.locktime = readAsInt(4); + obj.locktime = readAsInt(4) - return new Transaction(obj); + return new Transaction(obj) } /** * Signs a standard output at some index with the given key */ - 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().toBytes(), - hash160 = util.sha256ripe160(pub), - script = Script.createOutputScript(new Address(hash160)), - hash = this.hashTransactionForSignature(script, index, type), - sig = key.sign(hash).concat([type]); - this.ins[index].script = Script.createInputScript(sig, pub); + 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().toBytes(), + hash160 = util.sha256ripe160(pub), + script = Script.createOutputScript(new Address(hash160)), + hash = this.hashTransactionForSignature(script, index, type), + sig = key.sign(hash).concat([type]) + this.ins[index].script = Script.createInputScript(sig, pub) } // Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...] Transaction.prototype.signWithKeys = function(keys, outputs, type) { - type = type || SIGHASH_ALL; + type = type || SIGHASH_ALL - var addrdata = keys.map(function(key) { - key = new ECKey(key); - return { - key: key, - address: key.getAddress().toString() - } - }); + var addrdata = keys.map(function(key) { + key = new ECKey(key) + return { + key: key, + address: key.getAddress().toString() + } + }) - var hmap = {}; - outputs.forEach(function(o) { - hmap[o.output] = o; - }); + var hmap = {} + 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; - var histItem = hmap[outpoint]; + for (var i = 0; i < this.ins.length; i++) { + var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index + var histItem = hmap[outpoint] - if (!histItem) continue; + if (!histItem) continue; - var thisInputAddrdata = addrdata.filter(function(a) { - return a.address == histItem.address; - }); + 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); - } + this.sign(i,thisInputAddrdata[0].key) + } } /** * Signs a P2SH output at some index with the given key */ - Transaction.prototype.p2shsign = function(index, script, key, type) { - script = new Script(script); - key = new ECKey(key); - type = type || SIGHASH_ALL; - var hash = this.hashTransactionForSignature(script, index, type), - sig = key.sign(hash).concat([type]); - return sig; + script = new Script(script) + key = new ECKey(key) + type = type || SIGHASH_ALL + var hash = this.hashTransactionForSignature(script, index, type), + sig = key.sign(hash).concat([type]) + return sig } -Transaction.prototype.multisign = Transaction.prototype.p2shsign; +Transaction.prototype.multisign = Transaction.prototype.p2shsign Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) { - this.ins[index].script = Script.createMultiSigInputScript(sigs, script); + this.ins[index].script = Script.createMultiSigInputScript(sigs, script) } Transaction.prototype.validateSig = function(index, script, sig, pub) { - script = new Script(script); - var hash = this.hashTransactionForSignature(script,index,1); - return ECDSA.verify(hash, convert.coerceToBytes(sig), - convert.coerceToBytes(pub)); + script = new Script(script) + var hash = this.hashTransactionForSignature(script,index,1) + return ECDSA.verify(hash, convert.coerceToBytes(sig), + convert.coerceToBytes(pub)) } Transaction.feePerKb = 20000 @@ -383,66 +374,68 @@ Transaction.prototype.estimateFee = function(feePerKb){ var outSize = 34 var fixedPadding = 34 - if(feePerKb == undefined) feePerKb = Transaction.feePerKb + if(feePerKb == undefined) feePerKb = Transaction.feePerKb; var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding return feePerKb * Math.ceil(size / 1000) } var TransactionIn = function (data) { - if (typeof data == "string") - this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } - else if (data.outpoint) - this.outpoint = data.outpoint - else - this.outpoint = { hash: data.hash, index: data.index } - - if (data.scriptSig) - this.script = Script.fromScriptSig(data.scriptSig) - else if (data.script) - this.script = data.script - else - this.script = new Script(data.script) - - this.sequence = data.sequence || this.defaultSequence -}; + if (typeof data == "string") { + this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } + } else if (data.outpoint) { + this.outpoint = data.outpoint + } else { + this.outpoint = { hash: data.hash, index: data.index } + } + + if (data.scriptSig) { + this.script = Script.fromScriptSig(data.scriptSig) + } else if (data.script) { + this.script = data.script + } else { + this.script = new Script(data.script) + } + + this.sequence = data.sequence || this.defaultSequence +} TransactionIn.prototype.clone = function () { - return new TransactionIn({ - outpoint: { - hash: this.outpoint.hash, - index: this.outpoint.index - }, - script: this.script.clone(), - sequence: this.sequence - }); -}; + return new TransactionIn({ + outpoint: { + hash: this.outpoint.hash, + index: this.outpoint.index + }, + script: this.script.clone(), + sequence: this.sequence + }) +} var TransactionOut = function (data) { - this.script = - data.script instanceof Script ? data.script.clone() - : Array.isArray(data.script) ? new Script(data.script) - : typeof data.script == "string" ? new Script(convert.hexToBytes(data.script)) - : data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey) - : data.address ? Script.createOutputScript(data.address) - : new Script(); - - if (this.script.buffer.length > 0) this.address = this.script.getToAddress(); - - this.value = - Array.isArray(data.value) ? convert.bytesToNum(data.value) - : "string" == typeof data.value ? parseInt(data.value) - : data.value instanceof BigInteger ? parseInt(data.value.toString()) - : data.value; -}; + this.script = + data.script instanceof Script ? data.script.clone() + : Array.isArray(data.script) ? new Script(data.script) + : typeof data.script == "string" ? new Script(convert.hexToBytes(data.script)) + : data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey) + : data.address ? Script.createOutputScript(data.address) + : new Script() + + if (this.script.buffer.length > 0) this.address = this.script.getToAddress(); + + this.value = + Array.isArray(data.value) ? convert.bytesToNum(data.value) + : "string" == typeof data.value ? parseInt(data.value) + : data.value instanceof BigInteger ? parseInt(data.value.toString()) + : data.value +} TransactionOut.prototype.clone = function() { var newTxout = new TransactionOut({ script: this.script.clone(), value: this.value - }); - return newTxout; -}; + }) + return newTxout +} TransactionOut.prototype.scriptPubKey = function() { return convert.bytesToHex(this.script.buffer) diff --git a/src/util.js b/src/util.js index f33d04d..9cbc541 100644 --- a/src/util.js +++ b/src/util.js @@ -3,21 +3,11 @@ var Crypto = require('crypto-js') var RIPEMD160 = Crypto.RIPEMD160 var SHA256 = Crypto.SHA256 -/** - * Calculate RIPEMD160(SHA256(data)). - * - * Takes an arbitrary byte array as inputs and returns the hash as a byte - * array. - */ exports.sha256ripe160 = function (data) { var wordArray = RIPEMD160(SHA256(convert.bytesToWordArray(data))) return convert.wordArrayToBytes(wordArray) } -/** - * Convenience method for throw new Error('some message'), e.g. - * error('something went wrong') - */ exports.error = function (msg) { throw new Error(msg) } diff --git a/src/wallet.js b/src/wallet.js index 88499b3..58cc098 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -1,311 +1,311 @@ -var convert = require('./convert'); -var Transaction = require('./transaction').Transaction; +var convert = require('./convert') +var Transaction = require('./transaction').Transaction var HDNode = require('./hdwallet.js') -var rng = require('secure-random'); +var rng = require('secure-random') var Wallet = function (seed, options) { - if (!(this instanceof Wallet)) { return new Wallet(seed, options); } - - var options = options || {} - var network = options.network || 'mainnet' - - // Stored in a closure to make accidental serialization less likely - var masterkey = null; - var me = this; - var accountZero = null; - var internalAccount = null; - var externalAccount = null; - - // Addresses - this.addresses = []; - this.changeAddresses = []; - - // Transaction output data - this.outputs = {}; - - // Make a new master key - this.newMasterKey = function(seed, network) { - if (!seed) seed= rng(32, { array: true }) - masterkey = new HDNode(seed, network); - - // HD first-level child derivation method should be private - // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254 - accountZero = masterkey.derivePrivate(0) - externalAccount = accountZero.derive(0) - internalAccount = accountZero.derive(1) - - me.addresses = []; - me.changeAddresses = []; - - me.outputs = {}; - } - this.newMasterKey(seed, network) - - - this.generateAddress = function() { - var key = externalAccount.derive(this.addresses.length) - this.addresses.push(key.getAddress().toString()) - return this.addresses[this.addresses.length - 1] - } - - this.generateChangeAddress = function() { - var key = internalAccount.derive(this.changeAddresses.length) - this.changeAddresses.push(key.getAddress().toString()) - return this.changeAddresses[this.changeAddresses.length - 1] + if (!(this instanceof Wallet)) { return new Wallet(seed, options); } + + var options = options || {} + var network = options.network || 'mainnet' + + // Stored in a closure to make accidental serialization less likely + var masterkey = null + var me = this + var accountZero = null + var internalAccount = null + var externalAccount = null + + // Addresses + this.addresses = [] + this.changeAddresses = [] + + // Transaction output data + this.outputs = {} + + // Make a new master key + this.newMasterKey = function(seed, network) { + if (!seed) seed = rng(32, { array: true }); + masterkey = new HDNode(seed, network) + + // HD first-level child derivation method should be private + // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254 + accountZero = masterkey.derivePrivate(0) + externalAccount = accountZero.derive(0) + internalAccount = accountZero.derive(1) + + me.addresses = [] + me.changeAddresses = [] + + me.outputs = {} + } + this.newMasterKey(seed, network) + + + this.generateAddress = function() { + var key = externalAccount.derive(this.addresses.length) + this.addresses.push(key.getAddress().toString()) + return this.addresses[this.addresses.length - 1] + } + + this.generateChangeAddress = function() { + var key = internalAccount.derive(this.changeAddresses.length) + this.changeAddresses.push(key.getAddress().toString()) + return this.changeAddresses[this.changeAddresses.length - 1] + } + + this.getBalance = function() { + return this.getUnspentOutputs().reduce(function(memo, output){ + return memo + output.value + }, 0) + } + + this.getUnspentOutputs = function() { + var utxo = [] + + for(var key in this.outputs){ + var output = this.outputs[key] + if(!output.spend) utxo.push(outputToUnspentOutput(output)) } - this.getBalance = function() { - return this.getUnspentOutputs().reduce(function(memo, output){ - return memo + output.value - }, 0) + return utxo + } + + this.setUnspentOutputs = function(utxo) { + var outputs = {} + + utxo.forEach(function(uo){ + validateUnspentOutput(uo) + var o = unspentOutputToOutput(uo) + outputs[o.receive] = o + }) + + this.outputs = outputs + } + + this.setUnspentOutputsAsync = function(utxo, callback) { + var error = null + try { + this.setUnspentOutputs(utxo) + } catch(err) { + error = err + } finally { + process.nextTick(function(){ callback(error) }) } + } - this.getUnspentOutputs = function() { - var utxo = [] + function outputToUnspentOutput(output){ + var hashAndIndex = output.receive.split(":") - for(var key in this.outputs){ - var output = this.outputs[key] - if(!output.spend) utxo.push(outputToUnspentOutput(output)) - } - - return utxo + return { + hash: hashAndIndex[0], + hashLittleEndian: convert.reverseEndian(hashAndIndex[0]), + outputIndex: parseInt(hashAndIndex[1]), + address: output.address, + value: output.value } - - this.setUnspentOutputs = function(utxo) { - var outputs = {} - - utxo.forEach(function(uo){ - validateUnspentOutput(uo) - var o = unspentOutputToOutput(uo) - outputs[o.receive] = o - }) - - this.outputs = outputs + } + + function unspentOutputToOutput(o) { + var hash = o.hash || convert.reverseEndian(o.hashLittleEndian) + var key = hash + ":" + o.outputIndex + return { + receive: key, + address: o.address, + value: o.value } + } - this.setUnspentOutputsAsync = function(utxo, callback) { - var error = null - try { - this.setUnspentOutputs(utxo) - } catch(err) { - error = err - } finally { - process.nextTick(function(){ callback(error) }) - } - } + function validateUnspentOutput(uo) { + var missingField - function outputToUnspentOutput(output){ - var hashAndIndex = output.receive.split(":") - - return { - hash: hashAndIndex[0], - hashLittleEndian: convert.reverseEndian(hashAndIndex[0]), - outputIndex: parseInt(hashAndIndex[1]), - address: output.address, - value: output.value - } + if(isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)){ + missingField = "hash(or hashLittleEndian)" } - function unspentOutputToOutput(o) { - var hash = o.hash || convert.reverseEndian(o.hashLittleEndian) - var key = hash + ":" + o.outputIndex - return { - receive: key, - address: o.address, - value: o.value + var requiredKeys = ['outputIndex', 'address', 'value'] + requiredKeys.forEach(function(key){ + if(isNullOrUndefined(uo[key])){ + missingField = key } + }) + + if(missingField) { + var message = [ + 'Invalid unspent output: key', field, 'is missing.', + 'A valid unspent output must contain' + ] + message.push(requiredKeys.join(', ')) + message.push("and hash(or hashLittleEndian)") + throw new Error(message.join(' ')) } - - function validateUnspentOutput(uo) { - var missingField; - - if(isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)){ - missingField = "hash(or hashLittleEndian)" - } - - var requiredKeys = ['outputIndex', 'address', 'value'] - requiredKeys.forEach(function(key){ - if(isNullOrUndefined(uo[key])){ - missingField = key + } + + function isNullOrUndefined(value){ + return value == undefined + } + + this.processTx = function(tx) { + var txhash = convert.bytesToHex(tx.getHash()) + + tx.outs.forEach(function(txOut, i){ + var address = txOut.address.toString() + if (isMyAddress(address)) { + var output = txhash+':'+i + me.outputs[output] = { + receive: output, + value: txOut.value, + address: address, } - }) - - if(missingField) { - var message = [ - 'Invalid unspent output: key', field, 'is missing.', - 'A valid unspent output must contain' - ] - message.push(requiredKeys.join(', ')) - message.push("and hash(or hashLittleEndian)") - throw new Error(message.join(' ')) } - } - - function isNullOrUndefined(value){ - return value == undefined - } - - this.processTx = function(tx) { - var txhash = convert.bytesToHex(tx.getHash()) - - tx.outs.forEach(function(txOut, i){ - var address = txOut.address.toString() - if (isMyAddress(address)) { - var output = txhash+':'+i - me.outputs[output] = { - receive: output, - value: txOut.value, - address: address, - } - } - }) - - tx.ins.forEach(function(txIn, i){ - var op = txIn.outpoint - var o = me.outputs[op.hash+':'+op.index] - if (o) { - o.spend = txhash+':'+i - } - }) - } - - this.createTx = function(to, value, fixedFee) { - checkDust(value) - - var tx = new Transaction() - tx.addOutput(to, value) + }) - var utxo = getCandidateOutputs(value) - var totalInValue = 0 - for(var i=0; i 0 && !isDust(change)) { - tx.addOutput(getChangeAddress(), change) - } - break; - } + var tx = new Transaction() + tx.addOutput(to, value) - checkInsufficientFund(totalInValue, value, fee) + var utxo = getCandidateOutputs(value) + var totalInValue = 0 + for(var i=0; i 0 && !isDust(change)) { + tx.addOutput(getChangeAddress(), change) } + break } - this.dustThreshold = 5430 - function isDust(amount) { - return amount <= me.dustThreshold - } - - function checkDust(value){ - if (isNullOrUndefined(value) || isDust(value)) { - throw new Error("Value must be above dust threshold") - } - } + checkInsufficientFund(totalInValue, value, fee) - function getCandidateOutputs(value){ - var unspent = [] - for (var key in me.outputs){ - var output = me.outputs[key] - if(!output.spend) unspent.push(output) - } + this.sign(tx) - var sortByValueDesc = unspent.sort(function(o1, o2){ - return o2.value - o1.value - }) + return tx + } - return sortByValueDesc; + this.createTxAsync = function(to, value, fixedFee, callback){ + if(fixedFee instanceof Function) { + callback = fixedFee + fixedFee = undefined } - - function estimateFeePadChangeOutput(tx){ - var tmpTx = tx.clone() - tmpTx.addOutput(getChangeAddress(), 0) - return tmpTx.estimateFee() + var tx = null + var error = null + + try { + tx = this.createTx(to, value, fixedFee) + } catch(err) { + error = err + } finally { + process.nextTick(function(){ callback(error, tx) }) } + } - function getChangeAddress() { - if(me.changeAddresses.length === 0) me.generateChangeAddress() - return me.changeAddresses[me.changeAddresses.length - 1] - } + this.dustThreshold = 5430 + function isDust(amount) { + return amount <= me.dustThreshold + } - function checkInsufficientFund(totalInValue, value, fee) { - if(totalInValue < value + fee) { - throw new Error('Not enough money to send funds including transaction fee. Have: ' + - totalInValue + ', needed: ' + (value + fee)) - } + function checkDust(value){ + if (isNullOrUndefined(value) || isDust(value)) { + throw new Error("Value must be above dust threshold") } + } - 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)) - } - }) - return tx; + function getCandidateOutputs(value){ + var unspent = [] + for (var key in me.outputs){ + var output = me.outputs[key] + if(!output.spend) unspent.push(output) } - this.getMasterKey = function() { return masterkey } - this.getAccountZero = function() { return accountZero } - this.getInternalAccount = function() { return internalAccount } - this.getExternalAccount = function() { return externalAccount } + var sortByValueDesc = unspent.sort(function(o1, o2){ + return o2.value - o1.value + }) - this.getPrivateKey = function(index) { - return externalAccount.derive(index).priv - } + return sortByValueDesc + } + + function estimateFeePadChangeOutput(tx){ + var tmpTx = tx.clone() + tmpTx.addOutput(getChangeAddress(), 0) + return tmpTx.estimateFee() + } + + function getChangeAddress() { + if(me.changeAddresses.length === 0) me.generateChangeAddress(); + return me.changeAddresses[me.changeAddresses.length - 1] + } - this.getInternalPrivateKey = function(index) { - return internalAccount.derive(index).priv + function checkInsufficientFund(totalInValue, value, fee) { + if(totalInValue < value + fee) { + throw new Error('Not enough money to send funds including transaction fee. Have: ' + + totalInValue + ', needed: ' + (value + fee)) } + } - this.getPrivateKeyForAddress = function(address) { - var index; - if((index = this.addresses.indexOf(address)) > -1) { - return this.getPrivateKey(index) - } else if((index = this.changeAddresses.indexOf(address)) > -1) { - return this.getInternalPrivateKey(index) - } else { - throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.') + 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)) } + }) + return tx + } + + this.getMasterKey = function() { return masterkey } + this.getAccountZero = function() { return accountZero } + this.getInternalAccount = function() { return internalAccount } + this.getExternalAccount = function() { return externalAccount } + + this.getPrivateKey = function(index) { + return externalAccount.derive(index).priv + } + + this.getInternalPrivateKey = function(index) { + return internalAccount.derive(index).priv + } + + this.getPrivateKeyForAddress = function(address) { + var index + if((index = this.addresses.indexOf(address)) > -1) { + return this.getPrivateKey(index) + } else if((index = this.changeAddresses.indexOf(address)) > -1) { + return this.getInternalPrivateKey(index) + } else { + throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.') } + } - function isReceiveAddress(address){ - return me.addresses.indexOf(address) > -1 - } + function isReceiveAddress(address){ + return me.addresses.indexOf(address) > -1 + } - function isChangeAddress(address){ - return me.changeAddresses.indexOf(address) > -1 - } + function isChangeAddress(address){ + return me.changeAddresses.indexOf(address) > -1 + } - function isMyAddress(address) { - return isReceiveAddress(address) || isChangeAddress(address) - } -}; + function isMyAddress(address) { + return isReceiveAddress(address) || isChangeAddress(address) + } +} -module.exports = Wallet; +module.exports = Wallet diff --git a/test/address.js b/test/address.js index 4b5f8d1..ff6cea6 100644 --- a/test/address.js +++ b/test/address.js @@ -1,4 +1,3 @@ -/* global describe, it */ var assert = require('assert') var Address = require('../src/address.js') var network = require('../src/network.js') @@ -7,105 +6,105 @@ var mainnet = network.mainnet.addressVersion var testnet = network.testnet.addressVersion describe('Address', function() { - var testnetAddress, mainnetAddress; - var testnetP2shAddress, mainnetP2shAddress - - beforeEach(function(){ - testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef' - mainnetAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' - testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7' - mainnetP2shAddress = '3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt' + var testnetAddress, mainnetAddress + var testnetP2shAddress, mainnetP2shAddress + + beforeEach(function(){ + testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef' + mainnetAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' + testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7' + mainnetP2shAddress = '3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt' + }) + + describe('parsing', function() { + it('works with Address object', function() { + var addr = new Address(new Address('mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3', network.testnet.addressVersion)) + + assert.equal(addr.toString(), 'mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3') + assert.equal(addr.version, network.testnet.addressVersion) }) - describe('parsing', function() { - it('works with Address object', function() { - var addr = new Address(new Address('mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3', network.testnet.addressVersion)) - - assert.equal(addr.toString(), 'mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3') - assert.equal(addr.version, network.testnet.addressVersion) - }) - - it('works with hex', function() { - var addr = new Address('13483382d3c3d43fc9d7b52e652b6bbb70e8b667') - assert.equal(addr.toString(), '12kxLGqrnnchwN9bHHNV2fWDtJGwxKTcJS') - }) - - it('throws error for invalid or unrecognized input', function() { - assert.throws(function() { - new Address('beepboopbeepboopbeepboopbeepboopbeepboopbeep') - }, Error) - }) - - it('works for byte input', function() { - var hash = base58.checkDecode('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa') - var addr = new Address(hash) - assert.equal(addr.hash, hash) - assert.equal(network.mainnet.addressVersion, hash.version) - - var hash = base58.checkDecode('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef') - var addr = new Address(hash) - assert.equal(addr.hash, hash) - assert.equal(network.testnet.addressVersion, hash.version) - }) - - it('fails for bad input', function() { - assert.throws(function() { - new Address('foo') - }, Error) - }) + it('works with hex', function() { + var addr = new Address('13483382d3c3d43fc9d7b52e652b6bbb70e8b667') + assert.equal(addr.toString(), '12kxLGqrnnchwN9bHHNV2fWDtJGwxKTcJS') }) - describe('getVersion', function() { - it('returns the proper address version', function() { - assert.equal(Address.getVersion('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'), network.mainnet.addressVersion) - assert.equal(Address.getVersion('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'), network.testnet.addressVersion) - }) + it('throws error for invalid or unrecognized input', function() { + assert.throws(function() { + new Address('beepboopbeepboopbeepboopbeepboopbeepboopbeep') + }, Error) }) - describe('toString', function() { - it('defaults to base58', function() { - var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ'; - assert.equal((new Address(addr)).toString(), addr); - }) + it('works for byte input', function() { + var hash = base58.checkDecode('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa') + var addr = new Address(hash) + assert.equal(addr.hash, hash) + assert.equal(network.mainnet.addressVersion, hash.version) + + var hash = base58.checkDecode('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef') + var addr = new Address(hash) + assert.equal(addr.hash, hash) + assert.equal(network.testnet.addressVersion, hash.version) + }) + + it('fails for bad input', function() { + assert.throws(function() { + new Address('foo') + }, Error) + }) + }) + + describe('getVersion', function() { + it('returns the proper address version', function() { + assert.equal(Address.getVersion('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'), network.mainnet.addressVersion) + assert.equal(Address.getVersion('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'), network.testnet.addressVersion) }) + }) - describe('Constructor', function(){ - it('resolves version correctly', function(){ - assert.equal((new Address(testnetAddress)).version, testnet) - assert.equal((new Address(mainnetAddress)).version, mainnet) - assert.equal((new Address(testnetP2shAddress)).version, network.testnet.p2shVersion) - assert.equal((new Address(mainnetP2shAddress)).version, network.mainnet.p2shVersion) - }) + describe('toString', function() { + it('defaults to base58', function() { + var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ' + assert.equal((new Address(addr)).toString(), addr) + }) + }) + + describe('Constructor', function(){ + it('resolves version correctly', function(){ + assert.equal((new Address(testnetAddress)).version, testnet) + assert.equal((new Address(mainnetAddress)).version, mainnet) + assert.equal((new Address(testnetP2shAddress)).version, network.testnet.p2shVersion) + assert.equal((new Address(mainnetP2shAddress)).version, network.mainnet.p2shVersion) }) + }) + + describe('validate', function() { + it('validates known good addresses', function() { + function validate(addr, expectedVersion) { + assert.ok(Address.validate(addr)) + } + + validate(testnetAddress) + validate(mainnetAddress) + validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP') + validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y') + validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs') + validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez') + validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd') + + // p2sh addresses + validate(testnetP2shAddress) + validate(mainnetP2shAddress) + }) + + it('does not validate illegal examples', function() { + function invalid(addr) { + assert.ok(!Address.validate(addr)) + } - describe('validate', function() { - it('validates known good addresses', function() { - function validate(addr, expectedVersion) { - assert.ok(Address.validate(addr)); - } - - validate(testnetAddress); - validate(mainnetAddress); - validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP'); - validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y'); - validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs'); - validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez'); - validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd'); - - // p2sh addresses - validate(testnetP2shAddress); - validate(mainnetP2shAddress); - }) - - it('does not validate illegal examples', function() { - function invalid(addr) { - assert.ok(!Address.validate(addr)); - } - - invalid(''); //empty should be invalid - invalid('%%@'); // invalid base58 string - invalid('1A1zP1eP5QGefi2DzPTf2L5SLmv7DivfNz'); // bad address (doesn't checksum) - invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); // bad address (doesn't checksum) - }) + invalid(''); //empty should be invalid + invalid('%%@'); // invalid base58 string + invalid('1A1zP1eP5QGefi2DzPTf2L5SLmv7DivfNz'); // bad address (doesn't checksum) + invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); // bad address (doesn't checksum) }) + }) }) diff --git a/test/base58.js b/test/base58.js index c95bfd7..574db6d 100644 --- a/test/base58.js +++ b/test/base58.js @@ -1,49 +1,48 @@ -/* global describe, it */ -var assert = require('assert'); -var base58 = require('../').base58; -var convert = require('../').convert; +var assert = require('assert') +var base58 = require('../').base58 +var convert = require('../').convert describe('base58', function() { - describe('decode', function() { - it('validates known examples', function() { - var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; - var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; - assert.deepEqual(base58.decode(enc), convert.hexToBytes(hex)); - }) + describe('decode', function() { + it('validates known examples', function() { + var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ' + var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d' + assert.deepEqual(base58.decode(enc), convert.hexToBytes(hex)) }) + }) - describe('encode', function() { - it('handles known examples', function() { - var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; - var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; - assert.equal(base58.encode(convert.hexToBytes(hex)), enc); - }) + describe('encode', function() { + it('handles known examples', function() { + var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ' + var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d' + assert.equal(base58.encode(convert.hexToBytes(hex)), enc) }) + }) - describe('checkEncode', function() { - it('handles known examples', function() { - var input = [ - 171, 210, 178, 125, 2, 16, 86, 184, 248, 88, 235, - 163, 244, 160, 83, 156, 184, 186, 45, 167, 169, 164, - 67, 125, 163, 89, 106, 243, 207, 193, 149, 206 - ] - var vbyte = 239 + describe('checkEncode', function() { + it('handles known examples', function() { + var input = [ + 171, 210, 178, 125, 2, 16, 86, 184, 248, 88, 235, + 163, 244, 160, 83, 156, 184, 186, 45, 167, 169, 164, + 67, 125, 163, 89, 106, 243, 207, 193, 149, 206 + ] + var vbyte = 239 - assert.equal(base58.checkEncode(input, vbyte), - '92tb9mjz6q9eKZjYvLsgk87kPrMoh7BGRumSzPeUGhmigtsfrbP'); - }) + assert.equal(base58.checkEncode(input, vbyte), + '92tb9mjz6q9eKZjYvLsgk87kPrMoh7BGRumSzPeUGhmigtsfrbP') }) + }) - describe('checkDecode', function() { - it('handles known examples', function() { - var input = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' - var expected = [ - 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, - 153, 235, 246, 240, 251, 80, 235, 184, 143, 24 - ]; - expected.version = 0 + describe('checkDecode', function() { + it('handles known examples', function() { + var input = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' + var expected = [ + 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, + 153, 235, 246, 240, 251, 80, 235, 184, 143, 24 + ] + expected.version = 0 - assert.deepEqual(base58.checkDecode(input), expected); - }) + assert.deepEqual(base58.checkDecode(input), expected) }) + }) }) diff --git a/test/convert.js b/test/convert.js index 3fb03b1..b1fe4f7 100644 --- a/test/convert.js +++ b/test/convert.js @@ -1,144 +1,143 @@ -/* global describe, it */ -var assert = require('assert'); -var convert = require('../src/convert.js'); +var assert = require('assert') +var convert = require('../src/convert.js') describe('convert', function() { - describe('bytesToHex', function() { - it('handles example 1', function() { - assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff'); - }) + describe('bytesToHex', function() { + it('handles example 1', function() { + assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff') }) + }) - describe('hexToBytes', function() { - it('handles example 1', function() { - assert.deepEqual(convert.hexToBytes('000102ff'), [0, 1, 2, 255]); - }) + describe('hexToBytes', function() { + it('handles example 1', function() { + assert.deepEqual(convert.hexToBytes('000102ff'), [0, 1, 2, 255]) }) - - it('converts from bytes to hex and back', function() { - var bytes = []; - for (var i=0 ; i<256 ; ++i) { - bytes.push(i); - } - - var hex = convert.bytesToHex(bytes); - assert.equal(hex.length, 512); - assert.deepEqual(convert.hexToBytes(hex), bytes); + }) + + it('converts from bytes to hex and back', function() { + var bytes = [] + for (var i=0 ; i<256 ; ++i) { + bytes.push(i) + } + + var hex = convert.bytesToHex(bytes) + assert.equal(hex.length, 512) + assert.deepEqual(convert.hexToBytes(hex), bytes) + }) + + describe('bytesToBase64', function() { + it('passes RFC4648 test vectors', function() { + // Test vectors from: + // http://tools.ietf.org/html/rfc4648#page-12 + + var b64 = function(s) { + return convert.bytesToBase64(convert.stringToBytes(s)) + } + + assert.equal(b64(''), '') + assert.equal(b64('f'), 'Zg==') + assert.equal(b64('fo'), 'Zm8=') + assert.equal(b64('foo'), 'Zm9v') + assert.equal(b64('foob'), 'Zm9vYg==') + assert.equal(b64('fooba'), 'Zm9vYmE=') + assert.equal(b64('foobar'), 'Zm9vYmFy') }) - - describe('bytesToBase64', function() { - it('passes RFC4648 test vectors', function() { - // Test vectors from: - // http://tools.ietf.org/html/rfc4648#page-12 - - var b64 = function(s) { - return convert.bytesToBase64(convert.stringToBytes(s)) - } - - assert.equal(b64(''), '') - assert.equal(b64('f'), 'Zg==') - assert.equal(b64('fo'), 'Zm8=') - assert.equal(b64('foo'), 'Zm9v') - assert.equal(b64('foob'), 'Zm9vYg==') - assert.equal(b64('fooba'), 'Zm9vYmE=') - assert.equal(b64('foobar'), 'Zm9vYmFy') - }) - }) - - describe('byte array and word array conversions', function(){ - var bytes, wordArray; - - beforeEach(function(){ - bytes = [ - 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, - 153, 235, 246, 240, 251, 80, 235, 184, 143, 24 - ] - wordArray = { - words: [1659439025, 1556031445, 1112775147, -151979184, -340226280], - sigBytes: 20 - } - }) - - describe('bytesToWords', function() { - it('works', function() { - assert.deepEqual(convert.bytesToWordArray(bytes), wordArray) - }) - }) - - describe('bytesToWords', function() { - it('works', function() { - assert.deepEqual(convert.wordArrayToBytes(wordArray), bytes) - }) - }) + }) + + describe('byte array and word array conversions', function(){ + var bytes, wordArray + + beforeEach(function(){ + bytes = [ + 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, + 153, 235, 246, 240, 251, 80, 235, 184, 143, 24 + ] + wordArray = { + words: [1659439025, 1556031445, 1112775147, -151979184, -340226280], + sigBytes: 20 + } }) - describe('numToVarInt', function() { - describe('works', function() { - var data = [ - 0, 128, 252, // 8-bit - 256, 512, 1024, // 16-bit - 65541, // 32-bit - 4294967299, // 64-bit - ] - var expected = [ - [0], [128], [252], // 8-bit - [253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit - [254, 5, 0, 1, 0], // 32-bit - [255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit - ] - - for (var i = 0; i < data.length; ++i) { - var actual = convert.numToVarInt(data[i]) - assert.deepEqual(actual, expected[i]) - } + describe('bytesToWords', function() { + it('works', function() { + assert.deepEqual(convert.bytesToWordArray(bytes), wordArray) }) }) - describe('varIntToNum', function() { - it('works on valid input', function() { - var data = [ - [0], [128], [252], // 8-bit - [253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit - [254, 5, 0, 1, 0], // 32-bit - [255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit - ] - var expected = [ - 0, 128, 252, // 8-bit - 256, 512, 1024, // 16-bit - 65541, // 32-bit - 4294967299, // 64-bit - ] - - for (var i = 0; i < data.length; ++i) { - var actual = convert.varIntToNum(data[i]) - assert.equal(actual.number, expected[i]) - assert.deepEqual(actual.bytes, data[i]) - } - }) - - it('uses only what is necessary', function() { - var data = [ - [0, 99], - [253, 0, 1, 99], - [254, 5, 0, 1, 0, 99], - [255, 3, 0, 0, 0, 1, 0, 0, 0, 99] - ] - var expected = [0, 256, 65541, 4294967299] - - for (var i = 0; i < data.length; ++i) { - var actual = convert.varIntToNum(data[i]) - assert.equal(actual.number, expected[i]) - assert.deepEqual(actual.bytes, data[i].slice(0, -1)) - } + describe('bytesToWords', function() { + it('works', function() { + assert.deepEqual(convert.wordArrayToBytes(wordArray), bytes) }) }) + }) + + describe('numToVarInt', function() { + describe('works', function() { + var data = [ + 0, 128, 252, // 8-bit + 256, 512, 1024, // 16-bit + 65541, // 32-bit + 4294967299, // 64-bit + ] + var expected = [ + [0], [128], [252], // 8-bit + [253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit + [254, 5, 0, 1, 0], // 32-bit + [255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit + ] + + for (var i = 0; i < data.length; ++i) { + var actual = convert.numToVarInt(data[i]) + assert.deepEqual(actual, expected[i]) + } + }) + }) + + describe('varIntToNum', function() { + it('works on valid input', function() { + var data = [ + [0], [128], [252], // 8-bit + [253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit + [254, 5, 0, 1, 0], // 32-bit + [255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit + ] + var expected = [ + 0, 128, 252, // 8-bit + 256, 512, 1024, // 16-bit + 65541, // 32-bit + 4294967299, // 64-bit + ] + + for (var i = 0; i < data.length; ++i) { + var actual = convert.varIntToNum(data[i]) + assert.equal(actual.number, expected[i]) + assert.deepEqual(actual.bytes, data[i]) + } + }) - describe('reverseEndian', function() { - it('works', function() { - var bigEndian = "6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7" - var littleEdian = "c7b97b432e7b3a8e31d1a4a0fa326c8fb002d19f2da5fc4feaf9c43a2762406a" - assert.deepEqual(convert.reverseEndian(bigEndian), littleEdian) - assert.deepEqual(convert.reverseEndian(littleEdian), bigEndian) - }) + it('uses only what is necessary', function() { + var data = [ + [0, 99], + [253, 0, 1, 99], + [254, 5, 0, 1, 0, 99], + [255, 3, 0, 0, 0, 1, 0, 0, 0, 99] + ] + var expected = [0, 256, 65541, 4294967299] + + for (var i = 0; i < data.length; ++i) { + var actual = convert.varIntToNum(data[i]) + assert.equal(actual.number, expected[i]) + assert.deepEqual(actual.bytes, data[i].slice(0, -1)) + } + }) + }) + + describe('reverseEndian', function() { + it('works', function() { + var bigEndian = "6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7" + var littleEdian = "c7b97b432e7b3a8e31d1a4a0fa326c8fb002d19f2da5fc4feaf9c43a2762406a" + assert.deepEqual(convert.reverseEndian(bigEndian), littleEdian) + assert.deepEqual(convert.reverseEndian(littleEdian), bigEndian) }) + }) }) diff --git a/test/ec.js b/test/ec.js index 2be6f72..eaadbd9 100644 --- a/test/ec.js +++ b/test/ec.js @@ -1,23 +1,22 @@ -/* global describe, it */ -var assert = require('assert'); -var sec = require('../src/jsbn/sec'); -var ecdsa = require('../').ecdsa; +var assert = require('assert') +var sec = require('../src/jsbn/sec') +var ecdsa = require('../').ecdsa -var ecparams = sec('secp256k1'); +var ecparams = sec('secp256k1') describe('ecdsa', function() { - it('handles point multiplication', function() { - var G = ecparams.getG(); - var n = ecparams.getN(); + it('handles point multiplication', function() { + var G = ecparams.getG() + var n = ecparams.getN() - assert.ok(G.multiply(n).isInfinity(), "Gn is infinite"); + assert.ok(G.multiply(n).isInfinity(), "Gn is infinite") - var k = ecdsa.getBigRandom(n); - var P = G.multiply(k); - assert.ok(!P.isInfinity(), "kG is not infinite"); - assert.ok(P.isOnCurve(), "kG on curve"); - assert.ok(P.multiply(n).isInfinity(), "kGn is infinite"); + var k = ecdsa.getBigRandom(n) + var P = G.multiply(k) + assert.ok(!P.isInfinity(), "kG is not infinite") + assert.ok(P.isOnCurve(), "kG on curve") + assert.ok(P.multiply(n).isInfinity(), "kGn is infinite") - assert.ok(P.validate(), "kG validates as a public key"); - }) + assert.ok(P.validate(), "kG validates as a public key") + }) }) diff --git a/test/eckey.js b/test/eckey.js index 92f66ae..94d03c7 100644 --- a/test/eckey.js +++ b/test/eckey.js @@ -1,190 +1,187 @@ -/* global describe, it */ -var assert = require('assert'); - -var ECKey = require('../src/eckey.js').ECKey; -var ECPubKey = require('../src/eckey.js').ECPubKey; - -var convert = require('../src/convert.js'); -var bytesToHex = convert.bytesToHex; -var hexToBytes = convert.hexToBytes; -var Address = require('../src/address'); +var assert = require('assert') +var ECKey = require('../src/eckey.js').ECKey +var ECPubKey = require('../src/eckey.js').ECPubKey +var convert = require('../src/convert.js') +var bytesToHex = convert.bytesToHex +var hexToBytes = convert.hexToBytes +var Address = require('../src/address') var Network = require('../src/network') var testnet = Network.testnet.addressVersion describe('ECKey', function() { - describe('constructor', function() { - it('parses hex', function() { - var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; - var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' + - '22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'; - var key = new ECKey(priv); - - assert.equal(key.getPub().toHex(), pub); - assert.equal(key.compressed, false); - }) - - it('parses base64', function() { - var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU='; - var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' + - '8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b'; - var key = new ECKey(priv); - - assert.equal(key.getPub().toHex(), pub); - assert.equal(key.compressed, false); - }) - - it('parses WIF', function() { - var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'; - var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' + - 'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'; - var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'; - var key = new ECKey(priv); - - assert.equal(key.compressed, false); - assert.equal(key.getPub().toHex(), pub); - assert.equal(key.getAddress().toString(), addr); - }) - - it('parses compressed WIF', function() { - var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'; - var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' - var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'; - var key = new ECKey(priv); - - assert.equal(key.compressed, true); - assert.equal(key.getPub().toHex(), pub); - assert.equal(key.getAddress().toString(), addr); - }) - - it('alternative constructor syntax', function() { - var priv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458'; - var pub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199' + - '283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566'; - var key = ECKey(priv, false); - - assert.equal(key.getPub().toHex(), pub); - assert.equal(key.compressed, false); - assert.equal(key.toHex(), priv); - }) + describe('constructor', function() { + it('parses hex', function() { + var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725' + var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' + + '22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6' + var key = new ECKey(priv) + + assert.equal(key.getPub().toHex(), pub) + assert.equal(key.compressed, false) + }) + + it('parses base64', function() { + var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU=' + var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' + + '8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b' + var key = new ECKey(priv) + + assert.equal(key.getPub().toHex(), pub) + assert.equal(key.compressed, false) }) - describe('toAddress', function() { - var privkeys = [ - 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458', - '1111111111111111111111111111111111111111111111111111111111111111', - '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725' - ]; - - // compressed pubkeys - var cpubkeys = [ - '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992', - '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa', - '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' - ]; - - var pubkeys = cpubkeys.map(function(x) { - return ECPubKey(x).toHex(false) - }); - - it('mainnet', function() { - var addresses = [ - '19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui', - '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', - '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' - ]; - var compressedAddresses = [ - '1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q', - '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9', - '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs', - ]; - - for (var i = 0; i < addresses.length; ++i) { - var priv = new ECKey(privkeys[i], false); - var pub = new ECPubKey(pubkeys[i], false); - var cpub = new ECPubKey(cpubkeys[i], true); - - var addr = addresses[i]; - var caddr = compressedAddresses[i]; - - assert.equal(priv.getAddress().toString(), addr); - assert.equal(pub.getAddress().toString(), addr); - assert.equal(cpub.getAddress().toString(), caddr); - } - }) - - it('testnet', function() { - var addresses = [ - '19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui', - '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', - '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' - ]; - var compressedAddresses = [ - '1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q', - '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9', - '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs', - ]; - - for (var i = 0; i < addresses.length; ++i) { - var priv = new ECKey(privkeys[i], false); - var pub = new ECPubKey(pubkeys[i], false); - var cpub = new ECPubKey(cpubkeys[i], true); - - var addr = addresses[i]; - var caddr = compressedAddresses[i]; - - assert.equal(priv.getAddress().toString(), addr); - assert.equal(pub.getAddress().toString(), addr); - assert.equal(cpub.getAddress().toString(), caddr); - } - }) - }); - - describe('signing', function() { - var hpriv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458' - var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992' - var message = 'Vires in numeris' - - it('should verify against the private key', function() { - var priv = new ECKey(hpriv) - var signature = priv.sign(message) - - assert(priv.verify(message, signature)) - }) - - it('should verify against the public key', function() { - var priv = new ECKey(hpriv) - var pub = new ECPubKey(hcpub, true) - var signature = priv.sign(message) - - assert(pub.verify(message, signature)) - }) - - it('should not verify against the wrong private key', function() { - var priv1 = new ECKey(hpriv) - var priv2 = new ECKey('1111111111111111111111111111111111111111111111111111111111111111') - - var signature = priv1.sign(message) - - assert(!priv2.verify(message, signature)) - }) + it('parses WIF', function() { + var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh' + var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' + + 'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1' + var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a' + var key = new ECKey(priv) + + assert.equal(key.compressed, false) + assert.equal(key.getPub().toHex(), pub) + assert.equal(key.getAddress().toString(), addr) }) - describe('output of ECPubKey', function() { - var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992' - var hpub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566' + it('parses compressed WIF', function() { + var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp' + var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' + var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9' + var key = new ECKey(priv) + + assert.equal(key.compressed, true) + assert.equal(key.getPub().toHex(), pub) + assert.equal(key.getAddress().toString(), addr) + }) - it('using toHex should support compression', function() { - var pub = new ECPubKey(hpub) + it('alternative constructor syntax', function() { + var priv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458' + var pub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199' + + '283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566' + var key = ECKey(priv, false) + + assert.equal(key.getPub().toHex(), pub) + assert.equal(key.compressed, false) + assert.equal(key.toHex(), priv) + }) + }) + + describe('toAddress', function() { + var privkeys = [ + 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458', + '1111111111111111111111111111111111111111111111111111111111111111', + '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725' + ] + + // compressed pubkeys + var cpubkeys = [ + '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992', + '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa', + '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' + ] + + var pubkeys = cpubkeys.map(function(x) { + return ECPubKey(x).toHex(false) + }) + + it('mainnet', function() { + var addresses = [ + '19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui', + '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', + '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' + ] + var compressedAddresses = [ + '1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q', + '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9', + '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs', + ] + + for (var i = 0; i < addresses.length; ++i) { + var priv = new ECKey(privkeys[i], false) + var pub = new ECPubKey(pubkeys[i], false) + var cpub = new ECPubKey(cpubkeys[i], true) + + var addr = addresses[i] + var caddr = compressedAddresses[i] + + assert.equal(priv.getAddress().toString(), addr) + assert.equal(pub.getAddress().toString(), addr) + assert.equal(cpub.getAddress().toString(), caddr) + } + }) + + it('testnet', function() { + var addresses = [ + '19SgmoUj4xowEjwtXvNAtYTAgbvR9iBCui', + '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', + '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' + ] + var compressedAddresses = [ + '1AA4sjKW2aUmbtN3MtegdvhYtDBbDEke1q', + '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9', + '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs', + ] + + for (var i = 0; i < addresses.length; ++i) { + var priv = new ECKey(privkeys[i], false) + var pub = new ECPubKey(pubkeys[i], false) + var cpub = new ECPubKey(cpubkeys[i], true) + + var addr = addresses[i] + var caddr = compressedAddresses[i] + + assert.equal(priv.getAddress().toString(), addr) + assert.equal(pub.getAddress().toString(), addr) + assert.equal(cpub.getAddress().toString(), caddr) + } + }) + }) - assert.equal(pub.toHex(true), hcpub) - assert.equal(pub.toHex(false), hpub) - }) + describe('signing', function() { + var hpriv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458' + var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992' + var message = 'Vires in numeris' + + it('should verify against the private key', function() { + var priv = new ECKey(hpriv) + var signature = priv.sign(message) + + assert(priv.verify(message, signature)) + }) + + it('should verify against the public key', function() { + var priv = new ECKey(hpriv) + var pub = new ECPubKey(hcpub, true) + var signature = priv.sign(message) + + assert(pub.verify(message, signature)) + }) + + it('should not verify against the wrong private key', function() { + var priv1 = new ECKey(hpriv) + var priv2 = new ECKey('1111111111111111111111111111111111111111111111111111111111111111') + + var signature = priv1.sign(message) + + assert(!priv2.verify(message, signature)) + }) + }) + + describe('output of ECPubKey', function() { + var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992' + var hpub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566' + + it('using toHex should support compression', function() { + var pub = new ECPubKey(hpub) + + assert.equal(pub.toHex(true), hcpub) + assert.equal(pub.toHex(false), hpub) + }) - it('using toBytes should support compression', function() { - var pub = new ECPubKey(hpub) + it('using toBytes should support compression', function() { + var pub = new ECPubKey(hpub) - assert.equal(bytesToHex(pub.toBytes(true)), hcpub) - assert.equal(bytesToHex(pub.toBytes(false)), hpub) - }) + assert.equal(bytesToHex(pub.toBytes(true)), hcpub) + assert.equal(bytesToHex(pub.toBytes(false)), hpub) }) + }) }) diff --git a/test/hdwallet.js b/test/hdwallet.js index bc18a78..86f4b1e 100644 --- a/test/hdwallet.js +++ b/test/hdwallet.js @@ -1,4 +1,3 @@ -/* global describe, it */ var HDWallet = require('../src/hdwallet.js') var assert = require('assert') var convert = require('../src/convert.js') @@ -9,251 +8,251 @@ var testnet = Network.testnet.addressVersion var b2h = convert.bytesToHex describe('HDWallet', function() { - describe('toBase58', function() { - it('reproduces input', function() { - var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5' - , output = HDWallet.fromBase58(input).toBase58(false) - assert.equal(output, input) + describe('toBase58', function() { + it('reproduces input', function() { + var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5' + var output = HDWallet.fromBase58(input).toBase58(false) + assert.equal(output, input) - input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334' - output = HDWallet.fromBase58(input).toBase58(true) - assert.equal(output, input) - }) + input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334' + output = HDWallet.fromBase58(input).toBase58(true) + assert.equal(output, input) + }) - it('fails with priv=true when theres no private key', function() { - var hd = HDWallet.fromBase58('xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') - try { - hd.toBase58(true) - } catch(e) { - assert(e.message.match(/private key/i)) - return - } - assert.fail() - }) + it('fails with priv=true when theres no private key', function() { + var hd = HDWallet.fromBase58('xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') + try { + hd.toBase58(true) + } catch(e) { + assert(e.message.match(/private key/i)) + return + } + assert.fail() }) + }) - describe('constructor & seed deserialization', function() { - var expectedPrivateKey = '0fd71c652e847ba7ea7956e3cf3fc0a0985871846b1b2c23b9c6a29a38cee86001'; - var seed = [ - 99, 114, 97, 122, 121, 32, 104, 111, 114, 115, 101, 32, 98, - 97, 116, 116, 101, 114, 121, 32, 115, 116, 97, 112, 108, 101 - ]; + describe('constructor & seed deserialization', function() { + var expectedPrivateKey = '0fd71c652e847ba7ea7956e3cf3fc0a0985871846b1b2c23b9c6a29a38cee86001' + var seed = [ + 99, 114, 97, 122, 121, 32, 104, 111, 114, 115, 101, 32, 98, + 97, 116, 116, 101, 114, 121, 32, 115, 116, 97, 112, 108, 101 + ] - it('creates from binary seed', function() { - var hd = new HDWallet(seed) + it('creates from binary seed', function() { + var hd = new HDWallet(seed) - assert.equal(hd.priv.toHex(), expectedPrivateKey) - assert(hd.pub) - }) + assert.equal(hd.priv.toHex(), expectedPrivateKey) + assert(hd.pub) + }) - describe('fromSeedHex', function() { - it('creates from hex seed', function() { - var hd = HDWallet.fromSeedHex(b2h(seed)) + describe('fromSeedHex', function() { + it('creates from hex seed', function() { + var hd = HDWallet.fromSeedHex(b2h(seed)) - assert.equal(hd.priv.toHex(), expectedPrivateKey) - assert(hd.pub) - }) - }) + assert.equal(hd.priv.toHex(), expectedPrivateKey) + assert(hd.pub) + }) + }) - describe('fromSeedString', function() { - it('creates from string seed', function() { - var hd = HDWallet.fromSeedString(convert.bytesToString(seed)) + describe('fromSeedString', function() { + it('creates from string seed', function() { + var hd = HDWallet.fromSeedString(convert.bytesToString(seed)) - assert.equal(hd.priv.toHex(), expectedPrivateKey) - assert(hd.pub) - }) - }) + assert.equal(hd.priv.toHex(), expectedPrivateKey) + assert(hd.pub) + }) }) + }) - describe('Test vectors', function() { - it('Test vector 1', function() { - var hd = HDWallet.fromSeedHex('000102030405060708090a0b0c0d0e0f') + describe('Test vectors', function() { + it('Test vector 1', function() { + var hd = HDWallet.fromSeedHex('000102030405060708090a0b0c0d0e0f') - // m - assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811') - assert.equal(b2h(hd.getFingerprint()), '3442193e') - assert.equal(hd.getAddress().toString(), '15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma') - assert.equal(hd.priv.toHex(), 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b3501') - assert.equal(hd.priv.toWif(), 'L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW') - assert.equal(hd.pub.toHex(), '0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') - assert.equal(b2h(hd.chaincode), '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508') - assert.equal(hd.toHex(false), '0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') - assert.equal(hd.toHex(true), '0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35') - assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8') - assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi') + // m + assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811') + assert.equal(b2h(hd.getFingerprint()), '3442193e') + assert.equal(hd.getAddress().toString(), '15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma') + assert.equal(hd.priv.toHex(), 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b3501') + assert.equal(hd.priv.toWif(), 'L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW') + assert.equal(hd.pub.toHex(), '0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') + assert.equal(b2h(hd.chaincode), '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508') + assert.equal(hd.toHex(false), '0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2') + assert.equal(hd.toHex(true), '0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35') + assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8') + assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi') - // m/0' - hd = hd.derivePrivate(0) - assert.equal(b2h(hd.getIdentifier()), '5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7') - assert.equal(b2h(hd.getFingerprint()), '5c1bd648') - assert.equal(hd.getAddress().toString(), '19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh') - assert.equal(hd.priv.toHex().slice(0, 64), 'edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') - assert.equal(hd.priv.toWif(), 'L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT') - assert.equal(hd.pub.toHex(), '035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') - assert.equal(b2h(hd.chaincode), '47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141') - assert.equal(hd.toHex(false), '0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') - assert.equal(hd.toHex(true), '0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') - assert.equal(hd.toBase58(false), 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw') - assert.equal(hd.toBase58(true), 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7') + // m/0' + hd = hd.derivePrivate(0) + assert.equal(b2h(hd.getIdentifier()), '5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7') + assert.equal(b2h(hd.getFingerprint()), '5c1bd648') + assert.equal(hd.getAddress().toString(), '19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh') + assert.equal(hd.priv.toHex().slice(0, 64), 'edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') + assert.equal(hd.priv.toWif(), 'L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT') + assert.equal(hd.pub.toHex(), '035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') + assert.equal(b2h(hd.chaincode), '47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141') + assert.equal(hd.toHex(false), '0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56') + assert.equal(hd.toHex(true), '0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea') + assert.equal(hd.toBase58(false), 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw') + assert.equal(hd.toBase58(true), 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7') - // m/0'/1 - hd = hd.derive(1) - assert.equal(b2h(hd.getIdentifier()), 'bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe') - assert.equal(b2h(hd.getFingerprint()), 'bef5a2f9') - assert.equal(hd.getAddress().toString(), '1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj') - assert.equal(hd.priv.toHex().slice(0, 64), '3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') - assert.equal(hd.priv.toWif(), 'KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM') - assert.equal(hd.pub.toHex(), '03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') - assert.equal(b2h(hd.chaincode), '2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19') - assert.equal(hd.toHex(false), '0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') - assert.equal(hd.toHex(true), '0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') - assert.equal(hd.toBase58(false), 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ') - assert.equal(hd.toBase58(true), 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs') + // m/0'/1 + hd = hd.derive(1) + assert.equal(b2h(hd.getIdentifier()), 'bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe') + assert.equal(b2h(hd.getFingerprint()), 'bef5a2f9') + assert.equal(hd.getAddress().toString(), '1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj') + assert.equal(hd.priv.toHex().slice(0, 64), '3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') + assert.equal(hd.priv.toWif(), 'KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM') + assert.equal(hd.pub.toHex(), '03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') + assert.equal(b2h(hd.chaincode), '2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19') + assert.equal(hd.toHex(false), '0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c') + assert.equal(hd.toHex(true), '0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368') + assert.equal(hd.toBase58(false), 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ') + assert.equal(hd.toBase58(true), 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs') - // m/0'/1/2' - hd = hd.derivePrivate(2) - assert.equal(b2h(hd.getIdentifier()), 'ee7ab90cde56a8c0e2bb086ac49748b8db9dce72') - assert.equal(b2h(hd.getFingerprint()), 'ee7ab90c') - assert.equal(hd.getAddress().toString(), '1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x') - assert.equal(hd.priv.toHex().slice(0, 64), 'cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') - assert.equal(hd.priv.toWif(), 'L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU') - assert.equal(hd.pub.toHex(), '0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') - assert.equal(b2h(hd.chaincode), '04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f') - assert.equal(hd.toHex(false), '0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') - assert.equal(hd.toHex(true), '0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') - assert.equal(hd.toBase58(false), 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5') - assert.equal(hd.toBase58(true), 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM') + // m/0'/1/2' + hd = hd.derivePrivate(2) + assert.equal(b2h(hd.getIdentifier()), 'ee7ab90cde56a8c0e2bb086ac49748b8db9dce72') + assert.equal(b2h(hd.getFingerprint()), 'ee7ab90c') + assert.equal(hd.getAddress().toString(), '1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x') + assert.equal(hd.priv.toHex().slice(0, 64), 'cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') + assert.equal(hd.priv.toWif(), 'L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU') + assert.equal(hd.pub.toHex(), '0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') + assert.equal(b2h(hd.chaincode), '04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f') + assert.equal(hd.toHex(false), '0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2') + assert.equal(hd.toHex(true), '0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca') + assert.equal(hd.toBase58(false), 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5') + assert.equal(hd.toBase58(true), 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM') - // m/0'/1/2'/2 - hd = hd.derive(2) - assert.equal(b2h(hd.getIdentifier()), 'd880d7d893848509a62d8fb74e32148dac68412f') - assert.equal(b2h(hd.getFingerprint()), 'd880d7d8') - assert.equal(hd.getAddress().toString(), '1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt') - assert.equal(hd.priv.toHex().slice(0, 64), '0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') - assert.equal(hd.priv.toWif(), 'KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR') - assert.equal(hd.pub.toHex(), '02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') - assert.equal(b2h(hd.chaincode), 'cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd') - assert.equal(hd.toHex(false), '0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') - assert.equal(hd.toHex(true), '0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') - assert.equal(hd.toBase58(false), 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV') - assert.equal(hd.toBase58(true), 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334') + // m/0'/1/2'/2 + hd = hd.derive(2) + assert.equal(b2h(hd.getIdentifier()), 'd880d7d893848509a62d8fb74e32148dac68412f') + assert.equal(b2h(hd.getFingerprint()), 'd880d7d8') + assert.equal(hd.getAddress().toString(), '1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt') + assert.equal(hd.priv.toHex().slice(0, 64), '0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') + assert.equal(hd.priv.toWif(), 'KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR') + assert.equal(hd.pub.toHex(), '02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') + assert.equal(b2h(hd.chaincode), 'cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd') + assert.equal(hd.toHex(false), '0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29') + assert.equal(hd.toHex(true), '0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4') + assert.equal(hd.toBase58(false), 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV') + assert.equal(hd.toBase58(true), 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334') - // m/0'/1/2'/2/1000000000 - hd = hd.derive(1000000000) - assert.equal(b2h(hd.getIdentifier()), 'd69aa102255fed74378278c7812701ea641fdf32') - assert.equal(b2h(hd.getFingerprint()), 'd69aa102') - assert.equal(hd.getAddress().toString(), '1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam') - assert.equal(hd.priv.toHex().slice(0, 64), '471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') - assert.equal(hd.priv.toWif(), 'Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs') - assert.equal(hd.pub.toHex(), '022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') - assert.equal(b2h(hd.chaincode), 'c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e') - assert.equal(hd.toHex(false), '0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') - assert.equal(hd.toHex(true), '0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') - assert.equal(hd.toBase58(false), 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy') - assert.equal(hd.toBase58(true), 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76') - }) + // m/0'/1/2'/2/1000000000 + hd = hd.derive(1000000000) + assert.equal(b2h(hd.getIdentifier()), 'd69aa102255fed74378278c7812701ea641fdf32') + assert.equal(b2h(hd.getFingerprint()), 'd69aa102') + assert.equal(hd.getAddress().toString(), '1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam') + assert.equal(hd.priv.toHex().slice(0, 64), '471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') + assert.equal(hd.priv.toWif(), 'Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs') + assert.equal(hd.pub.toHex(), '022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') + assert.equal(b2h(hd.chaincode), 'c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e') + assert.equal(hd.toHex(false), '0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011') + assert.equal(hd.toHex(true), '0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8') + assert.equal(hd.toBase58(false), 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy') + assert.equal(hd.toBase58(true), 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76') + }) - it('Test vector 2', function() { - var hd = HDWallet.fromSeedHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542') + it('Test vector 2', function() { + var hd = HDWallet.fromSeedHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542') - // m - assert.equal(b2h(hd.getIdentifier()), 'bd16bee53961a47d6ad888e29545434a89bdfe95') - assert.equal(b2h(hd.getFingerprint()), 'bd16bee5') - assert.equal(hd.getAddress().toString(), '1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg') - assert.equal(hd.priv.toHex().slice(0, 64), '4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') - assert.equal(hd.priv.toWif(), 'KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy') - assert.equal(hd.pub.toHex(), '03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') - assert.equal(b2h(hd.chaincode), '60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689') - assert.equal(hd.toHex(false), '0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') - assert.equal(hd.toHex(true), '0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') - assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB') - assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U') + // m + assert.equal(b2h(hd.getIdentifier()), 'bd16bee53961a47d6ad888e29545434a89bdfe95') + assert.equal(b2h(hd.getFingerprint()), 'bd16bee5') + assert.equal(hd.getAddress().toString(), '1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg') + assert.equal(hd.priv.toHex().slice(0, 64), '4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') + assert.equal(hd.priv.toWif(), 'KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy') + assert.equal(hd.pub.toHex(), '03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') + assert.equal(b2h(hd.chaincode), '60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689') + assert.equal(hd.toHex(false), '0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7') + assert.equal(hd.toHex(true), '0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e') + assert.equal(hd.toBase58(false), 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB') + assert.equal(hd.toBase58(true), 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U') - // m/0 - hd = hd.derive(0) - assert.equal(b2h(hd.getIdentifier()), '5a61ff8eb7aaca3010db97ebda76121610b78096') - assert.equal(b2h(hd.getFingerprint()), '5a61ff8e') - assert.equal(hd.getAddress().toString(), '19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ') - assert.equal(hd.priv.toHex().slice(0, 64), 'abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') - assert.equal(hd.priv.toWif(), 'L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj') - assert.equal(hd.pub.toHex(), '02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') - assert.equal(b2h(hd.chaincode), 'f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c') - assert.equal(hd.toHex(false), '0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') - assert.equal(hd.toHex(true), '0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') - assert.equal(hd.toBase58(false), 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH') - assert.equal(hd.toBase58(true), 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt') + // m/0 + hd = hd.derive(0) + assert.equal(b2h(hd.getIdentifier()), '5a61ff8eb7aaca3010db97ebda76121610b78096') + assert.equal(b2h(hd.getFingerprint()), '5a61ff8e') + assert.equal(hd.getAddress().toString(), '19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ') + assert.equal(hd.priv.toHex().slice(0, 64), 'abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') + assert.equal(hd.priv.toWif(), 'L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj') + assert.equal(hd.pub.toHex(), '02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') + assert.equal(b2h(hd.chaincode), 'f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c') + assert.equal(hd.toHex(false), '0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea') + assert.equal(hd.toHex(true), '0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e') + assert.equal(hd.toBase58(false), 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH') + assert.equal(hd.toBase58(true), 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt') - // m/0/2147483647' - hd = hd.derivePrivate(2147483647) - assert.equal(b2h(hd.getIdentifier()), 'd8ab493736da02f11ed682f88339e720fb0379d1') - assert.equal(b2h(hd.getFingerprint()), 'd8ab4937') - assert.equal(hd.getAddress().toString(), '1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk') - assert.equal(hd.priv.toHex().slice(0, 64), '877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') - assert.equal(hd.priv.toWif(), 'L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr') - assert.equal(hd.pub.toHex(), '03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') - assert.equal(b2h(hd.chaincode), 'be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9') - assert.equal(hd.toHex(false), '0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') - assert.equal(hd.toHex(true), '0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') - assert.equal(hd.toBase58(false), 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a') - assert.equal(hd.toBase58(true), 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9') + // m/0/2147483647' + hd = hd.derivePrivate(2147483647) + assert.equal(b2h(hd.getIdentifier()), 'd8ab493736da02f11ed682f88339e720fb0379d1') + assert.equal(b2h(hd.getFingerprint()), 'd8ab4937') + assert.equal(hd.getAddress().toString(), '1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk') + assert.equal(hd.priv.toHex().slice(0, 64), '877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') + assert.equal(hd.priv.toWif(), 'L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr') + assert.equal(hd.pub.toHex(), '03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') + assert.equal(b2h(hd.chaincode), 'be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9') + assert.equal(hd.toHex(false), '0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b') + assert.equal(hd.toHex(true), '0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93') + assert.equal(hd.toBase58(false), 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a') + assert.equal(hd.toBase58(true), 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9') - // m/0/2147483647'/1 - hd = hd.derive(1) - assert.equal(b2h(hd.getIdentifier()), '78412e3a2296a40de124307b6485bd19833e2e34') - assert.equal(b2h(hd.getFingerprint()), '78412e3a') - assert.equal(hd.getAddress().toString(), '1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW') - assert.equal(hd.priv.toHex().slice(0, 64), '704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') - assert.equal(hd.priv.toWif(), 'KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2') - assert.equal(hd.pub.toHex(), '03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') - assert.equal(b2h(hd.chaincode), 'f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb') - assert.equal(hd.toHex(false), '0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') - assert.equal(hd.toHex(true), '0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') - assert.equal(hd.toBase58(false), 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') - assert.equal(hd.toBase58(true), 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef') + // m/0/2147483647'/1 + hd = hd.derive(1) + assert.equal(b2h(hd.getIdentifier()), '78412e3a2296a40de124307b6485bd19833e2e34') + assert.equal(b2h(hd.getFingerprint()), '78412e3a') + assert.equal(hd.getAddress().toString(), '1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW') + assert.equal(hd.priv.toHex().slice(0, 64), '704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') + assert.equal(hd.priv.toWif(), 'KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2') + assert.equal(hd.pub.toHex(), '03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') + assert.equal(b2h(hd.chaincode), 'f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb') + assert.equal(hd.toHex(false), '0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9') + assert.equal(hd.toHex(true), '0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7') + assert.equal(hd.toBase58(false), 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon') + assert.equal(hd.toBase58(true), 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef') - // m/0/2147483647'/1/2147483646' - hd = hd.derivePrivate(2147483646) - assert.equal(b2h(hd.getIdentifier()), '31a507b815593dfc51ffc7245ae7e5aee304246e') - assert.equal(b2h(hd.getFingerprint()), '31a507b8') - assert.equal(hd.getAddress().toString(), '15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R') - assert.equal(hd.priv.toHex().slice(0, 64), 'f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') - assert.equal(hd.priv.toWif(), 'L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF') - assert.equal(hd.pub.toHex(), '02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') - assert.equal(b2h(hd.chaincode), '637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29') - assert.equal(hd.toHex(false), '0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') - assert.equal(hd.toHex(true), '0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') - assert.equal(hd.toBase58(false), 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL') - assert.equal(hd.toBase58(true), 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc') + // m/0/2147483647'/1/2147483646' + hd = hd.derivePrivate(2147483646) + assert.equal(b2h(hd.getIdentifier()), '31a507b815593dfc51ffc7245ae7e5aee304246e') + assert.equal(b2h(hd.getFingerprint()), '31a507b8') + assert.equal(hd.getAddress().toString(), '15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R') + assert.equal(hd.priv.toHex().slice(0, 64), 'f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') + assert.equal(hd.priv.toWif(), 'L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF') + assert.equal(hd.pub.toHex(), '02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') + assert.equal(b2h(hd.chaincode), '637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29') + assert.equal(hd.toHex(false), '0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0') + assert.equal(hd.toHex(true), '0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d') + assert.equal(hd.toBase58(false), 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL') + assert.equal(hd.toBase58(true), 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc') - // Chain m/0/2147483647'/1/2147483646'/2 - hd = hd.derive(2) - assert.equal(b2h(hd.getIdentifier()), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') - assert.equal(b2h(hd.getFingerprint()), '26132fdb') - assert.equal(hd.getAddress().toString(), '14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt') - assert.equal(hd.priv.toHex().slice(0, 64), 'bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') - assert.equal(hd.priv.toWif(), 'L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK') - assert.equal(hd.pub.toHex(), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(b2h(hd.chaincode), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') - assert.equal(hd.toHex(false), '0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') - assert.equal(hd.toHex(true), '0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') - assert.equal(hd.toBase58(false), 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt') - assert.equal(hd.toBase58(true), 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j') - }) + // Chain m/0/2147483647'/1/2147483646'/2 + hd = hd.derive(2) + assert.equal(b2h(hd.getIdentifier()), '26132fdbe7bf89cbc64cf8dafa3f9f88b8666220') + assert.equal(b2h(hd.getFingerprint()), '26132fdb') + assert.equal(hd.getAddress().toString(), '14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt') + assert.equal(hd.priv.toHex().slice(0, 64), 'bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') + assert.equal(hd.priv.toWif(), 'L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK') + assert.equal(hd.pub.toHex(), '024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') + assert.equal(b2h(hd.chaincode), '9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271') + assert.equal(hd.toHex(false), '0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c') + assert.equal(hd.toHex(true), '0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23') + assert.equal(hd.toBase58(false), 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt') + assert.equal(hd.toBase58(true), 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j') }) + }) - describe('network types', function() { - it('ensures that a mainnet Wallet generates mainnet addresses', function() { - var wallet = new HDWallet('foobar', 'mainnet') - assert.equal(wallet.getAddress().toString(), '1JNymexJHEr5u1BndiChMStFkCgPm4EQ6o'); - }) + describe('network types', function() { + it('ensures that a mainnet Wallet generates mainnet addresses', function() { + var wallet = new HDWallet('foobar', 'mainnet') + assert.equal(wallet.getAddress().toString(), '1JNymexJHEr5u1BndiChMStFkCgPm4EQ6o') + }) - it('ensures that a testnet Wallet generates testnet addresses', function() { - var wallet = new HDWallet('foobar', 'testnet') - assert.equal(wallet.getAddress().toString(), 'mxtw4i3H6GHLg7fQMHB5BN6acCH6kQ7aoY'); - }) + it('ensures that a testnet Wallet generates testnet addresses', function() { + var wallet = new HDWallet('foobar', 'testnet') + assert.equal(wallet.getAddress().toString(), 'mxtw4i3H6GHLg7fQMHB5BN6acCH6kQ7aoY') + }) - it('throws an excption when unknown network type is passed in', function() { - assert.throws(function() { new HDWallet("foobar", "doge") }) - }) + it('throws an excption when unknown network type is passed in', function() { + assert.throws(function() { new HDWallet("foobar", "doge") }) }) + }) }) diff --git a/test/jsbn.js b/test/jsbn.js index 686dab0..19429fa 100644 --- a/test/jsbn.js +++ b/test/jsbn.js @@ -1,37 +1,36 @@ -/* global describe, it */ -var assert = require('assert'); +var assert = require('assert') var BigInteger = require('../src/jsbn/jsbn.js') -var bytesToHex = require('../src/convert.js').bytesToHex; -var secureRandom = require('secure-random'); +var bytesToHex = require('../src/convert.js').bytesToHex +var secureRandom = require('secure-random') describe('BigInteger', function() { - describe('toByteArraySigned', function() { - it('handles examples', function() { - function hex(num) { - var bytes = BigInteger.valueOf(num).toByteArraySigned(); - var h = bytesToHex(bytes); - return '0x' + h; - } + describe('toByteArraySigned', function() { + it('handles examples', function() { + function hex(num) { + var bytes = BigInteger.valueOf(num).toByteArraySigned() + var h = bytesToHex(bytes) + return '0x' + h + } - assert.equal(hex( 0), '0x'); - assert.equal(hex( 1), '0x01'); - assert.equal(hex(-1), '0x81'); - assert.equal(hex( 127), '0x7f'); - assert.equal(hex(-127), '0xff'); - assert.equal(hex( 255), '0x00ff'); - assert.equal(hex(-255), '0x80ff'); - assert.equal(hex( 16300), '0x3fac'); - assert.equal(hex(-16300), '0xbfac'); - assert.equal(hex( 62300), '0x00f35c'); - assert.equal(hex(-62300), '0x80f35c'); - }) + assert.equal(hex( 0), '0x') + assert.equal(hex( 1), '0x01') + assert.equal(hex(-1), '0x81') + assert.equal(hex( 127), '0x7f') + assert.equal(hex(-127), '0xff') + assert.equal(hex( 255), '0x00ff') + assert.equal(hex(-255), '0x80ff') + assert.equal(hex( 16300), '0x3fac') + assert.equal(hex(-16300), '0xbfac') + assert.equal(hex( 62300), '0x00f35c') + assert.equal(hex(-62300), '0x80f35c') }) + }) - describe('with RNG passed into constructor as the 2nd argument', function(){ - it('returns a BigInteger with the limit of the specified length', function(){ - var bitLength = 256 - var i = new BigInteger(bitLength, secureRandom) - assert(i.bitLength() <= 256) - }) + describe('with RNG passed into constructor as the 2nd argument', function(){ + it('returns a BigInteger with the limit of the specified length', function(){ + var bitLength = 256 + var i = new BigInteger(bitLength, secureRandom) + assert(i.bitLength() <= 256) }) + }) }) diff --git a/test/misc.js b/test/misc.js index a3dca30..e02c924 100644 --- a/test/misc.js +++ b/test/misc.js @@ -1,65 +1,65 @@ -var assert = require('assert'); -var bitcoinjs = require('../'); -var sec = require('../src/jsbn/sec'); -var BigInteger = require('../src/jsbn/jsbn.js'); -var SHA256 = require('crypto-js/sha256'); -var rng = require('secure-random'); -var ecparams = sec('secp256k1'); -var ECPointFp = bitcoinjs.ECPointFp; -var convert = require('../src/convert'); +var assert = require('assert') +var bitcoinjs = require('../') +var sec = require('../src/jsbn/sec') +var BigInteger = require('../src/jsbn/jsbn.js') +var SHA256 = require('crypto-js/sha256') +var rng = require('secure-random') +var ecparams = sec('secp256k1') +var ECPointFp = bitcoinjs.ECPointFp +var convert = require('../src/convert') function sha256FromBytesToBytes(message){ return convert.wordArrayToBytes(SHA256(convert.bytesToWordArray(message))) } it('Keys & Key Management', function () { - var p1 = bitcoinjs.Key().getPub().toBytes() - assert.equal(p1.length, 65) + var p1 = bitcoinjs.Key().getPub().toBytes() + assert.equal(p1.length, 65) - var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1); - assert.ok(p1_q); - assert.ok(p1_q.validate()); + var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1) + assert.ok(p1_q) + assert.ok(p1_q.validate()) - var p2 = bitcoinjs.convert.hexToBytes( - '0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' + - 'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c'); + var p2 = bitcoinjs.convert.hexToBytes( + '0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' + + 'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c') - var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2); - assert.ok(p2_q); - assert.ok(p2_q.validate()); -}); + var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2) + assert.ok(p2_q) + assert.ok(p2_q.validate()) +}) it('Signing and Verifying', function () { - var s1 = bitcoinjs.Key(); - var sig_a = s1.sign(BigInteger.ZERO); - assert.ok(sig_a, 'Sign null'); + var s1 = bitcoinjs.Key() + var sig_a = s1.sign(BigInteger.ZERO) + assert.ok(sig_a, 'Sign null') - assert.ok(s1.verify(BigInteger.ZERO, sig_a)); + assert.ok(s1.verify(BigInteger.ZERO, sig_a)) - var message = new BigInteger(1024, rng).toByteArrayUnsigned(); - var hash = sha256FromBytesToBytes(message); - var sig_b = s1.sign(hash); - assert.ok(sig_b, 'Sign random string'); - assert.ok(s1.verify(hash, sig_b)); + var message = new BigInteger(1024, rng).toByteArrayUnsigned() + var hash = sha256FromBytesToBytes(message) + var sig_b = s1.sign(hash) + assert.ok(sig_b, 'Sign random string') + assert.ok(s1.verify(hash, sig_b)) - var message2 = bitcoinjs.convert.hexToBytes( - '12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' + - 'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' + - '1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' + - '8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' + - '3d82507b932b84e4'); + var message2 = bitcoinjs.convert.hexToBytes( + '12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' + + 'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' + + '1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' + + '8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' + + '3d82507b932b84e4') - var hash2 = sha256FromBytesToBytes(message2); + var hash2 = sha256FromBytesToBytes(message2) - var sig_c = bitcoinjs.convert.hexToBytes( - '3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' + - '79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' + - 'f273ab082801a95506c4'); + var sig_c = bitcoinjs.convert.hexToBytes( + '3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' + + '79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' + + 'f273ab082801a95506c4') - var s2 = bitcoinjs.convert.hexToBytes( - '045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' + - 'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' + - '609a978528'); + var s2 = bitcoinjs.convert.hexToBytes( + '045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' + + 'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' + + '609a978528') - assert.ok(bitcoinjs.ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature'); -}); + assert.ok(bitcoinjs.ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature') +}) diff --git a/test/script.js b/test/script.js index a81985a..b6ce5e9 100644 --- a/test/script.js +++ b/test/script.js @@ -2,13 +2,11 @@ var Script = require('../src/script.js') var assert = require('assert') var Address = require('../src/address.js') var Network = require('../src/network.js') - var Util = require('../src/util.js') -var sha256ripe160 = Util.sha256ripe160; - +var sha256ripe160 = Util.sha256ripe160 var Convert = require('../src/convert.js') -var bytesToHex = Convert.bytesToHex; -var hexToBytes = Convert.hexToBytes; +var bytesToHex = Convert.bytesToHex +var hexToBytes = Convert.hexToBytes describe('Script', function() { var p2shScriptPubKey, pubkeyScriptPubkey, addressScriptSig @@ -72,15 +70,15 @@ describe('Script', function() { }) describe('2-of-3 Multi-Signature', function() { - var compressedPubKeys = []; + var compressedPubKeys = [] var numSigs, script, multisig, network beforeEach(function() { compressedPubKeys = ['02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f', - '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f', - '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19'] - numSigs = 2 - network = Network.mainnet.p2shVersion + '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f', + '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19'] + numSigs = 2 + network = Network.mainnet.p2shVersion }) it('should create valid multi-sig address', function() { @@ -98,8 +96,11 @@ describe('Script', function() { var deserialized = new Script(redeemScript) var numOfSignatures = deserialized.chunks[deserialized.chunks.length - 2] - 80 var signaturesRequired = deserialized.chunks[0] - 80 - var sigs = [bytesToHex(deserialized.chunks[1]), bytesToHex(deserialized.chunks[2]), - bytesToHex(deserialized.chunks[3])] + var sigs = [ + bytesToHex(deserialized.chunks[1]), + bytesToHex(deserialized.chunks[2]), + bytesToHex(deserialized.chunks[3]) + ] assert.equal(numOfSignatures, 3) assert.equal(signaturesRequired, 2) diff --git a/test/transaction.js b/test/transaction.js index cade7cd..8d6de6b 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,7 +1,6 @@ var T = require('../src/transaction') var Transaction = T.Transaction var TransactionOut = T.TransactionOut - var convert = require('../src/convert') var ECKey = require('../src/eckey').ECKey var Script = require('../src/script') @@ -84,7 +83,7 @@ describe('Transaction', function() { // the deserialized-serialized transaction should return to its original state (== tx) var bytes2 = Transaction.deserialize(mutated).serialize() assert.deepEqual(bytes, bytes2) - }); + }) }) describe('creating a transaction', function() { diff --git a/test/wallet.js b/test/wallet.js index 1db22cf..5bcd7c0 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -6,6 +6,7 @@ var TransactionOut = T.TransactionOut var Script = require('../src/script.js') var convert = require('../src/convert.js') var assert = require('assert') +var sinon = require('sinon') var SHA256 = require('crypto-js/sha256') var Crypto = require('crypto-js') @@ -13,10 +14,8 @@ var fixtureTxes = require('./fixtures/mainnet_tx') var fixtureTx1Hex = fixtureTxes.prevTx var fixtureTx2Hex = fixtureTxes.tx -var sinon = require('sinon') - describe('Wallet', function() { - var seed, wallet; + var seed, wallet beforeEach(function(){ seed = convert.wordArrayToBytes(SHA256("don't use a string seed like this in real life")) wallet = new Wallet(seed) @@ -141,9 +140,9 @@ describe('Wallet', function() { wallet.generateAddress() assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"), - wallet.getExternalAccount().derive(1).priv) - assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"), - wallet.getInternalAccount().derive(0).priv) + wallet.getExternalAccount().derive(1).priv) + assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"), + wallet.getInternalAccount().derive(0).priv) }) it('raises an error when address is not found', function(){ @@ -155,7 +154,7 @@ describe('Wallet', function() { }) describe('Unspent Outputs', function(){ - var expectedUtxo, expectedOutputKey; + var expectedUtxo, expectedOutputKey beforeEach(function(){ expectedUtxo = { "hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7", @@ -216,7 +215,7 @@ describe('Wallet', function() { }) describe('setUnspentOutputs', function(){ - var utxo; + var utxo beforeEach(function(){ utxo = cloneObject([expectedUtxo]) }) @@ -275,7 +274,7 @@ describe('Wallet', function() { }) describe('setUnspentOutputsAsync', function(){ - var utxo; + var utxo beforeEach(function(){ utxo = cloneObject([expectedUtxo]) }) @@ -308,7 +307,7 @@ describe('Wallet', function() { }) describe('processTx', function(){ - var tx; + var tx beforeEach(function(){ tx = Transaction.deserialize(fixtureTx1Hex) @@ -382,8 +381,8 @@ describe('Wallet', function() { }) describe('createTx', function(){ - var to, value; - var address1, address2; + var to, value + var address1, address2 beforeEach(function(){ to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' @@ -396,23 +395,23 @@ describe('Wallet', function() { // set up 3 utxo utxo = [ { - "hash": fakeTxHash(1), - "outputIndex": 0, - "address" : address1, - "value": 400000 // not enough for value - }, - { - "hash": fakeTxHash(2), - "outputIndex": 1, - "address" : address1, - "value": 500000 // enough for only value - }, - { - "hash": fakeTxHash(3), - "outputIndex": 0, - "address" : address2, - "value": 520000 // enough for value and fee - } + "hash": fakeTxHash(1), + "outputIndex": 0, + "address" : address1, + "value": 400000 // not enough for value + }, + { + "hash": fakeTxHash(2), + "outputIndex": 1, + "address" : address1, + "value": 500000 // enough for only value + }, + { + "hash": fakeTxHash(3), + "outputIndex": 0, + "address" : address2, + "value": 520000 // enough for value and fee + } ] wallet.setUnspentOutputs(utxo) }) @@ -446,11 +445,11 @@ describe('Wallet', function() { it('ignores spent outputs', function(){ utxo.push( { - "hash": fakeTxHash(4), - "outputIndex": 0, - "address" : address2, - "value": 530000 // enough but spent before createTx - } + "hash": fakeTxHash(4), + "outputIndex": 0, + "address" : address2, + "value": 530000 // enough but spent before createTx + } ) wallet.setUnspentOutputs(utxo) wallet.outputs[fakeTxHash(4) + ":" + 0].spend = fakeTxHash(5) + ":" + 0 @@ -547,7 +546,7 @@ describe('Wallet', function() { }) describe('createTxAsync', function(){ - var to, value, fee; + var to, value, fee beforeEach(function(){ to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'