Merge justcoin/master
This commit is contained in:
commit
fcf3fde089
50 changed files with 4531 additions and 5424 deletions
6
.travis.yml
Normal file
6
.travis.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.11"
|
||||||
|
- "0.10"
|
||||||
|
- "0.8"
|
||||||
|
- "0.6"
|
53
Jakefile.js
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
|
|
||||||
});
|
|
|
@ -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
vendored
Normal file
3
bitcoinjs-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
build/bitcoinjs-exit-min.js
vendored
8
build/bitcoinjs-exit-min.js
vendored
|
@ -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
vendored
28
build/bitcoinjs-min.js
vendored
File diff suppressed because one or more lines are too long
0
demo/split-key.html
Executable file → Normal file
0
demo/split-key.html
Executable file → Normal file
18
package.json
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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');
|
||||||
this.hash = bytes;
|
|
||||||
|
|
||||||
this.version = 0x00;
|
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;
|
||||||
|
}
|
||||||
|
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 = 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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.Address.prototype.getHashBase64 = function () {
|
Address.prototype.getHash = function () {
|
||||||
return Crypto.util.bytesToBase64(this.hash);
|
return conv.bytesToHex(this.hash);
|
||||||
|
};
|
||||||
|
|
||||||
|
Address.getVersion = function(string) {
|
||||||
|
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;
|
||||||
|
|
174
src/base58.js
174
src/base58.js
|
@ -1,71 +1,119 @@
|
||||||
(function (Bitcoin) {
|
|
||||||
Bitcoin.Base58 = {
|
|
||||||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
||||||
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/,
|
|
||||||
base: BigInteger.valueOf(58),
|
|
||||||
|
|
||||||
/**
|
// https://en.bitcoin.it/wiki/Base58Check_encoding
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
encode: function (input) {
|
|
||||||
var bi = BigInteger.fromByteArrayUnsigned(input);
|
|
||||||
var chars = [];
|
|
||||||
|
|
||||||
while (bi.compareTo(B58.base) >= 0) {
|
var BigInteger = require('./jsbn/jsbn');
|
||||||
var mod = bi.mod(B58.base);
|
var Crypto = require('./crypto-js/crypto');
|
||||||
chars.unshift(B58.alphabet[mod.intValue()]);
|
var conv = require('./convert');
|
||||||
bi = bi.subtract(mod).divide(B58.base);
|
|
||||||
}
|
|
||||||
chars.unshift(B58.alphabet[bi.intValue()]);
|
|
||||||
|
|
||||||
// Convert leading zeros too.
|
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
for (var i = 0; i < input.length; i++) {
|
var base = BigInteger.valueOf(58);
|
||||||
if (input[i] == 0x00) {
|
|
||||||
chars.unshift(B58.alphabet[0]);
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return chars.join('');
|
var positions = {};
|
||||||
},
|
for (var i=0 ; i < alphabet.length ; ++i) {
|
||||||
|
positions[alphabet[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Convert a byte array to a base58-encoded string.
|
||||||
* Convert a base58-encoded string to a byte array.
|
// Written by Mike Hearn for BitcoinJ.
|
||||||
*
|
// Copyright (c) 2011 Google Inc.
|
||||||
* Written by Mike Hearn for BitcoinJ.
|
// Ported to JavaScript by Stefan Thomas.
|
||||||
* Copyright (c) 2011 Google Inc.
|
module.exports.encode = function (input) {
|
||||||
*
|
var bi = BigInteger.fromByteArrayUnsigned(input);
|
||||||
* Ported to JavaScript by Stefan Thomas.
|
var chars = [];
|
||||||
*/
|
|
||||||
decode: function (input) {
|
|
||||||
var bi = BigInteger.valueOf(0);
|
|
||||||
var leadingZerosNum = 0;
|
|
||||||
for (var i = input.length - 1; i >= 0; i--) {
|
|
||||||
var alphaIndex = B58.alphabet.indexOf(input[i]);
|
|
||||||
if (alphaIndex < 0) {
|
|
||||||
throw "Invalid character";
|
|
||||||
}
|
|
||||||
bi = bi.add(BigInteger.valueOf(alphaIndex)
|
|
||||||
.multiply(B58.base.pow(input.length - 1 -i)));
|
|
||||||
|
|
||||||
// This counts leading zero bytes
|
while (bi.compareTo(base) >= 0) {
|
||||||
if (input[i] == "1") leadingZerosNum++;
|
var mod = bi.mod(base);
|
||||||
else leadingZerosNum = 0;
|
chars.push(alphabet[mod.intValue()]);
|
||||||
}
|
bi = bi.subtract(mod).divide(base);
|
||||||
var bytes = bi.toByteArrayUnsigned();
|
|
||||||
|
|
||||||
// Add leading zeros
|
|
||||||
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
}
|
||||||
};
|
chars.push(alphabet[bi.intValue()]);
|
||||||
|
|
||||||
|
// Convert leading zeros too.
|
||||||
|
for (var i = 0; i < input.length; i++) {
|
||||||
|
if (input[i] == 0x00) {
|
||||||
|
chars.push(alphabet[0]);
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chars.reverse().join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
module.exports.encodeHex = function (input) {
|
||||||
|
return conv.bytesToHex(module.exports.encode(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode a base58 string into a byte array
|
||||||
|
// input should be a base58 encoded string
|
||||||
|
// @return Array
|
||||||
|
module.exports.decode = function (input) {
|
||||||
|
|
||||||
|
var base = BigInteger.valueOf(58);
|
||||||
|
|
||||||
|
var length = input.length;
|
||||||
|
var num = BigInteger.valueOf(0);
|
||||||
|
var leading_zero = 0;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
num = num.multiply(base).add(BigInteger.valueOf(p));
|
||||||
|
|
||||||
|
if (chr == '1' && !seen_other) {
|
||||||
|
++leading_zero;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
seen_other = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = num.toByteArrayUnsigned();
|
||||||
|
|
||||||
|
// remove leading zeros
|
||||||
|
while (leading_zero-- > 0) {
|
||||||
|
bytes.unshift(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
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
Normal file
74
src/convert.js
Normal file
|
@ -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
vendored
7
src/crypto-js/crypto-min.js
vendored
|
@ -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
Executable file → Normal file
104
src/crypto-js/crypto.js
Executable file → Normal file
|
@ -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
Normal file
38
src/crypto-js/hmac.js
Normal file
|
@ -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);
|
||||||
|
|
||||||
|
};
|
361
src/crypto-js/ripemd160.js
Executable file → Normal file
361
src/crypto-js/ripemd160.js
Executable file → Normal file
|
@ -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
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
* See http://pajhome.org.uk/crypt/md5 for details.
|
|
||||||
* Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
|
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
* Ported to Crypto-JS by Stefan Thomas.
|
- 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var conv = require('../convert');
|
||||||
|
var UTF8 = require('./crypto').charenc.UTF8;
|
||||||
|
|
||||||
|
// Constants table
|
||||||
|
var zl = [
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13];
|
||||||
|
var zr = [
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11];
|
||||||
|
var sl = [
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ];
|
||||||
|
var sr = [
|
||||||
|
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, 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,
|
||||||
|
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ];
|
||||||
|
|
||||||
|
var hl = [ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E];
|
||||||
|
var hr = [ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000];
|
||||||
|
|
||||||
|
var bytesToWords = function (bytes) {
|
||||||
|
var words = [];
|
||||||
|
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
|
||||||
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
|
module.exports = function(message, options) {
|
||||||
|
|
||||||
(function () {
|
if (message.constructor === String) {
|
||||||
// Shortcuts
|
message = UTF8.stringToBytes(message);
|
||||||
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
|
var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
|
||||||
util.bytesToLWords = function (bytes) {
|
var m = bytesToWords(message);
|
||||||
|
|
||||||
var output = Array(bytes.length >> 2);
|
var nBitsLeft = message.length * 8;
|
||||||
for (var i = 0; i < output.length; i++)
|
var nBitsTotal = message.length * 8;
|
||||||
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
|
// Add padding
|
||||||
util.lWordsToBytes = function (words) {
|
m[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||||
var output = [];
|
m[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
||||||
for (var i = 0; i < words.length * 32; i += 8)
|
(((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) |
|
||||||
output.push((words[i>>5] >>> (i % 32)) & 0xff);
|
(((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00)
|
||||||
return output;
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Public API
|
for (var i=0 ; i<m.length; i += 16) {
|
||||||
var RIPEMD160 = C.RIPEMD160 = function (message, options) {
|
processBlock(H, m, i);
|
||||||
var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message));
|
}
|
||||||
return options && options.asBytes ? digestbytes :
|
|
||||||
options && options.asString ? Binary.bytesToString(digestbytes) :
|
|
||||||
util.bytesToHex(digestbytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The core
|
// Swap endian
|
||||||
RIPEMD160._rmd160 = function (message)
|
for (var i = 0; i < 5; i++) {
|
||||||
{
|
// Shortcut
|
||||||
// Convert to byte array
|
var H_i = H[i];
|
||||||
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
||||||
|
|
||||||
var x = util.bytesToLWords(message),
|
// Swap
|
||||||
len = message.length * 8;
|
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
||||||
|
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
||||||
|
}
|
||||||
|
|
||||||
/* append padding */
|
var digestbytes = wordsToBytes(H);
|
||||||
x[len >> 5] |= 0x80 << (len % 32);
|
return options && options.asBytes ? digestbytes :
|
||||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
||||||
|
conv.bytesToHex(digestbytes);
|
||||||
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)
|
|
||||||
{
|
|
||||||
return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
|
|
||||||
(16 <= j && j <= 31) ? (x & y) | (~x & z) :
|
|
||||||
(32 <= j && j <= 47) ? (x | ~y) ^ z :
|
|
||||||
(48 <= j && j <= 63) ? (x & z) | (y & ~z) :
|
|
||||||
(64 <= j && j <= 79) ? x ^ (y | ~z) :
|
|
||||||
"rmd160_f: j out of range";
|
|
||||||
}
|
|
||||||
function rmd160_K1(j)
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
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
|
|
||||||
];
|
|
||||||
var rmd160_r2 = [
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
|
|
||||||
];
|
|
||||||
var rmd160_s1 = [
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
|
|
||||||
];
|
|
||||||
var rmd160_s2 = [
|
|
||||||
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, 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,
|
|
||||||
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
|
|
||||||
];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
|
||||||
* to work around bugs in some JS interpreters.
|
|
||||||
*/
|
|
||||||
function safe_add(x, y)
|
|
||||||
{
|
|
||||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
|
||||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
|
||||||
return (msw << 16) | (lsw & 0xFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bitwise rotate a 32-bit number to the left.
|
|
||||||
*/
|
|
||||||
function bit_rol(num, cnt)
|
|
||||||
{
|
|
||||||
return (num << cnt) | (num >>> (32 - cnt));
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
7
src/crypto-js/sha256-min.js
vendored
7
src/crypto-js/sha256-min.js
vendored
|
@ -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})();
|
|
255
src/crypto-js/sha256.js
Executable file → Normal file
255
src/crypto-js/sha256.js
Executable file → Normal file
|
@ -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
|
||||||
|
*/
|
||||||
|
|
||||||
|
var conv = require('../convert'),
|
||||||
|
util = require('../util');
|
||||||
|
|
||||||
|
// Initialization round constants tables
|
||||||
|
var K = [];
|
||||||
|
|
||||||
|
// Compute constants
|
||||||
|
(function () {
|
||||||
|
function isPrime(n) {
|
||||||
|
var sqrtN = Math.sqrt(n);
|
||||||
|
for (var factor = 2; factor <= sqrtN; factor++) {
|
||||||
|
if (!(n % factor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFractionalBits(n) {
|
||||||
|
return ((n - (n | 0)) * 0x100000000) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = 2;
|
||||||
|
var nPrime = 0;
|
||||||
|
while (nPrime < 64) {
|
||||||
|
if (isPrime(n)) {
|
||||||
|
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
|
||||||
|
nPrime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
// 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 >>> 3);
|
||||||
|
|
||||||
|
var gamma1x = W[i - 2];
|
||||||
|
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
||||||
|
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
||||||
|
(gamma1x >>> 10);
|
||||||
|
|
||||||
|
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
var ch = (e & f) ^ (~e & g);
|
||||||
|
var maj = (a & b) ^ (a & c) ^ (b & c);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
var t1 = h + sigma1 + ch + K[i] + W[i];
|
||||||
|
var t2 = sigma0 + maj;
|
||||||
|
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = (d + t1) | 0;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = (t1 + t2) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intermediate hash value
|
||||||
|
H[0] = (H[0] + a) | 0;
|
||||||
|
H[1] = (H[1] + b) | 0;
|
||||||
|
H[2] = (H[2] + c) | 0;
|
||||||
|
H[3] = (H[3] + d) | 0;
|
||||||
|
H[4] = (H[4] + e) | 0;
|
||||||
|
H[5] = (H[5] + f) | 0;
|
||||||
|
H[6] = (H[6] + g) | 0;
|
||||||
|
H[7] = (H[7] + h) | 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA-256 hash algorithm.
|
||||||
*/
|
*/
|
||||||
(function(){
|
module.exports = function(message, options) {;
|
||||||
|
|
||||||
// Shortcuts
|
var H =[ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||||
var C = Crypto,
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ];
|
||||||
util = C.util,
|
|
||||||
charenc = C.charenc,
|
|
||||||
UTF8 = charenc.UTF8,
|
|
||||||
Binary = charenc.Binary;
|
|
||||||
|
|
||||||
// Constants
|
if (message.constructor === String) {
|
||||||
var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
message = conv.stringToBytes(message);
|
||||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
}
|
||||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
|
||||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
|
||||||
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 m = util.bytesToWords(message);
|
||||||
var SHA256 = C.SHA256 = function (message, options) {
|
var l = message.length * 8;
|
||||||
var digestbytes = util.wordsToBytes(SHA256._sha256(message));
|
|
||||||
return options && options.asBytes ? digestbytes :
|
m[l >> 5] |= 0x80 << (24 - l % 32);
|
||||||
options && options.asString ? Binary.bytesToString(digestbytes) :
|
m[((l + 64 >> 9) << 4) + 15] = l;
|
||||||
util.bytesToHex(digestbytes);
|
|
||||||
|
for (var i=0 ; i<m.length; i += 16) {
|
||||||
|
processBlock(H, m, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var digestbytes = util.wordsToBytes(H);
|
||||||
|
return options && options.asBytes ? digestbytes :
|
||||||
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
||||||
|
conv.bytesToHex(digestbytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The core
|
module.exports._blocksize = 64
|
||||||
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];
|
|
||||||
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++) {
|
|
||||||
|
|
||||||
if (j < 16) w[j] = m[j + i];
|
|
||||||
else {
|
|
||||||
|
|
||||||
var gamma0x = w[j - 15],
|
|
||||||
gamma1x = w[j - 2],
|
|
||||||
gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
|
||||||
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
|
||||||
(gamma0x >>> 3),
|
|
||||||
gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
|
||||||
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
|
||||||
(gamma1x >>> 10);
|
|
||||||
|
|
||||||
w[j] = gamma0 + (w[j - 7] >>> 0) +
|
|
||||||
gamma1 + (w[j - 16] >>> 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var ch = e & f ^ ~e & g,
|
|
||||||
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));
|
|
||||||
|
|
||||||
|
|
||||||
t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0);
|
|
||||||
t2 = sigma0 + maj;
|
|
||||||
|
|
||||||
h = g;
|
|
||||||
g = f;
|
|
||||||
f = e;
|
|
||||||
e = d + t1;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b = a;
|
|
||||||
a = t1 + t2;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
H[0] += a;
|
|
||||||
H[1] += b;
|
|
||||||
H[2] += c;
|
|
||||||
H[3] += d;
|
|
||||||
H[4] += e;
|
|
||||||
H[5] += f;
|
|
||||||
H[6] += g;
|
|
||||||
H[7] += h;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return H;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Package private blocksize
|
|
||||||
SHA256._blocksize = 16;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
239
src/crypto-js/sha512.js
Normal file
239
src/crypto-js/sha512.js
Normal file
|
@ -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
|
720
src/ecdsa.js
720
src/ecdsa.js
|
@ -1,475 +1,305 @@
|
||||||
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;
|
var rng = new SecureRandom();
|
||||||
};
|
var ecparams = sec("secp256k1");
|
||||||
|
var P_OVER_FOUR = null;
|
||||||
|
|
||||||
ECFieldElementFp.prototype.getByteLength = function () {
|
function implShamirsTrick(P, k, Q, l)
|
||||||
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
|
{
|
||||||
};
|
var m = Math.max(k.bitLength(), l.bitLength());
|
||||||
|
var Z = P.add2D(Q);
|
||||||
|
var R = P.curve.getInfinity();
|
||||||
|
|
||||||
ECPointFp.prototype.getEncoded = function (compressed) {
|
for (var i = m - 1; i >= 0; --i) {
|
||||||
var x = this.getX().toBigInteger();
|
R = R.twice2D();
|
||||||
var y = this.getY().toBigInteger();
|
|
||||||
|
|
||||||
// Get value as a 32-byte Buffer
|
R.z = BigInteger.ONE;
|
||||||
// Fixed length based on a patch by bitaddress.org and Casascius
|
|
||||||
var enc = integerToBytes(x, 32);
|
|
||||||
|
|
||||||
if (compressed) {
|
if (k.testBit(i)) {
|
||||||
if (y.isEven()) {
|
if (l.testBit(i)) {
|
||||||
// Compressed even pubkey
|
R = R.add2D(Z);
|
||||||
// M = 02 || X
|
} else {
|
||||||
enc.unshift(0x02);
|
R = R.add2D(P);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Compressed uneven pubkey
|
if (l.testBit(i)) {
|
||||||
// M = 03 || X
|
R = R.add2D(Q);
|
||||||
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 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;
|
return R;
|
||||||
};
|
};
|
||||||
|
|
||||||
ECPointFp.prototype.isOnCurve = function () {
|
function deterministicGenerateK(hash,key) {
|
||||||
var x = this.getX().toBigInteger();
|
var v = [];
|
||||||
var y = this.getY().toBigInteger();
|
var k = [];
|
||||||
var a = this.curve.getA().toBigInteger();
|
for (var i = 0;i < 32;i++) v.push(1);
|
||||||
var b = this.curve.getB().toBigInteger();
|
for (var i = 0;i < 32;i++) k.push(0);
|
||||||
var n = this.curve.getQ();
|
k = Crypto.HMAC(Crypto.SHA256,v.concat([0]).concat(key).concat(hash),k,{ asBytes: true })
|
||||||
var lhs = y.multiply(y).mod(n);
|
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
|
||||||
var rhs = x.multiply(x).multiply(x)
|
k = Crypto.HMAC(Crypto.SHA256,v.concat([1]).concat(key).concat(hash),k,{ asBytes: true })
|
||||||
.add(a.multiply(x)).add(b).mod(n);
|
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
|
||||||
return lhs.equals(rhs);
|
v = Crypto.HMAC(Crypto.SHA256,v,k,{ asBytes: true })
|
||||||
};
|
return BigInteger.fromByteArrayUnsigned(v);
|
||||||
|
}
|
||||||
|
|
||||||
ECPointFp.prototype.toString = function () {
|
var ECDSA = {
|
||||||
return '('+this.getX().toBigInteger().toString()+','+
|
getBigRandom: function (limit) {
|
||||||
this.getY().toBigInteger().toString()+')';
|
return new BigInteger(limit.bitLength(), rng)
|
||||||
};
|
.mod(limit.subtract(BigInteger.ONE))
|
||||||
|
.add(BigInteger.ONE)
|
||||||
|
;
|
||||||
|
},
|
||||||
|
sign: function (hash, priv) {
|
||||||
|
var d = priv;
|
||||||
|
var n = ecparams.getN();
|
||||||
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
|
||||||
/**
|
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
|
||||||
* Validate an elliptic curve point.
|
var G = ecparams.getG();
|
||||||
*
|
var Q = G.multiply(k);
|
||||||
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive
|
var r = Q.getX().toBigInteger().mod(n);
|
||||||
*/
|
|
||||||
ECPointFp.prototype.validate = function () {
|
|
||||||
var n = this.curve.getQ();
|
|
||||||
|
|
||||||
// Check Q != O
|
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
||||||
if (this.isInfinity()) {
|
|
||||||
throw new Error("Point is at infinity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check coordinate bounds
|
return ECDSA.serializeSig(r, s);
|
||||||
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)
|
verify: function (hash, sig, pubkey) {
|
||||||
if (!this.isOnCurve()) {
|
var r,s;
|
||||||
throw new Error("Point is not on the curve.");
|
if (util.isArray(sig)) {
|
||||||
}
|
var obj = ECDSA.parseSig(sig);
|
||||||
|
r = obj.r;
|
||||||
|
s = obj.s;
|
||||||
|
} else if ("object" === typeof sig && sig.r && sig.s) {
|
||||||
|
r = sig.r;
|
||||||
|
s = sig.s;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid value for signature");
|
||||||
|
}
|
||||||
|
|
||||||
// Check nQ = 0 (Q is a scalar multiple of G)
|
var Q;
|
||||||
if (this.multiply(n).isInfinity()) {
|
if (pubkey instanceof ECPointFp) {
|
||||||
// TODO: This check doesn't work - fix.
|
Q = pubkey;
|
||||||
throw new Error("Point is not a scalar multiple of G.");
|
} else if (util.isArray(pubkey)) {
|
||||||
}
|
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp");
|
||||||
|
}
|
||||||
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
|
||||||
return true;
|
return ECDSA.verifyRaw(e, r, s, Q);
|
||||||
};
|
},
|
||||||
|
|
||||||
function dmp(v) {
|
verifyRaw: function (e, r, s, Q) {
|
||||||
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
var n = ecparams.getN();
|
||||||
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
|
var G = ecparams.getG();
|
||||||
};
|
|
||||||
|
|
||||||
Bitcoin.ECDSA = (function () {
|
if (r.compareTo(BigInteger.ONE) < 0 ||
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
r.compareTo(n) >= 0)
|
||||||
var rng = new SecureRandom();
|
return false;
|
||||||
|
|
||||||
var P_OVER_FOUR = null;
|
if (s.compareTo(BigInteger.ONE) < 0 ||
|
||||||
|
s.compareTo(n) >= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
function implShamirsTrick(P, k, Q, l)
|
var c = s.modInverse(n);
|
||||||
|
|
||||||
|
var u1 = e.multiply(c).mod(n);
|
||||||
|
var u2 = r.multiply(c).mod(n);
|
||||||
|
|
||||||
|
// TODO(!!!): For some reason Shamir's trick isn't working with
|
||||||
|
// signed message verification!? Probably an implementation
|
||||||
|
// error!
|
||||||
|
//var point = implShamirsTrick(G, u1, Q, u2);
|
||||||
|
var point = G.multiply(u1).add(Q.multiply(u2));
|
||||||
|
|
||||||
|
var v = point.getX().toBigInteger().mod(n);
|
||||||
|
|
||||||
|
return v.equals(r);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a signature into DER format.
|
||||||
|
*
|
||||||
|
* Takes two BigIntegers representing r and s and returns a byte array.
|
||||||
|
*/
|
||||||
|
serializeSig: function (r, s) {
|
||||||
|
var rBa = r.toByteArraySigned();
|
||||||
|
var sBa = s.toByteArraySigned();
|
||||||
|
|
||||||
|
var sequence = [];
|
||||||
|
sequence.push(0x02); // INTEGER
|
||||||
|
sequence.push(rBa.length);
|
||||||
|
sequence = sequence.concat(rBa);
|
||||||
|
|
||||||
|
sequence.push(0x02); // INTEGER
|
||||||
|
sequence.push(sBa.length);
|
||||||
|
sequence = sequence.concat(sBa);
|
||||||
|
|
||||||
|
sequence.unshift(sequence.length);
|
||||||
|
sequence.unshift(0x30); // SEQUENCE
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a byte array containing a DER-encoded signature.
|
||||||
|
*
|
||||||
|
* This function will return an object of the form:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* r: BigInteger,
|
||||||
|
* s: BigInteger
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
parseSig: function (sig) {
|
||||||
|
var cursor;
|
||||||
|
if (sig[0] != 0x30)
|
||||||
|
throw new Error("Signature not a valid DERSequence");
|
||||||
|
|
||||||
|
cursor = 2;
|
||||||
|
if (sig[cursor] != 0x02)
|
||||||
|
throw new Error("First element in signature must be a DERInteger");;
|
||||||
|
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
||||||
|
|
||||||
|
cursor += 2+sig[cursor+1];
|
||||||
|
if (sig[cursor] != 0x02)
|
||||||
|
throw new Error("Second element in signature must be a DERInteger");
|
||||||
|
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
||||||
|
|
||||||
|
cursor += 2+sig[cursor+1];
|
||||||
|
|
||||||
|
//if (cursor != sig.length)
|
||||||
|
// throw new Error("Extra bytes in signature");
|
||||||
|
|
||||||
|
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
||||||
|
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
||||||
|
|
||||||
|
return {r: r, s: s};
|
||||||
|
},
|
||||||
|
|
||||||
|
parseSigCompact: function (sig) {
|
||||||
|
if (sig.length !== 65) {
|
||||||
|
throw new Error("Signature has the wrong length");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature is prefixed with a type byte storing three bits of
|
||||||
|
// information.
|
||||||
|
var i = sig[0] - 27;
|
||||||
|
if (i < 0 || i > 7) {
|
||||||
|
throw new Error("Invalid signature type");
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = ecparams.getN();
|
||||||
|
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n);
|
||||||
|
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n);
|
||||||
|
|
||||||
|
return {r: r, s: s, i: i};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recover a public key from a signature.
|
||||||
|
*
|
||||||
|
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
|
||||||
|
* Key Recovery Operation".
|
||||||
|
*
|
||||||
|
* http://www.secg.org/download/aid-780/sec1-v2.pdf
|
||||||
|
*/
|
||||||
|
recoverPubKey: function (r, s, hash, i) {
|
||||||
|
// The recovery parameter i has two bits.
|
||||||
|
i = i & 3;
|
||||||
|
|
||||||
|
// The less significant bit specifies whether the y coordinate
|
||||||
|
// of the compressed point is even or not.
|
||||||
|
var isYEven = i & 1;
|
||||||
|
|
||||||
|
// The more significant bit specifies whether we should use the
|
||||||
|
// first or second candidate key.
|
||||||
|
var isSecondKey = i >> 1;
|
||||||
|
|
||||||
|
var n = ecparams.getN();
|
||||||
|
var G = ecparams.getG();
|
||||||
|
var curve = ecparams.getCurve();
|
||||||
|
var p = curve.getQ();
|
||||||
|
var a = curve.getA().toBigInteger();
|
||||||
|
var b = curve.getB().toBigInteger();
|
||||||
|
|
||||||
|
// We precalculate (p + 1) / 4 where p is if the field order
|
||||||
|
if (!P_OVER_FOUR) {
|
||||||
|
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.1 Compute x
|
||||||
|
var x = isSecondKey ? r.add(n) : r;
|
||||||
|
|
||||||
|
// 1.3 Convert x to point
|
||||||
|
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
|
||||||
|
var beta = alpha.modPow(P_OVER_FOUR, p);
|
||||||
|
|
||||||
|
var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2);
|
||||||
|
// If beta is even, but y isn't or vice versa, then convert it,
|
||||||
|
// otherwise we're done and y == beta.
|
||||||
|
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta);
|
||||||
|
|
||||||
|
// 1.4 Check that nR is at infinity
|
||||||
|
var R = new ECPointFp(curve,
|
||||||
|
curve.fromBigInteger(x),
|
||||||
|
curve.fromBigInteger(y));
|
||||||
|
R.validate();
|
||||||
|
|
||||||
|
// 1.5 Compute e from M
|
||||||
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
var eNeg = BigInteger.ZERO.subtract(e).mod(n);
|
||||||
|
|
||||||
|
// 1.6 Compute Q = r^-1 (sR - eG)
|
||||||
|
var rInv = r.modInverse(n);
|
||||||
|
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv);
|
||||||
|
|
||||||
|
Q.validate();
|
||||||
|
if (!ECDSA.verifyRaw(e, r, s, Q)) {
|
||||||
|
throw new Error("Pubkey recovery unsuccessful");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (shtylman) this is stupid because this file and eckey
|
||||||
|
// have circular dependencies
|
||||||
|
var ECPubKey = require('./eckey').ECPubKey;
|
||||||
|
return ECPubKey(Q);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate pubkey extraction parameter.
|
||||||
|
*
|
||||||
|
* When extracting a pubkey from a signature, we have to
|
||||||
|
* distinguish four different cases. Rather than putting this
|
||||||
|
* burden on the verifier, Bitcoin includes a 2-bit value with the
|
||||||
|
* signature.
|
||||||
|
*
|
||||||
|
* This function simply tries all four cases and returns the value
|
||||||
|
* that resulted in a successful pubkey recovery.
|
||||||
|
*/
|
||||||
|
calcPubkeyRecoveryParam: function (origPubkey, r, s, hash)
|
||||||
{
|
{
|
||||||
var m = Math.max(k.bitLength(), l.bitLength());
|
var address = origPubkey.getBitcoinAddress().toString();
|
||||||
var Z = P.add2D(Q);
|
for (var i = 0; i < 4; i++) {
|
||||||
var R = P.curve.getInfinity();
|
var pubkey = ECDSA.recoverPubKey(r, s, hash, i);
|
||||||
|
pubkey.compressed = origPubkey.compressed;
|
||||||
for (var i = m - 1; i >= 0; --i) {
|
if (pubkey.getBitcoinAddress().toString() == address) {
|
||||||
R = R.twice2D();
|
return i;
|
||||||
|
|
||||||
R.z = BigInteger.ONE;
|
|
||||||
|
|
||||||
if (k.testBit(i)) {
|
|
||||||
if (l.testBit(i)) {
|
|
||||||
R = R.add2D(Z);
|
|
||||||
} else {
|
|
||||||
R = R.add2D(P);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (l.testBit(i)) {
|
|
||||||
R = R.add2D(Q);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R;
|
throw new Error("Unable to find valid recovery factor");
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var ECDSA = {
|
module.exports = ECDSA;
|
||||||
getBigRandom: function (limit) {
|
|
||||||
return new BigInteger(limit.bitLength(), rng)
|
|
||||||
.mod(limit.subtract(BigInteger.ONE))
|
|
||||||
.add(BigInteger.ONE)
|
|
||||||
;
|
|
||||||
},
|
|
||||||
sign: function (hash, priv) {
|
|
||||||
var d = priv;
|
|
||||||
var n = ecparams.getN();
|
|
||||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
|
||||||
|
|
||||||
do {
|
|
||||||
var k = ECDSA.getBigRandom(n);
|
|
||||||
var G = ecparams.getG();
|
|
||||||
var Q = G.multiply(k);
|
|
||||||
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);
|
|
||||||
|
|
||||||
return ECDSA.serializeSig(r, s);
|
|
||||||
},
|
|
||||||
|
|
||||||
verify: function (hash, sig, pubkey) {
|
|
||||||
var r,s;
|
|
||||||
if (Bitcoin.Util.isArray(sig)) {
|
|
||||||
var obj = ECDSA.parseSig(sig);
|
|
||||||
r = obj.r;
|
|
||||||
s = obj.s;
|
|
||||||
} else if ("object" === typeof sig && sig.r && sig.s) {
|
|
||||||
r = sig.r;
|
|
||||||
s = sig.s;
|
|
||||||
} else {
|
|
||||||
throw "Invalid value for signature";
|
|
||||||
}
|
|
||||||
|
|
||||||
var Q;
|
|
||||||
if (pubkey instanceof ECPointFp) {
|
|
||||||
Q = pubkey;
|
|
||||||
} else if (Bitcoin.Util.isArray(pubkey)) {
|
|
||||||
Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
|
||||||
} else {
|
|
||||||
throw "Invalid format for pubkey value, must be byte array or ECPointFp";
|
|
||||||
}
|
|
||||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
|
||||||
|
|
||||||
return ECDSA.verifyRaw(e, r, s, Q);
|
|
||||||
},
|
|
||||||
|
|
||||||
verifyRaw: function (e, r, s, Q) {
|
|
||||||
var n = ecparams.getN();
|
|
||||||
var G = ecparams.getG();
|
|
||||||
|
|
||||||
if (r.compareTo(BigInteger.ONE) < 0 ||
|
|
||||||
r.compareTo(n) >= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (s.compareTo(BigInteger.ONE) < 0 ||
|
|
||||||
s.compareTo(n) >= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var c = s.modInverse(n);
|
|
||||||
|
|
||||||
var u1 = e.multiply(c).mod(n);
|
|
||||||
var u2 = r.multiply(c).mod(n);
|
|
||||||
|
|
||||||
// TODO(!!!): For some reason Shamir's trick isn't working with
|
|
||||||
// signed message verification!? Probably an implementation
|
|
||||||
// error!
|
|
||||||
//var point = implShamirsTrick(G, u1, Q, u2);
|
|
||||||
var point = G.multiply(u1).add(Q.multiply(u2));
|
|
||||||
|
|
||||||
var v = point.getX().toBigInteger().mod(n);
|
|
||||||
|
|
||||||
return v.equals(r);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a signature into DER format.
|
|
||||||
*
|
|
||||||
* Takes two BigIntegers representing r and s and returns a byte array.
|
|
||||||
*/
|
|
||||||
serializeSig: function (r, s) {
|
|
||||||
var rBa = r.toByteArraySigned();
|
|
||||||
var sBa = s.toByteArraySigned();
|
|
||||||
|
|
||||||
var sequence = [];
|
|
||||||
sequence.push(0x02); // INTEGER
|
|
||||||
sequence.push(rBa.length);
|
|
||||||
sequence = sequence.concat(rBa);
|
|
||||||
|
|
||||||
sequence.push(0x02); // INTEGER
|
|
||||||
sequence.push(sBa.length);
|
|
||||||
sequence = sequence.concat(sBa);
|
|
||||||
|
|
||||||
sequence.unshift(sequence.length);
|
|
||||||
sequence.unshift(0x30); // SEQUENCE
|
|
||||||
|
|
||||||
return sequence;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a byte array containing a DER-encoded signature.
|
|
||||||
*
|
|
||||||
* This function will return an object of the form:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* r: BigInteger,
|
|
||||||
* s: BigInteger
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
parseSig: function (sig) {
|
|
||||||
var cursor;
|
|
||||||
if (sig[0] != 0x30)
|
|
||||||
throw new Error("Signature not a valid DERSequence");
|
|
||||||
|
|
||||||
cursor = 2;
|
|
||||||
if (sig[cursor] != 0x02)
|
|
||||||
throw new Error("First element in signature must be a DERInteger");;
|
|
||||||
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
|
||||||
|
|
||||||
cursor += 2+sig[cursor+1];
|
|
||||||
if (sig[cursor] != 0x02)
|
|
||||||
throw new Error("Second element in signature must be a DERInteger");
|
|
||||||
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
|
|
||||||
|
|
||||||
cursor += 2+sig[cursor+1];
|
|
||||||
|
|
||||||
//if (cursor != sig.length)
|
|
||||||
// throw new Error("Extra bytes in signature");
|
|
||||||
|
|
||||||
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
|
||||||
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
|
||||||
|
|
||||||
return {r: r, s: s};
|
|
||||||
},
|
|
||||||
|
|
||||||
parseSigCompact: function (sig) {
|
|
||||||
if (sig.length !== 65) {
|
|
||||||
throw "Signature has the wrong length";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signature is prefixed with a type byte storing three bits of
|
|
||||||
// information.
|
|
||||||
var i = sig[0] - 27;
|
|
||||||
if (i < 0 || i > 7) {
|
|
||||||
throw "Invalid signature type";
|
|
||||||
}
|
|
||||||
|
|
||||||
var n = ecparams.getN();
|
|
||||||
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n);
|
|
||||||
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n);
|
|
||||||
|
|
||||||
return {r: r, s: s, i: i};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recover a public key from a signature.
|
|
||||||
*
|
|
||||||
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
|
|
||||||
* Key Recovery Operation".
|
|
||||||
*
|
|
||||||
* http://www.secg.org/download/aid-780/sec1-v2.pdf
|
|
||||||
*/
|
|
||||||
recoverPubKey: function (r, s, hash, i) {
|
|
||||||
// The recovery parameter i has two bits.
|
|
||||||
i = i & 3;
|
|
||||||
|
|
||||||
// The less significant bit specifies whether the y coordinate
|
|
||||||
// of the compressed point is even or not.
|
|
||||||
var isYEven = i & 1;
|
|
||||||
|
|
||||||
// The more significant bit specifies whether we should use the
|
|
||||||
// first or second candidate key.
|
|
||||||
var isSecondKey = i >> 1;
|
|
||||||
|
|
||||||
var n = ecparams.getN();
|
|
||||||
var G = ecparams.getG();
|
|
||||||
var curve = ecparams.getCurve();
|
|
||||||
var p = curve.getQ();
|
|
||||||
var a = curve.getA().toBigInteger();
|
|
||||||
var b = curve.getB().toBigInteger();
|
|
||||||
|
|
||||||
// We precalculate (p + 1) / 4 where p is if the field order
|
|
||||||
if (!P_OVER_FOUR) {
|
|
||||||
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.1 Compute x
|
|
||||||
var x = isSecondKey ? r.add(n) : r;
|
|
||||||
|
|
||||||
// 1.3 Convert x to point
|
|
||||||
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
|
|
||||||
var beta = alpha.modPow(P_OVER_FOUR, p);
|
|
||||||
|
|
||||||
var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2);
|
|
||||||
// If beta is even, but y isn't or vice versa, then convert it,
|
|
||||||
// otherwise we're done and y == beta.
|
|
||||||
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta);
|
|
||||||
|
|
||||||
// 1.4 Check that nR is at infinity
|
|
||||||
var R = new ECPointFp(curve,
|
|
||||||
curve.fromBigInteger(x),
|
|
||||||
curve.fromBigInteger(y));
|
|
||||||
R.validate();
|
|
||||||
|
|
||||||
// 1.5 Compute e from M
|
|
||||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
|
||||||
var eNeg = BigInteger.ZERO.subtract(e).mod(n);
|
|
||||||
|
|
||||||
// 1.6 Compute Q = r^-1 (sR - eG)
|
|
||||||
var rInv = r.modInverse(n);
|
|
||||||
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv);
|
|
||||||
|
|
||||||
Q.validate();
|
|
||||||
if (!ECDSA.verifyRaw(e, r, s, Q)) {
|
|
||||||
throw "Pubkey recovery unsuccessful";
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubKey = new Bitcoin.ECKey();
|
|
||||||
pubKey.pub = Q;
|
|
||||||
return pubKey;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate pubkey extraction parameter.
|
|
||||||
*
|
|
||||||
* When extracting a pubkey from a signature, we have to
|
|
||||||
* distinguish four different cases. Rather than putting this
|
|
||||||
* burden on the verifier, Bitcoin includes a 2-bit value with the
|
|
||||||
* signature.
|
|
||||||
*
|
|
||||||
* This function simply tries all four cases and returns the value
|
|
||||||
* that resulted in a successful pubkey recovery.
|
|
||||||
*/
|
|
||||||
calcPubkeyRecoveryParam: function (address, r, s, hash)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < 4; i++) {
|
|
||||||
try {
|
|
||||||
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i);
|
|
||||||
if (pubkey.getBitcoinAddress().toString() == address) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
throw "Unable to find valid recovery factor";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return ECDSA;
|
|
||||||
})();
|
|
||||||
|
|
260
src/eckey.js
260
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 conv = require('./convert');
|
||||||
|
var Address = require('./address');
|
||||||
|
var ecdsa = require('./ecdsa');
|
||||||
|
var ECPointFp = require('./jsbn/ec').ECPointFp;
|
||||||
|
|
||||||
var ECKey = function (input) {
|
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) }
|
||||||
ECKey.compressByDefault = false;
|
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 =
|
||||||
* Set whether the public key should be returned compressed or not.
|
compressed !== undefined ? compressed
|
||||||
*/
|
: input instanceof ECKey ? input.compressed
|
||||||
ECKey.prototype.setCompressed = function (v) {
|
: input instanceof BigInteger ? false
|
||||||
this.compressed = !!v;
|
: 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) {
|
||||||
* Return public key in DER encoding.
|
if (compressed === undefined) compressed = this.compressed
|
||||||
*/
|
return ECPubKey(ecparams.getG().multiply(this.priv),compressed)
|
||||||
ECKey.prototype.getPub = function () {
|
}
|
||||||
return this.getPubPoint().getEncoded(this.compressed);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return public point as ECPoint object.
|
* @deprecated Reserved keyword, factory pattern. Use toHex, toBytes, etc.
|
||||||
*/
|
*/
|
||||||
ECKey.prototype.getPubPoint = function () {
|
ECKey.prototype['export'] = function(format) {
|
||||||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
|
format || (format = 'hex')
|
||||||
|
return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]()
|
||||||
|
};
|
||||||
|
|
||||||
return this.pub;
|
ECKey.prototype.toBin = function() {
|
||||||
};
|
return conv.bytesToString(this.toBytes())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
ECKey.prototype.toBase58 = function() {
|
||||||
* Get the pubKeyHash for this key.
|
return base58.checkEncode(this.toBytes(), 128)
|
||||||
*
|
}
|
||||||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as
|
|
||||||
* a byte array.
|
|
||||||
*/
|
|
||||||
ECKey.prototype.getPubKeyHash = function () {
|
|
||||||
if (this.pubKeyHash) return this.pubKeyHash;
|
|
||||||
|
|
||||||
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub());
|
ECKey.prototype.toWif = ECKey.prototype.toBase58
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.getBitcoinAddress = function () {
|
ECKey.prototype.toHex = function() {
|
||||||
var hash = this.getPubKeyHash();
|
return conv.bytesToHex(this.toBytes())
|
||||||
var addr = new Bitcoin.Address(hash);
|
}
|
||||||
return addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.getExportedPrivateKey = function () {
|
ECKey.prototype.toBytes = function() {
|
||||||
var hash = this.priv.toByteArrayUnsigned();
|
var bytes = this.priv.toByteArrayUnsigned();
|
||||||
while (hash.length < 32) hash.unshift(0);
|
if (this.compressed) bytes.push(1)
|
||||||
hash.unshift(0x80);
|
return bytes
|
||||||
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) {
|
ECKey.prototype.toString = ECKey.prototype.toBase58
|
||||||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
|
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.toString = function (format) {
|
ECKey.prototype.getBitcoinAddress = function(v) {
|
||||||
if (format === "base64") {
|
return this.getPub().getBitcoinAddress(v)
|
||||||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
|
}
|
||||||
} else {
|
|
||||||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
|
ECKey.prototype.add = function(key) {
|
||||||
|
return ECKey(this.priv.add(ECKey(key).priv),this.compressed)
|
||||||
|
}
|
||||||
|
|
||||||
|
ECKey.prototype.multiply = function(key) {
|
||||||
|
return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ECPubKey = function(input,compressed) {
|
||||||
|
if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed); }
|
||||||
|
if (!input) {
|
||||||
|
// Generate new key
|
||||||
|
var n = ecparams.getN();
|
||||||
|
this.pub = ecparams.getG().multiply(ecdsa.getBigRandom(n))
|
||||||
|
this.compressed = compressed || false;
|
||||||
}
|
}
|
||||||
};
|
else this.import(input,compressed)
|
||||||
|
}
|
||||||
|
|
||||||
ECKey.prototype.sign = function (hash) {
|
ECPubKey.prototype.import = function(input,compressed) {
|
||||||
return ECDSA.sign(hash, this.priv);
|
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()))
|
||||||
|
|
||||||
ECKey.prototype.verify = function (hash, sig) {
|
this.compressed =
|
||||||
return ECDSA.verify(hash, sig, this.getPub());
|
arguments.length > 1 ? compressed
|
||||||
};
|
: input instanceof ECPointFp ? input.compressed
|
||||||
|
: input instanceof ECPubKey ? input.compressed
|
||||||
|
: (this.pub[0] < 4)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
ECPubKey.prototype.add = function(key) {
|
||||||
* Parse an exported private key contained in a string.
|
return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed)
|
||||||
*/
|
}
|
||||||
ECKey.decodeString = function (string) {
|
|
||||||
var bytes = Bitcoin.Base58.decode(string);
|
|
||||||
|
|
||||||
var hash = bytes.slice(0, 33);
|
ECPubKey.prototype.multiply = function(key) {
|
||||||
|
return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed)
|
||||||
|
}
|
||||||
|
|
||||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
|
ECPubKey.prototype['export'] = function(format) {
|
||||||
|
format || (format = 'hex')
|
||||||
|
return this['to' + format.substr(0, 1).toUpperCase() + format.substr(1)]()
|
||||||
|
}
|
||||||
|
|
||||||
if (checksum[0] != bytes[33] ||
|
ECPubKey.prototype.toBytes = function(compressed) {
|
||||||
checksum[1] != bytes[34] ||
|
if (compressed === undefined) compressed = this.compressed
|
||||||
checksum[2] != bytes[35] ||
|
return this.pub.getEncoded(compressed)
|
||||||
checksum[3] != bytes[36]) {
|
}
|
||||||
throw "Checksum validation failed!";
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = hash.shift();
|
ECPubKey.prototype.toHex = function() {
|
||||||
|
return conv.bytesToHex(this.toBytes())
|
||||||
|
}
|
||||||
|
|
||||||
if (version != 0x80) {
|
ECPubKey.prototype.toBin = function() {
|
||||||
throw "Version "+version+" not supported!";
|
return conv.bytesToString(this.toBytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
ECPubKey.prototype.toBase58 = function() {
|
||||||
};
|
return base58.checkEncode(this.toBytes(), 128)
|
||||||
|
}
|
||||||
|
|
||||||
return ECKey;
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { ECKey: ECKey, ECPubKey: ECPubKey };
|
||||||
|
|
235
src/hdwallet.js
Normal file
235
src/hdwallet.js
Normal file
|
@ -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
Normal file
54
src/index.js
Normal file
|
@ -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
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
912
src/jsbn/jsbn.js
File diff suppressed because it is too large
Load diff
|
@ -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)
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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
Executable file → Normal file
5
src/jsbn/sec.js
Executable file → Normal file
|
@ -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;
|
||||||
|
|
|
@ -1,69 +1,68 @@
|
||||||
/**
|
/// 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);
|
|
||||||
|
|
||||||
var buffer = [];
|
Message.magicPrefix = "Bitcoin Signed Message:\n";
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length));
|
|
||||||
buffer = buffer.concat(magicBytes);
|
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length));
|
|
||||||
buffer = buffer.concat(messageBytes);
|
|
||||||
|
|
||||||
return buffer;
|
Message.makeMagicMessage = function (message) {
|
||||||
};
|
var magicBytes = conv.stringToBytes(Message.magicPrefix);
|
||||||
|
var messageBytes = conv.stringToBytes(message);
|
||||||
|
|
||||||
Message.getHash = function (message) {
|
var buffer = [];
|
||||||
var buffer = Message.makeMagicMessage(message);
|
buffer = buffer.concat(util.numToVarInt(magicBytes.length));
|
||||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
buffer = buffer.concat(magicBytes);
|
||||||
};
|
buffer = buffer.concat(util.numToVarInt(messageBytes.length));
|
||||||
|
buffer = buffer.concat(messageBytes);
|
||||||
|
|
||||||
Message.signMessage = function (key, message, compressed) {
|
return buffer;
|
||||||
var hash = Message.getHash(message);
|
};
|
||||||
|
|
||||||
var sig = key.sign(hash);
|
Message.getHash = function (message) {
|
||||||
|
var buffer = Message.makeMagicMessage(message);
|
||||||
|
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
||||||
|
};
|
||||||
|
|
||||||
var obj = Bitcoin.ECDSA.parseSig(sig);
|
Message.signMessage = function (key, message, compressed) {
|
||||||
|
var hash = Message.getHash(message);
|
||||||
|
|
||||||
var address = key.getBitcoinAddress().toString();
|
var sig = key.sign(hash);
|
||||||
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash);
|
|
||||||
|
|
||||||
i += 27;
|
var obj = ecdsa.parseSig(sig);
|
||||||
if (compressed) i += 4;
|
|
||||||
|
|
||||||
var rBa = obj.r.toByteArrayUnsigned();
|
var i = ecdsa.calcPubkeyRecoveryParam(key, obj.r, obj.s, hash);
|
||||||
var sBa = obj.s.toByteArrayUnsigned();
|
|
||||||
|
|
||||||
// Pad to 32 bytes per value
|
i += 27;
|
||||||
while (rBa.length < 32) rBa.unshift(0);
|
if (compressed) i += 4;
|
||||||
while (sBa.length < 32) sBa.unshift(0);
|
|
||||||
|
|
||||||
sig = [i].concat(rBa).concat(sBa);
|
var rBa = obj.r.toByteArrayUnsigned();
|
||||||
|
var sBa = obj.s.toByteArrayUnsigned();
|
||||||
|
|
||||||
return Crypto.util.bytesToBase64(sig);
|
// Pad to 32 bytes per value
|
||||||
};
|
while (rBa.length < 32) rBa.unshift(0);
|
||||||
|
while (sBa.length < 32) sBa.unshift(0);
|
||||||
|
|
||||||
Message.verifyMessage = function (address, sig, message) {
|
sig = [i].concat(rBa).concat(sBa);
|
||||||
sig = Crypto.util.base64ToBytes(sig);
|
|
||||||
sig = Bitcoin.ECDSA.parseSigCompact(sig);
|
|
||||||
|
|
||||||
var hash = Message.getHash(message);
|
return conv.bytesToHex(sig);
|
||||||
|
};
|
||||||
|
|
||||||
var isCompressed = !!(sig.i & 4);
|
Message.verifyMessage = function (address, sig, message) {
|
||||||
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i);
|
sig = conv.hexToBytes(sig);
|
||||||
|
sig = ecdsa.parseSigCompact(sig);
|
||||||
|
|
||||||
pubKey.setCompressed(isCompressed);
|
var hash = Message.getHash(message);
|
||||||
|
|
||||||
var expectedAddress = pubKey.getBitcoinAddress().toString();
|
var isCompressed = !!(sig.i & 4);
|
||||||
|
var pubKey = ecdsa.recoverPubKey(sig.r, sig.s, hash, sig.i);
|
||||||
|
|
||||||
return (address === expectedAddress);
|
var expectedAddress = pubKey.getBitcoinAddress().toString();
|
||||||
};
|
|
||||||
|
|
||||||
return Message;
|
return (address === expectedAddress);
|
||||||
})();
|
};
|
||||||
|
|
||||||
|
module.exports = Message;
|
||||||
|
|
278
src/opcode.js
278
src/opcode.js
|
@ -1,154 +1,154 @@
|
||||||
(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,
|
||||||
OP_PUSHDATA1 : 76,
|
OP_PUSHDATA1 : 76,
|
||||||
OP_PUSHDATA2 : 77,
|
OP_PUSHDATA2 : 77,
|
||||||
OP_PUSHDATA4 : 78,
|
OP_PUSHDATA4 : 78,
|
||||||
OP_1NEGATE : 79,
|
OP_1NEGATE : 79,
|
||||||
OP_RESERVED : 80,
|
OP_RESERVED : 80,
|
||||||
OP_1 : 81,
|
OP_1 : 81,
|
||||||
OP_TRUE : 81,
|
OP_TRUE : 81,
|
||||||
OP_2 : 82,
|
OP_2 : 82,
|
||||||
OP_3 : 83,
|
OP_3 : 83,
|
||||||
OP_4 : 84,
|
OP_4 : 84,
|
||||||
OP_5 : 85,
|
OP_5 : 85,
|
||||||
OP_6 : 86,
|
OP_6 : 86,
|
||||||
OP_7 : 87,
|
OP_7 : 87,
|
||||||
OP_8 : 88,
|
OP_8 : 88,
|
||||||
OP_9 : 89,
|
OP_9 : 89,
|
||||||
OP_10 : 90,
|
OP_10 : 90,
|
||||||
OP_11 : 91,
|
OP_11 : 91,
|
||||||
OP_12 : 92,
|
OP_12 : 92,
|
||||||
OP_13 : 93,
|
OP_13 : 93,
|
||||||
OP_14 : 94,
|
OP_14 : 94,
|
||||||
OP_15 : 95,
|
OP_15 : 95,
|
||||||
OP_16 : 96,
|
OP_16 : 96,
|
||||||
|
|
||||||
// control
|
// control
|
||||||
OP_NOP : 97,
|
OP_NOP : 97,
|
||||||
OP_VER : 98,
|
OP_VER : 98,
|
||||||
OP_IF : 99,
|
OP_IF : 99,
|
||||||
OP_NOTIF : 100,
|
OP_NOTIF : 100,
|
||||||
OP_VERIF : 101,
|
OP_VERIF : 101,
|
||||||
OP_VERNOTIF : 102,
|
OP_VERNOTIF : 102,
|
||||||
OP_ELSE : 103,
|
OP_ELSE : 103,
|
||||||
OP_ENDIF : 104,
|
OP_ENDIF : 104,
|
||||||
OP_VERIFY : 105,
|
OP_VERIFY : 105,
|
||||||
OP_RETURN : 106,
|
OP_RETURN : 106,
|
||||||
|
|
||||||
// stack ops
|
// stack ops
|
||||||
OP_TOALTSTACK : 107,
|
OP_TOALTSTACK : 107,
|
||||||
OP_FROMALTSTACK : 108,
|
OP_FROMALTSTACK : 108,
|
||||||
OP_2DROP : 109,
|
OP_2DROP : 109,
|
||||||
OP_2DUP : 110,
|
OP_2DUP : 110,
|
||||||
OP_3DUP : 111,
|
OP_3DUP : 111,
|
||||||
OP_2OVER : 112,
|
OP_2OVER : 112,
|
||||||
OP_2ROT : 113,
|
OP_2ROT : 113,
|
||||||
OP_2SWAP : 114,
|
OP_2SWAP : 114,
|
||||||
OP_IFDUP : 115,
|
OP_IFDUP : 115,
|
||||||
OP_DEPTH : 116,
|
OP_DEPTH : 116,
|
||||||
OP_DROP : 117,
|
OP_DROP : 117,
|
||||||
OP_DUP : 118,
|
OP_DUP : 118,
|
||||||
OP_NIP : 119,
|
OP_NIP : 119,
|
||||||
OP_OVER : 120,
|
OP_OVER : 120,
|
||||||
OP_PICK : 121,
|
OP_PICK : 121,
|
||||||
OP_ROLL : 122,
|
OP_ROLL : 122,
|
||||||
OP_ROT : 123,
|
OP_ROT : 123,
|
||||||
OP_SWAP : 124,
|
OP_SWAP : 124,
|
||||||
OP_TUCK : 125,
|
OP_TUCK : 125,
|
||||||
|
|
||||||
// splice ops
|
// splice ops
|
||||||
OP_CAT : 126,
|
OP_CAT : 126,
|
||||||
OP_SUBSTR : 127,
|
OP_SUBSTR : 127,
|
||||||
OP_LEFT : 128,
|
OP_LEFT : 128,
|
||||||
OP_RIGHT : 129,
|
OP_RIGHT : 129,
|
||||||
OP_SIZE : 130,
|
OP_SIZE : 130,
|
||||||
|
|
||||||
// bit logic
|
// bit logic
|
||||||
OP_INVERT : 131,
|
OP_INVERT : 131,
|
||||||
OP_AND : 132,
|
OP_AND : 132,
|
||||||
OP_OR : 133,
|
OP_OR : 133,
|
||||||
OP_XOR : 134,
|
OP_XOR : 134,
|
||||||
OP_EQUAL : 135,
|
OP_EQUAL : 135,
|
||||||
OP_EQUALVERIFY : 136,
|
OP_EQUALVERIFY : 136,
|
||||||
OP_RESERVED1 : 137,
|
OP_RESERVED1 : 137,
|
||||||
OP_RESERVED2 : 138,
|
OP_RESERVED2 : 138,
|
||||||
|
|
||||||
// numeric
|
// numeric
|
||||||
OP_1ADD : 139,
|
OP_1ADD : 139,
|
||||||
OP_1SUB : 140,
|
OP_1SUB : 140,
|
||||||
OP_2MUL : 141,
|
OP_2MUL : 141,
|
||||||
OP_2DIV : 142,
|
OP_2DIV : 142,
|
||||||
OP_NEGATE : 143,
|
OP_NEGATE : 143,
|
||||||
OP_ABS : 144,
|
OP_ABS : 144,
|
||||||
OP_NOT : 145,
|
OP_NOT : 145,
|
||||||
OP_0NOTEQUAL : 146,
|
OP_0NOTEQUAL : 146,
|
||||||
|
|
||||||
OP_ADD : 147,
|
OP_ADD : 147,
|
||||||
OP_SUB : 148,
|
OP_SUB : 148,
|
||||||
OP_MUL : 149,
|
OP_MUL : 149,
|
||||||
OP_DIV : 150,
|
OP_DIV : 150,
|
||||||
OP_MOD : 151,
|
OP_MOD : 151,
|
||||||
OP_LSHIFT : 152,
|
OP_LSHIFT : 152,
|
||||||
OP_RSHIFT : 153,
|
OP_RSHIFT : 153,
|
||||||
|
|
||||||
OP_BOOLAND : 154,
|
OP_BOOLAND : 154,
|
||||||
OP_BOOLOR : 155,
|
OP_BOOLOR : 155,
|
||||||
OP_NUMEQUAL : 156,
|
OP_NUMEQUAL : 156,
|
||||||
OP_NUMEQUALVERIFY : 157,
|
OP_NUMEQUALVERIFY : 157,
|
||||||
OP_NUMNOTEQUAL : 158,
|
OP_NUMNOTEQUAL : 158,
|
||||||
OP_LESSTHAN : 159,
|
OP_LESSTHAN : 159,
|
||||||
OP_GREATERTHAN : 160,
|
OP_GREATERTHAN : 160,
|
||||||
OP_LESSTHANOREQUAL : 161,
|
OP_LESSTHANOREQUAL : 161,
|
||||||
OP_GREATERTHANOREQUAL : 162,
|
OP_GREATERTHANOREQUAL : 162,
|
||||||
OP_MIN : 163,
|
OP_MIN : 163,
|
||||||
OP_MAX : 164,
|
OP_MAX : 164,
|
||||||
|
|
||||||
OP_WITHIN : 165,
|
OP_WITHIN : 165,
|
||||||
|
|
||||||
// crypto
|
// crypto
|
||||||
OP_RIPEMD160 : 166,
|
OP_RIPEMD160 : 166,
|
||||||
OP_SHA1 : 167,
|
OP_SHA1 : 167,
|
||||||
OP_SHA256 : 168,
|
OP_SHA256 : 168,
|
||||||
OP_HASH160 : 169,
|
OP_HASH160 : 169,
|
||||||
OP_HASH256 : 170,
|
OP_HASH256 : 170,
|
||||||
OP_CODESEPARATOR : 171,
|
OP_CODESEPARATOR : 171,
|
||||||
OP_CHECKSIG : 172,
|
OP_CHECKSIG : 172,
|
||||||
OP_CHECKSIGVERIFY : 173,
|
OP_CHECKSIGVERIFY : 173,
|
||||||
OP_CHECKMULTISIG : 174,
|
OP_CHECKMULTISIG : 174,
|
||||||
OP_CHECKMULTISIGVERIFY : 175,
|
OP_CHECKMULTISIGVERIFY : 175,
|
||||||
|
|
||||||
// expansion
|
// expansion
|
||||||
OP_NOP1 : 176,
|
OP_NOP1 : 176,
|
||||||
OP_NOP2 : 177,
|
OP_NOP2 : 177,
|
||||||
OP_NOP3 : 178,
|
OP_NOP3 : 178,
|
||||||
OP_NOP4 : 179,
|
OP_NOP4 : 179,
|
||||||
OP_NOP5 : 180,
|
OP_NOP5 : 180,
|
||||||
OP_NOP6 : 181,
|
OP_NOP6 : 181,
|
||||||
OP_NOP7 : 182,
|
OP_NOP7 : 182,
|
||||||
OP_NOP8 : 183,
|
OP_NOP8 : 183,
|
||||||
OP_NOP9 : 184,
|
OP_NOP9 : 184,
|
||||||
OP_NOP10 : 185,
|
OP_NOP10 : 185,
|
||||||
|
|
||||||
// template matching params
|
// template matching params
|
||||||
OP_PUBKEYHASH : 253,
|
OP_PUBKEYHASH : 253,
|
||||||
OP_PUBKEY : 254,
|
OP_PUBKEY : 254,
|
||||||
OP_INVALIDOPCODE : 255
|
OP_INVALIDOPCODE : 255
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|
675
src/script.js
675
src/script.js
|
@ -1,347 +1,382 @@
|
||||||
(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) {
|
if (!data) {
|
||||||
eval("var " + i + " = " + Opcode.map[i] + ";");
|
this.buffer = [];
|
||||||
|
} else if ("string" == typeof data) {
|
||||||
|
this.buffer = conv.hexToBytes(data);
|
||||||
|
} else if (util.isArray(data)) {
|
||||||
|
this.buffer = data;
|
||||||
|
} else if (data instanceof Script) {
|
||||||
|
this.buffer = data.buffer;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid script");
|
||||||
}
|
}
|
||||||
|
|
||||||
var Script = Bitcoin.Script = function (data) {
|
this.parse();
|
||||||
if (!data) {
|
};
|
||||||
this.buffer = [];
|
|
||||||
} else if ("string" == typeof data) {
|
Script.fromPubKey = function(str) {
|
||||||
this.buffer = Crypto.util.base64ToBytes(data);
|
var script = new Script();
|
||||||
} else if (Bitcoin.Util.isArray(data)) {
|
var s = str.split(" ");
|
||||||
this.buffer = data;
|
for (var i in s) {
|
||||||
} else if (data instanceof Script) {
|
if (Opcode.map.hasOwnProperty(s[i])){
|
||||||
this.buffer = data.buffer;
|
script.writeOp(Opcode.map[s[i]]);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid script");
|
script.writeBytes(conv.hexToBytes(s[i]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return script;
|
||||||
|
};
|
||||||
|
|
||||||
this.parse();
|
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.
|
||||||
|
*
|
||||||
|
* Each Script object stores the script in two formats. First as a raw byte
|
||||||
|
* array and second as an array of "chunks", such as opcodes and pieces of
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* This method updates the chunks cache. Normally this is called by the
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
Script.prototype.parse = function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.chunks = [];
|
||||||
|
|
||||||
|
// Cursor
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
// Read n bytes and store result as a chunk
|
||||||
|
function readChunk(n) {
|
||||||
|
self.chunks.push(self.buffer.slice(i, i + n));
|
||||||
|
i += n;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
while (i < this.buffer.length) {
|
||||||
* Update the parsed script representation.
|
var opcode = this.buffer[i++];
|
||||||
*
|
if (opcode >= 0xF0) {
|
||||||
* Each Script object stores the script in two formats. First as a raw byte
|
// Two byte opcode
|
||||||
* array and second as an array of "chunks", such as opcodes and pieces of
|
opcode = (opcode << 8) | this.buffer[i++];
|
||||||
* data.
|
|
||||||
*
|
|
||||||
* This method updates the chunks cache. Normally this is called by the
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
Script.prototype.parse = function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.chunks = [];
|
|
||||||
|
|
||||||
// Cursor
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
// Read n bytes and store result as a chunk
|
|
||||||
function readChunk(n) {
|
|
||||||
self.chunks.push(self.buffer.slice(i, i + n));
|
|
||||||
i += n;
|
|
||||||
};
|
|
||||||
|
|
||||||
while (i < this.buffer.length) {
|
|
||||||
var opcode = this.buffer[i++];
|
|
||||||
if (opcode >= 0xF0) {
|
|
||||||
// Two byte opcode
|
|
||||||
opcode = (opcode << 8) | this.buffer[i++];
|
|
||||||
}
|
|
||||||
|
|
||||||
var len;
|
|
||||||
if (opcode > 0 && opcode < OP_PUSHDATA1) {
|
|
||||||
// Read some bytes of data, opcode value is the length of data
|
|
||||||
readChunk(opcode);
|
|
||||||
} else if (opcode == OP_PUSHDATA1) {
|
|
||||||
len = this.buffer[i++];
|
|
||||||
readChunk(len);
|
|
||||||
} else if (opcode == OP_PUSHDATA2) {
|
|
||||||
len = (this.buffer[i++] << 8) | this.buffer[i++];
|
|
||||||
readChunk(len);
|
|
||||||
} else if (opcode == OP_PUSHDATA4) {
|
|
||||||
len = (this.buffer[i++] << 24) |
|
|
||||||
(this.buffer[i++] << 16) |
|
|
||||||
(this.buffer[i++] << 8) |
|
|
||||||
this.buffer[i++];
|
|
||||||
readChunk(len);
|
|
||||||
} else {
|
|
||||||
this.chunks.push(opcode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
var len;
|
||||||
* Compare the script to known templates of scriptPubKey.
|
if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) {
|
||||||
*
|
// Read some bytes of data, opcode value is the length of data
|
||||||
* This method will compare the script to a small number of standard script
|
readChunk(opcode);
|
||||||
* templates and return a string naming the detected type.
|
} else if (opcode == Opcode.map.OP_PUSHDATA1) {
|
||||||
*
|
len = this.buffer[i++];
|
||||||
* Currently supported are:
|
readChunk(len);
|
||||||
* Address:
|
} else if (opcode == Opcode.map.OP_PUSHDATA2) {
|
||||||
* Paying to a Bitcoin address which is the hash of a pubkey.
|
len = (this.buffer[i++] << 8) | this.buffer[i++];
|
||||||
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
|
readChunk(len);
|
||||||
*
|
} else if (opcode == Opcode.map.OP_PUSHDATA4) {
|
||||||
* Pubkey:
|
len = (this.buffer[i++] << 24) |
|
||||||
* Paying to a public key directly.
|
(this.buffer[i++] << 16) |
|
||||||
* [pubKey] OP_CHECKSIG
|
(this.buffer[i++] << 8) |
|
||||||
*
|
this.buffer[i++];
|
||||||
* Strange:
|
readChunk(len);
|
||||||
* Any other script (no template matched).
|
} else {
|
||||||
*/
|
this.chunks.push(opcode);
|
||||||
Script.prototype.getOutType = function () {
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) {
|
/**
|
||||||
// Transfer to M-OF-N
|
* Compare the script to known templates of scriptPubKey.
|
||||||
return 'Multisig';
|
*
|
||||||
} else if (this.chunks.length == 5 &&
|
* This method will compare the script to a small number of standard script
|
||||||
this.chunks[0] == OP_DUP &&
|
* templates and return a string naming the detected type.
|
||||||
this.chunks[1] == OP_HASH160 &&
|
*
|
||||||
this.chunks[3] == OP_EQUALVERIFY &&
|
* Currently supported are:
|
||||||
this.chunks[4] == OP_CHECKSIG) {
|
* Address:
|
||||||
// Transfer to Bitcoin address
|
* Paying to a Bitcoin address which is the hash of a pubkey.
|
||||||
return 'Address';
|
* OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
|
||||||
} else if (this.chunks.length == 2 &&
|
*
|
||||||
this.chunks[1] == OP_CHECKSIG) {
|
* Pubkey:
|
||||||
// Transfer to IP address
|
* Paying to a public key directly.
|
||||||
|
* [pubKey] OP_CHECKSIG
|
||||||
|
*
|
||||||
|
* Strange:
|
||||||
|
* Any other script (no template matched).
|
||||||
|
*/
|
||||||
|
Script.prototype.getOutType = function () {
|
||||||
|
if (this.chunks[this.chunks.length-1] == Opcode.map.OP_EQUAL &&
|
||||||
|
this.chunks[0] == Opcode.map.OP_HASH160 &&
|
||||||
|
this.chunks.length == 3) {
|
||||||
|
// Transfer to M-OF-N
|
||||||
|
return 'P2SH';
|
||||||
|
}
|
||||||
|
else if (this.chunks.length == 5 &&
|
||||||
|
this.chunks[0] == Opcode.map.OP_DUP &&
|
||||||
|
this.chunks[1] == Opcode.map.OP_HASH160 &&
|
||||||
|
this.chunks[3] == Opcode.map.OP_EQUALVERIFY &&
|
||||||
|
this.chunks[4] == Opcode.map.OP_CHECKSIG) {
|
||||||
|
// Transfer to Bitcoin address
|
||||||
|
return 'Pubkey';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'Strange';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address corresponding to this output in hash160 form.
|
||||||
|
* Assumes strange scripts are P2SH
|
||||||
|
*/
|
||||||
|
Script.prototype.toScriptHash = function () {
|
||||||
|
var outType = this.getOutType();
|
||||||
|
|
||||||
|
return outType == 'Pubkey' ? this.chunks[2]
|
||||||
|
: outType == 'P2SH' ? util.sha256ripe160(this.buffer)
|
||||||
|
: util.sha256ripe160(this.buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* This method will compare the script to a small number of standard script
|
||||||
|
* templates and return a string naming the detected type.
|
||||||
|
*
|
||||||
|
* WARNING: Use this method with caution. It merely represents a heuristic
|
||||||
|
* based on common transaction formats. A non-standard transaction could
|
||||||
|
* very easily match one of these templates by accident.
|
||||||
|
*
|
||||||
|
* Currently supported are:
|
||||||
|
* Address:
|
||||||
|
* Paying to a Bitcoin address which is the hash of a pubkey.
|
||||||
|
* [sig] [pubKey]
|
||||||
|
*
|
||||||
|
* Pubkey:
|
||||||
|
* Paying to a public key directly.
|
||||||
|
* [sig]
|
||||||
|
*
|
||||||
|
* Multisig:
|
||||||
|
* Paying to M-of-N public keys.
|
||||||
|
*
|
||||||
|
* Strange:
|
||||||
|
* Any other script (no template matched).
|
||||||
|
*/
|
||||||
|
Script.prototype.getInType = function ()
|
||||||
|
{
|
||||||
|
if (this.chunks.length == 1 &&
|
||||||
|
util.isArray(this.chunks[0])) {
|
||||||
|
// Direct IP to IP transactions only have the signature in their scriptSig.
|
||||||
|
// TODO: We could also check that the length of the data is correct.
|
||||||
return 'Pubkey';
|
return 'Pubkey';
|
||||||
|
} else if (this.chunks.length == 2 &&
|
||||||
|
util.isArray(this.chunks[0]) &&
|
||||||
|
util.isArray(this.chunks[1])) {
|
||||||
|
return 'Address';
|
||||||
|
} else if (this.chunks[0] == Opcode.map.OP_0 &&
|
||||||
|
this.chunks.slice(1).reduce(function(t,chunk,i) {
|
||||||
|
return t && 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.
|
||||||
|
*
|
||||||
|
* This currently only works with payToPubKeyHash transactions. It will also
|
||||||
|
* work in the future for standard payToScriptHash transactions that use a
|
||||||
|
* single public key.
|
||||||
|
*
|
||||||
|
* However for multi-key and other complex transactions, this will only return
|
||||||
|
* one of the keys or raise an error. Therefore, it is recommended for indexing
|
||||||
|
* purposes to use Script#simpleInHash or Script#simpleOutHash instead.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
Script.prototype.simpleInPubKey = function ()
|
||||||
|
{
|
||||||
|
switch (this.getInType()) {
|
||||||
|
case 'Address':
|
||||||
|
return this.chunks[1];
|
||||||
|
case 'Pubkey':
|
||||||
|
// TODO: Theoretically, we could recover the pubkey from the sig here.
|
||||||
|
// See https://bitcointalk.org/?topic=6430.0
|
||||||
|
throw new Error("Script does not contain pubkey.");
|
||||||
|
default:
|
||||||
|
throw new Error("Encountered non-standard scriptSig");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the affected address hash for this input.
|
||||||
|
*
|
||||||
|
* For standard transactions, this will return the hash of the pubKey that
|
||||||
|
* can spend this output.
|
||||||
|
*
|
||||||
|
* In the future, for standard payToScriptHash inputs, this will return the
|
||||||
|
* scriptHash.
|
||||||
|
*
|
||||||
|
* Note: This function provided for convenience. If you have the corresponding
|
||||||
|
* scriptPubKey available, you are urged to use Script#simpleOutHash instead
|
||||||
|
* as it is more reliable for non-standard payToScriptHash transactions.
|
||||||
|
*
|
||||||
|
* This method is useful for indexing transactions.
|
||||||
|
*/
|
||||||
|
Script.prototype.simpleInHash = function ()
|
||||||
|
{
|
||||||
|
return util.sha256ripe160(this.simpleInPubKey());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old name for Script#simpleInHash.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an op code to the script.
|
||||||
|
*/
|
||||||
|
Script.prototype.writeOp = function (opcode)
|
||||||
|
{
|
||||||
|
this.buffer.push(opcode);
|
||||||
|
this.chunks.push(opcode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a data chunk to the script.
|
||||||
|
*/
|
||||||
|
Script.prototype.writeBytes = function (data)
|
||||||
|
{
|
||||||
|
if (data.length < Opcode.map.OP_PUSHDATA1) {
|
||||||
|
this.buffer.push(data.length);
|
||||||
|
} else if (data.length <= 0xff) {
|
||||||
|
this.buffer.push(Opcode.map.OP_PUSHDATA1);
|
||||||
|
this.buffer.push(data.length);
|
||||||
|
} else if (data.length <= 0xffff) {
|
||||||
|
this.buffer.push(Opcode.map.OP_PUSHDATA2);
|
||||||
|
this.buffer.push(data.length & 0xff);
|
||||||
|
this.buffer.push((data.length >>> 8) & 0xff);
|
||||||
|
} else {
|
||||||
|
this.buffer.push(Opcode.map.OP_PUSHDATA4);
|
||||||
|
this.buffer.push(data.length & 0xff);
|
||||||
|
this.buffer.push((data.length >>> 8) & 0xff);
|
||||||
|
this.buffer.push((data.length >>> 16) & 0xff);
|
||||||
|
this.buffer.push((data.length >>> 24) & 0xff);
|
||||||
|
}
|
||||||
|
this.buffer = this.buffer.concat(data);
|
||||||
|
this.chunks.push(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an output for an address
|
||||||
|
*/
|
||||||
|
Script.createOutputScript = function (address)
|
||||||
|
{
|
||||||
|
var script = new Script();
|
||||||
|
address = new Address(address);
|
||||||
|
// 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.writeOp(Opcode.map.OP_EQUAL);
|
||||||
|
}
|
||||||
|
return script;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract pubkeys from a multisig script
|
||||||
|
*/
|
||||||
|
|
||||||
|
Script.prototype.extractPubkeys = function() {
|
||||||
|
return this.chunks.filter(function(chunk) {
|
||||||
|
return (chunk[0] == 4 && chunk.length == 65
|
||||||
|
|| chunk[0] < 4 && chunk.length == 33)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the affected address hash for this output.
|
* Create an m-of-n output script
|
||||||
*
|
*/
|
||||||
* For standard transactions, this will return the hash of the pubKey that
|
Script.createMultiSigOutputScript = function (m, pubkeys)
|
||||||
* can spend this output.
|
{
|
||||||
*
|
var script = new Script();
|
||||||
* 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 ()
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
pubkeys = pubkeys.sort();
|
||||||
* Old name for Script#simpleOutHash.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash;
|
|
||||||
|
|
||||||
/**
|
script.writeOp(Opcode.map.OP_1 + m - 1);
|
||||||
* Compare the script to known templates of scriptSig.
|
|
||||||
*
|
|
||||||
* This method will compare the script to a small number of standard script
|
|
||||||
* templates and return a string naming the detected type.
|
|
||||||
*
|
|
||||||
* WARNING: Use this method with caution. It merely represents a heuristic
|
|
||||||
* based on common transaction formats. A non-standard transaction could
|
|
||||||
* very easily match one of these templates by accident.
|
|
||||||
*
|
|
||||||
* Currently supported are:
|
|
||||||
* Address:
|
|
||||||
* Paying to a Bitcoin address which is the hash of a pubkey.
|
|
||||||
* [sig] [pubKey]
|
|
||||||
*
|
|
||||||
* Pubkey:
|
|
||||||
* Paying to a public key directly.
|
|
||||||
* [sig]
|
|
||||||
*
|
|
||||||
* Strange:
|
|
||||||
* Any other script (no template matched).
|
|
||||||
*/
|
|
||||||
Script.prototype.getInType = function ()
|
|
||||||
{
|
|
||||||
if (this.chunks.length == 1 &&
|
|
||||||
Bitcoin.Util.isArray(this.chunks[0])) {
|
|
||||||
// Direct IP to IP transactions only have the signature in their scriptSig.
|
|
||||||
// TODO: We could also check that the length of the data is correct.
|
|
||||||
return 'Pubkey';
|
|
||||||
} else if (this.chunks.length == 2 &&
|
|
||||||
Bitcoin.Util.isArray(this.chunks[0]) &&
|
|
||||||
Bitcoin.Util.isArray(this.chunks[1])) {
|
|
||||||
return 'Address';
|
|
||||||
} else {
|
|
||||||
return 'Strange';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
for (var i = 0; i < pubkeys.length; ++i) {
|
||||||
* Returns the affected public key for this input.
|
script.writeBytes(pubkeys[i]);
|
||||||
*
|
}
|
||||||
* This currently only works with payToPubKeyHash transactions. It will also
|
|
||||||
* work in the future for standard payToScriptHash transactions that use a
|
|
||||||
* single public key.
|
|
||||||
*
|
|
||||||
* However for multi-key and other complex transactions, this will only return
|
|
||||||
* one of the keys or raise an error. Therefore, it is recommended for indexing
|
|
||||||
* purposes to use Script#simpleInHash or Script#simpleOutHash instead.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
Script.prototype.simpleInPubKey = function ()
|
|
||||||
{
|
|
||||||
switch (this.getInType()) {
|
|
||||||
case 'Address':
|
|
||||||
return this.chunks[1];
|
|
||||||
case 'Pubkey':
|
|
||||||
// TODO: Theoretically, we could recover the pubkey from the sig here.
|
|
||||||
// See https://bitcointalk.org/?topic=6430.0
|
|
||||||
throw new Error("Script does not contain pubkey.");
|
|
||||||
default:
|
|
||||||
throw new Error("Encountered non-standard scriptSig");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1);
|
||||||
* Returns the affected address hash for this input.
|
|
||||||
*
|
|
||||||
* For standard transactions, this will return the hash of the pubKey that
|
|
||||||
* can spend this output.
|
|
||||||
*
|
|
||||||
* In the future, for standard payToScriptHash inputs, this will return the
|
|
||||||
* scriptHash.
|
|
||||||
*
|
|
||||||
* Note: This function provided for convenience. If you have the corresponding
|
|
||||||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead
|
|
||||||
* as it is more reliable for non-standard payToScriptHash transactions.
|
|
||||||
*
|
|
||||||
* This method is useful for indexing transactions.
|
|
||||||
*/
|
|
||||||
Script.prototype.simpleInHash = function ()
|
|
||||||
{
|
|
||||||
return Bitcoin.Util.sha256ripe160(this.simpleInPubKey());
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
script.writeOp(Opcode.map.OP_CHECKMULTISIG);
|
||||||
* Old name for Script#simpleInHash.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash;
|
|
||||||
|
|
||||||
/**
|
return script;
|
||||||
* Add an op code to the script.
|
};
|
||||||
*/
|
|
||||||
Script.prototype.writeOp = function (opcode)
|
|
||||||
{
|
|
||||||
this.buffer.push(opcode);
|
|
||||||
this.chunks.push(opcode);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a data chunk to the script.
|
* Create a standard payToPubKeyHash input.
|
||||||
*/
|
*/
|
||||||
Script.prototype.writeBytes = function (data)
|
Script.createInputScript = function (signature, pubKey)
|
||||||
{
|
{
|
||||||
if (data.length < OP_PUSHDATA1) {
|
var script = new Script();
|
||||||
this.buffer.push(data.length);
|
script.writeBytes(signature);
|
||||||
} else if (data.length <= 0xff) {
|
script.writeBytes(pubKey);
|
||||||
this.buffer.push(OP_PUSHDATA1);
|
return script;
|
||||||
this.buffer.push(data.length);
|
};
|
||||||
} else if (data.length <= 0xffff) {
|
|
||||||
this.buffer.push(OP_PUSHDATA2);
|
|
||||||
this.buffer.push(data.length & 0xff);
|
|
||||||
this.buffer.push((data.length >>> 8) & 0xff);
|
|
||||||
} else {
|
|
||||||
this.buffer.push(OP_PUSHDATA4);
|
|
||||||
this.buffer.push(data.length & 0xff);
|
|
||||||
this.buffer.push((data.length >>> 8) & 0xff);
|
|
||||||
this.buffer.push((data.length >>> 16) & 0xff);
|
|
||||||
this.buffer.push((data.length >>> 24) & 0xff);
|
|
||||||
}
|
|
||||||
this.buffer = this.buffer.concat(data);
|
|
||||||
this.chunks.push(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a standard payToPubKeyHash output.
|
* Create a multisig input
|
||||||
*/
|
*/
|
||||||
Script.createOutputScript = function (address)
|
Script.createMultiSigInputScript = function(signatures, script)
|
||||||
{
|
{
|
||||||
var script = new Script();
|
script = new Script(script);
|
||||||
script.writeOp(OP_DUP);
|
var k = script.chunks[0][0];
|
||||||
script.writeOp(OP_HASH160);
|
if (signatures.length < k) return false; //Not enough sigs
|
||||||
script.writeBytes(address.hash);
|
var inScript = new Script();
|
||||||
script.writeOp(OP_EQUALVERIFY);
|
inScript.writeOp(Opcode.map.OP_0);
|
||||||
script.writeOp(OP_CHECKSIG);
|
signatures.map(function(sig) { inScript.writeBytes(sig) });
|
||||||
return script;
|
inScript.writeBytes(script.buffer);
|
||||||
};
|
return inScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.prototype.clone = function ()
|
||||||
|
{
|
||||||
|
return new Script(this.buffer);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
module.exports = Script;
|
||||||
* Extract bitcoin addresses from an output 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");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an m-of-n output script
|
|
||||||
*/
|
|
||||||
Script.createMultiSigOutputScript = function (m, pubkeys)
|
|
||||||
{
|
|
||||||
var script = new Bitcoin.Script();
|
|
||||||
|
|
||||||
script.writeOp(OP_1 + m - 1);
|
|
||||||
|
|
||||||
for (var i = 0; i < pubkeys.length; ++i) {
|
|
||||||
script.writeBytes(pubkeys[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
script.writeOp(OP_1 + pubkeys.length - 1);
|
|
||||||
|
|
||||||
script.writeOp(OP_CHECKMULTISIG);
|
|
||||||
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a standard payToPubKeyHash input.
|
|
||||||
*/
|
|
||||||
Script.createInputScript = function (signature, pubKey)
|
|
||||||
{
|
|
||||||
var script = new Script();
|
|
||||||
script.writeBytes(signature);
|
|
||||||
script.writeBytes(pubKey);
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
Script.prototype.clone = function ()
|
|
||||||
{
|
|
||||||
return new Script(this.buffer);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
(function () {
|
var BigInteger = require('./jsbn/jsbn');
|
||||||
var Script = Bitcoin.Script;
|
var Script = require('./script');
|
||||||
|
var util = require('./util');
|
||||||
|
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 = Bitcoin.Transaction = function (doc) {
|
var Transaction = function (doc) {
|
||||||
this.version = 1;
|
this.version = 1;
|
||||||
this.lock_time = 0;
|
this.lock_time = 0;
|
||||||
this.ins = [];
|
this.ins = [];
|
||||||
|
@ -10,436 +17,571 @@
|
||||||
this.block = null;
|
this.block = null;
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
if (doc.hash) this.hash = doc.hash;
|
if (typeof doc == "string" || util.isArray(doc)) {
|
||||||
if (doc.version) this.version = doc.version;
|
doc = Transaction.deserialize(doc)
|
||||||
if (doc.lock_time) this.lock_time = doc.lock_time;
|
|
||||||
if (doc.ins && doc.ins.length) {
|
|
||||||
for (var i = 0; i < doc.ins.length; i++) {
|
|
||||||
this.addInput(new TransactionIn(doc.ins[i]));
|
|
||||||
}
|
}
|
||||||
}
|
if (doc.hash) this.hash = doc.hash;
|
||||||
if (doc.outs && doc.outs.length) {
|
if (doc.version) this.version = doc.version;
|
||||||
for (var i = 0; i < doc.outs.length; i++) {
|
if (doc.lock_time) this.lock_time = doc.lock_time;
|
||||||
this.addOutput(new TransactionOut(doc.outs[i]));
|
if (doc.ins && doc.ins.length) {
|
||||||
|
for (var i = 0; i < doc.ins.length; i++) {
|
||||||
|
this.addInput(new TransactionIn(doc.ins[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (doc.outs && doc.outs.length) {
|
||||||
if (doc.timestamp) this.timestamp = doc.timestamp;
|
for (var i = 0; i < doc.outs.length; i++) {
|
||||||
if (doc.block) this.block = doc.block;
|
this.addOutput(new TransactionOut(doc.outs[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doc.timestamp) this.timestamp = doc.timestamp;
|
||||||
|
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
|
||||||
* Note that this method does not sign the created input.
|
* - A single string argument of the form txhash:index
|
||||||
*/
|
*
|
||||||
Transaction.prototype.addInput = function (tx, outIndex) {
|
* Note that this method does not sign the created input.
|
||||||
|
*/
|
||||||
|
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 {
|
|
||||||
this.ins.push(new TransactionIn({
|
|
||||||
outpoint: {
|
|
||||||
hash: tx.hash,
|
|
||||||
index: outIndex
|
|
||||||
},
|
|
||||||
script: new Bitcoin.Script(),
|
|
||||||
sequence: 4294967295
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
};
|
else if (arguments[0].length > 65) {
|
||||||
|
var args = arguments[0].split(':');
|
||||||
|
return this.addInput(args[0], args[1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.ins.push(new TransactionIn({
|
||||||
|
outpoint: {
|
||||||
|
hash: tx.hash || tx,
|
||||||
|
index: outIndex
|
||||||
|
},
|
||||||
|
script: new Script(),
|
||||||
|
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
|
||||||
var scriptBytes = txin.script.buffer;
|
// else use little-endian hashes? No idea...
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
buffer = buffer.concat(conv.hexToBytes(txin.outpoint.hash).reverse());
|
||||||
buffer = buffer.concat(scriptBytes);
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse());
|
buffer = buffer.concat(util.numToBytes(parseInt(txin.outpoint.index),4));
|
||||||
|
var scriptBytes = txin.script.buffer;
|
||||||
|
buffer = buffer.concat(util.numToVarInt(scriptBytes.length));
|
||||||
|
buffer = buffer.concat(scriptBytes);
|
||||||
|
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;
|
||||||
* Hash transaction for signing a specific input.
|
var SIGHASH_NONE = 2;
|
||||||
*
|
var SIGHASH_SINGLE = 3;
|
||||||
* Bitcoin uses a different hash for each signed transaction input. This
|
var SIGHASH_ANYONECANPAY = 80;
|
||||||
* method copies the transaction, makes the necessary changes based on the
|
|
||||||
* hashType, serializes and finally hashes the result. This hash can then be
|
|
||||||
* used to sign the transaction input in question.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.hashTransactionForSignature =
|
|
||||||
function (connectedScript, inIndex, hashType)
|
|
||||||
{
|
|
||||||
var txTmp = this.clone();
|
|
||||||
|
|
||||||
// In case concatenating two scripts ends up with two codeseparators,
|
/**
|
||||||
// or an extra one at the end, this prevents all those possible
|
* Hash transaction for signing a specific input.
|
||||||
// incompatibilities.
|
*
|
||||||
/*scriptCode = scriptCode.filter(function (val) {
|
* Bitcoin uses a different hash for each signed transaction input. This
|
||||||
return val !== OP_CODESEPARATOR;
|
* method copies the transaction, makes the necessary changes based on the
|
||||||
});*/
|
* hashType, serializes and finally hashes the result. This hash can then be
|
||||||
|
* used to sign the transaction input in question.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.hashTransactionForSignature =
|
||||||
|
function (connectedScript, inIndex, hashType)
|
||||||
|
{
|
||||||
|
var txTmp = this.clone();
|
||||||
|
|
||||||
// Blank out other inputs' signatures
|
// In case concatenating two scripts ends up with two codeseparators,
|
||||||
for (var i = 0; i < txTmp.ins.length; i++) {
|
// or an extra one at the end, this prevents all those possible
|
||||||
txTmp.ins[i].script = new Script();
|
// 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 = new Script();
|
||||||
|
}
|
||||||
|
|
||||||
|
txTmp.ins[inIndex].script = connectedScript;
|
||||||
|
|
||||||
|
// Blank out some of the outputs
|
||||||
|
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
||||||
|
txTmp.outs = [];
|
||||||
|
|
||||||
|
// Let the others update at will
|
||||||
|
for (var i = 0; i < txTmp.ins.length; i++)
|
||||||
|
if (i != inIndex)
|
||||||
|
txTmp.ins[i].sequence = 0;
|
||||||
|
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blank out other inputs completely, not recommended for open transactions
|
||||||
|
if (hashType & SIGHASH_ANYONECANPAY) {
|
||||||
|
txTmp.ins = [txTmp.ins[inIndex]];
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = txTmp.serialize();
|
||||||
|
|
||||||
|
buffer = buffer.concat(util.numToBytes(parseInt(hashType),4));
|
||||||
|
|
||||||
|
var hash1 = Crypto.SHA256(buffer, {asBytes: true});
|
||||||
|
|
||||||
|
return Crypto.SHA256(hash1, {asBytes: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ()
|
||||||
|
{
|
||||||
|
var buffer = this.serialize();
|
||||||
|
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}).reverse();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of this transaction object.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.clone = function ()
|
||||||
|
{
|
||||||
|
var newTx = new Transaction();
|
||||||
|
newTx.version = this.version;
|
||||||
|
newTx.lock_time = this.lock_time;
|
||||||
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
|
var txin = this.ins[i].clone();
|
||||||
|
newTx.addInput(txin);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < this.outs.length; i++) {
|
||||||
|
var txout = this.outs[i].clone();
|
||||||
|
newTx.addOutput(txout);
|
||||||
|
}
|
||||||
|
return newTx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyze how this transaction affects a wallet.
|
||||||
|
*
|
||||||
|
* Returns an object with properties 'impact', 'type' and 'addr'.
|
||||||
|
*
|
||||||
|
* 'impact' is an object, see Transaction#calcImpact.
|
||||||
|
*
|
||||||
|
* 'type' can be one of the following:
|
||||||
|
*
|
||||||
|
* recv:
|
||||||
|
* This is an incoming transaction, the wallet received money.
|
||||||
|
* 'addr' contains the first address in the wallet that receives money
|
||||||
|
* from this transaction.
|
||||||
|
*
|
||||||
|
* self:
|
||||||
|
* This is an internal transaction, money was sent within the wallet.
|
||||||
|
* 'addr' is undefined.
|
||||||
|
*
|
||||||
|
* sent:
|
||||||
|
* This is an outgoing transaction, money was sent out from the wallet.
|
||||||
|
* 'addr' contains the first external address, i.e. the recipient.
|
||||||
|
*
|
||||||
|
* other:
|
||||||
|
* This method was unable to detect what the transaction does. Either it
|
||||||
|
*/
|
||||||
|
Transaction.prototype.analyze = function (wallet) {
|
||||||
|
if (!(wallet instanceof Wallet)) return null;
|
||||||
|
|
||||||
|
var allFromMe = true,
|
||||||
|
allToMe = true,
|
||||||
|
firstRecvHash = null,
|
||||||
|
firstMeRecvHash = null,
|
||||||
|
firstSendHash = null;
|
||||||
|
|
||||||
|
for (var i = this.outs.length-1; i >= 0; i--) {
|
||||||
|
var txout = this.outs[i];
|
||||||
|
var hash = txout.script.simpleOutPubKeyHash();
|
||||||
|
if (!wallet.hasHash(hash)) {
|
||||||
|
allToMe = false;
|
||||||
|
} else {
|
||||||
|
firstMeRecvHash = hash;
|
||||||
}
|
}
|
||||||
|
firstRecvHash = hash;
|
||||||
txTmp.ins[inIndex].script = connectedScript;
|
}
|
||||||
|
for (var i = this.ins.length-1; i >= 0; i--) {
|
||||||
// Blank out some of the outputs
|
var txin = this.ins[i];
|
||||||
if ((hashType & 0x1f) == SIGHASH_NONE) {
|
firstSendHash = txin.script.simpleInPubKeyHash();
|
||||||
txTmp.outs = [];
|
if (!wallet.hasHash(firstSendHash)) {
|
||||||
|
allFromMe = false;
|
||||||
// Let the others update at will
|
|
||||||
for (var i = 0; i < txTmp.ins.length; i++)
|
|
||||||
if (i != inIndex)
|
|
||||||
txTmp.ins[i].sequence = 0;
|
|
||||||
} else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
|
|
||||||
// TODO: Implement
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blank out other inputs completely, not recommended for open transactions
|
|
||||||
if (hashType & SIGHASH_ANYONECANPAY) {
|
|
||||||
txTmp.ins = [txTmp.ins[inIndex]];
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = txTmp.serialize();
|
|
||||||
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse());
|
|
||||||
|
|
||||||
var hash1 = Crypto.SHA256(buffer, {asBytes: true});
|
|
||||||
|
|
||||||
return Crypto.SHA256(hash1, {asBytes: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate and return the transaction's hash.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.getHash = function ()
|
|
||||||
{
|
|
||||||
var buffer = this.serialize();
|
|
||||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a copy of this transaction object.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.clone = function ()
|
|
||||||
{
|
|
||||||
var newTx = new Transaction();
|
|
||||||
newTx.version = this.version;
|
|
||||||
newTx.lock_time = this.lock_time;
|
|
||||||
for (var i = 0; i < this.ins.length; i++) {
|
|
||||||
var txin = this.ins[i].clone();
|
|
||||||
newTx.addInput(txin);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < this.outs.length; i++) {
|
|
||||||
var txout = this.outs[i].clone();
|
|
||||||
newTx.addOutput(txout);
|
|
||||||
}
|
|
||||||
return newTx;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Analyze how this transaction affects a wallet.
|
|
||||||
*
|
|
||||||
* Returns an object with properties 'impact', 'type' and 'addr'.
|
|
||||||
*
|
|
||||||
* 'impact' is an object, see Transaction#calcImpact.
|
|
||||||
*
|
|
||||||
* 'type' can be one of the following:
|
|
||||||
*
|
|
||||||
* recv:
|
|
||||||
* This is an incoming transaction, the wallet received money.
|
|
||||||
* 'addr' contains the first address in the wallet that receives money
|
|
||||||
* from this transaction.
|
|
||||||
*
|
|
||||||
* self:
|
|
||||||
* This is an internal transaction, money was sent within the wallet.
|
|
||||||
* 'addr' is undefined.
|
|
||||||
*
|
|
||||||
* sent:
|
|
||||||
* This is an outgoing transaction, money was sent out from the wallet.
|
|
||||||
* 'addr' contains the first external address, i.e. the recipient.
|
|
||||||
*
|
|
||||||
* other:
|
|
||||||
* This method was unable to detect what the transaction does. Either it
|
|
||||||
*/
|
|
||||||
Transaction.prototype.analyze = function (wallet) {
|
|
||||||
if (!(wallet instanceof Bitcoin.Wallet)) return null;
|
|
||||||
|
|
||||||
var allFromMe = true,
|
|
||||||
allToMe = true,
|
|
||||||
firstRecvHash = null,
|
|
||||||
firstMeRecvHash = null,
|
|
||||||
firstSendHash = null;
|
|
||||||
|
|
||||||
for (var i = this.outs.length-1; i >= 0; i--) {
|
|
||||||
var txout = this.outs[i];
|
|
||||||
var hash = txout.script.simpleOutPubKeyHash();
|
|
||||||
if (!wallet.hasHash(hash)) {
|
|
||||||
allToMe = false;
|
|
||||||
} else {
|
|
||||||
firstMeRecvHash = hash;
|
|
||||||
}
|
|
||||||
firstRecvHash = hash;
|
|
||||||
}
|
|
||||||
for (var i = this.ins.length-1; i >= 0; i--) {
|
|
||||||
var txin = this.ins[i];
|
|
||||||
firstSendHash = txin.script.simpleInPubKeyHash();
|
|
||||||
if (!wallet.hasHash(firstSendHash)) {
|
|
||||||
allFromMe = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var impact = this.calcImpact(wallet);
|
|
||||||
|
|
||||||
var analysis = {};
|
|
||||||
|
|
||||||
analysis.impact = impact;
|
|
||||||
|
|
||||||
if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) {
|
|
||||||
analysis.type = 'recv';
|
|
||||||
analysis.addr = new Bitcoin.Address(firstMeRecvHash);
|
|
||||||
} else if (allFromMe && allToMe) {
|
|
||||||
analysis.type = 'self';
|
|
||||||
} else if (allFromMe) {
|
|
||||||
analysis.type = 'sent';
|
|
||||||
// TODO: Right now, firstRecvHash is the first output, which - if the
|
|
||||||
// transaction was not generated by this library could be the
|
|
||||||
// change address.
|
|
||||||
analysis.addr = new Bitcoin.Address(firstRecvHash);
|
|
||||||
} else {
|
|
||||||
analysis.type = "other";
|
|
||||||
}
|
|
||||||
|
|
||||||
return analysis;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a human-readable version of the data returned by Transaction#analyze.
|
|
||||||
*
|
|
||||||
* This is merely a convenience function. Clients should consider implementing
|
|
||||||
* this themselves based on their UI, I18N, etc.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.getDescription = function (wallet) {
|
|
||||||
var analysis = this.analyze(wallet);
|
|
||||||
|
|
||||||
if (!analysis) return "";
|
|
||||||
|
|
||||||
switch (analysis.type) {
|
|
||||||
case 'recv':
|
|
||||||
return "Received with "+analysis.addr;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sent':
|
|
||||||
return "Payment to "+analysis.addr;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'self':
|
|
||||||
return "Payment to yourself";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'other':
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
var impact = this.calcImpact(wallet);
|
||||||
* Get the total amount of a transaction's outputs.
|
|
||||||
*/
|
var analysis = {};
|
||||||
Transaction.prototype.getTotalOutValue = function () {
|
|
||||||
var totalValue = BigInteger.ZERO;
|
analysis.impact = impact;
|
||||||
for (var j = 0; j < this.outs.length; j++) {
|
|
||||||
var txout = this.outs[j];
|
if (impact.sign > 0 && impact.value > 0) {
|
||||||
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
|
analysis.type = 'recv';
|
||||||
|
analysis.addr = new Address(firstMeRecvHash);
|
||||||
|
} else if (allFromMe && allToMe) {
|
||||||
|
analysis.type = 'self';
|
||||||
|
} else if (allFromMe) {
|
||||||
|
analysis.type = 'sent';
|
||||||
|
// TODO: Right now, firstRecvHash is the first output, which - if the
|
||||||
|
// transaction was not generated by this library could be the
|
||||||
|
// change address.
|
||||||
|
analysis.addr = new Address(firstRecvHash);
|
||||||
|
} else {
|
||||||
|
analysis.type = "other";
|
||||||
|
}
|
||||||
|
|
||||||
|
return analysis;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a human-readable version of the data returned by Transaction#analyze.
|
||||||
|
*
|
||||||
|
* This is merely a convenience function. Clients should consider implementing
|
||||||
|
* this themselves based on their UI, I18N, etc.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.getDescription = function (wallet) {
|
||||||
|
var analysis = this.analyze(wallet);
|
||||||
|
|
||||||
|
if (!analysis) return "";
|
||||||
|
|
||||||
|
switch (analysis.type) {
|
||||||
|
case 'recv':
|
||||||
|
return "Received with "+analysis.addr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sent':
|
||||||
|
return "Payment to "+analysis.addr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'self':
|
||||||
|
return "Payment to yourself";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'other':
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total amount of a transaction's outputs.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.getTotalOutValue = function () {
|
||||||
|
return this.outs.reduce(function(t,o) { return t + o.value },0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old name for Transaction#getTotalOutValue.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the impact a transaction has on this wallet.
|
||||||
|
*
|
||||||
|
* Based on the its public keys, the wallet will calculate the
|
||||||
|
* credit or debit of this transaction.
|
||||||
|
*
|
||||||
|
* It will return an object with two properties:
|
||||||
|
* - sign: 1 or -1 depending on sign of the calculated impact.
|
||||||
|
* - value: amount of calculated impact
|
||||||
|
*
|
||||||
|
* @returns Object Impact on wallet
|
||||||
|
*/
|
||||||
|
Transaction.prototype.calcImpact = function (wallet) {
|
||||||
|
if (!(wallet instanceof Wallet)) return 0;
|
||||||
|
|
||||||
|
// Calculate credit to us from all outputs
|
||||||
|
var valueOut = this.outs.filter(function(o) {
|
||||||
|
return wallet.hasHash(conv.bytesToHex(o.script.simpleOutPubKeyHash()));
|
||||||
|
})
|
||||||
|
.reduce(function(t,o) { return t+o.value },0);
|
||||||
|
|
||||||
|
var valueIn = this.ins.filter(function(i) {
|
||||||
|
return wallet.hasHash(conv.bytesToHex(i.script.simpleInPubKeyHash()))
|
||||||
|
&& wallet.txIndex[i.outpoint.hash];
|
||||||
|
})
|
||||||
|
.reduce(function(t,i) {
|
||||||
|
return t + wallet.txIndex[i.outpoint.hash].outs[i.outpoint.index].value
|
||||||
|
},0);
|
||||||
|
|
||||||
|
if (valueOut > valueIn) {
|
||||||
|
return {
|
||||||
|
sign: 1,
|
||||||
|
value: valueOut - valueIn
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
sign: -1,
|
||||||
|
value: valueIn - valueOut
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a serialized transaction into a transaction object
|
||||||
|
*/
|
||||||
|
|
||||||
|
Transaction.deserialize = function(buffer) {
|
||||||
|
if (typeof buffer == "string") {
|
||||||
|
buffer = conv.hexToBytes(buffer)
|
||||||
}
|
}
|
||||||
return totalValue;
|
var pos = 0;
|
||||||
};
|
var readAsInt = function(bytes) {
|
||||||
|
if (bytes == 0) return 0;
|
||||||
/**
|
pos++;
|
||||||
* Old name for Transaction#getTotalOutValue.
|
return buffer[pos-1] + readAsInt(bytes-1) * 256;
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
Transaction.prototype.getTotalValue = Transaction.prototype.getTotalOutValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the impact a transaction has on this wallet.
|
|
||||||
*
|
|
||||||
* Based on the its public keys, the wallet will calculate the
|
|
||||||
* credit or debit of this transaction.
|
|
||||||
*
|
|
||||||
* It will return an object with two properties:
|
|
||||||
* - sign: 1 or -1 depending on sign of the calculated impact.
|
|
||||||
* - value: amount of calculated impact
|
|
||||||
*
|
|
||||||
* @returns Object Impact on wallet
|
|
||||||
*/
|
|
||||||
Transaction.prototype.calcImpact = function (wallet) {
|
|
||||||
if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO;
|
|
||||||
|
|
||||||
// Calculate credit to us from all outputs
|
|
||||||
var valueOut = BigInteger.ZERO;
|
|
||||||
for (var j = 0; j < this.outs.length; j++) {
|
|
||||||
var txout = this.outs[j];
|
|
||||||
var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash());
|
|
||||||
if (wallet.hasHash(hash)) {
|
|
||||||
valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var readVarInt = function() {
|
||||||
// Calculate debit to us from all ins
|
pos++;
|
||||||
var valueIn = BigInteger.ZERO;
|
if (buffer[pos-1] < 253) {
|
||||||
for (var j = 0; j < this.ins.length; j++) {
|
return buffer[pos-1];
|
||||||
var txin = this.ins[j];
|
|
||||||
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash());
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
return readAsInt(buffer[pos-1] - 251);
|
||||||
}
|
}
|
||||||
if (valueOut.compareTo(valueIn) >= 0) {
|
var readBytes = function(bytes) {
|
||||||
return {
|
pos += bytes;
|
||||||
sign: 1,
|
return buffer.slice(pos - bytes, pos);
|
||||||
value: valueOut.subtract(valueIn)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
sign: -1,
|
|
||||||
value: valueIn.subtract(valueOut)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
var TransactionIn = Bitcoin.TransactionIn = function (data)
|
/**
|
||||||
{
|
* Signs a standard output at some index with the given key
|
||||||
this.outpoint = data.outpoint;
|
*/
|
||||||
if (data.script instanceof Script) {
|
|
||||||
this.script = data.script;
|
|
||||||
} else {
|
|
||||||
this.script = new Script(data.script);
|
|
||||||
}
|
|
||||||
this.sequence = data.sequence;
|
|
||||||
};
|
|
||||||
|
|
||||||
TransactionIn.prototype.clone = function ()
|
Transaction.prototype.sign = function(index, key, type) {
|
||||||
{
|
type = type || SIGHASH_ALL;
|
||||||
var newTxin = new TransactionIn({
|
key = new ECKey(key);
|
||||||
outpoint: {
|
var pub = key.getPub().export('bytes'),
|
||||||
hash: this.outpoint.hash,
|
hash160 = util.sha256ripe160(pub),
|
||||||
index: this.outpoint.index
|
script = Script.createOutputScript(new Address(hash160)),
|
||||||
},
|
hash = this.hashTransactionForSignature( script, index, type),
|
||||||
script: this.script.clone(),
|
sig = key.sign(hash).concat([type]);
|
||||||
sequence: this.sequence
|
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()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return newTxin;
|
var hmap = {};
|
||||||
};
|
for (var o in outputs) {
|
||||||
|
hmap[outputs[o].output] = outputs[o];
|
||||||
var TransactionOut = Bitcoin.TransactionOut = function (data)
|
|
||||||
{
|
|
||||||
if (data.script instanceof Script) {
|
|
||||||
this.script = data.script;
|
|
||||||
} else {
|
|
||||||
this.script = new Script(data.script);
|
|
||||||
}
|
}
|
||||||
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
if (Bitcoin.Util.isArray(data.value)) {
|
var outpoint = this.ins[i].outpoint.hash+':'+this.ins[i].outpoint.index,
|
||||||
this.value = data.value;
|
histItem = hmap[outpoint];
|
||||||
} else if ("string" == typeof data.value) {
|
if (!histItem) continue;
|
||||||
var valueHex = (new BigInteger(data.value, 10)).toString(16);
|
var thisInputAddrdata = addrdata.filter(function(a) {
|
||||||
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
return a.address == histItem.address;
|
||||||
this.value = Crypto.util.hexToBytes(valueHex);
|
});
|
||||||
|
if (thisInputAddrdata.length == 0) continue;
|
||||||
|
this.sign(i,thisInputAddrdata[0].key);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
TransactionOut.prototype.clone = function ()
|
/**
|
||||||
{
|
* Signs a P2SH output at some index with the given key
|
||||||
var newTxout = new TransactionOut({
|
*/
|
||||||
script: this.script.clone(),
|
|
||||||
value: this.value.slice(0)
|
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: {
|
||||||
|
hash: this.outpoint.hash,
|
||||||
|
index: this.outpoint.index
|
||||||
|
},
|
||||||
|
script: this.script.clone(),
|
||||||
|
sequence: this.sequence
|
||||||
});
|
});
|
||||||
return newTxout;
|
};
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
var TransactionOut = function (data) {
|
||||||
|
this.script =
|
||||||
|
data.script instanceof Script ? data.script.clone()
|
||||||
|
: util.isArray(data.script) ? new Script(data.script)
|
||||||
|
: typeof data.script == "string" ? new Script(conv.hexToBytes(data.script))
|
||||||
|
: data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey)
|
||||||
|
: data.address ? Script.createOutputScript(data.address)
|
||||||
|
: new Script();
|
||||||
|
|
||||||
|
if (this.script.buffer.length > 0) this.address = this.script.toAddress();
|
||||||
|
|
||||||
|
this.value =
|
||||||
|
util.isArray(data.value) ? util.bytesToNum(data.value)
|
||||||
|
: "string" == typeof data.value ? parseInt(data.value)
|
||||||
|
: data.value instanceof BigInteger ? parseInt(data.value.toString())
|
||||||
|
: data.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionOut.prototype.clone = function ()
|
||||||
|
{
|
||||||
|
var newTxout = new TransactionOut({
|
||||||
|
script: this.script.clone(),
|
||||||
|
value: this.value
|
||||||
|
});
|
||||||
|
return newTxout;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Transaction = Transaction;
|
||||||
|
module.exports.TransactionIn = TransactionIn;
|
||||||
|
module.exports.TransactionOut = TransactionOut;
|
||||||
|
|
4
src/txdb.js
Executable file → Normal file
4
src/txdb.js
Executable file → Normal file
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
287
src/util.js
287
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();
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* This function will interpret a byte array as a big integer in big
|
|
||||||
* endian notation and ignore leading zeros.
|
|
||||||
*/
|
|
||||||
BigInteger.fromByteArrayUnsigned = function (ba) {
|
|
||||||
if (!ba.length) {
|
|
||||||
return ba.valueOf(0);
|
|
||||||
} else if (ba[0] & 0x80) {
|
|
||||||
// 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.
|
|
||||||
*
|
|
||||||
* 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 () {
|
|
||||||
var val = this.abs().toByteArrayUnsigned();
|
|
||||||
var neg = this.compareTo(BigInteger.ZERO) < 0;
|
|
||||||
|
|
||||||
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]';
|
return Object.prototype.toString.call(o) === '[object Array]';
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an array of a certain length filled with a specific value.
|
* Create a byte array representing a number with the given length
|
||||||
*/
|
*/
|
||||||
makeFilledArray: function (len, val)
|
exports.numToBytes = function(num, bytes) {
|
||||||
{
|
if (bytes === undefined) bytes = 8;
|
||||||
var array = [];
|
if (bytes === 0) return [];
|
||||||
var i = 0;
|
return [num % 256].concat(module.exports.numToBytes(Math.floor(num / 256), bytes - 1));
|
||||||
while (i < len) {
|
}
|
||||||
array[i++] = val;
|
|
||||||
}
|
/**
|
||||||
return array;
|
* Convert a byte array to the number that it represents
|
||||||
},
|
*/
|
||||||
|
exports.bytesToNum = function(bytes) {
|
||||||
/**
|
if (bytes.length === 0) return 0;
|
||||||
* Turn an integer into a "var_int".
|
return bytes[0] + 256 * module.exports.bytesToNum(bytes.slice(1));
|
||||||
*
|
}
|
||||||
* "var_int" is a variable length integer used by Bitcoin's binary format.
|
|
||||||
*
|
/**
|
||||||
* Returns a byte array.
|
* Turn an integer into a "var_int".
|
||||||
*/
|
*
|
||||||
numToVarInt: function (i)
|
* "var_int" is a variable length integer used by Bitcoin's binary format.
|
||||||
{
|
*
|
||||||
if (i < 0xfd) {
|
* Returns a byte array.
|
||||||
// unsigned char
|
*/
|
||||||
return [i];
|
exports.numToVarInt = function(num) {
|
||||||
} else if (i <= 1<<16) {
|
if (num < 253) return [num];
|
||||||
// unsigned short (LE)
|
if (num < 65536) return [253].concat(exports.numToBytes(num, 2));
|
||||||
return [0xfd, i >>> 8, i & 255];
|
if (num < 4294967296) return [254].concat(exports.numToBytes(num, 4));
|
||||||
} else if (i <= 1<<32) {
|
return [253].concat(exports.numToBytes(num, 8));
|
||||||
// unsigned int (LE)
|
}
|
||||||
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
|
||||||
} else {
|
exports.bytesToWords = function (bytes) {
|
||||||
// unsigned long long (LE)
|
var words = [];
|
||||||
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
|
||||||
}
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
||||||
},
|
}
|
||||||
|
return words;
|
||||||
/**
|
}
|
||||||
* Parse a Bitcoin value byte array, returning a BigInteger.
|
|
||||||
*/
|
exports.wordsToBytes = function (words) {
|
||||||
valueToBigInt: function (valueBuffer)
|
var bytes = [];
|
||||||
{
|
for (var b = 0; b < words.length * 32; b += 8) {
|
||||||
if (valueBuffer instanceof BigInteger) return valueBuffer;
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
||||||
|
}
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
return bytes;
|
||||||
return BigInteger.fromByteArrayUnsigned(valueBuffer);
|
},
|
||||||
},
|
|
||||||
|
/**
|
||||||
/**
|
* Calculate RIPEMD160(SHA256(data)).
|
||||||
* Format a Bitcoin value as a string.
|
*
|
||||||
*
|
* Takes an arbitrary byte array as inputs and returns the hash as a byte
|
||||||
* Takes a BigInteger or byte-array and returns that amount of Bitcoins in a
|
* array.
|
||||||
* nice standard formatting.
|
*/
|
||||||
*
|
exports.sha256ripe160 = function (data) {
|
||||||
* Examples:
|
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
||||||
* 12.3555
|
}
|
||||||
* 0.1234
|
|
||||||
* 900.99998888
|
exports.error = function(msg) {
|
||||||
* 34.00
|
throw new Error(msg);
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a floating point string as a Bitcoin value.
|
|
||||||
*
|
|
||||||
* Keep in mind that parsing user input is messy. You should always display
|
|
||||||
* the parsed value back to the user to make sure we understood his input
|
|
||||||
* correctly.
|
|
||||||
*/
|
|
||||||
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)).
|
|
||||||
*
|
|
||||||
* Takes an arbitrary byte array as inputs and returns the hash as a byte
|
|
||||||
* array.
|
|
||||||
*/
|
|
||||||
sha256ripe160: function (data) {
|
|
||||||
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i in Crypto.util) {
|
|
||||||
if (Crypto.util.hasOwnProperty(i)) {
|
|
||||||
Bitcoin.Util[i] = Crypto.util[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
436
src/wallet.js
Executable file → Normal file
436
src/wallet.js
Executable file → Normal file
|
@ -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
|
|
||||||
//
|
var Transaction = require('./transaction').Transaction;
|
||||||
// The keychain is stored as a var in this closure to make accidental
|
var TransactionIn = require('./transaction').TransactionIn;
|
||||||
// serialization less likely.
|
var TransactionOut = require('./transaction').TransactionOut;
|
||||||
//
|
var HDWallet = require('./hdwallet.js')
|
||||||
// Any functions accessing this value therefore have to be defined in
|
|
||||||
// the closure of this constructor.
|
var SecureRandom = require('./jsbn/rng');
|
||||||
|
var rng = new SecureRandom();
|
||||||
|
|
||||||
|
var Wallet = function (seed) {
|
||||||
|
|
||||||
|
// Stored in a closure to make accidental serialization less likely
|
||||||
var keys = [];
|
var keys = [];
|
||||||
|
var masterkey = null;
|
||||||
|
var me = this;
|
||||||
|
|
||||||
// Public hashes of our keys
|
// Addresses
|
||||||
this.addressHashes = [];
|
this.addresses = [];
|
||||||
|
|
||||||
// Transaction data
|
// Transaction output data
|
||||||
this.txIndex = {};
|
this.outputs = {};
|
||||||
this.unspentOuts = [];
|
|
||||||
|
|
||||||
// Other fields
|
// Make a new master key
|
||||||
this.addressPointer = 0;
|
this.newMasterKey = function(seed) {
|
||||||
|
if (!seed) {
|
||||||
/**
|
var seedBytes = new Array(32);
|
||||||
* Add a key to the keychain.
|
rng.nextBytes(seedBytes);
|
||||||
*
|
seed = conv.bytesToString(seedBytes)
|
||||||
* 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);
|
masterkey = new HDWallet(seed);
|
||||||
}
|
keys = []
|
||||||
|
}
|
||||||
|
this.newMasterKey(seed)
|
||||||
|
|
||||||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
|
// Add a new address
|
||||||
};
|
this.generateAddress = function() {
|
||||||
|
keys.push(masterkey.ckd(keys.length).key)
|
||||||
/**
|
this.addresses.push(keys[keys.length-1].getBitcoinAddress().toString())
|
||||||
* Add multiple keys at once.
|
return this.addresses[this.addresses.length - 1]
|
||||||
*/
|
|
||||||
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 k;
|
|
||||||
var hash;
|
|
||||||
// Gather outputs
|
|
||||||
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
|
// Processes a transaction object
|
||||||
for (j = 0; j < tx.ins.length; j++) {
|
// If "verified" is true, then we trust the transaction as "final"
|
||||||
var txin = new TransactionIn(tx.ins[j]);
|
this.processTx = function(tx, verified) {
|
||||||
var pubkey = txin.script.simpleInPubKey();
|
var txhash = conv.bytesToHex(tx.getHash())
|
||||||
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey));
|
for (var i = 0; i < tx.outs.length; i++) {
|
||||||
for (k = 0; k < this.addressHashes.length; k++) {
|
if (this.addresses.indexOf(tx.outs[i].address.toString()) >= 0) {
|
||||||
if (this.addressHashes[k] === hash) {
|
me.outputs[txhash+':'+i] = {
|
||||||
for (var l = 0; l < this.unspentOuts.length; l++) {
|
output: txhash+':'+i,
|
||||||
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash &&
|
value: tx.outs[i].value,
|
||||||
txin.outpoint.index == this.unspentOuts[l].index) {
|
address: tx.outs[i].address.toString(),
|
||||||
this.unspentOuts.splice(l, 1);
|
timestamp: new Date().getTime() / 1000,
|
||||||
|
pending: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
for (var i = 0; i < tx.ins.length; i++) {
|
||||||
|
var op = tx.ins[i].outpoint
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index transaction
|
this.processExistingOutputs = function() {
|
||||||
this.txIndex[tx.hash] = tx;
|
var t = new Date().getTime() / 1000
|
||||||
};
|
for (var o in this.outputs) {
|
||||||
|
if (o.pending && t > o.timestamp + 1200)
|
||||||
Wallet.prototype.getBalance = function () {
|
delete this.outputs[o]
|
||||||
var balance = BigInteger.valueOf(0);
|
if (o.spendpending && t > o.timestamp + 1200) {
|
||||||
for (var i = 0; i < this.unspentOuts.length; i++) {
|
o.spendpending = false
|
||||||
var txout = this.unspentOuts[i].out;
|
o.spend = false
|
||||||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value));
|
delete o.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return balance;
|
var peoInterval = setInterval(this.processExistingOutputs, 10000)
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
|
this.getUtxoToPay = function(value) {
|
||||||
var selectedOuts = [];
|
var h = []
|
||||||
var txValue = sendValue.add(feeValue);
|
for (var out in this.outputs) h.push(this.outputs[out])
|
||||||
var availableValue = BigInteger.ZERO;
|
var utxo = h.filter(function(x) { return !x.spend });
|
||||||
var i;
|
var valuecompare = function(a,b) { return a.value > b.value; }
|
||||||
for (i = 0; i < this.unspentOuts.length; i++) {
|
var high = utxo.filter(function(o) { return o.value >= value; })
|
||||||
selectedOuts.push(this.unspentOuts[i]);
|
.sort(valuecompare);
|
||||||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value));
|
if (high.length > 0) return [high[0]];
|
||||||
|
utxo.sort(valuecompare);
|
||||||
if (availableValue.compareTo(txValue) >= 0) break;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (availableValue.compareTo(txValue) < 0) {
|
this.mkSend = function(to, value, fee) {
|
||||||
throw new Error('Insufficient funds.');
|
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 changeValue = availableValue.subtract(txValue);
|
var value = outputs.reduce(function(t,o) { return t + o.value },0),
|
||||||
|
utxo = this.getUtxoToPay(value + fee),
|
||||||
var sendTx = new Bitcoin.Transaction();
|
sum = utxo.reduce(function(t,p) { return t + o.value },0);
|
||||||
|
utxo[changeIndex].value += sum - value - fee;
|
||||||
for (i = 0; i < selectedOuts.length; i++) {
|
var tx = new Bitcoin.Transaction({
|
||||||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index);
|
ins: utxo.map(function(x) { return x.output }),
|
||||||
|
outs: outputs
|
||||||
|
})
|
||||||
|
this.sign(tx)
|
||||||
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
sendTx.addOutput(address, sendValue);
|
this.sign = function(tx) {
|
||||||
if (changeValue.compareTo(BigInteger.ZERO) > 0) {
|
tx.ins.map(function(inp,i) {
|
||||||
sendTx.addOutput(this.getNextAddress(), changeValue);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashType = 1; // SIGHASH_ALL
|
this.getMasterKey = function() { return masterkey }
|
||||||
|
|
||||||
for (i = 0; i < sendTx.ins.length; i++) {
|
this.getPrivateKey = function(index) {
|
||||||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType);
|
if (typeof index == "string")
|
||||||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash();
|
return keys.filter(function(i,k){ return addresses[i] == index })[0]
|
||||||
var signature = this.signWithKey(pubKeyHash, hash);
|
else
|
||||||
|
return keys[index]
|
||||||
// Append hash type
|
|
||||||
signature.push(parseInt(hashType, 10));
|
|
||||||
|
|
||||||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendTx;
|
this.getPrivateKeys = function() { return keys }
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.clearTransactions = function () {
|
|
||||||
this.txIndex = {};
|
|
||||||
this.unspentOuts = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if a pubKeyHash belongs to this wallet.
|
|
||||||
*/
|
|
||||||
Wallet.prototype.hasHash = function (hash) {
|
|
||||||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash);
|
|
||||||
|
|
||||||
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
|
||||||
for (var k = 0; k < this.addressHashes.length; k++) {
|
|
||||||
if (this.addressHashes[k] === hash) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Wallet;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
module.exports = Wallet;
|
||||||
|
|
52
test/address.js
Normal file
52
test/address.js
Normal file
|
@ -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
Normal file
22
test/base58.js
Normal file
|
@ -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
Normal file
28
test/convert.js
Normal file
|
@ -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
Normal file
23
test/ec.js
Normal file
|
@ -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
Normal file
43
test/eckey.js
Normal file
|
@ -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
Normal file
217
test/hdwallet.js
Normal file
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -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
Normal file
28
test/jsbn.js
Normal file
|
@ -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
Normal file
34
test/message.js
Normal file
|
@ -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
Normal file
68
test/misc.js
Normal file
|
@ -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
Normal file
1
test/mocha.opts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
--ui qunit
|
112
test/test.js
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
vendored
226
vendor/qunit/qunit.css
vendored
|
@ -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
vendored
1510
vendor/qunit/qunit.js
vendored
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue