cosmetic standardization

[closes #56]
This commit is contained in:
Wei Lu 2014-03-31 11:47:47 +08:00
parent 161127d65a
commit 93fe1b4c78
25 changed files with 2092 additions and 2142 deletions

View file

@ -4,8 +4,9 @@ var error = require('./util').error
var mainnet = require('./network').mainnet.addressVersion var mainnet = require('./network').mainnet.addressVersion
function Address(bytes, version) { function Address(bytes, version) {
if (!(this instanceof Address)) if (!(this instanceof Address)) {
return new Address(bytes, version) return new Address(bytes, version)
}
if (bytes instanceof Address) { if (bytes instanceof Address) {
this.hash = bytes.hash this.hash = bytes.hash

View file

@ -1,109 +1,108 @@
// https://en.bitcoin.it/wiki/Base58Check_encoding // https://en.bitcoin.it/wiki/Base58Check_encoding
var BigInteger = require('./jsbn/jsbn'); var BigInteger = require('./jsbn/jsbn')
var Crypto = require('crypto-js'); var Crypto = require('crypto-js')
var convert = require('./convert'); var convert = require('./convert')
var SHA256 = Crypto.SHA256; var SHA256 = Crypto.SHA256
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
var base = BigInteger.valueOf(58); var base = BigInteger.valueOf(58)
var positions = {}; var positions = {}
for (var i=0 ; i < alphabet.length ; ++i) { for (var i=0; i<alphabet.length; ++i) {
positions[alphabet[i]] = i; positions[alphabet[i]] = i
} }
// Convert a byte array to a base58-encoded string. // Convert a byte array to a base58-encoded string.
// Written by Mike Hearn for BitcoinJ. // Written by Mike Hearn for BitcoinJ.
// Copyright (c) 2011 Google Inc. // Copyright (c) 2011 Google Inc.
// Ported to JavaScript by Stefan Thomas. // Ported to JavaScript by Stefan Thomas.
function encode(input) { function encode(input) {
var bi = BigInteger.fromByteArrayUnsigned(input); var bi = BigInteger.fromByteArrayUnsigned(input)
var chars = []; var chars = []
while (bi.compareTo(base) >= 0) { while (bi.compareTo(base) >= 0) {
var mod = bi.mod(base); var mod = bi.mod(base)
chars.push(alphabet[mod.intValue()]); chars.push(alphabet[mod.intValue()])
bi = bi.subtract(mod).divide(base); bi = bi.subtract(mod).divide(base)
} }
chars.push(alphabet[bi.intValue()]); chars.push(alphabet[bi.intValue()])
// Convert leading zeros too. // Convert leading zeros too.
for (var i = 0; i < input.length; i++) { for (var i=0; i<input.length; i++) {
if (input[i] == 0x00) { if (input[i] == 0x00) {
chars.push(alphabet[0]); chars.push(alphabet[0])
} else break; } else break
} }
return chars.reverse().join(''); return chars.reverse().join('')
} }
// decode a base58 string into a byte array // decode a base58 string into a byte array
// input should be a base58 encoded string // input should be a base58 encoded string
// @return Array // @return Array
function decode(input) { function decode(input) {
var base = BigInteger.valueOf(58); var base = BigInteger.valueOf(58)
var length = input.length; var length = input.length
var num = BigInteger.valueOf(0); var num = BigInteger.valueOf(0)
var leading_zero = 0; var leading_zero = 0
var seen_other = false; var seen_other = false
for (var i=0; i<length ; ++i) { for (var i=0; i<length; ++i) {
var chr = input[i]; var chr = input[i]
var p = positions[chr]; var p = positions[chr]
// if we encounter an invalid character, decoding fails // if we encounter an invalid character, decoding fails
if (p === undefined) { if (p === undefined) {
throw new Error('invalid base58 string: ' + input); throw new Error('invalid base58 string: ' + input)
} }
num = num.multiply(base).add(BigInteger.valueOf(p)); num = num.multiply(base).add(BigInteger.valueOf(p))
if (chr == '1' && !seen_other) { if (chr == '1' && !seen_other) {
++leading_zero; ++leading_zero
} } else {
else { seen_other = true
seen_other = true;
} }
} }
var bytes = num.toByteArrayUnsigned(); var bytes = num.toByteArrayUnsigned()
// remove leading zeros // remove leading zeros
while (leading_zero-- > 0) { while (leading_zero-- > 0) {
bytes.unshift(0); bytes.unshift(0)
} }
return bytes; return bytes
} }
function checkEncode(input, vbyte) { function checkEncode(input, vbyte) {
vbyte = vbyte || 0; vbyte = vbyte || 0
var front = [vbyte].concat(input); var front = [vbyte].concat(input)
return encode(front.concat(getChecksum(front))); return encode(front.concat(getChecksum(front)))
} }
function checkDecode(input) { function checkDecode(input) {
var bytes = decode(input), var bytes = decode(input),
front = bytes.slice(0,bytes.length-4), front = bytes.slice(0, bytes.length-4),
back = bytes.slice(bytes.length-4); back = bytes.slice(bytes.length-4)
var checksum = getChecksum(front); var checksum = getChecksum(front)
if ("" + checksum != "" + back) { if ("" + checksum != "" + back) {
throw new Error("Checksum failed"); throw new Error("Checksum failed")
} }
var o = front.slice(1); var o = front.slice(1)
o.version = front[0]; o.version = front[0]
return o; return o
} }
function getChecksum(bytes) { function getChecksum(bytes) {
var wordArray = convert.bytesToWordArray(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 = { module.exports = {

View file

@ -1,74 +1,62 @@
var Crypto = require('crypto-js'); var Crypto = require('crypto-js')
var WordArray = Crypto.lib.WordArray; var WordArray = Crypto.lib.WordArray
var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function lpad(str, padString, length) { function lpad(str, padString, length) {
while (str.length < length) str = padString + str; while (str.length < length) str = padString + str
return str; return str
} }
/**
* Convert a byte array to a hex string
*/
function bytesToHex(bytes) { function bytesToHex(bytes) {
return bytes.map(function(x) { return bytes.map(function(x) {
return lpad(x.toString(16), '0', 2) return lpad(x.toString(16), '0', 2)
}).join(''); }).join('')
}; }
/**
* Convert a hex string to a byte array
*/
function hexToBytes(hex) { function hexToBytes(hex) {
return hex.match(/../g).map(function(x) { return hex.match(/../g).map(function(x) {
return parseInt(x,16) return parseInt(x,16)
}); })
} }
/**
* Convert a byte array to a base-64 string
*/
function bytesToBase64(bytes) { function bytesToBase64(bytes) {
var base64 = [] var base64 = []
for (var i = 0; i < bytes.length; i += 3) { 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++) { for (var j = 0; j < 4; j++) {
if (i * 8 + j * 6 <= bytes.length * 8) { 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 { } else {
base64.push('='); base64.push('=')
} }
} }
} }
return base64.join(''); return base64.join('')
} }
/**
* Convert a base-64 string to a byte array
*/
function base64ToBytes(base64) { function base64ToBytes(base64) {
// Remove non-base-64 characters // Remove non-base-64 characters
base64 = base64.replace(/[^A-Z0-9+\/]/ig, ''); base64 = base64.replace(/[^A-Z0-9+\/]/ig, '')
var bytes = []; var bytes = []
var imod4 = 0; var imod4 = 0
for (var i = 0; i < base64.length; imod4 = ++i % 4) { for (var i = 0; i < base64.length; imod4 = ++i % 4) {
if (!imod4) continue if (!imod4) continue
bytes.push( bytes.push(
( (
(base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) <<
(imod4 * 2) (imod4 * 2)
) | ) |
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)) (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))
); )
} }
return bytes; return bytes
} }
/** /**
@ -76,48 +64,48 @@ function base64ToBytes(base64) {
*/ */
function coerceToBytes(input) { function coerceToBytes(input) {
if (typeof input != 'string') return input if (typeof input != 'string') return input
return hexToBytes(input); return hexToBytes(input)
} }
function binToBytes(bin) { function binToBytes(bin) {
return bin.match(/......../g).map(function(x) { return bin.match(/......../g).map(function(x) {
return parseInt(x,2) return parseInt(x,2)
}); })
} }
function bytesToBin(bytes) { function bytesToBin(bytes) {
return bytes.map(function(x) { return bytes.map(function(x) {
return lpad(x.toString(2), '0', 8) return lpad(x.toString(2), '0', 8)
}).join(''); }).join('')
} }
function bytesToString(bytes) { function bytesToString(bytes) {
return bytes.map(function(x){ return bytes.map(function(x){
return String.fromCharCode(x) return String.fromCharCode(x)
}).join(''); }).join('')
} }
function stringToBytes(string) { function stringToBytes(string) {
return string.split('').map(function(x) { return string.split('').map(function(x) {
return x.charCodeAt(0) return x.charCodeAt(0)
}); })
} }
/** /**
* Create a byte array representing a number with the given length * Create a byte array representing a number with the given length
*/ */
function numToBytes(num, bytes) { function numToBytes(num, bytes) {
if (bytes === undefined) bytes = 8; if (bytes === undefined) bytes = 8
if (bytes === 0) return []; if (bytes === 0) return []
return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1)); return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1))
} }
/** /**
* Convert a byte array to the number that it represents * Convert a byte array to the number that it represents
*/ */
function bytesToNum(bytes) { function bytesToNum(bytes) {
if (bytes.length === 0) return 0; if (bytes.length === 0) return 0
return bytes[0] + 256 * bytesToNum(bytes.slice(1)); return bytes[0] + 256 * bytesToNum(bytes.slice(1))
} }
/** /**
@ -128,10 +116,10 @@ function bytesToNum(bytes) {
* Returns a byte array. * Returns a byte array.
*/ */
function numToVarInt(num) { function numToVarInt(num) {
if (num < 253) return [num]; if (num < 253) return [num]
if (num < 65536) return [253].concat(numToBytes(num, 2)); if (num < 65536) return [253].concat(numToBytes(num, 2))
if (num < 4294967296) return [254].concat(numToBytes(num, 4)); if (num < 4294967296) return [254].concat(numToBytes(num, 4))
return [255].concat(numToBytes(num, 8)); return [255].concat(numToBytes(num, 8))
} }
/** /**
@ -148,7 +136,7 @@ function varIntToNum(bytes) {
prefix < 253 ? bytes.slice(0, 1) prefix < 253 ? bytes.slice(0, 1)
: prefix === 253 ? bytes.slice(1, 3) : prefix === 253 ? bytes.slice(1, 3)
: prefix === 254 ? bytes.slice(1, 5) : prefix === 254 ? bytes.slice(1, 5)
: bytes.slice(1, 9) : bytes.slice(1, 9)
return { return {
bytes: prefix < 253 ? viBytes : bytes.slice(0, viBytes.length + 1), bytes: prefix < 253 ? viBytes : bytes.slice(0, viBytes.length + 1),
@ -157,19 +145,19 @@ function varIntToNum(bytes) {
} }
function bytesToWords(bytes) { function bytesToWords(bytes) {
var words = []; var words = []
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) { 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) { function wordsToBytes(words) {
var bytes = []; var bytes = []
for (var b = 0; b < words.length * 32; b += 8) { 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) { function bytesToWordArray(bytes) {

View file

@ -1,133 +1,129 @@
var sec = require('./jsbn/sec'); var sec = require('./jsbn/sec')
var rng = require('secure-random'); var rng = require('secure-random')
var BigInteger = require('./jsbn/jsbn'); var BigInteger = require('./jsbn/jsbn')
var convert = require('./convert') 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; function implShamirsTrick(P, k, Q, l) {
var m = Math.max(k.bitLength(), l.bitLength())
var ecparams = sec("secp256k1"); var Z = P.add2D(Q)
var P_OVER_FOUR = null; 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) { 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 (k.testBit(i)) {
if (l.testBit(i)) { if (l.testBit(i)) {
R = R.add2D(Z); R = R.add2D(Z)
} else { } else {
R = R.add2D(P); R = R.add2D(P)
} }
} else { } else {
if (l.testBit(i)) { if (l.testBit(i)) {
R = R.add2D(Q); R = R.add2D(Q)
} }
} }
} }
return R; return R
}; }
function deterministicGenerateK(hash,key) { function deterministicGenerateK(hash,key) {
var vArr = []; var vArr = []
var kArr = []; var kArr = []
for (var i = 0;i < 32;i++) vArr.push(1); for (var i = 0;i < 32;i++) vArr.push(1)
for (var i = 0;i < 32;i++) kArr.push(0); for (var i = 0;i < 32;i++) kArr.push(0)
var v = convert.bytesToWordArray(vArr) var v = convert.bytesToWordArray(vArr)
var k = convert.bytesToWordArray(kArr) var k = convert.bytesToWordArray(kArr)
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k) k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(key).concat(hash)), k)
v = HmacSHA256(v, k) v = HmacSHA256(v, k)
vArr = convert.wordArrayToBytes(v) vArr = convert.wordArrayToBytes(v)
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k) k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(key).concat(hash)), k)
v = HmacSHA256(v,k) v = HmacSHA256(v,k)
v = HmacSHA256(v,k) v = HmacSHA256(v,k)
vArr = convert.wordArrayToBytes(v) vArr = convert.wordArrayToBytes(v)
return BigInteger.fromByteArrayUnsigned(vArr); return BigInteger.fromByteArrayUnsigned(vArr)
} }
var ECDSA = { var ECDSA = {
getBigRandom: function (limit) { getBigRandom: function (limit) {
return new BigInteger(limit.bitLength(), rng) return new BigInteger(limit.bitLength(), rng).
.mod(limit.subtract(BigInteger.ONE)) mod(limit.subtract(BigInteger.ONE)).
.add(BigInteger.ONE) add(BigInteger.ONE)
;
}, },
sign: function (hash, priv) { sign: function (hash, priv) {
var d = priv; var d = priv
var n = ecparams.getN(); var n = ecparams.getN()
var e = BigInteger.fromByteArrayUnsigned(hash); var e = BigInteger.fromByteArrayUnsigned(hash)
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned()) var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
var G = ecparams.getG(); var G = ecparams.getG()
var Q = G.multiply(k); var Q = G.multiply(k)
var r = Q.getX().toBigInteger().mod(n); 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) { verify: function (hash, sig, pubkey) {
var r,s; var r,s
if (Array.isArray(sig)) { if (Array.isArray(sig)) {
var obj = ECDSA.parseSig(sig); var obj = ECDSA.parseSig(sig)
r = obj.r; r = obj.r
s = obj.s; s = obj.s
} else if ("object" === typeof sig && sig.r && sig.s) { } else if ("object" === typeof sig && sig.r && sig.s) {
r = sig.r; r = sig.r
s = sig.s; s = sig.s
} else { } else {
throw new Error("Invalid value for signature"); throw new Error("Invalid value for signature")
} }
var Q; var Q
if (pubkey instanceof ECPointFp) { if (pubkey instanceof ECPointFp) {
Q = pubkey; Q = pubkey
} else if (Array.isArray(pubkey)) { } else if (Array.isArray(pubkey)) {
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey)
} else { } 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) { verifyRaw: function (e, r, s, Q) {
var n = ecparams.getN(); var n = ecparams.getN()
var G = ecparams.getG(); var G = ecparams.getG()
if (r.compareTo(BigInteger.ONE) < 0 || if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) {
r.compareTo(n) >= 0) return false
return false; }
if (s.compareTo(BigInteger.ONE) < 0 || if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
s.compareTo(n) >= 0) return false
return false; }
var c = s.modInverse(n); var c = s.modInverse(n)
var u1 = e.multiply(c).mod(n); var u1 = e.multiply(c).mod(n)
var u2 = r.multiply(c).mod(n); var u2 = r.multiply(c).mod(n)
// TODO(!!!): For some reason Shamir's trick isn't working with // TODO(!!!): For some reason Shamir's trick isn't working with
// signed message verification!? Probably an implementation // signed message verification!? Probably an implementation
// error! // error!
//var point = implShamirsTrick(G, u1, Q, u2); //var point = implShamirsTrick(G, u1, Q, u2)
var point = G.multiply(u1).add(Q.multiply(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. * Takes two BigIntegers representing r and s and returns a byte array.
*/ */
serializeSig: function (r, s) { serializeSig: function (r, s) {
var rBa = r.toByteArraySigned(); var rBa = r.toByteArraySigned()
var sBa = s.toByteArraySigned(); var sBa = s.toByteArraySigned()
var sequence = []; var sequence = []
sequence.push(0x02); // INTEGER sequence.push(0x02); // INTEGER
sequence.push(rBa.length); sequence.push(rBa.length)
sequence = sequence.concat(rBa); sequence = sequence.concat(rBa)
sequence.push(0x02); // INTEGER sequence.push(0x02); // INTEGER
sequence.push(sBa.length); sequence.push(sBa.length)
sequence = sequence.concat(sBa); sequence = sequence.concat(sBa)
sequence.unshift(sequence.length); sequence.unshift(sequence.length)
sequence.unshift(0x30); // SEQUENCE sequence.unshift(0x30); // SEQUENCE
return sequence; return sequence
}, },
/** /**
@ -165,48 +161,51 @@ var ECDSA = {
* } * }
*/ */
parseSig: function (sig) { parseSig: function (sig) {
var cursor; var cursor
if (sig[0] != 0x30) if (sig[0] != 0x30) {
throw new Error("Signature not a valid DERSequence"); throw new Error("Signature not a valid DERSequence")
}
cursor = 2; cursor = 2
if (sig[cursor] != 0x02) if (sig[cursor] != 0x02) {
throw new Error("First element in signature must be a DERInteger");; throw new Error("First element in signature must be a DERInteger")
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); }
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])
cursor += 2+sig[cursor+1]; cursor += 2+sig[cursor+1]
if (sig[cursor] != 0x02) if (sig[cursor] != 0x02) {
throw new Error("Second element in signature must be a DERInteger"); throw new Error("Second element in signature must be a DERInteger")
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); }
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) //if (cursor != sig.length)
// throw new Error("Extra bytes in signature"); // throw new Error("Extra bytes in signature")
var r = BigInteger.fromByteArrayUnsigned(rBa); var r = BigInteger.fromByteArrayUnsigned(rBa)
var s = BigInteger.fromByteArrayUnsigned(sBa); var s = BigInteger.fromByteArrayUnsigned(sBa)
return {r: r, s: s}; return {r: r, s: s}
}, },
parseSigCompact: function (sig) { parseSigCompact: function (sig) {
if (sig.length !== 65) { 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 // Signature is prefixed with a type byte storing three bits of
// information. // information.
var i = sig[0] - 27; var i = sig[0] - 27
if (i < 0 || i > 7) { if (i < 0 || i > 7) {
throw new Error("Invalid signature type"); throw new Error("Invalid signature type")
} }
var n = ecparams.getN(); var n = ecparams.getN()
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n); var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n)
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).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) { recoverPubKey: function (r, s, hash, i) {
// The recovery parameter i has two bits. // The recovery parameter i has two bits.
i = i & 3; i = i & 3
// The less significant bit specifies whether the y coordinate // The less significant bit specifies whether the y coordinate
// of the compressed point is even or not. // 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 // The more significant bit specifies whether we should use the
// first or second candidate key. // first or second candidate key.
var isSecondKey = i >> 1; var isSecondKey = i >> 1
var n = ecparams.getN(); var n = ecparams.getN()
var G = ecparams.getG(); var G = ecparams.getG()
var curve = ecparams.getCurve(); var curve = ecparams.getCurve()
var p = curve.getQ(); var p = curve.getQ()
var a = curve.getA().toBigInteger(); var a = curve.getA().toBigInteger()
var b = curve.getB().toBigInteger(); var b = curve.getB().toBigInteger()
// We precalculate (p + 1) / 4 where p is if the field order // We precalculate (p + 1) / 4 where p is if the field order
if (!P_OVER_FOUR) { 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 // 1.1 Compute x
var x = isSecondKey ? r.add(n) : r; var x = isSecondKey ? r.add(n) : r
// 1.3 Convert x to point // 1.3 Convert x to point
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(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 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, // If beta is even, but y isn't or vice versa, then convert it,
// otherwise we're done and y == beta. // 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 // 1.4 Check that nR is at infinity
var R = new ECPointFp(curve, var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))
curve.fromBigInteger(x), R.validate()
curve.fromBigInteger(y));
R.validate();
// 1.5 Compute e from M // 1.5 Compute e from M
var e = BigInteger.fromByteArrayUnsigned(hash); var e = BigInteger.fromByteArrayUnsigned(hash)
var eNeg = BigInteger.ZERO.subtract(e).mod(n); var eNeg = BigInteger.ZERO.subtract(e).mod(n)
// 1.6 Compute Q = r^-1 (sR - eG) // 1.6 Compute Q = r^-1 (sR - eG)
var rInv = r.modInverse(n); var rInv = r.modInverse(n)
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv)
Q.validate(); Q.validate()
if (!ECDSA.verifyRaw(e, r, s, Q)) { if (!ECDSA.verifyRaw(e, r, s, Q)) {
throw new Error("Pubkey recovery unsuccessful"); throw new Error("Pubkey recovery unsuccessful")
} }
return Q return Q
@ -297,6 +294,6 @@ var ECDSA = {
throw new Error("Unable to find valid recovery factor") throw new Error("Unable to find valid recovery factor")
} }
}; }
module.exports = ECDSA; module.exports = ECDSA

View file

@ -8,7 +8,6 @@ var ECPointFp = require('./jsbn/ec').ECPointFp
var sec = require('./jsbn/sec') var sec = require('./jsbn/sec')
var Network = require('./network') var Network = require('./network')
var util = require('./util') var util = require('./util')
var ecparams = sec("secp256k1") var ecparams = sec("secp256k1")
// input can be nothing, array of bytes, hex string, or base58 string // 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) { ECKey.prototype.getPub = function(compressed) {
if (compressed === undefined) compressed = this.compressed if (compressed === undefined) compressed = this.compressed
return ECPubKey(ecparams.getG().multiply(this.priv), compressed) return ECPubKey(ecparams.getG().multiply(this.priv), compressed)
} }
ECKey.prototype.toBin = function() { ECKey.prototype.toBin = function() {
return convert.bytesToString(this.toBytes()) return convert.bytesToString(this.toBytes())
} }
ECKey.version_bytes = { ECKey.version_bytes = {
@ -75,41 +74,41 @@ ECKey.version_bytes = {
} }
ECKey.prototype.toWif = function(version) { 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() { ECKey.prototype.toHex = function() {
return convert.bytesToHex(this.toBytes()) return convert.bytesToHex(this.toBytes())
} }
ECKey.prototype.toBytes = function() { ECKey.prototype.toBytes = function() {
var bytes = this.priv.toByteArrayUnsigned(); var bytes = this.priv.toByteArrayUnsigned()
if (this.compressed) bytes.push(1) if (this.compressed) bytes.push(1)
return bytes return bytes
} }
ECKey.prototype.toBase64 = function() { ECKey.prototype.toBase64 = function() {
return convert.bytesToBase64(this.toBytes()) return convert.bytesToBase64(this.toBytes())
} }
ECKey.prototype.toString = ECKey.prototype.toHex ECKey.prototype.toString = ECKey.prototype.toHex
ECKey.prototype.getAddress = function(version) { ECKey.prototype.getAddress = function(version) {
return this.getPub().getAddress(version) return this.getPub().getAddress(version)
} }
ECKey.prototype.add = function(key) { 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) { 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) { ECKey.prototype.sign = function(hash) {
return ecdsa.sign(hash, this.priv); return ecdsa.sign(hash, this.priv)
} }
ECKey.prototype.verify = function(hash, sig) { ECKey.prototype.verify = function(hash, sig) {
@ -145,38 +144,38 @@ ECPubKey.prototype.import = function(input, compressed) {
} }
ECPubKey.prototype.add = function(key) { 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) { 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) { ECPubKey.prototype.toBytes = function(compressed) {
if (compressed === undefined) compressed = this.compressed if (compressed === undefined) compressed = this.compressed
return this.pub.getEncoded(compressed) return this.pub.getEncoded(compressed)
} }
ECPubKey.prototype.toHex = function(compressed) { ECPubKey.prototype.toHex = function(compressed) {
return convert.bytesToHex(this.toBytes(compressed)) return convert.bytesToHex(this.toBytes(compressed))
} }
ECPubKey.prototype.toBin = function(compressed) { ECPubKey.prototype.toBin = function(compressed) {
return convert.bytesToString(this.toBytes(compressed)) return convert.bytesToString(this.toBytes(compressed))
} }
ECPubKey.prototype.toWif = function(version) { 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.toString = ECPubKey.prototype.toHex
ECPubKey.prototype.getAddress = function(version) { 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) { ECPubKey.prototype.verify = function(hash, sig) {

View file

@ -3,7 +3,7 @@ var base58 = require('./base58.js')
var assert = require('assert') var assert = require('assert')
var format = require('util').format var format = require('util').format
var util = require('./util.js') var util = require('./util.js')
var Crypto = require('crypto-js'); var Crypto = require('crypto-js')
var HmacSHA512 = Crypto.HmacSHA512 var HmacSHA512 = Crypto.HmacSHA512
var HMAC= Crypto.algo.HMAC var HMAC= Crypto.algo.HMAC
var ECKey = require('./eckey.js').ECKey var ECKey = require('./eckey.js').ECKey
@ -12,234 +12,234 @@ var Address = require('./address.js')
var Network = require('./network') var Network = require('./network')
var HDWallet = module.exports = function(seed, network) { var HDWallet = module.exports = function(seed, network) {
if (seed === undefined) return if (seed === undefined) return;
var seedWords = convert.bytesToWordArray(seed) var seedWords = convert.bytesToWordArray(seed)
var I = convert.wordArrayToBytes(HmacSHA512(seedWords, 'Bitcoin seed')) var I = convert.wordArrayToBytes(HmacSHA512(seedWords, 'Bitcoin seed'))
this.chaincode = I.slice(32) this.chaincode = I.slice(32)
this.network = network || 'mainnet' this.network = network || 'mainnet'
if(!Network.hasOwnProperty(this.network)) { if(!Network.hasOwnProperty(this.network)) {
throw new Error("Unknown network: " + this.network) throw new Error("Unknown network: " + this.network)
} }
this.priv = new ECKey(I.slice(0, 32).concat([1]), true) this.priv = new ECKey(I.slice(0, 32).concat([1]), true)
this.pub = this.priv.getPub() this.pub = this.priv.getPub()
this.index = 0 this.index = 0
this.depth = 0 this.depth = 0
} }
HDWallet.HIGHEST_BIT = 0x80000000 HDWallet.HIGHEST_BIT = 0x80000000
HDWallet.LENGTH = 78 HDWallet.LENGTH = 78
function arrayEqual(a, b) { 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) { HDWallet.fromSeedHex = function(hex, network) {
return new HDWallet(convert.hexToBytes(hex), network) return new HDWallet(convert.hexToBytes(hex), network)
} }
HDWallet.fromSeedString = function(string, network) { HDWallet.fromSeedString = function(string, network) {
return new HDWallet(convert.stringToBytes(string), network) return new HDWallet(convert.stringToBytes(string), network)
} }
HDWallet.fromBase58 = function(input) { HDWallet.fromBase58 = function(input) {
var buffer = base58.decode(input) var buffer = base58.decode(input)
if (buffer.length == HDWallet.LENGTH + 4) { if (buffer.length == HDWallet.LENGTH + 4) {
var expectedChecksum = buffer.slice(HDWallet.LENGTH, HDWallet.LENGTH + 4) var expectedChecksum = buffer.slice(HDWallet.LENGTH, HDWallet.LENGTH + 4)
buffer = buffer.slice(0, HDWallet.LENGTH) buffer = buffer.slice(0, HDWallet.LENGTH)
var actualChecksum = HDWallet.getChecksum(buffer) var actualChecksum = HDWallet.getChecksum(buffer)
if (!arrayEqual(expectedChecksum, actualChecksum)) { if (!arrayEqual(expectedChecksum, actualChecksum)) {
throw new Error('Checksum mismatch') throw new Error('Checksum mismatch')
}
} }
}
return HDWallet.fromBytes(buffer) return HDWallet.fromBytes(buffer)
} }
HDWallet.fromHex = function(input) { HDWallet.fromHex = function(input) {
return HDWallet.fromBytes(convert.hexToBytes(input)) return HDWallet.fromBytes(convert.hexToBytes(input))
} }
HDWallet.fromBytes = function(input) { HDWallet.fromBytes = function(input) {
// This 78 byte structure can be encoded like other Bitcoin data in Base58. (+32 bits checksum) // This 78 byte structure can be encoded like other Bitcoin data in Base58. (+32 bits checksum)
if (input.length != HDWallet.LENGTH) { if (input.length != HDWallet.LENGTH) {
throw new Error(format('Invalid input length, %s. Expected %s.', 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() if (!hd.network) {
throw new Error(format('Could not find version %s', convert.bytesToHex(versionBytes)))
}
// 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
// testnet: 0x043587CF public, 0x04358394 private) hd.depth = input[4]
var versionBytes = input.slice(0, 4)
var versionWord = convert.bytesToWords(versionBytes)[0]
var type
for(var name in Network) { // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
var network = Network[name] hd.parentFingerprint = input.slice(5, 9)
for(var t in network.hdVersions) { assert((hd.depth === 0) == arrayEqual(hd.parentFingerprint, [0, 0, 0, 0]))
if (versionWord != network.hdVersions[t]) continue
type = t
hd.network = name
}
}
if (!hd.network) { // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
throw new Error(format('Could not find version %s', convert.bytesToHex(versionBytes))) // 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)
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... // 32 bytes: the chain code
hd.depth = input[4] hd.chaincode = input.slice(13, 45)
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) // 33 bytes: the public key or private key data (0x02 + X or 0x03 + X for
hd.parentFingerprint = input.slice(5, 9) // public keys, 0x00 + k for private keys)
assert((hd.depth === 0) == arrayEqual(hd.parentFingerprint, [0, 0, 0, 0])) 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)
}
// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. return hd
// 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)
// 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
} }
HDWallet.prototype.getIdentifier = function() { HDWallet.prototype.getIdentifier = function() {
return util.sha256ripe160(this.pub.toBytes()) return util.sha256ripe160(this.pub.toBytes())
} }
HDWallet.prototype.getFingerprint = function() { HDWallet.prototype.getFingerprint = function() {
return this.getIdentifier().slice(0, 4) return this.getIdentifier().slice(0, 4)
} }
HDWallet.prototype.getAddress = function() { 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) { HDWallet.prototype.toBytes = function(priv) {
var buffer = [] var buffer = []
// Version // Version
// 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, // 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public,
// 0x04358394 private) // 0x04358394 private)
var version = Network[this.network].hdVersions[priv ? 'priv' : 'pub'] var version = Network[this.network].hdVersions[priv ? 'priv' : 'pub']
var vBytes = convert.wordsToBytes([version]) var vBytes = convert.wordsToBytes([version])
buffer = buffer.concat(vBytes) buffer = buffer.concat(vBytes)
assert.equal(buffer.length, 4) assert.equal(buffer.length, 4)
// Depth // Depth
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
buffer.push(this.depth) buffer.push(this.depth)
assert.equal(buffer.length, 4 + 1) assert.equal(buffer.length, 4 + 1)
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
buffer = buffer.concat(this.depth ? this.parentFingerprint : [0, 0, 0, 0]) buffer = buffer.concat(this.depth ? this.parentFingerprint : [0, 0, 0, 0])
assert.equal(buffer.length, 4 + 1 + 4) 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. // 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) // This is encoded in MSB order. (0x00000000 if master key)
buffer = buffer.concat(convert.numToBytes(this.index, 4).reverse()) buffer = buffer.concat(convert.numToBytes(this.index, 4).reverse())
assert.equal(buffer.length, 4 + 1 + 4 + 4) assert.equal(buffer.length, 4 + 1 + 4 + 4)
// 32 bytes: the chain code // 32 bytes: the chain code
buffer = buffer.concat(this.chaincode) buffer = buffer.concat(this.chaincode)
assert.equal(buffer.length, 4 + 1 + 4 + 4 + 32) assert.equal(buffer.length, 4 + 1 + 4 + 4 + 32)
// 33 bytes: the public key or private key data // 33 bytes: the public key or private key data
// (0x02 + X or 0x03 + X for public keys, 0x00 + k for private keys) // (0x02 + X or 0x03 + X for public keys, 0x00 + k for private keys)
if (priv) { if (priv) {
assert(this.priv, 'Cannot serialize to private without private key') assert(this.priv, 'Cannot serialize to private without private key')
buffer.push(0) buffer.push(0)
buffer = buffer.concat(this.priv.toBytes().slice(0, 32)) buffer = buffer.concat(this.priv.toBytes().slice(0, 32))
} else { } else {
buffer = buffer.concat(this.pub.toBytes(true)) buffer = buffer.concat(this.pub.toBytes(true))
} }
return buffer return buffer
} }
HDWallet.prototype.toHex = function(priv) { HDWallet.prototype.toHex = function(priv) {
var bytes = this.toBytes(priv) var bytes = this.toBytes(priv)
return convert.bytesToHex(bytes) return convert.bytesToHex(bytes)
} }
HDWallet.prototype.toBase58 = function(priv) { HDWallet.prototype.toBase58 = function(priv) {
var buffer = this.toBytes(priv) var buffer = this.toBytes(priv)
, checksum = HDWallet.getChecksum(buffer) , checksum = HDWallet.getChecksum(buffer)
buffer = buffer.concat(checksum) buffer = buffer.concat(checksum)
return base58.encode(buffer) return base58.encode(buffer)
} }
HDWallet.prototype.derive = function(i) { HDWallet.prototype.derive = function(i) {
var I var I
, iBytes = convert.numToBytes(i, 4).reverse() , iBytes = convert.numToBytes(i, 4).reverse()
, cPar = this.chaincode , cPar = this.chaincode
, usePriv = i >= HDWallet.HIGHEST_BIT , usePriv = i >= HDWallet.HIGHEST_BIT
, SHA512 = Crypto.algo.SHA512 , SHA512 = Crypto.algo.SHA512
if (usePriv) { if (usePriv) {
assert(this.priv, 'Private derive on public key') assert(this.priv, 'Private derive on public key')
// If 1, private derivation is used: // If 1, private derivation is used:
// let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:] // let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:]
var kPar = this.priv.toBytes().slice(0, 32) var kPar = this.priv.toBytes().slice(0, 32)
I = HmacFromBytesToBytes(SHA512, [0].concat(kPar, iBytes), cPar) I = HmacFromBytesToBytes(SHA512, [0].concat(kPar, iBytes), cPar)
} else { } else {
// If 0, public derivation is used: // If 0, public derivation is used:
// let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i) // let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i)
var KPar = this.pub.toBytes(true) var KPar = this.pub.toBytes(true)
I = HmacFromBytesToBytes(SHA512, KPar.concat(iBytes), cPar) I = HmacFromBytesToBytes(SHA512, KPar.concat(iBytes), cPar)
} }
// Split I = IL || IR into two 32-byte sequences, IL and IR. // Split I = IL || IR into two 32-byte sequences, IL and IR.
var IL = I.slice(0, 32) var IL = I.slice(0, 32)
, IR = I.slice(32) , IR = I.slice(32)
var hd = new HDWallet() var hd = new HDWallet()
hd.network = this.network hd.network = this.network
if (this.priv) { if (this.priv) {
// ki = IL + kpar (mod n). // ki = IL + kpar (mod n).
hd.priv = this.priv.add(new ECKey(IL.concat([1]))) hd.priv = this.priv.add(new ECKey(IL.concat([1])))
hd.priv.compressed = true hd.priv.compressed = true
hd.priv.version = this.getKeyVersion() hd.priv.version = this.getKeyVersion()
hd.pub = hd.priv.getPub() hd.pub = hd.priv.getPub()
} else { } else {
// Ki = (IL + kpar)*G = IL*G + Kpar // Ki = (IL + kpar)*G = IL*G + Kpar
hd.pub = this.pub.add(new ECKey(IL.concat([1]), true).getPub()) hd.pub = this.pub.add(new ECKey(IL.concat([1]), true).getPub())
} }
// ci = IR. // ci = IR.
hd.chaincode = IR hd.chaincode = IR
hd.parentFingerprint = this.getFingerprint() hd.parentFingerprint = this.getFingerprint()
hd.depth = this.depth + 1 hd.depth = this.depth + 1
hd.index = i hd.index = i
hd.pub.compressed = true hd.pub.compressed = true
return hd return hd
} }
HDWallet.prototype.derivePrivate = function(index) { HDWallet.prototype.derivePrivate = function(index) {
return this.derive(index + HDWallet.HIGHEST_BIT) return this.derive(index + HDWallet.HIGHEST_BIT)
} }
HDWallet.prototype.getKeyVersion = function() { HDWallet.prototype.getKeyVersion = function() {
return Network[this.network].addressVersion return Network[this.network].addressVersion
} }
HDWallet.prototype.toString = HDWallet.prototype.toBase58 HDWallet.prototype.toString = HDWallet.prototype.toBase58

View file

@ -1,29 +1,25 @@
var Key = require('./eckey'); var Key = require('./eckey')
var T = require('./transaction')
module.exports = { module.exports = {
Address: require('./address'), Address: require('./address'),
Key: Key.ECKey, Key: Key.ECKey,
ECKey: Key.ECKey, ECKey: Key.ECKey,
ECPubKey: Key.ECPubKey, ECPubKey: Key.ECPubKey,
Message: require('./message'), Message: require('./message'),
BigInteger: require('./jsbn/jsbn'), BigInteger: require('./jsbn/jsbn'),
Crypto: require('crypto-js'), //should we expose this at all? Crypto: require('crypto-js'), //should we expose this at all?
Script: require('./script'), Script: require('./script'),
Opcode: require('./opcode'), Opcode: require('./opcode'),
Transaction: require('./transaction').Transaction, Transaction: T.Transaction,
Util: require('./util'), Util: require('./util'),
TransactionIn: require('./transaction').TransactionIn, TransactionIn: T.TransactionIn,
TransactionOut: require('./transaction').TransactionOut, TransactionOut: T.TransactionOut,
ECPointFp: require('./jsbn/ec').ECPointFp, ECPointFp: require('./jsbn/ec').ECPointFp,
Wallet: require('./wallet'), Wallet: require('./wallet'),
network: require('./network'), network: require('./network'),
ecdsa: require('./ecdsa'),
ecdsa: require('./ecdsa'), HDWallet: require('./hdwallet.js'),
HDWallet: require('./hdwallet.js'), base58: require('./base58'),
convert: require('./convert')
// base58 encoding/decoding to bytes
base58: require('./base58'),
// conversions
convert: require('./convert')
} }

View file

@ -38,8 +38,8 @@ function sign(key, message) {
var sBa = obj.s.toByteArrayUnsigned() var sBa = obj.s.toByteArrayUnsigned()
// Pad to 32 bytes per value // Pad to 32 bytes per value
while (rBa.length < 32) rBa.unshift(0) while (rBa.length < 32) rBa.unshift(0);
while (sBa.length < 32) sBa.unshift(0) while (sBa.length < 32) sBa.unshift(0);
sig = [i].concat(rBa, sBa) sig = [i].concat(rBa, sBa)

View file

@ -15,5 +15,5 @@ module.exports = {
priv: 0x04358394 priv: 0x04358394
} }
} }
}; }

View file

@ -1,147 +1,147 @@
var Opcode = { var Opcode = {
map: { map: {
// push value // push value
OP_0 : 0, OP_0 : 0,
OP_FALSE : 0, OP_FALSE : 0,
OP_PUSHDATA1 : 76, OP_PUSHDATA1 : 76,
OP_PUSHDATA2 : 77, OP_PUSHDATA2 : 77,
OP_PUSHDATA4 : 78, OP_PUSHDATA4 : 78,
OP_1NEGATE : 79, OP_1NEGATE : 79,
OP_RESERVED : 80, OP_RESERVED : 80,
OP_1 : 81, OP_1 : 81,
OP_TRUE : 81, OP_TRUE : 81,
OP_2 : 82, OP_2 : 82,
OP_3 : 83, OP_3 : 83,
OP_4 : 84, OP_4 : 84,
OP_5 : 85, OP_5 : 85,
OP_6 : 86, OP_6 : 86,
OP_7 : 87, OP_7 : 87,
OP_8 : 88, OP_8 : 88,
OP_9 : 89, OP_9 : 89,
OP_10 : 90, OP_10 : 90,
OP_11 : 91, OP_11 : 91,
OP_12 : 92, OP_12 : 92,
OP_13 : 93, OP_13 : 93,
OP_14 : 94, OP_14 : 94,
OP_15 : 95, OP_15 : 95,
OP_16 : 96, OP_16 : 96,
// control // control
OP_NOP : 97, OP_NOP : 97,
OP_VER : 98, OP_VER : 98,
OP_IF : 99, OP_IF : 99,
OP_NOTIF : 100, OP_NOTIF : 100,
OP_VERIF : 101, OP_VERIF : 101,
OP_VERNOTIF : 102, OP_VERNOTIF : 102,
OP_ELSE : 103, OP_ELSE : 103,
OP_ENDIF : 104, OP_ENDIF : 104,
OP_VERIFY : 105, OP_VERIFY : 105,
OP_RETURN : 106, OP_RETURN : 106,
// stack ops // stack ops
OP_TOALTSTACK : 107, OP_TOALTSTACK : 107,
OP_FROMALTSTACK : 108, OP_FROMALTSTACK : 108,
OP_2DROP : 109, OP_2DROP : 109,
OP_2DUP : 110, OP_2DUP : 110,
OP_3DUP : 111, OP_3DUP : 111,
OP_2OVER : 112, OP_2OVER : 112,
OP_2ROT : 113, OP_2ROT : 113,
OP_2SWAP : 114, OP_2SWAP : 114,
OP_IFDUP : 115, OP_IFDUP : 115,
OP_DEPTH : 116, OP_DEPTH : 116,
OP_DROP : 117, OP_DROP : 117,
OP_DUP : 118, OP_DUP : 118,
OP_NIP : 119, OP_NIP : 119,
OP_OVER : 120, OP_OVER : 120,
OP_PICK : 121, OP_PICK : 121,
OP_ROLL : 122, OP_ROLL : 122,
OP_ROT : 123, OP_ROT : 123,
OP_SWAP : 124, OP_SWAP : 124,
OP_TUCK : 125, OP_TUCK : 125,
// splice ops // splice ops
OP_CAT : 126, OP_CAT : 126,
OP_SUBSTR : 127, OP_SUBSTR : 127,
OP_LEFT : 128, OP_LEFT : 128,
OP_RIGHT : 129, OP_RIGHT : 129,
OP_SIZE : 130, OP_SIZE : 130,
// bit logic // bit logic
OP_INVERT : 131, OP_INVERT : 131,
OP_AND : 132, OP_AND : 132,
OP_OR : 133, OP_OR : 133,
OP_XOR : 134, OP_XOR : 134,
OP_EQUAL : 135, OP_EQUAL : 135,
OP_EQUALVERIFY : 136, OP_EQUALVERIFY : 136,
OP_RESERVED1 : 137, OP_RESERVED1 : 137,
OP_RESERVED2 : 138, OP_RESERVED2 : 138,
// numeric // numeric
OP_1ADD : 139, OP_1ADD : 139,
OP_1SUB : 140, OP_1SUB : 140,
OP_2MUL : 141, OP_2MUL : 141,
OP_2DIV : 142, OP_2DIV : 142,
OP_NEGATE : 143, OP_NEGATE : 143,
OP_ABS : 144, OP_ABS : 144,
OP_NOT : 145, OP_NOT : 145,
OP_0NOTEQUAL : 146, OP_0NOTEQUAL : 146,
OP_ADD : 147, OP_ADD : 147,
OP_SUB : 148, OP_SUB : 148,
OP_MUL : 149, OP_MUL : 149,
OP_DIV : 150, OP_DIV : 150,
OP_MOD : 151, OP_MOD : 151,
OP_LSHIFT : 152, OP_LSHIFT : 152,
OP_RSHIFT : 153, OP_RSHIFT : 153,
OP_BOOLAND : 154, OP_BOOLAND : 154,
OP_BOOLOR : 155, OP_BOOLOR : 155,
OP_NUMEQUAL : 156, OP_NUMEQUAL : 156,
OP_NUMEQUALVERIFY : 157, OP_NUMEQUALVERIFY : 157,
OP_NUMNOTEQUAL : 158, OP_NUMNOTEQUAL : 158,
OP_LESSTHAN : 159, OP_LESSTHAN : 159,
OP_GREATERTHAN : 160, OP_GREATERTHAN : 160,
OP_LESSTHANOREQUAL : 161, OP_LESSTHANOREQUAL : 161,
OP_GREATERTHANOREQUAL : 162, OP_GREATERTHANOREQUAL : 162,
OP_MIN : 163, OP_MIN : 163,
OP_MAX : 164, OP_MAX : 164,
OP_WITHIN : 165, OP_WITHIN : 165,
// crypto // crypto
OP_RIPEMD160 : 166, OP_RIPEMD160 : 166,
OP_SHA1 : 167, OP_SHA1 : 167,
OP_SHA256 : 168, OP_SHA256 : 168,
OP_HASH160 : 169, OP_HASH160 : 169,
OP_HASH256 : 170, OP_HASH256 : 170,
OP_CODESEPARATOR : 171, OP_CODESEPARATOR : 171,
OP_CHECKSIG : 172, OP_CHECKSIG : 172,
OP_CHECKSIGVERIFY : 173, OP_CHECKSIGVERIFY : 173,
OP_CHECKMULTISIG : 174, OP_CHECKMULTISIG : 174,
OP_CHECKMULTISIGVERIFY : 175, OP_CHECKMULTISIGVERIFY : 175,
// expansion // expansion
OP_NOP1 : 176, OP_NOP1 : 176,
OP_NOP2 : 177, OP_NOP2 : 177,
OP_NOP3 : 178, OP_NOP3 : 178,
OP_NOP4 : 179, OP_NOP4 : 179,
OP_NOP5 : 180, OP_NOP5 : 180,
OP_NOP6 : 181, OP_NOP6 : 181,
OP_NOP7 : 182, OP_NOP7 : 182,
OP_NOP8 : 183, OP_NOP8 : 183,
OP_NOP9 : 184, OP_NOP9 : 184,
OP_NOP10 : 185, OP_NOP10 : 185,
// template matching params // template matching params
OP_PUBKEYHASH : 253, OP_PUBKEYHASH : 253,
OP_PUBKEY : 254, OP_PUBKEY : 254,
OP_INVALIDOPCODE : 255 OP_INVALIDOPCODE : 255
}, },
reverseMap: [] reverseMap: []
} }
for(var i in Opcode.map) { for(var i in Opcode.map) {
Opcode.reverseMap[Opcode.map[i]] = i Opcode.reverseMap[Opcode.map[i]] = i
} }
module.exports = Opcode; module.exports = Opcode

View file

@ -1,46 +1,46 @@
var Opcode = require('./opcode'); var Opcode = require('./opcode')
var util = require('./util'); var util = require('./util')
var convert = require('./convert'); var convert = require('./convert')
var Address = require('./address'); var Address = require('./address')
var network = require('./network'); var network = require('./network')
var Script = function(data) { var Script = function(data) {
this.buffer = data || []; this.buffer = data || []
if(!Array.isArray(this.buffer)) { if(!Array.isArray(this.buffer)) {
throw new Error('expect Script to be initialized with Array, but got ' + data) throw new Error('expect Script to be initialized with Array, but got ' + data)
} }
this.parse(); this.parse()
}; }
Script.fromHex = function(data) { Script.fromHex = function(data) {
return new Script(convert.hexToBytes(data)) return new Script(convert.hexToBytes(data))
}; }
Script.fromPubKey = function(str) { Script.fromPubKey = function(str) {
var script = new Script(); var script = new Script()
var s = str.split(' '); var s = str.split(' ')
for (var i in s) { for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])) { if (Opcode.map.hasOwnProperty(s[i])) {
script.writeOp(Opcode.map[s[i]]); script.writeOp(Opcode.map[s[i]])
} else { } else {
script.writeBytes(convert.hexToBytes(s[i])); script.writeBytes(convert.hexToBytes(s[i]))
}
} }
return script; }
}; return script
}
Script.fromScriptSig = function(str) { Script.fromScriptSig = function(str) {
var script = new Script(); var script = new Script()
var s = str.split(' '); var s = str.split(' ')
for (var i in s) { for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])) { if (Opcode.map.hasOwnProperty(s[i])) {
script.writeOp(Opcode.map[s[i]]); script.writeOp(Opcode.map[s[i]])
} else { } else {
script.writeBytes(convert.hexToBytes(s[i])); script.writeBytes(convert.hexToBytes(s[i]))
}
} }
return script; }
}; return script
}
/** /**
* Update the parsed script representation. * 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. * the script buffer manually, you should update the chunks using this method.
*/ */
Script.prototype.parse = function() { Script.prototype.parse = function() {
var self = this; var self = this
this.chunks = []; this.chunks = []
// Cursor // Cursor
var i = 0; var i = 0
// Read n bytes and store result as a chunk // Read n bytes and store result as a chunk
function readChunk(n) { function readChunk(n) {
self.chunks.push(self.buffer.slice(i, i + n)); self.chunks.push(self.buffer.slice(i, i + n))
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 len
var opcode = this.buffer[i++]; if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) {
if (opcode >= 0xF0) { // Read some bytes of data, opcode value is the length of data
// Two byte opcode readChunk(opcode)
opcode = (opcode << 8) | this.buffer[i++]; } else if (opcode == Opcode.map.OP_PUSHDATA1) {
} len = this.buffer[i++]
readChunk(len)
var len; } else if (opcode == Opcode.map.OP_PUSHDATA2) {
if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { len = (this.buffer[i++] << 8) | this.buffer[i++]
// Read some bytes of data, opcode value is the length of data readChunk(len)
readChunk(opcode); } else if (opcode == Opcode.map.OP_PUSHDATA4) {
} else if (opcode == Opcode.map.OP_PUSHDATA1) { len = (this.buffer[i++] << 24) |
len = this.buffer[i++]; (this.buffer[i++] << 16) |
readChunk(len); (this.buffer[i++] << 8) |
} else if (opcode == Opcode.map.OP_PUSHDATA2) { this.buffer[i++]
len = (this.buffer[i++] << 8) | this.buffer[i++]; readChunk(len)
readChunk(len); } else {
} else if (opcode == Opcode.map.OP_PUSHDATA4) { this.chunks.push(opcode)
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. * Compare the script to known templates of scriptPubKey.
@ -115,21 +115,21 @@ Script.prototype.parse = function() {
* Any other script (no template matched). * Any other script (no template matched).
*/ */
Script.prototype.getOutType = function() { Script.prototype.getOutType = function() {
if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL && if (this.chunks[this.chunks.length - 1] == Opcode.map.OP_EQUAL &&
this.chunks[0] == Opcode.map.OP_HASH160 && this.chunks[0] == Opcode.map.OP_HASH160 &&
this.chunks.length == 3) { this.chunks.length == 3) {
// Transfer to M-OF-N // Transfer to M-OF-N
return 'P2SH'; return 'P2SH'
} else if (this.chunks.length == 5 && } else if (this.chunks.length == 5 &&
this.chunks[0] == Opcode.map.OP_DUP && this.chunks[0] == Opcode.map.OP_DUP &&
this.chunks[1] == Opcode.map.OP_HASH160 && this.chunks[1] == Opcode.map.OP_HASH160 &&
this.chunks[3] == Opcode.map.OP_EQUALVERIFY && this.chunks[3] == Opcode.map.OP_EQUALVERIFY &&
this.chunks[4] == Opcode.map.OP_CHECKSIG) { this.chunks[4] == Opcode.map.OP_CHECKSIG) {
// Transfer to Bitcoin address // Transfer to Bitcoin address
return 'Pubkey'; return 'Pubkey'
} else { } else {
return 'Strange'; return 'Strange'
} }
} }
/** /**
@ -137,37 +137,37 @@ Script.prototype.getOutType = function() {
* Assumes strange scripts are P2SH * Assumes strange scripts are P2SH
*/ */
Script.prototype.toScriptHash = function() { Script.prototype.toScriptHash = function() {
var outType = this.getOutType(); var outType = this.getOutType()
if (outType == 'Pubkey') { if (outType == 'Pubkey') {
return this.chunks[2] return this.chunks[2]
} }
if (outType == 'P2SH') {
return util.sha256ripe160(this.buffer)
}
if (outType == 'P2SH') {
return util.sha256ripe160(this.buffer) return util.sha256ripe160(this.buffer)
}
return util.sha256ripe160(this.buffer)
} }
//TODO: support testnet //TODO: support testnet
Script.prototype.getToAddress = function() { Script.prototype.getToAddress = function() {
var outType = this.getOutType(); var outType = this.getOutType()
if (outType == 'Pubkey') { if (outType == 'Pubkey') {
return new Address(this.chunks[2]) return new Address(this.chunks[2])
} }
if (outType == 'P2SH') {
return new Address(this.chunks[1], 5)
}
if (outType == 'P2SH') {
return new Address(this.chunks[1], 5) return new Address(this.chunks[1], 5)
}
return new Address(this.chunks[1], 5)
} }
//TODO: support testnet //TODO: support testnet
Script.prototype.getFromAddress = function(){ 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). * Any other script (no template matched).
*/ */
Script.prototype.getInType = function() { Script.prototype.getInType = function() {
if (this.chunks.length == 1 && if (this.chunks.length == 1 &&
Array.isArray(this.chunks[0])) { Array.isArray(this.chunks[0])) {
// Direct IP to IP transactions only have the signature in their scriptSig. // 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. // TODO: We could also check that the length of the data is correct.
return 'Pubkey'; return 'Pubkey'
} else if (this.chunks.length == 2 && } else if (this.chunks.length == 2 &&
Array.isArray(this.chunks[0]) && Array.isArray(this.chunks[0]) &&
Array.isArray(this.chunks[1])) { Array.isArray(this.chunks[1])) {
return 'Address'; return 'Address'
} else if (this.chunks[0] == Opcode.map.OP_0 && } else if (this.chunks[0] == Opcode.map.OP_0 &&
this.chunks.slice(1).reduce(function(t, chunk, i) { this.chunks.slice(1).reduce(function(t, chunk, i) {
return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1); return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1)
}, true)) { }, true)) {
return 'Multisig'; return 'Multisig'
} else { } else {
return 'Strange'; return 'Strange'
} }
}; }
/** /**
* Returns the affected public key for this input. * Returns the affected public key for this input.
@ -229,17 +229,17 @@ Script.prototype.getInType = function() {
* @deprecated * @deprecated
*/ */
Script.prototype.simpleInPubKey = function() { Script.prototype.simpleInPubKey = function() {
switch (this.getInType()) { switch (this.getInType()) {
case 'Address': case 'Address':
return this.chunks[1]; return this.chunks[1]
case 'Pubkey': case 'Pubkey':
// TODO: Theoretically, we could recover the pubkey from the sig here. // TODO: Theoretically, we could recover the pubkey from the sig here.
// See https://bitcointalk.org/?topic=6430.0 // See https://bitcointalk.org/?topic=6430.0
throw new Error('Script does not contain pubkey'); throw new Error('Script does not contain pubkey')
default: default:
throw new Error('Encountered non-standard scriptSig'); throw new Error('Encountered non-standard scriptSig')
} }
}; }
/** /**
* Returns the affected address hash for this input. * Returns the affected address hash for this input.
@ -257,133 +257,130 @@ Script.prototype.simpleInPubKey = function() {
* This method is useful for indexing transactions. * This method is useful for indexing transactions.
*/ */
Script.prototype.simpleInHash = function() { Script.prototype.simpleInHash = function() {
return util.sha256ripe160(this.simpleInPubKey()); return util.sha256ripe160(this.simpleInPubKey())
}; }
/** /**
* Old name for Script#simpleInHash. * Old name for Script#simpleInHash.
* *
* @deprecated * @deprecated
*/ */
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash; Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash
/** /**
* Add an op code to the script. * Add an op code to the script.
*/ */
Script.prototype.writeOp = function(opcode) { Script.prototype.writeOp = function(opcode) {
this.buffer.push(opcode); this.buffer.push(opcode)
this.chunks.push(opcode); this.chunks.push(opcode)
}; }
/** /**
* Add a data chunk to the script. * Add a data chunk to the script.
*/ */
Script.prototype.writeBytes = function(data) { Script.prototype.writeBytes = function(data) {
if (data.length < Opcode.map.OP_PUSHDATA1) { if (data.length < Opcode.map.OP_PUSHDATA1) {
this.buffer.push(data.length); this.buffer.push(data.length)
} else if (data.length <= 0xff) { } else if (data.length <= 0xff) {
this.buffer.push(Opcode.map.OP_PUSHDATA1); this.buffer.push(Opcode.map.OP_PUSHDATA1)
this.buffer.push(data.length); this.buffer.push(data.length)
} else if (data.length <= 0xffff) { } else if (data.length <= 0xffff) {
this.buffer.push(Opcode.map.OP_PUSHDATA2); this.buffer.push(Opcode.map.OP_PUSHDATA2)
this.buffer.push(data.length & 0xff); this.buffer.push(data.length & 0xff)
this.buffer.push((data.length >>> 8) & 0xff); this.buffer.push((data.length >>> 8) & 0xff)
} else { } else {
this.buffer.push(Opcode.map.OP_PUSHDATA4); this.buffer.push(Opcode.map.OP_PUSHDATA4)
this.buffer.push(data.length & 0xff); this.buffer.push(data.length & 0xff)
this.buffer.push((data.length >>> 8) & 0xff); this.buffer.push((data.length >>> 8) & 0xff)
this.buffer.push((data.length >>> 16) & 0xff); this.buffer.push((data.length >>> 16) & 0xff)
this.buffer.push((data.length >>> 24) & 0xff); this.buffer.push((data.length >>> 24) & 0xff)
} }
this.buffer = this.buffer.concat(data); this.buffer = this.buffer.concat(data)
this.chunks.push(data); this.chunks.push(data)
}; }
/** /**
* Create an output for an address * Create an output for an address
*/ */
Script.createOutputScript = function(address) { Script.createOutputScript = function(address) {
var script = new Script(); var script = new Script()
address = new Address(address); address = new Address(address)
if (address.version == network.mainnet.p2shVersion || address.version == network.testnet.p2shVersion) { if (address.version == network.mainnet.p2shVersion ||
// Standard pay-to-script-hash address.version == network.testnet.p2shVersion) {
script.writeOp(Opcode.map.OP_HASH160); // Standard pay-to-script-hash
script.writeBytes(address.hash); script.writeOp(Opcode.map.OP_HASH160)
script.writeOp(Opcode.map.OP_EQUAL); script.writeBytes(address.hash)
} script.writeOp(Opcode.map.OP_EQUAL)
else { }
// Standard pay-to-pubkey-hash else {
script.writeOp(Opcode.map.OP_DUP); // Standard pay-to-pubkey-hash
script.writeOp(Opcode.map.OP_HASH160); script.writeOp(Opcode.map.OP_DUP)
script.writeBytes(address.hash); script.writeOp(Opcode.map.OP_HASH160)
script.writeOp(Opcode.map.OP_EQUALVERIFY); script.writeBytes(address.hash)
script.writeOp(Opcode.map.OP_CHECKSIG); script.writeOp(Opcode.map.OP_EQUALVERIFY)
} script.writeOp(Opcode.map.OP_CHECKSIG)
return script; }
}; return script
}
/** /**
* Extract pubkeys from a multisig script * Extract pubkeys from a multisig script
*/ */
Script.prototype.extractPubkeys = function() { Script.prototype.extractPubkeys = function() {
return this.chunks.filter(function(chunk) { return this.chunks.filter(function(chunk) {
return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33) return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33)
}); })
} }
/** /**
* Create an m-of-n output script * Create an m-of-n output script
*/ */
Script.createMultiSigOutputScript = function(m, pubkeys) { Script.createMultiSigOutputScript = function(m, pubkeys) {
var script = new Script(); var script = new Script()
pubkeys = pubkeys.sort()
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)
script.writeOp(Opcode.map.OP_CHECKMULTISIG)
script.writeOp(Opcode.map.OP_1 + m - 1); return script
}
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;
};
/** /**
* Create a standard payToPubKeyHash input. * Create a standard payToPubKeyHash input.
*/ */
Script.createInputScript = function(signature, pubKey) { Script.createInputScript = function(signature, pubKey) {
var script = new Script(); var script = new Script()
script.writeBytes(signature); script.writeBytes(signature)
script.writeBytes(pubKey); script.writeBytes(pubKey)
return script; return script
}; }
/** /**
* Create a multisig input * Create a multisig input
*/ */
Script.createMultiSigInputScript = function(signatures, script) { Script.createMultiSigInputScript = function(signatures, script) {
script = new Script(script); script = new Script(script)
var k = script.chunks[0][0]; var k = script.chunks[0][0]
//Not enough sigs //Not enough sigs
if (signatures.length < k) return false; if (signatures.length < k) return false;
var inScript = new Script(); var inScript = new Script()
inScript.writeOp(Opcode.map.OP_0); inScript.writeOp(Opcode.map.OP_0)
signatures.map(function(sig) { signatures.map(function(sig) {
inScript.writeBytes(sig) inScript.writeBytes(sig)
}); })
inScript.writeBytes(script.buffer); inScript.writeBytes(script.buffer)
return inScript; return inScript
} }
Script.prototype.clone = function() { Script.prototype.clone = function() {
return new Script(this.buffer); return new Script(this.buffer)
}; }
module.exports = Script; module.exports = Script

View file

@ -1,43 +1,43 @@
var BigInteger = require('./jsbn/jsbn'); var BigInteger = require('./jsbn/jsbn')
var Script = require('./script'); var Script = require('./script')
var util = require('./util'); var util = require('./util')
var convert = require('./convert'); var convert = require('./convert')
var ECKey = require('./eckey').ECKey; var ECKey = require('./eckey').ECKey
var ECDSA = require('./ecdsa'); var ECDSA = require('./ecdsa')
var Address = require('./address'); var Address = require('./address')
var SHA256 = require('crypto-js/sha256'); var SHA256 = require('crypto-js/sha256')
var Transaction = function (doc) { var Transaction = function (doc) {
if (!(this instanceof Transaction)) { return new Transaction(doc); } if (!(this instanceof Transaction)) { return new Transaction(doc) }
this.version = 1; this.version = 1
this.locktime = 0; this.locktime = 0
this.ins = []; this.ins = []
this.outs = []; this.outs = []
this.defaultSequence = [255, 255, 255, 255]; // 0xFFFFFFFF this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF
if (doc) { if (doc) {
if (typeof doc == "string" || Array.isArray(doc)) { if (typeof doc == "string" || Array.isArray(doc)) {
doc = Transaction.deserialize(doc); doc = Transaction.deserialize(doc)
}
if (doc.hash) this.hash = doc.hash;
if (doc.version) this.version = doc.version;
if (doc.locktime) this.locktime = doc.locktime;
if (doc.ins && doc.ins.length) {
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 (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. * Create a new txin.
@ -52,27 +52,27 @@ var Transaction = function (doc) {
* Note that this method does not sign the created input. * Note that this method does not sign the created input.
*/ */
Transaction.prototype.addInput = function (tx, outIndex) { Transaction.prototype.addInput = function (tx, outIndex) {
if (arguments[0] instanceof TransactionIn) { if (arguments[0] instanceof TransactionIn) {
this.ins.push(arguments[0]); this.ins.push(arguments[0])
} }
else if (arguments[0].length > 65) { else if (arguments[0].length > 65) {
var args = arguments[0].split(':'); var args = arguments[0].split(':')
return this.addInput(args[0], args[1]); return this.addInput(args[0], args[1])
} }
else { else {
var hash = typeof tx === "string" ? tx : tx.hash; var hash = typeof tx === "string" ? tx : tx.hash
hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash; hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash
this.ins.push(new TransactionIn({ this.ins.push(new TransactionIn({
outpoint: { outpoint: {
hash: hash, hash: hash,
index: outIndex index: outIndex
}, },
script: new Script(), script: new Script(),
sequence: this.defaultSequence sequence: this.defaultSequence
})); }))
} }
}; }
/** /**
* Create a new txout. * Create a new txout.
@ -85,22 +85,22 @@ Transaction.prototype.addInput = function (tx, outIndex) {
* *
*/ */
Transaction.prototype.addOutput = function (address, value) { Transaction.prototype.addOutput = function (address, value) {
if (arguments[0] instanceof TransactionOut) { if (arguments[0] instanceof TransactionOut) {
this.outs.push(arguments[0]); this.outs.push(arguments[0])
return; return
} }
if (arguments[0].indexOf(':') >= 0) { if (arguments[0].indexOf(':') >= 0) {
var args = arguments[0].split(':'); var args = arguments[0].split(':')
address = args[0]; address = args[0]
value = parseInt(args[1]); value = parseInt(args[1])
} }
this.outs.push(new TransactionOut({ this.outs.push(new TransactionOut({
value: value, value: value,
script: Script.createOutputScript(address) script: Script.createOutputScript(address)
})); }))
}; }
/** /**
* Serialize this transaction. * Serialize this transaction.
@ -110,48 +110,48 @@ Transaction.prototype.addOutput = function (address, value) {
* be hashed to get the transaction's standard Bitcoin hash. * be hashed to get the transaction's standard Bitcoin hash.
*/ */
Transaction.prototype.serialize = function () { Transaction.prototype.serialize = function () {
var buffer = []; var buffer = []
buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4)); buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
buffer = buffer.concat(convert.numToVarInt(this.ins.length)); buffer = buffer.concat(convert.numToVarInt(this.ins.length))
this.ins.forEach(function(txin) { this.ins.forEach(function(txin) {
// Why do blockchain.info, blockexplorer.com, sx and just about everybody // Why do blockchain.info, blockexplorer.com, sx and just about everybody
// else use little-endian hashes? No idea... // else use little-endian hashes? No idea...
buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse()); 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; var scriptBytes = txin.script.buffer
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)); buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
buffer = buffer.concat(scriptBytes); buffer = buffer.concat(scriptBytes)
buffer = buffer.concat(txin.sequence); 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) { this.outs.forEach(function(txout) {
buffer = buffer.concat(convert.numToBytes(txout.value,8)); buffer = buffer.concat(convert.numToBytes(txout.value,8))
var scriptBytes = txout.script.buffer; var scriptBytes = txout.script.buffer
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length)); buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
buffer = buffer.concat(scriptBytes); 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());
} }
//var OP_CODESEPARATOR = 171; Transaction.prototype.serializeHex = function() {
return convert.bytesToHex(this.serialize())
}
var SIGHASH_ALL = 1; //var OP_CODESEPARATOR = 171
var SIGHASH_NONE = 2;
var SIGHASH_SINGLE = 3; var SIGHASH_ALL = 1
var SIGHASH_ANYONECANPAY = 80; var SIGHASH_NONE = 2
var SIGHASH_SINGLE = 3
var SIGHASH_ANYONECANPAY = 80
/** /**
* Hash transaction for signing a specific input. * Hash transaction for signing a specific input.
@ -162,34 +162,34 @@ var SIGHASH_ANYONECANPAY = 80;
* used to sign the transaction input in question. * used to sign the transaction input in question.
*/ */
Transaction.prototype.hashTransactionForSignature = 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, // In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible // or an extra one at the end, this prevents all those possible
// incompatibilities. // incompatibilities.
/*scriptCode = scriptCode.filter(function (val) { /*scriptCode = scriptCode.filter(function (val) {
return val !== OP_CODESEPARATOR; return val !== OP_CODESEPARATOR
});*/ });*/
// Blank out other inputs' signatures // Blank out other inputs' signatures
txTmp.ins.forEach(function(txin) { 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 // Blank out some of the outputs
if ((hashType & 0x1f) == SIGHASH_NONE) { if ((hashType & 0x1f) == SIGHASH_NONE) {
txTmp.outs = []; txTmp.outs = []
// Let the others update at will // Let the others update at will
txTmp.ins.forEach(function(txin, i) { txTmp.ins.forEach(function(txin, i) {
if (i != inIndex) { if (i != inIndex) {
txTmp.ins[i].sequence = 0; txTmp.ins[i].sequence = 0
} }
}); })
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) { } else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
// TODO: Implement // TODO: Implement
@ -197,16 +197,16 @@ function (connectedScript, inIndex, hashType)
// Blank out other inputs completely, not recommended for open transactions // Blank out other inputs completely, not recommended for open transactions
if (hashType & SIGHASH_ANYONECANPAY) { 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 = buffer.concat(convert.numToBytes(parseInt(hashType), 4))
buffer = convert.bytesToWordArray(buffer); buffer = convert.bytesToWordArray(buffer)
return convert.wordArrayToBytes(SHA256(SHA256(buffer))); return convert.wordArrayToBytes(SHA256(SHA256(buffer)))
}; }
/** /**
* Calculate and return the transaction's hash. * Calculate and return the transaction's hash.
@ -215,166 +215,157 @@ function (connectedScript, inIndex, hashType)
*/ */
Transaction.prototype.getHash = function () Transaction.prototype.getHash = function ()
{ {
var buffer = convert.bytesToWordArray(this.serialize()); var buffer = convert.bytesToWordArray(this.serialize())
return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse(); return convert.wordArrayToBytes(SHA256(SHA256(buffer))).reverse()
}; }
/**
* Create a copy of this transaction object.
*/
Transaction.prototype.clone = function () Transaction.prototype.clone = function ()
{ {
var newTx = new Transaction(); var newTx = new Transaction()
newTx.version = this.version; newTx.version = this.version
newTx.locktime = this.locktime; newTx.locktime = this.locktime
this.ins.forEach(function(txin) { this.ins.forEach(function(txin) {
newTx.addInput(txin.clone()); newTx.addInput(txin.clone())
}); })
this.outs.forEach(function(txout) { this.outs.forEach(function(txout) {
newTx.addOutput(txout.clone()); newTx.addOutput(txout.clone())
}); })
return newTx; return newTx
}; }
/**
* Converts a serialized transaction into a transaction object
*/
Transaction.deserialize = function(buffer) { Transaction.deserialize = function(buffer) {
if (typeof buffer == "string") { if (typeof buffer == "string") {
buffer = convert.hexToBytes(buffer) buffer = convert.hexToBytes(buffer)
} }
var pos = 0; var pos = 0
var readAsInt = function(bytes) { var readAsInt = function(bytes) {
if (bytes === 0) return 0; if (bytes === 0) return 0;
pos++; pos++;
return buffer[pos-1] + readAsInt(bytes-1) * 256; return buffer[pos-1] + readAsInt(bytes-1) * 256
} }
var readVarInt = function() { var readVarInt = function() {
var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read
var result = convert.varIntToNum(bytes) var result = convert.varIntToNum(bytes)
pos += result.bytes.length pos += result.bytes.length
return result.number return result.number
} }
var readBytes = function(bytes) { var readBytes = function(bytes) {
pos += bytes; pos += bytes
return buffer.slice(pos - bytes, pos); return buffer.slice(pos - bytes, pos)
} }
var readVarString = function() { var readVarString = function() {
var size = readVarInt(); var size = readVarInt()
return readBytes(size); return readBytes(size)
} }
var obj = { var obj = {
ins: [], ins: [],
outs: [] outs: []
} }
obj.version = readAsInt(4); obj.version = readAsInt(4)
var ins = readVarInt(); var ins = readVarInt()
var i; var i
for (i = 0; i < ins; i++) { for (i = 0; i < ins; i++) {
obj.ins.push({ obj.ins.push({
outpoint: { outpoint: {
hash: convert.bytesToHex(readBytes(32).reverse()), hash: convert.bytesToHex(readBytes(32).reverse()),
index: readAsInt(4) index: readAsInt(4)
}, },
script: new Script(readVarString()), script: new Script(readVarString()),
sequence: readBytes(4) sequence: readBytes(4)
}); })
} }
var outs = readVarInt(); var outs = readVarInt()
for (i = 0; i < outs; i++) { for (i = 0; i < outs; i++) {
obj.outs.push({ obj.outs.push({
value: convert.bytesToNum(readBytes(8)), value: convert.bytesToNum(readBytes(8)),
script: new Script(readVarString()) 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 * Signs a standard output at some index with the given key
*/ */
Transaction.prototype.sign = function(index, key, type) { Transaction.prototype.sign = function(index, key, type) {
type = type || SIGHASH_ALL; type = type || SIGHASH_ALL
key = new ECKey(key); key = new ECKey(key)
// TODO: getPub is slow, sha256ripe160 probably is too. // TODO: getPub is slow, sha256ripe160 probably is too.
// This could be sped up a lot by providing these as inputs. // This could be sped up a lot by providing these as inputs.
var pub = key.getPub().toBytes(), var pub = key.getPub().toBytes(),
hash160 = util.sha256ripe160(pub), hash160 = util.sha256ripe160(pub),
script = Script.createOutputScript(new Address(hash160)), script = Script.createOutputScript(new Address(hash160)),
hash = this.hashTransactionForSignature(script, index, type), hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type]); sig = key.sign(hash).concat([type])
this.ins[index].script = Script.createInputScript(sig, pub); this.ins[index].script = Script.createInputScript(sig, pub)
} }
// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...] // Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Transaction.prototype.signWithKeys = function(keys, outputs, type) { Transaction.prototype.signWithKeys = function(keys, outputs, type) {
type = type || SIGHASH_ALL; type = type || SIGHASH_ALL
var addrdata = keys.map(function(key) { var addrdata = keys.map(function(key) {
key = new ECKey(key); key = new ECKey(key)
return { return {
key: key, key: key,
address: key.getAddress().toString() address: key.getAddress().toString()
}
});
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];
if (!histItem) continue;
var thisInputAddrdata = addrdata.filter(function(a) {
return a.address == histItem.address;
});
if (thisInputAddrdata.length === 0) continue;
this.sign(i,thisInputAddrdata[0].key);
} }
})
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]
if (!histItem) continue;
var thisInputAddrdata = addrdata.filter(function(a) {
return a.address == histItem.address
})
if (thisInputAddrdata.length === 0) continue;
this.sign(i,thisInputAddrdata[0].key)
}
} }
/** /**
* Signs a P2SH output at some index with the given key * Signs a P2SH output at some index with the given key
*/ */
Transaction.prototype.p2shsign = function(index, script, key, type) { Transaction.prototype.p2shsign = function(index, script, key, type) {
script = new Script(script); script = new Script(script)
key = new ECKey(key); key = new ECKey(key)
type = type || SIGHASH_ALL; type = type || SIGHASH_ALL
var hash = this.hashTransactionForSignature(script, index, type), var hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type]); sig = key.sign(hash).concat([type])
return sig; return sig
} }
Transaction.prototype.multisign = Transaction.prototype.p2shsign; Transaction.prototype.multisign = Transaction.prototype.p2shsign
Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) { Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) {
this.ins[index].script = Script.createMultiSigInputScript(sigs, script); this.ins[index].script = Script.createMultiSigInputScript(sigs, script)
} }
Transaction.prototype.validateSig = function(index, script, sig, pub) { Transaction.prototype.validateSig = function(index, script, sig, pub) {
script = new Script(script); script = new Script(script)
var hash = this.hashTransactionForSignature(script,index,1); var hash = this.hashTransactionForSignature(script,index,1)
return ECDSA.verify(hash, convert.coerceToBytes(sig), return ECDSA.verify(hash, convert.coerceToBytes(sig),
convert.coerceToBytes(pub)); convert.coerceToBytes(pub))
} }
Transaction.feePerKb = 20000 Transaction.feePerKb = 20000
@ -383,66 +374,68 @@ Transaction.prototype.estimateFee = function(feePerKb){
var outSize = 34 var outSize = 34
var fixedPadding = 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 var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding
return feePerKb * Math.ceil(size / 1000) return feePerKb * Math.ceil(size / 1000)
} }
var TransactionIn = function (data) { var TransactionIn = function (data) {
if (typeof data == "string") if (typeof data == "string") {
this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] } this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
else if (data.outpoint) } else if (data.outpoint) {
this.outpoint = data.outpoint this.outpoint = data.outpoint
else } else {
this.outpoint = { hash: data.hash, index: data.index } this.outpoint = { hash: data.hash, index: data.index }
}
if (data.scriptSig) if (data.scriptSig) {
this.script = Script.fromScriptSig(data.scriptSig) this.script = Script.fromScriptSig(data.scriptSig)
else if (data.script) } else if (data.script) {
this.script = data.script this.script = data.script
else } else {
this.script = new Script(data.script) this.script = new Script(data.script)
}
this.sequence = data.sequence || this.defaultSequence this.sequence = data.sequence || this.defaultSequence
}; }
TransactionIn.prototype.clone = function () { TransactionIn.prototype.clone = function () {
return new TransactionIn({ return new TransactionIn({
outpoint: { outpoint: {
hash: this.outpoint.hash, hash: this.outpoint.hash,
index: this.outpoint.index index: this.outpoint.index
}, },
script: this.script.clone(), script: this.script.clone(),
sequence: this.sequence sequence: this.sequence
}); })
}; }
var TransactionOut = function (data) { var TransactionOut = function (data) {
this.script = this.script =
data.script instanceof Script ? data.script.clone() data.script instanceof Script ? data.script.clone()
: Array.isArray(data.script) ? new Script(data.script) : Array.isArray(data.script) ? new Script(data.script)
: typeof data.script == "string" ? new Script(convert.hexToBytes(data.script)) : typeof data.script == "string" ? new Script(convert.hexToBytes(data.script))
: data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey) : data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey)
: data.address ? Script.createOutputScript(data.address) : data.address ? Script.createOutputScript(data.address)
: new Script(); : new Script()
if (this.script.buffer.length > 0) this.address = this.script.getToAddress(); if (this.script.buffer.length > 0) this.address = this.script.getToAddress();
this.value = this.value =
Array.isArray(data.value) ? convert.bytesToNum(data.value) Array.isArray(data.value) ? convert.bytesToNum(data.value)
: "string" == typeof data.value ? parseInt(data.value) : "string" == typeof data.value ? parseInt(data.value)
: data.value instanceof BigInteger ? parseInt(data.value.toString()) : data.value instanceof BigInteger ? parseInt(data.value.toString())
: data.value; : data.value
}; }
TransactionOut.prototype.clone = function() { TransactionOut.prototype.clone = function() {
var newTxout = new TransactionOut({ var newTxout = new TransactionOut({
script: this.script.clone(), script: this.script.clone(),
value: this.value value: this.value
}); })
return newTxout; return newTxout
}; }
TransactionOut.prototype.scriptPubKey = function() { TransactionOut.prototype.scriptPubKey = function() {
return convert.bytesToHex(this.script.buffer) return convert.bytesToHex(this.script.buffer)

View file

@ -3,21 +3,11 @@ var Crypto = require('crypto-js')
var RIPEMD160 = Crypto.RIPEMD160 var RIPEMD160 = Crypto.RIPEMD160
var SHA256 = Crypto.SHA256 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) { exports.sha256ripe160 = function (data) {
var wordArray = RIPEMD160(SHA256(convert.bytesToWordArray(data))) var wordArray = RIPEMD160(SHA256(convert.bytesToWordArray(data)))
return convert.wordArrayToBytes(wordArray) return convert.wordArrayToBytes(wordArray)
} }
/**
* Convenience method for throw new Error('some message'), e.g.
* error('something went wrong')
*/
exports.error = function (msg) { exports.error = function (msg) {
throw new Error(msg) throw new Error(msg)
} }

View file

@ -1,311 +1,311 @@
var convert = require('./convert'); var convert = require('./convert')
var Transaction = require('./transaction').Transaction; var Transaction = require('./transaction').Transaction
var HDNode = require('./hdwallet.js') var HDNode = require('./hdwallet.js')
var rng = require('secure-random'); var rng = require('secure-random')
var Wallet = function (seed, options) { var Wallet = function (seed, options) {
if (!(this instanceof Wallet)) { return new Wallet(seed, options); } if (!(this instanceof Wallet)) { return new Wallet(seed, options); }
var options = options || {} var options = options || {}
var network = options.network || 'mainnet' var network = options.network || 'mainnet'
// Stored in a closure to make accidental serialization less likely // Stored in a closure to make accidental serialization less likely
var masterkey = null; var masterkey = null
var me = this; var me = this
var accountZero = null; var accountZero = null
var internalAccount = null; var internalAccount = null
var externalAccount = null; var externalAccount = null
// Addresses // Addresses
this.addresses = []; this.addresses = []
this.changeAddresses = []; this.changeAddresses = []
// Transaction output data // Transaction output data
this.outputs = {}; this.outputs = {}
// Make a new master key // Make a new master key
this.newMasterKey = function(seed, network) { this.newMasterKey = function(seed, network) {
if (!seed) seed= rng(32, { array: true }) if (!seed) seed = rng(32, { array: true });
masterkey = new HDNode(seed, network); masterkey = new HDNode(seed, network)
// HD first-level child derivation method should be private // HD first-level child derivation method should be private
// See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254 // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
accountZero = masterkey.derivePrivate(0) accountZero = masterkey.derivePrivate(0)
externalAccount = accountZero.derive(0) externalAccount = accountZero.derive(0)
internalAccount = accountZero.derive(1) internalAccount = accountZero.derive(1)
me.addresses = []; me.addresses = []
me.changeAddresses = []; me.changeAddresses = []
me.outputs = {}; me.outputs = {}
} }
this.newMasterKey(seed, network) this.newMasterKey(seed, network)
this.generateAddress = function() { this.generateAddress = function() {
var key = externalAccount.derive(this.addresses.length) var key = externalAccount.derive(this.addresses.length)
this.addresses.push(key.getAddress().toString()) this.addresses.push(key.getAddress().toString())
return this.addresses[this.addresses.length - 1] 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.generateChangeAddress = function() { return utxo
var key = internalAccount.derive(this.changeAddresses.length) }
this.changeAddresses.push(key.getAddress().toString())
return this.changeAddresses[this.changeAddresses.length - 1] 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) })
}
}
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
}
}
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
}
}
function validateUnspentOutput(uo) {
var missingField
if(isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)){
missingField = "hash(or hashLittleEndian)"
} }
this.getBalance = function() { var requiredKeys = ['outputIndex', 'address', 'value']
return this.getUnspentOutputs().reduce(function(memo, output){ requiredKeys.forEach(function(key){
return memo + output.value if(isNullOrUndefined(uo[key])){
}, 0) missingField = key
}
this.getUnspentOutputs = function() {
var utxo = []
for(var key in this.outputs){
var output = this.outputs[key]
if(!output.spend) utxo.push(outputToUnspentOutput(output))
} }
})
return utxo 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(' '))
} }
}
this.setUnspentOutputs = function(utxo) { function isNullOrUndefined(value){
var outputs = {} return value == undefined
}
utxo.forEach(function(uo){ this.processTx = function(tx) {
validateUnspentOutput(uo) var txhash = convert.bytesToHex(tx.getHash())
var o = unspentOutputToOutput(uo)
outputs[o.receive] = o
})
this.outputs = outputs tx.outs.forEach(function(txOut, i){
} var address = txOut.address.toString()
if (isMyAddress(address)) {
this.setUnspentOutputsAsync = function(utxo, callback) { var output = txhash+':'+i
var error = null me.outputs[output] = {
try { receive: output,
this.setUnspentOutputs(utxo) value: txOut.value,
} catch(err) { address: address,
error = err
} finally {
process.nextTick(function(){ callback(error) })
}
}
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
}
}
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
}
}
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
} }
})
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){ tx.ins.forEach(function(txIn, i){
return value == undefined var op = txIn.outpoint
} var o = me.outputs[op.hash+':'+op.index]
if (o) {
this.processTx = function(tx) { o.spend = txhash+':'+i
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<utxo.length; i++){
var output = utxo[i]
tx.addInput(output.receive)
totalInValue += output.value
if(totalInValue < value) continue;
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
if(totalInValue < value + fee) continue;
var change = totalInValue - value - fee
if(change > 0 && !isDust(change)) {
tx.addOutput(getChangeAddress(), change)
}
break;
}
checkInsufficientFund(totalInValue, value, fee)
this.sign(tx)
return tx
}
this.createTxAsync = function(to, value, fixedFee, callback){
if(fixedFee instanceof Function) {
callback = fixedFee
fixedFee = undefined
} }
var tx = null })
var error = null }
try { this.createTx = function(to, value, fixedFee) {
tx = this.createTx(to, value, fixedFee) checkDust(value)
} catch(err) {
error = err var tx = new Transaction()
} finally { tx.addOutput(to, value)
process.nextTick(function(){ callback(error, tx) })
var utxo = getCandidateOutputs(value)
var totalInValue = 0
for(var i=0; i<utxo.length; i++){
var output = utxo[i]
tx.addInput(output.receive)
totalInValue += output.value
if(totalInValue < value) continue
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
if(totalInValue < value + fee) continue
var change = totalInValue - value - fee
if(change > 0 && !isDust(change)) {
tx.addOutput(getChangeAddress(), change)
} }
break
} }
this.dustThreshold = 5430 checkInsufficientFund(totalInValue, value, fee)
function isDust(amount) {
return amount <= me.dustThreshold this.sign(tx)
return tx
}
this.createTxAsync = function(to, value, fixedFee, callback){
if(fixedFee instanceof Function) {
callback = fixedFee
fixedFee = undefined
}
var tx = null
var error = null
try {
tx = this.createTx(to, value, fixedFee)
} catch(err) {
error = err
} finally {
process.nextTick(function(){ callback(error, tx) })
}
}
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")
}
}
function getCandidateOutputs(value){
var unspent = []
for (var key in me.outputs){
var output = me.outputs[key]
if(!output.spend) unspent.push(output)
} }
function checkDust(value){ var sortByValueDesc = unspent.sort(function(o1, o2){
if (isNullOrUndefined(value) || isDust(value)) { return o2.value - o1.value
throw new Error("Value must be above dust threshold") })
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]
}
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.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 getCandidateOutputs(value){ function isReceiveAddress(address){
var unspent = [] return me.addresses.indexOf(address) > -1
for (var key in me.outputs){ }
var output = me.outputs[key]
if(!output.spend) unspent.push(output)
}
var sortByValueDesc = unspent.sort(function(o1, o2){ function isChangeAddress(address){
return o2.value - o1.value return me.changeAddresses.indexOf(address) > -1
}) }
return sortByValueDesc; function isMyAddress(address) {
} return isReceiveAddress(address) || isChangeAddress(address)
}
}
function estimateFeePadChangeOutput(tx){ module.exports = Wallet
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]
}
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.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 isChangeAddress(address){
return me.changeAddresses.indexOf(address) > -1
}
function isMyAddress(address) {
return isReceiveAddress(address) || isChangeAddress(address)
}
};
module.exports = Wallet;

View file

@ -1,4 +1,3 @@
/* global describe, it */
var assert = require('assert') var assert = require('assert')
var Address = require('../src/address.js') var Address = require('../src/address.js')
var network = require('../src/network.js') var network = require('../src/network.js')
@ -7,105 +6,105 @@ var mainnet = network.mainnet.addressVersion
var testnet = network.testnet.addressVersion var testnet = network.testnet.addressVersion
describe('Address', function() { describe('Address', function() {
var testnetAddress, mainnetAddress; var testnetAddress, mainnetAddress
var testnetP2shAddress, mainnetP2shAddress var testnetP2shAddress, mainnetP2shAddress
beforeEach(function(){ beforeEach(function(){
testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef' testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'
mainnetAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' mainnetAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7' testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7'
mainnetP2shAddress = '3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt' 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 hex', function() {
it('works with Address object', function() { var addr = new Address('13483382d3c3d43fc9d7b52e652b6bbb70e8b667')
var addr = new Address(new Address('mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3', network.testnet.addressVersion)) assert.equal(addr.toString(), '12kxLGqrnnchwN9bHHNV2fWDtJGwxKTcJS')
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)
})
}) })
describe('getVersion', function() { it('throws error for invalid or unrecognized input', function() {
it('returns the proper address version', function() { assert.throws(function() {
assert.equal(Address.getVersion('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'), network.mainnet.addressVersion) new Address('beepboopbeepboopbeepboopbeepboopbeepboopbeep')
assert.equal(Address.getVersion('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef'), network.testnet.addressVersion) }, Error)
})
}) })
describe('toString', function() { it('works for byte input', function() {
it('defaults to base58', function() { var hash = base58.checkDecode('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa')
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ'; var addr = new Address(hash)
assert.equal((new Address(addr)).toString(), addr); 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)
}) })
describe('Constructor', function(){ it('fails for bad input', function() {
it('resolves version correctly', function(){ assert.throws(function() {
assert.equal((new Address(testnetAddress)).version, testnet) new Address('foo')
assert.equal((new Address(mainnetAddress)).version, mainnet) }, Error)
assert.equal((new Address(testnetP2shAddress)).version, network.testnet.p2shVersion) })
assert.equal((new Address(mainnetP2shAddress)).version, network.mainnet.p2shVersion) })
})
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('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)
}) })
describe('validate', function() { it('does not validate illegal examples', function() {
it('validates known good addresses', function() { function invalid(addr) {
function validate(addr, expectedVersion) { assert.ok(!Address.validate(addr))
assert.ok(Address.validate(addr)); }
}
validate(testnetAddress); invalid(''); //empty should be invalid
validate(mainnetAddress); invalid('%%@'); // invalid base58 string
validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP'); invalid('1A1zP1eP5QGefi2DzPTf2L5SLmv7DivfNz'); // bad address (doesn't checksum)
validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y'); invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); // bad address (doesn't checksum)
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)
})
}) })
})
}) })

View file

@ -1,49 +1,48 @@
/* global describe, it */ var assert = require('assert')
var assert = require('assert'); var base58 = require('../').base58
var base58 = require('../').base58; var convert = require('../').convert
var convert = require('../').convert;
describe('base58', function() { describe('base58', function() {
describe('decode', function() { describe('decode', function() {
it('validates known examples', function() { it('validates known examples', function() {
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'
assert.deepEqual(base58.decode(enc), convert.hexToBytes(hex)); assert.deepEqual(base58.decode(enc), convert.hexToBytes(hex))
})
}) })
})
describe('encode', function() { describe('encode', function() {
it('handles known examples', function() { it('handles known examples', function() {
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'; var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'; var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'
assert.equal(base58.encode(convert.hexToBytes(hex)), enc); assert.equal(base58.encode(convert.hexToBytes(hex)), enc)
})
}) })
})
describe('checkEncode', function() { describe('checkEncode', function() {
it('handles known examples', function() { it('handles known examples', function() {
var input = [ var input = [
171, 210, 178, 125, 2, 16, 86, 184, 248, 88, 235, 171, 210, 178, 125, 2, 16, 86, 184, 248, 88, 235,
163, 244, 160, 83, 156, 184, 186, 45, 167, 169, 164, 163, 244, 160, 83, 156, 184, 186, 45, 167, 169, 164,
67, 125, 163, 89, 106, 243, 207, 193, 149, 206 67, 125, 163, 89, 106, 243, 207, 193, 149, 206
] ]
var vbyte = 239 var vbyte = 239
assert.equal(base58.checkEncode(input, vbyte), assert.equal(base58.checkEncode(input, vbyte),
'92tb9mjz6q9eKZjYvLsgk87kPrMoh7BGRumSzPeUGhmigtsfrbP'); '92tb9mjz6q9eKZjYvLsgk87kPrMoh7BGRumSzPeUGhmigtsfrbP')
})
}) })
})
describe('checkDecode', function() { describe('checkDecode', function() {
it('handles known examples', function() { it('handles known examples', function() {
var input = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' var input = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
var expected = [ var expected = [
98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83,
153, 235, 246, 240, 251, 80, 235, 184, 143, 24 153, 235, 246, 240, 251, 80, 235, 184, 143, 24
]; ]
expected.version = 0 expected.version = 0
assert.deepEqual(base58.checkDecode(input), expected); assert.deepEqual(base58.checkDecode(input), expected)
})
}) })
})
}) })

View file

@ -1,144 +1,143 @@
/* global describe, it */ var assert = require('assert')
var assert = require('assert'); var convert = require('../src/convert.js')
var convert = require('../src/convert.js');
describe('convert', function() { describe('convert', function() {
describe('bytesToHex', function() { describe('bytesToHex', function() {
it('handles example 1', function() { it('handles example 1', function() {
assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff'); 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])
})
})
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('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('hexToBytes', function() { describe('bytesToWords', function() {
it('handles example 1', function() { it('works', function() {
assert.deepEqual(convert.hexToBytes('000102ff'), [0, 1, 2, 255]); assert.deepEqual(convert.bytesToWordArray(bytes), wordArray)
})
})
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('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('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() { describe('bytesToWords', function() {
it('works on valid input', function() { it('works', function() {
var data = [ assert.deepEqual(convert.wordArrayToBytes(wordArray), bytes)
[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('reverseEndian', function() { describe('numToVarInt', function() {
it('works', function() { describe('works', function() {
var bigEndian = "6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7" var data = [
var littleEdian = "c7b97b432e7b3a8e31d1a4a0fa326c8fb002d19f2da5fc4feaf9c43a2762406a" 0, 128, 252, // 8-bit
assert.deepEqual(convert.reverseEndian(bigEndian), littleEdian) 256, 512, 1024, // 16-bit
assert.deepEqual(convert.reverseEndian(littleEdian), bigEndian) 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])
}
})
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)
})
})
}) })

View file

@ -1,23 +1,22 @@
/* global describe, it */ var assert = require('assert')
var assert = require('assert'); var sec = require('../src/jsbn/sec')
var sec = require('../src/jsbn/sec'); var ecdsa = require('../').ecdsa
var ecdsa = require('../').ecdsa;
var ecparams = sec('secp256k1'); var ecparams = sec('secp256k1')
describe('ecdsa', function() { describe('ecdsa', function() {
it('handles point multiplication', function() { it('handles point multiplication', function() {
var G = ecparams.getG(); var G = ecparams.getG()
var n = ecparams.getN(); 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 k = ecdsa.getBigRandom(n)
var P = G.multiply(k); var P = G.multiply(k)
assert.ok(!P.isInfinity(), "kG is not infinite"); assert.ok(!P.isInfinity(), "kG is not infinite")
assert.ok(P.isOnCurve(), "kG on curve"); assert.ok(P.isOnCurve(), "kG on curve")
assert.ok(P.multiply(n).isInfinity(), "kGn is infinite"); 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")
}) })
}) })

View file

@ -1,190 +1,187 @@
/* global describe, it */ var assert = require('assert')
var assert = require('assert'); var ECKey = require('../src/eckey.js').ECKey
var ECPubKey = require('../src/eckey.js').ECPubKey
var ECKey = require('../src/eckey.js').ECKey; var convert = require('../src/convert.js')
var ECPubKey = require('../src/eckey.js').ECPubKey; var bytesToHex = convert.bytesToHex
var hexToBytes = convert.hexToBytes
var convert = require('../src/convert.js'); var Address = require('../src/address')
var bytesToHex = convert.bytesToHex;
var hexToBytes = convert.hexToBytes;
var Address = require('../src/address');
var Network = require('../src/network') var Network = require('../src/network')
var testnet = Network.testnet.addressVersion var testnet = Network.testnet.addressVersion
describe('ECKey', function() { describe('ECKey', function() {
describe('constructor', function() { describe('constructor', function() {
it('parses hex', function() { it('parses hex', function() {
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'; var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' + var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' +
'22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'; '22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'
var key = new ECKey(priv); var key = new ECKey(priv)
assert.equal(key.getPub().toHex(), pub); assert.equal(key.getPub().toHex(), pub)
assert.equal(key.compressed, false); 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('toAddress', function() { it('parses base64', function() {
var privkeys = [ var priv = 'VYdB+iv47y5FaUVIPdQInkgATrABeuD1lACUoM4x7tU='
'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458', var pub = '042f43c16c08849fed20a35bb7b1947bbf0923c52d613ee13b5c665a1e10d24b2' +
'1111111111111111111111111111111111111111111111111111111111111111', '8be909a70f5f87c1adb79fbcd1b3f17d20aa91c04fc355112dba2ce9b1cbf013b'
'18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725' var key = new ECKey(priv)
];
// compressed pubkeys assert.equal(key.getPub().toHex(), pub)
var cpubkeys = [ assert.equal(key.compressed, false)
'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))
})
}) })
describe('output of ECPubKey', function() { it('parses WIF', function() {
var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992' var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
var hpub = '044b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea81199283fbec990dad6fb98f93f712d50cb874dd717de6a184158d63886dda3090f566' var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' +
'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'
var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'
var key = new ECKey(priv)
it('using toHex should support compression', function() { assert.equal(key.compressed, false)
var pub = new ECPubKey(hpub) assert.equal(key.getPub().toHex(), pub)
assert.equal(key.getAddress().toString(), addr)
assert.equal(pub.toHex(true), hcpub)
assert.equal(pub.toHex(false), 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)
})
}) })
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('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))
})
})
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)
assert.equal(bytesToHex(pub.toBytes(true)), hcpub)
assert.equal(bytesToHex(pub.toBytes(false)), hpub)
})
})
}) })

View file

@ -1,4 +1,3 @@
/* global describe, it */
var HDWallet = require('../src/hdwallet.js') var HDWallet = require('../src/hdwallet.js')
var assert = require('assert') var assert = require('assert')
var convert = require('../src/convert.js') var convert = require('../src/convert.js')
@ -9,251 +8,251 @@ var testnet = Network.testnet.addressVersion
var b2h = convert.bytesToHex var b2h = convert.bytesToHex
describe('HDWallet', function() { describe('HDWallet', function() {
describe('toBase58', function() { describe('toBase58', function() {
it('reproduces input', function() { it('reproduces input', function() {
var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5' var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5'
, output = HDWallet.fromBase58(input).toBase58(false) var output = HDWallet.fromBase58(input).toBase58(false)
assert.equal(output, input) assert.equal(output, input)
input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334' input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334'
output = HDWallet.fromBase58(input).toBase58(true) output = HDWallet.fromBase58(input).toBase58(true)
assert.equal(output, input) 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()
})
}) })
describe('constructor & seed deserialization', function() { it('fails with priv=true when theres no private key', function() {
var expectedPrivateKey = '0fd71c652e847ba7ea7956e3cf3fc0a0985871846b1b2c23b9c6a29a38cee86001'; var hd = HDWallet.fromBase58('xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon')
var seed = [ try {
99, 114, 97, 122, 121, 32, 104, 111, 114, 115, 101, 32, 98, hd.toBase58(true)
97, 116, 116, 101, 114, 121, 32, 115, 116, 97, 112, 108, 101 } catch(e) {
]; assert(e.message.match(/private key/i))
return
}
assert.fail()
})
})
it('creates from binary seed', function() { describe('constructor & seed deserialization', function() {
var hd = new HDWallet(seed) 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
]
assert.equal(hd.priv.toHex(), expectedPrivateKey) it('creates from binary seed', function() {
assert(hd.pub) var hd = new HDWallet(seed)
})
describe('fromSeedHex', function() { assert.equal(hd.priv.toHex(), expectedPrivateKey)
it('creates from hex seed', function() { assert(hd.pub)
var hd = HDWallet.fromSeedHex(b2h(seed))
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))
assert.equal(hd.priv.toHex(), expectedPrivateKey)
assert(hd.pub)
})
})
}) })
describe('Test vectors', function() { describe('fromSeedHex', function() {
it('Test vector 1', function() { it('creates from hex seed', function() {
var hd = HDWallet.fromSeedHex('000102030405060708090a0b0c0d0e0f') var hd = HDWallet.fromSeedHex(b2h(seed))
// m assert.equal(hd.priv.toHex(), expectedPrivateKey)
assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811') assert(hd.pub)
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'/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'/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')
})
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/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'/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')
// 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() { describe('fromSeedString', function() {
it('ensures that a mainnet Wallet generates mainnet addresses', function() { it('creates from string seed', function() {
var wallet = new HDWallet('foobar', 'mainnet') var hd = HDWallet.fromSeedString(convert.bytesToString(seed))
assert.equal(wallet.getAddress().toString(), '1JNymexJHEr5u1BndiChMStFkCgPm4EQ6o');
})
it('ensures that a testnet Wallet generates testnet addresses', function() { assert.equal(hd.priv.toHex(), expectedPrivateKey)
var wallet = new HDWallet('foobar', 'testnet') assert(hd.pub)
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") })
})
}) })
})
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/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/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/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')
// 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/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/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')
})
})
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('throws an excption when unknown network type is passed in', function() {
assert.throws(function() { new HDWallet("foobar", "doge") })
})
})
}) })

View file

@ -1,37 +1,36 @@
/* global describe, it */ var assert = require('assert')
var assert = require('assert');
var BigInteger = require('../src/jsbn/jsbn.js') var BigInteger = require('../src/jsbn/jsbn.js')
var bytesToHex = require('../src/convert.js').bytesToHex; var bytesToHex = require('../src/convert.js').bytesToHex
var secureRandom = require('secure-random'); var secureRandom = require('secure-random')
describe('BigInteger', function() { describe('BigInteger', function() {
describe('toByteArraySigned', function() { describe('toByteArraySigned', function() {
it('handles examples', function() { it('handles examples', function() {
function hex(num) { function hex(num) {
var bytes = BigInteger.valueOf(num).toByteArraySigned(); var bytes = BigInteger.valueOf(num).toByteArraySigned()
var h = bytesToHex(bytes); var h = bytesToHex(bytes)
return '0x' + h; return '0x' + h
} }
assert.equal(hex( 0), '0x'); assert.equal(hex( 0), '0x')
assert.equal(hex( 1), '0x01'); assert.equal(hex( 1), '0x01')
assert.equal(hex(-1), '0x81'); assert.equal(hex(-1), '0x81')
assert.equal(hex( 127), '0x7f'); assert.equal(hex( 127), '0x7f')
assert.equal(hex(-127), '0xff'); assert.equal(hex(-127), '0xff')
assert.equal(hex( 255), '0x00ff'); assert.equal(hex( 255), '0x00ff')
assert.equal(hex(-255), '0x80ff'); assert.equal(hex(-255), '0x80ff')
assert.equal(hex( 16300), '0x3fac'); assert.equal(hex( 16300), '0x3fac')
assert.equal(hex(-16300), '0xbfac'); assert.equal(hex(-16300), '0xbfac')
assert.equal(hex( 62300), '0x00f35c'); assert.equal(hex( 62300), '0x00f35c')
assert.equal(hex(-62300), '0x80f35c'); assert.equal(hex(-62300), '0x80f35c')
})
}) })
})
describe('with RNG passed into constructor as the 2nd argument', function(){ describe('with RNG passed into constructor as the 2nd argument', function(){
it('returns a BigInteger with the limit of the specified length', function(){ it('returns a BigInteger with the limit of the specified length', function(){
var bitLength = 256 var bitLength = 256
var i = new BigInteger(bitLength, secureRandom) var i = new BigInteger(bitLength, secureRandom)
assert(i.bitLength() <= 256) assert(i.bitLength() <= 256)
})
}) })
})
}) })

View file

@ -1,65 +1,65 @@
var assert = require('assert'); var assert = require('assert')
var bitcoinjs = require('../'); var bitcoinjs = require('../')
var sec = require('../src/jsbn/sec'); var sec = require('../src/jsbn/sec')
var BigInteger = require('../src/jsbn/jsbn.js'); var BigInteger = require('../src/jsbn/jsbn.js')
var SHA256 = require('crypto-js/sha256'); var SHA256 = require('crypto-js/sha256')
var rng = require('secure-random'); var rng = require('secure-random')
var ecparams = sec('secp256k1'); var ecparams = sec('secp256k1')
var ECPointFp = bitcoinjs.ECPointFp; var ECPointFp = bitcoinjs.ECPointFp
var convert = require('../src/convert'); var convert = require('../src/convert')
function sha256FromBytesToBytes(message){ function sha256FromBytesToBytes(message){
return convert.wordArrayToBytes(SHA256(convert.bytesToWordArray(message))) return convert.wordArrayToBytes(SHA256(convert.bytesToWordArray(message)))
} }
it('Keys & Key Management', function () { it('Keys & Key Management', function () {
var p1 = bitcoinjs.Key().getPub().toBytes() var p1 = bitcoinjs.Key().getPub().toBytes()
assert.equal(p1.length, 65) assert.equal(p1.length, 65)
var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1); var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1)
assert.ok(p1_q); assert.ok(p1_q)
assert.ok(p1_q.validate()); assert.ok(p1_q.validate())
var p2 = bitcoinjs.convert.hexToBytes( var p2 = bitcoinjs.convert.hexToBytes(
'0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' + '0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' +
'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c'); 'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c')
var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2); var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2)
assert.ok(p2_q); assert.ok(p2_q)
assert.ok(p2_q.validate()); assert.ok(p2_q.validate())
}); })
it('Signing and Verifying', function () { it('Signing and Verifying', function () {
var s1 = bitcoinjs.Key(); var s1 = bitcoinjs.Key()
var sig_a = s1.sign(BigInteger.ZERO); var sig_a = s1.sign(BigInteger.ZERO)
assert.ok(sig_a, 'Sign null'); 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 message = new BigInteger(1024, rng).toByteArrayUnsigned()
var hash = sha256FromBytesToBytes(message); var hash = sha256FromBytesToBytes(message)
var sig_b = s1.sign(hash); var sig_b = s1.sign(hash)
assert.ok(sig_b, 'Sign random string'); assert.ok(sig_b, 'Sign random string')
assert.ok(s1.verify(hash, sig_b)); assert.ok(s1.verify(hash, sig_b))
var message2 = bitcoinjs.convert.hexToBytes( var message2 = bitcoinjs.convert.hexToBytes(
'12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' + '12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' +
'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' + 'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' +
'1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' + '1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' +
'8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' + '8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' +
'3d82507b932b84e4'); '3d82507b932b84e4')
var hash2 = sha256FromBytesToBytes(message2); var hash2 = sha256FromBytesToBytes(message2)
var sig_c = bitcoinjs.convert.hexToBytes( var sig_c = bitcoinjs.convert.hexToBytes(
'3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' + '3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' +
'79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' + '79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' +
'f273ab082801a95506c4'); 'f273ab082801a95506c4')
var s2 = bitcoinjs.convert.hexToBytes( var s2 = bitcoinjs.convert.hexToBytes(
'045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' + '045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' +
'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' + 'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' +
'609a978528'); '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')
}); })

View file

@ -2,13 +2,11 @@ var Script = require('../src/script.js')
var assert = require('assert') var assert = require('assert')
var Address = require('../src/address.js') var Address = require('../src/address.js')
var Network = require('../src/network.js') var Network = require('../src/network.js')
var Util = require('../src/util.js') var Util = require('../src/util.js')
var sha256ripe160 = Util.sha256ripe160; var sha256ripe160 = Util.sha256ripe160
var Convert = require('../src/convert.js') var Convert = require('../src/convert.js')
var bytesToHex = Convert.bytesToHex; var bytesToHex = Convert.bytesToHex
var hexToBytes = Convert.hexToBytes; var hexToBytes = Convert.hexToBytes
describe('Script', function() { describe('Script', function() {
var p2shScriptPubKey, pubkeyScriptPubkey, addressScriptSig var p2shScriptPubKey, pubkeyScriptPubkey, addressScriptSig
@ -72,15 +70,15 @@ describe('Script', function() {
}) })
describe('2-of-3 Multi-Signature', function() { describe('2-of-3 Multi-Signature', function() {
var compressedPubKeys = []; var compressedPubKeys = []
var numSigs, script, multisig, network var numSigs, script, multisig, network
beforeEach(function() { beforeEach(function() {
compressedPubKeys = ['02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f', compressedPubKeys = ['02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f',
'02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f', '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f',
'036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19'] '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19']
numSigs = 2 numSigs = 2
network = Network.mainnet.p2shVersion network = Network.mainnet.p2shVersion
}) })
it('should create valid multi-sig address', function() { it('should create valid multi-sig address', function() {
@ -98,8 +96,11 @@ describe('Script', function() {
var deserialized = new Script(redeemScript) var deserialized = new Script(redeemScript)
var numOfSignatures = deserialized.chunks[deserialized.chunks.length - 2] - 80 var numOfSignatures = deserialized.chunks[deserialized.chunks.length - 2] - 80
var signaturesRequired = deserialized.chunks[0] - 80 var signaturesRequired = deserialized.chunks[0] - 80
var sigs = [bytesToHex(deserialized.chunks[1]), bytesToHex(deserialized.chunks[2]), var sigs = [
bytesToHex(deserialized.chunks[3])] bytesToHex(deserialized.chunks[1]),
bytesToHex(deserialized.chunks[2]),
bytesToHex(deserialized.chunks[3])
]
assert.equal(numOfSignatures, 3) assert.equal(numOfSignatures, 3)
assert.equal(signaturesRequired, 2) assert.equal(signaturesRequired, 2)

View file

@ -1,7 +1,6 @@
var T = require('../src/transaction') var T = require('../src/transaction')
var Transaction = T.Transaction var Transaction = T.Transaction
var TransactionOut = T.TransactionOut var TransactionOut = T.TransactionOut
var convert = require('../src/convert') var convert = require('../src/convert')
var ECKey = require('../src/eckey').ECKey var ECKey = require('../src/eckey').ECKey
var Script = require('../src/script') var Script = require('../src/script')
@ -84,7 +83,7 @@ describe('Transaction', function() {
// the deserialized-serialized transaction should return to its original state (== tx) // the deserialized-serialized transaction should return to its original state (== tx)
var bytes2 = Transaction.deserialize(mutated).serialize() var bytes2 = Transaction.deserialize(mutated).serialize()
assert.deepEqual(bytes, bytes2) assert.deepEqual(bytes, bytes2)
}); })
}) })
describe('creating a transaction', function() { describe('creating a transaction', function() {

View file

@ -6,6 +6,7 @@ var TransactionOut = T.TransactionOut
var Script = require('../src/script.js') var Script = require('../src/script.js')
var convert = require('../src/convert.js') var convert = require('../src/convert.js')
var assert = require('assert') var assert = require('assert')
var sinon = require('sinon')
var SHA256 = require('crypto-js/sha256') var SHA256 = require('crypto-js/sha256')
var Crypto = require('crypto-js') var Crypto = require('crypto-js')
@ -13,10 +14,8 @@ var fixtureTxes = require('./fixtures/mainnet_tx')
var fixtureTx1Hex = fixtureTxes.prevTx var fixtureTx1Hex = fixtureTxes.prevTx
var fixtureTx2Hex = fixtureTxes.tx var fixtureTx2Hex = fixtureTxes.tx
var sinon = require('sinon')
describe('Wallet', function() { describe('Wallet', function() {
var seed, wallet; var seed, wallet
beforeEach(function(){ beforeEach(function(){
seed = convert.wordArrayToBytes(SHA256("don't use a string seed like this in real life")) seed = convert.wordArrayToBytes(SHA256("don't use a string seed like this in real life"))
wallet = new Wallet(seed) wallet = new Wallet(seed)
@ -141,9 +140,9 @@ describe('Wallet', function() {
wallet.generateAddress() wallet.generateAddress()
assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"), assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
wallet.getExternalAccount().derive(1).priv) wallet.getExternalAccount().derive(1).priv)
assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"), assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
wallet.getInternalAccount().derive(0).priv) wallet.getInternalAccount().derive(0).priv)
}) })
it('raises an error when address is not found', function(){ it('raises an error when address is not found', function(){
@ -155,7 +154,7 @@ describe('Wallet', function() {
}) })
describe('Unspent Outputs', function(){ describe('Unspent Outputs', function(){
var expectedUtxo, expectedOutputKey; var expectedUtxo, expectedOutputKey
beforeEach(function(){ beforeEach(function(){
expectedUtxo = { expectedUtxo = {
"hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7", "hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7",
@ -216,7 +215,7 @@ describe('Wallet', function() {
}) })
describe('setUnspentOutputs', function(){ describe('setUnspentOutputs', function(){
var utxo; var utxo
beforeEach(function(){ beforeEach(function(){
utxo = cloneObject([expectedUtxo]) utxo = cloneObject([expectedUtxo])
}) })
@ -275,7 +274,7 @@ describe('Wallet', function() {
}) })
describe('setUnspentOutputsAsync', function(){ describe('setUnspentOutputsAsync', function(){
var utxo; var utxo
beforeEach(function(){ beforeEach(function(){
utxo = cloneObject([expectedUtxo]) utxo = cloneObject([expectedUtxo])
}) })
@ -308,7 +307,7 @@ describe('Wallet', function() {
}) })
describe('processTx', function(){ describe('processTx', function(){
var tx; var tx
beforeEach(function(){ beforeEach(function(){
tx = Transaction.deserialize(fixtureTx1Hex) tx = Transaction.deserialize(fixtureTx1Hex)
@ -382,8 +381,8 @@ describe('Wallet', function() {
}) })
describe('createTx', function(){ describe('createTx', function(){
var to, value; var to, value
var address1, address2; var address1, address2
beforeEach(function(){ beforeEach(function(){
to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'
@ -396,23 +395,23 @@ describe('Wallet', function() {
// set up 3 utxo // set up 3 utxo
utxo = [ utxo = [
{ {
"hash": fakeTxHash(1), "hash": fakeTxHash(1),
"outputIndex": 0, "outputIndex": 0,
"address" : address1, "address" : address1,
"value": 400000 // not enough for value "value": 400000 // not enough for value
}, },
{ {
"hash": fakeTxHash(2), "hash": fakeTxHash(2),
"outputIndex": 1, "outputIndex": 1,
"address" : address1, "address" : address1,
"value": 500000 // enough for only value "value": 500000 // enough for only value
}, },
{ {
"hash": fakeTxHash(3), "hash": fakeTxHash(3),
"outputIndex": 0, "outputIndex": 0,
"address" : address2, "address" : address2,
"value": 520000 // enough for value and fee "value": 520000 // enough for value and fee
} }
] ]
wallet.setUnspentOutputs(utxo) wallet.setUnspentOutputs(utxo)
}) })
@ -446,11 +445,11 @@ describe('Wallet', function() {
it('ignores spent outputs', function(){ it('ignores spent outputs', function(){
utxo.push( utxo.push(
{ {
"hash": fakeTxHash(4), "hash": fakeTxHash(4),
"outputIndex": 0, "outputIndex": 0,
"address" : address2, "address" : address2,
"value": 530000 // enough but spent before createTx "value": 530000 // enough but spent before createTx
} }
) )
wallet.setUnspentOutputs(utxo) wallet.setUnspentOutputs(utxo)
wallet.outputs[fakeTxHash(4) + ":" + 0].spend = fakeTxHash(5) + ":" + 0 wallet.outputs[fakeTxHash(4) + ":" + 0].spend = fakeTxHash(5) + ":" + 0
@ -547,7 +546,7 @@ describe('Wallet', function() {
}) })
describe('createTxAsync', function(){ describe('createTxAsync', function(){
var to, value, fee; var to, value, fee
beforeEach(function(){ beforeEach(function(){
to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' to = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3'