Browse Source

Merge justcoin/master

hk-custom-address
Kyle Drake 11 years ago
parent
commit
fcf3fde089
  1. 6
      .travis.yml
  2. 53
      Jakefile.js
  3. 8
      README.md
  4. 3
      bitcoinjs-min.js
  5. 8
      build/bitcoinjs-exit-min.js
  6. 28
      build/bitcoinjs-min.js
  7. 0
      demo/split-key.html
  8. 18
      package.json
  9. 89
      src/address.js
  10. 152
      src/base58.js
  11. 175
      src/bitcoin.js
  12. 74
      src/convert.js
  13. 7
      src/crypto-js/crypto-min.js
  14. 104
      src/crypto-js/crypto.js
  15. 38
      src/crypto-js/hmac.js
  16. 333
      src/crypto-js/ripemd160.js
  17. 7
      src/crypto-js/sha256-min.js
  18. 213
      src/crypto-js/sha256.js
  19. 239
      src/crypto-js/sha512.js
  20. 266
      src/ecdsa.js
  21. 274
      src/eckey.js
  22. 235
      src/hdwallet.js
  23. 54
      src/index.js
  24. 208
      src/jsbn/ec.js
  25. 912
      src/jsbn/jsbn.js
  26. 656
      src/jsbn/jsbn2.js
  27. 8
      src/jsbn/prng4.js
  28. 17
      src/jsbn/rng.js
  29. 5
      src/jsbn/sec.js
  30. 57
      src/message.js
  31. 22
      src/opcode.js
  32. 309
      src/script.js
  33. 446
      src/transaction.js
  34. 4
      src/txdb.js
  35. 241
      src/util.js
  36. 480
      src/wallet.js
  37. 52
      test/address.js
  38. 22
      test/base58.js
  39. 28
      test/convert.js
  40. 23
      test/ec.js
  41. 43
      test/eckey.js
  42. 217
      test/hdwallet.js
  43. 36
      test/index.html
  44. 28
      test/jsbn.js
  45. 34
      test/message.js
  46. 68
      test/misc.js
  47. 1
      test/mocha.opts
  48. 112
      test/test.js
  49. 226
      vendor/qunit/qunit.css
  50. 1510
      vendor/qunit/qunit.js

6
.travis.yml

@ -0,0 +1,6 @@
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
- "0.6"

53
Jakefile.js

@ -1,53 +0,0 @@
var pkginfo = require('pkginfo')(module);
var minify = require('jake-uglify').minify;
var headerJS = "\
/**\n\
* BitcoinJS-lib v"+exports.version+"-default\n\
* Copyright (c) 2011 BitcoinJS Project\n\
* \n\
* This program is free software; you can redistribute it and/or modify\n\
* it under the terms of the MIT license.\n\
*/";
task({'default': [
'build/bitcoinjs-min.js',
'build/bitcoinjs-exit-min.js'
]});
desc('General-purpose build containing most features');
minify({'build/bitcoinjs-min.js': [
'src/crypto-js/crypto.js',
'src/crypto-js/sha256.js',
'src/crypto-js/ripemd160.js',
'src/jsbn/prng4.js',
'src/jsbn/rng.js',
'src/jsbn/jsbn.js',
'src/jsbn/jsbn2.js',
'src/jsbn/ec.js',
'src/jsbn/sec.js',
'src/events/eventemitter.js',
'src/bitcoin.js',
'src/util.js',
'src/base58.js',
'src/address.js',
'src/ecdsa.js',
'src/eckey.js',
'src/opcode.js',
'src/script.js',
'src/transaction.js',
'src/wallet.js',
'src/txdb.js'
]}, {
header: headerJS
});
desc('Exit node client implementation');
minify({'build/bitcoinjs-exit-min.js': [
'src/exit/client.js'
]}, {
header: headerJS
});

8
README.md

@ -13,6 +13,14 @@ system in place.
Prototype software, use at your own peril. Prototype software, use at your own peril.
# How to use
* Run `npm run-script compile` to compile to a browser-friendly minified
file. Once in the browser, the global Bitcoin object will contain everything
you need.
* To use in NodeJS, install this package as you would any other and
put in `var Bitcoin = require('bitcoinjs-lib')`.
# License # License
This library is free and open-source software released under the MIT This library is free and open-source software released under the MIT

3
bitcoinjs-min.js

File diff suppressed because one or more lines are too long

8
build/bitcoinjs-exit-min.js

@ -1,8 +0,0 @@
/**
* BitcoinJS-lib v0.1.0-default
* Copyright (c) 2011 BitcoinJS Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the MIT license.
*/
(function(a,b,c,d,e){function f(a,b,c){this.setUri(a,b,c),this.unique=1,this.connected=!1,this.callbacks=[]}a.ExitNode=f,b.EventEmitter.augment(f),f.prototype.setUri=function(a,b,c){this.uri=(c?"https://":"http://")+a+":"+b},f.prototype.connect=function(a){this.wallet=a,delete c.sockets[this.uri],c.j=[],this.socket=c.connect(this.uri),this.socket.on("connect",d.proxy(this.handleConnect,this)),this.socket.on("error",function(){console.log("error, test")}),this.socket.on("message",d.proxy(this.handleMessage,this)),this.socket.on("disconnect",d.proxy(this.handleDisconnect,this))},f.prototype.disconnect=function(){this.socket&&(this.socket.disconnect(),this.socket=null,this.connected=!1),this.trigger("connectStatus",{status:"unknown"})},f.prototype.call=function(a,b,c){this.socket.send(d.toJSON({method:a,params:[b],id:this.unique})),c&&(this.callbacks[this.unique]=c),this.unique++},f.prototype.handleConnect=function(){var a=this;this.connected=!0},f.prototype.listen=function(a){self.call("pubkeysRegister",{keys:a.join(",")},function(a,b){if(a){console.error("Could not register public keys");return}self.call("pubkeysListen",{handle:b.handle},function(a,b){self.trigger("blockInit",{height:b.height}),self.trigger("txData",{confirmed:!0,txs:b.txs}),self.trigger("connectStatus",{status:"ok"})}),self.call("pubkeysUnconfirmed",{handle:b.handle},function(a,b){self.trigger("txData",{confirmed:!1,txs:b.txs})})})},f.prototype.handleMessage=function(a){"undefined"!=typeof a.result&&"function"==typeof this.callbacks[a.id]?this.callbacks[a.id](a.error,a.result):"undefined"!=typeof a.method&&this.trigger(a.method,a.params[0])},f.prototype.handleDisconnect=function(){},f.prototype.query=function(a,b,c,e){"function"==typeof c&&(e=c,c=!1),b=b||{},e="function"==typeof e?e:function(){};var f=this.uri+"/"+a;c&&(f+="?callback=?"),d.getJSON(f,b,e)}})("undefined"!=typeof Bitcoin?Bitcoin:module.exports,"undefined"!=typeof Bitcoin?Bitcoin:require("bitcoinjs-lib"),"undefined"!=typeof io?io:require("io"),jQuery,this)

28
build/bitcoinjs-min.js

File diff suppressed because one or more lines are too long

0
demo/split-key.html

18
package.json

@ -2,6 +2,7 @@
"name": "bitcoinjs-lib", "name": "bitcoinjs-lib",
"version": "0.1.3", "version": "0.1.3",
"description": "Client-side Bitcoin JavaScript library", "description": "Client-side Bitcoin JavaScript library",
"main": "./src/index.js",
"keywords": [ "keywords": [
"bitcoin", "bitcoin",
@ -18,7 +19,20 @@
}, },
"devDependencies" : { "devDependencies" : {
"pkginfo" : ">=0.2.1", "mocha": "1.8.1",
"jake-uglify" : ">=1.0.0" "istanbul": "0.1.30",
"uglify-js": "*",
"node-browserify": "https://github.com/substack/node-browserify/tarball/master"
},
"testling": {
"browsers": ["ie/9..latest", "firefox/15..latest", "chrome/22..latest"],
"harness" : "mocha-qunit",
"files": "test/*.js"
},
"scripts": {
"test": "istanbul test ./node_modules/.bin/_mocha -- --reporter list test/*.js",
"compile": "browserify src/index.js -s Bitcoin | uglifyjs > bitcoinjs-min.js"
} }
} }

89
src/address.js

@ -1,10 +1,35 @@
Bitcoin.Address = function (bytes) { var base58 = require('./base58');
if ("string" == typeof bytes) { var Crypto = require('./crypto-js/crypto');
bytes = Bitcoin.Address.decodeString(bytes); var conv = require('./convert');
var util = require('./util');
var address_types = {
prod: 0,
testnet: 111
};
var p2sh_types = {
prod: 5,
testnet: 196
};
var Address = function (bytes, version) {
if (arguments[0] instanceof Address) {
this.hash = arguments[0].hash;
this.version = arguments[0].version;
} }
this.hash = bytes; else if (typeof bytes === 'string') {
this.hash =
bytes.length <= 34 ? base58.checkDecode(bytes)
: bytes.length <= 40 ? conv.hexToBytes(bytes)
: util.error('Bad input');
this.version = 0x00; this.version = version || this.hash.version || 0;
}
else {
this.hash = bytes;
this.version = version || 0;
}
}; };
/** /**
@ -12,46 +37,32 @@ Bitcoin.Address = function (bytes) {
* *
* Returns the address as a base58-encoded string in the standardized format. * Returns the address as a base58-encoded string in the standardized format.
*/ */
Bitcoin.Address.prototype.toString = function () { Address.prototype.toString = function () {
// Get a copy of the hash return base58.checkEncode(this.hash.slice(0),this.version);
var hash = this.hash.slice(0); };
// Version
hash.unshift(this.version);
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
var bytes = hash.concat(checksum.slice(0,4));
return Bitcoin.Base58.encode(bytes); Address.prototype.getHash = function () {
return conv.bytesToHex(this.hash);
}; };
Bitcoin.Address.prototype.getHashBase64 = function () { Address.getVersion = function(string) {
return Crypto.util.bytesToBase64(this.hash); return base58.decode(string)[0];
}
Address.validate = function(string) {
try {
base58.checkDecode(string);
return true;
} catch (e) {
return false;
}
}; };
/** /**
* Parse a Bitcoin address contained in a string. * Parse a Bitcoin address contained in a string.
*/ */
Bitcoin.Address.decodeString = function (string) { Address.decodeString = function (string) {
var bytes = Bitcoin.Base58.decode(string); return base58.checkDecode(string);
var hash = bytes.slice(0, 21);
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
if (checksum[0] != bytes[21] ||
checksum[1] != bytes[22] ||
checksum[2] != bytes[23] ||
checksum[3] != bytes[24]) {
throw "Checksum validation failed!";
}
var version = hash.shift();
if (version != 0) {
throw "Version "+version+" not supported!";
}
return hash;
}; };
module.exports = Address;

152
src/base58.js

@ -1,71 +1,119 @@
(function (Bitcoin) {
Bitcoin.Base58 = { // https://en.bitcoin.it/wiki/Base58Check_encoding
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/, var BigInteger = require('./jsbn/jsbn');
base: BigInteger.valueOf(58), var Crypto = require('./crypto-js/crypto');
var conv = require('./convert');
/**
* Convert a byte array to a base58-encoded string. var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
* var base = BigInteger.valueOf(58);
* Written by Mike Hearn for BitcoinJ.
* Copyright (c) 2011 Google Inc. var positions = {};
* for (var i=0 ; i < alphabet.length ; ++i) {
* Ported to JavaScript by Stefan Thomas. positions[alphabet[i]] = i;
*/ }
encode: function (input) {
// Convert a byte array to a base58-encoded string.
// Written by Mike Hearn for BitcoinJ.
// Copyright (c) 2011 Google Inc.
// Ported to JavaScript by Stefan Thomas.
module.exports.encode = function (input) {
var bi = BigInteger.fromByteArrayUnsigned(input); var bi = BigInteger.fromByteArrayUnsigned(input);
var chars = []; var chars = [];
while (bi.compareTo(B58.base) >= 0) { while (bi.compareTo(base) >= 0) {
var mod = bi.mod(B58.base); var mod = bi.mod(base);
chars.unshift(B58.alphabet[mod.intValue()]); chars.push(alphabet[mod.intValue()]);
bi = bi.subtract(mod).divide(B58.base); bi = bi.subtract(mod).divide(base);
} }
chars.unshift(B58.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.unshift(B58.alphabet[0]); chars.push(alphabet[0]);
} else break; } else break;
} }
return chars.join(''); return chars.reverse().join('');
}, },
/** module.exports.encodeHex = function (input) {
* Convert a base58-encoded string to a byte array. return conv.bytesToHex(module.exports.encode(input));
* }
* Written by Mike Hearn for BitcoinJ.
* Copyright (c) 2011 Google Inc. // decode a base58 string into a byte array
* // input should be a base58 encoded string
* Ported to JavaScript by Stefan Thomas. // @return Array
*/ module.exports.decode = function (input) {
decode: function (input) {
var bi = BigInteger.valueOf(0); var base = BigInteger.valueOf(58);
var leadingZerosNum = 0;
for (var i = input.length - 1; i >= 0; i--) { var length = input.length;
var alphaIndex = B58.alphabet.indexOf(input[i]); var num = BigInteger.valueOf(0);
if (alphaIndex < 0) { var leading_zero = 0;
throw "Invalid character"; var seen_other = false;
for (var i=0; i<length ; ++i) {
var chr = input[i];
var p = positions[chr];
// if we encounter an invalid character, decoding fails
if (p === undefined) {
throw new Error('invalid base58 string: ' + input);
} }
bi = bi.add(BigInteger.valueOf(alphaIndex)
.multiply(B58.base.pow(input.length - 1 -i)));
// This counts leading zero bytes num = num.multiply(base).add(BigInteger.valueOf(p));
if (input[i] == "1") leadingZerosNum++;
else leadingZerosNum = 0; if (chr == '1' && !seen_other) {
++leading_zero;
}
else {
seen_other = true;
}
} }
var bytes = bi.toByteArrayUnsigned();
// Add leading zeros var bytes = num.toByteArrayUnsigned();
while (leadingZerosNum-- > 0) bytes.unshift(0);
// remove leading zeros
while (leading_zero-- > 0) {
bytes.unshift(0);
}
return bytes; return bytes;
}
module.exports.decodeHex = function (input) {
return module.exports.decode(conv.hexToBytes(input));
}
module.exports.checkEncode = function(input, vbyte) {
vbyte = vbyte || 0;
var front = [vbyte].concat(input);
var checksum = Crypto.SHA256(Crypto.SHA256(front, {asBytes: true}), {asBytes: true})
.slice(0,4);
return module.exports.encode(front.concat(checksum));
}
module.exports.checkEncodeHex = function (input, vbyte) {
return conv.bytesToHex(module.exports.encode(input));
}
module.exports.checkDecode = function(input) {
var bytes = module.exports.decode(input),
front = bytes.slice(0,bytes.length-4),
back = bytes.slice(bytes.length-4);
var checksum = Crypto.SHA256(Crypto.SHA256(front,{asBytes: true}), {asBytes: true})
.slice(0,4);
if (""+checksum != ""+back) {
throw new Error("Checksum failed");
} }
}; var o = front.slice(1);
o.version = front[0];
return o;
}
module.exports.checkDecodeHex = function (input) {
return module.exports.checkDecode(conv.hexToBytes(input));
}
var B58 = Bitcoin.Base58;
})(
'undefined' != typeof Bitcoin ? Bitcoin : module.exports
);

175
src/bitcoin.js

@ -1,175 +0,0 @@
(function (exports) {
var Bitcoin = exports;
if ('object' !== typeof module) {
Bitcoin.EventEmitter = EventEmitter;
}
})(
'object' === typeof module ? module.exports : (window.Bitcoin = {})
);
/*
function makeKeypair()
{
// Generate private key
var n = ecparams.getN();
var n1 = n.subtract(BigInteger.ONE);
var r = new BigInteger(n.bitLength(), rng);
var privateKey = r.mod(n1).add(BigInteger.ONE);
// Generate public key
var G = ecparams.getG();
var publicPoint = G.multiply(privateKey);
return {priv: privateKey, pubkey: publicPoint};
};
function serializeTransaction(tx)
{
var buffer = [];
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.version)]));
buffer = buffer.concat(numToVarInt(tx.ins.length));
for (var i = 0; i < tx.ins.length; i++) {
var txin = tx.ins[i];
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.index)]));
var scriptBytes = Crypto.util.base64ToBytes(txin.script);
buffer = buffer.concat(numToVarInt(scriptBytes.length));
buffer = buffer.concat(scriptBytes);
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]));
}
buffer = buffer.concat(numToVarInt(tx.outs.length));
for (var i = 0; i < tx.outs.length; i++) {
var txout = tx.outs[i];
var valueHex = (new BigInteger(txout.value, 10)).toString(16);
while (valueHex.length < 16) valueHex = "0" + valueHex;
buffer = buffer.concat(Crypto.util.hexToBytes(valueHex));
var scriptBytes = Crypto.util.base64ToBytes(txout.script);
buffer = buffer.concat(numToVarInt(scriptBytes.length));
buffer = buffer.concat(scriptBytes);
}
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.lock_time)]));
return buffer;
};
var OP_CODESEPARATOR = 171;
var SIGHASH_ALL = 1;
var SIGHASH_NONE = 2;
var SIGHASH_SINGLE = 3;
var SIGHASH_ANYONECANPAY = 80;
function hashTransactionForSignature(scriptCode, tx, inIndex, hashType)
{
// TODO: We need to actually deep copy here
var txTmp = tx;
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
scriptCode = scriptCode.filter(function (val) {
return val !== OP_CODESEPARATOR;
});
// Blank out other inputs' signatures
for (var i = 0; i < txTmp.ins.length; i++) {
txTmp.ins[i].script = Crypto.util.bytesToBase64([]);
}
txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode);
// Blank out some of the outputs
if ((hashType & 0x1f) == SIGHASH_NONE) {
txTmp.outs = [];
// Let the others update at will
for (var i = 0; i < txTmp.ins.length; i++)
if (i != inIndex)
txTmp.ins[i].sequence = 0;
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
// TODO: Implement
}
// Blank out other inputs completely, not recommended for open transactions
if (hashType & SIGHASH_ANYONECANPAY) {
txTmp.ins = [txTmp.ins[inIndex]];
}
var buffer = serializeTransaction(txTmp);
buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]));
console.log(buffer);
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
};
function verifyTransactionSignature(tx) {
var hash = hashTransactionForSignature([], tx, 0, 0);
return Crypto.util.bytesToHex(hash);
};
function numToVarInt(i)
{
// TODO: THIS IS TOTALLY UNTESTED!
if (i < 0xfd) {
// unsigned char
return [i];
} else if (i <= 1<<16) {
// unsigned short (LE)
return [0xfd, i >>> 8, i & 255];
} else if (i <= 1<<32) {
// unsigned int (LE)
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
} else {
// unsigned long long (LE)
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
}
};
var testTx = {
"version":"1",
"lock_time":"0",
"block": {
"hash":"N/A",
"height":115806
},
"index":6,
"hash":"WUFzjKubG1kqfJWMb4qZdlhU2F3l5NGXN7AUg8Jwl14=",
"ins":[{
"outpoint":{
"hash":"nqcbMM1oRhfLdZga11q7x0CpUMujm+vtxHXO9V0gnwE=",
"index":0
},
"script":"RzBEAiB2XXkx1pca9SlfCmCGNUVf+h2sAFBttcxG1VnypIcvEgIgXrOp7LSdYBYp3nPsQAz8BOLD3K4pAlXfZImP1rkzk2EBQQRi7NcODzNfnVqLtG79Axp5UF6EhFIhCmzqKqssfKpfCIOmzCuXEeDFUFvFzeGLJx5N+wp2qRS1TqYezGD3yERk",
"sequence":4294967295
}],
"outs":[{
"value":"3000000000",
"script":"dqkUBLZwqhAPRVgZvwI8MN5gLHbU8NOIrA=="
},{
"value":"25937000000",
"script":"dqkUQ82gJ0O5vOBg6yK5/yorLLV5zLKIrA=="
}]
};
TODO: Make this stuff into test cases ;)
$(function () {
var key = new Bitcoin.ECKey(Crypto.util.hexToBytes("5c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4a"));
key = new Bitcoin.ECKey(Crypto.util.hexToBytes("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"));
//console.log(key.getBitcoinAddress().toString());
//var message = Crypto.util.hexToBytes("2aec28d323ee7b06a799d540d224b351161fe48967174ca5e43164e86137da11");
//message = [0];
//var out = key.sign(message);
//console.log("pubkey: "+Crypto.util.bytesToHex(key.getPub()));
//console.log("sig: "+Crypto.util.bytesToHex(out));
//console.log(key.verify(message, out));
//console.log(Bitcoin.ECDSA.verify(message, Crypto.util.hexToBytes("3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d"), Crypto.util.hexToBytes("0401de173aa944eacf7e44e5073baca93fb34fe4b7897a1c82c92dfdc8a1f75ef58cd1b06e8052096980cb6e1ad6d3df143c34b3d7394bae2782a4df570554c2fb")));
//console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("230aba77ccde46bb17fcb0295a92c0cc42a6ea9f439aaadeb0094625f49e6ed8"), Crypto.util.hexToBytes("3046022100a3ee5408f0003d8ef00ff2e0537f54ba09771626ff70dca1f01296b05c510e85022100d4dc70a5bb50685b65833a97e536909a6951dd247a2fdbde6688c33ba6d6407501"),Crypto.util.hexToBytes("04a19c1f07c7a0868d86dbb37510305843cc730eb3bea8a99d92131f44950cecd923788419bfef2f635fad621d753f30d4b4b63b29da44b4f3d92db974537ad5a4")));
//console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("c2c75bb77d7a5acddceb1d45ceef58e7451fd0d3abc9d4c16df7848eefafe00d"), Crypto.util.hexToBytes("3045022100ff9362dadcbf1f6ef954bc8eb27144bbb4f49abd32be1eb04c311151dcf4bcf802205112c2ca6a25aefb8be98bf460c5a9056c01253f31e118d80b81ec9604e3201a01"),Crypto.util.hexToBytes("04fe62ce7892ec209310c176ef7f06565865e286e8699e884603657efa9aa51086785099d544d4e04f1f7b4b065205c1783fade8daf4ba1e0d1962292e8eb722cd")));
});
//
*/

74
src/convert.js

@ -0,0 +1,74 @@
// convert to/from various values
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
String.prototype.lpad = function(padString, length) {
var str = this;
while (str.length < length) str = padString + str;
return str;
}
// Convert a byte array to a hex string
module.exports.bytesToHex = function(bytes) {
return bytes.map(function(x) { return x.toString(16).lpad('0',2) }).join('');
};
// Convert a hex string to a byte array
module.exports.hexToBytes = function(hex) {
return hex.match(/../g).map(function(x) { return parseInt(x,16) });
}
// Convert a byte array to a base-64 string
module.exports.bytesToBase64 = function(bytes) {
for(var base64 = [], i = 0; i < bytes.length; i += 3) {
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
for (var j = 0; j < 4; j++) {
if (i * 8 + j * 6 <= bytes.length * 8)
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
else base64.push("=");
}
}
return base64.join("");
}
// Convert a base-64 string to a byte array
module.exports.base64ToBytes = function(base64) {
// Remove non-base-64 characters
base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
if (imod4 == 0) continue;
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) |
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
}
return bytes;
}
// Hex only (allowing bin would be potentially risky, as 01010101 = \x01 * 4 or 85)
module.exports.coerceToBytes = function(input) {
if (typeof input == "string") return module.exports.hexToBytes(input);
return input;
}
module.exports.binToBytes = function(bin) {
return bin.match(/......../g).map(function(x) { return parseInt(x,2) });
}
module.exports.bytesToBin = function(bytes) {
return bytes.map(function(x) { return x.toString(2).lpad('0',8) }).join('');
}
module.exports.bytesToString = function(bytes) {
return bytes.map(function(x){ return String.fromCharCode(x) }).join('');
}
module.exports.stringToBytes = function(string) {
return string.split('').map(function(x) { return x.charCodeAt(0) });
}
// utf8

7
src/crypto-js/crypto-min.js

@ -1,7 +0,0 @@
/*
* Crypto-JS v2.0.0
* http://code.google.com/p/crypto-js/
* Copyright (c) 2009, Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License
*/
(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();

104
src/crypto-js/crypto.js

@ -4,42 +4,13 @@
* Copyright (c) 2009, Jeff Mott. All rights reserved. * Copyright (c) 2009, Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License * http://code.google.com/p/crypto-js/wiki/License
*/ */
(function(){
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Global Crypto object // Global Crypto object
var Crypto = window.Crypto = {}; var Crypto = module.exports = {};
// Crypto utilities // Crypto utilities
var util = Crypto.util = { var util = Crypto.util = {
// Bit-wise rotate left
rotl: function (n, b) {
return (n << b) | (n >>> (32 - b));
},
// Bit-wise rotate right
rotr: function (n, b) {
return (n << (32 - b)) | (n >>> b);
},
// Swap big-endian to little-endian and vice versa
endian: function (n) {
// If number given, swap endian
if (n.constructor == Number) {
return util.rotl(n, 8) & 0x00FF00FF |
util.rotl(n, 24) & 0xFF00FF00;
}
// Else, assume array and swap all items
for (var i = 0; i < n.length; i++)
n[i] = util.endian(n[i]);
return n;
},
// Generate an array of any length of random bytes // Generate an array of any length of random bytes
randomBytes: function (n) { randomBytes: function (n) {
for (var bytes = []; n > 0; n--) for (var bytes = []; n > 0; n--)
@ -47,74 +18,6 @@ var util = Crypto.util = {
return bytes; return bytes;
}, },
// Convert a byte array to big-endian 32-bit words
bytesToWords: function (bytes) {
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
words[b >>> 5] |= bytes[i] << (24 - b % 32);
return words;
},
// Convert big-endian 32-bit words to a byte array
wordsToBytes: function (words) {
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
return bytes;
},
// Convert a byte array to a hex string
bytesToHex: function (bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
hex.push((bytes[i] >>> 4).toString(16));
hex.push((bytes[i] & 0xF).toString(16));
}
return hex.join("");
},
// Convert a hex string to a byte array
hexToBytes: function (hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
},
// Convert a byte array to a base-64 string
bytesToBase64: function (bytes) {
// Use browser-native function if it exists
if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes));
for(var base64 = [], i = 0; i < bytes.length; i += 3) {
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
for (var j = 0; j < 4; j++) {
if (i * 8 + j * 6 <= bytes.length * 8)
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
else base64.push("=");
}
}
return base64.join("");
},
// Convert a base-64 string to a byte array
base64ToBytes: function (base64) {
// Use browser-native function if it exists
if (typeof atob == "function") return Binary.stringToBytes(atob(base64));
// Remove non-base-64 characters
base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
if (imod4 == 0) continue;
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) |
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
}
return bytes;
}
}; };
// Crypto mode namespace // Crypto mode namespace
@ -157,4 +60,7 @@ var Binary = charenc.Binary = {
}; };
})(); module.exports.SHA256 = require('./sha256');
module.exports.SHA512 = require('./sha512');
module.exports.RIPEMD160 = require('./ripemd160');
module.exports.HMAC = require('./hmac');

38
src/crypto-js/hmac.js

@ -0,0 +1,38 @@
/*!
* Crypto-JS v2.0.0
* http://code.google.com/p/crypto-js/
* Copyright (c) 2009, Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License
*/
var conv = require('../convert'),
util = require('../util');
// Shortcuts
module.exports = function (hasher, message, key, options) {
// Convert to byte arrays
if (message.constructor == String) message = conv.stringToBytes(message);
if (key.constructor == String) key = conv.stringToBytes(key);
/* else, assume byte arrays already */
// Allow arbitrary length keys
if (key.length > hasher._blocksize)
key = hasher(key, { asBytes: true });
// XOR keys with pad constants
var okey = key.slice(0),
ikey = key.slice(0);
for (var i = 0; i < hasher._blocksize; i++) {
okey[i] ^= 0x5C;
ikey[i] ^= 0x36;
}
var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), { asBytes: true })), { asBytes: true });
return options && options.asBytes ? hmacbytes :
options && options.asString ? conv.bytesToString(hmacbytes) :
conv.bytesToHex(hmacbytes);
};

333
src/crypto-js/ripemd160.js

@ -1,170 +1,209 @@
/*! /*
* Crypto-JS v2.0.0 CryptoJS v3.1.2
* http://code.google.com/p/crypto-js/ code.google.com/p/crypto-js
* Copyright (c) 2009, Jeff Mott. All rights reserved. (c) 2009-2013 by Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License code.google.com/p/crypto-js/wiki/License
* */
* A JavaScript implementation of the RIPEMD-160 Algorithm /** @preserve
* Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009. (c) 2012 by Cédric Mesnil. All rights reserved.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for details.
* Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
* Ported to Crypto-JS by Stefan Thomas.
*/
(function () { Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// Shortcuts
var C = Crypto,
util = C.util,
charenc = C.charenc,
UTF8 = charenc.UTF8,
Binary = charenc.Binary;
// Convert a byte array to little-endian 32-bit words
util.bytesToLWords = function (bytes) {
var output = Array(bytes.length >> 2);
for (var i = 0; i < output.length; i++)
output[i] = 0;
for (var i = 0; i < bytes.length * 8; i += 8)
output[i>>5] |= (bytes[i / 8] & 0xFF) << (i%32);
return output;
};
// Convert little-endian 32-bit words to a byte array
util.lWordsToBytes = function (words) {
var output = [];
for (var i = 0; i < words.length * 32; i += 8)
output.push((words[i>>5] >>> (i % 32)) & 0xff);
return output;
};
// Public API
var RIPEMD160 = C.RIPEMD160 = function (message, options) {
var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message));
return options && options.asBytes ? digestbytes :
options && options.asString ? Binary.bytesToString(digestbytes) :
util.bytesToHex(digestbytes);
};
// The core
RIPEMD160._rmd160 = function (message)
{
// Convert to byte array
if (message.constructor == String) message = UTF8.stringToBytes(message);
var x = util.bytesToLWords(message),
len = message.length * 8;
/* append padding */
x[len >> 5] |= 0x80 << (len % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var h0 = 0x67452301;
var h1 = 0xefcdab89;
var h2 = 0x98badcfe;
var h3 = 0x10325476;
var h4 = 0xc3d2e1f0;
for (var i = 0; i < x.length; i += 16) {
var T;
var A1 = h0, B1 = h1, C1 = h2, D1 = h3, E1 = h4;
var A2 = h0, B2 = h1, C2 = h2, D2 = h3, E2 = h4;
for (var j = 0; j <= 79; ++j) {
T = safe_add(A1, rmd160_f(j, B1, C1, D1));
T = safe_add(T, x[i + rmd160_r1[j]]);
T = safe_add(T, rmd160_K1(j));
T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
T = safe_add(T, x[i + rmd160_r2[j]]);
T = safe_add(T, rmd160_K2(j));
T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
}
T = safe_add(h1, safe_add(C1, D2));
h1 = safe_add(h2, safe_add(D1, E2));
h2 = safe_add(h3, safe_add(E1, A2));
h3 = safe_add(h4, safe_add(A1, B2));
h4 = safe_add(h0, safe_add(B1, C2));
h0 = T;
}
return [h0, h1, h2, h3, h4];
}
function rmd160_f(j, x, y, z) - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
{ - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
(16 <= j && j <= 31) ? (x & y) | (~x & z) : THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(32 <= j && j <= 47) ? (x | ~y) ^ z : */
(48 <= j && j <= 63) ? (x & z) | (y & ~z) :
(64 <= j && j <= 79) ? x ^ (y | ~z) : var conv = require('../convert');
"rmd160_f: j out of range"; var UTF8 = require('./crypto').charenc.UTF8;
}
function rmd160_K1(j) // Constants table
{ var zl = [
return ( 0 <= j && j <= 15) ? 0x00000000 :
(16 <= j && j <= 31) ? 0x5a827999 :
(32 <= j && j <= 47) ? 0x6ed9eba1 :
(48 <= j && j <= 63) ? 0x8f1bbcdc :
(64 <= j && j <= 79) ? 0xa953fd4e :
"rmd160_K1: j out of range";
}
function rmd160_K2(j)
{
return ( 0 <= j && j <= 15) ? 0x50a28be6 :
(16 <= j && j <= 31) ? 0x5c4dd124 :
(32 <= j && j <= 47) ? 0x6d703ef3 :
(48 <= j && j <= 63) ? 0x7a6d76e9 :
(64 <= j && j <= 79) ? 0x00000000 :
"rmd160_K2: j out of range";
}
var rmd160_r1 = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13];
]; var zr = [
var rmd160_r2 = [
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11];
]; var sl = [
var rmd160_s1 = [
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ];
]; var sr = [
var rmd160_s2 = [
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ];
];
/* var hl = [ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E];
* Add integers, wrapping at 2^32. This uses 16-bit operations internally var hr = [ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000];
* to work around bugs in some JS interpreters.
*/ var bytesToWords = function (bytes) {
function safe_add(x, y) var words = [];
{ for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF); words[b >>> 5] |= bytes[i] << (24 - b % 32);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16); }
return (msw << 16) | (lsw & 0xFFFF); return words;
};
var wordsToBytes = function (words) {
var bytes = [];
for (var b = 0; b < words.length * 32; b += 8) {
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
}
return bytes;
};
var processBlock = function (H, M, offset) {
// Swap endian
for (var i = 0; i < 16; i++) {
var offset_i = offset + i;
var M_offset_i = M[offset_i];
// Swap
M[offset_i] = (
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
);
}
// Working variables
var al, bl, cl, dl, el;
var ar, br, cr, dr, er;
ar = al = H[0];
br = bl = H[1];
cr = cl = H[2];
dr = dl = H[3];
er = el = H[4];
// Computation
var t;
for (var i = 0; i < 80; i += 1) {
t = (al + M[offset+zl[i]])|0;
if (i<16){
t += f1(bl,cl,dl) + hl[0];
} else if (i<32) {
t += f2(bl,cl,dl) + hl[1];
} else if (i<48) {
t += f3(bl,cl,dl) + hl[2];
} else if (i<64) {
t += f4(bl,cl,dl) + hl[3];
} else {// if (i<80) {
t += f5(bl,cl,dl) + hl[4];
} }
t = t|0;
t = rotl(t,sl[i]);
t = (t+el)|0;
al = el;
el = dl;
dl = rotl(cl, 10);
cl = bl;
bl = t;
/* t = (ar + M[offset+zr[i]])|0;
* Bitwise rotate a 32-bit number to the left. if (i<16){
t += f5(br,cr,dr) + hr[0];
} else if (i<32) {
t += f4(br,cr,dr) + hr[1];
} else if (i<48) {
t += f3(br,cr,dr) + hr[2];
} else if (i<64) {
t += f2(br,cr,dr) + hr[3];
} else {// if (i<80) {
t += f1(br,cr,dr) + hr[4];
}
t = t|0;
t = rotl(t,sr[i]) ;
t = (t+er)|0;
ar = er;
er = dr;
dr = rotl(cr, 10);
cr = br;
br = t;
}
// Intermediate hash value
t = (H[1] + cl + dr)|0;
H[1] = (H[2] + dl + er)|0;
H[2] = (H[3] + el + ar)|0;
H[3] = (H[4] + al + br)|0;
H[4] = (H[0] + bl + cr)|0;
H[0] = t;
};
function f1(x, y, z) {
return ((x) ^ (y) ^ (z));
}
function f2(x, y, z) {
return (((x)&(y)) | ((~x)&(z)));
}
function f3(x, y, z) {
return (((x) | (~(y))) ^ (z));
}
function f4(x, y, z) {
return (((x) & (z)) | ((y)&(~(z))));
}
function f5(x, y, z) {
return ((x) ^ ((y) |(~(z))));
}
function rotl(x,n) {
return (x<<n) | (x>>>(32-n));
}
/**
* RIPEMD160 hash algorithm.
*/ */
function bit_rol(num, cnt) module.exports = function(message, options) {
{
return (num << cnt) | (num >>> (32 - cnt)); if (message.constructor === String) {
message = UTF8.stringToBytes(message);
} }
})();
var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
var m = bytesToWords(message);
var nBitsLeft = message.length * 8;
var nBitsTotal = message.length * 8;
// Add padding
m[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
m[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
(((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) |
(((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00)
);
for (var i=0 ; i<m.length; i += 16) {
processBlock(H, m, i);
}
// Swap endian
for (var i = 0; i < 5; i++) {
// Shortcut
var H_i = H[i];
// Swap
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
}
var digestbytes = wordsToBytes(H);
return options && options.asBytes ? digestbytes :
options && options.asString ? Binary.bytesToString(digestbytes) :
conv.bytesToHex(digestbytes);
};

7
src/crypto-js/sha256-min.js

@ -1,7 +0,0 @@
/*
* Crypto-JS v2.0.0
* http://code.google.com/p/crypto-js/
* Copyright (c) 2009, Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License
*/
(function(){var g=Crypto,b=g.util,c=g.charenc,f=c.UTF8,e=c.Binary;var a=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];var d=g.SHA256=function(j,h){var i=b.wordsToBytes(d._sha256(j));return h&&h.asBytes?i:h&&h.asString?e.bytesToString(i):b.bytesToHex(i)};d._sha256=function(q){if(q.constructor==String){q=f.stringToBytes(q)}var y=b.bytesToWords(q),z=q.length*8,r=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],s=[],K,J,I,G,F,E,D,C,B,A,p,o;y[z>>5]|=128<<(24-z%32);y[((z+64>>9)<<4)+15]=z;for(var B=0;B<y.length;B+=16){K=r[0];J=r[1];I=r[2];G=r[3];F=r[4];E=r[5];D=r[6];C=r[7];for(var A=0;A<64;A++){if(A<16){s[A]=y[A+B]}else{var n=s[A-15],u=s[A-2],M=((n<<25)|(n>>>7))^((n<<14)|(n>>>18))^(n>>>3),L=((u<<15)|(u>>>17))^((u<<13)|(u>>>19))^(u>>>10);s[A]=M+(s[A-7]>>>0)+L+(s[A-16]>>>0)}var t=F&E^~F&D,k=K&J^K&I^J&I,x=((K<<30)|(K>>>2))^((K<<19)|(K>>>13))^((K<<10)|(K>>>22)),v=((F<<26)|(F>>>6))^((F<<21)|(F>>>11))^((F<<7)|(F>>>25));p=(C>>>0)+v+t+(a[A])+(s[A]>>>0);o=x+k;C=D;D=E;E=F;F=G+p;G=I;I=J;J=K;K=p+o}r[0]+=K;r[1]+=J;r[2]+=I;r[3]+=G;r[4]+=F;r[5]+=E;r[6]+=D;r[7]+=C}return r};d._blocksize=16})();

213
src/crypto-js/sha256.js

@ -1,133 +1,134 @@
/*! /*
* Crypto-JS v2.0.0 CryptoJS v3.1.2
* http://code.google.com/p/crypto-js/ code.google.com/p/crypto-js
* Copyright (c) 2009, Jeff Mott. All rights reserved. (c) 2009-2013 by Jeff Mott. All rights reserved.
* http://code.google.com/p/crypto-js/wiki/License code.google.com/p/crypto-js/wiki/License
*/ */
(function(){
var conv = require('../convert'),
// Shortcuts util = require('../util');
var C = Crypto,
util = C.util, // Initialization round constants tables
charenc = C.charenc, var K = [];
UTF8 = charenc.UTF8,
Binary = charenc.Binary; // Compute constants
(function () {
// Constants function isPrime(n) {
var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, var sqrtN = Math.sqrt(n);
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, for (var factor = 2; factor <= sqrtN; factor++) {
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, if (!(n % factor)) {
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, return false;
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, }
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, }
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ];
// Public API
var SHA256 = C.SHA256 = function (message, options) {
var digestbytes = util.wordsToBytes(SHA256._sha256(message));
return options && options.asBytes ? digestbytes :
options && options.asString ? Binary.bytesToString(digestbytes) :
util.bytesToHex(digestbytes);
};
// The core
SHA256._sha256 = function (message) {
// Convert to byte array
if (message.constructor == String) message = UTF8.stringToBytes(message);
/* else, assume byte array already */
var m = util.bytesToWords(message),
l = message.length * 8,
H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ],
w = [],
a, b, c, d, e, f, g, h, i, j,
t1, t2;
// Padding
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for (var i = 0; i < m.length; i += 16) {
a = H[0]; return true;
b = H[1]; }
c = H[2];
d = H[3];
e = H[4];
f = H[5];
g = H[6];
h = H[7];
for (var j = 0; j < 64; j++) { function getFractionalBits(n) {
return ((n - (n | 0)) * 0x100000000) | 0;
}
if (j < 16) w[j] = m[j + i]; var n = 2;
else { var nPrime = 0;
while (nPrime < 64) {
if (isPrime(n)) {
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
nPrime++;
}
var gamma0x = w[j - 15], n++;
gamma1x = w[j - 2], }
gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ }());
// Reusable object
var W = [];
var processBlock = function (H, M, offset) {
// Working variables
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
var e = H[4];
var f = H[5];
var g = H[6];
var h = H[7];
// Computation
for (var i = 0; i < 64; i++) {
if (i < 16) {
W[i] = M[offset + i] | 0;
} else {
var gamma0x = W[i - 15];
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
((gamma0x << 14) | (gamma0x >>> 18)) ^ ((gamma0x << 14) | (gamma0x >>> 18)) ^
(gamma0x >>> 3), (gamma0x >>> 3);
gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
var gamma1x = W[i - 2];
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
((gamma1x << 13) | (gamma1x >>> 19)) ^ ((gamma1x << 13) | (gamma1x >>> 19)) ^
(gamma1x >>> 10); (gamma1x >>> 10);
w[j] = gamma0 + (w[j - 7] >>> 0) + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
gamma1 + (w[j - 16] >>> 0);
} }
var ch = e & f ^ ~e & g, var ch = (e & f) ^ (~e & g);
maj = a & b ^ a & c ^ b & c, var maj = (a & b) ^ (a & c) ^ (b & c);
sigma0 = ((a << 30) | (a >>> 2)) ^
((a << 19) | (a >>> 13)) ^
((a << 10) | (a >>> 22)),
sigma1 = ((e << 26) | (e >>> 6)) ^
((e << 21) | (e >>> 11)) ^
((e << 7) | (e >>> 25));
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0); var t1 = h + sigma1 + ch + K[i] + W[i];
t2 = sigma0 + maj; var t2 = sigma0 + maj;
h = g; h = g;
g = f; g = f;
f = e; f = e;
e = d + t1; e = (d + t1) | 0;
d = c; d = c;
c = b; c = b;
b = a; b = a;
a = t1 + t2; a = (t1 + t2) | 0;
} }
H[0] += a; // Intermediate hash value
H[1] += b; H[0] = (H[0] + a) | 0;
H[2] += c; H[1] = (H[1] + b) | 0;
H[3] += d; H[2] = (H[2] + c) | 0;
H[4] += e; H[3] = (H[3] + d) | 0;
H[5] += f; H[4] = (H[4] + e) | 0;
H[6] += g; H[5] = (H[5] + f) | 0;
H[7] += h; H[6] = (H[6] + g) | 0;
H[7] = (H[7] + h) | 0;
};
/**
* SHA-256 hash algorithm.
*/
module.exports = function(message, options) {;
var H =[ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ];
if (message.constructor === String) {
message = conv.stringToBytes(message);
} }
return H; var m = util.bytesToWords(message);
var l = message.length * 8;
}; m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for (var i=0 ; i<m.length; i += 16) {
processBlock(H, m, i);
}
// Package private blocksize var digestbytes = util.wordsToBytes(H);
SHA256._blocksize = 16; return options && options.asBytes ? digestbytes :
options && options.asString ? Binary.bytesToString(digestbytes) :
conv.bytesToHex(digestbytes);
};
})(); module.exports._blocksize = 64

239
src/crypto-js/sha512.js

@ -0,0 +1,239 @@
var conv = require('../convert');
var util = require('../util');
var K = [
[0x428a2f98, 0xd728ae22], [0x71374491, 0x23ef65cd],
[0xb5c0fbcf, 0xec4d3b2f], [0xe9b5dba5, 0x8189dbbc],
[0x3956c25b, 0xf348b538], [0x59f111f1, 0xb605d019],
[0x923f82a4, 0xaf194f9b], [0xab1c5ed5, 0xda6d8118],
[0xd807aa98, 0xa3030242], [0x12835b01, 0x45706fbe],
[0x243185be, 0x4ee4b28c], [0x550c7dc3, 0xd5ffb4e2],
[0x72be5d74, 0xf27b896f], [0x80deb1fe, 0x3b1696b1],
[0x9bdc06a7, 0x25c71235], [0xc19bf174, 0xcf692694],
[0xe49b69c1, 0x9ef14ad2], [0xefbe4786, 0x384f25e3],
[0x0fc19dc6, 0x8b8cd5b5], [0x240ca1cc, 0x77ac9c65],
[0x2de92c6f, 0x592b0275], [0x4a7484aa, 0x6ea6e483],
[0x5cb0a9dc, 0xbd41fbd4], [0x76f988da, 0x831153b5],
[0x983e5152, 0xee66dfab], [0xa831c66d, 0x2db43210],
[0xb00327c8, 0x98fb213f], [0xbf597fc7, 0xbeef0ee4],
[0xc6e00bf3, 0x3da88fc2], [0xd5a79147, 0x930aa725],
[0x06ca6351, 0xe003826f], [0x14292967, 0x0a0e6e70],
[0x27b70a85, 0x46d22ffc], [0x2e1b2138, 0x5c26c926],
[0x4d2c6dfc, 0x5ac42aed], [0x53380d13, 0x9d95b3df],
[0x650a7354, 0x8baf63de], [0x766a0abb, 0x3c77b2a8],
[0x81c2c92e, 0x47edaee6], [0x92722c85, 0x1482353b],
[0xa2bfe8a1, 0x4cf10364], [0xa81a664b, 0xbc423001],
[0xc24b8b70, 0xd0f89791], [0xc76c51a3, 0x0654be30],
[0xd192e819, 0xd6ef5218], [0xd6990624, 0x5565a910],
[0xf40e3585, 0x5771202a], [0x106aa070, 0x32bbd1b8],
[0x19a4c116, 0xb8d2d0c8], [0x1e376c08, 0x5141ab53],
[0x2748774c, 0xdf8eeb99], [0x34b0bcb5, 0xe19b48a8],
[0x391c0cb3, 0xc5c95a63], [0x4ed8aa4a, 0xe3418acb],
[0x5b9cca4f, 0x7763e373], [0x682e6ff3, 0xd6b2b8a3],
[0x748f82ee, 0x5defb2fc], [0x78a5636f, 0x43172f60],
[0x84c87814, 0xa1f0ab72], [0x8cc70208, 0x1a6439ec],
[0x90befffa, 0x23631e28], [0xa4506ceb, 0xde82bde9],
[0xbef9a3f7, 0xb2c67915], [0xc67178f2, 0xe372532b],
[0xca273ece, 0xea26619c], [0xd186b8c7, 0x21c0c207],
[0xeada7dd6, 0xcde0eb1e], [0xf57d4f7f, 0xee6ed178],
[0x06f067aa, 0x72176fba], [0x0a637dc5, 0xa2c898a6],
[0x113f9804, 0xbef90dae], [0x1b710b35, 0x131c471b],
[0x28db77f5, 0x23047d84], [0x32caab7b, 0x40c72493],
[0x3c9ebe0a, 0x15c9bebc], [0x431d67c4, 0x9c100d4c],
[0x4cc5d4be, 0xcb3e42b6], [0x597f299c, 0xfc657e2a],
[0x5fcb6fab, 0x3ad6faec], [0x6c44198c, 0x4a475817]
];
// Reusable objects
var W = [];
for (var i = 0; i < 80; i++) W.push([0,0])
/**
* SHA-512 hash algorithm.
*/
var processBlock = function (H, M, offset) {
// Shortcuts
var H0 = H[0];
var H1 = H[1];
var H2 = H[2];
var H3 = H[3];
var H4 = H[4];
var H5 = H[5];
var H6 = H[6];
var H7 = H[7];
var H0h = H0[0];
var H0l = H0[1];
var H1h = H1[0];
var H1l = H1[1];
var H2h = H2[0];
var H2l = H2[1];
var H3h = H3[0];
var H3l = H3[1];
var H4h = H4[0];
var H4l = H4[1];
var H5h = H5[0];
var H5l = H5[1];
var H6h = H6[0];
var H6l = H6[1];
var H7h = H7[0];
var H7l = H7[1];
// Working variables
var ah = H0h;
var al = H0l;
var bh = H1h;
var bl = H1l;
var ch = H2h;
var cl = H2l;
var dh = H3h;
var dl = H3l;
var eh = H4h;
var el = H4l;
var fh = H5h;
var fl = H5l;
var gh = H6h;
var gl = H6l;
var hh = H7h;
var hl = H7l;
// Rounds
for (var i = 0; i < 80; i++) {
// Shortcut
var Wi = W[i];
// Extend message
if (i < 16) {
var Wih = Wi[0] = M[offset + i * 2] | 0;
var Wil = Wi[1] = M[offset + i * 2 + 1] | 0;
} else {
// Gamma0
var gamma0x = W[i - 15];
var gamma0xh = gamma0x[0];
var gamma0xl = gamma0x[1];
var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7);
var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25));
// Gamma1
var gamma1x = W[i - 2];
var gamma1xh = gamma1x[0];
var gamma1xl = gamma1x[1];
var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6);
var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26));
// W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]
var Wi7 = W[i - 7];
var Wi7h = Wi7[0];
var Wi7l = Wi7[1];
var Wi16 = W[i - 16];
var Wi16h = Wi16[0];
var Wi16l = Wi16[1];
var Wil = gamma0l + Wi7l;
var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0);
var Wil = Wil + gamma1l;
var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0);
var Wil = Wil + Wi16l;
var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0);
Wi[0] = Wih;
Wi[1] = Wil;
}
var chh = (eh & fh) ^ (~eh & gh);
var chl = (el & fl) ^ (~el & gl);
var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);
var majl = (al & bl) ^ (al & cl) ^ (bl & cl);
var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));
var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));
var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9));
var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9));
// t1 = h + sigma1 + ch + K[i] + W[i]
var Ki = K[i];
var Kih = Ki[0];
var Kil = Ki[1];
var t1l = hl + sigma1l;
var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);
var t1l = t1l + chl;
var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
var t1l = t1l + Kil;
var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0);
var t1l = t1l + Wil;
var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0);
// t2 = sigma0 + maj
var t2l = sigma0l + majl;
var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);
// Update working variables
hh = gh;
hl = gl;
gh = fh;
gl = fl;
fh = eh;
fl = el;
el = (dl + t1l) | 0;
eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
dh = ch;
dl = cl;
ch = bh;
cl = bl;
bh = ah;
bl = al;
al = (t1l + t2l) | 0;
ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;
}
// Intermediate hash value
H0l = H0[1] = (H0l + al);
H0[0] = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0));
H1l = H1[1] = (H1l + bl);
H1[0] = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0));
H2l = H2[1] = (H2l + cl);
H2[0] = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0));
H3l = H3[1] = (H3l + dl);
H3[0] = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0));
H4l = H4[1] = (H4l + el);
H4[0] = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0));
H5l = H5[1] = (H5l + fl);
H5[0] = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0));
H6l = H6[1] = (H6l + gl);
H6[0] = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0));
H7l = H7[1] = (H7l + hl);
H7[0] = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0));
return H
}
module.exports = function(message, options) {;
var H = [
[0x6a09e667, 0xf3bcc908], [0xbb67ae85, 0x84caa73b],
[0x3c6ef372, 0xfe94f82b], [0xa54ff53a, 0x5f1d36f1],
[0x510e527f, 0xade682d1], [0x9b05688c, 0x2b3e6c1f],
[0x1f83d9ab, 0xfb41bd6b], [0x5be0cd19, 0x137e2179]
];
if (message.constructor === String) {
message = conv.stringToBytes(message);
}
var m = util.bytesToWords(message);
var l = message.length * 8;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 10) << 5) + 31] = l;
for (var i=0 ; i<m.length; i += 32) {
processBlock(H, m, i);
}
var digestbytes = util.wordsToBytes(Array.prototype.concat.apply([],H))
return options && options.asBytes ? digestbytes :
options && options.asString ? Binary.bytesToString(digestbytes) :
conv.bytesToHex(digestbytes);
};
module.exports._blocksize = 128

266
src/ecdsa.js

@ -1,201 +1,18 @@
function integerToBytes(i, len) { var sec = require('./jsbn/sec');
var bytes = i.toByteArrayUnsigned(); var util = require('./util');
var SecureRandom = require('./jsbn/rng');
var BigInteger = require('./jsbn/jsbn');
var conv = require('./convert')
var Crypto = require('./crypto-js/crypto.js')
if (len < bytes.length) { var ECPointFp = require('./jsbn/ec').ECPointFp;
bytes = bytes.slice(bytes.length-len);
} else while (len > bytes.length) {
bytes.unshift(0);
}
return bytes;
};
ECFieldElementFp.prototype.getByteLength = function () {
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
};
ECPointFp.prototype.getEncoded = function (compressed) {
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
// Get value as a 32-byte Buffer
// Fixed length based on a patch by bitaddress.org and Casascius
var enc = integerToBytes(x, 32);
if (compressed) {
if (y.isEven()) {
// Compressed even pubkey
// M = 02 || X
enc.unshift(0x02);
} else {
// Compressed uneven pubkey
// M = 03 || X
enc.unshift(0x03);
}
} else {
// Uncompressed pubkey
// M = 04 || X || Y
enc.unshift(0x04);
enc = enc.concat(integerToBytes(y, 32));
}
return enc;
};
ECPointFp.decodeFrom = function (curve, enc) {
var type = enc[0];
var dataLen = enc.length-1;
// Extract x and y as byte arrays
var xBa = enc.slice(1, 1 + dataLen/2);
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
// Prepend zero byte to prevent interpretation as negative integer
xBa.unshift(0);
yBa.unshift(0);
// Convert to BigIntegers
var x = new BigInteger(xBa);
var y = new BigInteger(yBa);
// Return point
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
};
ECPointFp.prototype.add2D = function (b) {
if(this.isInfinity()) return b;
if(b.isInfinity()) return this;
if (this.x.equals(b.x)) {
if (this.y.equals(b.y)) {
// this = b, i.e. this must be doubled
return this.twice();
}
// this = -b, i.e. the result is the point at infinity
return this.curve.getInfinity();
}
var x_x = b.x.subtract(this.x);
var y_y = b.y.subtract(this.y);
var gamma = y_y.divide(x_x);
var x3 = gamma.square().subtract(this.x).subtract(b.x);
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.twice2D = function () {
if (this.isInfinity()) return this;
if (this.y.toBigInteger().signum() == 0) {
// if y1 == 0, then (x1, y1) == (x1, -y1)
// and hence this = -this and thus 2(x1, y1) == infinity
return this.curve.getInfinity();
}
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO));
var x3 = gamma.square().subtract(this.x.multiply(TWO));
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.multiply2D = function (k) {
if(this.isInfinity()) return this;
if(k.signum() == 0) return this.curve.getInfinity();
var e = k;
var h = e.multiply(new BigInteger("3"));
var neg = this.negate(); var rng = new SecureRandom();
var R = this; var ecparams = sec("secp256k1");
var P_OVER_FOUR = null;
var i; function implShamirsTrick(P, k, Q, l)
for (i = h.bitLength() - 2; i > 0; --i) { {
R = R.twice();
var hBit = h.testBit(i);
var eBit = e.testBit(i);
if (hBit != eBit) {
R = R.add2D(hBit ? this : neg);
}
}
return R;
};
ECPointFp.prototype.isOnCurve = function () {
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
var a = this.curve.getA().toBigInteger();
var b = this.curve.getB().toBigInteger();
var n = this.curve.getQ();
var lhs = y.multiply(y).mod(n);
var rhs = x.multiply(x).multiply(x)
.add(a.multiply(x)).add(b).mod(n);
return lhs.equals(rhs);
};
ECPointFp.prototype.toString = function () {
return '('+this.getX().toBigInteger().toString()+','+
this.getY().toBigInteger().toString()+')';
};
/**
* Validate an elliptic curve point.
*
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive
*/
ECPointFp.prototype.validate = function () {
var n = this.curve.getQ();
// Check Q != O
if (this.isInfinity()) {
throw new Error("Point is at infinity.");
}
// Check coordinate bounds
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
if (x.compareTo(BigInteger.ONE) < 0 ||
x.compareTo(n.subtract(BigInteger.ONE)) > 0) {
throw new Error('x coordinate out of bounds');
}
if (y.compareTo(BigInteger.ONE) < 0 ||
y.compareTo(n.subtract(BigInteger.ONE)) > 0) {
throw new Error('y coordinate out of bounds');
}
// Check y^2 = x^3 + ax + b (mod n)
if (!this.isOnCurve()) {
throw new Error("Point is not on the curve.");
}
// Check nQ = 0 (Q is a scalar multiple of G)
if (this.multiply(n).isInfinity()) {
// TODO: This check doesn't work - fix.
throw new Error("Point is not a scalar multiple of G.");
}
return true;
};
function dmp(v) {
if (!(v instanceof BigInteger)) v = v.toBigInteger();
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
};
Bitcoin.ECDSA = (function () {
var ecparams = getSECCurveByName("secp256k1");
var rng = new SecureRandom();
var P_OVER_FOUR = null;
function implShamirsTrick(P, k, Q, l)
{
var m = Math.max(k.bitLength(), l.bitLength()); var m = Math.max(k.bitLength(), l.bitLength());
var Z = P.add2D(Q); var Z = P.add2D(Q);
var R = P.curve.getInfinity(); var R = P.curve.getInfinity();
@ -219,9 +36,22 @@ Bitcoin.ECDSA = (function () {
} }
return R; return R;
}; };
var ECDSA = { function deterministicGenerateK(hash,key) {
var v = [];
var k = [];
for (var i = 0;i < 32;i++) v.push(1);
for (var i = 0;i < 32;i++) k.push(0);
k = Crypto.HMAC(Crypto.SHA256,v.concat([0]).concat(key).concat(hash),k,{ asBytes: true })
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
k = Crypto.HMAC(Crypto.SHA256,v.concat([1]).concat(key).concat(hash),k,{ asBytes: true })
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
return BigInteger.fromByteArrayUnsigned(v);
}
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))
@ -233,12 +63,10 @@ Bitcoin.ECDSA = (function () {
var n = ecparams.getN(); var n = ecparams.getN();
var e = BigInteger.fromByteArrayUnsigned(hash); var e = BigInteger.fromByteArrayUnsigned(hash);
do { var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
var k = ECDSA.getBigRandom(n);
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);
} while (r.compareTo(BigInteger.ZERO) <= 0);
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);
@ -247,7 +75,7 @@ Bitcoin.ECDSA = (function () {
verify: function (hash, sig, pubkey) { verify: function (hash, sig, pubkey) {
var r,s; var r,s;
if (Bitcoin.Util.isArray(sig)) { if (util.isArray(sig)) {
var obj = ECDSA.parseSig(sig); var obj = ECDSA.parseSig(sig);
r = obj.r; r = obj.r;
s = obj.s; s = obj.s;
@ -255,16 +83,16 @@ Bitcoin.ECDSA = (function () {
r = sig.r; r = sig.r;
s = sig.s; s = sig.s;
} else { } else {
throw "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 (Bitcoin.Util.isArray(pubkey)) { } else if (util.isArray(pubkey)) {
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
} else { } else {
throw "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);
@ -361,14 +189,14 @@ Bitcoin.ECDSA = (function () {
parseSigCompact: function (sig) { parseSigCompact: function (sig) {
if (sig.length !== 65) { if (sig.length !== 65) {
throw "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 "Invalid signature type"; throw new Error("Invalid signature type");
} }
var n = ecparams.getN(); var n = ecparams.getN();
@ -438,12 +266,13 @@ Bitcoin.ECDSA = (function () {
Q.validate(); Q.validate();
if (!ECDSA.verifyRaw(e, r, s, Q)) { if (!ECDSA.verifyRaw(e, r, s, Q)) {
throw "Pubkey recovery unsuccessful"; throw new Error("Pubkey recovery unsuccessful");
} }
var pubKey = new Bitcoin.ECKey(); // TODO (shtylman) this is stupid because this file and eckey
pubKey.pub = Q; // have circular dependencies
return pubKey; var ECPubKey = require('./eckey').ECPubKey;
return ECPubKey(Q);
}, },
/** /**
@ -457,19 +286,20 @@ Bitcoin.ECDSA = (function () {
* This function simply tries all four cases and returns the value * This function simply tries all four cases and returns the value
* that resulted in a successful pubkey recovery. * that resulted in a successful pubkey recovery.
*/ */
calcPubkeyRecoveryParam: function (address, r, s, hash) calcPubkeyRecoveryParam: function (origPubkey, r, s, hash)
{ {
var address = origPubkey.getBitcoinAddress().toString();
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
try { var pubkey = ECDSA.recoverPubKey(r, s, hash, i);
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i); pubkey.compressed = origPubkey.compressed;
if (pubkey.getBitcoinAddress().toString() == address) { if (pubkey.getBitcoinAddress().toString() == address) {
return i; return i;
} }
} catch (e) {}
} }
throw "Unable to find valid recovery factor";
throw new Error("Unable to find valid recovery factor");
} }
}; };
module.exports = ECDSA;
return ECDSA;
})();

274
src/eckey.js

@ -1,131 +1,179 @@
Bitcoin.ECKey = (function () { var BigInteger = require('./jsbn/jsbn');
var ECDSA = Bitcoin.ECDSA; var sec = require('./jsbn/sec');
var ecparams = getSECCurveByName("secp256k1"); var base58 = require('./base58');
var rng = new SecureRandom(); var Crypto = require('./crypto-js/crypto');
var util = require('./util');
var ECKey = function (input) { var conv = require('./convert');
var Address = require('./address');
var ecdsa = require('./ecdsa');
var ECPointFp = require('./jsbn/ec').ECPointFp;
var ecparams = sec("secp256k1");
// input can be nothing, array of bytes, hex string, or base58 string
var ECKey = function (input,compressed) {
if (!(this instanceof ECKey)) { return new ECKey(input); }
if (!input) { if (!input) {
// Generate new key // Generate new key
var n = ecparams.getN(); var n = ecparams.getN();
this.priv = ECDSA.getBigRandom(n); this.priv = ecdsa.getBigRandom(n);
} else if (input instanceof BigInteger) { this.compressed = compressed || false;
// Input is a private key value
this.priv = input;
} else if (Bitcoin.Util.isArray(input)) {
// Prepend zero byte to prevent interpretation as negative integer
this.priv = BigInteger.fromByteArrayUnsigned(input);
} else if ("string" == typeof input) {
if (input.length == 51 && input[0] == '5') {
// Base58 encoded private key
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input));
} else {
// Prepend zero byte to prevent interpretation as negative integer
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input));
}
} }
this.compressed = !!ECKey.compressByDefault; else this.import(input,compressed)
}; };
/** ECKey.prototype.import = function (input,compressed) {
* Whether public keys should be returned compressed by default. function has(li,v) { return li.indexOf(v) >= 0 }
function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) }
this.priv =
input instanceof ECKey ? input.priv
: input instanceof BigInteger ? input.mod(ecparams.getN())
: util.isArray(input) ? fromBin(input.slice(0,32))
: typeof input != "string" ? null
: input.length == 51 && input[0] == '5' ? fromBin(base58.checkDecode(input))
: input.length == 52 && has('LK',input[0]) ? fromBin(base58.checkDecode(input))
: has([64,65],input.length) ? fromBin(conv.hexToBytes(input.slice(0,64)))
: null
this.compressed =
compressed !== undefined ? compressed
: input instanceof ECKey ? input.compressed
: input instanceof BigInteger ? false
: util.isArray(input) ? false
: typeof input != "string" ? null
: input.length == 51 && input[0] == '5' ? false
: input.length == 52 && has('LK',input[0]) ? true
: input.length == 64 ? false
: input.length == 65 ? true
: null
};
ECKey.prototype.getPub = function(compressed) {
if (compressed === undefined) compressed = this.compressed
return ECPubKey(ecparams.getG().multiply(this.priv),compressed)
}
/**
* @deprecated Reserved keyword, factory pattern. Use toHex, toBytes, etc.
*/ */
ECKey.compressByDefault = false; ECKey.prototype['export'] = function(format) {
format || (format = 'hex')
return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]()
};
/** ECKey.prototype.toBin = function() {
* Set whether the public key should be returned compressed or not. return conv.bytesToString(this.toBytes())
*/ }
ECKey.prototype.setCompressed = function (v) {
this.compressed = !!v;
};
/** ECKey.prototype.toBase58 = function() {
* Return public key in DER encoding. return base58.checkEncode(this.toBytes(), 128)
*/ }
ECKey.prototype.getPub = function () {
return this.getPubPoint().getEncoded(this.compressed);
};
/** ECKey.prototype.toWif = ECKey.prototype.toBase58
* Return public point as ECPoint object.
*/
ECKey.prototype.getPubPoint = function () {
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
return this.pub; ECKey.prototype.toHex = function() {
}; return conv.bytesToHex(this.toBytes())
}
/** ECKey.prototype.toBytes = function() {
* Get the pubKeyHash for this key. var bytes = this.priv.toByteArrayUnsigned();
* if (this.compressed) bytes.push(1)
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as return bytes
* a byte array. }
*/
ECKey.prototype.getPubKeyHash = function () {
if (this.pubKeyHash) return this.pubKeyHash;
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub());
};
ECKey.prototype.getBitcoinAddress = function () {
var hash = this.getPubKeyHash();
var addr = new Bitcoin.Address(hash);
return addr;
};
ECKey.prototype.getExportedPrivateKey = function () {
var hash = this.priv.toByteArrayUnsigned();
while (hash.length < 32) hash.unshift(0);
hash.unshift(0x80);
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
var bytes = hash.concat(checksum.slice(0,4));
return Bitcoin.Base58.encode(bytes);
};
ECKey.prototype.setPub = function (pub) {
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
};
ECKey.prototype.toString = function (format) {
if (format === "base64") {
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
} else {
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
}
};
ECKey.prototype.sign = function (hash) { ECKey.prototype.toString = ECKey.prototype.toBase58
return ECDSA.sign(hash, this.priv);
};
ECKey.prototype.verify = function (hash, sig) { ECKey.prototype.getBitcoinAddress = function(v) {
return ECDSA.verify(hash, sig, this.getPub()); return this.getPub().getBitcoinAddress(v)
}; }
/** ECKey.prototype.add = function(key) {
* Parse an exported private key contained in a string. return ECKey(this.priv.add(ECKey(key).priv),this.compressed)
*/ }
ECKey.decodeString = function (string) {
var bytes = Bitcoin.Base58.decode(string);
var hash = bytes.slice(0, 33); ECKey.prototype.multiply = function(key) {
return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed)
}
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); var ECPubKey = function(input,compressed) {
if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed); }
if (checksum[0] != bytes[33] || if (!input) {
checksum[1] != bytes[34] || // Generate new key
checksum[2] != bytes[35] || var n = ecparams.getN();
checksum[3] != bytes[36]) { this.pub = ecparams.getG().multiply(ecdsa.getBigRandom(n))
throw "Checksum validation failed!"; this.compressed = compressed || false;
}
var version = hash.shift();
if (version != 0x80) {
throw "Version "+version+" not supported!";
} }
else this.import(input,compressed)
}
ECPubKey.prototype.import = function(input,compressed) {
var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) }
this.pub =
input instanceof ECPointFp ? input
: input instanceof ECKey ? ecparams.getG().multiply(input.priv)
: input instanceof ECPubKey ? input.pub
: typeof input == "string" ? decode(conv.hexToBytes(input))
: util.isArray(input) ? decode(input)
: ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN()))
this.compressed =
arguments.length > 1 ? compressed
: input instanceof ECPointFp ? input.compressed
: input instanceof ECPubKey ? input.compressed
: (this.pub[0] < 4)
}
ECPubKey.prototype.add = function(key) {
return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed)
}
ECPubKey.prototype.multiply = function(key) {
return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed)
}
ECPubKey.prototype['export'] = function(format) {
format || (format = 'hex')
return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]()
}
ECPubKey.prototype.toBytes = function(compressed) {
if (compressed === undefined) compressed = this.compressed
return this.pub.getEncoded(compressed)
}
ECPubKey.prototype.toHex = function() {
return conv.bytesToHex(this.toBytes())
}
ECPubKey.prototype.toBin = function() {
return conv.bytesToString(this.toBytes())
}
ECPubKey.prototype.toBase58 = function() {
return base58.checkEncode(this.toBytes(), 128)
}
ECPubKey.prototype.toWif = ECPubKey.prototype.toBase58
ECPubKey.prototype.toString = function() {
return this.getBitcoinAddress().toString()
}
ECPubKey.prototype.getBitcoinAddress = function(v) {
return new Address(util.sha256ripe160(this.toBytes()), v);
}
ECKey.prototype.sign = function (hash) {
return ecdsa.sign(hash, this.priv);
};
ECKey.prototype.verify = function (hash, sig) {
return ecdsa.verify(hash, sig, this.getPub()['export']('bytes'));
};
/**
* Parse an exported private key contained in a string.
*/
return hash;
};
return ECKey; module.exports = { ECKey: ECKey, ECPubKey: ECPubKey };
})();

235
src/hdwallet.js

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

54
src/index.js

@ -0,0 +1,54 @@
// Bit-wise rotate left
var rotl = function (n, b) {
return (n << b) | (n >>> (32 - b));
};
// Bit-wise rotate right
var rotr = function (n, b) {
return (n << (32 - b)) | (n >>> b);
};
// Swap big-endian to little-endian and vice versa
var endian = function (n) {
// If number given, swap endian
if (n.constructor == Number) {
return rotl(n, 8) & 0x00FF00FF | rotl(n, 24) & 0xFF00FF00;
}
// Else, assume array and swap all items
for (var i = 0; i < n.length; i++) {
n[i] = endian(n[i]);
}
return n;
}
var Key = require('./eckey');
module.exports = {
Address: require('./address'),
Key: Key.ECKey,
ECKey: Key.ECKey,
ECPubKey: Key.ECPubKey,
Message: require('./message'),
BigInteger: require('./jsbn/jsbn'),
Crypto: require('./crypto-js/crypto'),
Script: require('./script'),
Opcode: require('./opcode'),
Transaction: require('./transaction').Transaction,
Util: require('./util'),
TransactionIn: require('./transaction').TransactionIn,
TransactionOut: require('./transaction').TransactionOut,
ECPointFp: require('./jsbn/ec').ECPointFp,
Wallet: require('./wallet'),
ecdsa: require('./ecdsa'),
HDWallet: require('./hdwallet.js'),
// base58 encoding/decoding to bytes
base58: require('./base58'),
// conversions
convert: require('./convert'),
endian: endian
}

208
src/jsbn/ec.js

@ -2,7 +2,8 @@
// Ported loosely from BouncyCastle's Java EC code // Ported loosely from BouncyCastle's Java EC code
// Only Fp curves implemented for now // Only Fp curves implemented for now
// Requires jsbn.js and jsbn2.js var BigInteger = require('./jsbn'),
sec = require('./sec');
// ---------------- // ----------------
// ECFieldElementFp // ECFieldElementFp
@ -314,3 +315,208 @@ ECCurveFp.prototype.equals = curveFpEquals;
ECCurveFp.prototype.getInfinity = curveFpGetInfinity; ECCurveFp.prototype.getInfinity = curveFpGetInfinity;
ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger;
ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex;
// prepends 0 if bytes < len
// cuts off start if bytes > len
function integerToBytes(i, len) {
var bytes = i.toByteArrayUnsigned();
if (len < bytes.length) {
bytes = bytes.slice(bytes.length-len);
} else while (len > bytes.length) {
bytes.unshift(0);
}
return bytes;
};
ECFieldElementFp.prototype.getByteLength = function () {
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
};
ECPointFp.prototype.getEncoded = function (compressed) {
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
// Get value as a 32-byte Buffer
// Fixed length based on a patch by bitaddress.org and Casascius
var enc = integerToBytes(x, 32);
if (compressed) {
if (y.isEven()) {
// Compressed even pubkey
// M = 02 || X
enc.unshift(0x02);
} else {
// Compressed uneven pubkey
// M = 03 || X
enc.unshift(0x03);
}
} else {
// Uncompressed pubkey
// M = 04 || X || Y
enc.unshift(0x04);
enc = enc.concat(integerToBytes(y, 32));
}
return enc;
};
ECPointFp.decodeFrom = function (ecparams, enc) {
var type = enc[0];
var dataLen = enc.length-1;
// Extract x and y as byte arrays
if (type == 4) {
var xBa = enc.slice(1, 1 + dataLen/2),
yBa = enc.slice(1 + dataLen/2, 1 + dataLen),
x = BigInteger.fromByteArrayUnsigned(xBa),
y = BigInteger.fromByteArrayUnsigned(yBa);
}
else {
var xBa = enc.slice(1),
x = BigInteger.fromByteArrayUnsigned(xBa),
p = ecparams.getQ(),
xCubedPlus7 = x.multiply(x).multiply(x).add(new BigInteger('7')).mod(p),
pPlus1Over4 = p.add(new BigInteger('1'))
.divide(new BigInteger('4')),
y = xCubedPlus7.modPow(pPlus1Over4,p);
if (y.mod(new BigInteger('2')).toString() != ''+(type % 2)) {
y = p.subtract(y)
}
}
// Prepend zero byte to prevent interpretation as negative integer
// Convert to BigIntegers
// Return point
return new ECPointFp(ecparams,
ecparams.fromBigInteger(x),
ecparams.fromBigInteger(y));
};
ECPointFp.prototype.add2D = function (b) {
if(this.isInfinity()) return b;
if(b.isInfinity()) return this;
if (this.x.equals(b.x)) {
if (this.y.equals(b.y)) {
// this = b, i.e. this must be doubled
return this.twice();
}
// this = -b, i.e. the result is the point at infinity
return this.curve.getInfinity();
}
var x_x = b.x.subtract(this.x);
var y_y = b.y.subtract(this.y);
var gamma = y_y.divide(x_x);
var x3 = gamma.square().subtract(this.x).subtract(b.x);
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.twice2D = function () {
if (this.isInfinity()) return this;
if (this.y.toBigInteger().signum() == 0) {
// if y1 == 0, then (x1, y1) == (x1, -y1)
// and hence this = -this and thus 2(x1, y1) == infinity
return this.curve.getInfinity();
}
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO));
var x3 = gamma.square().subtract(this.x.multiply(TWO));
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.multiply2D = function (k) {
if(this.isInfinity()) return this;
if(k.signum() == 0) return this.curve.getInfinity();
var e = k;
var h = e.multiply(new BigInteger("3"));
var neg = this.negate();
var R = this;
var i;
for (i = h.bitLength() - 2; i > 0; --i) {
R = R.twice();
var hBit = h.testBit(i);
var eBit = e.testBit(i);
if (hBit != eBit) {
R = R.add2D(hBit ? this : neg);
}
}
return R;
};
ECPointFp.prototype.isOnCurve = function () {
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
var a = this.curve.getA().toBigInteger();
var b = this.curve.getB().toBigInteger();
var n = this.curve.getQ();
var lhs = y.multiply(y).mod(n);
var rhs = x.multiply(x).multiply(x)
.add(a.multiply(x)).add(b).mod(n);
return lhs.equals(rhs);
};
ECPointFp.prototype.toString = function () {
return '('+this.getX().toBigInteger().toString()+','+
this.getY().toBigInteger().toString()+')';
};
/**
* Validate an elliptic curve point.
*
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive
*/
ECPointFp.prototype.validate = function () {
var n = this.curve.getQ();
// Check Q != O
if (this.isInfinity()) {
throw new Error("Point is at infinity.");
}
// Check coordinate bounds
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
if (x.compareTo(BigInteger.ONE) < 0 ||
x.compareTo(n.subtract(BigInteger.ONE)) > 0) {
throw new Error('x coordinate out of bounds');
}
if (y.compareTo(BigInteger.ONE) < 0 ||
y.compareTo(n.subtract(BigInteger.ONE)) > 0) {
throw new Error('y coordinate out of bounds');
}
// Check y^2 = x^3 + ax + b (mod n)
if (!this.isOnCurve()) {
throw new Error("Point is not on the curve.");
}
// Check nQ = 0 (Q is a scalar multiple of G)
if (this.multiply(n).isInfinity()) {
// TODO: This check doesn't work - fix.
throw new Error("Point is not a scalar multiple of G.");
}
return true;
};
module.exports = ECCurveFp;
module.exports.ECPointFp = ECPointFp;

912
src/jsbn/jsbn.js

File diff suppressed because it is too large

656
src/jsbn/jsbn2.js

@ -1,656 +0,0 @@
// Copyright (c) 2005-2009 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Extended JavaScript BN functions, required for RSA private ops.
// Version 1.1: new BigInteger("0", 10) returns "proper" zero
// Version 1.2: square() API, isProbablePrime fix
// (public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
// (public) return value as integer
function bnIntValue() {
if(this.s < 0) {
if(this.t == 1) return this[0]-this.DV;
else if(this.t == 0) return -1;
}
else if(this.t == 1) return this[0];
else if(this.t == 0) return 0;
// assumes 16 < DB < 32
return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
}
// (public) return value as byte
function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }
// (public) return value as short (assumes DB>=16)
function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; }
// (protected) return x s.t. r^x < DV
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }
// (public) 0 if this == 0, 1 if this > 0
function bnSigNum() {
if(this.s < 0) return -1;
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
else return 1;
}
// (protected) convert to radix string
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
return z.intValue().toString(b) + r;
}
// (protected) convert from radix string
function bnpFromRadix(s,b) {
this.fromInt(0);
if(b == null) b = 10;
var cs = this.chunkSize(b);
var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
for(var i = 0; i < s.length; ++i) {
var x = intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
this.dAddOffset(w,0);
}
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) alternate constructor
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new BigInteger(int,RNG)
var x = new Array(), t = a&7;
x.length = (a>>3)+1;
b.nextBytes(x);
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
this.fromString(x,256);
}
}
// (public) convert to bigendian byte array
function bnToByteArray() {
var i = this.t, r = new Array();
r[0] = this.s;
var p = this.DB-(i*this.DB)%8, d, k = 0;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
r[k++] = d|(this.s<<(this.DB-p));
while(i >= 0) {
if(p < 8) {
d = (this[i]&((1<<p)-1))<<(8-p);
d |= this[--i]>>(p+=this.DB-8);
}
else {
d = (this[i]>>(p-=8))&0xff;
if(p <= 0) { p += this.DB; --i; }
}
if((d&0x80) != 0) d |= -256;
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
if(k > 0 || d != this.s) r[k++] = d;
}
}
return r;
}
function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
// (protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
if(a.t < this.t) {
f = a.s&this.DM;
for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
r.t = this.t;
}
else {
f = this.s&this.DM;
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}
// (public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
// (public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
// (public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
// (public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
// (public) ~this
function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
r.t = this.t;
r.s = ~this.s;
return r;
}
// (public) this << n
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}
// (public) this >> n
function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}
// return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
if(x == 0) return -1;
var r = 0;
if((x&0xffff) == 0) { x >>= 16; r += 16; }
if((x&0xff) == 0) { x >>= 8; r += 8; }
if((x&0xf) == 0) { x >>= 4; r += 4; }
if((x&3) == 0) { x >>= 2; r += 2; }
if((x&1) == 0) ++r;
return r;
}
// (public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
if(this[i] != 0) return i*this.DB+lbit(this[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}
// return number of 1 bits in x
function cbit(x) {
var r = 0;
while(x != 0) { x &= x-1; ++r; }
return r;
}
// (public) return number of set bits
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
return r;
}
// (public) true iff nth bit is set
function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this[j]&(1<<(n%this.DB)))!=0);
}
// (protected) this op (1<<n)
function bnpChangeBit(n,op) {
var r = BigInteger.ONE.shiftLeft(n);
this.bitwiseTo(r,op,r);
return r;
}
// (public) this | (1<<n)
function bnSetBit(n) { return this.changeBit(n,op_or); }
// (public) this & ~(1<<n)
function bnClearBit(n) { return this.changeBit(n,op_andnot); }
// (public) this ^ (1<<n)
function bnFlipBit(n) { return this.changeBit(n,op_xor); }
// (protected) r = this + a
function bnpAddTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]+a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c += a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c += a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += a.s;
}
r.s = (c<0)?-1:0;
if(c > 0) r[i++] = c;
else if(c < -1) r[i++] = this.DV+c;
r.t = i;
r.clamp();
}
// (public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
// (public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
// (public) this^2
function bnSquare() { var r = nbi(); this.squareTo(r); return r; }
// (public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
// (public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
// (public) [this/a,this%a]
function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}
// (protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
this[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}
// (protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
if(n == 0) return;
while(this.t <= w) this[this.t++] = 0;
this[w] += n;
while(this[w] >= this.DV) {
this[w] -= this.DV;
if(++w >= this.t) this[this.t++] = 0;
++this[w];
}
}
// A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }
NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;
// (public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }
// (protected) r = lower n words of "this * a", a.t <= n
// "this" should be the larger one if appropriate.
function bnpMultiplyLowerTo(a,n,r) {
var i = Math.min(this.t+a.t,n);
r.s = 0; // assumes a,this >= 0
r.t = i;
while(i > 0) r[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
r.clamp();
}
// (protected) r = "this * a" without lower n words, n > 0
// "this" should be the larger one if appropriate.
function bnpMultiplyUpperTo(a,n,r) {
--n;
var i = r.t = this.t+a.t-n;
r.s = 0; // assumes a,this >= 0
while(--i >= 0) r[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,r);
}
// Barrett modular reduction
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}
function barrettRevert(x) { return x; }
// x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;
// (public) this^e % m (HAC 14.85)
function bnModPow(e,m) {
var i = e.bitLength(), k, r = nbv(1), z;
if(i <= 0) return r;
else if(i < 18) k = 1;
else if(i < 48) k = 3;
else if(i < 144) k = 4;
else if(i < 768) k = 5;
else k = 6;
if(i < 8)
z = new Classic(m);
else if(m.isEven())
z = new Barrett(m);
else
z = new Montgomery(m);
// precomputation
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
g[1] = z.convert(this);
if(k > 1) {
var g2 = nbi();
z.sqrTo(g[1],g2);
while(n <= km) {
g[n] = nbi();
z.mulTo(g2,g[n-2],g[n]);
n += 2;
}
}
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
i = nbits(e[j])-1;
while(j >= 0) {
if(i >= k1) w = (e[j]>>(i-k1))&km;
else {
w = (e[j]&((1<<(i+1))-1))<<(k1-i);
if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
}
n = k;
while((w&1) == 0) { w >>= 1; --n; }
if((i -= n) < 0) { i += this.DB; --j; }
if(is1) { // ret == 1, don't bother squaring or multiplying it
g[w].copyTo(r);
is1 = false;
}
else {
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
z.mulTo(r2,g[w],r);
}
while(j >= 0 && (e[j]&(1<<i)) == 0) {
z.sqrTo(r,r2); t = r; r = r2; r2 = t;
if(--i < 0) { i = this.DB-1; --j; }
}
}
return z.revert(r);
}
// (public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
if(x.compareTo(y) >= 0) {
x.subTo(y,x);
x.rShiftTo(1,x);
}
else {
y.subTo(x,y);
y.rShiftTo(1,y);
}
}
if(g > 0) y.lShiftTo(g,y);
return y;
}
// (protected) this % n, n < 2^26
function bnpModInt(n) {
if(n <= 0) return 0;
var d = this.DV%n, r = (this.s<0)?n-1:0;
if(this.t > 0)
if(d == 0) r = this[0]%n;
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
return r;
}
// (public) 1/this % m (HAC 14.61)
function bnModInverse(m) {
var ac = m.isEven();
if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
var u = m.clone(), v = this.clone();
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
while(u.signum() != 0) {
while(u.isEven()) {
u.rShiftTo(1,u);
if(ac) {
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
a.rShiftTo(1,a);
}
else if(!b.isEven()) b.subTo(m,b);
b.rShiftTo(1,b);
}
while(v.isEven()) {
v.rShiftTo(1,v);
if(ac) {
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
c.rShiftTo(1,c);
}
else if(!d.isEven()) d.subTo(m,d);
d.rShiftTo(1,d);
}
if(u.compareTo(v) >= 0) {
u.subTo(v,u);
if(ac) a.subTo(c,a);
b.subTo(d,b);
}
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
// (public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
for(i = 0; i < lowprimes.length; ++i)
if(x[0] == lowprimes[i]) return true;
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}
// protected
BigInteger.prototype.chunkSize = bnpChunkSize;
BigInteger.prototype.toRadix = bnpToRadix;
BigInteger.prototype.fromRadix = bnpFromRadix;
BigInteger.prototype.fromNumber = bnpFromNumber;
BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
BigInteger.prototype.changeBit = bnpChangeBit;
BigInteger.prototype.addTo = bnpAddTo;
BigInteger.prototype.dMultiply = bnpDMultiply;
BigInteger.prototype.dAddOffset = bnpDAddOffset;
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
BigInteger.prototype.modInt = bnpModInt;
BigInteger.prototype.millerRabin = bnpMillerRabin;
// public
BigInteger.prototype.clone = bnClone;
BigInteger.prototype.intValue = bnIntValue;
BigInteger.prototype.byteValue = bnByteValue;
BigInteger.prototype.shortValue = bnShortValue;
BigInteger.prototype.signum = bnSigNum;
BigInteger.prototype.toByteArray = bnToByteArray;
BigInteger.prototype.equals = bnEquals;
BigInteger.prototype.min = bnMin;
BigInteger.prototype.max = bnMax;
BigInteger.prototype.and = bnAnd;
BigInteger.prototype.or = bnOr;
BigInteger.prototype.xor = bnXor;
BigInteger.prototype.andNot = bnAndNot;
BigInteger.prototype.not = bnNot;
BigInteger.prototype.shiftLeft = bnShiftLeft;
BigInteger.prototype.shiftRight = bnShiftRight;
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
BigInteger.prototype.bitCount = bnBitCount;
BigInteger.prototype.testBit = bnTestBit;
BigInteger.prototype.setBit = bnSetBit;
BigInteger.prototype.clearBit = bnClearBit;
BigInteger.prototype.flipBit = bnFlipBit;
BigInteger.prototype.add = bnAdd;
BigInteger.prototype.subtract = bnSubtract;
BigInteger.prototype.multiply = bnMultiply;
BigInteger.prototype.divide = bnDivide;
BigInteger.prototype.remainder = bnRemainder;
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
BigInteger.prototype.modPow = bnModPow;
BigInteger.prototype.modInverse = bnModInverse;
BigInteger.prototype.pow = bnPow;
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
// JSBN-specific extension
BigInteger.prototype.square = bnSquare;
// BigInteger interfaces not implemented in jsbn:
// BigInteger(int signum, byte[] magnitude)
// double doubleValue()
// float floatValue()
// int hashCode()
// long longValue()
// static BigInteger valueOf(long val)

8
src/jsbn/prng4.js

@ -35,11 +35,5 @@ function ARC4next() {
Arcfour.prototype.init = ARC4init; Arcfour.prototype.init = ARC4init;
Arcfour.prototype.next = ARC4next; Arcfour.prototype.next = ARC4next;
// Plug in your RNG constructor here module.exports = Arcfour;
function prng_newstate() {
return new Arcfour();
}
// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
var rng_psize = 256;

17
src/jsbn/rng.js

@ -1,4 +1,16 @@
// Random number generator - requires a PRNG backend, e.g. prng4.js // Random number generator - requires a PRNG backend, e.g. prng4.js
// prng4.js - uses Arcfour as a PRNG
var Arcfour = require('./prng4');
// Plug in your RNG constructor here
function prng_newstate() {
return new Arcfour();
}
// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
var rng_psize = 256;
// For best results, put code like // For best results, put code like
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'> // <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
@ -27,12 +39,15 @@ if(rng_pool == null) {
rng_pool = new Array(); rng_pool = new Array();
rng_pptr = 0; rng_pptr = 0;
var t; var t;
// TODO(shtylman) use browser crypto if available
/*
if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
// Extract entropy (256 bits) from NS4 RNG if available // Extract entropy (256 bits) from NS4 RNG if available
var z = window.crypto.random(32); var z = window.crypto.random(32);
for(t = 0; t < z.length; ++t) for(t = 0; t < z.length; ++t)
rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
} }
*/
while(rng_pptr < rng_psize) { // extract some randomness from Math.random() while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random()); t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t >>> 8;
@ -66,3 +81,5 @@ function rng_get_bytes(ba) {
function SecureRandom() {} function SecureRandom() {}
SecureRandom.prototype.nextBytes = rng_get_bytes; SecureRandom.prototype.nextBytes = rng_get_bytes;
module.exports = SecureRandom;

5
src/jsbn/sec.js

@ -2,6 +2,9 @@
// Requires ec.js, jsbn.js, and jsbn2.js // Requires ec.js, jsbn.js, and jsbn2.js
var ECCurveFp = require('./ec');
var BigInteger = require('./jsbn');
// ---------------- // ----------------
// X9ECParameters // X9ECParameters
@ -171,3 +174,5 @@ function getSECCurveByName(name) {
if(name == "secp256r1") return secp256r1(); if(name == "secp256r1") return secp256r1();
return null; return null;
} }
module.exports = getSECCurveByName;

57
src/message.js

@ -1,38 +1,40 @@
/** /// Implements Bitcoin's feature for signing arbitrary messages.
* Implements Bitcoin's feature for signing arbitrary messages.
*/
Bitcoin.Message = (function () {
var Message = {};
Message.magicPrefix = "Bitcoin Signed Message:\n"; var Crypto = require('./crypto-js/crypto');
var ecdsa = require('./ecdsa');
var conv = require('./convert');
var util = require('./util');
Message.makeMagicMessage = function (message) { var Message = {};
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix);
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message); Message.magicPrefix = "Bitcoin Signed Message:\n";
Message.makeMagicMessage = function (message) {
var magicBytes = conv.stringToBytes(Message.magicPrefix);
var messageBytes = conv.stringToBytes(message);
var buffer = []; var buffer = [];
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length)); buffer = buffer.concat(util.numToVarInt(magicBytes.length));
buffer = buffer.concat(magicBytes); buffer = buffer.concat(magicBytes);
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length)); buffer = buffer.concat(util.numToVarInt(messageBytes.length));
buffer = buffer.concat(messageBytes); buffer = buffer.concat(messageBytes);
return buffer; return buffer;
}; };
Message.getHash = function (message) { Message.getHash = function (message) {
var buffer = Message.makeMagicMessage(message); var buffer = Message.makeMagicMessage(message);
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
}; };
Message.signMessage = function (key, message, compressed) { Message.signMessage = function (key, message, compressed) {
var hash = Message.getHash(message); var hash = Message.getHash(message);
var sig = key.sign(hash); var sig = key.sign(hash);
var obj = Bitcoin.ECDSA.parseSig(sig); var obj = ecdsa.parseSig(sig);
var address = key.getBitcoinAddress().toString(); var i = ecdsa.calcPubkeyRecoveryParam(key, obj.r, obj.s, hash);
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash);
i += 27; i += 27;
if (compressed) i += 4; if (compressed) i += 4;
@ -46,24 +48,21 @@ Bitcoin.Message = (function () {
sig = [i].concat(rBa).concat(sBa); sig = [i].concat(rBa).concat(sBa);
return Crypto.util.bytesToBase64(sig); return conv.bytesToHex(sig);
}; };
Message.verifyMessage = function (address, sig, message) { Message.verifyMessage = function (address, sig, message) {
sig = Crypto.util.base64ToBytes(sig); sig = conv.hexToBytes(sig);
sig = Bitcoin.ECDSA.parseSigCompact(sig); sig = ecdsa.parseSigCompact(sig);
var hash = Message.getHash(message); var hash = Message.getHash(message);
var isCompressed = !!(sig.i & 4); var isCompressed = !!(sig.i & 4);
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i); var pubKey = ecdsa.recoverPubKey(sig.r, sig.s, hash, sig.i);
pubKey.setCompressed(isCompressed);
var expectedAddress = pubKey.getBitcoinAddress().toString(); var expectedAddress = pubKey.getBitcoinAddress().toString();
return (address === expectedAddress); return (address === expectedAddress);
}; };
return Message; module.exports = Message;
})();

22
src/opcode.js

@ -1,13 +1,12 @@
(function () { var Opcode = function (num) {
var Opcode = Bitcoin.Opcode = function (num) {
this.code = num; this.code = num;
}; };
Opcode.prototype.toString = function () { Opcode.prototype.toString = function () {
return Opcode.reverseMap[this.code]; return Opcode.reverseMap[this.code];
}; };
Opcode.map = { Opcode.map = {
// push value // push value
OP_0 : 0, OP_0 : 0,
OP_FALSE : 0, OP_FALSE : 0,
@ -144,11 +143,12 @@
OP_PUBKEYHASH : 253, OP_PUBKEYHASH : 253,
OP_PUBKEY : 254, OP_PUBKEY : 254,
OP_INVALIDOPCODE : 255 OP_INVALIDOPCODE : 255
}; };
Opcode.reverseMap = []; Opcode.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;

309
src/script.js

@ -1,17 +1,14 @@
(function () { var Opcode = require('./opcode');
var Opcode = Bitcoin.Opcode; var util = require('./util');
var conv = require('./convert');
var Address = require('./address');
// Make opcodes available as pseudo-constants var Script = function (data) {
for (var i in Opcode.map) {
eval("var " + i + " = " + Opcode.map[i] + ";");
}
var Script = Bitcoin.Script = function (data) {
if (!data) { if (!data) {
this.buffer = []; this.buffer = [];
} else if ("string" == typeof data) { } else if ("string" == typeof data) {
this.buffer = Crypto.util.base64ToBytes(data); this.buffer = conv.hexToBytes(data);
} else if (Bitcoin.Util.isArray(data)) { } else if (util.isArray(data)) {
this.buffer = data; this.buffer = data;
} else if (data instanceof Script) { } else if (data instanceof Script) {
this.buffer = data.buffer; this.buffer = data.buffer;
@ -20,9 +17,35 @@
} }
this.parse(); this.parse();
}; };
Script.fromPubKey = function(str) {
var script = new Script();
var s = str.split(" ");
for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])){
script.writeOp(Opcode.map[s[i]]);
} else {
script.writeBytes(conv.hexToBytes(s[i]));
}
}
return script;
};
/** Script.fromScriptSig = function(str) {
var script = new Script();
var s = str.split(" ");
for (var i in s) {
if (Opcode.map.hasOwnProperty(s[i])){
script.writeOp(Opcode.map[s[i]]);
} else {
script.writeBytes(conv.hexToBytes(s[i]));
}
}
return script;
};
/**
* Update the parsed script representation. * Update the parsed script representation.
* *
* Each Script object stores the script in two formats. First as a raw byte * Each Script object stores the script in two formats. First as a raw byte
@ -33,7 +56,7 @@
* constructor and you don't need to worry about it. However, if you change * constructor and you don't need to worry about it. However, if you change
* 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 = [];
@ -55,16 +78,16 @@
} }
var len; var len;
if (opcode > 0 && opcode < OP_PUSHDATA1) { if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data // Read some bytes of data, opcode value is the length of data
readChunk(opcode); readChunk(opcode);
} else if (opcode == OP_PUSHDATA1) { } else if (opcode == Opcode.map.OP_PUSHDATA1) {
len = this.buffer[i++]; len = this.buffer[i++];
readChunk(len); readChunk(len);
} else if (opcode == OP_PUSHDATA2) { } else if (opcode == Opcode.map.OP_PUSHDATA2) {
len = (this.buffer[i++] << 8) | this.buffer[i++]; len = (this.buffer[i++] << 8) | this.buffer[i++];
readChunk(len); readChunk(len);
} else if (opcode == OP_PUSHDATA4) { } else if (opcode == Opcode.map.OP_PUSHDATA4) {
len = (this.buffer[i++] << 24) | len = (this.buffer[i++] << 24) |
(this.buffer[i++] << 16) | (this.buffer[i++] << 16) |
(this.buffer[i++] << 8) | (this.buffer[i++] << 8) |
@ -74,9 +97,9 @@
this.chunks.push(opcode); this.chunks.push(opcode);
} }
} }
}; };
/** /**
* Compare the script to known templates of scriptPubKey. * Compare the script to known templates of scriptPubKey.
* *
* This method will compare the script to a small number of standard script * This method will compare the script to a small number of standard script
@ -94,59 +117,46 @@
* Strange: * Strange:
* 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] == OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) { this.chunks[0] == Opcode.map.OP_HASH160 &&
this.chunks.length == 3) {
// Transfer to M-OF-N // Transfer to M-OF-N
return 'Multisig'; return 'P2SH';
} else if (this.chunks.length == 5 && }
this.chunks[0] == OP_DUP && else if (this.chunks.length == 5 &&
this.chunks[1] == OP_HASH160 && this.chunks[0] == Opcode.map.OP_DUP &&
this.chunks[3] == OP_EQUALVERIFY && this.chunks[1] == Opcode.map.OP_HASH160 &&
this.chunks[4] == OP_CHECKSIG) { this.chunks[3] == Opcode.map.OP_EQUALVERIFY &&
this.chunks[4] == Opcode.map.OP_CHECKSIG) {
// Transfer to Bitcoin address // Transfer to Bitcoin address
return 'Address';
} else if (this.chunks.length == 2 &&
this.chunks[1] == OP_CHECKSIG) {
// Transfer to IP address
return 'Pubkey'; return 'Pubkey';
} else { }
else {
return 'Strange'; return 'Strange';
} }
} }
/** /**
* Returns the affected address hash for this output. * Returns the address corresponding to this output in hash160 form.
* * Assumes strange scripts are P2SH
* For standard transactions, this will return the hash of the pubKey that
* can spend this output.
*
* In the future, for payToScriptHash outputs, this will return the
* scriptHash. Note that non-standard and standard payToScriptHash transactions
* look the same
*
* This method is useful for indexing transactions.
*/ */
Script.prototype.simpleOutHash = function () Script.prototype.toScriptHash = function () {
{ var outType = this.getOutType();
switch (this.getOutType()) {
case 'Address':
return this.chunks[2];
case 'Pubkey':
return Bitcoin.Util.sha256ripe160(this.chunks[0]);
default:
throw new Error("Encountered non-standard scriptPubKey");
}
};
/** return outType == 'Pubkey' ? this.chunks[2]
* Old name for Script#simpleOutHash. : outType == 'P2SH' ? util.sha256ripe160(this.buffer)
* : util.sha256ripe160(this.buffer)
* @deprecated };
*/
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash; Script.prototype.toAddress = function() {
var outType = this.getOutType();
return outType == 'Pubkey' ? new Address(this.chunks[2])
: outType == 'P2SH' ? new Address(this.chunks[1],5)
: new Address(this.chunks[1],5)
}
/** /**
* Compare the script to known templates of scriptSig. * Compare the script to known templates of scriptSig.
* *
* This method will compare the script to a small number of standard script * This method will compare the script to a small number of standard script
@ -165,26 +175,35 @@
* Paying to a public key directly. * Paying to a public key directly.
* [sig] * [sig]
* *
* Multisig:
* Paying to M-of-N public keys.
*
* Strange: * Strange:
* 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 &&
Bitcoin.Util.isArray(this.chunks[0])) { util.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 &&
Bitcoin.Util.isArray(this.chunks[0]) && util.isArray(this.chunks[0]) &&
Bitcoin.Util.isArray(this.chunks[1])) { util.isArray(this.chunks[1])) {
return 'Address'; return 'Address';
} else if (this.chunks[0] == Opcode.map.OP_0 &&
this.chunks.slice(1).reduce(function(t,chunk,i) {
return t && util.isArray(chunk)
&& (chunk[0] == 48 || i == this.chunks.length - 1);
},true)) {
return 'Multisig';
} else { } else {
return 'Strange'; return 'Strange';
} }
}; };
/** /**
* Returns the affected public key for this input. * Returns the affected public key for this input.
* *
* This currently only works with payToPubKeyHash transactions. It will also * This currently only works with payToPubKeyHash transactions. It will also
@ -197,8 +216,8 @@
* *
* @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];
@ -209,9 +228,9 @@
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.
* *
* For standard transactions, this will return the hash of the pubKey that * For standard transactions, this will return the hash of the pubKey that
@ -226,43 +245,43 @@
* *
* This method is useful for indexing transactions. * This method is useful for indexing transactions.
*/ */
Script.prototype.simpleInHash = function () Script.prototype.simpleInHash = function ()
{ {
return Bitcoin.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 < 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(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(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(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);
@ -270,78 +289,94 @@
} }
this.buffer = this.buffer.concat(data); this.buffer = this.buffer.concat(data);
this.chunks.push(data); this.chunks.push(data);
}; };
/** /**
* Create a standard payToPubKeyHash output. * Create an output for an address
*/ */
Script.createOutputScript = function (address) Script.createOutputScript = function (address)
{ {
var script = new Script(); var script = new Script();
script.writeOp(OP_DUP); address = new Address(address);
script.writeOp(OP_HASH160); // Standard pay-to-pubkey-hash
if (!address.version) {
script.writeOp(Opcode.map.OP_DUP);
script.writeOp(Opcode.map.OP_HASH160);
script.writeBytes(address.hash);
script.writeOp(Opcode.map.OP_EQUALVERIFY);
script.writeOp(Opcode.map.OP_CHECKSIG);
}
// Standard pay-to-script-hash
else {
script.writeOp(Opcode.map.OP_HASH160);
script.writeBytes(address.hash); script.writeBytes(address.hash);
script.writeOp(OP_EQUALVERIFY); script.writeOp(Opcode.map.OP_EQUAL);
script.writeOp(OP_CHECKSIG); }
return script; return script;
}; };
/** /**
* Extract bitcoin addresses from an output script * Extract pubkeys from a multisig script
*/ */
Script.prototype.extractAddresses = function (addresses)
{
switch (this.getOutType()) {
case 'Address':
addresses.push(new Address(this.chunks[2]));
return 1;
case 'Pubkey':
addresses.push(new Address(Util.sha256ripe160(this.chunks[0])));
return 1;
case 'Multisig':
for (var i = 1; i < this.chunks.length-2; ++i) {
addresses.push(new Address(Util.sha256ripe160(this.chunks[i])));
}
return this.chunks[0] - OP_1 + 1;
default:
throw new Error("Encountered non-standard scriptPubKey");
}
};
/** Script.prototype.extractPubkeys = function() {
return this.chunks.filter(function(chunk) {
return (chunk[0] == 4 && chunk.length == 65
|| chunk[0] < 4 && chunk.length == 33)
});
}
/**
* Create an m-of-n output script * Create an m-of-n output script
*/ */
Script.createMultiSigOutputScript = function (m, pubkeys) Script.createMultiSigOutputScript = function (m, pubkeys)
{ {
var script = new Bitcoin.Script(); var script = new Script();
script.writeOp(OP_1 + m - 1); pubkeys = pubkeys.sort();
script.writeOp(Opcode.map.OP_1 + m - 1);
for (var i = 0; i < pubkeys.length; ++i) { for (var i = 0; i < pubkeys.length; ++i) {
script.writeBytes(pubkeys[i]); script.writeBytes(pubkeys[i]);
} }
script.writeOp(OP_1 + pubkeys.length - 1); script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1);
script.writeOp(OP_CHECKMULTISIG); script.writeOp(Opcode.map.OP_CHECKMULTISIG);
return script; 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
*/
Script.createMultiSigInputScript = function(signatures, script)
{
script = new Script(script);
var k = script.chunks[0][0];
if (signatures.length < k) return false; //Not enough sigs
var inScript = new Script();
inScript.writeOp(Opcode.map.OP_0);
signatures.map(function(sig) { inScript.writeBytes(sig) });
inScript.writeBytes(script.buffer);
return inScript;
}
Script.prototype.clone = function () Script.prototype.clone = function ()
{ {
return new Script(this.buffer); return new Script(this.buffer);
}; };
})();
module.exports = Script;

446
src/transaction.js

@ -1,7 +1,14 @@
(function () { var BigInteger = require('./jsbn/jsbn');
var Script = Bitcoin.Script; var Script = require('./script');
var util = require('./util');
var Transaction = Bitcoin.Transaction = function (doc) { var conv = require('./convert');
var Crypto = require('./crypto-js/crypto');
var Wallet = require('./wallet');
var ECKey = require('./eckey').ECKey;
var ECDSA = require('./ecdsa');
var Address = require('./address');
var Transaction = function (doc) {
this.version = 1; this.version = 1;
this.lock_time = 0; this.lock_time = 0;
this.ins = []; this.ins = [];
@ -10,6 +17,9 @@
this.block = null; this.block = null;
if (doc) { if (doc) {
if (typeof doc == "string" || util.isArray(doc)) {
doc = Transaction.deserialize(doc)
}
if (doc.hash) this.hash = doc.hash; if (doc.hash) this.hash = doc.hash;
if (doc.version) this.version = doc.version; if (doc.version) this.version = doc.version;
if (doc.lock_time) this.lock_time = doc.lock_time; if (doc.lock_time) this.lock_time = doc.lock_time;
@ -26,115 +36,129 @@
if (doc.timestamp) this.timestamp = doc.timestamp; if (doc.timestamp) this.timestamp = doc.timestamp;
if (doc.block) this.block = doc.block; if (doc.block) this.block = doc.block;
} }
}; };
/** /**
* Turn transaction data into Transaction objects. * Turn transaction data into Transaction objects.
* *
* Takes an array of plain JavaScript objects containing transaction data and * Takes an array of plain JavaScript objects containing transaction data and
* returns an array of Transaction objects. * returns an array of Transaction objects.
*/ */
Transaction.objectify = function (txs) { Transaction.objectify = function (txs) {
var objs = []; var objs = [];
for (var i = 0; i < txs.length; i++) { for (var i = 0; i < txs.length; i++) {
objs.push(new Transaction(txs[i])); objs.push(new Transaction(txs[i]));
} }
return objs; return objs;
}; };
/** /**
* Create a new txin. * Create a new txin.
* *
* Can be called with an existing TransactionIn object to add it to the * Can be called with any of:
* transaction. Or it can be called with a Transaction object and an integer *
* output index, in which case a new TransactionIn object pointing to the * - An existing TransactionOut object
* referenced output will be created. * - A transaction and an index
* - A transaction hash and an index
* - A single string argument of the form txhash:index
* *
* Note that this method does not sign the created input. * 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 { }
else if (arguments[0].length > 65) {
var args = arguments[0].split(':');
return this.addInput(args[0], args[1]);
}
else {
this.ins.push(new TransactionIn({ this.ins.push(new TransactionIn({
outpoint: { outpoint: {
hash: tx.hash, hash: tx.hash || tx,
index: outIndex index: outIndex
}, },
script: new Bitcoin.Script(), script: new Script(),
sequence: 4294967295 sequence: 4294967295
})); }));
} }
}; };
/** /**
* Create a new txout. * Create a new txout.
* *
* Can be called with an existing TransactionOut object to add it to the * Can be called with:
* transaction. Or it can be called with an Address object and a BigInteger *
* for the amount, in which case a new TransactionOut object with those * i) An existing TransactionOut object
* values will be created. * ii) An address object or an address and a value
* iii) An address:value string
*
*/ */
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]);
} else { return;
if (value instanceof BigInteger) { }
value = value.toByteArrayUnsigned().reverse(); if (arguments[0].indexOf(':') >= 0) {
while (value.length < 8) value.push(0); var args = arguments[0].split(':');
} else if (Bitcoin.Util.isArray(value)) { address = args[0];
// Nothing to do 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.
* *
* Returns the transaction as a byte array in the standard Bitcoin binary * Returns the transaction as a byte array in the standard Bitcoin binary
* format. This method is byte-perfect, i.e. the resulting byte array can * format. This method is byte-perfect, i.e. the resulting byte array can
* 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(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); buffer = buffer.concat(util.numToBytes(parseInt(this.version),4));
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); buffer = buffer.concat(util.numToVarInt(this.ins.length));
for (var i = 0; i < this.ins.length; i++) { for (var i = 0; i < this.ins.length; i++) {
var txin = this.ins[i]; var txin = this.ins[i];
buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash));
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); // Why do blockchain.info, blockexplorer.com, sx and just about everybody
// else use little-endian hashes? No idea...
buffer = buffer.concat(conv.hexToBytes(txin.outpoint.hash).reverse());
buffer = buffer.concat(util.numToBytes(parseInt(txin.outpoint.index),4));
var scriptBytes = txin.script.buffer; var scriptBytes = txin.script.buffer;
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); buffer = buffer.concat(util.numToVarInt(scriptBytes.length));
buffer = buffer.concat(scriptBytes); buffer = buffer.concat(scriptBytes);
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); buffer = buffer.concat(util.numToBytes(parseInt(txin.sequence),4));
} }
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); buffer = buffer.concat(util.numToVarInt(this.outs.length));
for (var i = 0; i < this.outs.length; i++) { for (var i = 0; i < this.outs.length; i++) {
var txout = this.outs[i]; var txout = this.outs[i];
buffer = buffer.concat(txout.value); buffer = buffer.concat(util.numToBytes(txout.value,8));
var scriptBytes = txout.script.buffer; var scriptBytes = txout.script.buffer;
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); buffer = buffer.concat(util.numToVarInt(scriptBytes.length));
buffer = buffer.concat(scriptBytes); buffer = buffer.concat(scriptBytes);
} }
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); buffer = buffer.concat(util.numToBytes(parseInt(this.lock_time),4));
return buffer; return buffer;
}; };
var OP_CODESEPARATOR = 171; Transaction.prototype.serializeHex = function() {
return conv.bytesToHex(this.serialize());
}
var SIGHASH_ALL = 1; var OP_CODESEPARATOR = 171;
var SIGHASH_NONE = 2;
var SIGHASH_SINGLE = 3;
var SIGHASH_ANYONECANPAY = 80;
/** var SIGHASH_ALL = 1;
var SIGHASH_NONE = 2;
var SIGHASH_SINGLE = 3;
var SIGHASH_ANYONECANPAY = 80;
/**
* Hash transaction for signing a specific input. * Hash transaction for signing a specific input.
* *
* Bitcoin uses a different hash for each signed transaction input. This * Bitcoin uses a different hash for each signed transaction input. This
@ -142,9 +166,9 @@
* hashType, serializes and finally hashes the result. This hash can then be * hashType, serializes and finally hashes the result. This hash can then be
* 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,
@ -180,27 +204,29 @@
var buffer = txTmp.serialize(); var buffer = txTmp.serialize();
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); buffer = buffer.concat(util.numToBytes(parseInt(hashType),4));
var hash1 = Crypto.SHA256(buffer, {asBytes: true}); var hash1 = Crypto.SHA256(buffer, {asBytes: true});
return Crypto.SHA256(hash1, {asBytes: true}); return Crypto.SHA256(hash1, {asBytes: true});
}; };
/** /**
* Calculate and return the transaction's hash. * Calculate and return the transaction's hash.
* Reverses hash since blockchain.info, blockexplorer.com and others
* use little-endian hashes for some stupid reason
*/ */
Transaction.prototype.getHash = function () Transaction.prototype.getHash = function ()
{ {
var buffer = this.serialize(); var buffer = this.serialize();
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}).reverse();
}; };
/** /**
* Create a copy of this transaction object. * 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.lock_time = this.lock_time; newTx.lock_time = this.lock_time;
@ -213,9 +239,9 @@
newTx.addOutput(txout); newTx.addOutput(txout);
} }
return newTx; return newTx;
}; };
/** /**
* Analyze how this transaction affects a wallet. * Analyze how this transaction affects a wallet.
* *
* Returns an object with properties 'impact', 'type' and 'addr'. * Returns an object with properties 'impact', 'type' and 'addr'.
@ -240,8 +266,8 @@
* other: * other:
* This method was unable to detect what the transaction does. Either it * This method was unable to detect what the transaction does. Either it
*/ */
Transaction.prototype.analyze = function (wallet) { Transaction.prototype.analyze = function (wallet) {
if (!(wallet instanceof Bitcoin.Wallet)) return null; if (!(wallet instanceof Wallet)) return null;
var allFromMe = true, var allFromMe = true,
allToMe = true, allToMe = true,
@ -274,9 +300,9 @@
analysis.impact = impact; analysis.impact = impact;
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { if (impact.sign > 0 && impact.value > 0) {
analysis.type = 'recv'; analysis.type = 'recv';
analysis.addr = new Bitcoin.Address(firstMeRecvHash); analysis.addr = new Address(firstMeRecvHash);
} else if (allFromMe && allToMe) { } else if (allFromMe && allToMe) {
analysis.type = 'self'; analysis.type = 'self';
} else if (allFromMe) { } else if (allFromMe) {
@ -284,21 +310,21 @@
// TODO: Right now, firstRecvHash is the first output, which - if the // TODO: Right now, firstRecvHash is the first output, which - if the
// transaction was not generated by this library could be the // transaction was not generated by this library could be the
// change address. // change address.
analysis.addr = new Bitcoin.Address(firstRecvHash); analysis.addr = new Address(firstRecvHash);
} else { } else {
analysis.type = "other"; analysis.type = "other";
} }
return analysis; return analysis;
}; };
/** /**
* Get a human-readable version of the data returned by Transaction#analyze. * Get a human-readable version of the data returned by Transaction#analyze.
* *
* This is merely a convenience function. Clients should consider implementing * This is merely a convenience function. Clients should consider implementing
* this themselves based on their UI, I18N, etc. * this themselves based on their UI, I18N, etc.
*/ */
Transaction.prototype.getDescription = function (wallet) { Transaction.prototype.getDescription = function (wallet) {
var analysis = this.analyze(wallet); var analysis = this.analyze(wallet);
if (!analysis) return ""; if (!analysis) return "";
@ -320,19 +346,14 @@
default: default:
return ""; return "";
} }
}; };
/** /**
* Get the total amount of a transaction's outputs. * Get the total amount of a transaction's outputs.
*/ */
Transaction.prototype.getTotalOutValue = function () { Transaction.prototype.getTotalOutValue = function () {
var totalValue = BigInteger.ZERO; return this.outs.reduce(function(t,o) { return t + o.value },0);
for (var j = 0; j < this.outs.length; j++) { };
var txout = this.outs[j];
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
}
return totalValue;
};
/** /**
* Old name for Transaction#getTotalOutValue. * Old name for Transaction#getTotalOutValue.
@ -341,7 +362,7 @@
*/ */
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue; Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue;
/** /**
* Calculates the impact a transaction has on this wallet. * Calculates the impact a transaction has on this wallet.
* *
* Based on the its public keys, the wallet will calculate the * Based on the its public keys, the wallet will calculate the
@ -353,58 +374,178 @@
* *
* @returns Object Impact on wallet * @returns Object Impact on wallet
*/ */
Transaction.prototype.calcImpact = function (wallet) { Transaction.prototype.calcImpact = function (wallet) {
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; if (!(wallet instanceof Wallet)) return 0;
// Calculate credit to us from all outputs // Calculate credit to us from all outputs
var valueOut = BigInteger.ZERO; var valueOut = this.outs.filter(function(o) {
for (var j = 0; j < this.outs.length; j++) { return wallet.hasHash(conv.bytesToHex(o.script.simpleOutPubKeyHash()));
var txout = this.outs[j]; })
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); .reduce(function(t,o) { return t+o.value },0);
if (wallet.hasHash(hash)) {
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); var valueIn = this.ins.filter(function(i) {
} return wallet.hasHash(conv.bytesToHex(i.script.simpleInPubKeyHash()))
} && wallet.txIndex[i.outpoint.hash];
})
// Calculate debit to us from all ins .reduce(function(t,i) {
var valueIn = BigInteger.ZERO; return t + wallet.txIndex[i.outpoint.hash].outs[i.outpoint.index].value
for (var j = 0; j < this.ins.length; j++) { },0);
var txin = this.ins[j];
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); if (valueOut > valueIn) {
if (wallet.hasHash(hash)) {
var fromTx = wallet.txIndex[txin.outpoint.hash];
if (fromTx) {
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value));
}
}
}
if (valueOut.compareTo(valueIn) >= 0) {
return { return {
sign: 1, sign: 1,
value: valueOut.subtract(valueIn) value: valueOut - valueIn
}; };
} else { } else {
return { return {
sign: -1, sign: -1,
value: valueIn.subtract(valueOut) value: valueIn - valueOut
}; };
} }
}; };
var TransactionIn = Bitcoin.TransactionIn = function (data) /**
{ * Converts a serialized transaction into a transaction object
this.outpoint = data.outpoint; */
if (data.script instanceof Script) {
this.script = data.script; Transaction.deserialize = function(buffer) {
} else { if (typeof buffer == "string") {
this.script = new Script(data.script); buffer = conv.hexToBytes(buffer)
} }
this.sequence = data.sequence; var pos = 0;
}; var readAsInt = function(bytes) {
if (bytes == 0) return 0;
pos++;
return buffer[pos-1] + readAsInt(bytes-1) * 256;
}
var readVarInt = function() {
pos++;
if (buffer[pos-1] < 253) {
return buffer[pos-1];
}
return readAsInt(buffer[pos-1] - 251);
}
var readBytes = function(bytes) {
pos += bytes;
return buffer.slice(pos - bytes, pos);
}
var readVarString = function() {
var size = readVarInt();
return readBytes(size);
}
var obj = {
ins: [],
outs: []
}
obj.version = readAsInt(4);
var ins = readVarInt();
for (var i = 0; i < ins; i++) {
obj.ins.push({
outpoint: {
hash: conv.bytesToHex(readBytes(32).reverse()),
index: readAsInt(4)
},
script: new Script(readVarString()),
sequence: readAsInt(4)
});
}
var outs = readVarInt();
for (var i = 0; i < outs; i++) {
obj.outs.push({
value: util.bytesToNum(readBytes(8)),
script: new Script(readVarString())
});
}
obj.locktime = readAsInt(4);
return new Transaction(obj);
}
/**
* Signs a standard output at some index with the given key
*/
TransactionIn.prototype.clone = function () Transaction.prototype.sign = function(index, key, type) {
{ type = type || SIGHASH_ALL;
var newTxin = new TransactionIn({ key = new ECKey(key);
var pub = key.getPub().export('bytes'),
hash160 = util.sha256ripe160(pub),
script = Script.createOutputScript(new Address(hash160)),
hash = this.hashTransactionForSignature( script, index, type),
sig = key.sign(hash).concat([type]);
this.ins[index].script = Script.createInputScript(sig,pub);
}
// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Transaction.prototype.signWithKeys = function(keys, outputs, type) {
type = type || SIGHASH_ALL;
var addrdata = keys.map(function(key) {
key = new ECKey(key);
return {
key: key,
address: key.getBitcoinAddress().toString()
}
});
var hmap = {};
for (var o in outputs) {
hmap[outputs[o].output] = outputs[o];
}
for (var i = 0; i < this.ins.length; i++) {
var outpoint = this.ins[i].outpoint.hash+':'+this.ins[i].outpoint.index,
histItem = hmap[outpoint];
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
*/
Transaction.prototype.p2shsign = function(index, script, key, type) {
script = new Script(script);
key = new ECKey(key);
type = type || SIGHASH_ALL;
var hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type]);
return sig;
}
Transaction.prototype.multisign = Transaction.prototype.p2shsign;
Transaction.prototype.applyMultisigs = function(index, script, sigs, type) {
this.ins[index].script = Script.createMultiSigInputScript(sigs, script);
}
Transaction.prototype.validateSig = function(index, script, sig, pub) {
script = new Script(script);
var hash = this.hashTransactionForSignature(script,index,1);
return ECDSA.verify(hash, conv.coerceToBytes(sig),
conv.coerceToBytes(pub));
}
var TransactionIn = function (data) {
if (typeof data == "string")
this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
else if (data.outpoint)
this.outpoint = data.outpoint
else
this.outpoint = { hash: data.hash, index: data.index }
if (data.scriptSig)
this.script = Script.fromScriptSig(data.scriptSig)
else
this.script = new Script(data.script)
this.sequence = data.sequence || 4294967295;
};
TransactionIn.prototype.clone = function () {
return new TransactionIn({
outpoint: { outpoint: {
hash: this.outpoint.hash, hash: this.outpoint.hash,
index: this.outpoint.index index: this.outpoint.index
@ -412,34 +553,35 @@
script: this.script.clone(), script: this.script.clone(),
sequence: this.sequence sequence: this.sequence
}); });
return newTxin; };
};
var TransactionOut = function (data) {
var TransactionOut = Bitcoin.TransactionOut = function (data) this.script =
{ data.script instanceof Script ? data.script.clone()
if (data.script instanceof Script) { : util.isArray(data.script) ? new Script(data.script)
this.script = data.script; : typeof data.script == "string" ? new Script(conv.hexToBytes(data.script))
} else { : data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey)
this.script = new Script(data.script); : data.address ? Script.createOutputScript(data.address)
} : new Script();
if (Bitcoin.Util.isArray(data.value)) { if (this.script.buffer.length > 0) this.address = this.script.toAddress();
this.value = data.value;
} else if ("string" == typeof data.value) { this.value =
var valueHex = (new BigInteger(data.value, 10)).toString(16); util.isArray(data.value) ? util.bytesToNum(data.value)
while (valueHex.length < 16) valueHex = "0" + valueHex; : "string" == typeof data.value ? parseInt(data.value)
this.value = Crypto.util.hexToBytes(valueHex); : data.value instanceof BigInteger ? parseInt(data.value.toString())
} : 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.slice(0) value: this.value
}); });
return newTxout; return newTxout;
}; };
})();
module.exports.Transaction = Transaction;
module.exports.TransactionIn = TransactionIn;
module.exports.TransactionOut = TransactionOut;

4
src/txdb.js

@ -1,3 +1,5 @@
var Transaction = require('./transaction');
var TransactionDatabase = function () { var TransactionDatabase = function () {
this.txs = []; this.txs = [];
this.txIndex = {}; this.txIndex = {};
@ -16,7 +18,7 @@ TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) {
return; return;
} }
this.txs.push(new Bitcoin.Transaction(tx)); this.txs.push(new Transaction(tx));
this.txIndex[tx.hash] = tx; this.txIndex[tx.hash] = tx;
}; };

241
src/util.js

@ -1,228 +1,69 @@
// BigInteger monkey patching var Crypto = require('./crypto-js/crypto');
BigInteger.valueOf = nbv;
/** /**
* Returns a byte array representation of the big integer. * Cross-browser compatibility version of Array.isArray.
*
* This returns the absolute of the contained value in big endian
* form. A value of zero results in an empty array.
*/ */
BigInteger.prototype.toByteArrayUnsigned = function () { exports.isArray = Array.isArray || function(o) {
var ba = this.abs().toByteArray(); return Object.prototype.toString.call(o) === '[object Array]';
if (ba.length) { }
if (ba[0] == 0) {
ba = ba.slice(1);
}
return ba.map(function (v) {
return (v < 0) ? v + 256 : v;
});
} else {
// Empty array, nothing to do
return ba;
}
};
/** /**
* Turns a byte array into a big integer. * Create a byte array representing a number with the given length
*
* This function will interpret a byte array as a big integer in big
* endian notation and ignore leading zeros.
*/ */
BigInteger.fromByteArrayUnsigned = function (ba) { exports.numToBytes = function(num, bytes) {
if (!ba.length) { if (bytes === undefined) bytes = 8;
return ba.valueOf(0); if (bytes === 0) return [];
} else if (ba[0] & 0x80) { return [num % 256].concat(module.exports.numToBytes(Math.floor(num / 256), bytes - 1));
// Prepend a zero so the BigInteger class doesn't mistake this }
// for a negative integer.
return new BigInteger([0].concat(ba));
} else {
return new BigInteger(ba);
}
};
/** /**
* Converts big integer to signed byte representation. * Convert a byte array to the number that it represents
*
* The format for this value uses a the most significant bit as a sign
* bit. If the most significant bit is already occupied by the
* absolute value, an extra byte is prepended and the sign bit is set
* there.
*
* Examples:
*
* 0 => 0x00
* 1 => 0x01
* -1 => 0x81
* 127 => 0x7f
* -127 => 0xff
* 128 => 0x0080
* -128 => 0x8080
* 255 => 0x00ff
* -255 => 0x80ff
* 16300 => 0x3fac
* -16300 => 0xbfac
* 62300 => 0x00f35c
* -62300 => 0x80f35c
*/ */
BigInteger.prototype.toByteArraySigned = function () { exports.bytesToNum = function(bytes) {
var val = this.abs().toByteArrayUnsigned(); if (bytes.length === 0) return 0;
var neg = this.compareTo(BigInteger.ZERO) < 0; return bytes[0] + 256 * module.exports.bytesToNum(bytes.slice(1));
}
if (neg) {
if (val[0] & 0x80) {
val.unshift(0x80);
} else {
val[0] |= 0x80;
}
} else {
if (val[0] & 0x80) {
val.unshift(0x00);
}
}
return val;
};
/** /**
* Parse a signed big integer byte representation.
*
* For details on the format please see BigInteger.toByteArraySigned.
*/
BigInteger.fromByteArraySigned = function (ba) {
// Check for negative value
if (ba[0] & 0x80) {
// Remove sign bit
ba[0] &= 0x7f;
return BigInteger.fromByteArrayUnsigned(ba).negate();
} else {
return BigInteger.fromByteArrayUnsigned(ba);
}
};
// Console ignore
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
"dirxml", "group", "groupEnd", "time", "timeEnd", "count",
"trace", "profile", "profileEnd"];
if ("undefined" == typeof window.console) window.console = {};
for (var i = 0; i < names.length; ++i)
if ("undefined" == typeof window.console[names[i]])
window.console[names[i]] = function() {};
// Bitcoin utility functions
Bitcoin.Util = {
/**
* Cross-browser compatibility version of Array.isArray.
*/
isArray: Array.isArray || function(o)
{
return Object.prototype.toString.call(o) === '[object Array]';
},
/**
* Create an array of a certain length filled with a specific value.
*/
makeFilledArray: function (len, val)
{
var array = [];
var i = 0;
while (i < len) {
array[i++] = val;
}
return array;
},
/**
* Turn an integer into a "var_int". * Turn an integer into a "var_int".
* *
* "var_int" is a variable length integer used by Bitcoin's binary format. * "var_int" is a variable length integer used by Bitcoin's binary format.
* *
* Returns a byte array. * Returns a byte array.
*/ */
numToVarInt: function (i) exports.numToVarInt = function(num) {
{ if (num < 253) return [num];
if (i < 0xfd) { if (num < 65536) return [253].concat(exports.numToBytes(num, 2));
// unsigned char if (num < 4294967296) return [254].concat(exports.numToBytes(num, 4));
return [i]; return [253].concat(exports.numToBytes(num, 8));
} else if (i <= 1<<16) { }
// unsigned short (LE)
return [0xfd, i >>> 8, i & 255];
} else if (i <= 1<<32) {
// unsigned int (LE)
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
} else {
// unsigned long long (LE)
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
}
},
/**
* Parse a Bitcoin value byte array, returning a BigInteger.
*/
valueToBigInt: function (valueBuffer)
{
if (valueBuffer instanceof BigInteger) return valueBuffer;
// Prepend zero byte to prevent interpretation as negative integer
return BigInteger.fromByteArrayUnsigned(valueBuffer);
},
/** exports.bytesToWords = function (bytes) {
* Format a Bitcoin value as a string. var words = [];
* for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
* Takes a BigInteger or byte-array and returns that amount of Bitcoins in a words[b >>> 5] |= bytes[i] << (24 - b % 32);
* nice standard formatting. }
* return words;
* Examples: }
* 12.3555
* 0.1234
* 900.99998888
* 34.00
*/
formatValue: function (valueBuffer) {
var value = this.valueToBigInt(valueBuffer).toString();
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value;
while (decimalPart.length < 8) decimalPart = "0"+decimalPart;
decimalPart = decimalPart.replace(/0*$/, '');
while (decimalPart.length < 2) decimalPart += "0";
return integerPart+"."+decimalPart;
},
/** exports.wordsToBytes = function (words) {
* Parse a floating point string as a Bitcoin value. var bytes = [];
* for (var b = 0; b < words.length * 32; b += 8) {
* Keep in mind that parsing user input is messy. You should always display bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
* the parsed value back to the user to make sure we understood his input }
* correctly. return bytes;
*/ },
parseValue: function (valueString) {
// TODO: Detect other number formats (e.g. comma as decimal separator)
var valueComp = valueString.split('.');
var integralPart = valueComp[0];
var fractionalPart = valueComp[1] || "0";
while (fractionalPart.length < 8) fractionalPart += "0";
fractionalPart = fractionalPart.replace(/^0+/g, '');
var value = BigInteger.valueOf(parseInt(integralPart));
value = value.multiply(BigInteger.valueOf(100000000));
value = value.add(BigInteger.valueOf(parseInt(fractionalPart)));
return value;
},
/** /**
* Calculate RIPEMD160(SHA256(data)). * Calculate RIPEMD160(SHA256(data)).
* *
* Takes an arbitrary byte array as inputs and returns the hash as a byte * Takes an arbitrary byte array as inputs and returns the hash as a byte
* array. * array.
*/ */
sha256ripe160: function (data) { exports.sha256ripe160 = function (data) {
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
} }
};
for (var i in Crypto.util) { exports.error = function(msg) {
if (Crypto.util.hasOwnProperty(i)) { throw new Error(msg);
Bitcoin.Util[i] = Crypto.util[i];
}
} }

480
src/wallet.js

@ -1,308 +1,182 @@
Bitcoin.Wallet = (function () { var Script = require('./script');
var Script = Bitcoin.Script, var ECKey = require('./eckey').ECKey;
TransactionIn = Bitcoin.TransactionIn, var conv = require('./convert');
TransactionOut = Bitcoin.TransactionOut; var util = require('./util');
var Wallet = function () { var BigInteger = require('./jsbn/jsbn');
// Keychain
//
// The keychain is stored as a var in this closure to make accidental
// serialization less likely.
//
// Any functions accessing this value therefore have to be defined in
// the closure of this constructor.
var keys = [];
// Public hashes of our keys
this.addressHashes = [];
// Transaction data
this.txIndex = {};
this.unspentOuts = [];
// Other fields
this.addressPointer = 0;
/**
* Add a key to the keychain.
*
* The corresponding public key can be provided as a second parameter. This
* adds it to the cache in the ECKey object and avoid the need to
* expensively calculate it later.
*/
this.addKey = function (key, pub) {
if (!(key instanceof Bitcoin.ECKey)) {
key = new Bitcoin.ECKey(key);
}
keys.push(key);
if (pub) {
if ("string" === typeof pub) {
pub = Crypto.util.base64ToBytes(pub);
}
key.setPub(pub);
}
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
};
/**
* Add multiple keys at once.
*/
this.addKeys = function (keys, pubs) {
if ("string" === typeof keys) {
keys = keys.split(',');
}
if ("string" === typeof pubs) {
pubs = pubs.split(',');
}
var i;
if (Array.isArray(pubs) && keys.length == pubs.length) {
for (i = 0; i < keys.length; i++) {
this.addKey(keys[i], pubs[i]);
}
} else {
for (i = 0; i < keys.length; i++) {
this.addKey(keys[i]);
}
}
};
/**
* Get the key chain.
*
* Returns an array of base64-encoded private values.
*/
this.getKeys = function () {
var serializedWallet = [];
for (var i = 0; i < keys.length; i++) {
serializedWallet.push(keys[i].toString('base64'));
}
return serializedWallet;
};
/**
* Get the public keys.
*
* Returns an array of base64-encoded public keys.
*/
this.getPubKeys = function () {
var pubs = [];
for (var i = 0; i < keys.length; i++) {
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub()));
}
return pubs;
};
/**
* Delete all keys.
*/
this.clear = function () {
keys = [];
};
/**
* Return the number of keys in this wallet.
*/
this.getLength = function () {
return keys.length;
};
/**
* Get the addresses for this wallet.
*
* Returns an array of Address objects.
*/
this.getAllAddresses = function () {
var addresses = [];
for (var i = 0; i < keys.length; i++) {
addresses.push(keys[i].getBitcoinAddress());
}
return addresses;
};
this.getCurAddress = function () {
if (keys[this.addressPointer]) {
return keys[this.addressPointer].getBitcoinAddress();
} else {
return null;
}
};
/**
* Go to the next address.
*
* If there are no more new addresses available, one will be generated
* automatically.
*/
this.getNextAddress = function () {
this.addressPointer++;
if (!keys[this.addressPointer]) {
this.generateAddress();
}
return keys[this.addressPointer].getBitcoinAddress();
};
/**
* Sign a hash with a key.
*
* This method expects the pubKeyHash as the first parameter and the hash
* to be signed as the second parameter.
*/
this.signWithKey = function (pubKeyHash, hash) {
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
for (var i = 0; i < this.addressHashes.length; i++) {
if (this.addressHashes[i] == pubKeyHash) {
return keys[i].sign(hash);
}
}
throw new Error("Missing key for signature");
};
/**
* Retrieve the corresponding pubKey for a pubKeyHash.
*
* This function only works if the pubKey in question is part of this
* wallet.
*/
this.getPubKeyFromHash = function (pubKeyHash) {
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
for (var i = 0; i < this.addressHashes.length; i++) {
if (this.addressHashes[i] == pubKeyHash) {
return keys[i].getPub();
}
}
throw new Error("Hash unknown");
};
};
Wallet.prototype.generateAddress = function () {
this.addKey(new Bitcoin.ECKey());
};
/**
* Add a transaction to the wallet's processed transaction.
*
* This will add a transaction to the wallet, updating its balance and
* available unspent outputs.
*/
Wallet.prototype.process = function (tx) {
if (this.txIndex[tx.hash]) return;
var j; var Transaction = require('./transaction').Transaction;
var k; var TransactionIn = require('./transaction').TransactionIn;
var hash; var TransactionOut = require('./transaction').TransactionOut;
// Gather outputs var HDWallet = require('./hdwallet.js')
for (j = 0; j < tx.outs.length; j++) {
var txout = new TransactionOut(tx.outs[j]);
hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
for (k = 0; k < this.addressHashes.length; k++) {
if (this.addressHashes[k] === hash) {
this.unspentOuts.push({tx: tx, index: j, out: txout});
break;
}
}
}
// Remove spent outputs var SecureRandom = require('./jsbn/rng');
for (j = 0; j < tx.ins.length; j++) { var rng = new SecureRandom();
var txin = new TransactionIn(tx.ins[j]);
var pubkey = txin.script.simpleInPubKey();
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey));
for (k = 0; k < this.addressHashes.length; k++) {
if (this.addressHashes[k] === hash) {
for (var l = 0; l < this.unspentOuts.length; l++) {
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash &&
txin.outpoint.index == this.unspentOuts[l].index) {
this.unspentOuts.splice(l, 1);
}
}
break;
}
}
}
// Index transaction
this.txIndex[tx.hash] = tx;
};
Wallet.prototype.getBalance = function () {
var balance = BigInteger.valueOf(0);
for (var i = 0; i < this.unspentOuts.length; i++) {
var txout = this.unspentOuts[i].out;
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value));
}
return balance;
};
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
var selectedOuts = [];
var txValue = sendValue.add(feeValue);
var availableValue = BigInteger.ZERO;
var i;
for (i = 0; i < this.unspentOuts.length; i++) {
selectedOuts.push(this.unspentOuts[i]);
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value));
if (availableValue.compareTo(txValue) >= 0) break;
}
if (availableValue.compareTo(txValue) < 0) {
throw new Error('Insufficient funds.');
}
var Wallet = function (seed) {
var changeValue = availableValue.subtract(txValue); // Stored in a closure to make accidental serialization less likely
var keys = [];
var sendTx = new Bitcoin.Transaction(); var masterkey = null;
var me = this;
for (i = 0; i < selectedOuts.length; i++) {
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); // Addresses
} this.addresses = [];
sendTx.addOutput(address, sendValue); // Transaction output data
if (changeValue.compareTo(BigInteger.ZERO) > 0) { this.outputs = {};
sendTx.addOutput(this.getNextAddress(), changeValue);
} // Make a new master key
this.newMasterKey = function(seed) {
var hashType = 1; // SIGHASH_ALL if (!seed) {
var seedBytes = new Array(32);
for (i = 0; i < sendTx.ins.length; i++) { rng.nextBytes(seedBytes);
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); seed = conv.bytesToString(seedBytes)
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); }
var signature = this.signWithKey(pubKeyHash, hash); masterkey = new HDWallet(seed);
keys = []
// Append hash type }
signature.push(parseInt(hashType, 10)); this.newMasterKey(seed)
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); // Add a new address
} this.generateAddress = function() {
keys.push(masterkey.ckd(keys.length).key)
return sendTx; this.addresses.push(keys[keys.length-1].getBitcoinAddress().toString())
}; return this.addresses[this.addresses.length - 1]
}
Wallet.prototype.clearTransactions = function () {
this.txIndex = {}; // Processes a transaction object
this.unspentOuts = []; // If "verified" is true, then we trust the transaction as "final"
}; this.processTx = function(tx, verified) {
var txhash = conv.bytesToHex(tx.getHash())
/** for (var i = 0; i < tx.outs.length; i++) {
* Check to see if a pubKeyHash belongs to this wallet. if (this.addresses.indexOf(tx.outs[i].address.toString()) >= 0) {
*/ me.outputs[txhash+':'+i] = {
Wallet.prototype.hasHash = function (hash) { output: txhash+':'+i,
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); value: tx.outs[i].value,
address: tx.outs[i].address.toString(),
// TODO: Just create an object with base64 hashes as keys for faster lookup timestamp: new Date().getTime() / 1000,
for (var k = 0; k < this.addressHashes.length; k++) { pending: true
if (this.addressHashes[k] === hash) return true; }
} }
return false; }
}; for (var i = 0; i < tx.ins.length; i++) {
var op = tx.ins[i].outpoint
return Wallet; var o = me.outputs[op.hash+':'+op.index]
})(); if (o) {
o.spend = txhash+':'+i
o.spendpending = true
o.timestamp = new Date().getTime() / 1000
}
}
}
// Processes an output from an external source of the form
// { output: txhash:index, value: integer, address: address }
// Excellent compatibility with SX and pybitcointools
this.processOutput = function(o) {
if (!this.outputs[o.output] || this.outputs[o.output].pending)
this.outputs[o.output] = o;
}
this.processExistingOutputs = function() {
var t = new Date().getTime() / 1000
for (var o in this.outputs) {
if (o.pending && t > o.timestamp + 1200)
delete this.outputs[o]
if (o.spendpending && t > o.timestamp + 1200) {
o.spendpending = false
o.spend = false
delete o.timestamp
}
}
}
var peoInterval = setInterval(this.processExistingOutputs, 10000)
this.getUtxoToPay = function(value) {
var h = []
for (var out in this.outputs) h.push(this.outputs[out])
var utxo = h.filter(function(x) { return !x.spend });
var valuecompare = function(a,b) { return a.value > b.value; }
var high = utxo.filter(function(o) { return o.value >= value; })
.sort(valuecompare);
if (high.length > 0) return [high[0]];
utxo.sort(valuecompare);
var totalval = 0;
for (var i = 0; i < utxo.length; i++) {
totalval += utxo[i].value;
if (totalval >= value) return utxo.slice(0,i+1);
}
throw ("Not enough money to send funds including transaction fee. Have: "
+ (totalval / 100000000) + ", needed: " + (value / 100000000));
}
this.mkSend = function(to, value, fee) {
var utxo = this.getUtxoToPay(value + fee)
var sum = utxo.reduce(function(t,o) { return t + o.value },0),
remainder = sum - value - fee
if (value < 5430) throw new Error("Amount below dust threshold!")
var unspentOuts = 0;
for (var o in this.outputs) {
if (!this.outputs[o].spend) unspentOuts += 1
if (unspentOuts >= 5) return
}
var change = this.addresses[this.addresses.length - 1]
var toOut = { address: to, value: value },
changeOut = { address: change, value: remainder }
halfChangeOut = { address: change, value: Math.floor(remainder/2) };
var outs =
remainder < 5430 ? [toOut]
: remainder < 10860 ? [toOut, changeOut]
: unspentOuts == 5 ? [toOut, changeOut]
: [toOut, halfChangeOut, halfChangeOut]
var tx = new Bitcoin.Transaction({
ins: utxo.map(function(x) { return x.output }),
outs: outs
})
this.sign(tx)
return tx
}
this.mkSendToOutputs = function(outputs, changeIndex, fee) {
var value = outputs.reduce(function(t,o) { return t + o.value },0),
utxo = this.getUtxoToPay(value + fee),
sum = utxo.reduce(function(t,p) { return t + o.value },0);
utxo[changeIndex].value += sum - value - fee;
var tx = new Bitcoin.Transaction({
ins: utxo.map(function(x) { return x.output }),
outs: outputs
})
this.sign(tx)
return tx
}
this.sign = function(tx) {
tx.ins.map(function(inp,i) {
var inp = inp.outpoint.hash+':'+inp.outpoint.index;
if (me.outputs[inp]) {
var address = me.outputs[inp].address,
ind = me.addresses.indexOf(address);
if (ind >= 0) {
var key = keys[ind]
tx.sign(ind,key)
}
}
})
return tx;
}
this.getMasterKey = function() { return masterkey }
this.getPrivateKey = function(index) {
if (typeof index == "string")
return keys.filter(function(i,k){ return addresses[i] == index })[0]
else
return keys[index]
}
this.getPrivateKeys = function() { return keys }
};
module.exports = Wallet;

52
test/address.js

@ -0,0 +1,52 @@
/* global describe, it */
var assert = require('assert');
var Address = require('../src/address.js');
describe('Address', function() {
describe('toString', function() {
it('defaults to base58', function() {
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ';
assert.equal((new Address(addr)).toString(), addr);
})
})
describe('validate', function() {
it('validates known good addresses', function() {
function validate(addr) {
assert.ok(Address.validate(addr));
}
validate('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa');
// validate('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'prod');
validate('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef');
// validate('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'testnet');
validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP');
validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y');
validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs');
validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez');
validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd');
// p2sh addresses
validate('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt');
// validate('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'prod');
validate('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7');
// validate('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7', 'testnet');
})
it('does not validate illegal examples', function() {
function invalid(addr) {
assert.ok(!Address.validate(addr));
}
invalid('');
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe');
// invalid('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'testnet');
// invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'prod');
// invalid base58 string
invalid('%%@');
})
})
})

22
test/base58.js

@ -0,0 +1,22 @@
/* global describe, it */
var assert = require('assert');
var base58 = require('../').base58;
var conv = require('../').convert;
describe('base58', function() {
describe('decode', function() {
it('validates known examples', function() {
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ';
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d';
assert.deepEqual(base58.decode(enc), conv.hexToBytes(hex));
})
})
describe('encode', function() {
it('handles known examples', function() {
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ';
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d';
assert.equal(base58.encode(conv.hexToBytes(hex)), enc);
})
})
})

28
test/convert.js

@ -0,0 +1,28 @@
/* global describe, it */
var assert = require('assert');
var convert = require('../src/convert.js');
describe('convert', function() {
describe('bytesToHex', function() {
it('handles example 1', function() {
assert.equal(convert.bytesToHex([0, 1, 2, 255]), '000102ff');
})
})
describe('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);
})
})

23
test/ec.js

@ -0,0 +1,23 @@
/* global describe, it */
var assert = require('assert');
var sec = require('../src/jsbn/sec');
var ecdsa = require('../').ecdsa;
var ecparams = sec('secp256k1');
describe('ecdsa', function() {
it('handles point ultiplication', function() {
var G = ecparams.getG();
var n = ecparams.getN();
assert.ok(G.multiply(n).isInfinity(), "Gn is infinite");
var k = ecdsa.getBigRandom(n);
var P = G.multiply(k);
assert.ok(!P.isInfinity(), "kG is not infinite");
assert.ok(P.isOnCurve(), "kG on curve");
assert.ok(P.multiply(n).isInfinity(), "kGn is infinite");
assert.ok(P.validate(), "kG validates as a public key");
})
})

43
test/eckey.js

@ -0,0 +1,43 @@
/* global describe, it */
var assert = require('assert');
var ECKey = require('../src/eckey.js').ECKey;
var convert = require('../src/convert.js');
var bytesToHex = convert.bytesToHex;
var hexToBytes = convert.hexToBytes;
describe('ECKey', function() {
describe('constructor (base58 private)', function() {
it('parses case 1', function() {
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725';
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235' +
'22cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6';
var key = new ECKey(hexToBytes(priv));
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub);
assert.equal(key.compressed, false);
})
it('parses case 2', function() {
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh';
var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0' +
'f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1';
var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a';
var key = new ECKey(priv);
assert.equal(key.compressed, false);
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub);
assert.equal(key.getBitcoinAddress().toString(), addr);
})
it('parses case 3', function() {
var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp';
var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa'
var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9';
var key = new ECKey(priv);
assert.equal(key.compressed, true);
assert.equal(bytesToHex(key.getPub()['export']('bytes')), pub);
assert.equal(key.getBitcoinAddress().toString(), addr);
})
})
})

217
test/hdwallet.js

@ -0,0 +1,217 @@
/* global describe, it */
var HDWallet = require('../src/hdwallet.js')
, assert = require('assert')
, convert = require('../src/convert.js')
var b2h = convert.bytesToHex
describe('HDWallet', function() {
describe('toBase58', function() {
it('reproduces input', function() {
var input = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5'
, output = HDWallet.fromBase58(input).toBase58(false)
assert.equal(output, input)
input = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334'
output = HDWallet.fromBase58(input).toBase58(true)
assert.equal(output, input)
})
it('fails with priv=true when theres no private key', function() {
var hd = HDWallet.fromBase58('xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon')
try {
hd.toBase58(true)
} catch(e) {
assert(e.message.match(/private key/i))
return
}
assert.fail()
})
})
describe('ctor', function() {
it('creates from seed', function() {
var seed = 'crazy horse battery staple'
, hd = new HDWallet(seed)
assert(hd.priv)
assert(hd.pub)
})
})
describe('Test vectors', function() {
it('Test vector 1', function() {
var hd = HDWallet.fromMasterHex('000102030405060708090a0b0c0d0e0f')
// m
assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811')
assert.equal(b2h(hd.getFingerprint()), '3442193e')
assert.equal(hd.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.fromMasterHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542')
// m
assert.equal(b2h(hd.getIdentifier()), 'bd16bee53961a47d6ad888e29545434a89bdfe95')
assert.equal(b2h(hd.getFingerprint()), 'bd16bee5')
assert.equal(hd.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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.getBitcoinAddress().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')
})
})
})

36
test/index.html

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>BitcoinJS-lib Test Suite</title>
<link rel="stylesheet" href="../vendor/qunit/qunit.css" type="text/css" media="screen">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="../vendor/qunit/qunit.js"></script>
<script type="text/javascript" src="../src/crypto-js/crypto.js"></script>
<script type="text/javascript" src="../src/crypto-js/sha256.js"></script>
<script type="text/javascript" src="../src/jsbn/prng4.js"></script>
<script type="text/javascript" src="../src/jsbn/rng.js"></script>
<script type="text/javascript" src="../src/jsbn/jsbn.js"></script>
<script type="text/javascript" src="../src/jsbn/jsbn2.js"></script>
<script type="text/javascript" src="../src/jsbn/ec.js"></script>
<script type="text/javascript" src="../src/jsbn/sec.js"></script>
<script type="text/javascript" src="../src/events/eventemitter.js"></script>
<script type="text/javascript" src="../src/bitcoin.js"></script>
<script type="text/javascript" src="../src/util.js"></script>
<script type="text/javascript" src="../src/base58.js"></script>
<script type="text/javascript" src="../src/address.js"></script>
<script type="text/javascript" src="../src/ecdsa.js"></script>
<script type="text/javascript" src="../src/eckey.js"></script>
<script type="text/javascript" src="../src/paillier.js"></script>
<script type="text/javascript" src="test.js"></script>
</head>
<body>
<h1 id="qunit-header">BitcoinJS-lib Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>
</body>
</html>

28
test/jsbn.js

@ -0,0 +1,28 @@
/* global describe, it */
var assert = require('assert');
var BigInteger = require('../src/jsbn/jsbn.js')
var bytesToHex = require('../src/convert.js').bytesToHex;
describe('BigInteger', function() {
describe('toByteArraySigned', function() {
it('handles examples', function() {
function hex(num) {
var bytes = BigInteger.valueOf(num).toByteArraySigned();
var h = bytesToHex(bytes);
return '0x' + h;
}
assert.equal(hex( 0), '0x');
assert.equal(hex( 1), '0x01');
assert.equal(hex(-1), '0x81');
assert.equal(hex( 127), '0x7f');
assert.equal(hex(-127), '0xff');
assert.equal(hex( 255), '0x00ff');
assert.equal(hex(-255), '0x80ff');
assert.equal(hex( 16300), '0x3fac');
assert.equal(hex(-16300), '0xbfac');
assert.equal(hex( 62300), '0x00f35c');
assert.equal(hex(-62300), '0x80f35c');
})
})
})

34
test/message.js

@ -0,0 +1,34 @@
/* global describe, it */
var assert = require('assert');
var Message = require('../src/message.js');
var ECKey = require('../src/eckey.js').ECKey;
var hexToBytes = require('../src/convert.js').hexToBytes;
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725';
var addr = '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM';
var msg = 'foobar';
describe('Message', function() {
describe('verify', function() {
it('passes case 1', function() {
var key = new ECKey(hexToBytes(priv));
assert.equal(key.getBitcoinAddress().toString(), addr);
var sig = Message.signMessage(key, msg);
assert.ok(Message.verifyMessage(addr, sig, msg));
// wrong message
assert.ok(!Message.verifyMessage(addr, sig, 'not foobar'));
// wrong address
assert.ok(!Message.verifyMessage('1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', sig, msg));
})
it('passes case 2', function() {
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh';
var key = new ECKey(hexToBytes(priv));
var sig = Message.signMessage(key, msg);
assert.ok(!Message.verifyMessage(addr, sig, msg));
})
})
})

68
test/misc.js

@ -0,0 +1,68 @@
/* global it */
var assert = require('assert');
var bitcoinjs = require('../');
var sec = require('../src/jsbn/sec');
var BigInteger = require('../src/jsbn/jsbn.js');
var Crypto = require('../src/crypto-js/crypto.js');
var SecureRandom = require('../src/jsbn/rng');
var rng = new SecureRandom();
var ecparams = sec('secp256k1');
var ECPointFp = bitcoinjs.ECPointFp;
it('Keys & Key Management', function () {
var p1 = bitcoinjs.Key().getPub()['export']('bytes');
assert.equal(p1.length, 65);
var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1);
assert.ok(p1_q);
assert.ok(p1_q.validate());
var p2 = bitcoinjs.convert.hexToBytes(
'0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a' +
'b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c');
var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2);
assert.ok(p2_q);
assert.ok(p2_q.validate());
});
it('Signing and Verifying', function () {
var s1 = bitcoinjs.Key();
var sig_a = s1.sign(BigInteger.ZERO);
assert.ok(sig_a, 'Sign null');
assert.ok(s1.verify(BigInteger.ZERO, sig_a));
var message = new BigInteger(1024, rng).toByteArrayUnsigned();
var hash = Crypto.SHA256(message, {
asBytes: true
});
var sig_b = s1.sign(hash);
assert.ok(sig_b, 'Sign random string');
assert.ok(s1.verify(hash, sig_b));
var message2 = bitcoinjs.convert.hexToBytes(
'12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' +
'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' +
'1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' +
'8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' +
'3d82507b932b84e4');
var hash2 = Crypto.SHA256(message2, {
asBytes: true
});
var sig_c = bitcoinjs.convert.hexToBytes(
'3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' +
'79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' +
'f273ab082801a95506c4');
var s2 = bitcoinjs.convert.hexToBytes(
'045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' +
'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' +
'609a978528');
assert.ok(bitcoinjs.ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature');
});

1
test/mocha.opts

@ -0,0 +1 @@
--ui qunit

112
test/test.js

@ -1,112 +0,0 @@
//
// Testing elliptic curve math
// -----------------------------------------------------------------------------
module("ec");
var ecparams = getSECCurveByName("secp256k1");
var rng = new SecureRandom();
test("Classes", function () {
expect(3);
ok(ECPointFp, "ECPointFp");
ok(ECFieldElementFp, "ECFieldElementFp");
ok(ECCurveFp, "ECCurveFp");
});
test("Point multiplication", function () {
expect(5);
var G = ecparams.getG();
var n = ecparams.getN();
ok(G.multiply(n).isInfinity(), "Gn is infinite");
var k = Bitcoin.ECDSA.getBigRandom(n);
var P = G.multiply(k);
ok(!P.isInfinity(), "kG is not infinite");
ok(P.isOnCurve(), "kG on curve");
ok(P.multiply(n).isInfinity(), "kGn is infinite");
ok(P.validate(), "kG validates as a public key");
});
//
// Testing ECDSA
// -----------------------------------------------------------------------------
module("ecdsa");
test("Classes", function () {
expect(2);
ok(Bitcoin.ECDSA, "Bitcoin.ECDSA");
ok(Bitcoin.ECKey, "Bitcoin.ECKey");
});
test("Keys & Key Management", function () {
expect(5);
var s1 = new Bitcoin.ECKey();
var p1 = s1.getPub();
equals(p1.length, 65, "Public key is correct length");
var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1);
ok(p1_q, "Decode point from generated bytestring");
ok(p1_q.validate(), "Is a valid public point");
var p2 = Crypto.util.hexToBytes(
"0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a" +
"b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c"
);
var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2);
ok(p2_q, "Decode point from constant");
ok(p2_q.validate(), "Is a valid public point");
});
test("Signing and Verifying", function () {
expect(7);
var s1 = new Bitcoin.ECKey();
var sig_a = s1.sign(BigInteger.ZERO);
ok(sig_a, "Sign null");
equals(sig_a.length, 70, "Signature is correct length");
ok(s1.verify(BigInteger.ZERO, sig_a));
var message = new BigInteger(1024, rng).toByteArrayUnsigned();
var hash = Crypto.SHA256(message, {asBytes: true});
var sig_b = s1.sign(hash);
ok(sig_b, "Sign random string");
equals(sig_b.length, 70, "Signature is correct length");
ok(s1.verify(hash, sig_b));
var message2 = Crypto.util.hexToBytes(
"12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d" +
"d296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13" +
"1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66" +
"8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa" +
"3d82507b932b84e4"
);
var hash2 = Crypto.SHA256(message2, {asBytes: true});
var sig_c = Crypto.util.hexToBytes(
"3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3" +
"79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3" +
"f273ab082801a95506c4"
);
var s2 = Crypto.util.hexToBytes(
"045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51" +
"b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828" +
"609a978528"
);
ok(Bitcoin.ECDSA.verify(hash2, sig_c, s2), "Verify constant signature");
});
//
// Testing Paillier
// -----------------------------------------------------------------------------
module("paillier");
test("Classes", function () {
expect(3);
ok(Bitcoin.Paillier, "Bitcoin.Paillier");
ok(Bitcoin.Paillier.PublicKey, "Bitcoin.Paillier.PublicKey");
ok(Bitcoin.Paillier.PrivateKey, "Bitcoin.Paillier.PrivateKey");
});

226
vendor/qunit/qunit.css

@ -1,226 +0,0 @@
/**
* QUnit - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}

1510
vendor/qunit/qunit.js

File diff suppressed because it is too large
Loading…
Cancel
Save