start to split into node commonjs style modules
- no longer is the global Bitcoin used for modules - cleaner and more maintainable code - add more tests
This commit is contained in:
parent
a6f05fb505
commit
0faac29134
35 changed files with 3401 additions and 3169 deletions
|
@ -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,10 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"devDependencies" : {
|
"devDependencies" : {
|
||||||
"pkginfo" : ">=0.2.1",
|
"mocha": "1.8.1"
|
||||||
"jake-uglify" : ">=1.0.0"
|
},
|
||||||
|
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha test/*.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
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 address_types = {
|
||||||
|
prod: 0,
|
||||||
|
testnet: 111
|
||||||
|
};
|
||||||
|
|
||||||
|
var p2sh_types = {
|
||||||
|
prod: 5,
|
||||||
|
testnet: 196
|
||||||
|
};
|
||||||
|
|
||||||
|
var Address = function (bytes) {
|
||||||
|
if (typeof bytes === 'string') {
|
||||||
|
bytes = Address.decodeString(bytes);
|
||||||
}
|
}
|
||||||
this.hash = bytes;
|
this.hash = bytes;
|
||||||
|
|
||||||
|
@ -12,7 +26,7 @@ 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
|
// Get a copy of the hash
|
||||||
var hash = this.hash.slice(0);
|
var hash = this.hash.slice(0);
|
||||||
|
|
||||||
|
@ -23,18 +37,20 @@ Bitcoin.Address.prototype.toString = function () {
|
||||||
|
|
||||||
var bytes = hash.concat(checksum.slice(0,4));
|
var bytes = hash.concat(checksum.slice(0,4));
|
||||||
|
|
||||||
return Bitcoin.Base58.encode(bytes);
|
return base58.encode(bytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.Address.prototype.getHashBase64 = function () {
|
Address.prototype.getHashBase64 = function () {
|
||||||
return Crypto.util.bytesToBase64(this.hash);
|
return conv.bytesToBase64(this.hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// TODO(shtylman) isValid?
|
||||||
* Parse a Bitcoin address contained in a string.
|
Address.validate = function(string, type) {
|
||||||
*/
|
try {
|
||||||
Bitcoin.Address.decodeString = function (string) {
|
var bytes = base58.decode(string);
|
||||||
var bytes = Bitcoin.Base58.decode(string);
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var hash = bytes.slice(0, 21);
|
var hash = bytes.slice(0, 21);
|
||||||
|
|
||||||
|
@ -44,14 +60,42 @@ Bitcoin.Address.decodeString = function (string) {
|
||||||
checksum[1] != bytes[22] ||
|
checksum[1] != bytes[22] ||
|
||||||
checksum[2] != bytes[23] ||
|
checksum[2] != bytes[23] ||
|
||||||
checksum[3] != bytes[24]) {
|
checksum[3] != bytes[24]) {
|
||||||
throw "Checksum validation failed!";
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var version = hash[0];
|
||||||
|
|
||||||
|
if (type && version !== address_types[type] && version !== p2sh_types[type]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a Bitcoin address contained in a string.
|
||||||
|
*/
|
||||||
|
Address.decodeString = function (string) {
|
||||||
|
var bytes = base58.decode(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 new Error('Address Checksum validation failed: ' + string);
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = hash.shift();
|
var version = hash.shift();
|
||||||
|
// TODO(shtylman) allow for specific version decoding same as validate above
|
||||||
if (version != 0) {
|
if (version != 0) {
|
||||||
throw "Version "+version+" not supported!";
|
throw new Error('Address version not supported: ' + string);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Address;
|
||||||
|
|
133
src/base58.js
133
src/base58.js
|
@ -1,71 +1,78 @@
|
||||||
(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);
|
|
||||||
chars.unshift(B58.alphabet[mod.intValue()]);
|
|
||||||
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('');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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 char = input[i];
|
||||||
|
var p = positions[char];
|
||||||
|
|
||||||
|
// 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 (char == '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;
|
||||||
|
}
|
||||||
|
|
||||||
var B58 = Bitcoin.Base58;
|
|
||||||
})(
|
|
||||||
'undefined' != typeof Bitcoin ? Bitcoin : module.exports
|
|
||||||
);
|
|
||||||
|
|
10
src/bitcoin.js
Executable file → Normal file
10
src/bitcoin.js
Executable file → Normal file
|
@ -1,13 +1,3 @@
|
||||||
(function (exports) {
|
|
||||||
var Bitcoin = exports;
|
|
||||||
|
|
||||||
if ('object' !== typeof module) {
|
|
||||||
Bitcoin.EventEmitter = EventEmitter;
|
|
||||||
}
|
|
||||||
})(
|
|
||||||
'object' === typeof module ? module.exports : (window.Bitcoin = {})
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
function makeKeypair()
|
function makeKeypair()
|
||||||
{
|
{
|
||||||
|
|
59
src/convert.js
Normal file
59
src/convert.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// convert to/from various values
|
||||||
|
|
||||||
|
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
// Convert a byte array to a hex string
|
||||||
|
module.exports.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
|
||||||
|
module.exports.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
|
||||||
|
module.exports.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
|
||||||
|
module.exports.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// utf8 and binary?
|
||||||
|
//stringToBytes
|
||||||
|
//bytesToString
|
||||||
|
|
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("")}}})();
|
|
224
src/crypto-js/crypto.js
Executable file → Normal file
224
src/crypto-js/crypto.js
Executable file → Normal file
|
@ -1,160 +1,64 @@
|
||||||
/*!
|
/*!
|
||||||
* Crypto-JS v2.0.0
|
* Crypto-JS v2.0.0
|
||||||
* http://code.google.com/p/crypto-js/
|
* http://code.google.com/p/crypto-js/
|
||||||
* 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(){
|
|
||||||
|
// Global Crypto object
|
||||||
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
var Crypto = module.exports = {};
|
||||||
|
|
||||||
// Global Crypto object
|
// Crypto utilities
|
||||||
var Crypto = window.Crypto = {};
|
var util = Crypto.util = {
|
||||||
|
|
||||||
// Crypto utilities
|
// Generate an array of any length of random bytes
|
||||||
var util = Crypto.util = {
|
randomBytes: function (n) {
|
||||||
|
for (var bytes = []; n > 0; n--)
|
||||||
// Bit-wise rotate left
|
bytes.push(Math.floor(Math.random() * 256));
|
||||||
rotl: function (n, b) {
|
return bytes;
|
||||||
return (n << b) | (n >>> (32 - b));
|
},
|
||||||
},
|
|
||||||
|
};
|
||||||
// Bit-wise rotate right
|
|
||||||
rotr: function (n, b) {
|
// Crypto mode namespace
|
||||||
return (n << (32 - b)) | (n >>> b);
|
Crypto.mode = {};
|
||||||
},
|
|
||||||
|
// Crypto character encodings
|
||||||
// Swap big-endian to little-endian and vice versa
|
var charenc = Crypto.charenc = {};
|
||||||
endian: function (n) {
|
|
||||||
|
// UTF-8 encoding
|
||||||
// If number given, swap endian
|
var UTF8 = charenc.UTF8 = {
|
||||||
if (n.constructor == Number) {
|
|
||||||
return util.rotl(n, 8) & 0x00FF00FF |
|
// Convert a string to a byte array
|
||||||
util.rotl(n, 24) & 0xFF00FF00;
|
stringToBytes: function (str) {
|
||||||
}
|
return Binary.stringToBytes(unescape(encodeURIComponent(str)));
|
||||||
|
},
|
||||||
// Else, assume array and swap all items
|
|
||||||
for (var i = 0; i < n.length; i++)
|
// Convert a byte array to a string
|
||||||
n[i] = util.endian(n[i]);
|
bytesToString: function (bytes) {
|
||||||
return n;
|
return decodeURIComponent(escape(Binary.bytesToString(bytes)));
|
||||||
|
}
|
||||||
},
|
|
||||||
|
};
|
||||||
// Generate an array of any length of random bytes
|
|
||||||
randomBytes: function (n) {
|
// Binary encoding
|
||||||
for (var bytes = []; n > 0; n--)
|
var Binary = charenc.Binary = {
|
||||||
bytes.push(Math.floor(Math.random() * 256));
|
|
||||||
return bytes;
|
// Convert a string to a byte array
|
||||||
},
|
stringToBytes: function (str) {
|
||||||
|
for (var bytes = [], i = 0; i < str.length; i++)
|
||||||
// Convert a byte array to big-endian 32-bit words
|
bytes.push(str.charCodeAt(i));
|
||||||
bytesToWords: function (bytes) {
|
return 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 a byte array to a string
|
||||||
},
|
bytesToString: function (bytes) {
|
||||||
|
for (var str = [], i = 0; i < bytes.length; i++)
|
||||||
// Convert big-endian 32-bit words to a byte array
|
str.push(String.fromCharCode(bytes[i]));
|
||||||
wordsToBytes: function (words) {
|
return str.join("");
|
||||||
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
}
|
||||||
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
|
||||||
return bytes;
|
};
|
||||||
},
|
|
||||||
|
module.exports.SHA256 = require('./sha256');
|
||||||
// Convert a byte array to a hex string
|
module.exports.RIPEMD160 = require('./ripemd160');
|
||||||
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 = {};
|
|
||||||
|
|
||||||
// Crypto character encodings
|
|
||||||
var charenc = Crypto.charenc = {};
|
|
||||||
|
|
||||||
// UTF-8 encoding
|
|
||||||
var UTF8 = charenc.UTF8 = {
|
|
||||||
|
|
||||||
// Convert a string to a byte array
|
|
||||||
stringToBytes: function (str) {
|
|
||||||
return Binary.stringToBytes(unescape(encodeURIComponent(str)));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Convert a byte array to a string
|
|
||||||
bytesToString: function (bytes) {
|
|
||||||
return decodeURIComponent(escape(Binary.bytesToString(bytes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Binary encoding
|
|
||||||
var Binary = charenc.Binary = {
|
|
||||||
|
|
||||||
// Convert a string to a byte array
|
|
||||||
stringToBytes: function (str) {
|
|
||||||
for (var bytes = [], i = 0; i < str.length; i++)
|
|
||||||
bytes.push(str.charCodeAt(i));
|
|
||||||
return bytes;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Convert a byte array to a string
|
|
||||||
bytesToString: function (bytes) {
|
|
||||||
for (var str = [], i = 0; i < bytes.length; i++)
|
|
||||||
str.push(String.fromCharCode(bytes[i]));
|
|
||||||
return str.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
14
src/crypto-js/ripemd160.js
Executable file → Normal file
14
src/crypto-js/ripemd160.js
Executable file → Normal file
|
@ -13,7 +13,8 @@
|
||||||
* Ported to Crypto-JS by Stefan Thomas.
|
* Ported to Crypto-JS by Stefan Thomas.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () {
|
var Crypto = require('./crypto');
|
||||||
|
|
||||||
// Shortcuts
|
// Shortcuts
|
||||||
var C = Crypto,
|
var C = Crypto,
|
||||||
util = C.util,
|
util = C.util,
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
Binary = charenc.Binary;
|
Binary = charenc.Binary;
|
||||||
|
|
||||||
// Convert a byte array to little-endian 32-bit words
|
// Convert a byte array to little-endian 32-bit words
|
||||||
util.bytesToLWords = function (bytes) {
|
var bytesToLWords = function (bytes) {
|
||||||
|
|
||||||
var output = Array(bytes.length >> 2);
|
var output = Array(bytes.length >> 2);
|
||||||
for (var i = 0; i < output.length; i++)
|
for (var i = 0; i < output.length; i++)
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert little-endian 32-bit words to a byte array
|
// Convert little-endian 32-bit words to a byte array
|
||||||
util.lWordsToBytes = function (words) {
|
var lWordsToBytes = function (words) {
|
||||||
var output = [];
|
var output = [];
|
||||||
for (var i = 0; i < words.length * 32; i += 8)
|
for (var i = 0; i < words.length * 32; i += 8)
|
||||||
output.push((words[i>>5] >>> (i % 32)) & 0xff);
|
output.push((words[i>>5] >>> (i % 32)) & 0xff);
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
var RIPEMD160 = C.RIPEMD160 = function (message, options) {
|
var RIPEMD160 = C.RIPEMD160 = function (message, options) {
|
||||||
var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message));
|
var digestbytes = lWordsToBytes(RIPEMD160._rmd160(message));
|
||||||
return options && options.asBytes ? digestbytes :
|
return options && options.asBytes ? digestbytes :
|
||||||
options && options.asString ? Binary.bytesToString(digestbytes) :
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
||||||
util.bytesToHex(digestbytes);
|
util.bytesToHex(digestbytes);
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
// Convert to byte array
|
// Convert to byte array
|
||||||
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
||||||
|
|
||||||
var x = util.bytesToLWords(message),
|
var x = bytesToLWords(message),
|
||||||
len = message.length * 8;
|
len = message.length * 8;
|
||||||
|
|
||||||
/* append padding */
|
/* append padding */
|
||||||
|
@ -167,4 +168,5 @@
|
||||||
{
|
{
|
||||||
return (num << cnt) | (num >>> (32 - cnt));
|
return (num << cnt) | (num >>> (32 - cnt));
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
module.exports = RIPEMD160
|
||||||
|
|
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})();
|
|
281
src/crypto-js/sha256.js
Executable file → Normal file
281
src/crypto-js/sha256.js
Executable file → Normal file
|
@ -1,133 +1,148 @@
|
||||||
/*!
|
/*!
|
||||||
* Crypto-JS v2.0.0
|
* Crypto-JS v2.0.0
|
||||||
* http://code.google.com/p/crypto-js/
|
* http://code.google.com/p/crypto-js/
|
||||||
* 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(){
|
|
||||||
|
// Convert a byte array to big-endian 32-bit words
|
||||||
// Shortcuts
|
var bytesToWords = function (bytes) {
|
||||||
var C = Crypto,
|
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
|
||||||
util = C.util,
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
||||||
charenc = C.charenc,
|
return words;
|
||||||
UTF8 = charenc.UTF8,
|
};
|
||||||
Binary = charenc.Binary;
|
|
||||||
|
// Convert big-endian 32-bit words to a byte array
|
||||||
// Constants
|
var wordsToBytes = function (words) {
|
||||||
var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
||||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
||||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
return bytes;
|
||||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
};
|
||||||
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
|
||||||
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
var Crypto = require('./crypto');
|
||||||
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
|
||||||
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
// Shortcuts
|
||||||
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
var C = Crypto,
|
||||||
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
util = C.util,
|
||||||
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
charenc = C.charenc,
|
||||||
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
UTF8 = charenc.UTF8,
|
||||||
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
Binary = charenc.Binary;
|
||||||
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
|
||||||
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
// Constants
|
||||||
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ];
|
var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
||||||
|
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||||
// Public API
|
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||||
var SHA256 = C.SHA256 = function (message, options) {
|
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||||
var digestbytes = util.wordsToBytes(SHA256._sha256(message));
|
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||||
return options && options.asBytes ? digestbytes :
|
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||||
options && options.asString ? Binary.bytesToString(digestbytes) :
|
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||||
util.bytesToHex(digestbytes);
|
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||||
};
|
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||||
|
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||||
// The core
|
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||||
SHA256._sha256 = function (message) {
|
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||||
|
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||||
// Convert to byte array
|
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||||
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||||
/* else, assume byte array already */
|
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ];
|
||||||
|
|
||||||
var m = util.bytesToWords(message),
|
// Public API
|
||||||
l = message.length * 8,
|
var SHA256 = C.SHA256 = function (message, options) {
|
||||||
H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
var digestbytes = wordsToBytes(SHA256._sha256(message));
|
||||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ],
|
return options && options.asBytes ? digestbytes :
|
||||||
w = [],
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
||||||
a, b, c, d, e, f, g, h, i, j,
|
util.bytesToHex(digestbytes);
|
||||||
t1, t2;
|
};
|
||||||
|
|
||||||
// Padding
|
// The core
|
||||||
m[l >> 5] |= 0x80 << (24 - l % 32);
|
SHA256._sha256 = function (message) {
|
||||||
m[((l + 64 >> 9) << 4) + 15] = l;
|
|
||||||
|
// Convert to byte array
|
||||||
for (var i = 0; i < m.length; i += 16) {
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
||||||
|
/* else, assume byte array already */
|
||||||
a = H[0];
|
|
||||||
b = H[1];
|
var m = bytesToWords(message),
|
||||||
c = H[2];
|
l = message.length * 8,
|
||||||
d = H[3];
|
H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||||
e = H[4];
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ],
|
||||||
f = H[5];
|
w = [],
|
||||||
g = H[6];
|
a, b, c, d, e, f, g, h, i, j,
|
||||||
h = H[7];
|
t1, t2;
|
||||||
|
|
||||||
for (var j = 0; j < 64; j++) {
|
// Padding
|
||||||
|
m[l >> 5] |= 0x80 << (24 - l % 32);
|
||||||
if (j < 16) w[j] = m[j + i];
|
m[((l + 64 >> 9) << 4) + 15] = l;
|
||||||
else {
|
|
||||||
|
for (var i = 0; i < m.length; i += 16) {
|
||||||
var gamma0x = w[j - 15],
|
|
||||||
gamma1x = w[j - 2],
|
a = H[0];
|
||||||
gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
b = H[1];
|
||||||
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
c = H[2];
|
||||||
(gamma0x >>> 3),
|
d = H[3];
|
||||||
gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
e = H[4];
|
||||||
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
f = H[5];
|
||||||
(gamma1x >>> 10);
|
g = H[6];
|
||||||
|
h = H[7];
|
||||||
w[j] = gamma0 + (w[j - 7] >>> 0) +
|
|
||||||
gamma1 + (w[j - 16] >>> 0);
|
for (var j = 0; j < 64; j++) {
|
||||||
|
|
||||||
}
|
if (j < 16) w[j] = m[j + i];
|
||||||
|
else {
|
||||||
var ch = e & f ^ ~e & g,
|
|
||||||
maj = a & b ^ a & c ^ b & c,
|
var gamma0x = w[j - 15],
|
||||||
sigma0 = ((a << 30) | (a >>> 2)) ^
|
gamma1x = w[j - 2],
|
||||||
((a << 19) | (a >>> 13)) ^
|
gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
||||||
((a << 10) | (a >>> 22)),
|
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
||||||
sigma1 = ((e << 26) | (e >>> 6)) ^
|
(gamma0x >>> 3),
|
||||||
((e << 21) | (e >>> 11)) ^
|
gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
||||||
((e << 7) | (e >>> 25));
|
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
||||||
|
(gamma1x >>> 10);
|
||||||
|
|
||||||
t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0);
|
w[j] = gamma0 + (w[j - 7] >>> 0) +
|
||||||
t2 = sigma0 + maj;
|
gamma1 + (w[j - 16] >>> 0);
|
||||||
|
|
||||||
h = g;
|
}
|
||||||
g = f;
|
|
||||||
f = e;
|
var ch = e & f ^ ~e & g,
|
||||||
e = d + t1;
|
maj = a & b ^ a & c ^ b & c,
|
||||||
d = c;
|
sigma0 = ((a << 30) | (a >>> 2)) ^
|
||||||
c = b;
|
((a << 19) | (a >>> 13)) ^
|
||||||
b = a;
|
((a << 10) | (a >>> 22)),
|
||||||
a = t1 + t2;
|
sigma1 = ((e << 26) | (e >>> 6)) ^
|
||||||
|
((e << 21) | (e >>> 11)) ^
|
||||||
}
|
((e << 7) | (e >>> 25));
|
||||||
|
|
||||||
H[0] += a;
|
|
||||||
H[1] += b;
|
t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0);
|
||||||
H[2] += c;
|
t2 = sigma0 + maj;
|
||||||
H[3] += d;
|
|
||||||
H[4] += e;
|
h = g;
|
||||||
H[5] += f;
|
g = f;
|
||||||
H[6] += g;
|
f = e;
|
||||||
H[7] += h;
|
e = d + t1;
|
||||||
|
d = c;
|
||||||
}
|
c = b;
|
||||||
|
b = a;
|
||||||
return H;
|
a = t1 + t2;
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
// Package private blocksize
|
H[0] += a;
|
||||||
SHA256._blocksize = 16;
|
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;
|
||||||
|
|
||||||
|
module.exports = SHA256;
|
||||||
|
|
707
src/ecdsa.js
707
src/ecdsa.js
|
@ -1,475 +1,290 @@
|
||||||
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');
|
||||||
|
|
||||||
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 () {
|
var ECDSA = {
|
||||||
var x = this.getX().toBigInteger();
|
getBigRandom: function (limit) {
|
||||||
var y = this.getY().toBigInteger();
|
return new BigInteger(limit.bitLength(), rng)
|
||||||
var a = this.curve.getA().toBigInteger();
|
.mod(limit.subtract(BigInteger.ONE))
|
||||||
var b = this.curve.getB().toBigInteger();
|
.add(BigInteger.ONE)
|
||||||
var n = this.curve.getQ();
|
;
|
||||||
var lhs = y.multiply(y).mod(n);
|
},
|
||||||
var rhs = x.multiply(x).multiply(x)
|
sign: function (hash, priv) {
|
||||||
.add(a.multiply(x)).add(b).mod(n);
|
var d = priv;
|
||||||
return lhs.equals(rhs);
|
var n = ecparams.getN();
|
||||||
};
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||||
|
|
||||||
ECPointFp.prototype.toString = function () {
|
do {
|
||||||
return '('+this.getX().toBigInteger().toString()+','+
|
var k = ECDSA.getBigRandom(n);
|
||||||
this.getY().toBigInteger().toString()+')';
|
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);
|
||||||
* 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
|
return ECDSA.serializeSig(r, s);
|
||||||
if (this.isInfinity()) {
|
},
|
||||||
throw new Error("Point is at infinity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check coordinate bounds
|
verify: function (hash, sig, pubkey) {
|
||||||
var x = this.getX().toBigInteger();
|
var r,s;
|
||||||
var y = this.getY().toBigInteger();
|
if (util.isArray(sig)) {
|
||||||
if (x.compareTo(BigInteger.ONE) < 0 ||
|
var obj = ECDSA.parseSig(sig);
|
||||||
x.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
r = obj.r;
|
||||||
throw new Error('x coordinate out of bounds');
|
s = obj.s;
|
||||||
}
|
} else if ("object" === typeof sig && sig.r && sig.s) {
|
||||||
if (y.compareTo(BigInteger.ONE) < 0 ||
|
r = sig.r;
|
||||||
y.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
s = sig.s;
|
||||||
throw new Error('y coordinate out of bounds');
|
} else {
|
||||||
}
|
throw "Invalid value for signature";
|
||||||
|
}
|
||||||
|
|
||||||
// Check y^2 = x^3 + ax + b (mod n)
|
var Q;
|
||||||
if (!this.isOnCurve()) {
|
if (pubkey instanceof ECPointFp) {
|
||||||
throw new Error("Point is not on the curve.");
|
Q = pubkey;
|
||||||
}
|
} else if (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);
|
||||||
|
|
||||||
// Check nQ = 0 (Q is a scalar multiple of G)
|
return ECDSA.verifyRaw(e, r, s, Q);
|
||||||
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;
|
verifyRaw: function (e, r, s, Q) {
|
||||||
};
|
var n = ecparams.getN();
|
||||||
|
var G = ecparams.getG();
|
||||||
|
|
||||||
function dmp(v) {
|
if (r.compareTo(BigInteger.ONE) < 0 ||
|
||||||
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
r.compareTo(n) >= 0)
|
||||||
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
|
return false;
|
||||||
};
|
|
||||||
|
|
||||||
Bitcoin.ECDSA = (function () {
|
if (s.compareTo(BigInteger.ONE) < 0 ||
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
s.compareTo(n) >= 0)
|
||||||
var rng = new SecureRandom();
|
return false;
|
||||||
|
|
||||||
var P_OVER_FOUR = null;
|
var c = s.modInverse(n);
|
||||||
|
|
||||||
function implShamirsTrick(P, k, Q, l)
|
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)
|
||||||
{
|
{
|
||||||
var m = Math.max(k.bitLength(), l.bitLength());
|
for (var i = 0; i < 4; i++) {
|
||||||
var Z = P.add2D(Q);
|
try {
|
||||||
var R = P.curve.getInfinity();
|
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i);
|
||||||
|
if (pubkey.getBitcoinAddress().toString() == address) {
|
||||||
for (var i = m - 1; i >= 0; --i) {
|
return i;
|
||||||
R = R.twice2D();
|
|
||||||
|
|
||||||
R.z = BigInteger.ONE;
|
|
||||||
|
|
||||||
if (k.testBit(i)) {
|
|
||||||
if (l.testBit(i)) {
|
|
||||||
R = R.add2D(Z);
|
|
||||||
} else {
|
|
||||||
R = R.add2D(P);
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (e) {}
|
||||||
if (l.testBit(i)) {
|
|
||||||
R = R.add2D(Q);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
throw "Unable to find valid recovery factor";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return R;
|
module.exports = ECDSA;
|
||||||
};
|
|
||||||
|
|
||||||
var 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;
|
|
||||||
})();
|
|
||||||
|
|
279
src/eckey.js
279
src/eckey.js
|
@ -1,131 +1,176 @@
|
||||||
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 ECKey = function (input) {
|
var ecparams = sec("secp256k1");
|
||||||
if (!input) {
|
|
||||||
// Generate new key
|
// input can be nothing, array of bytes, hex string, or base58 string
|
||||||
var n = ecparams.getN();
|
var ECKey = function (input) {
|
||||||
this.priv = ECDSA.getBigRandom(n);
|
if (!(this instanceof ECKey)) {
|
||||||
} else if (input instanceof BigInteger) {
|
return new ECKey(input);
|
||||||
// Input is a private key value
|
}
|
||||||
this.priv = input;
|
|
||||||
} else if (Bitcoin.Util.isArray(input)) {
|
this.compressed = !!ECKey.compressByDefault;
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
|
||||||
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
if (!input) {
|
||||||
} else if ("string" == typeof input) {
|
// Generate new key
|
||||||
if (input.length == 51 && input[0] == '5') {
|
var n = ecparams.getN();
|
||||||
// Base58 encoded private key
|
this.priv = ecdsa.getBigRandom(n);
|
||||||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input));
|
} else if (input instanceof BigInteger) {
|
||||||
} else {
|
// Input is a private key value
|
||||||
// Prepend zero byte to prevent interpretation as negative integer
|
this.priv = input;
|
||||||
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input));
|
} else if (util.isArray(input)) {
|
||||||
}
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
|
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
||||||
|
this.compressed = false;
|
||||||
|
} else if ("string" == typeof input) {
|
||||||
|
if (input.length == 51 && input[0] == '5') {
|
||||||
|
// Base58 encoded private key
|
||||||
|
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input));
|
||||||
|
this.compressed = false;
|
||||||
}
|
}
|
||||||
this.compressed = !!ECKey.compressByDefault;
|
else if (input.length == 52 && (input[0] === 'K' || input[0] === 'L')) {
|
||||||
};
|
// Base58 encoded private key
|
||||||
|
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input));
|
||||||
/**
|
this.compressed = true;
|
||||||
* Whether public keys should be returned compressed by default.
|
|
||||||
*/
|
|
||||||
ECKey.compressByDefault = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether the public key should be returned compressed or not.
|
|
||||||
*/
|
|
||||||
ECKey.prototype.setCompressed = function (v) {
|
|
||||||
this.compressed = !!v;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return public key in DER encoding.
|
|
||||||
*/
|
|
||||||
ECKey.prototype.getPub = function () {
|
|
||||||
return this.getPubPoint().getEncoded(this.compressed);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return public point as ECPoint object.
|
|
||||||
*/
|
|
||||||
ECKey.prototype.getPubPoint = function () {
|
|
||||||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
|
|
||||||
|
|
||||||
return this.pub;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the pubKeyHash for this key.
|
|
||||||
*
|
|
||||||
* 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.getBitcoinAddress = function () {
|
|
||||||
var hash = this.getPubKeyHash();
|
|
||||||
var addr = new Bitcoin.Address(hash);
|
|
||||||
return addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.getExportedPrivateKey = function () {
|
|
||||||
var hash = this.priv.toByteArrayUnsigned();
|
|
||||||
while (hash.length < 32) hash.unshift(0);
|
|
||||||
hash.unshift(0x80);
|
|
||||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
|
|
||||||
var bytes = hash.concat(checksum.slice(0,4));
|
|
||||||
return Bitcoin.Base58.encode(bytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.setPub = function (pub) {
|
|
||||||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
|
|
||||||
};
|
|
||||||
|
|
||||||
ECKey.prototype.toString = function (format) {
|
|
||||||
if (format === "base64") {
|
|
||||||
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
|
|
||||||
} else {
|
} else {
|
||||||
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
|
// hex string?
|
||||||
|
// //wtf is base64 here for?
|
||||||
|
// Prepend zero byte to prevent interpretation as negative integer
|
||||||
|
this.priv = BigInteger.fromByteArrayUnsigned(conv.base64ToBytes(input));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ECKey.prototype.sign = function (hash) {
|
// TODO(shtylman) methods
|
||||||
return ECDSA.sign(hash, this.priv);
|
// wallet import format (base58 check with meta info)
|
||||||
};
|
// fromWIF
|
||||||
|
// toWIF
|
||||||
|
// fromBytes
|
||||||
|
// toBytes
|
||||||
|
// fromHex
|
||||||
|
// toHex
|
||||||
|
|
||||||
ECKey.prototype.verify = function (hash, sig) {
|
/**
|
||||||
return ECDSA.verify(hash, sig, this.getPub());
|
* Whether public keys should be returned compressed by default.
|
||||||
};
|
*/
|
||||||
|
ECKey.compressByDefault = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an exported private key contained in a string.
|
* Set whether the public key should be returned compressed or not.
|
||||||
*/
|
*/
|
||||||
ECKey.decodeString = function (string) {
|
ECKey.prototype.setCompressed = function (v) {
|
||||||
var bytes = Bitcoin.Base58.decode(string);
|
this.compressed = !!v;
|
||||||
|
};
|
||||||
|
|
||||||
var hash = bytes.slice(0, 33);
|
/**
|
||||||
|
* Return public key in DER encoding.
|
||||||
|
*/
|
||||||
|
ECKey.prototype.getPub = function () {
|
||||||
|
return this.getPubPoint().getEncoded(this.compressed);
|
||||||
|
};
|
||||||
|
|
||||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
|
/**
|
||||||
|
* Return public point as ECPoint object.
|
||||||
|
*/
|
||||||
|
ECKey.prototype.getPubPoint = function () {
|
||||||
|
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
|
||||||
|
|
||||||
if (checksum[0] != bytes[33] ||
|
return this.pub;
|
||||||
checksum[1] != bytes[34] ||
|
};
|
||||||
checksum[2] != bytes[35] ||
|
|
||||||
checksum[3] != bytes[36]) {
|
|
||||||
throw "Checksum validation failed!";
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = hash.shift();
|
/**
|
||||||
|
* Get the pubKeyHash for this key.
|
||||||
|
*
|
||||||
|
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as
|
||||||
|
* a byte array.
|
||||||
|
*/
|
||||||
|
ECKey.prototype.getPubKeyHash = function () {
|
||||||
|
if (this.pubKeyHash) return this.pubKeyHash;
|
||||||
|
|
||||||
if (version != 0x80) {
|
return this.pubKeyHash = util.sha256ripe160(this.getPub());
|
||||||
throw "Version "+version+" not supported!";
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
ECKey.prototype.getBitcoinAddress = function () {
|
||||||
};
|
var hash = this.getPubKeyHash();
|
||||||
|
var addr = new Address(hash);
|
||||||
|
return addr;
|
||||||
|
};
|
||||||
|
|
||||||
return ECKey;
|
ECKey.prototype.getExportedPrivateKey = function () {
|
||||||
})();
|
var hash = this.priv.toByteArrayUnsigned();
|
||||||
|
while (hash.length < 32) hash.unshift(0);
|
||||||
|
hash.unshift(0x80);
|
||||||
|
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
|
||||||
|
var bytes = hash.concat(checksum.slice(0,4));
|
||||||
|
return Bitcoin.Base58.encode(bytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
ECKey.prototype.setPub = function (pub) {
|
||||||
|
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
|
||||||
|
};
|
||||||
|
|
||||||
|
ECKey.prototype.toString = function (format) {
|
||||||
|
if (format === "base64") {
|
||||||
|
return conv.bytesToBase64(this.priv.toByteArrayUnsigned());
|
||||||
|
} else {
|
||||||
|
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ECKey.prototype.sign = function (hash) {
|
||||||
|
return ecdsa.sign(hash, this.priv);
|
||||||
|
};
|
||||||
|
|
||||||
|
ECKey.prototype.verify = function (hash, sig) {
|
||||||
|
return ecdsa.verify(hash, sig, this.getPub());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an exported private key contained in a string.
|
||||||
|
*/
|
||||||
|
ECKey.decodeString = function (string) {
|
||||||
|
var bytes = base58.decode(string);
|
||||||
|
|
||||||
|
if (bytes.length !== 37 && bytes.length !== 38) {
|
||||||
|
throw new Error('not a valid base58 encoded private key');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Format:
|
||||||
|
//* uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of
|
||||||
|
//previous 33 bytes], base58 encoded
|
||||||
|
//* compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash()
|
||||||
|
//previous 34 bytes], base58 encoded
|
||||||
|
|
||||||
|
if (bytes[33] === 0x01) {
|
||||||
|
// compressed
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = bytes.slice(0, 33);
|
||||||
|
|
||||||
|
/*
|
||||||
|
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
|
||||||
|
|
||||||
|
if (checksum[0] != bytes[33] ||
|
||||||
|
checksum[1] != bytes[34] ||
|
||||||
|
checksum[2] != bytes[35] ||
|
||||||
|
checksum[3] != bytes[36]) {
|
||||||
|
throw "Checksum validation failed!";
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var version = hash.shift();
|
||||||
|
|
||||||
|
if (version != 0x80) {
|
||||||
|
throw "Version "+version+" not supported!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ECKey;
|
||||||
|
|
46
src/index.js
Normal file
46
src/index.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Address: require('./address'),
|
||||||
|
Key: require('./eckey'),
|
||||||
|
BigInteger: require('./jsbn/jsbn'),
|
||||||
|
Script: require('./script'),
|
||||||
|
Opcode: require('./opcode'),
|
||||||
|
Transaction: require('./transaction').Transaction,
|
||||||
|
TransactionIn: require('./transaction').TransactionIn,
|
||||||
|
TransactionOut: require('./transaction').TransactionOut,
|
||||||
|
ECPointFp: require('./jsbn/ec').ECPointFp,
|
||||||
|
Wallet: require('./wallet'),
|
||||||
|
|
||||||
|
ecdsa: require('./ecdsa'),
|
||||||
|
|
||||||
|
// base58 encoding/decoding to bytes
|
||||||
|
base58: require('./base58'),
|
||||||
|
|
||||||
|
// conversions
|
||||||
|
convert: require('./convert'),
|
||||||
|
|
||||||
|
endian: endian
|
||||||
|
}
|
193
src/jsbn/ec.js
193
src/jsbn/ec.js
|
@ -2,7 +2,7 @@
|
||||||
// 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');
|
||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// ECFieldElementFp
|
// ECFieldElementFp
|
||||||
|
@ -314,3 +314,194 @@ 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 (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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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,67 @@
|
||||||
/**
|
/**
|
||||||
* Implements Bitcoin's feature for signing arbitrary messages.
|
* Implements Bitcoin's feature for signing arbitrary messages.
|
||||||
*/
|
*/
|
||||||
Bitcoin.Message = (function () {
|
var Message = {};
|
||||||
var Message = {};
|
|
||||||
|
|
||||||
Message.magicPrefix = "Bitcoin Signed Message:\n";
|
Message.magicPrefix = "Bitcoin Signed Message:\n";
|
||||||
|
|
||||||
Message.makeMagicMessage = function (message) {
|
Message.makeMagicMessage = function (message) {
|
||||||
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix);
|
var magicBytes = Crypto.charenc.UTF8.stringToBytes(Message.magicPrefix);
|
||||||
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message);
|
var messageBytes = Crypto.charenc.UTF8.stringToBytes(message);
|
||||||
|
|
||||||
var buffer = [];
|
var buffer = [];
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(magicBytes.length));
|
||||||
buffer = buffer.concat(magicBytes);
|
buffer = buffer.concat(magicBytes);
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length));
|
buffer = buffer.concat(Bitcoin.Util.numToVarInt(messageBytes.length));
|
||||||
buffer = buffer.concat(messageBytes);
|
buffer = buffer.concat(messageBytes);
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
Message.getHash = function (message) {
|
Message.getHash = function (message) {
|
||||||
var buffer = Message.makeMagicMessage(message);
|
var buffer = Message.makeMagicMessage(message);
|
||||||
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
Message.signMessage = function (key, message, compressed) {
|
Message.signMessage = function (key, message, compressed) {
|
||||||
var hash = Message.getHash(message);
|
var hash = Message.getHash(message);
|
||||||
|
|
||||||
var sig = key.sign(hash);
|
var sig = key.sign(hash);
|
||||||
|
|
||||||
var obj = Bitcoin.ECDSA.parseSig(sig);
|
var obj = Bitcoin.ECDSA.parseSig(sig);
|
||||||
|
|
||||||
var address = key.getBitcoinAddress().toString();
|
var address = key.getBitcoinAddress().toString();
|
||||||
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash);
|
var i = Bitcoin.ECDSA.calcPubkeyRecoveryParam(address, obj.r, obj.s, hash);
|
||||||
|
|
||||||
i += 27;
|
i += 27;
|
||||||
if (compressed) i += 4;
|
if (compressed) i += 4;
|
||||||
|
|
||||||
var rBa = obj.r.toByteArrayUnsigned();
|
var rBa = obj.r.toByteArrayUnsigned();
|
||||||
var sBa = obj.s.toByteArrayUnsigned();
|
var sBa = obj.s.toByteArrayUnsigned();
|
||||||
|
|
||||||
// Pad to 32 bytes per value
|
// Pad to 32 bytes per value
|
||||||
while (rBa.length < 32) rBa.unshift(0);
|
while (rBa.length < 32) rBa.unshift(0);
|
||||||
while (sBa.length < 32) sBa.unshift(0);
|
while (sBa.length < 32) sBa.unshift(0);
|
||||||
|
|
||||||
sig = [i].concat(rBa).concat(sBa);
|
sig = [i].concat(rBa).concat(sBa);
|
||||||
|
|
||||||
return Crypto.util.bytesToBase64(sig);
|
return Crypto.util.bytesToBase64(sig);
|
||||||
};
|
};
|
||||||
|
|
||||||
Message.verifyMessage = function (address, sig, message) {
|
Message.verifyMessage = function (address, sig, message) {
|
||||||
sig = Crypto.util.base64ToBytes(sig);
|
sig = Crypto.util.base64ToBytes(sig);
|
||||||
sig = Bitcoin.ECDSA.parseSigCompact(sig);
|
sig = Bitcoin.ECDSA.parseSigCompact(sig);
|
||||||
|
|
||||||
var hash = Message.getHash(message);
|
var hash = Message.getHash(message);
|
||||||
|
|
||||||
var isCompressed = !!(sig.i & 4);
|
var isCompressed = !!(sig.i & 4);
|
||||||
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i);
|
var pubKey = Bitcoin.ECDSA.recoverPubKey(sig.r, sig.s, hash, sig.i);
|
||||||
|
|
||||||
pubKey.setCompressed(isCompressed);
|
pubKey.setCompressed(isCompressed);
|
||||||
|
|
||||||
var expectedAddress = pubKey.getBitcoinAddress().toString();
|
var expectedAddress = pubKey.getBitcoinAddress().toString();
|
||||||
|
|
||||||
return (address === expectedAddress);
|
return (address === expectedAddress);
|
||||||
};
|
};
|
||||||
|
|
||||||
return Message;
|
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;
|
||||||
|
|
672
src/script.js
672
src/script.js
|
@ -1,347 +1,369 @@
|
||||||
(function () {
|
var Opcode = require('./opcode');
|
||||||
var Opcode = Bitcoin.Opcode;
|
var util = require('./util');
|
||||||
|
var conv = require('./convert');
|
||||||
|
|
||||||
// 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 = Crypto.util.base64ToBytes(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.
|
||||||
return 'Pubkey';
|
* [pubKey] OP_CHECKSIG
|
||||||
} else {
|
*
|
||||||
return 'Strange';
|
* Strange:
|
||||||
}
|
* Any other script (no template matched).
|
||||||
|
*/
|
||||||
|
Script.prototype.getOutType = function () {
|
||||||
|
if (this.chunks[this.chunks.length-1] == Opcode.map.OP_CHECKMULTISIG && this.chunks[this.chunks.length-2] <= 3) {
|
||||||
|
// Transfer to M-OF-N
|
||||||
|
return 'Multisig';
|
||||||
|
} 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 'Address';
|
||||||
|
} else if (this.chunks.length == 2 &&
|
||||||
|
this.chunks[1] == Opcode.map.OP_CHECKSIG) {
|
||||||
|
// Transfer to IP address
|
||||||
|
return 'Pubkey';
|
||||||
|
} else {
|
||||||
|
return 'Strange';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the affected address hash for this output.
|
* Returns the affected address hash for this output.
|
||||||
*
|
*
|
||||||
* For standard transactions, this will return the hash of the pubKey that
|
* For standard transactions, this will return the hash of the pubKey that
|
||||||
* can spend this output.
|
* can spend this output.
|
||||||
*
|
*
|
||||||
* In the future, for payToScriptHash outputs, this will return the
|
* In the future, for payToScriptHash outputs, this will return the
|
||||||
* scriptHash. Note that non-standard and standard payToScriptHash transactions
|
* scriptHash. Note that non-standard and standard payToScriptHash transactions
|
||||||
* look the same
|
* look the same
|
||||||
*
|
*
|
||||||
* This method is useful for indexing transactions.
|
* This method is useful for indexing transactions.
|
||||||
*/
|
*/
|
||||||
Script.prototype.simpleOutHash = function ()
|
Script.prototype.simpleOutHash = function ()
|
||||||
{
|
{
|
||||||
switch (this.getOutType()) {
|
switch (this.getOutType()) {
|
||||||
case 'Address':
|
case 'Address':
|
||||||
return this.chunks[2];
|
return this.chunks[2];
|
||||||
case 'Pubkey':
|
case 'Pubkey':
|
||||||
return Bitcoin.Util.sha256ripe160(this.chunks[0]);
|
return Bitcoin.Util.sha256ripe160(this.chunks[0]);
|
||||||
default:
|
default:
|
||||||
throw new Error("Encountered non-standard scriptPubKey");
|
throw new Error("Encountered non-standard scriptPubKey: " + this.getOutType());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old name for Script#simpleOutHash.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 &&
|
||||||
|
util.isArray(this.chunks[0]) &&
|
||||||
|
util.isArray(this.chunks[1])) {
|
||||||
|
return 'Address';
|
||||||
|
} else {
|
||||||
|
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 Bitcoin.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 a standard payToPubKeyHash output.
|
||||||
|
*/
|
||||||
|
Script.createOutputScript = function (address)
|
||||||
|
{
|
||||||
|
var script = new Script();
|
||||||
|
script.writeOp(Opcode.map.OP_DUP);
|
||||||
|
script.writeOp(Opcode.map.OP_HASH160);
|
||||||
|
script.writeBytes(address.hash);
|
||||||
|
script.writeOp(Opcode.map.OP_EQUALVERIFY);
|
||||||
|
script.writeOp(Opcode.map.OP_CHECKSIG);
|
||||||
|
return script;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract 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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Old name for Script#simpleOutHash.
|
* Create an m-of-n output script
|
||||||
*
|
*/
|
||||||
* @deprecated
|
Script.createMultiSigOutputScript = function (m, pubkeys)
|
||||||
*/
|
{
|
||||||
Script.prototype.simpleOutPubKeyHash = Script.prototype.simpleOutHash;
|
var script = new Script();
|
||||||
|
|
||||||
/**
|
|
||||||
* 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';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 Bitcoin.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 < OP_PUSHDATA1) {
|
|
||||||
this.buffer.push(data.length);
|
|
||||||
} else if (data.length <= 0xff) {
|
|
||||||
this.buffer.push(OP_PUSHDATA1);
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
Script.createOutputScript = function (address)
|
|
||||||
{
|
|
||||||
var script = new Script();
|
|
||||||
script.writeOp(OP_DUP);
|
|
||||||
script.writeOp(OP_HASH160);
|
|
||||||
script.writeBytes(address.hash);
|
|
||||||
script.writeOp(OP_EQUALVERIFY);
|
|
||||||
script.writeOp(OP_CHECKSIG);
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
script.writeOp(Opcode.map.OP_1 + m - 1);
|
||||||
|
|
||||||
/**
|
for (var i = 0; i < pubkeys.length; ++i) {
|
||||||
* Extract bitcoin addresses from an output script
|
script.writeBytes(pubkeys[i]);
|
||||||
*/
|
}
|
||||||
Script.prototype.extractAddresses = function (addresses)
|
|
||||||
{
|
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1);
|
||||||
switch (this.getOutType()) {
|
|
||||||
case 'Address':
|
|
||||||
addresses.push(new Address(this.chunks[2]));
|
|
||||||
return 1;
|
|
||||||
case 'Pubkey':
|
|
||||||
addresses.push(new Address(Util.sha256ripe160(this.chunks[0])));
|
|
||||||
return 1;
|
|
||||||
case 'Multisig':
|
|
||||||
for (var i = 1; i < this.chunks.length-2; ++i) {
|
|
||||||
addresses.push(new Address(Util.sha256ripe160(this.chunks[i])));
|
|
||||||
}
|
|
||||||
return this.chunks[0] - OP_1 + 1;
|
|
||||||
default:
|
|
||||||
throw new Error("Encountered non-standard scriptPubKey");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
script.writeOp(Opcode.map.OP_CHECKMULTISIG);
|
||||||
* 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;
|
||||||
|
};
|
||||||
|
|
||||||
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 ()
|
||||||
* Create a standard payToPubKeyHash input.
|
{
|
||||||
*/
|
return new Script(this.buffer);
|
||||||
Script.createInputScript = function (signature, pubKey)
|
};
|
||||||
{
|
|
||||||
var script = new Script();
|
|
||||||
script.writeBytes(signature);
|
|
||||||
script.writeBytes(pubKey);
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
Script.prototype.clone = function ()
|
module.exports = Script;
|
||||||
{
|
|
||||||
return new Script(this.buffer);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
|
@ -1,445 +1,467 @@
|
||||||
(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 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 = [];
|
||||||
this.outs = [];
|
this.outs = [];
|
||||||
this.timestamp = null;
|
this.timestamp = null;
|
||||||
this.block = null;
|
this.block = null;
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
if (doc.hash) this.hash = doc.hash;
|
if (doc.hash) this.hash = doc.hash;
|
||||||
if (doc.version) this.version = doc.version;
|
if (doc.version) this.version = doc.version;
|
||||||
if (doc.lock_time) this.lock_time = doc.lock_time;
|
if (doc.lock_time) this.lock_time = doc.lock_time;
|
||||||
if (doc.ins && doc.ins.length) {
|
if (doc.ins && doc.ins.length) {
|
||||||
for (var i = 0; i < doc.ins.length; i++) {
|
for (var i = 0; i < doc.ins.length; i++) {
|
||||||
this.addInput(new TransactionIn(doc.ins[i]));
|
this.addInput(new TransactionIn(doc.ins[i]));
|
||||||
}
|
|
||||||
}
|
|
||||||
if (doc.outs && doc.outs.length) {
|
|
||||||
for (var i = 0; i < doc.outs.length; i++) {
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* Takes an array of plain JavaScript objects containing transaction data and
|
|
||||||
* returns an array of Transaction objects.
|
|
||||||
*/
|
|
||||||
Transaction.objectify = function (txs) {
|
|
||||||
var objs = [];
|
|
||||||
for (var i = 0; i < txs.length; i++) {
|
|
||||||
objs.push(new Transaction(txs[i]));
|
|
||||||
}
|
|
||||||
return objs;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new txin.
|
|
||||||
*
|
|
||||||
* Can be called with an existing TransactionIn object to add it to the
|
|
||||||
* 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
|
|
||||||
* referenced output will be created.
|
|
||||||
*
|
|
||||||
* Note that this method does not sign the created input.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.addInput = function (tx, outIndex) {
|
|
||||||
if (arguments[0] instanceof TransactionIn) {
|
|
||||||
this.ins.push(arguments[0]);
|
|
||||||
} else {
|
|
||||||
this.ins.push(new TransactionIn({
|
|
||||||
outpoint: {
|
|
||||||
hash: tx.hash,
|
|
||||||
index: outIndex
|
|
||||||
},
|
|
||||||
script: new Bitcoin.Script(),
|
|
||||||
sequence: 4294967295
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new txout.
|
|
||||||
*
|
|
||||||
* Can be called with an existing TransactionOut object to add it to the
|
|
||||||
* 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
|
|
||||||
* values will be created.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.addOutput = function (address, value) {
|
|
||||||
if (arguments[0] instanceof TransactionOut) {
|
|
||||||
this.outs.push(arguments[0]);
|
|
||||||
} else {
|
|
||||||
if (value instanceof BigInteger) {
|
|
||||||
value = value.toByteArrayUnsigned().reverse();
|
|
||||||
while (value.length < 8) value.push(0);
|
|
||||||
} else if (Bitcoin.Util.isArray(value)) {
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
this.outs.push(new TransactionOut({
|
|
||||||
value: value,
|
|
||||||
script: Script.createOutputScript(address)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize this transaction.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* be hashed to get the transaction's standard Bitcoin hash.
|
|
||||||
*/
|
|
||||||
Transaction.prototype.serialize = function ()
|
|
||||||
{
|
|
||||||
var buffer = [];
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse());
|
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length));
|
|
||||||
for (var i = 0; i < this.ins.length; 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());
|
|
||||||
var scriptBytes = txin.script.buffer;
|
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
|
||||||
buffer = buffer.concat(scriptBytes);
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse());
|
|
||||||
}
|
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length));
|
|
||||||
for (var i = 0; i < this.outs.length; i++) {
|
|
||||||
var txout = this.outs[i];
|
|
||||||
buffer = buffer.concat(txout.value);
|
|
||||||
var scriptBytes = txout.script.buffer;
|
|
||||||
buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length));
|
|
||||||
buffer = buffer.concat(scriptBytes);
|
|
||||||
}
|
|
||||||
buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse());
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
var OP_CODESEPARATOR = 171;
|
|
||||||
|
|
||||||
var SIGHASH_ALL = 1;
|
|
||||||
var SIGHASH_NONE = 2;
|
|
||||||
var SIGHASH_SINGLE = 3;
|
|
||||||
var SIGHASH_ANYONECANPAY = 80;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash transaction for signing a specific input.
|
|
||||||
*
|
|
||||||
* Bitcoin uses a different hash for each signed transaction input. This
|
|
||||||
* 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
|
|
||||||
// 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(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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (doc.outs && doc.outs.length) {
|
||||||
var impact = this.calcImpact(wallet);
|
for (var i = 0; i < doc.outs.length; i++) {
|
||||||
|
this.addOutput(new TransactionOut(doc.outs[i]));
|
||||||
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;
|
|
||||||
|
|
||||||
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 () {
|
|
||||||
var totalValue = BigInteger.ZERO;
|
|
||||||
for (var j = 0; j < this.outs.length; j++) {
|
|
||||||
var txout = this.outs[j];
|
|
||||||
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
|
|
||||||
}
|
|
||||||
return totalValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Old name for Transaction#getTotalOutValue.
|
|
||||||
*
|
|
||||||
* @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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (doc.timestamp) this.timestamp = doc.timestamp;
|
||||||
|
if (doc.block) this.block = doc.block;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate debit to us from all ins
|
/**
|
||||||
var valueIn = BigInteger.ZERO;
|
* Turn transaction data into Transaction objects.
|
||||||
for (var j = 0; j < this.ins.length; j++) {
|
*
|
||||||
var txin = this.ins[j];
|
* Takes an array of plain JavaScript objects containing transaction data and
|
||||||
var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash());
|
* returns an array of Transaction objects.
|
||||||
if (wallet.hasHash(hash)) {
|
*/
|
||||||
var fromTx = wallet.txIndex[txin.outpoint.hash];
|
Transaction.objectify = function (txs) {
|
||||||
if (fromTx) {
|
var objs = [];
|
||||||
valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value));
|
for (var i = 0; i < txs.length; i++) {
|
||||||
}
|
objs.push(new Transaction(txs[i]));
|
||||||
}
|
}
|
||||||
}
|
return objs;
|
||||||
if (valueOut.compareTo(valueIn) >= 0) {
|
};
|
||||||
return {
|
|
||||||
sign: 1,
|
|
||||||
value: valueOut.subtract(valueIn)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
sign: -1,
|
|
||||||
value: valueIn.subtract(valueOut)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var TransactionIn = Bitcoin.TransactionIn = function (data)
|
/**
|
||||||
{
|
* Create a new txin.
|
||||||
this.outpoint = data.outpoint;
|
*
|
||||||
if (data.script instanceof Script) {
|
* Can be called with an existing TransactionIn object to add it to the
|
||||||
this.script = data.script;
|
* transaction. Or it can be called with a Transaction object and an integer
|
||||||
} else {
|
* output index, in which case a new TransactionIn object pointing to the
|
||||||
this.script = new Script(data.script);
|
* referenced output will be created.
|
||||||
}
|
*
|
||||||
this.sequence = data.sequence;
|
* Note that this method does not sign the created input.
|
||||||
};
|
*/
|
||||||
|
Transaction.prototype.addInput = function (tx, outIndex) {
|
||||||
TransactionIn.prototype.clone = function ()
|
if (arguments[0] instanceof TransactionIn) {
|
||||||
{
|
this.ins.push(arguments[0]);
|
||||||
var newTxin = new TransactionIn({
|
} else {
|
||||||
|
this.ins.push(new TransactionIn({
|
||||||
outpoint: {
|
outpoint: {
|
||||||
hash: this.outpoint.hash,
|
hash: tx.hash,
|
||||||
index: this.outpoint.index
|
index: outIndex
|
||||||
},
|
},
|
||||||
script: this.script.clone(),
|
script: new Script(),
|
||||||
sequence: this.sequence
|
sequence: 4294967295
|
||||||
});
|
}));
|
||||||
return newTxin;
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var TransactionOut = Bitcoin.TransactionOut = function (data)
|
/**
|
||||||
{
|
* Create a new txout.
|
||||||
if (data.script instanceof Script) {
|
*
|
||||||
this.script = data.script;
|
* Can be called with an existing TransactionOut object to add it to the
|
||||||
|
* 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
|
||||||
|
* values will be created.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.addOutput = function (address, value) {
|
||||||
|
if (arguments[0] instanceof TransactionOut) {
|
||||||
|
this.outs.push(arguments[0]);
|
||||||
|
} else {
|
||||||
|
if (value instanceof BigInteger) {
|
||||||
|
value = value.toByteArrayUnsigned().reverse();
|
||||||
|
while (value.length < 8) value.push(0);
|
||||||
|
} else if (Bitcoin.Util.isArray(value)) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
this.outs.push(new TransactionOut({
|
||||||
|
value: value,
|
||||||
|
script: Script.createOutputScript(address)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(shtylman) crypto sha uses this also
|
||||||
|
// Convert a byte array to big-endian 32-bit words
|
||||||
|
var 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
|
||||||
|
var 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize this transaction.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* be hashed to get the transaction's standard Bitcoin hash.
|
||||||
|
*/
|
||||||
|
Transaction.prototype.serialize = function ()
|
||||||
|
{
|
||||||
|
var buffer = [];
|
||||||
|
buffer = buffer.concat(wordsToBytes([parseInt(this.version)]).reverse());
|
||||||
|
buffer = buffer.concat(util.numToVarInt(this.ins.length));
|
||||||
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
|
var txin = this.ins[i];
|
||||||
|
buffer = buffer.concat(conv.base64ToBytes(txin.outpoint.hash));
|
||||||
|
buffer = buffer.concat(wordsToBytes([parseInt(txin.outpoint.index)]).reverse());
|
||||||
|
var scriptBytes = txin.script.buffer;
|
||||||
|
buffer = buffer.concat(util.numToVarInt(scriptBytes.length));
|
||||||
|
buffer = buffer.concat(scriptBytes);
|
||||||
|
buffer = buffer.concat(wordsToBytes([parseInt(txin.sequence)]).reverse());
|
||||||
|
}
|
||||||
|
buffer = buffer.concat(util.numToVarInt(this.outs.length));
|
||||||
|
for (var i = 0; i < this.outs.length; i++) {
|
||||||
|
var txout = this.outs[i];
|
||||||
|
buffer = buffer.concat(txout.value);
|
||||||
|
var scriptBytes = txout.script.buffer;
|
||||||
|
buffer = buffer.concat(util.numToVarInt(scriptBytes.length));
|
||||||
|
buffer = buffer.concat(scriptBytes);
|
||||||
|
}
|
||||||
|
buffer = buffer.concat(wordsToBytes([parseInt(this.lock_time)]).reverse());
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
var OP_CODESEPARATOR = 171;
|
||||||
|
|
||||||
|
var SIGHASH_ALL = 1;
|
||||||
|
var SIGHASH_NONE = 2;
|
||||||
|
var SIGHASH_SINGLE = 3;
|
||||||
|
var SIGHASH_ANYONECANPAY = 80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash transaction for signing a specific input.
|
||||||
|
*
|
||||||
|
* Bitcoin uses a different hash for each signed transaction input. This
|
||||||
|
* 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
|
||||||
|
// 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(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 {
|
} else {
|
||||||
this.script = new Script(data.script);
|
firstMeRecvHash = hash;
|
||||||
}
|
}
|
||||||
|
firstRecvHash = hash;
|
||||||
if (Bitcoin.Util.isArray(data.value)) {
|
}
|
||||||
this.value = data.value;
|
for (var i = this.ins.length-1; i >= 0; i--) {
|
||||||
} else if ("string" == typeof data.value) {
|
var txin = this.ins[i];
|
||||||
var valueHex = (new BigInteger(data.value, 10)).toString(16);
|
firstSendHash = txin.script.simpleInPubKeyHash();
|
||||||
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
if (!wallet.hasHash(firstSendHash)) {
|
||||||
this.value = Crypto.util.hexToBytes(valueHex);
|
allFromMe = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
TransactionOut.prototype.clone = function ()
|
var impact = this.calcImpact(wallet);
|
||||||
{
|
|
||||||
var newTxout = new TransactionOut({
|
|
||||||
script: this.script.clone(),
|
|
||||||
value: this.value.slice(0)
|
|
||||||
});
|
|
||||||
return newTxout;
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
var totalValue = BigInteger.ZERO;
|
||||||
|
for (var j = 0; j < this.outs.length; j++) {
|
||||||
|
var txout = this.outs[j];
|
||||||
|
totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value));
|
||||||
|
}
|
||||||
|
return totalValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old name for Transaction#getTotalOutValue.
|
||||||
|
*
|
||||||
|
* @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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate debit to us from all ins
|
||||||
|
var valueIn = BigInteger.ZERO;
|
||||||
|
for (var j = 0; j < this.ins.length; j++) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valueOut.compareTo(valueIn) >= 0) {
|
||||||
|
return {
|
||||||
|
sign: 1,
|
||||||
|
value: valueOut.subtract(valueIn)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
sign: -1,
|
||||||
|
value: valueIn.subtract(valueOut)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var TransactionIn = function (data)
|
||||||
|
{
|
||||||
|
this.outpoint = data.outpoint;
|
||||||
|
if (data.script instanceof Script) {
|
||||||
|
this.script = data.script;
|
||||||
|
} else {
|
||||||
|
//this.script = new Script(data.script);
|
||||||
|
this.script = Script.fromScriptSig(data.scriptSig);
|
||||||
|
}
|
||||||
|
this.sequence = data.sequence;
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionIn.prototype.clone = function ()
|
||||||
|
{
|
||||||
|
var newTxin = new TransactionIn({
|
||||||
|
outpoint: {
|
||||||
|
hash: this.outpoint.hash,
|
||||||
|
index: this.outpoint.index
|
||||||
|
},
|
||||||
|
script: this.script.clone(),
|
||||||
|
sequence: this.sequence
|
||||||
|
});
|
||||||
|
return newTxin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var TransactionOut = function (data)
|
||||||
|
{
|
||||||
|
if (data.script instanceof Script) {
|
||||||
|
this.script = data.script;
|
||||||
|
} else {
|
||||||
|
//this.script = new Script(data.script);
|
||||||
|
this.script = Script.fromPubKey(data.scriptPubKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (util.isArray(data.value)) {
|
||||||
|
this.value = data.value;
|
||||||
|
} else if ("string" == typeof data.value) {
|
||||||
|
var valueHex = (new BigInteger(data.value, 10)).toString(16);
|
||||||
|
while (valueHex.length < 16) valueHex = "0" + valueHex;
|
||||||
|
this.value = conv.hexToBytes(valueHex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionOut.prototype.clone = function ()
|
||||||
|
{
|
||||||
|
var newTxout = new TransactionOut({
|
||||||
|
script: this.script.clone(),
|
||||||
|
value: this.value.slice(0)
|
||||||
|
});
|
||||||
|
return newTxout;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Transaction = Transaction;
|
||||||
|
module.exports.TransactionIn = TransactionIn;
|
||||||
|
module.exports.TransactionOut = TransactionOut;
|
||||||
|
|
||||||
|
|
0
src/txdb.js
Executable file → Normal file
0
src/txdb.js
Executable file → Normal file
122
src/util.js
122
src/util.js
|
@ -1,117 +1,7 @@
|
||||||
// BigInteger monkey patching
|
var BigInteger = require('./jsbn/jsbn');
|
||||||
BigInteger.valueOf = nbv;
|
var Crypto = require('./crypto-js/crypto');
|
||||||
|
|
||||||
/**
|
module.exports = {
|
||||||
* Returns a byte array representation of the big integer.
|
|
||||||
*
|
|
||||||
* 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 () {
|
|
||||||
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.
|
* Cross-browser compatibility version of Array.isArray.
|
||||||
*/
|
*/
|
||||||
|
@ -220,9 +110,3 @@ Bitcoin.Util = {
|
||||||
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i in Crypto.util) {
|
|
||||||
if (Crypto.util.hasOwnProperty(i)) {
|
|
||||||
Bitcoin.Util[i] = Crypto.util[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
587
src/wallet.js
Executable file → Normal file
587
src/wallet.js
Executable file → Normal file
|
@ -1,308 +1,323 @@
|
||||||
Bitcoin.Wallet = (function () {
|
var Script = require('./script');
|
||||||
var Script = Bitcoin.Script,
|
var ECKey = require('./eckey');
|
||||||
TransactionIn = Bitcoin.TransactionIn,
|
var conv = require('./convert');
|
||||||
TransactionOut = Bitcoin.TransactionOut;
|
var util = require('./util');
|
||||||
|
|
||||||
var Wallet = function () {
|
var BigInteger = require('./jsbn/jsbn');
|
||||||
// Keychain
|
|
||||||
//
|
|
||||||
// The keychain is stored as a var in this closure to make accidental
|
|
||||||
// serialization less likely.
|
|
||||||
//
|
|
||||||
// Any functions accessing this value therefore have to be defined in
|
|
||||||
// the closure of this constructor.
|
|
||||||
var keys = [];
|
|
||||||
|
|
||||||
// Public hashes of our keys
|
var Transaction = require('./transaction').Transaction;
|
||||||
this.addressHashes = [];
|
var TransactionIn = require('./transaction').TransactionIn;
|
||||||
|
var TransactionOut = require('./transaction').TransactionOut;
|
||||||
|
|
||||||
// Transaction data
|
var Wallet = function () {
|
||||||
this.txIndex = {};
|
// Keychain
|
||||||
this.unspentOuts = [];
|
//
|
||||||
|
// The keychain is stored as a var in this closure to make accidental
|
||||||
|
// serialization less likely.
|
||||||
|
//
|
||||||
|
// Any functions accessing this value therefore have to be defined in
|
||||||
|
// the closure of this constructor.
|
||||||
|
var keys = [];
|
||||||
|
|
||||||
// Other fields
|
// Public hashes of our keys
|
||||||
this.addressPointer = 0;
|
this.addressHashes = [];
|
||||||
|
|
||||||
/**
|
// Transaction data
|
||||||
* Add a key to the keychain.
|
this.txIndex = {};
|
||||||
*
|
this.unspentOuts = [];
|
||||||
* 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) {
|
// Other fields
|
||||||
if ("string" === typeof pub) {
|
this.addressPointer = 0;
|
||||||
pub = Crypto.util.base64ToBytes(pub);
|
|
||||||
}
|
|
||||||
key.setPub(pub);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add multiple keys at once.
|
|
||||||
*/
|
|
||||||
this.addKeys = function (keys, pubs) {
|
|
||||||
if ("string" === typeof keys) {
|
|
||||||
keys = keys.split(',');
|
|
||||||
}
|
|
||||||
if ("string" === typeof pubs) {
|
|
||||||
pubs = pubs.split(',');
|
|
||||||
}
|
|
||||||
var i;
|
|
||||||
if (Array.isArray(pubs) && keys.length == pubs.length) {
|
|
||||||
for (i = 0; i < keys.length; i++) {
|
|
||||||
this.addKey(keys[i], pubs[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < keys.length; i++) {
|
|
||||||
this.addKey(keys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the key chain.
|
|
||||||
*
|
|
||||||
* Returns an array of base64-encoded private values.
|
|
||||||
*/
|
|
||||||
this.getKeys = function () {
|
|
||||||
var serializedWallet = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
serializedWallet.push(keys[i].toString('base64'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return serializedWallet;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the public keys.
|
|
||||||
*
|
|
||||||
* Returns an array of base64-encoded public keys.
|
|
||||||
*/
|
|
||||||
this.getPubKeys = function () {
|
|
||||||
var pubs = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
pubs.push(Crypto.util.bytesToBase64(keys[i].getPub()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return pubs;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all keys.
|
|
||||||
*/
|
|
||||||
this.clear = function () {
|
|
||||||
keys = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of keys in this wallet.
|
|
||||||
*/
|
|
||||||
this.getLength = function () {
|
|
||||||
return keys.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the addresses for this wallet.
|
|
||||||
*
|
|
||||||
* Returns an array of Address objects.
|
|
||||||
*/
|
|
||||||
this.getAllAddresses = function () {
|
|
||||||
var addresses = [];
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
addresses.push(keys[i].getBitcoinAddress());
|
|
||||||
}
|
|
||||||
return addresses;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getCurAddress = function () {
|
|
||||||
if (keys[this.addressPointer]) {
|
|
||||||
return keys[this.addressPointer].getBitcoinAddress();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Go to the next address.
|
|
||||||
*
|
|
||||||
* If there are no more new addresses available, one will be generated
|
|
||||||
* automatically.
|
|
||||||
*/
|
|
||||||
this.getNextAddress = function () {
|
|
||||||
this.addressPointer++;
|
|
||||||
if (!keys[this.addressPointer]) {
|
|
||||||
this.generateAddress();
|
|
||||||
}
|
|
||||||
return keys[this.addressPointer].getBitcoinAddress();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a hash with a key.
|
|
||||||
*
|
|
||||||
* This method expects the pubKeyHash as the first parameter and the hash
|
|
||||||
* to be signed as the second parameter.
|
|
||||||
*/
|
|
||||||
this.signWithKey = function (pubKeyHash, hash) {
|
|
||||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
|
||||||
for (var i = 0; i < this.addressHashes.length; i++) {
|
|
||||||
if (this.addressHashes[i] == pubKeyHash) {
|
|
||||||
return keys[i].sign(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("Missing key for signature");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the corresponding pubKey for a pubKeyHash.
|
|
||||||
*
|
|
||||||
* This function only works if the pubKey in question is part of this
|
|
||||||
* wallet.
|
|
||||||
*/
|
|
||||||
this.getPubKeyFromHash = function (pubKeyHash) {
|
|
||||||
pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash);
|
|
||||||
for (var i = 0; i < this.addressHashes.length; i++) {
|
|
||||||
if (this.addressHashes[i] == pubKeyHash) {
|
|
||||||
return keys[i].getPub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("Hash unknown");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.generateAddress = function () {
|
|
||||||
this.addKey(new Bitcoin.ECKey());
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a transaction to the wallet's processed transaction.
|
* Add a key to the keychain.
|
||||||
*
|
*
|
||||||
* This will add a transaction to the wallet, updating its balance and
|
* The corresponding public key can be provided as a second parameter. This
|
||||||
* available unspent outputs.
|
* adds it to the cache in the ECKey object and avoid the need to
|
||||||
|
* expensively calculate it later.
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.process = function (tx) {
|
this.addKey = function (key, pub) {
|
||||||
if (this.txIndex[tx.hash]) return;
|
if (!(key instanceof ECKey)) {
|
||||||
|
key = new ECKey(key);
|
||||||
|
}
|
||||||
|
keys.push(key);
|
||||||
|
|
||||||
var j;
|
if (pub) {
|
||||||
var k;
|
if ("string" === typeof pub) {
|
||||||
var hash;
|
pub = Crypto.util.base64ToBytes(pub);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
key.setPub(pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove spent outputs
|
this.addressHashes.push(key.getBitcoinAddress().getHashBase64());
|
||||||
for (j = 0; j < tx.ins.length; j++) {
|
|
||||||
var txin = new TransactionIn(tx.ins[j]);
|
|
||||||
var pubkey = txin.script.simpleInPubKey();
|
|
||||||
hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey));
|
|
||||||
for (k = 0; k < this.addressHashes.length; k++) {
|
|
||||||
if (this.addressHashes[k] === hash) {
|
|
||||||
for (var l = 0; l < this.unspentOuts.length; l++) {
|
|
||||||
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash &&
|
|
||||||
txin.outpoint.index == this.unspentOuts[l].index) {
|
|
||||||
this.unspentOuts.splice(l, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index transaction
|
|
||||||
this.txIndex[tx.hash] = tx;
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.getBalance = function () {
|
|
||||||
var balance = BigInteger.valueOf(0);
|
|
||||||
for (var i = 0; i < this.unspentOuts.length; i++) {
|
|
||||||
var txout = this.unspentOuts[i].out;
|
|
||||||
balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value));
|
|
||||||
}
|
|
||||||
return balance;
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
|
|
||||||
var selectedOuts = [];
|
|
||||||
var txValue = sendValue.add(feeValue);
|
|
||||||
var availableValue = BigInteger.ZERO;
|
|
||||||
var i;
|
|
||||||
for (i = 0; i < this.unspentOuts.length; i++) {
|
|
||||||
selectedOuts.push(this.unspentOuts[i]);
|
|
||||||
availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value));
|
|
||||||
|
|
||||||
if (availableValue.compareTo(txValue) >= 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (availableValue.compareTo(txValue) < 0) {
|
|
||||||
throw new Error('Insufficient funds.');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var changeValue = availableValue.subtract(txValue);
|
|
||||||
|
|
||||||
var sendTx = new Bitcoin.Transaction();
|
|
||||||
|
|
||||||
for (i = 0; i < selectedOuts.length; i++) {
|
|
||||||
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendTx.addOutput(address, sendValue);
|
|
||||||
if (changeValue.compareTo(BigInteger.ZERO) > 0) {
|
|
||||||
sendTx.addOutput(this.getNextAddress(), changeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashType = 1; // SIGHASH_ALL
|
|
||||||
|
|
||||||
for (i = 0; i < sendTx.ins.length; i++) {
|
|
||||||
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType);
|
|
||||||
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash();
|
|
||||||
var signature = this.signWithKey(pubKeyHash, hash);
|
|
||||||
|
|
||||||
// Append hash type
|
|
||||||
signature.push(parseInt(hashType, 10));
|
|
||||||
|
|
||||||
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sendTx;
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.clearTransactions = function () {
|
|
||||||
this.txIndex = {};
|
|
||||||
this.unspentOuts = [];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if a pubKeyHash belongs to this wallet.
|
* Add multiple keys at once.
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.hasHash = function (hash) {
|
this.addKeys = function (keys, pubs) {
|
||||||
if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash);
|
if ("string" === typeof keys) {
|
||||||
|
keys = keys.split(',');
|
||||||
// TODO: Just create an object with base64 hashes as keys for faster lookup
|
}
|
||||||
for (var k = 0; k < this.addressHashes.length; k++) {
|
if ("string" === typeof pubs) {
|
||||||
if (this.addressHashes[k] === hash) return true;
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Wallet;
|
/**
|
||||||
})();
|
* 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 () {
|
||||||
|
if (keys.length === 0) {
|
||||||
|
this.generateAddress();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.addressPointer++;
|
||||||
|
if (!keys[this.addressPointer]) {
|
||||||
|
this.generateAddress();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// TODO(shtylman) this shit is trying to be too smart
|
||||||
|
// it is making a new address when it shouldn't
|
||||||
|
// it should just stop being so "smart" and just do what it is told
|
||||||
|
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 = conv.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 = conv.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 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.out.length; j++) {
|
||||||
|
var txout = new TransactionOut(tx.out[j]);
|
||||||
|
hash = conv.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
|
||||||
|
for (j = 0; j < tx.in.length; j++) {
|
||||||
|
var txin = new TransactionIn(tx.in[j]);
|
||||||
|
var pubkey = txin.script.simpleInPubKey();
|
||||||
|
hash = conv.bytesToBase64(util.sha256ripe160(pubkey));
|
||||||
|
for (k = 0; k < this.addressHashes.length; k++) {
|
||||||
|
if (this.addressHashes[k] === hash) {
|
||||||
|
for (var l = 0; l < this.unspentOuts.length; l++) {
|
||||||
|
if (txin.outpoint.hash == this.unspentOuts[l].tx.hash &&
|
||||||
|
txin.outpoint.index == this.unspentOuts[l].index) {
|
||||||
|
this.unspentOuts.splice(l, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index transaction
|
||||||
|
this.txIndex[tx.hash] = tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.getBalance = function () {
|
||||||
|
var balance = BigInteger.valueOf(0);
|
||||||
|
for (var i = 0; i < this.unspentOuts.length; i++) {
|
||||||
|
var txout = this.unspentOuts[i].out;
|
||||||
|
balance = balance.add(util.valueToBigInt(txout.value));
|
||||||
|
}
|
||||||
|
return balance;
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.createSend = function (address, sendValue, feeValue) {
|
||||||
|
var selectedOuts = [];
|
||||||
|
var txValue = sendValue.add(feeValue);
|
||||||
|
var availableValue = BigInteger.ZERO;
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < this.unspentOuts.length; i++) {
|
||||||
|
var txout = this.unspentOuts[i];
|
||||||
|
selectedOuts.push(txout);
|
||||||
|
availableValue = availableValue.add(util.valueToBigInt(txout.out.value));
|
||||||
|
|
||||||
|
if (availableValue.compareTo(txValue) >= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableValue.compareTo(txValue) < 0) {
|
||||||
|
throw new Error('Insufficient funds.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var changeValue = availableValue.subtract(txValue);
|
||||||
|
|
||||||
|
var sendTx = new Transaction();
|
||||||
|
|
||||||
|
for (i = 0; i < selectedOuts.length; i++) {
|
||||||
|
sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTx.addOutput(address, sendValue);
|
||||||
|
if (changeValue.compareTo(BigInteger.ZERO) > 0) {
|
||||||
|
sendTx.addOutput(this.getNextAddress(), changeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashType = 1; // SIGHASH_ALL
|
||||||
|
|
||||||
|
for (i = 0; i < sendTx.ins.length; i++) {
|
||||||
|
var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType);
|
||||||
|
var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash();
|
||||||
|
var signature = this.signWithKey(pubKeyHash, hash);
|
||||||
|
|
||||||
|
// Append hash type
|
||||||
|
signature.push(parseInt(hashType, 10));
|
||||||
|
|
||||||
|
sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendTx;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Wallet;
|
||||||
|
|
46
test/address.js
Normal file
46
test/address.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var Address = require('../').Address;
|
||||||
|
|
||||||
|
test('string', function() {
|
||||||
|
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ';
|
||||||
|
assert.equal((new Address(addr)).toString(), addr);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('valid', function() {
|
||||||
|
function validate(addr, type) {
|
||||||
|
assert.ok(Address.validate(addr, type));
|
||||||
|
};
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('invalid', function() {
|
||||||
|
function invalid(addr, type) {
|
||||||
|
assert.ok(!Address.validate(addr, type));
|
||||||
|
};
|
||||||
|
|
||||||
|
invalid('');
|
||||||
|
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe');
|
||||||
|
invalid('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'testnet');
|
||||||
|
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'prod');
|
||||||
|
|
||||||
|
// invalid base58 string
|
||||||
|
invalid('%%@');
|
||||||
|
});
|
||||||
|
|
15
test/base58.js
Normal file
15
test/base58.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var base58 = require('../').base58;
|
||||||
|
var conv = require('../').convert;
|
||||||
|
|
||||||
|
test('decode base58', function() {
|
||||||
|
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ';
|
||||||
|
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d';
|
||||||
|
assert.deepEqual(base58.decode(enc), conv.hexToBytes(hex));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('encode base58', function() {
|
||||||
|
var enc = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ';
|
||||||
|
var hex = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d';
|
||||||
|
assert.equal(base58.encode(conv.hexToBytes(hex)), enc);
|
||||||
|
});
|
24
test/convert.js
Normal file
24
test/convert.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var conv = require('../').convert;
|
||||||
|
|
||||||
|
var bytesToHex = conv.bytesToHex;
|
||||||
|
var hexToBytes = conv.hexToBytes;
|
||||||
|
|
||||||
|
test('bytesToHex', function() {
|
||||||
|
assert.equal(bytesToHex([0, 1, 2, 255]), '000102ff');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hexToBytes', function() {
|
||||||
|
assert.deepEqual(hexToBytes('000102ff'), [0, 1, 2, 255]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('bytesToHex - hexToBytes', function() {
|
||||||
|
var bytes = [];
|
||||||
|
for (var i=0 ; i<256 ; ++i) {
|
||||||
|
bytes.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hex = bytesToHex(bytes);
|
||||||
|
assert.equal(hex.length, 512);
|
||||||
|
assert.deepEqual(hexToBytes(hex), bytes);
|
||||||
|
});
|
20
test/ec.js
Normal file
20
test/ec.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var sec = require('../src/jsbn/sec');
|
||||||
|
var ecdsa = require('../').ecdsa;
|
||||||
|
|
||||||
|
var ecparams = sec('secp256k1');
|
||||||
|
|
||||||
|
test("Point multiplication", 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");
|
||||||
|
});
|
|
@ -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>
|
|
24
test/integer.js
Normal file
24
test/integer.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var BigInteger = require('../').BigInteger;
|
||||||
|
var bytesToHex = require('../').convert.bytesToHex;
|
||||||
|
|
||||||
|
test('toByteArraySigned', function() {
|
||||||
|
function hex(num) {
|
||||||
|
var bytes = BigInteger.valueOf(num).toByteArraySigned();
|
||||||
|
var hex = bytesToHex(bytes);
|
||||||
|
return '0x' + hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
35
test/key.js
Normal file
35
test/key.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var Key = require('../').Key;
|
||||||
|
var bytesToHex = require('../').convert.bytesToHex;
|
||||||
|
var hexToBytes = require('../').convert.hexToBytes;
|
||||||
|
var base58 = require('../').base58;
|
||||||
|
|
||||||
|
// get public key from private key
|
||||||
|
test('from private base58', function() {
|
||||||
|
|
||||||
|
var priv = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725';
|
||||||
|
var pub = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6';
|
||||||
|
var key = Key(hexToBytes(priv));
|
||||||
|
|
||||||
|
assert.equal(bytesToHex(key.getPub()), pub);
|
||||||
|
assert.equal(key.compressed, false);
|
||||||
|
|
||||||
|
var priv = '5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh';
|
||||||
|
var pub = '044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1';
|
||||||
|
var addr = '1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a';
|
||||||
|
var key = Key(priv);
|
||||||
|
|
||||||
|
assert.equal(key.compressed, false);
|
||||||
|
assert.equal(bytesToHex(key.getPub()), pub);
|
||||||
|
assert.equal(key.getBitcoinAddress().toString(), addr);
|
||||||
|
|
||||||
|
var priv = 'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp';
|
||||||
|
var pub = '034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa'
|
||||||
|
var addr = '1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9';
|
||||||
|
var key = Key(priv);
|
||||||
|
|
||||||
|
assert.equal(key.compressed, true);
|
||||||
|
assert.equal(bytesToHex(key.getPub()), pub);
|
||||||
|
assert.equal(key.getBitcoinAddress().toString(), addr);
|
||||||
|
});
|
||||||
|
|
1
test/mocha.opts
Normal file
1
test/mocha.opts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
--ui qunit
|
102
test/test.js
Executable file → Normal file
102
test/test.js
Executable file → Normal file
|
@ -1,84 +1,50 @@
|
||||||
//
|
var assert = require('assert');
|
||||||
// Testing elliptic curve math
|
var bitcoinjs = require('../');
|
||||||
// -----------------------------------------------------------------------------
|
var sec = require('../src/jsbn/sec');
|
||||||
module("ec");
|
var BigInteger = require('../src/jsbn/jsbn');
|
||||||
|
|
||||||
var ecparams = getSECCurveByName("secp256k1");
|
var Crypto = require('../src/crypto-js/crypto');
|
||||||
|
|
||||||
|
var SecureRandom = require('../src/jsbn/rng');
|
||||||
var rng = new SecureRandom();
|
var rng = new SecureRandom();
|
||||||
|
|
||||||
test("Classes", function () {
|
var ecparams = sec('secp256k1');
|
||||||
expect(3);
|
var ECPointFp = bitcoinjs.ECPointFp;
|
||||||
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 () {
|
test("Keys & Key Management", function () {
|
||||||
expect(5);
|
|
||||||
|
|
||||||
var s1 = new Bitcoin.ECKey();
|
var p1 = bitcoinjs.Key().getPub();
|
||||||
var p1 = s1.getPub();
|
assert.equal(p1.length, 65);
|
||||||
equals(p1.length, 65, "Public key is correct length");
|
|
||||||
|
|
||||||
var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1);
|
var p1_q = ECPointFp.decodeFrom(ecparams.getCurve(), p1);
|
||||||
ok(p1_q, "Decode point from generated bytestring");
|
assert.ok(p1_q);
|
||||||
ok(p1_q.validate(), "Is a valid public point");
|
assert.ok(p1_q.validate());
|
||||||
|
|
||||||
var p2 = Crypto.util.hexToBytes(
|
var p2 = bitcoinjs.convert.hexToBytes(
|
||||||
"0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a" +
|
"0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1a" +
|
||||||
"b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c"
|
"b5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c"
|
||||||
);
|
);
|
||||||
|
|
||||||
var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2);
|
var p2_q = ECPointFp.decodeFrom(ecparams.getCurve(), p2);
|
||||||
ok(p2_q, "Decode point from constant");
|
assert.ok(p2_q);
|
||||||
ok(p2_q.validate(), "Is a valid public point");
|
assert.ok(p2_q.validate());
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Signing and Verifying", function () {
|
test("Signing and Verifying", function () {
|
||||||
expect(7);
|
|
||||||
|
|
||||||
var s1 = new Bitcoin.ECKey();
|
var s1 = bitcoinjs.Key();
|
||||||
var sig_a = s1.sign(BigInteger.ZERO);
|
var sig_a = s1.sign(BigInteger.ZERO);
|
||||||
ok(sig_a, "Sign null");
|
assert.ok(sig_a, "Sign null");
|
||||||
equals(sig_a.length, 70, "Signature is correct length");
|
|
||||||
ok(s1.verify(BigInteger.ZERO, sig_a));
|
assert.ok(s1.verify(BigInteger.ZERO, sig_a));
|
||||||
|
|
||||||
var message = new BigInteger(1024, rng).toByteArrayUnsigned();
|
var message = new BigInteger(1024, rng).toByteArrayUnsigned();
|
||||||
var hash = Crypto.SHA256(message, {asBytes: true});
|
var hash = Crypto.SHA256(message, {asBytes: true});
|
||||||
var sig_b = s1.sign(hash);
|
var sig_b = s1.sign(hash);
|
||||||
ok(sig_b, "Sign random string");
|
assert.ok(sig_b, "Sign random string");
|
||||||
equals(sig_b.length, 70, "Signature is correct length");
|
assert.ok(s1.verify(hash, sig_b));
|
||||||
ok(s1.verify(hash, sig_b));
|
|
||||||
|
|
||||||
var message2 = Crypto.util.hexToBytes(
|
var message2 = bitcoinjs.convert.hexToBytes(
|
||||||
"12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d" +
|
"12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d" +
|
||||||
"d296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13" +
|
"d296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13" +
|
||||||
"1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66" +
|
"1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66" +
|
||||||
|
@ -86,27 +52,15 @@ test("Signing and Verifying", function () {
|
||||||
"3d82507b932b84e4"
|
"3d82507b932b84e4"
|
||||||
);
|
);
|
||||||
var hash2 = Crypto.SHA256(message2, {asBytes: true});
|
var hash2 = Crypto.SHA256(message2, {asBytes: true});
|
||||||
var sig_c = Crypto.util.hexToBytes(
|
var sig_c = bitcoinjs.convert.hexToBytes(
|
||||||
"3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3" +
|
"3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3" +
|
||||||
"79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3" +
|
"79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3" +
|
||||||
"f273ab082801a95506c4"
|
"f273ab082801a95506c4"
|
||||||
);
|
);
|
||||||
var s2 = Crypto.util.hexToBytes(
|
var s2 = bitcoinjs.convert.hexToBytes(
|
||||||
"045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51" +
|
"045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51" +
|
||||||
"b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828" +
|
"b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828" +
|
||||||
"609a978528"
|
"609a978528"
|
||||||
);
|
);
|
||||||
ok(Bitcoin.ECDSA.verify(hash2, sig_c, s2), "Verify constant signature");
|
assert.ok(bitcoinjs.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");
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue