12620 lines
561 KiB
HTML
12620 lines
561 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
<title>Document</title>
|
|
|
|
<style>
|
|
.tradebox {
|
|
display: flex;
|
|
flex: 1;
|
|
}
|
|
ul {
|
|
list-style-type: none;
|
|
}
|
|
ul.panel-box > li {
|
|
width: auto;
|
|
height: auto;
|
|
padding: 10px 10px;
|
|
margin: 10px 10px;
|
|
}
|
|
.buybox > ul.panel-box > li {
|
|
background-color: chocolate;
|
|
}
|
|
.sellbox > ul.panel-box > li {
|
|
background-color: cornflowerblue;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div class="container">
|
|
|
|
<div id="withdraw_deposit_table"></div>
|
|
|
|
<h5>Indexed DB</h5>
|
|
<div class="box">
|
|
<label for="ask_flo_addr">Enter Your FLO Address: </label>
|
|
<input type="text" id="ask_flo_addr" class="form-control">
|
|
<input type="button" id="ask_flo_addr_btn" value="Submit">
|
|
<br>
|
|
<div id="localbitcoinuserdiv"></div>
|
|
</div>
|
|
|
|
<div class="generate_new_keys_div">
|
|
<h5>Generate New FLO Keys here:</h5>
|
|
<button id="new_flo_keys">Generate FLO Keys</button>
|
|
<ul id="new_flo_keys_ul"></ul>
|
|
</div>
|
|
|
|
<h5>Asset Box</h5>
|
|
<div class="box">
|
|
<div id="asset_box"></div>
|
|
</div>
|
|
|
|
<h5>Sync</h5>
|
|
<div class="box">
|
|
<div id="syncNodes"></div>
|
|
<div id="datablocksdiv"></div>
|
|
</div>
|
|
|
|
<h5>Buy--Sell</h5>
|
|
<div class="box tradebox" id="tradebox">
|
|
<!-- <div class="buybox">
|
|
<strong>Buy BTC</strong>
|
|
<ul class="panel-box trade_amount_box" id="buyul">
|
|
<li>CASH 10000</li>
|
|
<li>CASH 50000</li>
|
|
<li>CASH 100000</li>
|
|
</ul>
|
|
</div>
|
|
<div class="sellbox">
|
|
<strong>Sell BTC</strong>
|
|
<ul class="panel-box trade_amount_box" id="sellul">
|
|
<li>CASH 10000</li>
|
|
<li>CASH 50000</li>
|
|
<li>CASH 100000</li>
|
|
</ul>
|
|
</div> -->
|
|
</div>
|
|
|
|
<div id="output_div"></div>
|
|
|
|
</div>
|
|
|
|
<script type="text/javascript">
|
|
//crypto-sha256-hmac.js
|
|
/*
|
|
* Crypto-JS v2.5.4
|
|
* http://code.google.com/p/crypto-js/
|
|
* (c) 2009-2012 by Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
|
|
function ascii_to_hexa(str) {
|
|
var arr1 = [];
|
|
for (var n = 0, l = str.length; n < l; n++) {
|
|
var hex = Number(str.charCodeAt(n)).toString(16);
|
|
arr1.push(hex);
|
|
}
|
|
return arr1.join('');
|
|
}
|
|
|
|
//ripemd160.js
|
|
/*
|
|
CryptoJS v3.1.2
|
|
code.google.com/p/crypto-js
|
|
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
|
code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
/** @preserve
|
|
(c) 2012 by Cédric Mesnil. All rights reserved.
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
// Constants table
|
|
var zl = [
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
|
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
|
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
|
|
];
|
|
var zr = [
|
|
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
|
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
|
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
|
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
|
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
|
|
];
|
|
var sl = [
|
|
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
|
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
|
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
|
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
|
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
|
|
];
|
|
var sr = [
|
|
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
|
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
|
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
|
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
|
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
|
|
];
|
|
|
|
var hl = [0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E];
|
|
var hr = [0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000];
|
|
|
|
var bytesToWords = function (bytes) {
|
|
var words = [];
|
|
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) {
|
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
|
}
|
|
return words;
|
|
};
|
|
|
|
var wordsToBytes = function (words) {
|
|
var bytes = [];
|
|
for (var b = 0; b < words.length * 32; b += 8) {
|
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
|
}
|
|
return bytes;
|
|
};
|
|
|
|
var processBlock = function (H, M, offset) {
|
|
|
|
// Swap endian
|
|
for (var i = 0; i < 16; i++) {
|
|
var offset_i = offset + i;
|
|
var M_offset_i = M[offset_i];
|
|
|
|
// Swap
|
|
M[offset_i] = (
|
|
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
|
|
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
|
|
);
|
|
}
|
|
|
|
// Working variables
|
|
var al, bl, cl, dl, el;
|
|
var ar, br, cr, dr, er;
|
|
|
|
ar = al = H[0];
|
|
br = bl = H[1];
|
|
cr = cl = H[2];
|
|
dr = dl = H[3];
|
|
er = el = H[4];
|
|
// Computation
|
|
var t;
|
|
for (var i = 0; i < 80; i += 1) {
|
|
t = (al + M[offset + zl[i]]) | 0;
|
|
if (i < 16) {
|
|
t += f1(bl, cl, dl) + hl[0];
|
|
} else if (i < 32) {
|
|
t += f2(bl, cl, dl) + hl[1];
|
|
} else if (i < 48) {
|
|
t += f3(bl, cl, dl) + hl[2];
|
|
} else if (i < 64) {
|
|
t += f4(bl, cl, dl) + hl[3];
|
|
} else { // if (i<80) {
|
|
t += f5(bl, cl, dl) + hl[4];
|
|
}
|
|
t = t | 0;
|
|
t = rotl(t, sl[i]);
|
|
t = (t + el) | 0;
|
|
al = el;
|
|
el = dl;
|
|
dl = rotl(cl, 10);
|
|
cl = bl;
|
|
bl = t;
|
|
|
|
t = (ar + M[offset + zr[i]]) | 0;
|
|
if (i < 16) {
|
|
t += f5(br, cr, dr) + hr[0];
|
|
} else if (i < 32) {
|
|
t += f4(br, cr, dr) + hr[1];
|
|
} else if (i < 48) {
|
|
t += f3(br, cr, dr) + hr[2];
|
|
} else if (i < 64) {
|
|
t += f2(br, cr, dr) + hr[3];
|
|
} else { // if (i<80) {
|
|
t += f1(br, cr, dr) + hr[4];
|
|
}
|
|
t = t | 0;
|
|
t = rotl(t, sr[i]);
|
|
t = (t + er) | 0;
|
|
ar = er;
|
|
er = dr;
|
|
dr = rotl(cr, 10);
|
|
cr = br;
|
|
br = t;
|
|
}
|
|
// Intermediate hash value
|
|
t = (H[1] + cl + dr) | 0;
|
|
H[1] = (H[2] + dl + er) | 0;
|
|
H[2] = (H[3] + el + ar) | 0;
|
|
H[3] = (H[4] + al + br) | 0;
|
|
H[4] = (H[0] + bl + cr) | 0;
|
|
H[0] = t;
|
|
};
|
|
|
|
function f1(x, y, z) {
|
|
return ((x) ^ (y) ^ (z));
|
|
}
|
|
|
|
function f2(x, y, z) {
|
|
return (((x) & (y)) | ((~x) & (z)));
|
|
}
|
|
|
|
function f3(x, y, z) {
|
|
return (((x) | (~(y))) ^ (z));
|
|
}
|
|
|
|
function f4(x, y, z) {
|
|
return (((x) & (z)) | ((y) & (~(z))));
|
|
}
|
|
|
|
function f5(x, y, z) {
|
|
return ((x) ^ ((y) | (~(z))));
|
|
}
|
|
|
|
function rotl(x, n) {
|
|
return (x << n) | (x >>> (32 - n));
|
|
}
|
|
|
|
function ripemd160(message) {
|
|
var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
|
|
|
|
var m = bytesToWords(message);
|
|
|
|
var nBitsLeft = message.length * 8;
|
|
var nBitsTotal = message.length * 8;
|
|
|
|
// Add padding
|
|
m[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
|
m[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
|
(((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) |
|
|
(((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00)
|
|
);
|
|
|
|
for (var i = 0; i < m.length; i += 16) {
|
|
processBlock(H, m, i);
|
|
}
|
|
|
|
// Swap endian
|
|
for (var i = 0; i < 5; i++) {
|
|
// Shortcut
|
|
var H_i = H[i];
|
|
|
|
// Swap
|
|
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
|
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
|
}
|
|
|
|
var digestbytes = wordsToBytes(H);
|
|
return digestbytes;
|
|
}
|
|
</script>
|
|
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.5.4 Crypto.js
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
if (typeof Crypto == "undefined" || !Crypto.util) {
|
|
(function () {
|
|
|
|
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
// Global Crypto object
|
|
var Crypto = window.Crypto = {};
|
|
|
|
// Crypto utilities
|
|
var util = Crypto.util = {
|
|
|
|
// Bit-wise rotate left
|
|
rotl: function (n, b) {
|
|
return (n << b) | (n >>> (32 - b));
|
|
},
|
|
|
|
// Bit-wise rotate right
|
|
rotr: function (n, b) {
|
|
return (n << (32 - b)) | (n >>> b);
|
|
},
|
|
|
|
// Swap big-endian to little-endian and vice versa
|
|
endian: function (n) {
|
|
|
|
// If number given, swap endian
|
|
if (n.constructor == Number) {
|
|
return util.rotl(n, 8) & 0x00FF00FF |
|
|
util.rotl(n, 24) & 0xFF00FF00;
|
|
}
|
|
|
|
// Else, assume array and swap all items
|
|
for (var i = 0; i < n.length; i++)
|
|
n[i] = util.endian(n[i]);
|
|
return n;
|
|
|
|
},
|
|
|
|
// Generate an array of any length of random bytes
|
|
randomBytes: function (n) {
|
|
for (var bytes = []; n > 0; n--)
|
|
bytes.push(Math.floor(Math.random() * 256));
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to big-endian 32-bit words
|
|
bytesToWords: function (bytes) {
|
|
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
|
|
words[b >>> 5] |= (bytes[i] & 0xFF) << (24 - b % 32);
|
|
return words;
|
|
},
|
|
|
|
// Convert big-endian 32-bit words to a byte array
|
|
wordsToBytes: function (words) {
|
|
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to a hex string
|
|
bytesToHex: function (bytes) {
|
|
for (var hex = [], i = 0; i < bytes.length; i++) {
|
|
hex.push((bytes[i] >>> 4).toString(16));
|
|
hex.push((bytes[i] & 0xF).toString(16));
|
|
}
|
|
return hex.join("");
|
|
},
|
|
|
|
// Convert a hex string to a byte array
|
|
hexToBytes: function (hex) {
|
|
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to a base-64 string
|
|
bytesToBase64: function (bytes) {
|
|
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) {
|
|
// 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 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) & 0xFF);
|
|
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("");
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
}
|
|
</script>
|
|
|
|
<!-- SHA1 -->
|
|
<script type="text/javascript">
|
|
//Adding SHA1 to fix basic PKBDF2
|
|
/*
|
|
* Crypto-JS v2.5.4
|
|
* http://code.google.com/p/crypto-js/
|
|
* (c) 2009-2012 by Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
// Public API
|
|
var SHA1 = C.SHA1 = function (message, options) {
|
|
var digestbytes = util.wordsToBytes(SHA1._sha1(message));
|
|
return options && options.asBytes ? digestbytes :
|
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
|
util.bytesToHex(digestbytes);
|
|
};
|
|
|
|
// The core
|
|
SHA1._sha1 = function (message) {
|
|
|
|
// Convert to byte array
|
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
/* else, assume byte array already */
|
|
|
|
var m = util.bytesToWords(message),
|
|
l = message.length * 8,
|
|
w = [],
|
|
H0 = 1732584193,
|
|
H1 = -271733879,
|
|
H2 = -1732584194,
|
|
H3 = 271733878,
|
|
H4 = -1009589776;
|
|
|
|
// Padding
|
|
m[l >> 5] |= 0x80 << (24 - l % 32);
|
|
m[((l + 64 >>> 9) << 4) + 15] = l;
|
|
|
|
for (var i = 0; i < m.length; i += 16) {
|
|
|
|
var a = H0,
|
|
b = H1,
|
|
c = H2,
|
|
d = H3,
|
|
e = H4;
|
|
|
|
for (var j = 0; j < 80; j++) {
|
|
|
|
if (j < 16) w[j] = m[i + j];
|
|
else {
|
|
var n = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
|
|
w[j] = (n << 1) | (n >>> 31);
|
|
}
|
|
|
|
var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
|
|
j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
|
|
j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
|
|
j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
|
|
(H1 ^ H2 ^ H3) - 899497514);
|
|
|
|
H4 = H3;
|
|
H3 = H2;
|
|
H2 = (H1 << 30) | (H1 >>> 2);
|
|
H1 = H0;
|
|
H0 = t;
|
|
|
|
}
|
|
|
|
H0 += a;
|
|
H1 += b;
|
|
H2 += c;
|
|
H3 += d;
|
|
H4 += e;
|
|
|
|
}
|
|
|
|
return [H0, H1, H2, H3, H4];
|
|
|
|
};
|
|
|
|
// Package private blocksize
|
|
SHA1._blocksize = 16;
|
|
|
|
SHA1._digestsize = 20;
|
|
|
|
})();
|
|
</script>
|
|
|
|
<!-- HMAC -->
|
|
<script type="text/javascript">
|
|
//Added to make PKBDF2 work
|
|
/*
|
|
* Crypto-JS v2.5.4
|
|
* http://code.google.com/p/crypto-js/
|
|
* (c) 2009-2012 by Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
C.HMAC = function (hasher, message, key, options) {
|
|
|
|
// Convert to byte arrays
|
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
if (key.constructor == String) key = UTF8.stringToBytes(key);
|
|
/* else, assume byte arrays already */
|
|
|
|
// Allow arbitrary length keys
|
|
if (key.length > hasher._blocksize * 4)
|
|
key = hasher(key, {
|
|
asBytes: true
|
|
});
|
|
|
|
// XOR keys with pad constants
|
|
var okey = key.slice(0),
|
|
ikey = key.slice(0);
|
|
for (var i = 0; i < hasher._blocksize * 4; i++) {
|
|
okey[i] ^= 0x5C;
|
|
ikey[i] ^= 0x36;
|
|
}
|
|
|
|
var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), {
|
|
asBytes: true
|
|
})), {
|
|
asBytes: true
|
|
});
|
|
|
|
return options && options.asBytes ? hmacbytes :
|
|
options && options.asString ? Binary.bytesToString(hmacbytes) :
|
|
util.bytesToHex(hmacbytes);
|
|
|
|
};
|
|
|
|
})();
|
|
</script>
|
|
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.5.4 SHA256.js
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
// Constants
|
|
var K = [0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
|
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
|
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
|
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
|
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
|
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
|
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
|
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
|
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
|
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
|
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
|
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
|
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
|
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
|
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
|
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
|
|
];
|
|
|
|
// Public API
|
|
var SHA256 = C.SHA256 = function (message, options) {
|
|
var digestbytes = util.wordsToBytes(SHA256._sha256(message));
|
|
return options && options.asBytes ? digestbytes :
|
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
|
util.bytesToHex(digestbytes);
|
|
};
|
|
|
|
// The core
|
|
SHA256._sha256 = function (message) {
|
|
|
|
// Convert to byte array
|
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
/* else, assume byte array already */
|
|
|
|
var m = util.bytesToWords(message),
|
|
l = message.length * 8,
|
|
H = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
|
],
|
|
w = [],
|
|
a, b, c, d, e, f, g, h, i, j,
|
|
t1, t2;
|
|
|
|
// Padding
|
|
m[l >> 5] |= 0x80 << (24 - l % 32);
|
|
m[((l + 64 >> 9) << 4) + 15] = l;
|
|
|
|
for (var i = 0; i < m.length; i += 16) {
|
|
|
|
a = H[0];
|
|
b = H[1];
|
|
c = H[2];
|
|
d = H[3];
|
|
e = H[4];
|
|
f = H[5];
|
|
g = H[6];
|
|
h = H[7];
|
|
|
|
for (var j = 0; j < 64; j++) {
|
|
|
|
if (j < 16) w[j] = m[j + i];
|
|
else {
|
|
|
|
var gamma0x = w[j - 15],
|
|
gamma1x = w[j - 2],
|
|
gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
|
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
|
(gamma0x >>> 3),
|
|
gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
|
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
|
(gamma1x >>> 10);
|
|
|
|
w[j] = gamma0 + (w[j - 7] >>> 0) +
|
|
gamma1 + (w[j - 16] >>> 0);
|
|
|
|
}
|
|
|
|
var ch = e & f ^ ~e & g,
|
|
maj = a & b ^ a & c ^ b & c,
|
|
sigma0 = ((a << 30) | (a >>> 2)) ^
|
|
((a << 19) | (a >>> 13)) ^
|
|
((a << 10) | (a >>> 22)),
|
|
sigma1 = ((e << 26) | (e >>> 6)) ^
|
|
((e << 21) | (e >>> 11)) ^
|
|
((e << 7) | (e >>> 25));
|
|
|
|
|
|
t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0);
|
|
t2 = sigma0 + maj;
|
|
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = (d + t1) >>> 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = (t1 + t2) >>> 0;
|
|
|
|
}
|
|
|
|
H[0] += a;
|
|
H[1] += b;
|
|
H[2] += c;
|
|
H[3] += d;
|
|
H[4] += e;
|
|
H[5] += f;
|
|
H[6] += g;
|
|
H[7] += h;
|
|
|
|
}
|
|
|
|
return H;
|
|
|
|
};
|
|
|
|
// Package private blocksize
|
|
SHA256._blocksize = 16;
|
|
|
|
SHA256._digestsize = 32;
|
|
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.5.4 PBKDF2.js
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
C.PBKDF2 = function (password, salt, keylen, options) {
|
|
|
|
// Convert to byte arrays
|
|
if (password.constructor == String) password = UTF8.stringToBytes(password);
|
|
if (salt.constructor == String) salt = UTF8.stringToBytes(salt);
|
|
/* else, assume byte arrays already */
|
|
|
|
// Defaults
|
|
var hasher = options && options.hasher || C.SHA1,
|
|
iterations = options && options.iterations || 1;
|
|
|
|
// Pseudo-random function
|
|
function PRF(password, salt) {
|
|
return C.HMAC(hasher, salt, password, {
|
|
asBytes: true
|
|
});
|
|
}
|
|
|
|
// Generate key
|
|
var derivedKeyBytes = [],
|
|
blockindex = 1;
|
|
while (derivedKeyBytes.length < keylen) {
|
|
var block = PRF(password, salt.concat(util.wordsToBytes([blockindex])));
|
|
for (var u = block, i = 1; i < iterations; i++) {
|
|
u = PRF(password, u);
|
|
for (var j = 0; j < block.length; j++) block[j] ^= u[j];
|
|
}
|
|
derivedKeyBytes = derivedKeyBytes.concat(block);
|
|
blockindex++;
|
|
}
|
|
|
|
// Truncate excess bytes
|
|
derivedKeyBytes.length = keylen;
|
|
|
|
return options && options.asBytes ? derivedKeyBytes :
|
|
options && options.asString ? Binary.bytesToString(derivedKeyBytes) :
|
|
util.bytesToHex(derivedKeyBytes);
|
|
|
|
};
|
|
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.5.4 HMAC.js
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
C.HMAC = function (hasher, message, key, options) {
|
|
|
|
// Convert to byte arrays
|
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
if (key.constructor == String) key = UTF8.stringToBytes(key);
|
|
/* else, assume byte arrays already */
|
|
|
|
// Allow arbitrary length keys
|
|
if (key.length > hasher._blocksize * 4)
|
|
key = hasher(key, {
|
|
asBytes: true
|
|
});
|
|
|
|
// XOR keys with pad constants
|
|
var okey = key.slice(0),
|
|
ikey = key.slice(0);
|
|
for (var i = 0; i < hasher._blocksize * 4; i++) {
|
|
okey[i] ^= 0x5C;
|
|
ikey[i] ^= 0x36;
|
|
}
|
|
|
|
var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), {
|
|
asBytes: true
|
|
})), {
|
|
asBytes: true
|
|
});
|
|
|
|
return options && options.asBytes ? hmacbytes :
|
|
options && options.asString ? Binary.bytesToString(hmacbytes) :
|
|
util.bytesToHex(hmacbytes);
|
|
|
|
};
|
|
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.5.4 AES.js
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*/
|
|
(function () {
|
|
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8;
|
|
|
|
// Precomputed SBOX
|
|
var SBOX = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
|
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
|
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
|
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
|
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
|
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
|
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
|
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
|
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
|
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
|
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
|
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
|
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
|
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
|
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
|
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
|
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
|
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
|
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
|
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
|
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
|
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
|
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
|
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
|
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
|
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
|
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
|
];
|
|
|
|
// Compute inverse SBOX lookup table
|
|
for (var INVSBOX = [], i = 0; i < 256; i++) INVSBOX[SBOX[i]] = i;
|
|
|
|
// Compute multiplication in GF(2^8) lookup tables
|
|
var MULT2 = [],
|
|
MULT3 = [],
|
|
MULT9 = [],
|
|
MULTB = [],
|
|
MULTD = [],
|
|
MULTE = [];
|
|
|
|
function xtime(a, b) {
|
|
for (var result = 0, i = 0; i < 8; i++) {
|
|
if (b & 1) result ^= a;
|
|
var hiBitSet = a & 0x80;
|
|
a = (a << 1) & 0xFF;
|
|
if (hiBitSet) a ^= 0x1b;
|
|
b >>>= 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
for (var i = 0; i < 256; i++) {
|
|
MULT2[i] = xtime(i, 2);
|
|
MULT3[i] = xtime(i, 3);
|
|
MULT9[i] = xtime(i, 9);
|
|
MULTB[i] = xtime(i, 0xB);
|
|
MULTD[i] = xtime(i, 0xD);
|
|
MULTE[i] = xtime(i, 0xE);
|
|
}
|
|
|
|
// Precomputed RCon lookup
|
|
var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
|
|
|
|
// Inner state
|
|
var state = [
|
|
[],
|
|
[],
|
|
[],
|
|
[]
|
|
],
|
|
keylength,
|
|
nrounds,
|
|
keyschedule;
|
|
|
|
var AES = C.AES = {
|
|
|
|
/**
|
|
* Public API
|
|
*/
|
|
|
|
encrypt: function (message, password, options) {
|
|
|
|
options = options || {};
|
|
|
|
// Determine mode
|
|
var mode = options.mode || new C.mode.OFB;
|
|
|
|
// Allow mode to override options
|
|
if (mode.fixOptions) mode.fixOptions(options);
|
|
|
|
var
|
|
|
|
// Convert to bytes if message is a string
|
|
m = (
|
|
message.constructor == String ?
|
|
UTF8.stringToBytes(message) :
|
|
message
|
|
),
|
|
|
|
// Generate random IV
|
|
iv = options.iv || util.randomBytes(AES._blocksize * 4),
|
|
|
|
// Generate key
|
|
k = (
|
|
password.constructor == String ?
|
|
// Derive key from pass-phrase
|
|
C.PBKDF2(password, iv, 32, {
|
|
asBytes: true
|
|
}) :
|
|
// else, assume byte array representing cryptographic key
|
|
password
|
|
);
|
|
|
|
// Encrypt
|
|
AES._init(k);
|
|
mode.encrypt(AES, m, iv);
|
|
|
|
// Return ciphertext
|
|
m = options.iv ? m : iv.concat(m);
|
|
return (options && options.asBytes) ? m : util.bytesToBase64(m);
|
|
|
|
},
|
|
|
|
decrypt: function (ciphertext, password, options) {
|
|
|
|
options = options || {};
|
|
|
|
// Determine mode
|
|
var mode = options.mode || new C.mode.OFB;
|
|
|
|
// Allow mode to override options
|
|
if (mode.fixOptions) mode.fixOptions(options);
|
|
|
|
var
|
|
|
|
// Convert to bytes if ciphertext is a string
|
|
c = (
|
|
ciphertext.constructor == String ?
|
|
util.base64ToBytes(ciphertext) :
|
|
ciphertext
|
|
),
|
|
|
|
// Separate IV and message
|
|
iv = options.iv || c.splice(0, AES._blocksize * 4),
|
|
|
|
// Generate key
|
|
k = (
|
|
password.constructor == String ?
|
|
// Derive key from pass-phrase
|
|
C.PBKDF2(password, iv, 32, {
|
|
asBytes: true
|
|
}) :
|
|
// else, assume byte array representing cryptographic key
|
|
password
|
|
);
|
|
|
|
// Decrypt
|
|
AES._init(k);
|
|
mode.decrypt(AES, c, iv);
|
|
|
|
// Return plaintext
|
|
return (options && options.asBytes) ? c : UTF8.bytesToString(c);
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
* Package private methods and properties
|
|
*/
|
|
|
|
_blocksize: 4,
|
|
|
|
_encryptblock: function (m, offset) {
|
|
|
|
// Set input
|
|
for (var row = 0; row < AES._blocksize; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = m[offset + col * 4 + row];
|
|
}
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[col][row];
|
|
}
|
|
|
|
for (var round = 1; round < nrounds; round++) {
|
|
|
|
// Sub bytes
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = SBOX[state[row][col]];
|
|
}
|
|
|
|
// Shift rows
|
|
state[1].push(state[1].shift());
|
|
state[2].push(state[2].shift());
|
|
state[2].push(state[2].shift());
|
|
state[3].unshift(state[3].pop());
|
|
|
|
// Mix columns
|
|
for (var col = 0; col < 4; col++) {
|
|
|
|
var s0 = state[0][col],
|
|
s1 = state[1][col],
|
|
s2 = state[2][col],
|
|
s3 = state[3][col];
|
|
|
|
state[0][col] = MULT2[s0] ^ MULT3[s1] ^ s2 ^ s3;
|
|
state[1][col] = s0 ^ MULT2[s1] ^ MULT3[s2] ^ s3;
|
|
state[2][col] = s0 ^ s1 ^ MULT2[s2] ^ MULT3[s3];
|
|
state[3][col] = MULT3[s0] ^ s1 ^ s2 ^ MULT2[s3];
|
|
|
|
}
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[round * 4 + col][row];
|
|
}
|
|
|
|
}
|
|
|
|
// Sub bytes
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = SBOX[state[row][col]];
|
|
}
|
|
|
|
// Shift rows
|
|
state[1].push(state[1].shift());
|
|
state[2].push(state[2].shift());
|
|
state[2].push(state[2].shift());
|
|
state[3].unshift(state[3].pop());
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[nrounds * 4 + col][row];
|
|
}
|
|
|
|
// Set output
|
|
for (var row = 0; row < AES._blocksize; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
m[offset + col * 4 + row] = state[row][col];
|
|
}
|
|
|
|
},
|
|
|
|
_decryptblock: function (c, offset) {
|
|
|
|
// Set input
|
|
for (var row = 0; row < AES._blocksize; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = c[offset + col * 4 + row];
|
|
}
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[nrounds * 4 + col][row];
|
|
}
|
|
|
|
for (var round = 1; round < nrounds; round++) {
|
|
|
|
// Inv shift rows
|
|
state[1].unshift(state[1].pop());
|
|
state[2].push(state[2].shift());
|
|
state[2].push(state[2].shift());
|
|
state[3].push(state[3].shift());
|
|
|
|
// Inv sub bytes
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = INVSBOX[state[row][col]];
|
|
}
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[(nrounds - round) * 4 + col][row];
|
|
}
|
|
|
|
// Inv mix columns
|
|
for (var col = 0; col < 4; col++) {
|
|
|
|
var s0 = state[0][col],
|
|
s1 = state[1][col],
|
|
s2 = state[2][col],
|
|
s3 = state[3][col];
|
|
|
|
state[0][col] = MULTE[s0] ^ MULTB[s1] ^ MULTD[s2] ^ MULT9[s3];
|
|
state[1][col] = MULT9[s0] ^ MULTE[s1] ^ MULTB[s2] ^ MULTD[s3];
|
|
state[2][col] = MULTD[s0] ^ MULT9[s1] ^ MULTE[s2] ^ MULTB[s3];
|
|
state[3][col] = MULTB[s0] ^ MULTD[s1] ^ MULT9[s2] ^ MULTE[s3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Inv shift rows
|
|
state[1].unshift(state[1].pop());
|
|
state[2].push(state[2].shift());
|
|
state[2].push(state[2].shift());
|
|
state[3].push(state[3].shift());
|
|
|
|
// Inv sub bytes
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] = INVSBOX[state[row][col]];
|
|
}
|
|
|
|
// Add round key
|
|
for (var row = 0; row < 4; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
state[row][col] ^= keyschedule[col][row];
|
|
}
|
|
|
|
// Set output
|
|
for (var row = 0; row < AES._blocksize; row++) {
|
|
for (var col = 0; col < 4; col++)
|
|
c[offset + col * 4 + row] = state[row][col];
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
* Private methods
|
|
*/
|
|
|
|
_init: function (k) {
|
|
keylength = k.length / 4;
|
|
nrounds = keylength + 6;
|
|
AES._keyexpansion(k);
|
|
},
|
|
|
|
// Generate a key schedule
|
|
_keyexpansion: function (k) {
|
|
|
|
keyschedule = [];
|
|
|
|
for (var row = 0; row < keylength; row++) {
|
|
keyschedule[row] = [
|
|
k[row * 4],
|
|
k[row * 4 + 1],
|
|
k[row * 4 + 2],
|
|
k[row * 4 + 3]
|
|
];
|
|
}
|
|
|
|
for (var row = keylength; row < AES._blocksize * (nrounds + 1); row++) {
|
|
|
|
var temp = [
|
|
keyschedule[row - 1][0],
|
|
keyschedule[row - 1][1],
|
|
keyschedule[row - 1][2],
|
|
keyschedule[row - 1][3]
|
|
];
|
|
|
|
if (row % keylength == 0) {
|
|
|
|
// Rot word
|
|
temp.push(temp.shift());
|
|
|
|
// Sub word
|
|
temp[0] = SBOX[temp[0]];
|
|
temp[1] = SBOX[temp[1]];
|
|
temp[2] = SBOX[temp[2]];
|
|
temp[3] = SBOX[temp[3]];
|
|
|
|
temp[0] ^= RCON[row / keylength];
|
|
|
|
} else if (keylength > 6 && row % keylength == 4) {
|
|
|
|
// Sub word
|
|
temp[0] = SBOX[temp[0]];
|
|
temp[1] = SBOX[temp[1]];
|
|
temp[2] = SBOX[temp[2]];
|
|
temp[3] = SBOX[temp[3]];
|
|
|
|
}
|
|
|
|
keyschedule[row] = [
|
|
keyschedule[row - keylength][0] ^ temp[0],
|
|
keyschedule[row - keylength][1] ^ temp[1],
|
|
keyschedule[row - keylength][2] ^ temp[2],
|
|
keyschedule[row - keylength][3] ^ temp[3]
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS 2.5.4 BlockModes.js
|
|
* contribution from Simon Greatrix
|
|
*/
|
|
|
|
(function (C) {
|
|
|
|
// Create pad namespace
|
|
var C_pad = C.pad = {};
|
|
|
|
// Calculate the number of padding bytes required.
|
|
function _requiredPadding(cipher, message) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
var reqd = blockSizeInBytes - message.length % blockSizeInBytes;
|
|
return reqd;
|
|
}
|
|
|
|
// Remove padding when the final byte gives the number of padding bytes.
|
|
var _unpadLength = function (cipher, message, alg, padding) {
|
|
var pad = message.pop();
|
|
if (pad == 0) {
|
|
throw new Error("Invalid zero-length padding specified for " + alg +
|
|
". Wrong cipher specification or key used?");
|
|
}
|
|
var maxPad = cipher._blocksize * 4;
|
|
if (pad > maxPad) {
|
|
throw new Error("Invalid padding length of " + pad +
|
|
" specified for " + alg +
|
|
". Wrong cipher specification or key used?");
|
|
}
|
|
for (var i = 1; i < pad; i++) {
|
|
var b = message.pop();
|
|
if (padding != undefined && padding != b) {
|
|
throw new Error("Invalid padding byte of 0x" + b.toString(16) +
|
|
" specified for " + alg +
|
|
". Wrong cipher specification or key used?");
|
|
}
|
|
}
|
|
};
|
|
|
|
// No-operation padding, used for stream ciphers
|
|
C_pad.NoPadding = {
|
|
pad: function (cipher, message) {},
|
|
unpad: function (cipher, message) {}
|
|
};
|
|
|
|
// Zero Padding.
|
|
//
|
|
// If the message is not an exact number of blocks, the final block is
|
|
// completed with 0x00 bytes. There is no unpadding.
|
|
C_pad.ZeroPadding = {
|
|
pad: function (cipher, message) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
var reqd = message.length % blockSizeInBytes;
|
|
if (reqd != 0) {
|
|
for (reqd = blockSizeInBytes - reqd; reqd > 0; reqd--) {
|
|
message.push(0x00);
|
|
}
|
|
}
|
|
},
|
|
|
|
unpad: function (cipher, message) {
|
|
while (message[message.length - 1] == 0) {
|
|
message.pop();
|
|
}
|
|
}
|
|
};
|
|
|
|
// ISO/IEC 7816-4 padding.
|
|
//
|
|
// Pads the plain text with an 0x80 byte followed by as many 0x00
|
|
// bytes are required to complete the block.
|
|
C_pad.iso7816 = {
|
|
pad: function (cipher, message) {
|
|
var reqd = _requiredPadding(cipher, message);
|
|
message.push(0x80);
|
|
for (; reqd > 1; reqd--) {
|
|
message.push(0x00);
|
|
}
|
|
},
|
|
|
|
unpad: function (cipher, message) {
|
|
var padLength;
|
|
for (padLength = cipher._blocksize * 4; padLength > 0; padLength--) {
|
|
var b = message.pop();
|
|
if (b == 0x80) return;
|
|
if (b != 0x00) {
|
|
throw new Error("ISO-7816 padding byte must be 0, not 0x" + b.toString(16) +
|
|
". Wrong cipher specification or key used?");
|
|
}
|
|
}
|
|
throw new Error(
|
|
"ISO-7816 padded beyond cipher block size. Wrong cipher specification or key used?"
|
|
);
|
|
}
|
|
};
|
|
|
|
// ANSI X.923 padding
|
|
//
|
|
// The final block is padded with zeros except for the last byte of the
|
|
// last block which contains the number of padding bytes.
|
|
C_pad.ansix923 = {
|
|
pad: function (cipher, message) {
|
|
var reqd = _requiredPadding(cipher, message);
|
|
for (var i = 1; i < reqd; i++) {
|
|
message.push(0x00);
|
|
}
|
|
message.push(reqd);
|
|
},
|
|
|
|
unpad: function (cipher, message) {
|
|
_unpadLength(cipher, message, "ANSI X.923", 0);
|
|
}
|
|
};
|
|
|
|
// ISO 10126
|
|
//
|
|
// The final block is padded with random bytes except for the last
|
|
// byte of the last block which contains the number of padding bytes.
|
|
C_pad.iso10126 = {
|
|
pad: function (cipher, message) {
|
|
var reqd = _requiredPadding(cipher, message);
|
|
for (var i = 1; i < reqd; i++) {
|
|
message.push(Math.floor(Math.random() * 256));
|
|
}
|
|
message.push(reqd);
|
|
},
|
|
|
|
unpad: function (cipher, message) {
|
|
_unpadLength(cipher, message, "ISO 10126", undefined);
|
|
}
|
|
};
|
|
|
|
// PKCS7 padding
|
|
//
|
|
// PKCS7 is described in RFC 5652. Padding is in whole bytes. The
|
|
// value of each added byte is the number of bytes that are added,
|
|
// i.e. N bytes, each of value N are added.
|
|
C_pad.pkcs7 = {
|
|
pad: function (cipher, message) {
|
|
var reqd = _requiredPadding(cipher, message);
|
|
for (var i = 0; i < reqd; i++) {
|
|
message.push(reqd);
|
|
}
|
|
},
|
|
|
|
unpad: function (cipher, message) {
|
|
_unpadLength(cipher, message, "PKCS 7", message[message.length - 1]);
|
|
}
|
|
};
|
|
|
|
// Create mode namespace
|
|
var C_mode = C.mode = {};
|
|
|
|
/**
|
|
* Mode base "class".
|
|
*/
|
|
var Mode = C_mode.Mode = function (padding) {
|
|
if (padding) {
|
|
this._padding = padding;
|
|
}
|
|
};
|
|
|
|
Mode.prototype = {
|
|
encrypt: function (cipher, m, iv) {
|
|
this._padding.pad(cipher, m);
|
|
this._doEncrypt(cipher, m, iv);
|
|
},
|
|
|
|
decrypt: function (cipher, m, iv) {
|
|
this._doDecrypt(cipher, m, iv);
|
|
this._padding.unpad(cipher, m);
|
|
},
|
|
|
|
// Default padding
|
|
_padding: C_pad.iso7816
|
|
};
|
|
|
|
|
|
/**
|
|
* Electronic Code Book mode.
|
|
*
|
|
* ECB applies the cipher directly against each block of the input.
|
|
*
|
|
* ECB does not require an initialization vector.
|
|
*/
|
|
var ECB = C_mode.ECB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var ECB_prototype = ECB.prototype = new Mode;
|
|
|
|
// Concrete steps for Mode template
|
|
ECB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
// Encrypt each block
|
|
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
|
|
cipher._encryptblock(m, offset);
|
|
}
|
|
};
|
|
ECB_prototype._doDecrypt = function (cipher, c, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
// Decrypt each block
|
|
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
|
cipher._decryptblock(c, offset);
|
|
}
|
|
};
|
|
|
|
// ECB never uses an IV
|
|
ECB_prototype.fixOptions = function (options) {
|
|
options.iv = [];
|
|
};
|
|
|
|
|
|
/**
|
|
* Cipher block chaining
|
|
*
|
|
* The first block is XORed with the IV. Subsequent blocks are XOR with the
|
|
* previous cipher output.
|
|
*/
|
|
var CBC = C_mode.CBC = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CBC_prototype = CBC.prototype = new Mode;
|
|
|
|
// Concrete steps for Mode template
|
|
CBC_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
|
|
// Encrypt each block
|
|
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
|
|
if (offset == 0) {
|
|
// XOR first block using IV
|
|
for (var i = 0; i < blockSizeInBytes; i++)
|
|
m[i] ^= iv[i];
|
|
} else {
|
|
// XOR this block using previous crypted block
|
|
for (var i = 0; i < blockSizeInBytes; i++)
|
|
m[offset + i] ^= m[offset + i - blockSizeInBytes];
|
|
}
|
|
// Encrypt block
|
|
cipher._encryptblock(m, offset);
|
|
}
|
|
};
|
|
CBC_prototype._doDecrypt = function (cipher, c, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
|
|
// At the start, the previously crypted block is the IV
|
|
var prevCryptedBlock = iv;
|
|
|
|
// Decrypt each block
|
|
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
|
// Save this crypted block
|
|
var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);
|
|
// Decrypt block
|
|
cipher._decryptblock(c, offset);
|
|
// XOR decrypted block using previous crypted block
|
|
for (var i = 0; i < blockSizeInBytes; i++) {
|
|
c[offset + i] ^= prevCryptedBlock[i];
|
|
}
|
|
prevCryptedBlock = thisCryptedBlock;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Cipher feed back
|
|
*
|
|
* The cipher output is XORed with the plain text to produce the cipher output,
|
|
* which is then fed back into the cipher to produce a bit pattern to XOR the
|
|
* next block with.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var CFB = C_mode.CFB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CFB_prototype = CFB.prototype = new Mode;
|
|
|
|
// Override padding
|
|
CFB_prototype._padding = C_pad.NoPadding;
|
|
|
|
// Concrete steps for Mode template
|
|
CFB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4,
|
|
keystream = iv.slice(0);
|
|
|
|
// Encrypt each byte
|
|
for (var i = 0; i < m.length; i++) {
|
|
|
|
var j = i % blockSizeInBytes;
|
|
if (j == 0) cipher._encryptblock(keystream, 0);
|
|
|
|
m[i] ^= keystream[j];
|
|
keystream[j] = m[i];
|
|
}
|
|
};
|
|
CFB_prototype._doDecrypt = function (cipher, c, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4,
|
|
keystream = iv.slice(0);
|
|
|
|
// Encrypt each byte
|
|
for (var i = 0; i < c.length; i++) {
|
|
|
|
var j = i % blockSizeInBytes;
|
|
if (j == 0) cipher._encryptblock(keystream, 0);
|
|
|
|
var b = c[i];
|
|
c[i] ^= keystream[j];
|
|
keystream[j] = b;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Output feed back
|
|
*
|
|
* The cipher repeatedly encrypts its own output. The output is XORed with the
|
|
* plain text to produce the cipher text.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var OFB = C_mode.OFB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var OFB_prototype = OFB.prototype = new Mode;
|
|
|
|
// Override padding
|
|
OFB_prototype._padding = C_pad.NoPadding;
|
|
|
|
// Concrete steps for Mode template
|
|
OFB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
|
|
var blockSizeInBytes = cipher._blocksize * 4,
|
|
keystream = iv.slice(0);
|
|
|
|
// Encrypt each byte
|
|
for (var i = 0; i < m.length; i++) {
|
|
|
|
// Generate keystream
|
|
if (i % blockSizeInBytes == 0)
|
|
cipher._encryptblock(keystream, 0);
|
|
|
|
// Encrypt byte
|
|
m[i] ^= keystream[i % blockSizeInBytes];
|
|
|
|
}
|
|
};
|
|
OFB_prototype._doDecrypt = OFB_prototype._doEncrypt;
|
|
|
|
/**
|
|
* Counter
|
|
* @author Gergely Risko
|
|
*
|
|
* After every block the last 4 bytes of the IV is increased by one
|
|
* with carry and that IV is used for the next block.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var CTR = C_mode.CTR = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CTR_prototype = CTR.prototype = new Mode;
|
|
|
|
// Override padding
|
|
CTR_prototype._padding = C_pad.NoPadding;
|
|
|
|
CTR_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
var counter = iv.slice(0);
|
|
|
|
for (var i = 0; i < m.length;) {
|
|
// do not lose iv
|
|
var keystream = counter.slice(0);
|
|
|
|
// Generate keystream for next block
|
|
cipher._encryptblock(keystream, 0);
|
|
|
|
// XOR keystream with block
|
|
for (var j = 0; i < m.length && j < blockSizeInBytes; j++, i++) {
|
|
m[i] ^= keystream[j];
|
|
}
|
|
|
|
// Increase counter
|
|
if (++(counter[blockSizeInBytes - 1]) == 256) {
|
|
counter[blockSizeInBytes - 1] = 0;
|
|
if (++(counter[blockSizeInBytes - 2]) == 256) {
|
|
counter[blockSizeInBytes - 2] = 0;
|
|
if (++(counter[blockSizeInBytes - 3]) == 256) {
|
|
counter[blockSizeInBytes - 3] = 0;
|
|
++(counter[blockSizeInBytes - 4]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
CTR_prototype._doDecrypt = CTR_prototype._doEncrypt;
|
|
|
|
})(Crypto);
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Crypto-JS v2.0.0 RIPEMD-160
|
|
* http://code.google.com/p/crypto-js/
|
|
* Copyright (c) 2009, Jeff Mott. All rights reserved.
|
|
* http://code.google.com/p/crypto-js/wiki/License
|
|
*
|
|
* A JavaScript implementation of the RIPEMD-160 Algorithm
|
|
* Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
|
|
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
|
* Distributed under the BSD License
|
|
* See http://pajhome.org.uk/crypt/md5 for details.
|
|
* Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
|
|
* Ported to Crypto-JS by Stefan Thomas.
|
|
*/
|
|
|
|
(function () {
|
|
// Shortcuts
|
|
var C = Crypto,
|
|
util = C.util,
|
|
charenc = C.charenc,
|
|
UTF8 = charenc.UTF8,
|
|
Binary = charenc.Binary;
|
|
|
|
// Convert a byte array to little-endian 32-bit words
|
|
util.bytesToLWords = function (bytes) {
|
|
|
|
var output = Array(bytes.length >> 2);
|
|
for (var i = 0; i < output.length; i++)
|
|
output[i] = 0;
|
|
for (var i = 0; i < bytes.length * 8; i += 8)
|
|
output[i >> 5] |= (bytes[i / 8] & 0xFF) << (i % 32);
|
|
return output;
|
|
};
|
|
|
|
// Convert little-endian 32-bit words to a byte array
|
|
util.lWordsToBytes = function (words) {
|
|
var output = [];
|
|
for (var i = 0; i < words.length * 32; i += 8)
|
|
output.push((words[i >> 5] >>> (i % 32)) & 0xff);
|
|
return output;
|
|
};
|
|
|
|
// Public API
|
|
var RIPEMD160 = C.RIPEMD160 = function (message, options) {
|
|
var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message));
|
|
return options && options.asBytes ? digestbytes :
|
|
options && options.asString ? Binary.bytesToString(digestbytes) :
|
|
util.bytesToHex(digestbytes);
|
|
};
|
|
|
|
// The core
|
|
RIPEMD160._rmd160 = function (message) {
|
|
// Convert to byte array
|
|
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
|
|
|
var x = util.bytesToLWords(message),
|
|
len = message.length * 8;
|
|
|
|
/* append padding */
|
|
x[len >> 5] |= 0x80 << (len % 32);
|
|
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
|
|
|
var h0 = 0x67452301;
|
|
var h1 = 0xefcdab89;
|
|
var h2 = 0x98badcfe;
|
|
var h3 = 0x10325476;
|
|
var h4 = 0xc3d2e1f0;
|
|
|
|
for (var i = 0; i < x.length; i += 16) {
|
|
var T;
|
|
var A1 = h0,
|
|
B1 = h1,
|
|
C1 = h2,
|
|
D1 = h3,
|
|
E1 = h4;
|
|
var A2 = h0,
|
|
B2 = h1,
|
|
C2 = h2,
|
|
D2 = h3,
|
|
E2 = h4;
|
|
for (var j = 0; j <= 79; ++j) {
|
|
T = safe_add(A1, rmd160_f(j, B1, C1, D1));
|
|
T = safe_add(T, x[i + rmd160_r1[j]]);
|
|
T = safe_add(T, rmd160_K1(j));
|
|
T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
|
|
A1 = E1;
|
|
E1 = D1;
|
|
D1 = bit_rol(C1, 10);
|
|
C1 = B1;
|
|
B1 = T;
|
|
T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
|
|
T = safe_add(T, x[i + rmd160_r2[j]]);
|
|
T = safe_add(T, rmd160_K2(j));
|
|
T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
|
|
A2 = E2;
|
|
E2 = D2;
|
|
D2 = bit_rol(C2, 10);
|
|
C2 = B2;
|
|
B2 = T;
|
|
}
|
|
T = safe_add(h1, safe_add(C1, D2));
|
|
h1 = safe_add(h2, safe_add(D1, E2));
|
|
h2 = safe_add(h3, safe_add(E1, A2));
|
|
h3 = safe_add(h4, safe_add(A1, B2));
|
|
h4 = safe_add(h0, safe_add(B1, C2));
|
|
h0 = T;
|
|
}
|
|
return [h0, h1, h2, h3, h4];
|
|
}
|
|
|
|
function rmd160_f(j, x, y, z) {
|
|
return (0 <= j && j <= 15) ? (x ^ y ^ z) :
|
|
(16 <= j && j <= 31) ? (x & y) | (~x & z) :
|
|
(32 <= j && j <= 47) ? (x | ~y) ^ z :
|
|
(48 <= j && j <= 63) ? (x & z) | (y & ~z) :
|
|
(64 <= j && j <= 79) ? x ^ (y | ~z) :
|
|
"rmd160_f: j out of range";
|
|
}
|
|
|
|
function rmd160_K1(j) {
|
|
return (0 <= j && j <= 15) ? 0x00000000 :
|
|
(16 <= j && j <= 31) ? 0x5a827999 :
|
|
(32 <= j && j <= 47) ? 0x6ed9eba1 :
|
|
(48 <= j && j <= 63) ? 0x8f1bbcdc :
|
|
(64 <= j && j <= 79) ? 0xa953fd4e :
|
|
"rmd160_K1: j out of range";
|
|
}
|
|
|
|
function rmd160_K2(j) {
|
|
return (0 <= j && j <= 15) ? 0x50a28be6 :
|
|
(16 <= j && j <= 31) ? 0x5c4dd124 :
|
|
(32 <= j && j <= 47) ? 0x6d703ef3 :
|
|
(48 <= j && j <= 63) ? 0x7a6d76e9 :
|
|
(64 <= j && j <= 79) ? 0x00000000 :
|
|
"rmd160_K2: j out of range";
|
|
}
|
|
var rmd160_r1 = [
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
|
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
|
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
|
|
];
|
|
var rmd160_r2 = [
|
|
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
|
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
|
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
|
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
|
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
|
|
];
|
|
var rmd160_s1 = [
|
|
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
|
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
|
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
|
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
|
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
|
|
];
|
|
var rmd160_s2 = [
|
|
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
|
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
|
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
|
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
|
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
|
|
];
|
|
|
|
/*
|
|
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
|
* to work around bugs in some JS interpreters.
|
|
*/
|
|
function safe_add(x, y) {
|
|
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
|
return (msw << 16) | (lsw & 0xFFFF);
|
|
}
|
|
|
|
/*
|
|
* Bitwise rotate a 32-bit number to the left.
|
|
*/
|
|
function bit_rol(num, cnt) {
|
|
return (num << cnt) | (num >>> (32 - cnt));
|
|
}
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*!
|
|
* Random number generator with ArcFour PRNG
|
|
*
|
|
* NOTE: For best results, put code like
|
|
* <body onclick='SecureRandom.seedTime();' onkeypress='SecureRandom.seedTime();'>
|
|
* in your main HTML document.
|
|
*
|
|
* Copyright Tom Wu, bitaddress.org BSD License.
|
|
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
|
|
*/
|
|
(function () {
|
|
|
|
// Constructor function of Global SecureRandom object
|
|
var sr = window.SecureRandom = function () {};
|
|
|
|
// Properties
|
|
sr.state;
|
|
sr.pool;
|
|
sr.pptr;
|
|
sr.poolCopyOnInit;
|
|
|
|
// 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()
|
|
sr.poolSize = 256;
|
|
|
|
// --- object methods ---
|
|
|
|
// public method
|
|
// ba: byte array
|
|
sr.prototype.nextBytes = function (ba) {
|
|
var i;
|
|
if (window.crypto && window.crypto.getRandomValues && window.Uint8Array) {
|
|
try {
|
|
var rvBytes = new Uint8Array(ba.length);
|
|
window.crypto.getRandomValues(rvBytes);
|
|
for (i = 0; i < ba.length; ++i)
|
|
ba[i] = sr.getByte() ^ rvBytes[i];
|
|
return;
|
|
} catch (e) {
|
|
alert(e);
|
|
}
|
|
}
|
|
for (i = 0; i < ba.length; ++i) ba[i] = sr.getByte();
|
|
};
|
|
|
|
|
|
// --- static methods ---
|
|
|
|
// Mix in the current time (w/milliseconds) into the pool
|
|
// NOTE: this method should be called from body click/keypress event handlers to increase entropy
|
|
sr.seedTime = function () {
|
|
sr.seedInt(new Date().getTime());
|
|
}
|
|
|
|
sr.getByte = function () {
|
|
if (sr.state == null) {
|
|
sr.seedTime();
|
|
sr.state = sr.ArcFour(); // Plug in your RNG constructor here
|
|
sr.state.init(sr.pool);
|
|
sr.poolCopyOnInit = [];
|
|
for (sr.pptr = 0; sr.pptr < sr.pool.length; ++sr.pptr)
|
|
sr.poolCopyOnInit[sr.pptr] = sr.pool[sr.pptr];
|
|
sr.pptr = 0;
|
|
}
|
|
// TODO: allow reseeding after first request
|
|
return sr.state.next();
|
|
}
|
|
|
|
// Mix in a 32-bit integer into the pool
|
|
sr.seedInt = function (x) {
|
|
sr.seedInt8(x);
|
|
sr.seedInt8((x >> 8));
|
|
sr.seedInt8((x >> 16));
|
|
sr.seedInt8((x >> 24));
|
|
}
|
|
|
|
// Mix in a 16-bit integer into the pool
|
|
sr.seedInt16 = function (x) {
|
|
sr.seedInt8(x);
|
|
sr.seedInt8((x >> 8));
|
|
}
|
|
|
|
// Mix in a 8-bit integer into the pool
|
|
sr.seedInt8 = function (x) {
|
|
sr.pool[sr.pptr++] ^= x & 255;
|
|
if (sr.pptr >= sr.poolSize) sr.pptr -= sr.poolSize;
|
|
}
|
|
|
|
// Arcfour is a PRNG
|
|
sr.ArcFour = function () {
|
|
function Arcfour() {
|
|
this.i = 0;
|
|
this.j = 0;
|
|
this.S = new Array();
|
|
}
|
|
|
|
// Initialize arcfour context from key, an array of ints, each from [0..255]
|
|
function ARC4init(key) {
|
|
var i, j, t;
|
|
for (i = 0; i < 256; ++i)
|
|
this.S[i] = i;
|
|
j = 0;
|
|
for (i = 0; i < 256; ++i) {
|
|
j = (j + this.S[i] + key[i % key.length]) & 255;
|
|
t = this.S[i];
|
|
this.S[i] = this.S[j];
|
|
this.S[j] = t;
|
|
}
|
|
this.i = 0;
|
|
this.j = 0;
|
|
}
|
|
|
|
function ARC4next() {
|
|
var t;
|
|
this.i = (this.i + 1) & 255;
|
|
this.j = (this.j + this.S[this.i]) & 255;
|
|
t = this.S[this.i];
|
|
this.S[this.i] = this.S[this.j];
|
|
this.S[this.j] = t;
|
|
return this.S[(t + this.S[this.i]) & 255];
|
|
}
|
|
|
|
Arcfour.prototype.init = ARC4init;
|
|
Arcfour.prototype.next = ARC4next;
|
|
|
|
return new Arcfour();
|
|
};
|
|
|
|
|
|
// Initialize the pool with junk if needed.
|
|
if (sr.pool == null) {
|
|
sr.pool = new Array();
|
|
sr.pptr = 0;
|
|
var t;
|
|
if (window.crypto && window.crypto.getRandomValues && window.Uint8Array) {
|
|
try {
|
|
// Use webcrypto if available
|
|
var ua = new Uint8Array(sr.poolSize);
|
|
window.crypto.getRandomValues(ua);
|
|
for (t = 0; t < sr.poolSize; ++t)
|
|
sr.pool[sr.pptr++] = ua[t];
|
|
} catch (e) {
|
|
alert(e);
|
|
}
|
|
}
|
|
while (sr.pptr < sr.poolSize) { // extract some randomness from Math.random()
|
|
t = Math.floor(65536 * Math.random());
|
|
sr.pool[sr.pptr++] = t >>> 8;
|
|
sr.pool[sr.pptr++] = t & 255;
|
|
}
|
|
sr.pptr = Math.floor(sr.poolSize * Math.random());
|
|
sr.seedTime();
|
|
// entropy
|
|
var entropyStr = "";
|
|
// screen size and color depth: ~4.8 to ~5.4 bits
|
|
entropyStr += (window.screen.height * window.screen.width * window.screen.colorDepth);
|
|
entropyStr += (window.screen.availHeight * window.screen.availWidth * window.screen.pixelDepth);
|
|
// time zone offset: ~4 bits
|
|
var dateObj = new Date();
|
|
var timeZoneOffset = dateObj.getTimezoneOffset();
|
|
entropyStr += timeZoneOffset;
|
|
// user agent: ~8.3 to ~11.6 bits
|
|
entropyStr += navigator.userAgent;
|
|
// browser plugin details: ~16.2 to ~21.8 bits
|
|
var pluginsStr = "";
|
|
for (var i = 0; i < navigator.plugins.length; i++) {
|
|
pluginsStr += navigator.plugins[i].name + " " + navigator.plugins[i].filename + " " + navigator
|
|
.plugins[
|
|
i].description + " " + navigator.plugins[i].version + ", ";
|
|
}
|
|
var mimeTypesStr = "";
|
|
for (var i = 0; i < navigator.mimeTypes.length; i++) {
|
|
mimeTypesStr += navigator.mimeTypes[i].description + " " + navigator.mimeTypes[i].type + " " +
|
|
navigator.mimeTypes[i].suffixes + ", ";
|
|
}
|
|
entropyStr += pluginsStr + mimeTypesStr;
|
|
// cookies and storage: 1 bit
|
|
entropyStr += navigator.cookieEnabled + typeof (sessionStorage) + typeof (localStorage);
|
|
// language: ~7 bit
|
|
entropyStr += navigator.language;
|
|
// history: ~2 bit
|
|
entropyStr += window.history.length;
|
|
// location
|
|
entropyStr += window.location;
|
|
|
|
var entropyBytes = Crypto.SHA256(entropyStr, {
|
|
asBytes: true
|
|
});
|
|
for (var i = 0; i < entropyBytes.length; i++) {
|
|
sr.seedInt8(entropyBytes[i]);
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/faa10f0f6a1fff0b9a99fffb9bc30cee33b17212/src/ecdsa.js
|
|
/*!
|
|
* Basic Javascript Elliptic Curve implementation
|
|
* Ported loosely from BouncyCastle's Java EC code
|
|
* Only Fp curves implemented for now
|
|
*
|
|
* Copyright Tom Wu, bitaddress.org BSD License.
|
|
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
|
|
*/
|
|
(function () {
|
|
|
|
// Constructor function of Global EllipticCurve object
|
|
var ec = window.EllipticCurve = function () {};
|
|
|
|
|
|
// ----------------
|
|
// ECFieldElementFp constructor
|
|
// q instanceof BigInteger
|
|
// x instanceof BigInteger
|
|
ec.FieldElementFp = function (q, x) {
|
|
this.x = x;
|
|
// TODO if(x.compareTo(q) >= 0) error
|
|
this.q = q;
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.equals = function (other) {
|
|
if (other == this) return true;
|
|
return (this.q.equals(other.q) && this.x.equals(other.x));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.toBigInteger = function () {
|
|
return this.x;
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.negate = function () {
|
|
return new ec.FieldElementFp(this.q, this.x.negate().mod(this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.add = function (b) {
|
|
return new ec.FieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.subtract = function (b) {
|
|
return new ec.FieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.multiply = function (b) {
|
|
return new ec.FieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.square = function () {
|
|
return new ec.FieldElementFp(this.q, this.x.square().mod(this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.divide = function (b) {
|
|
return new ec.FieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(
|
|
this.q));
|
|
};
|
|
|
|
ec.FieldElementFp.prototype.getByteLength = function () {
|
|
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
|
|
};
|
|
|
|
// D.1.4 91
|
|
/**
|
|
* return a sqrt root - the routine verifies that the calculation
|
|
* returns the right value - if none exists it returns null.
|
|
*
|
|
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
|
* Ported to JavaScript by bitaddress.org
|
|
*/
|
|
ec.FieldElementFp.prototype.sqrt = function () {
|
|
if (!this.q.testBit(0)) throw new Error("even value of q");
|
|
|
|
// p mod 4 == 3
|
|
if (this.q.testBit(1)) {
|
|
// z = g^(u+1) + p, p = 4u + 3
|
|
var z = new ec.FieldElementFp(this.q, this.x.modPow(this.q.shiftRight(2).add(BigInteger.ONE),
|
|
this.q));
|
|
return z.square().equals(this) ? z : null;
|
|
}
|
|
|
|
// p mod 4 == 1
|
|
var qMinusOne = this.q.subtract(BigInteger.ONE);
|
|
var legendreExponent = qMinusOne.shiftRight(1);
|
|
if (!(this.x.modPow(legendreExponent, this.q).equals(BigInteger.ONE))) return null;
|
|
var u = qMinusOne.shiftRight(2);
|
|
var k = u.shiftLeft(1).add(BigInteger.ONE);
|
|
var Q = this.x;
|
|
var fourQ = Q.shiftLeft(2).mod(this.q);
|
|
var U, V;
|
|
|
|
do {
|
|
var rand = new SecureRandom();
|
|
var P;
|
|
do {
|
|
P = new BigInteger(this.q.bitLength(), rand);
|
|
}
|
|
while (P.compareTo(this.q) >= 0 || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent,
|
|
this.q).equals(qMinusOne)));
|
|
|
|
var result = ec.FieldElementFp.fastLucasSequence(this.q, P, Q, k);
|
|
|
|
U = result[0];
|
|
V = result[1];
|
|
if (V.multiply(V).mod(this.q).equals(fourQ)) {
|
|
// Integer division by 2, mod q
|
|
if (V.testBit(0)) {
|
|
V = V.add(this.q);
|
|
}
|
|
V = V.shiftRight(1);
|
|
return new ec.FieldElementFp(this.q, V);
|
|
}
|
|
}
|
|
while (U.equals(BigInteger.ONE) || U.equals(qMinusOne));
|
|
|
|
return null;
|
|
};
|
|
|
|
/*
|
|
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
|
* Ported to JavaScript by bitaddress.org
|
|
*/
|
|
ec.FieldElementFp.fastLucasSequence = function (p, P, Q, k) {
|
|
// TODO Research and apply "common-multiplicand multiplication here"
|
|
|
|
var n = k.bitLength();
|
|
var s = k.getLowestSetBit();
|
|
var Uh = BigInteger.ONE;
|
|
var Vl = BigInteger.TWO;
|
|
var Vh = P;
|
|
var Ql = BigInteger.ONE;
|
|
var Qh = BigInteger.ONE;
|
|
|
|
for (var j = n - 1; j >= s + 1; --j) {
|
|
Ql = Ql.multiply(Qh).mod(p);
|
|
if (k.testBit(j)) {
|
|
Qh = Ql.multiply(Q).mod(p);
|
|
Uh = Uh.multiply(Vh).mod(p);
|
|
Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
|
Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p);
|
|
} else {
|
|
Qh = Ql;
|
|
Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
|
|
Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
|
Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
|
|
}
|
|
}
|
|
|
|
Ql = Ql.multiply(Qh).mod(p);
|
|
Qh = Ql.multiply(Q).mod(p);
|
|
Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
|
|
Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
|
Ql = Ql.multiply(Qh).mod(p);
|
|
|
|
for (var j = 1; j <= s; ++j) {
|
|
Uh = Uh.multiply(Vl).mod(p);
|
|
Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
|
|
Ql = Ql.multiply(Ql).mod(p);
|
|
}
|
|
|
|
return [Uh, Vl];
|
|
};
|
|
|
|
// ----------------
|
|
// ECPointFp constructor
|
|
ec.PointFp = function (curve, x, y, z, compressed) {
|
|
this.curve = curve;
|
|
this.x = x;
|
|
this.y = y;
|
|
// Projective coordinates: either zinv == null or z * zinv == 1
|
|
// z and zinv are just BigIntegers, not fieldElements
|
|
if (z == null) {
|
|
this.z = BigInteger.ONE;
|
|
} else {
|
|
this.z = z;
|
|
}
|
|
this.zinv = null;
|
|
// compression flag
|
|
this.compressed = !!compressed;
|
|
};
|
|
|
|
ec.PointFp.prototype.getX = function () {
|
|
if (this.zinv == null) {
|
|
this.zinv = this.z.modInverse(this.curve.q);
|
|
}
|
|
var r = this.x.toBigInteger().multiply(this.zinv);
|
|
this.curve.reduce(r);
|
|
return this.curve.fromBigInteger(r);
|
|
};
|
|
|
|
ec.PointFp.prototype.getY = function () {
|
|
if (this.zinv == null) {
|
|
this.zinv = this.z.modInverse(this.curve.q);
|
|
}
|
|
var r = this.y.toBigInteger().multiply(this.zinv);
|
|
this.curve.reduce(r);
|
|
return this.curve.fromBigInteger(r);
|
|
};
|
|
|
|
ec.PointFp.prototype.equals = function (other) {
|
|
if (other == this) return true;
|
|
if (this.isInfinity()) return other.isInfinity();
|
|
if (other.isInfinity()) return this.isInfinity();
|
|
var u, v;
|
|
// u = Y2 * Z1 - Y1 * Z2
|
|
u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(
|
|
this.curve.q);
|
|
if (!u.equals(BigInteger.ZERO)) return false;
|
|
// v = X2 * Z1 - X1 * Z2
|
|
v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(
|
|
this.curve.q);
|
|
return v.equals(BigInteger.ZERO);
|
|
};
|
|
|
|
ec.PointFp.prototype.isInfinity = function () {
|
|
if ((this.x == null) && (this.y == null)) return true;
|
|
return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO);
|
|
};
|
|
|
|
ec.PointFp.prototype.negate = function () {
|
|
return new ec.PointFp(this.curve, this.x, this.y.negate(), this.z);
|
|
};
|
|
|
|
ec.PointFp.prototype.add = function (b) {
|
|
if (this.isInfinity()) return b;
|
|
if (b.isInfinity()) return this;
|
|
|
|
// u = Y2 * Z1 - Y1 * Z2
|
|
var u = b.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(b.z)).mod(
|
|
this.curve
|
|
.q);
|
|
// v = X2 * Z1 - X1 * Z2
|
|
var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(
|
|
this.curve
|
|
.q);
|
|
|
|
|
|
if (BigInteger.ZERO.equals(v)) {
|
|
if (BigInteger.ZERO.equals(u)) {
|
|
return this.twice(); // this == b, so double
|
|
}
|
|
return this.curve.getInfinity(); // this = -b, so infinity
|
|
}
|
|
|
|
var THREE = new BigInteger("3");
|
|
var x1 = this.x.toBigInteger();
|
|
var y1 = this.y.toBigInteger();
|
|
var x2 = b.x.toBigInteger();
|
|
var y2 = b.y.toBigInteger();
|
|
|
|
var v2 = v.square();
|
|
var v3 = v2.multiply(v);
|
|
var x1v2 = x1.multiply(v2);
|
|
var zu2 = u.square().multiply(this.z);
|
|
|
|
// x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3)
|
|
var x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.curve.q);
|
|
// y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3
|
|
var y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(
|
|
b.z).add(u.multiply(v3)).mod(this.curve.q);
|
|
// z3 = v^3 * z1 * z2
|
|
var z3 = v3.multiply(this.z).multiply(b.z).mod(this.curve.q);
|
|
|
|
return new ec.PointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3),
|
|
z3);
|
|
};
|
|
|
|
ec.PointFp.prototype.twice = function () {
|
|
if (this.isInfinity()) return this;
|
|
if (this.y.toBigInteger().signum() == 0) return this.curve.getInfinity();
|
|
|
|
// TODO: optimized handling of constants
|
|
var THREE = new BigInteger("3");
|
|
var x1 = this.x.toBigInteger();
|
|
var y1 = this.y.toBigInteger();
|
|
|
|
var y1z1 = y1.multiply(this.z);
|
|
var y1sqz1 = y1z1.multiply(y1).mod(this.curve.q);
|
|
var a = this.curve.a.toBigInteger();
|
|
|
|
// w = 3 * x1^2 + a * z1^2
|
|
var w = x1.square().multiply(THREE);
|
|
if (!BigInteger.ZERO.equals(a)) {
|
|
w = w.add(this.z.square().multiply(a));
|
|
}
|
|
w = w.mod(this.curve.q);
|
|
//this.curve.reduce(w);
|
|
// x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1)
|
|
var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(
|
|
this
|
|
.curve.q);
|
|
// y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3
|
|
var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(
|
|
y1sqz1)
|
|
.subtract(w.square().multiply(w)).mod(this.curve.q);
|
|
// z3 = 8 * (y1 * z1)^3
|
|
var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q);
|
|
|
|
return new ec.PointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3),
|
|
z3);
|
|
};
|
|
|
|
// Simple NAF (Non-Adjacent Form) multiplication algorithm
|
|
// TODO: modularize the multiplication algorithm
|
|
ec.PointFp.prototype.multiply = 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.add(hBit ? this : neg);
|
|
}
|
|
}
|
|
|
|
return R;
|
|
};
|
|
|
|
// Compute this*j + x*k (simultaneous multiplication)
|
|
ec.PointFp.prototype.multiplyTwo = function (j, x, k) {
|
|
var i;
|
|
if (j.bitLength() > k.bitLength())
|
|
i = j.bitLength() - 1;
|
|
else
|
|
i = k.bitLength() - 1;
|
|
|
|
var R = this.curve.getInfinity();
|
|
var both = this.add(x);
|
|
while (i >= 0) {
|
|
R = R.twice();
|
|
if (j.testBit(i)) {
|
|
if (k.testBit(i)) {
|
|
R = R.add(both);
|
|
} else {
|
|
R = R.add(this);
|
|
}
|
|
} else {
|
|
if (k.testBit(i)) {
|
|
R = R.add(x);
|
|
}
|
|
}
|
|
--i;
|
|
}
|
|
|
|
return R;
|
|
};
|
|
|
|
// patched by bitaddress.org and Casascius for use with Bitcoin.ECKey
|
|
// patched by coretechs to support compressed public keys
|
|
ec.PointFp.prototype.getEncoded = function (compressed) {
|
|
var x = this.getX().toBigInteger();
|
|
var y = this.getY().toBigInteger();
|
|
var len = 32; // integerToBytes will zero pad if integer is less than 32 bytes. 32 bytes length is required by the Bitcoin protocol.
|
|
var enc = ec.integerToBytes(x, len);
|
|
|
|
// when compressed prepend byte depending if y point is even or odd
|
|
if (compressed) {
|
|
if (y.isEven()) {
|
|
enc.unshift(0x02);
|
|
} else {
|
|
enc.unshift(0x03);
|
|
}
|
|
} else {
|
|
enc.unshift(0x04);
|
|
enc = enc.concat(ec.integerToBytes(y, len)); // uncompressed public key appends the bytes of the y point
|
|
}
|
|
return enc;
|
|
};
|
|
|
|
ec.PointFp.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 ec.PointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
|
|
};
|
|
|
|
ec.PointFp.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 ec.PointFp(this.curve, x3, y3);
|
|
};
|
|
|
|
ec.PointFp.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 ec.PointFp(this.curve, x3, y3);
|
|
};
|
|
|
|
ec.PointFp.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;
|
|
};
|
|
|
|
ec.PointFp.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);
|
|
};
|
|
|
|
ec.PointFp.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
|
|
*/
|
|
ec.PointFp.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;
|
|
};
|
|
|
|
|
|
|
|
|
|
// ----------------
|
|
// ECCurveFp constructor
|
|
ec.CurveFp = function (q, a, b) {
|
|
this.q = q;
|
|
this.a = this.fromBigInteger(a);
|
|
this.b = this.fromBigInteger(b);
|
|
this.infinity = new ec.PointFp(this, null, null);
|
|
this.reducer = new Barrett(this.q);
|
|
}
|
|
|
|
ec.CurveFp.prototype.getQ = function () {
|
|
return this.q;
|
|
};
|
|
|
|
ec.CurveFp.prototype.getA = function () {
|
|
return this.a;
|
|
};
|
|
|
|
ec.CurveFp.prototype.getB = function () {
|
|
return this.b;
|
|
};
|
|
|
|
ec.CurveFp.prototype.equals = function (other) {
|
|
if (other == this) return true;
|
|
return (this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b));
|
|
};
|
|
|
|
ec.CurveFp.prototype.getInfinity = function () {
|
|
return this.infinity;
|
|
};
|
|
|
|
ec.CurveFp.prototype.fromBigInteger = function (x) {
|
|
return new ec.FieldElementFp(this.q, x);
|
|
};
|
|
|
|
ec.CurveFp.prototype.reduce = function (x) {
|
|
this.reducer.reduce(x);
|
|
};
|
|
|
|
// for now, work with hex strings because they're easier in JS
|
|
// compressed support added by bitaddress.org
|
|
ec.CurveFp.prototype.decodePointHex = function (s) {
|
|
var firstByte = parseInt(s.substr(0, 2), 16);
|
|
switch (firstByte) { // first byte
|
|
case 0:
|
|
return this.infinity;
|
|
case 2: // compressed
|
|
case 3: // compressed
|
|
var yTilde = firstByte & 1;
|
|
var xHex = s.substr(2, s.length - 2);
|
|
var X1 = new BigInteger(xHex, 16);
|
|
return this.decompressPoint(yTilde, X1);
|
|
case 4: // uncompressed
|
|
case 6: // hybrid
|
|
case 7: // hybrid
|
|
var len = (s.length - 2) / 2;
|
|
var xHex = s.substr(2, len);
|
|
var yHex = s.substr(len + 2, len);
|
|
|
|
return new ec.PointFp(this,
|
|
this.fromBigInteger(new BigInteger(xHex, 16)),
|
|
this.fromBigInteger(new BigInteger(yHex, 16)));
|
|
|
|
default: // unsupported
|
|
return null;
|
|
}
|
|
};
|
|
|
|
ec.CurveFp.prototype.encodePointHex = function (p) {
|
|
if (p.isInfinity()) return "00";
|
|
var xHex = p.getX().toBigInteger().toString(16);
|
|
var yHex = p.getY().toBigInteger().toString(16);
|
|
var oLen = this.getQ().toString(16).length;
|
|
if ((oLen % 2) != 0) oLen++;
|
|
while (xHex.length < oLen) {
|
|
xHex = "0" + xHex;
|
|
}
|
|
while (yHex.length < oLen) {
|
|
yHex = "0" + yHex;
|
|
}
|
|
return "04" + xHex + yHex;
|
|
};
|
|
|
|
/*
|
|
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
|
* Ported to JavaScript by bitaddress.org
|
|
*
|
|
* Number yTilde
|
|
* BigInteger X1
|
|
*/
|
|
ec.CurveFp.prototype.decompressPoint = function (yTilde, X1) {
|
|
var x = this.fromBigInteger(X1);
|
|
var alpha = x.multiply(x.square().add(this.getA())).add(this.getB());
|
|
var beta = alpha.sqrt();
|
|
// if we can't find a sqrt we haven't got a point on the curve - run!
|
|
if (beta == null) throw new Error("Invalid point compression");
|
|
var betaValue = beta.toBigInteger();
|
|
var bit0 = betaValue.testBit(0) ? 1 : 0;
|
|
if (bit0 != yTilde) {
|
|
// Use the other root
|
|
beta = this.fromBigInteger(this.getQ().subtract(betaValue));
|
|
}
|
|
return new ec.PointFp(this, x, beta, null, true);
|
|
};
|
|
|
|
|
|
ec.fromHex = function (s) {
|
|
return new BigInteger(s, 16);
|
|
};
|
|
|
|
ec.integerToBytes = function (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;
|
|
};
|
|
|
|
|
|
// Named EC curves
|
|
// ----------------
|
|
// X9ECParameters constructor
|
|
ec.X9Parameters = function (curve, g, n, h) {
|
|
this.curve = curve;
|
|
this.g = g;
|
|
this.n = n;
|
|
this.h = h;
|
|
}
|
|
ec.X9Parameters.prototype.getCurve = function () {
|
|
return this.curve;
|
|
};
|
|
ec.X9Parameters.prototype.getG = function () {
|
|
return this.g;
|
|
};
|
|
ec.X9Parameters.prototype.getN = function () {
|
|
return this.n;
|
|
};
|
|
ec.X9Parameters.prototype.getH = function () {
|
|
return this.h;
|
|
};
|
|
|
|
// secp256k1 is the Curve used by Bitcoin
|
|
ec.secNamedCurves = {
|
|
// used by Bitcoin
|
|
"secp256k1": function () {
|
|
// p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
|
|
var p = ec.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
|
|
var a = BigInteger.ZERO;
|
|
var b = ec.fromHex("7");
|
|
var n = ec.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
|
|
var h = BigInteger.ONE;
|
|
var curve = new ec.CurveFp(p, a, b);
|
|
var G = curve.decodePointHex("04" +
|
|
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" +
|
|
"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
|
|
return new ec.X9Parameters(curve, G, n, h);
|
|
}
|
|
};
|
|
|
|
// secp256k1 called by Bitcoin's ECKEY
|
|
ec.getSECCurveByName = function (name) {
|
|
if (ec.secNamedCurves[name] == undefined) return null;
|
|
return ec.secNamedCurves[name]();
|
|
}
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
// secrets.js - by Alexander Stetsyuk - released under MIT License -- Implementation of Shamir Secret Sharing Scheme
|
|
(function (exports, global) {
|
|
var defaults = {
|
|
bits: 8, // default number of bits
|
|
radix: 16, // work with HEX by default
|
|
minBits: 3,
|
|
maxBits: 20, // this permits 1,048,575 shares, though going this high is NOT recommended in JS!
|
|
|
|
bytesPerChar: 2,
|
|
maxBytesPerChar: 6, // Math.pow(256,7) > Math.pow(2,53)
|
|
|
|
// Primitive polynomials (in decimal form) for Galois Fields GF(2^n), for 2 <= n <= 30
|
|
// The index of each term in the array corresponds to the n for that polynomial
|
|
// i.e. to get the polynomial for n=16, use primitivePolynomials[16]
|
|
primitivePolynomials: [null, null, 1, 3, 3, 5, 3, 3, 29, 17, 9, 5, 83, 27, 43, 3, 45, 9, 39, 39,
|
|
9,
|
|
5, 3, 33, 27, 9, 71, 39, 9, 5, 83
|
|
],
|
|
|
|
// warning for insecure PRNG
|
|
warning: 'WARNING:\nA secure random number generator was not found.\nUsing Math.random(), which is NOT cryptographically strong!'
|
|
};
|
|
|
|
// Protected settings object
|
|
var config = {};
|
|
|
|
/** @expose **/
|
|
exports.getConfig = function () {
|
|
return {
|
|
'bits': config.bits,
|
|
'unsafePRNG': config.unsafePRNG
|
|
};
|
|
};
|
|
|
|
function init(bits) {
|
|
if (bits && (typeof bits !== 'number' || bits % 1 !== 0 || bits < defaults.minBits || bits >
|
|
defaults.maxBits)) {
|
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' +
|
|
defaults.maxBits + ', inclusive.')
|
|
}
|
|
|
|
config.radix = defaults.radix;
|
|
config.bits = bits || defaults.bits;
|
|
config.size = Math.pow(2, config.bits);
|
|
config.max = config.size - 1;
|
|
|
|
// Construct the exp and log tables for multiplication.
|
|
var logs = [],
|
|
exps = [],
|
|
x = 1,
|
|
primitive = defaults.primitivePolynomials[config.bits];
|
|
for (var i = 0; i < config.size; i++) {
|
|
exps[i] = x;
|
|
logs[x] = i;
|
|
x <<= 1;
|
|
if (x >= config.size) {
|
|
x ^= primitive;
|
|
x &= config.max;
|
|
}
|
|
}
|
|
|
|
config.logs = logs;
|
|
config.exps = exps;
|
|
};
|
|
|
|
/** @expose **/
|
|
exports.init = init;
|
|
|
|
function isInited() {
|
|
if (!config.bits || !config.size || !config.max || !config.logs || !config.exps || config.logs.length !==
|
|
config.size || config.exps.length !== config.size) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Returns a pseudo-random number generator of the form function(bits){}
|
|
// which should output a random string of 1's and 0's of length `bits`
|
|
function getRNG() {
|
|
var randomBits, crypto;
|
|
|
|
function construct(bits, arr, radix, size) {
|
|
var str = '',
|
|
i = 0,
|
|
len = arr.length - 1;
|
|
while (i < len || (str.length < bits)) {
|
|
str += padLeft(parseInt(arr[i], radix).toString(2), size);
|
|
i++;
|
|
}
|
|
str = str.substr(-bits);
|
|
if ((str.match(/0/g) || []).length === str.length) { // all zeros?
|
|
return null;
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// node.js crypto.randomBytes()
|
|
if (typeof require === 'function' && (crypto = require('crypto')) && (randomBits = crypto[
|
|
'randomBytes'])) {
|
|
return function (bits) {
|
|
var bytes = Math.ceil(bits / 8),
|
|
str = null;
|
|
|
|
while (str === null) {
|
|
str = construct(bits, randomBits(bytes).toString('hex'), 16, 4);
|
|
}
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// browsers with window.crypto.getRandomValues()
|
|
if (global['crypto'] && typeof global['crypto']['getRandomValues'] === 'function' && typeof global[
|
|
'Uint32Array'] === 'function') {
|
|
crypto = global['crypto'];
|
|
return function (bits) {
|
|
var elems = Math.ceil(bits / 32),
|
|
str = null,
|
|
arr = new global['Uint32Array'](elems);
|
|
|
|
while (str === null) {
|
|
crypto['getRandomValues'](arr);
|
|
str = construct(bits, arr, 10, 32);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// A totally insecure RNG!!! (except in Safari)
|
|
// Will produce a warning every time it is called.
|
|
config.unsafePRNG = true;
|
|
warn();
|
|
|
|
var bitsPerNum = 32;
|
|
var max = Math.pow(2, bitsPerNum) - 1;
|
|
return function (bits) {
|
|
var elems = Math.ceil(bits / bitsPerNum);
|
|
var arr = [],
|
|
str = null;
|
|
while (str === null) {
|
|
for (var i = 0; i < elems; i++) {
|
|
arr[i] = Math.floor(Math.random() * max + 1);
|
|
}
|
|
str = construct(bits, arr, 10, bitsPerNum);
|
|
}
|
|
return str;
|
|
};
|
|
};
|
|
|
|
// Warn about using insecure rng.
|
|
// Called when Math.random() is being used.
|
|
function warn() {
|
|
global['console']['warn'](defaults.warning);
|
|
if (typeof global['alert'] === 'function' && config.alert) {
|
|
global['alert'](defaults.warning);
|
|
}
|
|
}
|
|
|
|
// Set the PRNG to use. If no RNG function is supplied, pick a default using getRNG()
|
|
/** @expose **/
|
|
exports.setRNG = function (rng, alert) {
|
|
if (!isInited()) {
|
|
this.init();
|
|
}
|
|
config.unsafePRNG = false;
|
|
rng = rng || getRNG();
|
|
|
|
// test the RNG (5 times)
|
|
if (typeof rng !== 'function' || typeof rng(config.bits) !== 'string' || !parseInt(rng(config.bits),
|
|
2) || rng(config.bits).length > config.bits || rng(config.bits).length < config.bits) {
|
|
throw new Error(
|
|
"Random number generator is invalid. Supply an RNG of the form function(bits){} that returns a string containing 'bits' number of random 1's and 0's."
|
|
)
|
|
} else {
|
|
config.rng = rng;
|
|
}
|
|
config.alert = !!alert;
|
|
|
|
return !!config.unsafePRNG;
|
|
};
|
|
|
|
function isSetRNG() {
|
|
return typeof config.rng === 'function';
|
|
};
|
|
|
|
// Generates a random bits-length number string using the PRNG
|
|
/** @expose **/
|
|
exports.random = function (bits) {
|
|
if (!isSetRNG()) {
|
|
this.setRNG();
|
|
}
|
|
|
|
if (typeof bits !== 'number' || bits % 1 !== 0 || bits < 2) {
|
|
throw new Error('Number of bits must be an integer greater than 1.')
|
|
}
|
|
|
|
if (config.unsafePRNG) {
|
|
warn();
|
|
}
|
|
return bin2hex(config.rng(bits));
|
|
}
|
|
|
|
// Divides a `secret` number String str expressed in radix `inputRadix` (optional, default 16)
|
|
// into `numShares` shares, each expressed in radix `outputRadix` (optional, default to `inputRadix`),
|
|
// requiring `threshold` number of shares to reconstruct the secret.
|
|
// Optionally, zero-pads the secret to a length that is a multiple of padLength before sharing.
|
|
/** @expose **/
|
|
exports.share = function (secret, numShares, threshold, padLength, withoutPrefix) {
|
|
if (!isInited()) {
|
|
this.init();
|
|
}
|
|
if (!isSetRNG()) {
|
|
this.setRNG();
|
|
}
|
|
|
|
padLength = padLength || 0;
|
|
|
|
if (typeof secret !== 'string') {
|
|
throw new Error('Secret must be a string.');
|
|
}
|
|
if (typeof numShares !== 'number' || numShares % 1 !== 0 || numShares < 2) {
|
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max +
|
|
'), inclusive.')
|
|
}
|
|
if (numShares > config.max) {
|
|
var neededBits = Math.ceil(Math.log(numShares + 1) / Math.LN2);
|
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max +
|
|
'), inclusive. To create ' + numShares + ' shares, use at least ' + neededBits +
|
|
' bits.')
|
|
}
|
|
if (typeof threshold !== 'number' || threshold % 1 !== 0 || threshold < 2) {
|
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' +
|
|
config.max + '), inclusive.');
|
|
}
|
|
if (threshold > config.max) {
|
|
var neededBits = Math.ceil(Math.log(threshold + 1) / Math.LN2);
|
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' +
|
|
config.max + '), inclusive. To use a threshold of ' + threshold +
|
|
', use at least ' +
|
|
neededBits + ' bits.');
|
|
}
|
|
if (typeof padLength !== 'number' || padLength % 1 !== 0) {
|
|
throw new Error('Zero-pad length must be an integer greater than 1.');
|
|
}
|
|
|
|
if (config.unsafePRNG) {
|
|
warn();
|
|
}
|
|
|
|
secret = '1' + hex2bin(secret); // append a 1 so that we can preserve the correct number of leading zeros in our secret
|
|
secret = split(secret, padLength);
|
|
var x = new Array(numShares),
|
|
y = new Array(numShares);
|
|
for (var i = 0, len = secret.length; i < len; i++) {
|
|
var subShares = this._getShares(secret[i], numShares, threshold);
|
|
for (var j = 0; j < numShares; j++) {
|
|
x[j] = x[j] || subShares[j].x.toString(config.radix);
|
|
y[j] = padLeft(subShares[j].y.toString(2)) + (y[j] ? y[j] : '');
|
|
}
|
|
}
|
|
var padding = config.max.toString(config.radix).length;
|
|
if (withoutPrefix) {
|
|
for (var i = 0; i < numShares; i++) {
|
|
x[i] = bin2hex(y[i]);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < numShares; i++) {
|
|
x[i] = config.bits.toString(36).toUpperCase() + padLeft(x[i], padding) + bin2hex(y[i]);
|
|
}
|
|
}
|
|
|
|
return x;
|
|
};
|
|
|
|
// This is the basic polynomial generation and evaluation function
|
|
// for a `config.bits`-length secret (NOT an arbitrary length)
|
|
// Note: no error-checking at this stage! If `secrets` is NOT
|
|
// a NUMBER less than 2^bits-1, the output will be incorrect!
|
|
/** @expose **/
|
|
exports._getShares = function (secret, numShares, threshold) {
|
|
var shares = [];
|
|
var coeffs = [secret];
|
|
|
|
for (var i = 1; i < threshold; i++) {
|
|
coeffs[i] = parseInt(config.rng(config.bits), 2);
|
|
}
|
|
for (var i = 1, len = numShares + 1; i < len; i++) {
|
|
shares[i - 1] = {
|
|
x: i,
|
|
y: horner(i, coeffs)
|
|
}
|
|
}
|
|
return shares;
|
|
};
|
|
|
|
// Polynomial evaluation at `x` using Horner's Method
|
|
// TODO: this can possibly be sped up using other methods
|
|
// NOTE: fx=fx * x + coeff[i] -> exp(log(fx) + log(x)) + coeff[i],
|
|
// so if fx===0, just set fx to coeff[i] because
|
|
// using the exp/log form will result in incorrect value
|
|
function horner(x, coeffs) {
|
|
var logx = config.logs[x];
|
|
var fx = 0;
|
|
for (var i = coeffs.length - 1; i >= 0; i--) {
|
|
if (fx === 0) {
|
|
fx = coeffs[i];
|
|
continue;
|
|
}
|
|
fx = config.exps[(logx + config.logs[fx]) % config.max] ^ coeffs[i];
|
|
}
|
|
return fx;
|
|
};
|
|
|
|
function inArray(arr, val) {
|
|
for (var i = 0, len = arr.length; i < len; i++) {
|
|
if (arr[i] === val) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
function processShare(share) {
|
|
|
|
var bits = parseInt(share[0], 36);
|
|
if (bits && (typeof bits !== 'number' || bits % 1 !== 0 || bits < defaults.minBits || bits >
|
|
defaults.maxBits)) {
|
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' +
|
|
defaults.maxBits + ', inclusive.')
|
|
}
|
|
|
|
var max = Math.pow(2, bits) - 1;
|
|
var idLength = max.toString(config.radix).length;
|
|
|
|
var id = parseInt(share.substr(1, idLength), config.radix);
|
|
if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > max) {
|
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
|
}
|
|
share = share.substr(idLength + 1);
|
|
if (!share.length) {
|
|
throw new Error('Invalid share: zero-length share.')
|
|
}
|
|
return {
|
|
'bits': bits,
|
|
'id': id,
|
|
'value': share
|
|
};
|
|
};
|
|
|
|
/** @expose **/
|
|
exports._processShare = processShare;
|
|
|
|
// Protected method that evaluates the Lagrange interpolation
|
|
// polynomial at x=`at` for individual config.bits-length
|
|
// segments of each share in the `shares` Array.
|
|
// Each share is expressed in base `inputRadix`. The output
|
|
// is expressed in base `outputRadix'
|
|
function combine(at, shares) {
|
|
var setBits, share, x = [],
|
|
y = [],
|
|
result = '',
|
|
idx;
|
|
|
|
for (var i = 0, len = shares.length; i < len; i++) {
|
|
share = processShare(shares[i]);
|
|
if (typeof setBits === 'undefined') {
|
|
setBits = share['bits'];
|
|
} else if (share['bits'] !== setBits) {
|
|
throw new Error('Mismatched shares: Different bit settings.')
|
|
}
|
|
|
|
if (config.bits !== setBits) {
|
|
init(setBits);
|
|
}
|
|
|
|
if (inArray(x, share['id'])) { // repeated x value?
|
|
continue;
|
|
}
|
|
|
|
idx = x.push(share['id']) - 1;
|
|
share = split(hex2bin(share['value']));
|
|
for (var j = 0, len2 = share.length; j < len2; j++) {
|
|
y[j] = y[j] || [];
|
|
y[j][idx] = share[j];
|
|
}
|
|
}
|
|
|
|
for (var i = 0, len = y.length; i < len; i++) {
|
|
result = padLeft(lagrange(at, x, y[i]).toString(2)) + result;
|
|
}
|
|
|
|
if (at === 0) { // reconstructing the secret
|
|
var idx = result.indexOf('1'); //find the first 1
|
|
return bin2hex(result.slice(idx + 1));
|
|
} else { // generating a new share
|
|
return bin2hex(result);
|
|
}
|
|
};
|
|
|
|
// Combine `shares` Array into the original secret
|
|
/** @expose **/
|
|
exports.combine = function (shares) {
|
|
return combine(0, shares);
|
|
};
|
|
|
|
// Generate a new share with id `id` (a number between 1 and 2^bits-1)
|
|
// `id` can be a Number or a String in the default radix (16)
|
|
/** @expose **/
|
|
exports.newShare = function (id, shares) {
|
|
if (typeof id === 'string') {
|
|
id = parseInt(id, config.radix);
|
|
}
|
|
|
|
var share = processShare(shares[0]);
|
|
var max = Math.pow(2, share['bits']) - 1;
|
|
|
|
if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > max) {
|
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
|
}
|
|
|
|
var padding = max.toString(config.radix).length;
|
|
return config.bits.toString(36).toUpperCase() + padLeft(id.toString(config.radix), padding) +
|
|
combine(id, shares);
|
|
};
|
|
|
|
// Evaluate the Lagrange interpolation polynomial at x = `at`
|
|
// using x and y Arrays that are of the same length, with
|
|
// corresponding elements constituting points on the polynomial.
|
|
function lagrange(at, x, y) {
|
|
var sum = 0,
|
|
product,
|
|
i, j;
|
|
|
|
for (var i = 0, len = x.length; i < len; i++) {
|
|
if (!y[i]) {
|
|
continue;
|
|
}
|
|
|
|
product = config.logs[y[i]];
|
|
for (var j = 0; j < len; j++) {
|
|
if (i === j) {
|
|
continue;
|
|
}
|
|
if (at === x[j]) { // happens when computing a share that is in the list of shares used to compute it
|
|
product = -1; // fix for a zero product term, after which the sum should be sum^0 = sum, not sum^1
|
|
break;
|
|
}
|
|
product = (product + config.logs[at ^ x[j]] - config.logs[x[i] ^ x[j]] + config.max /* to make sure it's not negative */ ) %
|
|
config.max;
|
|
}
|
|
|
|
sum = product === -1 ? sum : sum ^ config.exps[product]; // though exps[-1]= undefined and undefined ^ anything = anything in chrome, this behavior may not hold everywhere, so do the check
|
|
}
|
|
return sum;
|
|
};
|
|
|
|
/** @expose **/
|
|
exports._lagrange = lagrange;
|
|
|
|
// Splits a number string `bits`-length segments, after first
|
|
// optionally zero-padding it to a length that is a multiple of `padLength.
|
|
// Returns array of integers (each less than 2^bits-1), with each element
|
|
// representing a `bits`-length segment of the input string from right to left,
|
|
// i.e. parts[0] represents the right-most `bits`-length segment of the input string.
|
|
function split(str, padLength) {
|
|
if (padLength) {
|
|
str = padLeft(str, padLength)
|
|
}
|
|
var parts = [];
|
|
for (var i = str.length; i > config.bits; i -= config.bits) {
|
|
parts.push(parseInt(str.slice(i - config.bits, i), 2));
|
|
}
|
|
parts.push(parseInt(str.slice(0, i), 2));
|
|
return parts;
|
|
};
|
|
|
|
// Pads a string `str` with zeros on the left so that its length is a multiple of `bits`
|
|
function padLeft(str, bits) {
|
|
bits = bits || config.bits
|
|
var missing = str.length % bits;
|
|
return (missing ? new Array(bits - missing + 1).join('0') : '') + str;
|
|
};
|
|
|
|
function hex2bin(str) {
|
|
var bin = '',
|
|
num;
|
|
for (var i = str.length - 1; i >= 0; i--) {
|
|
num = parseInt(str[i], 16)
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid hex character.')
|
|
}
|
|
bin = padLeft(num.toString(2), 4) + bin;
|
|
}
|
|
return bin;
|
|
}
|
|
|
|
function bin2hex(str) {
|
|
var hex = '',
|
|
num;
|
|
str = padLeft(str, 4);
|
|
for (var i = str.length; i >= 4; i -= 4) {
|
|
num = parseInt(str.slice(i - 4, i), 2);
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid binary character.')
|
|
}
|
|
hex = num.toString(16) + hex;
|
|
}
|
|
return hex;
|
|
}
|
|
|
|
// Converts a given UTF16 character string to the HEX representation.
|
|
// Each character of the input string is represented by
|
|
// `bytesPerChar` bytes in the output string.
|
|
/** @expose **/
|
|
exports.str2hex = function (str, bytesPerChar) {
|
|
if (typeof str !== 'string') {
|
|
throw new Error('Input must be a character string.');
|
|
}
|
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
|
|
|
if (typeof bytesPerChar !== 'number' || bytesPerChar % 1 !== 0 || bytesPerChar < 1 ||
|
|
bytesPerChar >
|
|
defaults.maxBytesPerChar) {
|
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar +
|
|
', inclusive.')
|
|
}
|
|
|
|
var hexChars = 2 * bytesPerChar;
|
|
var max = Math.pow(16, hexChars) - 1;
|
|
var out = '',
|
|
num;
|
|
for (var i = 0, len = str.length; i < len; i++) {
|
|
num = str[i].charCodeAt();
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid character: ' + str[i]);
|
|
} else if (num > max) {
|
|
var neededBytes = Math.ceil(Math.log(num + 1) / Math.log(256));
|
|
throw new Error('Invalid character code (' + num +
|
|
'). Maximum allowable is 256^bytes-1 (' +
|
|
max + '). To convert this character, use at least ' + neededBytes + ' bytes.')
|
|
} else {
|
|
out = padLeft(num.toString(16), hexChars) + out;
|
|
}
|
|
}
|
|
return out;
|
|
};
|
|
|
|
// Converts a given HEX number string to a UTF16 character string.
|
|
/** @expose **/
|
|
exports.hex2str = function (str, bytesPerChar) {
|
|
if (typeof str !== 'string') {
|
|
throw new Error('Input must be a hexadecimal string.');
|
|
}
|
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
|
|
|
if (typeof bytesPerChar !== 'number' || bytesPerChar % 1 !== 0 || bytesPerChar < 1 ||
|
|
bytesPerChar >
|
|
defaults.maxBytesPerChar) {
|
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar +
|
|
', inclusive.')
|
|
}
|
|
|
|
var hexChars = 2 * bytesPerChar;
|
|
var out = '';
|
|
str = padLeft(str, hexChars);
|
|
for (var i = 0, len = str.length; i < len; i += hexChars) {
|
|
out = String.fromCharCode(parseInt(str.slice(i, i + hexChars), 16)) + out;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
// by default, initialize without an RNG
|
|
exports.init();
|
|
})(typeof module !== 'undefined' && module['exports'] ? module['exports'] : (window['secrets'] = {}), typeof GLOBAL !==
|
|
'undefined' ? GLOBAL : window);
|
|
</script>
|
|
<script type="text/javascript">
|
|
// Upstream 'BigInteger' here:
|
|
// Original Author: http://www-cs-students.stanford.edu/~tjw/jsbn/
|
|
// Follows 'jsbn' on Github: https://github.com/jasondavies/jsbn
|
|
// Review and Testing: https://github.com/cryptocoinjs/bigi/
|
|
/*!
|
|
* Basic JavaScript BN library - subset useful for RSA encryption. v1.4
|
|
*
|
|
* Copyright (c) 2005 Tom Wu
|
|
* All Rights Reserved.
|
|
* BSD License
|
|
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
|
|
*
|
|
* Copyright Stephan Thomas
|
|
* Copyright pointbiz
|
|
*/
|
|
|
|
(function () {
|
|
|
|
// (public) Constructor function of Global BigInteger object
|
|
var BigInteger = window.BigInteger = function BigInteger(a, b, c) {
|
|
if (!(this instanceof BigInteger))
|
|
return new BigInteger(a, b, c);
|
|
|
|
if (a != null)
|
|
if ("number" == typeof a) this.fromNumber(a, b, c);
|
|
else if (b == null && "string" != typeof a) this.fromString(a, 256);
|
|
else this.fromString(a, b);
|
|
};
|
|
|
|
// Bits per digit
|
|
var dbits;
|
|
|
|
// JavaScript engine analysis
|
|
var canary = 0xdeadbeefcafe;
|
|
var j_lm = ((canary & 0xffffff) == 0xefcafe);
|
|
|
|
// return new, unset BigInteger
|
|
function nbi() {
|
|
return new BigInteger(null);
|
|
}
|
|
|
|
// am: Compute w_j += (x*this_i), propagate carries,
|
|
// c is initial carry, returns final carry.
|
|
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
|
|
// We need to select the fastest one that works in this environment.
|
|
|
|
// am1: use a single mult and divide to get the high bits,
|
|
// max digit bits should be 26 because
|
|
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
|
|
function am1(i, x, w, j, c, n) {
|
|
while (--n >= 0) {
|
|
var v = x * this[i++] + w[j] + c;
|
|
c = Math.floor(v / 0x4000000);
|
|
w[j++] = v & 0x3ffffff;
|
|
}
|
|
return c;
|
|
}
|
|
// am2 avoids a big mult-and-extract completely.
|
|
// Max digit bits should be <= 30 because we do bitwise ops
|
|
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
|
|
function am2(i, x, w, j, c, n) {
|
|
var xl = x & 0x7fff,
|
|
xh = x >> 15;
|
|
while (--n >= 0) {
|
|
var l = this[i] & 0x7fff;
|
|
var h = this[i++] >> 15;
|
|
var m = xh * l + h * xl;
|
|
l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);
|
|
c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);
|
|
w[j++] = l & 0x3fffffff;
|
|
}
|
|
return c;
|
|
}
|
|
// Alternately, set max digit bits to 28 since some
|
|
// browsers slow down when dealing with 32-bit numbers.
|
|
function am3(i, x, w, j, c, n) {
|
|
var xl = x & 0x3fff,
|
|
xh = x >> 14;
|
|
while (--n >= 0) {
|
|
var l = this[i] & 0x3fff;
|
|
var h = this[i++] >> 14;
|
|
var m = xh * l + h * xl;
|
|
l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
|
|
c = (l >> 28) + (m >> 14) + xh * h;
|
|
w[j++] = l & 0xfffffff;
|
|
}
|
|
return c;
|
|
}
|
|
if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
|
|
BigInteger.prototype.am = am2;
|
|
dbits = 30;
|
|
} else if (j_lm && (navigator.appName != "Netscape")) {
|
|
BigInteger.prototype.am = am1;
|
|
dbits = 26;
|
|
} else { // Mozilla/Netscape seems to prefer am3
|
|
BigInteger.prototype.am = am3;
|
|
dbits = 28;
|
|
}
|
|
|
|
BigInteger.prototype.DB = dbits;
|
|
BigInteger.prototype.DM = ((1 << dbits) - 1);
|
|
BigInteger.prototype.DV = (1 << dbits);
|
|
|
|
var BI_FP = 52;
|
|
BigInteger.prototype.FV = Math.pow(2, BI_FP);
|
|
BigInteger.prototype.F1 = BI_FP - dbits;
|
|
BigInteger.prototype.F2 = 2 * dbits - BI_FP;
|
|
|
|
// Digit conversions
|
|
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
var BI_RC = new Array();
|
|
var rr, vv;
|
|
rr = "0".charCodeAt(0);
|
|
for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
|
|
rr = "a".charCodeAt(0);
|
|
for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
|
|
rr = "A".charCodeAt(0);
|
|
for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
|
|
|
|
function int2char(n) {
|
|
return BI_RM.charAt(n);
|
|
}
|
|
|
|
function intAt(s, i) {
|
|
var c = BI_RC[s.charCodeAt(i)];
|
|
return (c == null) ? -1 : c;
|
|
}
|
|
|
|
|
|
|
|
// return bigint initialized to value
|
|
function nbv(i) {
|
|
var r = nbi();
|
|
r.fromInt(i);
|
|
return r;
|
|
}
|
|
|
|
|
|
// returns bit length of the integer x
|
|
function nbits(x) {
|
|
var r = 1,
|
|
t;
|
|
if ((t = x >>> 16) != 0) {
|
|
x = t;
|
|
r += 16;
|
|
}
|
|
if ((t = x >> 8) != 0) {
|
|
x = t;
|
|
r += 8;
|
|
}
|
|
if ((t = x >> 4) != 0) {
|
|
x = t;
|
|
r += 4;
|
|
}
|
|
if ((t = x >> 2) != 0) {
|
|
x = t;
|
|
r += 2;
|
|
}
|
|
if ((t = x >> 1) != 0) {
|
|
x = t;
|
|
r += 1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// (protected) copy this to r
|
|
BigInteger.prototype.copyTo = function (r) {
|
|
for (var i = this.t - 1; i >= 0; --i) r[i] = this[i];
|
|
r.t = this.t;
|
|
r.s = this.s;
|
|
};
|
|
|
|
|
|
// (protected) set from integer value x, -DV <= x < DV
|
|
BigInteger.prototype.fromInt = function (x) {
|
|
this.t = 1;
|
|
this.s = (x < 0) ? -1 : 0;
|
|
if (x > 0) this[0] = x;
|
|
else if (x < -1) this[0] = x + this.DV;
|
|
else this.t = 0;
|
|
};
|
|
|
|
// (protected) set from string and radix
|
|
BigInteger.prototype.fromString = function (s, b) {
|
|
var k;
|
|
if (b == 16) k = 4;
|
|
else if (b == 8) k = 3;
|
|
else if (b == 256) k = 8; // byte array
|
|
else if (b == 2) k = 1;
|
|
else if (b == 32) k = 5;
|
|
else if (b == 4) k = 2;
|
|
else {
|
|
this.fromRadix(s, b);
|
|
return;
|
|
}
|
|
this.t = 0;
|
|
this.s = 0;
|
|
var i = s.length,
|
|
mi = false,
|
|
sh = 0;
|
|
while (--i >= 0) {
|
|
var x = (k == 8) ? s[i] & 0xff : intAt(s, i);
|
|
if (x < 0) {
|
|
if (s.charAt(i) == "-") mi = true;
|
|
continue;
|
|
}
|
|
mi = false;
|
|
if (sh == 0)
|
|
this[this.t++] = x;
|
|
else if (sh + k > this.DB) {
|
|
this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;
|
|
this[this.t++] = (x >> (this.DB - sh));
|
|
} else
|
|
this[this.t - 1] |= x << sh;
|
|
sh += k;
|
|
if (sh >= this.DB) sh -= this.DB;
|
|
}
|
|
if (k == 8 && (s[0] & 0x80) != 0) {
|
|
this.s = -1;
|
|
if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;
|
|
}
|
|
this.clamp();
|
|
if (mi) BigInteger.ZERO.subTo(this, this);
|
|
};
|
|
|
|
|
|
// (protected) clamp off excess high words
|
|
BigInteger.prototype.clamp = function () {
|
|
var c = this.s & this.DM;
|
|
while (this.t > 0 && this[this.t - 1] == c) --this.t;
|
|
};
|
|
|
|
// (protected) r = this << n*DB
|
|
BigInteger.prototype.dlShiftTo = function (n, r) {
|
|
var i;
|
|
for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i];
|
|
for (i = n - 1; i >= 0; --i) r[i] = 0;
|
|
r.t = this.t + n;
|
|
r.s = this.s;
|
|
};
|
|
|
|
// (protected) r = this >> n*DB
|
|
BigInteger.prototype.drShiftTo = function (n, r) {
|
|
for (var i = n; i < this.t; ++i) r[i - n] = this[i];
|
|
r.t = Math.max(this.t - n, 0);
|
|
r.s = this.s;
|
|
};
|
|
|
|
|
|
// (protected) r = this << n
|
|
BigInteger.prototype.lShiftTo = function (n, r) {
|
|
var bs = n % this.DB;
|
|
var cbs = this.DB - bs;
|
|
var bm = (1 << cbs) - 1;
|
|
var ds = Math.floor(n / this.DB),
|
|
c = (this.s << bs) & this.DM,
|
|
i;
|
|
for (i = this.t - 1; i >= 0; --i) {
|
|
r[i + ds + 1] = (this[i] >> cbs) | c;
|
|
c = (this[i] & bm) << bs;
|
|
}
|
|
for (i = ds - 1; i >= 0; --i) r[i] = 0;
|
|
r[ds] = c;
|
|
r.t = this.t + ds + 1;
|
|
r.s = this.s;
|
|
r.clamp();
|
|
};
|
|
|
|
|
|
// (protected) r = this >> n
|
|
BigInteger.prototype.rShiftTo = function (n, r) {
|
|
r.s = this.s;
|
|
var ds = Math.floor(n / this.DB);
|
|
if (ds >= this.t) {
|
|
r.t = 0;
|
|
return;
|
|
}
|
|
var bs = n % this.DB;
|
|
var cbs = this.DB - bs;
|
|
var bm = (1 << bs) - 1;
|
|
r[0] = this[ds] >> bs;
|
|
for (var i = ds + 1; i < this.t; ++i) {
|
|
r[i - ds - 1] |= (this[i] & bm) << cbs;
|
|
r[i - ds] = this[i] >> bs;
|
|
}
|
|
if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs;
|
|
r.t = this.t - ds;
|
|
r.clamp();
|
|
};
|
|
|
|
|
|
// (protected) r = this - a
|
|
BigInteger.prototype.subTo = function (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 < -1) r[i++] = this.DV + c;
|
|
else if (c > 0) r[i++] = c;
|
|
r.t = i;
|
|
r.clamp();
|
|
};
|
|
|
|
|
|
// (protected) r = this * a, r != this,a (HAC 14.12)
|
|
// "this" should be the larger one if appropriate.
|
|
BigInteger.prototype.multiplyTo = function (a, r) {
|
|
var x = this.abs(),
|
|
y = a.abs();
|
|
var i = x.t;
|
|
r.t = i + y.t;
|
|
while (--i >= 0) r[i] = 0;
|
|
for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);
|
|
r.s = 0;
|
|
r.clamp();
|
|
if (this.s != a.s) BigInteger.ZERO.subTo(r, r);
|
|
};
|
|
|
|
|
|
// (protected) r = this^2, r != this (HAC 14.16)
|
|
BigInteger.prototype.squareTo = function (r) {
|
|
var x = this.abs();
|
|
var i = r.t = 2 * x.t;
|
|
while (--i >= 0) r[i] = 0;
|
|
for (i = 0; i < x.t - 1; ++i) {
|
|
var c = x.am(i, x[i], r, 2 * i, 0, 1);
|
|
if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
|
|
r[i + x.t] -= x.DV;
|
|
r[i + x.t + 1] = 1;
|
|
}
|
|
}
|
|
if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);
|
|
r.s = 0;
|
|
r.clamp();
|
|
};
|
|
|
|
|
|
|
|
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
|
|
// r != q, this != m. q or r may be null.
|
|
BigInteger.prototype.divRemTo = function (m, q, r) {
|
|
var pm = m.abs();
|
|
if (pm.t <= 0) return;
|
|
var pt = this.abs();
|
|
if (pt.t < pm.t) {
|
|
if (q != null) q.fromInt(0);
|
|
if (r != null) this.copyTo(r);
|
|
return;
|
|
}
|
|
if (r == null) r = nbi();
|
|
var y = nbi(),
|
|
ts = this.s,
|
|
ms = m.s;
|
|
var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus
|
|
if (nsh > 0) {
|
|
pm.lShiftTo(nsh, y);
|
|
pt.lShiftTo(nsh, r);
|
|
} else {
|
|
pm.copyTo(y);
|
|
pt.copyTo(r);
|
|
}
|
|
var ys = y.t;
|
|
var y0 = y[ys - 1];
|
|
if (y0 == 0) return;
|
|
var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);
|
|
var d1 = this.FV / yt,
|
|
d2 = (1 << this.F1) / yt,
|
|
e = 1 << this.F2;
|
|
var i = r.t,
|
|
j = i - ys,
|
|
t = (q == null) ? nbi() : q;
|
|
y.dlShiftTo(j, t);
|
|
if (r.compareTo(t) >= 0) {
|
|
r[r.t++] = 1;
|
|
r.subTo(t, r);
|
|
}
|
|
BigInteger.ONE.dlShiftTo(ys, t);
|
|
t.subTo(y, y); // "negative" y so we can replace sub with am later
|
|
while (y.t < ys) y[y.t++] = 0;
|
|
while (--j >= 0) {
|
|
// Estimate quotient digit
|
|
var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
|
|
if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out
|
|
y.dlShiftTo(j, t);
|
|
r.subTo(t, r);
|
|
while (r[i] < --qd) r.subTo(t, r);
|
|
}
|
|
}
|
|
if (q != null) {
|
|
r.drShiftTo(ys, q);
|
|
if (ts != ms) BigInteger.ZERO.subTo(q, q);
|
|
}
|
|
r.t = ys;
|
|
r.clamp();
|
|
if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder
|
|
if (ts < 0) BigInteger.ZERO.subTo(r, r);
|
|
};
|
|
|
|
|
|
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
|
|
// justification:
|
|
// xy == 1 (mod m)
|
|
// xy = 1+km
|
|
// xy(2-xy) = (1+km)(1-km)
|
|
// x[y(2-xy)] = 1-k^2m^2
|
|
// x[y(2-xy)] == 1 (mod m^2)
|
|
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
|
|
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
|
|
// JS multiply "overflows" differently from C/C++, so care is needed here.
|
|
BigInteger.prototype.invDigit = function () {
|
|
if (this.t < 1) return 0;
|
|
var x = this[0];
|
|
if ((x & 1) == 0) return 0;
|
|
var y = x & 3; // y == 1/x mod 2^2
|
|
y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4
|
|
y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8
|
|
y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
|
|
// last step - calculate inverse mod DV directly;
|
|
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
|
|
y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits
|
|
// we really want the negative inverse, and -DV < y < DV
|
|
return (y > 0) ? this.DV - y : -y;
|
|
};
|
|
|
|
|
|
// (protected) true iff this is even
|
|
BigInteger.prototype.isEven = function () {
|
|
return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;
|
|
};
|
|
|
|
|
|
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
|
|
BigInteger.prototype.exp = function (e, z) {
|
|
if (e > 0xffffffff || e < 1) return BigInteger.ONE;
|
|
var r = nbi(),
|
|
r2 = nbi(),
|
|
g = z.convert(this),
|
|
i = nbits(e) - 1;
|
|
g.copyTo(r);
|
|
while (--i >= 0) {
|
|
z.sqrTo(r, r2);
|
|
if ((e & (1 << i)) > 0) z.mulTo(r2, g, r);
|
|
else {
|
|
var t = r;
|
|
r = r2;
|
|
r2 = t;
|
|
}
|
|
}
|
|
return z.revert(r);
|
|
};
|
|
|
|
|
|
// (public) return string representation in given radix
|
|
BigInteger.prototype.toString = function (b) {
|
|
if (this.s < 0) return "-" + this.negate().toString(b);
|
|
var k;
|
|
if (b == 16) k = 4;
|
|
else if (b == 8) k = 3;
|
|
else if (b == 2) k = 1;
|
|
else if (b == 32) k = 5;
|
|
else if (b == 4) k = 2;
|
|
else return this.toRadix(b);
|
|
var km = (1 << k) - 1,
|
|
d, m = false,
|
|
r = "",
|
|
i = this.t;
|
|
var p = this.DB - (i * this.DB) % k;
|
|
if (i-- > 0) {
|
|
if (p < this.DB && (d = this[i] >> p) > 0) {
|
|
m = true;
|
|
r = int2char(d);
|
|
}
|
|
while (i >= 0) {
|
|
if (p < k) {
|
|
d = (this[i] & ((1 << p) - 1)) << (k - p);
|
|
d |= this[--i] >> (p += this.DB - k);
|
|
} else {
|
|
d = (this[i] >> (p -= k)) & km;
|
|
if (p <= 0) {
|
|
p += this.DB;
|
|
--i;
|
|
}
|
|
}
|
|
if (d > 0) m = true;
|
|
if (m) r += int2char(d);
|
|
}
|
|
}
|
|
return m ? r : "0";
|
|
};
|
|
|
|
|
|
// (public) -this
|
|
BigInteger.prototype.negate = function () {
|
|
var r = nbi();
|
|
BigInteger.ZERO.subTo(this, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) |this|
|
|
BigInteger.prototype.abs = function () {
|
|
return (this.s < 0) ? this.negate() : this;
|
|
};
|
|
|
|
// (public) return + if this > a, - if this < a, 0 if equal
|
|
BigInteger.prototype.compareTo = function (a) {
|
|
var r = this.s - a.s;
|
|
if (r != 0) return r;
|
|
var i = this.t;
|
|
r = i - a.t;
|
|
if (r != 0) return (this.s < 0) ? -r : r;
|
|
while (--i >= 0)
|
|
if ((r = this[i] - a[i]) != 0) return r;
|
|
return 0;
|
|
}
|
|
|
|
// (public) return the number of bits in "this"
|
|
BigInteger.prototype.bitLength = function () {
|
|
if (this.t <= 0) return 0;
|
|
return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));
|
|
};
|
|
|
|
// (public) this mod a
|
|
BigInteger.prototype.mod = function (a) {
|
|
var r = nbi();
|
|
this.abs().divRemTo(a, null, r);
|
|
if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r);
|
|
return r;
|
|
}
|
|
|
|
// (public) this^e % m, 0 <= e < 2^32
|
|
BigInteger.prototype.modPowInt = function (e, m) {
|
|
var z;
|
|
if (e < 256 || m.isEven()) z = new Classic(m);
|
|
else z = new Montgomery(m);
|
|
return this.exp(e, z);
|
|
};
|
|
|
|
// "constants"
|
|
BigInteger.ZERO = nbv(0);
|
|
BigInteger.ONE = nbv(1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
// return number of 1 bits in x
|
|
function cbit(x) {
|
|
var r = 0;
|
|
while (x != 0) {
|
|
x &= x - 1;
|
|
++r;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
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];
|
|
|
|
|
|
|
|
// (protected) return x s.t. r^x < DV
|
|
BigInteger.prototype.chunkSize = function (r) {
|
|
return Math.floor(Math.LN2 * this.DB / Math.log(r));
|
|
};
|
|
|
|
// (protected) convert to radix string
|
|
BigInteger.prototype.toRadix = function (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
|
|
BigInteger.prototype.fromRadix = function (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
|
|
BigInteger.prototype.fromNumber = function (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);
|
|
}
|
|
};
|
|
|
|
// (protected) r = this op a (bitwise)
|
|
BigInteger.prototype.bitwiseTo = function (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();
|
|
};
|
|
|
|
// (protected) this op (1<<n)
|
|
BigInteger.prototype.changeBit = function (n, op) {
|
|
var r = BigInteger.ONE.shiftLeft(n);
|
|
this.bitwiseTo(r, op, r);
|
|
return r;
|
|
};
|
|
|
|
// (protected) r = this + a
|
|
BigInteger.prototype.addTo = function (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();
|
|
};
|
|
|
|
// (protected) this *= n, this >= 0, 1 < n < DV
|
|
BigInteger.prototype.dMultiply = function (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
|
|
BigInteger.prototype.dAddOffset = function (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];
|
|
}
|
|
};
|
|
|
|
// (protected) r = lower n words of "this * a", a.t <= n
|
|
// "this" should be the larger one if appropriate.
|
|
BigInteger.prototype.multiplyLowerTo = function (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.
|
|
BigInteger.prototype.multiplyUpperTo = function (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);
|
|
};
|
|
|
|
// (protected) this % n, n < 2^26
|
|
BigInteger.prototype.modInt = function (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;
|
|
};
|
|
|
|
|
|
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
|
|
BigInteger.prototype.millerRabin = function (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;
|
|
};
|
|
|
|
|
|
|
|
// (public)
|
|
BigInteger.prototype.clone = function () {
|
|
var r = nbi();
|
|
this.copyTo(r);
|
|
return r;
|
|
};
|
|
|
|
// (public) return value as integer
|
|
BigInteger.prototype.intValue = function () {
|
|
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
|
|
BigInteger.prototype.byteValue = function () {
|
|
return (this.t == 0) ? this.s : (this[0] << 24) >> 24;
|
|
};
|
|
|
|
// (public) return value as short (assumes DB>=16)
|
|
BigInteger.prototype.shortValue = function () {
|
|
return (this.t == 0) ? this.s : (this[0] << 16) >> 16;
|
|
};
|
|
|
|
// (public) 0 if this == 0, 1 if this > 0
|
|
BigInteger.prototype.signum = function () {
|
|
if (this.s < 0) return -1;
|
|
else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
|
|
else return 1;
|
|
};
|
|
|
|
|
|
// (public) convert to bigendian byte array
|
|
BigInteger.prototype.toByteArray = function () {
|
|
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;
|
|
};
|
|
|
|
BigInteger.prototype.equals = function (a) {
|
|
return (this.compareTo(a) == 0);
|
|
};
|
|
BigInteger.prototype.min = function (a) {
|
|
return (this.compareTo(a) < 0) ? this : a;
|
|
};
|
|
BigInteger.prototype.max = function (a) {
|
|
return (this.compareTo(a) > 0) ? this : a;
|
|
};
|
|
|
|
// (public) this & a
|
|
function op_and(x, y) {
|
|
return x & y;
|
|
}
|
|
BigInteger.prototype.and = function (a) {
|
|
var r = nbi();
|
|
this.bitwiseTo(a, op_and, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) this | a
|
|
function op_or(x, y) {
|
|
return x | y;
|
|
}
|
|
BigInteger.prototype.or = function (a) {
|
|
var r = nbi();
|
|
this.bitwiseTo(a, op_or, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) this ^ a
|
|
function op_xor(x, y) {
|
|
return x ^ y;
|
|
}
|
|
BigInteger.prototype.xor = function (a) {
|
|
var r = nbi();
|
|
this.bitwiseTo(a, op_xor, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) this & ~a
|
|
function op_andnot(x, y) {
|
|
return x & ~y;
|
|
}
|
|
BigInteger.prototype.andNot = function (a) {
|
|
var r = nbi();
|
|
this.bitwiseTo(a, op_andnot, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) ~this
|
|
BigInteger.prototype.not = function () {
|
|
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
|
|
BigInteger.prototype.shiftLeft = function (n) {
|
|
var r = nbi();
|
|
if (n < 0) this.rShiftTo(-n, r);
|
|
else this.lShiftTo(n, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) this >> n
|
|
BigInteger.prototype.shiftRight = function (n) {
|
|
var r = nbi();
|
|
if (n < 0) this.lShiftTo(-n, r);
|
|
else this.rShiftTo(n, r);
|
|
return r;
|
|
};
|
|
|
|
// (public) returns index of lowest 1-bit (or -1 if none)
|
|
BigInteger.prototype.getLowestSetBit = function () {
|
|
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;
|
|
};
|
|
|
|
// (public) return number of set bits
|
|
BigInteger.prototype.bitCount = function () {
|
|
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
|
|
BigInteger.prototype.testBit = function (n) {
|
|
var j = Math.floor(n / this.DB);
|
|
if (j >= this.t) return (this.s != 0);
|
|
return ((this[j] & (1 << (n % this.DB))) != 0);
|
|
};
|
|
|
|
// (public) this | (1<<n)
|
|
BigInteger.prototype.setBit = function (n) {
|
|
return this.changeBit(n, op_or);
|
|
};
|
|
// (public) this & ~(1<<n)
|
|
BigInteger.prototype.clearBit = function (n) {
|
|
return this.changeBit(n, op_andnot);
|
|
};
|
|
// (public) this ^ (1<<n)
|
|
BigInteger.prototype.flipBit = function (n) {
|
|
return this.changeBit(n, op_xor);
|
|
};
|
|
// (public) this + a
|
|
BigInteger.prototype.add = function (a) {
|
|
var r = nbi();
|
|
this.addTo(a, r);
|
|
return r;
|
|
};
|
|
// (public) this - a
|
|
BigInteger.prototype.subtract = function (a) {
|
|
var r = nbi();
|
|
this.subTo(a, r);
|
|
return r;
|
|
};
|
|
// (public) this * a
|
|
BigInteger.prototype.multiply = function (a) {
|
|
var r = nbi();
|
|
this.multiplyTo(a, r);
|
|
return r;
|
|
};
|
|
// (public) this / a
|
|
BigInteger.prototype.divide = function (a) {
|
|
var r = nbi();
|
|
this.divRemTo(a, r, null);
|
|
return r;
|
|
};
|
|
// (public) this % a
|
|
BigInteger.prototype.remainder = function (a) {
|
|
var r = nbi();
|
|
this.divRemTo(a, null, r);
|
|
return r;
|
|
};
|
|
// (public) [this/a,this%a]
|
|
BigInteger.prototype.divideAndRemainder = function (a) {
|
|
var q = nbi(),
|
|
r = nbi();
|
|
this.divRemTo(a, q, r);
|
|
return new Array(q, r);
|
|
};
|
|
|
|
// (public) this^e % m (HAC 14.85)
|
|
BigInteger.prototype.modPow = function (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) 1/this % m (HAC 14.61)
|
|
BigInteger.prototype.modInverse = function (m) {
|
|
var ac = m.isEven();
|
|
if (this.signum() === 0) throw new Error('division by zero');
|
|
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;
|
|
while (d.compareTo(m) >= 0) d.subTo(m, d);
|
|
while (d.signum() < 0) d.addTo(m, d);
|
|
return d;
|
|
};
|
|
|
|
|
|
// (public) this^e
|
|
BigInteger.prototype.pow = function (e) {
|
|
return this.exp(e, new NullExp());
|
|
};
|
|
|
|
// (public) gcd(this,a) (HAC 14.54)
|
|
BigInteger.prototype.gcd = function (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;
|
|
};
|
|
|
|
// (public) test primality with certainty >= 1-.5^t
|
|
BigInteger.prototype.isProbablePrime = function (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);
|
|
};
|
|
|
|
|
|
// JSBN-specific extension
|
|
|
|
// (public) this^2
|
|
BigInteger.prototype.square = function () {
|
|
var r = nbi();
|
|
this.squareTo(r);
|
|
return r;
|
|
};
|
|
|
|
|
|
// NOTE: BigInteger interfaces not implemented in jsbn:
|
|
// BigInteger(int signum, byte[] magnitude)
|
|
// double doubleValue()
|
|
// float floatValue()
|
|
// int hashCode()
|
|
// long longValue()
|
|
// static BigInteger valueOf(long val)
|
|
|
|
|
|
|
|
// Copyright Stephan Thomas (start) --- //
|
|
// https://raw.github.com/bitcoinjs/bitcoinjs-lib/07f9d55ccb6abd962efb6befdd37671f85ea4ff9/src/util.js
|
|
// BigInteger monkey patching
|
|
BigInteger.valueOf = nbv;
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
};
|
|
// Copyright Stephan Thomas (end) --- //
|
|
|
|
|
|
|
|
|
|
// ****** REDUCTION ******* //
|
|
|
|
// Modular reduction using "classic" algorithm
|
|
var Classic = window.Classic = function Classic(m) {
|
|
this.m = m;
|
|
}
|
|
Classic.prototype.convert = function (x) {
|
|
if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
|
|
else return x;
|
|
};
|
|
Classic.prototype.revert = function (x) {
|
|
return x;
|
|
};
|
|
Classic.prototype.reduce = function (x) {
|
|
x.divRemTo(this.m, null, x);
|
|
};
|
|
Classic.prototype.mulTo = function (x, y, r) {
|
|
x.multiplyTo(y, r);
|
|
this.reduce(r);
|
|
};
|
|
Classic.prototype.sqrTo = function (x, r) {
|
|
x.squareTo(r);
|
|
this.reduce(r);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Montgomery reduction
|
|
var Montgomery = window.Montgomery = function Montgomery(m) {
|
|
this.m = m;
|
|
this.mp = m.invDigit();
|
|
this.mpl = this.mp & 0x7fff;
|
|
this.mph = this.mp >> 15;
|
|
this.um = (1 << (m.DB - 15)) - 1;
|
|
this.mt2 = 2 * m.t;
|
|
}
|
|
// xR mod m
|
|
Montgomery.prototype.convert = function (x) {
|
|
var r = nbi();
|
|
x.abs().dlShiftTo(this.m.t, r);
|
|
r.divRemTo(this.m, null, r);
|
|
if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r);
|
|
return r;
|
|
}
|
|
// x/R mod m
|
|
Montgomery.prototype.revert = function (x) {
|
|
var r = nbi();
|
|
x.copyTo(r);
|
|
this.reduce(r);
|
|
return r;
|
|
};
|
|
// x = x/R mod m (HAC 14.32)
|
|
Montgomery.prototype.reduce = function (x) {
|
|
while (x.t <= this.mt2) // pad x so am has enough room later
|
|
x[x.t++] = 0;
|
|
for (var i = 0; i < this.m.t; ++i) {
|
|
// faster way of calculating u0 = x[i]*mp mod DV
|
|
var j = x[i] & 0x7fff;
|
|
var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;
|
|
// use am to combine the multiply-shift-add into one call
|
|
j = i + this.m.t;
|
|
x[j] += this.m.am(0, u0, x, i, 0, this.m.t);
|
|
// propagate carry
|
|
while (x[j] >= x.DV) {
|
|
x[j] -= x.DV;
|
|
x[++j]++;
|
|
}
|
|
}
|
|
x.clamp();
|
|
x.drShiftTo(this.m.t, x);
|
|
if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
|
|
};
|
|
// r = "xy/R mod m"; x,y != r
|
|
Montgomery.prototype.mulTo = function (x, y, r) {
|
|
x.multiplyTo(y, r);
|
|
this.reduce(r);
|
|
};
|
|
// r = "x^2/R mod m"; x != r
|
|
Montgomery.prototype.sqrTo = function (x, r) {
|
|
x.squareTo(r);
|
|
this.reduce(r);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// A "null" reducer
|
|
var NullExp = window.NullExp = function NullExp() {}
|
|
NullExp.prototype.convert = function (x) {
|
|
return x;
|
|
};
|
|
NullExp.prototype.revert = function (x) {
|
|
return x;
|
|
};
|
|
NullExp.prototype.mulTo = function (x, y, r) {
|
|
x.multiplyTo(y, r);
|
|
};
|
|
NullExp.prototype.sqrTo = function (x, r) {
|
|
x.squareTo(r);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Barrett modular reduction
|
|
var Barrett = window.Barrett = 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;
|
|
}
|
|
Barrett.prototype.convert = function (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;
|
|
}
|
|
};
|
|
Barrett.prototype.revert = function (x) {
|
|
return x;
|
|
};
|
|
// x = x mod m (HAC 14.42)
|
|
Barrett.prototype.reduce = function (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*y mod m; x,y != r
|
|
Barrett.prototype.mulTo = function (x, y, r) {
|
|
x.multiplyTo(y, r);
|
|
this.reduce(r);
|
|
};
|
|
// r = x^2 mod m; x != r
|
|
Barrett.prototype.sqrTo = function (x, r) {
|
|
x.squareTo(r);
|
|
this.reduce(r);
|
|
};
|
|
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
//---------------------------------------------------------------------
|
|
// QRCode for JavaScript
|
|
//
|
|
// Copyright (c) 2009 Kazuhiko Arase
|
|
//
|
|
// URL: http://www.d-project.com/
|
|
//
|
|
// Licensed under the MIT license:
|
|
// http://www.opensource.org/licenses/mit-license.php
|
|
//
|
|
// The word "QR Code" is registered trademark of
|
|
// DENSO WAVE INCORPORATED
|
|
// http://www.denso-wave.com/qrcode/faqpatent-e.html
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
(function () {
|
|
//---------------------------------------------------------------------
|
|
// QRCode
|
|
//---------------------------------------------------------------------
|
|
|
|
var QRCode = window.QRCode = function (typeNumber, errorCorrectLevel) {
|
|
this.typeNumber = typeNumber;
|
|
this.errorCorrectLevel = errorCorrectLevel;
|
|
this.modules = null;
|
|
this.moduleCount = 0;
|
|
this.dataCache = null;
|
|
this.dataList = new Array();
|
|
}
|
|
|
|
QRCode.prototype = {
|
|
|
|
addData: function (data) {
|
|
var newData = new QRCode.QR8bitByte(data);
|
|
this.dataList.push(newData);
|
|
this.dataCache = null;
|
|
},
|
|
|
|
isDark: function (row, col) {
|
|
if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
|
|
throw new Error(row + "," + col);
|
|
}
|
|
return this.modules[row][col];
|
|
},
|
|
|
|
getModuleCount: function () {
|
|
return this.moduleCount;
|
|
},
|
|
|
|
make: function () {
|
|
this.makeImpl(false, this.getBestMaskPattern());
|
|
},
|
|
|
|
makeImpl: function (test, maskPattern) {
|
|
|
|
this.moduleCount = this.typeNumber * 4 + 17;
|
|
this.modules = new Array(this.moduleCount);
|
|
|
|
for (var row = 0; row < this.moduleCount; row++) {
|
|
|
|
this.modules[row] = new Array(this.moduleCount);
|
|
|
|
for (var col = 0; col < this.moduleCount; col++) {
|
|
this.modules[row][col] = null; //(col + row) % 3;
|
|
}
|
|
}
|
|
|
|
this.setupPositionProbePattern(0, 0);
|
|
this.setupPositionProbePattern(this.moduleCount - 7, 0);
|
|
this.setupPositionProbePattern(0, this.moduleCount - 7);
|
|
this.setupPositionAdjustPattern();
|
|
this.setupTimingPattern();
|
|
this.setupTypeInfo(test, maskPattern);
|
|
|
|
if (this.typeNumber >= 7) {
|
|
this.setupTypeNumber(test);
|
|
}
|
|
|
|
if (this.dataCache == null) {
|
|
this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
|
|
}
|
|
|
|
this.mapData(this.dataCache, maskPattern);
|
|
},
|
|
|
|
setupPositionProbePattern: function (row, col) {
|
|
|
|
for (var r = -1; r <= 7; r++) {
|
|
|
|
if (row + r <= -1 || this.moduleCount <= row + r) continue;
|
|
|
|
for (var c = -1; c <= 7; c++) {
|
|
|
|
if (col + c <= -1 || this.moduleCount <= col + c) continue;
|
|
|
|
if ((0 <= r && r <= 6 && (c == 0 || c == 6)) ||
|
|
(0 <= c && c <= 6 && (r == 0 || r == 6)) ||
|
|
(2 <= r && r <= 4 && 2 <= c && c <= 4)) {
|
|
this.modules[row + r][col + c] = true;
|
|
} else {
|
|
this.modules[row + r][col + c] = false;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
getBestMaskPattern: function () {
|
|
|
|
var minLostPoint = 0;
|
|
var pattern = 0;
|
|
|
|
for (var i = 0; i < 8; i++) {
|
|
|
|
this.makeImpl(true, i);
|
|
|
|
var lostPoint = QRCode.Util.getLostPoint(this);
|
|
|
|
if (i == 0 || minLostPoint > lostPoint) {
|
|
minLostPoint = lostPoint;
|
|
pattern = i;
|
|
}
|
|
}
|
|
|
|
return pattern;
|
|
},
|
|
|
|
createMovieClip: function (target_mc, instance_name, depth) {
|
|
|
|
var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
|
|
var cs = 1;
|
|
|
|
this.make();
|
|
|
|
for (var row = 0; row < this.modules.length; row++) {
|
|
|
|
var y = row * cs;
|
|
|
|
for (var col = 0; col < this.modules[row].length; col++) {
|
|
|
|
var x = col * cs;
|
|
var dark = this.modules[row][col];
|
|
|
|
if (dark) {
|
|
qr_mc.beginFill(0, 100);
|
|
qr_mc.moveTo(x, y);
|
|
qr_mc.lineTo(x + cs, y);
|
|
qr_mc.lineTo(x + cs, y + cs);
|
|
qr_mc.lineTo(x, y + cs);
|
|
qr_mc.endFill();
|
|
}
|
|
}
|
|
}
|
|
|
|
return qr_mc;
|
|
},
|
|
|
|
setupTimingPattern: function () {
|
|
|
|
for (var r = 8; r < this.moduleCount - 8; r++) {
|
|
if (this.modules[r][6] != null) {
|
|
continue;
|
|
}
|
|
this.modules[r][6] = (r % 2 == 0);
|
|
}
|
|
|
|
for (var c = 8; c < this.moduleCount - 8; c++) {
|
|
if (this.modules[6][c] != null) {
|
|
continue;
|
|
}
|
|
this.modules[6][c] = (c % 2 == 0);
|
|
}
|
|
},
|
|
|
|
setupPositionAdjustPattern: function () {
|
|
|
|
var pos = QRCode.Util.getPatternPosition(this.typeNumber);
|
|
|
|
for (var i = 0; i < pos.length; i++) {
|
|
|
|
for (var j = 0; j < pos.length; j++) {
|
|
|
|
var row = pos[i];
|
|
var col = pos[j];
|
|
|
|
if (this.modules[row][col] != null) {
|
|
continue;
|
|
}
|
|
|
|
for (var r = -2; r <= 2; r++) {
|
|
|
|
for (var c = -2; c <= 2; c++) {
|
|
|
|
if (r == -2 || r == 2 || c == -2 || c == 2 ||
|
|
(r == 0 && c == 0)) {
|
|
this.modules[row + r][col + c] = true;
|
|
} else {
|
|
this.modules[row + r][col + c] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
setupTypeNumber: function (test) {
|
|
|
|
var bits = QRCode.Util.getBCHTypeNumber(this.typeNumber);
|
|
|
|
for (var i = 0; i < 18; i++) {
|
|
var mod = (!test && ((bits >> i) & 1) == 1);
|
|
this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
|
|
}
|
|
|
|
for (var i = 0; i < 18; i++) {
|
|
var mod = (!test && ((bits >> i) & 1) == 1);
|
|
this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
|
|
}
|
|
},
|
|
|
|
setupTypeInfo: function (test, maskPattern) {
|
|
|
|
var data = (this.errorCorrectLevel << 3) | maskPattern;
|
|
var bits = QRCode.Util.getBCHTypeInfo(data);
|
|
|
|
// vertical
|
|
for (var i = 0; i < 15; i++) {
|
|
|
|
var mod = (!test && ((bits >> i) & 1) == 1);
|
|
|
|
if (i < 6) {
|
|
this.modules[i][8] = mod;
|
|
} else if (i < 8) {
|
|
this.modules[i + 1][8] = mod;
|
|
} else {
|
|
this.modules[this.moduleCount - 15 + i][8] = mod;
|
|
}
|
|
}
|
|
|
|
// horizontal
|
|
for (var i = 0; i < 15; i++) {
|
|
|
|
var mod = (!test && ((bits >> i) & 1) == 1);
|
|
|
|
if (i < 8) {
|
|
this.modules[8][this.moduleCount - i - 1] = mod;
|
|
} else if (i < 9) {
|
|
this.modules[8][15 - i - 1 + 1] = mod;
|
|
} else {
|
|
this.modules[8][15 - i - 1] = mod;
|
|
}
|
|
}
|
|
|
|
// fixed module
|
|
this.modules[this.moduleCount - 8][8] = (!test);
|
|
|
|
},
|
|
|
|
mapData: function (data, maskPattern) {
|
|
|
|
var inc = -1;
|
|
var row = this.moduleCount - 1;
|
|
var bitIndex = 7;
|
|
var byteIndex = 0;
|
|
|
|
for (var col = this.moduleCount - 1; col > 0; col -= 2) {
|
|
|
|
if (col == 6) col--;
|
|
|
|
while (true) {
|
|
|
|
for (var c = 0; c < 2; c++) {
|
|
|
|
if (this.modules[row][col - c] == null) {
|
|
|
|
var dark = false;
|
|
|
|
if (byteIndex < data.length) {
|
|
dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
|
|
}
|
|
|
|
var mask = QRCode.Util.getMask(maskPattern, row, col - c);
|
|
|
|
if (mask) {
|
|
dark = !dark;
|
|
}
|
|
|
|
this.modules[row][col - c] = dark;
|
|
bitIndex--;
|
|
|
|
if (bitIndex == -1) {
|
|
byteIndex++;
|
|
bitIndex = 7;
|
|
}
|
|
}
|
|
}
|
|
|
|
row += inc;
|
|
|
|
if (row < 0 || this.moduleCount <= row) {
|
|
row -= inc;
|
|
inc = -inc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
QRCode.PAD0 = 0xEC;
|
|
QRCode.PAD1 = 0x11;
|
|
|
|
QRCode.createData = function (typeNumber, errorCorrectLevel, dataList) {
|
|
|
|
var rsBlocks = QRCode.RSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
|
|
|
|
var buffer = new QRCode.BitBuffer();
|
|
|
|
for (var i = 0; i < dataList.length; i++) {
|
|
var data = dataList[i];
|
|
buffer.put(data.mode, 4);
|
|
buffer.put(data.getLength(), QRCode.Util.getLengthInBits(data.mode, typeNumber));
|
|
data.write(buffer);
|
|
}
|
|
|
|
// calc num max data.
|
|
var totalDataCount = 0;
|
|
for (var i = 0; i < rsBlocks.length; i++) {
|
|
totalDataCount += rsBlocks[i].dataCount;
|
|
}
|
|
|
|
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
|
throw new Error("code length overflow. (" +
|
|
buffer.getLengthInBits() +
|
|
">" +
|
|
totalDataCount * 8 +
|
|
")");
|
|
}
|
|
|
|
// end code
|
|
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
|
buffer.put(0, 4);
|
|
}
|
|
|
|
// padding
|
|
while (buffer.getLengthInBits() % 8 != 0) {
|
|
buffer.putBit(false);
|
|
}
|
|
|
|
// padding
|
|
while (true) {
|
|
|
|
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
break;
|
|
}
|
|
buffer.put(QRCode.PAD0, 8);
|
|
|
|
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
break;
|
|
}
|
|
buffer.put(QRCode.PAD1, 8);
|
|
}
|
|
|
|
return QRCode.createBytes(buffer, rsBlocks);
|
|
};
|
|
|
|
QRCode.createBytes = function (buffer, rsBlocks) {
|
|
|
|
var offset = 0;
|
|
|
|
var maxDcCount = 0;
|
|
var maxEcCount = 0;
|
|
|
|
var dcdata = new Array(rsBlocks.length);
|
|
var ecdata = new Array(rsBlocks.length);
|
|
|
|
for (var r = 0; r < rsBlocks.length; r++) {
|
|
|
|
var dcCount = rsBlocks[r].dataCount;
|
|
var ecCount = rsBlocks[r].totalCount - dcCount;
|
|
|
|
maxDcCount = Math.max(maxDcCount, dcCount);
|
|
maxEcCount = Math.max(maxEcCount, ecCount);
|
|
|
|
dcdata[r] = new Array(dcCount);
|
|
|
|
for (var i = 0; i < dcdata[r].length; i++) {
|
|
dcdata[r][i] = 0xff & buffer.buffer[i + offset];
|
|
}
|
|
offset += dcCount;
|
|
|
|
var rsPoly = QRCode.Util.getErrorCorrectPolynomial(ecCount);
|
|
var rawPoly = new QRCode.Polynomial(dcdata[r], rsPoly.getLength() - 1);
|
|
|
|
var modPoly = rawPoly.mod(rsPoly);
|
|
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
|
for (var i = 0; i < ecdata[r].length; i++) {
|
|
var modIndex = i + modPoly.getLength() - ecdata[r].length;
|
|
ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
|
|
}
|
|
|
|
}
|
|
|
|
var totalCodeCount = 0;
|
|
for (var i = 0; i < rsBlocks.length; i++) {
|
|
totalCodeCount += rsBlocks[i].totalCount;
|
|
}
|
|
|
|
var data = new Array(totalCodeCount);
|
|
var index = 0;
|
|
|
|
for (var i = 0; i < maxDcCount; i++) {
|
|
for (var r = 0; r < rsBlocks.length; r++) {
|
|
if (i < dcdata[r].length) {
|
|
data[index++] = dcdata[r][i];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < maxEcCount; i++) {
|
|
for (var r = 0; r < rsBlocks.length; r++) {
|
|
if (i < ecdata[r].length) {
|
|
data[index++] = ecdata[r][i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// QR8bitByte
|
|
//---------------------------------------------------------------------
|
|
QRCode.QR8bitByte = function (data) {
|
|
this.mode = QRCode.Mode.MODE_8BIT_BYTE;
|
|
this.data = data;
|
|
}
|
|
|
|
QRCode.QR8bitByte.prototype = {
|
|
getLength: function (buffer) {
|
|
return this.data.length;
|
|
},
|
|
|
|
write: function (buffer) {
|
|
for (var i = 0; i < this.data.length; i++) {
|
|
// not JIS ...
|
|
buffer.put(this.data.charCodeAt(i), 8);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRMode
|
|
//---------------------------------------------------------------------
|
|
QRCode.Mode = {
|
|
MODE_NUMBER: 1 << 0,
|
|
MODE_ALPHA_NUM: 1 << 1,
|
|
MODE_8BIT_BYTE: 1 << 2,
|
|
MODE_KANJI: 1 << 3
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRErrorCorrectLevel
|
|
//---------------------------------------------------------------------
|
|
QRCode.ErrorCorrectLevel = {
|
|
L: 1,
|
|
M: 0,
|
|
Q: 3,
|
|
H: 2
|
|
};
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRMaskPattern
|
|
//---------------------------------------------------------------------
|
|
QRCode.MaskPattern = {
|
|
PATTERN000: 0,
|
|
PATTERN001: 1,
|
|
PATTERN010: 2,
|
|
PATTERN011: 3,
|
|
PATTERN100: 4,
|
|
PATTERN101: 5,
|
|
PATTERN110: 6,
|
|
PATTERN111: 7
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRUtil
|
|
//---------------------------------------------------------------------
|
|
|
|
QRCode.Util = {
|
|
|
|
PATTERN_POSITION_TABLE: [
|
|
[],
|
|
[6, 18],
|
|
[6, 22],
|
|
[6, 26],
|
|
[6, 30],
|
|
[6, 34],
|
|
[6, 22, 38],
|
|
[6, 24, 42],
|
|
[6, 26, 46],
|
|
[6, 28, 50],
|
|
[6, 30, 54],
|
|
[6, 32, 58],
|
|
[6, 34, 62],
|
|
[6, 26, 46, 66],
|
|
[6, 26, 48, 70],
|
|
[6, 26, 50, 74],
|
|
[6, 30, 54, 78],
|
|
[6, 30, 56, 82],
|
|
[6, 30, 58, 86],
|
|
[6, 34, 62, 90],
|
|
[6, 28, 50, 72, 94],
|
|
[6, 26, 50, 74, 98],
|
|
[6, 30, 54, 78, 102],
|
|
[6, 28, 54, 80, 106],
|
|
[6, 32, 58, 84, 110],
|
|
[6, 30, 58, 86, 114],
|
|
[6, 34, 62, 90, 118],
|
|
[6, 26, 50, 74, 98, 122],
|
|
[6, 30, 54, 78, 102, 126],
|
|
[6, 26, 52, 78, 104, 130],
|
|
[6, 30, 56, 82, 108, 134],
|
|
[6, 34, 60, 86, 112, 138],
|
|
[6, 30, 58, 86, 114, 142],
|
|
[6, 34, 62, 90, 118, 146],
|
|
[6, 30, 54, 78, 102, 126, 150],
|
|
[6, 24, 50, 76, 102, 128, 154],
|
|
[6, 28, 54, 80, 106, 132, 158],
|
|
[6, 32, 58, 84, 110, 136, 162],
|
|
[6, 26, 54, 82, 110, 138, 166],
|
|
[6, 30, 58, 86, 114, 142, 170]
|
|
],
|
|
|
|
G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
|
|
G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
|
|
G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
|
|
|
|
getBCHTypeInfo: function (data) {
|
|
var d = data << 10;
|
|
while (QRCode.Util.getBCHDigit(d) - QRCode.Util.getBCHDigit(QRCode.Util.G15) >= 0) {
|
|
d ^= (QRCode.Util.G15 << (QRCode.Util.getBCHDigit(d) - QRCode.Util.getBCHDigit(
|
|
QRCode.Util
|
|
.G15)));
|
|
}
|
|
return ((data << 10) | d) ^ QRCode.Util.G15_MASK;
|
|
},
|
|
|
|
getBCHTypeNumber: function (data) {
|
|
var d = data << 12;
|
|
while (QRCode.Util.getBCHDigit(d) - QRCode.Util.getBCHDigit(QRCode.Util.G18) >= 0) {
|
|
d ^= (QRCode.Util.G18 << (QRCode.Util.getBCHDigit(d) - QRCode.Util.getBCHDigit(
|
|
QRCode.Util
|
|
.G18)));
|
|
}
|
|
return (data << 12) | d;
|
|
},
|
|
|
|
getBCHDigit: function (data) {
|
|
|
|
var digit = 0;
|
|
|
|
while (data != 0) {
|
|
digit++;
|
|
data >>>= 1;
|
|
}
|
|
|
|
return digit;
|
|
},
|
|
|
|
getPatternPosition: function (typeNumber) {
|
|
return QRCode.Util.PATTERN_POSITION_TABLE[typeNumber - 1];
|
|
},
|
|
|
|
getMask: function (maskPattern, i, j) {
|
|
|
|
switch (maskPattern) {
|
|
|
|
case QRCode.MaskPattern.PATTERN000:
|
|
return (i + j) % 2 == 0;
|
|
case QRCode.MaskPattern.PATTERN001:
|
|
return i % 2 == 0;
|
|
case QRCode.MaskPattern.PATTERN010:
|
|
return j % 3 == 0;
|
|
case QRCode.MaskPattern.PATTERN011:
|
|
return (i + j) % 3 == 0;
|
|
case QRCode.MaskPattern.PATTERN100:
|
|
return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
|
|
case QRCode.MaskPattern.PATTERN101:
|
|
return (i * j) % 2 + (i * j) % 3 == 0;
|
|
case QRCode.MaskPattern.PATTERN110:
|
|
return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
|
|
case QRCode.MaskPattern.PATTERN111:
|
|
return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
|
|
|
|
default:
|
|
throw new Error("bad maskPattern:" + maskPattern);
|
|
}
|
|
},
|
|
|
|
getErrorCorrectPolynomial: function (errorCorrectLength) {
|
|
|
|
var a = new QRCode.Polynomial([1], 0);
|
|
|
|
for (var i = 0; i < errorCorrectLength; i++) {
|
|
a = a.multiply(new QRCode.Polynomial([1, QRCode.Math.gexp(i)], 0));
|
|
}
|
|
|
|
return a;
|
|
},
|
|
|
|
getLengthInBits: function (mode, type) {
|
|
|
|
if (1 <= type && type < 10) {
|
|
|
|
// 1 - 9
|
|
|
|
switch (mode) {
|
|
case QRCode.Mode.MODE_NUMBER:
|
|
return 10;
|
|
case QRCode.Mode.MODE_ALPHA_NUM:
|
|
return 9;
|
|
case QRCode.Mode.MODE_8BIT_BYTE:
|
|
return 8;
|
|
case QRCode.Mode.MODE_KANJI:
|
|
return 8;
|
|
default:
|
|
throw new Error("mode:" + mode);
|
|
}
|
|
|
|
} else if (type < 27) {
|
|
|
|
// 10 - 26
|
|
|
|
switch (mode) {
|
|
case QRCode.Mode.MODE_NUMBER:
|
|
return 12;
|
|
case QRCode.Mode.MODE_ALPHA_NUM:
|
|
return 11;
|
|
case QRCode.Mode.MODE_8BIT_BYTE:
|
|
return 16;
|
|
case QRCode.Mode.MODE_KANJI:
|
|
return 10;
|
|
default:
|
|
throw new Error("mode:" + mode);
|
|
}
|
|
|
|
} else if (type < 41) {
|
|
|
|
// 27 - 40
|
|
|
|
switch (mode) {
|
|
case QRCode.Mode.MODE_NUMBER:
|
|
return 14;
|
|
case QRCode.Mode.MODE_ALPHA_NUM:
|
|
return 13;
|
|
case QRCode.Mode.MODE_8BIT_BYTE:
|
|
return 16;
|
|
case QRCode.Mode.MODE_KANJI:
|
|
return 12;
|
|
default:
|
|
throw new Error("mode:" + mode);
|
|
}
|
|
|
|
} else {
|
|
throw new Error("type:" + type);
|
|
}
|
|
},
|
|
|
|
getLostPoint: function (qrCode) {
|
|
|
|
var moduleCount = qrCode.getModuleCount();
|
|
|
|
var lostPoint = 0;
|
|
|
|
// LEVEL1
|
|
|
|
for (var row = 0; row < moduleCount; row++) {
|
|
|
|
for (var col = 0; col < moduleCount; col++) {
|
|
|
|
var sameCount = 0;
|
|
var dark = qrCode.isDark(row, col);
|
|
|
|
for (var r = -1; r <= 1; r++) {
|
|
|
|
if (row + r < 0 || moduleCount <= row + r) {
|
|
continue;
|
|
}
|
|
|
|
for (var c = -1; c <= 1; c++) {
|
|
|
|
if (col + c < 0 || moduleCount <= col + c) {
|
|
continue;
|
|
}
|
|
|
|
if (r == 0 && c == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (dark == qrCode.isDark(row + r, col + c)) {
|
|
sameCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sameCount > 5) {
|
|
lostPoint += (3 + sameCount - 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
// LEVEL2
|
|
|
|
for (var row = 0; row < moduleCount - 1; row++) {
|
|
for (var col = 0; col < moduleCount - 1; col++) {
|
|
var count = 0;
|
|
if (qrCode.isDark(row, col)) count++;
|
|
if (qrCode.isDark(row + 1, col)) count++;
|
|
if (qrCode.isDark(row, col + 1)) count++;
|
|
if (qrCode.isDark(row + 1, col + 1)) count++;
|
|
if (count == 0 || count == 4) {
|
|
lostPoint += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
// LEVEL3
|
|
|
|
for (var row = 0; row < moduleCount; row++) {
|
|
for (var col = 0; col < moduleCount - 6; col++) {
|
|
if (qrCode.isDark(row, col) &&
|
|
!qrCode.isDark(row, col + 1) &&
|
|
qrCode.isDark(row, col + 2) &&
|
|
qrCode.isDark(row, col + 3) &&
|
|
qrCode.isDark(row, col + 4) &&
|
|
!qrCode.isDark(row, col + 5) &&
|
|
qrCode.isDark(row, col + 6)) {
|
|
lostPoint += 40;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var col = 0; col < moduleCount; col++) {
|
|
for (var row = 0; row < moduleCount - 6; row++) {
|
|
if (qrCode.isDark(row, col) &&
|
|
!qrCode.isDark(row + 1, col) &&
|
|
qrCode.isDark(row + 2, col) &&
|
|
qrCode.isDark(row + 3, col) &&
|
|
qrCode.isDark(row + 4, col) &&
|
|
!qrCode.isDark(row + 5, col) &&
|
|
qrCode.isDark(row + 6, col)) {
|
|
lostPoint += 40;
|
|
}
|
|
}
|
|
}
|
|
|
|
// LEVEL4
|
|
|
|
var darkCount = 0;
|
|
|
|
for (var col = 0; col < moduleCount; col++) {
|
|
for (var row = 0; row < moduleCount; row++) {
|
|
if (qrCode.isDark(row, col)) {
|
|
darkCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
|
lostPoint += ratio * 10;
|
|
|
|
return lostPoint;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRMath
|
|
//---------------------------------------------------------------------
|
|
|
|
QRCode.Math = {
|
|
|
|
glog: function (n) {
|
|
|
|
if (n < 1) {
|
|
throw new Error("glog(" + n + ")");
|
|
}
|
|
|
|
return QRCode.Math.LOG_TABLE[n];
|
|
},
|
|
|
|
gexp: function (n) {
|
|
|
|
while (n < 0) {
|
|
n += 255;
|
|
}
|
|
|
|
while (n >= 256) {
|
|
n -= 255;
|
|
}
|
|
|
|
return QRCode.Math.EXP_TABLE[n];
|
|
},
|
|
|
|
EXP_TABLE: new Array(256),
|
|
|
|
LOG_TABLE: new Array(256)
|
|
|
|
};
|
|
|
|
for (var i = 0; i < 8; i++) {
|
|
QRCode.Math.EXP_TABLE[i] = 1 << i;
|
|
}
|
|
for (var i = 8; i < 256; i++) {
|
|
QRCode.Math.EXP_TABLE[i] = QRCode.Math.EXP_TABLE[i - 4] ^
|
|
QRCode.Math.EXP_TABLE[i - 5] ^
|
|
QRCode.Math.EXP_TABLE[i - 6] ^
|
|
QRCode.Math.EXP_TABLE[i - 8];
|
|
}
|
|
for (var i = 0; i < 255; i++) {
|
|
QRCode.Math.LOG_TABLE[QRCode.Math.EXP_TABLE[i]] = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRPolynomial
|
|
//---------------------------------------------------------------------
|
|
|
|
QRCode.Polynomial = function (num, shift) {
|
|
|
|
if (num.length == undefined) {
|
|
throw new Error(num.length + "/" + shift);
|
|
}
|
|
|
|
var offset = 0;
|
|
|
|
while (offset < num.length && num[offset] == 0) {
|
|
offset++;
|
|
}
|
|
|
|
this.num = new Array(num.length - offset + shift);
|
|
for (var i = 0; i < num.length - offset; i++) {
|
|
this.num[i] = num[i + offset];
|
|
}
|
|
}
|
|
|
|
QRCode.Polynomial.prototype = {
|
|
|
|
get: function (index) {
|
|
return this.num[index];
|
|
},
|
|
|
|
getLength: function () {
|
|
return this.num.length;
|
|
},
|
|
|
|
multiply: function (e) {
|
|
|
|
var num = new Array(this.getLength() + e.getLength() - 1);
|
|
|
|
for (var i = 0; i < this.getLength(); i++) {
|
|
for (var j = 0; j < e.getLength(); j++) {
|
|
num[i + j] ^= QRCode.Math.gexp(QRCode.Math.glog(this.get(i)) + QRCode.Math.glog(
|
|
e.get(
|
|
j)));
|
|
}
|
|
}
|
|
|
|
return new QRCode.Polynomial(num, 0);
|
|
},
|
|
|
|
mod: function (e) {
|
|
|
|
if (this.getLength() - e.getLength() < 0) {
|
|
return this;
|
|
}
|
|
|
|
var ratio = QRCode.Math.glog(this.get(0)) - QRCode.Math.glog(e.get(0));
|
|
|
|
var num = new Array(this.getLength());
|
|
|
|
for (var i = 0; i < this.getLength(); i++) {
|
|
num[i] = this.get(i);
|
|
}
|
|
|
|
for (var i = 0; i < e.getLength(); i++) {
|
|
num[i] ^= QRCode.Math.gexp(QRCode.Math.glog(e.get(i)) + ratio);
|
|
}
|
|
|
|
// recursive call
|
|
return new QRCode.Polynomial(num, 0).mod(e);
|
|
}
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRRSBlock
|
|
//---------------------------------------------------------------------
|
|
|
|
QRCode.RSBlock = function (totalCount, dataCount) {
|
|
this.totalCount = totalCount;
|
|
this.dataCount = dataCount;
|
|
}
|
|
|
|
QRCode.RSBlock.RS_BLOCK_TABLE = [
|
|
|
|
// L
|
|
// M
|
|
// Q
|
|
// H
|
|
|
|
// 1
|
|
[1, 26, 19],
|
|
[1, 26, 16],
|
|
[1, 26, 13],
|
|
[1, 26, 9],
|
|
|
|
// 2
|
|
[1, 44, 34],
|
|
[1, 44, 28],
|
|
[1, 44, 22],
|
|
[1, 44, 16],
|
|
|
|
// 3
|
|
[1, 70, 55],
|
|
[1, 70, 44],
|
|
[2, 35, 17],
|
|
[2, 35, 13],
|
|
|
|
// 4
|
|
[1, 100, 80],
|
|
[2, 50, 32],
|
|
[2, 50, 24],
|
|
[4, 25, 9],
|
|
|
|
// 5
|
|
[1, 134, 108],
|
|
[2, 67, 43],
|
|
[2, 33, 15, 2, 34, 16],
|
|
[2, 33, 11, 2, 34, 12],
|
|
|
|
// 6
|
|
[2, 86, 68],
|
|
[4, 43, 27],
|
|
[4, 43, 19],
|
|
[4, 43, 15],
|
|
|
|
// 7
|
|
[2, 98, 78],
|
|
[4, 49, 31],
|
|
[2, 32, 14, 4, 33, 15],
|
|
[4, 39, 13, 1, 40, 14],
|
|
|
|
// 8
|
|
[2, 121, 97],
|
|
[2, 60, 38, 2, 61, 39],
|
|
[4, 40, 18, 2, 41, 19],
|
|
[4, 40, 14, 2, 41, 15],
|
|
|
|
// 9
|
|
[2, 146, 116],
|
|
[3, 58, 36, 2, 59, 37],
|
|
[4, 36, 16, 4, 37, 17],
|
|
[4, 36, 12, 4, 37, 13],
|
|
|
|
// 10
|
|
[2, 86, 68, 2, 87, 69],
|
|
[4, 69, 43, 1, 70, 44],
|
|
[6, 43, 19, 2, 44, 20],
|
|
[6, 43, 15, 2, 44, 16]
|
|
|
|
];
|
|
|
|
QRCode.RSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
|
|
|
|
var rsBlock = QRCode.RSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
|
|
|
|
if (rsBlock == undefined) {
|
|
throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" +
|
|
errorCorrectLevel);
|
|
}
|
|
|
|
var length = rsBlock.length / 3;
|
|
|
|
var list = new Array();
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
var count = rsBlock[i * 3 + 0];
|
|
var totalCount = rsBlock[i * 3 + 1];
|
|
var dataCount = rsBlock[i * 3 + 2];
|
|
|
|
for (var j = 0; j < count; j++) {
|
|
list.push(new QRCode.RSBlock(totalCount, dataCount));
|
|
}
|
|
}
|
|
|
|
return list;
|
|
};
|
|
|
|
QRCode.RSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {
|
|
|
|
switch (errorCorrectLevel) {
|
|
case QRCode.ErrorCorrectLevel.L:
|
|
return QRCode.RSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
|
case QRCode.ErrorCorrectLevel.M:
|
|
return QRCode.RSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
|
case QRCode.ErrorCorrectLevel.Q:
|
|
return QRCode.RSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
|
case QRCode.ErrorCorrectLevel.H:
|
|
return QRCode.RSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
|
default:
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// QRBitBuffer
|
|
//---------------------------------------------------------------------
|
|
|
|
QRCode.BitBuffer = function () {
|
|
this.buffer = new Array();
|
|
this.length = 0;
|
|
}
|
|
|
|
QRCode.BitBuffer.prototype = {
|
|
|
|
get: function (index) {
|
|
var bufIndex = Math.floor(index / 8);
|
|
return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1;
|
|
},
|
|
|
|
put: function (num, length) {
|
|
for (var i = 0; i < length; i++) {
|
|
this.putBit(((num >>> (length - i - 1)) & 1) == 1);
|
|
}
|
|
},
|
|
|
|
getLengthInBits: function () {
|
|
return this.length;
|
|
},
|
|
|
|
putBit: function (bit) {
|
|
|
|
var bufIndex = Math.floor(this.length / 8);
|
|
if (this.buffer.length <= bufIndex) {
|
|
this.buffer.push(0);
|
|
}
|
|
|
|
if (bit) {
|
|
this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
|
|
}
|
|
|
|
this.length++;
|
|
}
|
|
};
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*
|
|
Copyright (c) 2011 Stefan Thomas
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/1a7fc9d063f864058809d06ef4542af40be3558f/src/bitcoin.js
|
|
(function (exports) {
|
|
var Bitcoin = exports;
|
|
})(
|
|
'object' === typeof module ? module.exports : (window.Bitcoin = {})
|
|
);
|
|
</script>
|
|
<script type="text/javascript">
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/c952aaeb3ee472e3776655b8ea07299ebed702c7/src/base58.js
|
|
(function (Bitcoin) {
|
|
Bitcoin.Base58 = {
|
|
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/,
|
|
base: BigInteger.valueOf(58),
|
|
|
|
/**
|
|
* 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 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.
|
|
for (var i = 0; i < input.length; i++) {
|
|
if (input[i] == 0x00) {
|
|
chars.unshift(B58.alphabet[0]);
|
|
} else break;
|
|
}
|
|
|
|
return chars.join('');
|
|
},
|
|
|
|
/**
|
|
* Convert a base58-encoded string to a byte array.
|
|
*
|
|
* Written by Mike Hearn for BitcoinJ.
|
|
* Copyright (c) 2011 Google Inc.
|
|
*
|
|
* Ported to JavaScript by Stefan Thomas.
|
|
*/
|
|
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
|
|
if (input[i] == "1") leadingZerosNum++;
|
|
else leadingZerosNum = 0;
|
|
}
|
|
var bytes = bi.toByteArrayUnsigned();
|
|
|
|
// Add leading zeros
|
|
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
|
|
|
return bytes;
|
|
}
|
|
};
|
|
|
|
var B58 = Bitcoin.Base58;
|
|
})(
|
|
'undefined' != typeof Bitcoin ? Bitcoin : module.exports
|
|
);
|
|
</script>
|
|
<script type="text/javascript">
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/address.js
|
|
Bitcoin.Address = function (bytes) {
|
|
if ("string" == typeof bytes) {
|
|
bytes = Bitcoin.Address.decodeString(bytes);
|
|
}
|
|
this.hash = bytes;
|
|
this.version = Bitcoin.Address.networkVersion;
|
|
};
|
|
|
|
//Bitcoin.Address.networkVersion = 0x23; // (FLO mainnet 0x23, 35D), (Bitcoin Mainnet, 0x00, 0D)
|
|
Bitcoin.Address.networkVersion = 0x73; // (FLO mainnet 0x23, 35D), (Bitcoin Mainnet, 0x00, 0D)
|
|
|
|
/**
|
|
* Serialize this object as a standard Bitcoin address.
|
|
*
|
|
* Returns the address as a base58-encoded string in the standardized format.
|
|
*/
|
|
Bitcoin.Address.prototype.toString = function () {
|
|
// Get a copy of the hash
|
|
var hash = this.hash.slice(0);
|
|
|
|
// Version
|
|
hash.unshift(this.version);
|
|
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
var bytes = hash.concat(checksum.slice(0, 4));
|
|
return Bitcoin.Base58.encode(bytes);
|
|
};
|
|
|
|
Bitcoin.Address.prototype.getHashBase64 = function () {
|
|
return Crypto.util.bytesToBase64(this.hash);
|
|
};
|
|
|
|
/**
|
|
* Parse a Bitcoin address contained in a string.
|
|
*/
|
|
Bitcoin.Address.decodeString = function (string) {
|
|
var bytes = Bitcoin.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 "Checksum validation failed!";
|
|
}
|
|
|
|
var version = hash.shift();
|
|
|
|
if (version != 0) {
|
|
throw "Version " + version + " not supported!";
|
|
}
|
|
|
|
return hash;
|
|
};
|
|
</script>
|
|
<script type="text/javascript">
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/e90780d3d3b8fc0d027d2bcb38b80479902f223e/src/ecdsa.js
|
|
Bitcoin.ECDSA = (function () {
|
|
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var rng = new SecureRandom();
|
|
|
|
var P_OVER_FOUR = null;
|
|
|
|
function implShamirsTrick(P, k, Q, l) {
|
|
var m = Math.max(k.bitLength(), l.bitLength());
|
|
var Z = P.add2D(Q);
|
|
var R = P.curve.getInfinity();
|
|
|
|
for (var i = m - 1; i >= 0; --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 {
|
|
if (l.testBit(i)) {
|
|
R = R.add2D(Q);
|
|
}
|
|
}
|
|
}
|
|
|
|
return R;
|
|
};
|
|
|
|
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 ec.PointFp) {
|
|
Q = pubkey;
|
|
} else if (Bitcoin.Util.isArray(pubkey)) {
|
|
Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
|
} else {
|
|
throw "Invalid format for pubkey value, must be byte array or ec.PointFp";
|
|
}
|
|
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 EllipticCurve.PointFp(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;
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
Bitcoin.KeyPool = (function () {
|
|
var KeyPool = function () {
|
|
this.keyArray = [];
|
|
|
|
this.push = function (item) {
|
|
if (item == null || item.priv == null) return;
|
|
var doAdd = true;
|
|
// prevent duplicates from being added to the array
|
|
for (var index in this.keyArray) {
|
|
var currentItem = this.keyArray[index];
|
|
if (currentItem != null && currentItem.priv != null && item.getBitcoinAddress() ==
|
|
currentItem.getBitcoinAddress()) {
|
|
doAdd = false;
|
|
break;
|
|
}
|
|
}
|
|
if (doAdd) this.keyArray.push(item);
|
|
};
|
|
|
|
this.reset = function () {
|
|
this.keyArray = [];
|
|
};
|
|
|
|
this.getArray = function () {
|
|
// copy array
|
|
return this.keyArray.slice(0);
|
|
};
|
|
|
|
this.setArray = function (ka) {
|
|
this.keyArray = ka;
|
|
};
|
|
|
|
this.length = function () {
|
|
return this.keyArray.length;
|
|
};
|
|
|
|
this.toString = function () {
|
|
var keyPoolString = "# = " + this.length() + "\n";
|
|
var pool = this.getArray();
|
|
for (var index in pool) {
|
|
var item = pool[index];
|
|
if (Bitcoin.Util.hasMethods(item, 'getBitcoinAddress', 'toString')) {
|
|
if (item != null) {
|
|
keyPoolString += "\"" + item.getBitcoinAddress() + "\"" + ", \"" + item
|
|
.toString(
|
|
"wif") + "\"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
return keyPoolString;
|
|
};
|
|
|
|
return this;
|
|
};
|
|
|
|
return new KeyPool();
|
|
})();
|
|
|
|
Bitcoin.Bip38Key = (function () {
|
|
var Bip38 = function (address, encryptedKey) {
|
|
this.address = address;
|
|
this.priv = encryptedKey;
|
|
};
|
|
|
|
Bip38.prototype.getBitcoinAddress = function () {
|
|
return this.address;
|
|
};
|
|
|
|
Bip38.prototype.toString = function () {
|
|
return this.priv;
|
|
};
|
|
|
|
return Bip38;
|
|
})();
|
|
|
|
//https://raw.github.com/pointbiz/bitcoinjs-lib/9b2f94a028a7bc9bed94e0722563e9ff1d8e8db8/src/eckey.js
|
|
Bitcoin.ECKey = (function () {
|
|
var ECDSA = Bitcoin.ECDSA;
|
|
var KeyPool = Bitcoin.KeyPool;
|
|
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
|
|
var ECKey = function (input) {
|
|
if (!input) {
|
|
// Generate new key
|
|
var n = ecparams.getN();
|
|
this.priv = ECDSA.getBigRandom(n);
|
|
} else if (input instanceof BigInteger) {
|
|
// Input is a private key value
|
|
this.priv = input;
|
|
} else if (Bitcoin.Util.isArray(input)) {
|
|
// Prepend zero byte to prevent interpretation as negative integer
|
|
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
|
} else if ("string" == typeof input) {
|
|
var bytes = null;
|
|
try {
|
|
if (ECKey.isWalletImportFormat(input)) {
|
|
bytes = ECKey.decodeWalletImportFormat(input);
|
|
} else if (ECKey.isCompressedWalletImportFormat(input)) {
|
|
bytes = ECKey.decodeCompressedWalletImportFormat(input);
|
|
this.compressed = true;
|
|
} else if (ECKey.isMiniFormat(input)) {
|
|
bytes = Crypto.SHA256(input, {
|
|
asBytes: true
|
|
});
|
|
} else if (ECKey.isHexFormat(input)) {
|
|
bytes = Crypto.util.hexToBytes(input);
|
|
} else if (ECKey.isBase64Format(input)) {
|
|
bytes = Crypto.util.base64ToBytes(input);
|
|
}
|
|
} catch (exc1) {
|
|
this.setError(exc1);
|
|
}
|
|
|
|
if (ECKey.isBase6Format(input)) {
|
|
this.priv = new BigInteger(input, 6);
|
|
} else if (bytes == null || bytes.length != 32) {
|
|
this.priv = null;
|
|
} else {
|
|
// Prepend zero byte to prevent interpretation as negative integer
|
|
this.priv = BigInteger.fromByteArrayUnsigned(bytes);
|
|
}
|
|
}
|
|
|
|
this.compressed = (this.compressed == undefined) ? !!ECKey.compressByDefault : this.compressed;
|
|
try {
|
|
// check not zero
|
|
if (this.priv != null && BigInteger.ZERO.compareTo(this.priv) == 0) this.setError(
|
|
"Error: BigInteger equal to zero.");
|
|
// valid range [0x1, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140])
|
|
var hexKeyRangeLimit =
|
|
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140";
|
|
var rangeLimitBytes = Crypto.util.hexToBytes(hexKeyRangeLimit);
|
|
var limitBigInt = BigInteger.fromByteArrayUnsigned(rangeLimitBytes);
|
|
if (this.priv != null && limitBigInt.compareTo(this.priv) < 0) this.setError(
|
|
"Error: BigInteger outside of curve range.")
|
|
|
|
if (this.priv != null) {
|
|
KeyPool.push(this);
|
|
}
|
|
} catch (exc2) {
|
|
this.setError(exc2);
|
|
}
|
|
};
|
|
|
|
//FLO Mainnet (0xA3) --- FLO Testnet (0xEF)
|
|
ECKey.privateKeyPrefix = 0xEF; //(Bitcoin mainnet 0x80 testnet 0xEF) (FLO mainnet 0xA3 163 D)
|
|
|
|
/**
|
|
* 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.setError = function (err) {
|
|
this.error = err;
|
|
this.priv = null;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Set whether the public key should be returned compressed or not.
|
|
*/
|
|
ECKey.prototype.setCompressed = function (v) {
|
|
this.compressed = !!v;
|
|
if (this.pubPoint) this.pubPoint.compressed = this.compressed;
|
|
return this;
|
|
};
|
|
|
|
/*
|
|
* Return public key as a byte array in DER encoding
|
|
*/
|
|
ECKey.prototype.getPub = function () {
|
|
if (this.compressed) {
|
|
if (this.pubComp) return this.pubComp;
|
|
return this.pubComp = this.getPubPoint().getEncoded(1);
|
|
} else {
|
|
if (this.pubUncomp) return this.pubUncomp;
|
|
return this.pubUncomp = this.getPubPoint().getEncoded(0);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return public point as ECPoint object.
|
|
*/
|
|
ECKey.prototype.getPubPoint = function () {
|
|
if (!this.pubPoint) {
|
|
this.pubPoint = ecparams.getG().multiply(this.priv);
|
|
this.pubPoint.compressed = this.compressed;
|
|
}
|
|
return this.pubPoint;
|
|
};
|
|
|
|
ECKey.prototype.getPubKeyHex = function () {
|
|
if (this.compressed) {
|
|
if (this.pubKeyHexComp) return this.pubKeyHexComp;
|
|
return this.pubKeyHexComp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
|
|
} else {
|
|
if (this.pubKeyHexUncomp) return this.pubKeyHexUncomp;
|
|
return this.pubKeyHexUncomp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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.compressed) {
|
|
if (this.pubKeyHashComp) return this.pubKeyHashComp;
|
|
return this.pubKeyHashComp = Bitcoin.Util.sha256ripe160(this.getPub());
|
|
} else {
|
|
if (this.pubKeyHashUncomp) return this.pubKeyHashUncomp;
|
|
return this.pubKeyHashUncomp = Bitcoin.Util.sha256ripe160(this.getPub());
|
|
}
|
|
};
|
|
|
|
ECKey.prototype.getBitcoinAddress = function () {
|
|
var hash = this.getPubKeyHash();
|
|
var addr = new Bitcoin.Address(hash);
|
|
return addr.toString();
|
|
};
|
|
|
|
/*
|
|
* Takes a public point as a hex string or byte array
|
|
*/
|
|
ECKey.prototype.setPub = function (pub) {
|
|
// byte array
|
|
if (Bitcoin.Util.isArray(pub)) {
|
|
pub = Crypto.util.bytesToHex(pub).toString().toUpperCase();
|
|
}
|
|
var ecPoint = ecparams.getCurve().decodePointHex(pub);
|
|
this.setCompressed(ecPoint.compressed);
|
|
this.pubPoint = ecPoint;
|
|
return this;
|
|
};
|
|
|
|
// Sipa Private Key Wallet Import Format
|
|
ECKey.prototype.getBitcoinWalletImportFormat = function () {
|
|
var bytes = this.getBitcoinPrivateKeyByteArray();
|
|
if (bytes == null) return "";
|
|
bytes.unshift(ECKey.privateKeyPrefix); // prepend 0x80 byte
|
|
if (this.compressed) bytes.push(0x01); // append 0x01 byte for compressed format
|
|
var checksum = Crypto.SHA256(Crypto.SHA256(bytes, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
bytes = bytes.concat(checksum.slice(0, 4));
|
|
var privWif = Bitcoin.Base58.encode(bytes);
|
|
return privWif;
|
|
};
|
|
|
|
// Private Key Hex Format
|
|
ECKey.prototype.getBitcoinHexFormat = function () {
|
|
return Crypto.util.bytesToHex(this.getBitcoinPrivateKeyByteArray()).toString().toUpperCase();
|
|
};
|
|
|
|
// Private Key Base64 Format
|
|
ECKey.prototype.getBitcoinBase64Format = function () {
|
|
return Crypto.util.bytesToBase64(this.getBitcoinPrivateKeyByteArray());
|
|
};
|
|
|
|
ECKey.prototype.getBitcoinPrivateKeyByteArray = function () {
|
|
if (this.priv == null) return null;
|
|
// Get a copy of private key as a byte array
|
|
var bytes = this.priv.toByteArrayUnsigned();
|
|
// zero pad if private key is less than 32 bytes
|
|
while (bytes.length < 32) bytes.unshift(0x00);
|
|
return bytes;
|
|
};
|
|
|
|
ECKey.prototype.toString = function (format) {
|
|
format = format || "";
|
|
if (format.toString().toLowerCase() == "base64" || format.toString().toLowerCase() == "b64") {
|
|
return this.getBitcoinBase64Format();
|
|
}
|
|
// Wallet Import Format
|
|
else if (format.toString().toLowerCase() == "wif") {
|
|
return this.getBitcoinWalletImportFormat();
|
|
} else {
|
|
return this.getBitcoinHexFormat();
|
|
}
|
|
};
|
|
|
|
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 a wallet import format private key contained in a string.
|
|
*/
|
|
ECKey.decodeWalletImportFormat = function (privStr) {
|
|
var bytes = Bitcoin.Base58.decode(privStr);
|
|
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 != ECKey.privateKeyPrefix) {
|
|
throw "Version " + version + " not supported!";
|
|
}
|
|
return hash;
|
|
};
|
|
|
|
/**
|
|
* Parse a compressed wallet import format private key contained in a string.
|
|
*/
|
|
ECKey.decodeCompressedWalletImportFormat = function (privStr) {
|
|
var bytes = Bitcoin.Base58.decode(privStr);
|
|
var hash = bytes.slice(0, 34);
|
|
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
if (checksum[0] != bytes[34] ||
|
|
checksum[1] != bytes[35] ||
|
|
checksum[2] != bytes[36] ||
|
|
checksum[3] != bytes[37]) {
|
|
throw "Checksum validation failed!";
|
|
}
|
|
var version = hash.shift();
|
|
if (version != ECKey.privateKeyPrefix) {
|
|
throw "Version " + version + " not supported!";
|
|
}
|
|
hash.pop();
|
|
return hash;
|
|
};
|
|
|
|
// 64 characters [0-9A-F]
|
|
ECKey.isHexFormat = function (key) {
|
|
key = key.toString();
|
|
return /^[A-Fa-f0-9]{64}$/.test(key);
|
|
};
|
|
|
|
// 51 characters base58, always starts with a '5'
|
|
ECKey.isWalletImportFormat = function (key) {
|
|
key = key.toString();
|
|
return (ECKey.privateKeyPrefix == 0x80) ?
|
|
(/^5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key)) :
|
|
(/^9[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key));
|
|
};
|
|
|
|
// 52 characters base58
|
|
ECKey.isCompressedWalletImportFormat = function (key) {
|
|
key = key.toString();
|
|
return (ECKey.privateKeyPrefix == 0x80) ?
|
|
(/^[LK][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key)) :
|
|
(/^c[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key));
|
|
};
|
|
|
|
// 44 characters
|
|
ECKey.isBase64Format = function (key) {
|
|
key = key.toString();
|
|
return (/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+\/]{44}$/.test(
|
|
key));
|
|
};
|
|
|
|
// 99 characters, 1=1, if using dice convert 6 to 0
|
|
ECKey.isBase6Format = function (key) {
|
|
key = key.toString();
|
|
return (/^[012345]{99}$/.test(key));
|
|
};
|
|
|
|
// 22, 26 or 30 characters, always starts with an 'S'
|
|
ECKey.isMiniFormat = function (key) {
|
|
key = key.toString();
|
|
var validChars22 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21}$/.test(
|
|
key);
|
|
var validChars26 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25}$/.test(
|
|
key);
|
|
var validChars30 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{29}$/.test(
|
|
key);
|
|
var testBytes = Crypto.SHA256(key + "?", {
|
|
asBytes: true
|
|
});
|
|
|
|
return ((testBytes[0] === 0x00 || testBytes[0] === 0x01) && (validChars22 || validChars26 ||
|
|
validChars30));
|
|
};
|
|
|
|
return ECKey;
|
|
})();
|
|
</script>
|
|
<script type="text/javascript">
|
|
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/util.js
|
|
// Bitcoin utility functions
|
|
Bitcoin.Util = {
|
|
/**
|
|
* Cross-browser compatibility version of Array.isArray.
|
|
*/
|
|
isArray: Array.isArray || function (o) {
|
|
return Object.prototype.toString.call(o) === '[object Array]';
|
|
},
|
|
/**
|
|
* Create an array of a certain length filled with a specific value.
|
|
*/
|
|
makeFilledArray: function (len, val) {
|
|
var array = [];
|
|
var i = 0;
|
|
while (i < len) {
|
|
array[i++] = val;
|
|
}
|
|
return array;
|
|
},
|
|
/**
|
|
* Turn an integer into a "var_int".
|
|
*
|
|
* "var_int" is a variable length integer used by Bitcoin's binary format.
|
|
*
|
|
* Returns a byte array.
|
|
*/
|
|
numToVarInt: function (i) {
|
|
if (i < 0xfd) {
|
|
// unsigned char
|
|
return [i];
|
|
} else if (i <= 1 << 16) {
|
|
// unsigned short (LE)
|
|
return [0xfd, i >>> 8, i & 255];
|
|
} else if (i <= 1 << 32) {
|
|
// unsigned int (LE)
|
|
return [0xfe].concat(Crypto.util.wordsToBytes([i]));
|
|
} else {
|
|
// unsigned long long (LE)
|
|
return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i]));
|
|
}
|
|
},
|
|
/**
|
|
* Parse a Bitcoin value byte array, returning a BigInteger.
|
|
*/
|
|
valueToBigInt: function (valueBuffer) {
|
|
if (valueBuffer instanceof BigInteger) return valueBuffer;
|
|
|
|
// Prepend zero byte to prevent interpretation as negative integer
|
|
return BigInteger.fromByteArrayUnsigned(valueBuffer);
|
|
},
|
|
/**
|
|
* Format a Bitcoin value as a string.
|
|
*
|
|
* Takes a BigInteger or byte-array and returns that amount of Bitcoins in a
|
|
* nice standard formatting.
|
|
*
|
|
* Examples:
|
|
* 12.3555
|
|
* 0.1234
|
|
* 900.99998888
|
|
* 34.00
|
|
*/
|
|
formatValue: function (valueBuffer) {
|
|
var value = this.valueToBigInt(valueBuffer).toString();
|
|
var integerPart = value.length > 8 ? value.substr(0, value.length - 8) : '0';
|
|
var decimalPart = value.length > 8 ? value.substr(value.length - 8) : value;
|
|
while (decimalPart.length < 8) decimalPart = "0" + decimalPart;
|
|
decimalPart = decimalPart.replace(/0*$/, '');
|
|
while (decimalPart.length < 2) decimalPart += "0";
|
|
return integerPart + "." + decimalPart;
|
|
},
|
|
/**
|
|
* Parse a floating point string as a Bitcoin value.
|
|
*
|
|
* Keep in mind that parsing user input is messy. You should always display
|
|
* the parsed value back to the user to make sure we understood his input
|
|
* correctly.
|
|
*/
|
|
parseValue: function (valueString) {
|
|
// TODO: Detect other number formats (e.g. comma as decimal separator)
|
|
var valueComp = valueString.split('.');
|
|
var integralPart = valueComp[0];
|
|
var fractionalPart = valueComp[1] || "0";
|
|
while (fractionalPart.length < 8) fractionalPart += "0";
|
|
fractionalPart = fractionalPart.replace(/^0+/g, '');
|
|
var value = BigInteger.valueOf(parseInt(integralPart));
|
|
value = value.multiply(BigInteger.valueOf(100000000));
|
|
value = value.add(BigInteger.valueOf(parseInt(fractionalPart)));
|
|
return value;
|
|
},
|
|
/**
|
|
* Calculate RIPEMD160(SHA256(data)).
|
|
*
|
|
* Takes an arbitrary byte array as inputs and returns the hash as a byte
|
|
* array.
|
|
*/
|
|
sha256ripe160: function (data) {
|
|
return Crypto.RIPEMD160(Crypto.SHA256(data, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
},
|
|
// double sha256
|
|
dsha256: function (data) {
|
|
return Crypto.SHA256(Crypto.SHA256(data, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
},
|
|
// duck typing method
|
|
hasMethods: function (obj /*, method list as strings */ ) {
|
|
var i = 1,
|
|
methodName;
|
|
while ((methodName = arguments[i++])) {
|
|
if (typeof obj[methodName] != 'function') {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
</script>
|
|
<script type="text/javascript">
|
|
/*
|
|
* Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
// https://github.com/cheongwy/node-scrypt-js
|
|
(function () {
|
|
|
|
var MAX_VALUE = 2147483647;
|
|
var workerUrl = null;
|
|
|
|
//function scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen)
|
|
/*
|
|
* N = Cpu cost
|
|
* r = Memory cost
|
|
* p = parallelization cost
|
|
*
|
|
*/
|
|
window.Crypto_scrypt = function (passwd, salt, N, r, p, dkLen, callback) {
|
|
if (N == 0 || (N & (N - 1)) != 0) throw Error("N must be > 0 and a power of 2");
|
|
|
|
if (N > MAX_VALUE / 128 / r) throw Error("Parameter N is too large");
|
|
if (r > MAX_VALUE / 128 / p) throw Error("Parameter r is too large");
|
|
|
|
var PBKDF2_opts = {
|
|
iterations: 1,
|
|
hasher: Crypto.SHA256,
|
|
asBytes: true
|
|
};
|
|
|
|
var B = Crypto.PBKDF2(passwd, salt, p * 128 * r, PBKDF2_opts);
|
|
|
|
try {
|
|
var i = 0;
|
|
var worksDone = 0;
|
|
var makeWorker = function () {
|
|
if (!workerUrl) {
|
|
var code = '(' + scryptCore.toString() + ')()';
|
|
var blob;
|
|
try {
|
|
blob = new Blob([code], {
|
|
type: "text/javascript"
|
|
});
|
|
} catch (e) {
|
|
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
|
|
window.MozBlobBuilder ||
|
|
window.MSBlobBuilder;
|
|
blob = new BlobBuilder();
|
|
blob.append(code);
|
|
blob = blob.getBlob("text/javascript");
|
|
}
|
|
workerUrl = URL.createObjectURL(blob);
|
|
}
|
|
var worker = new Worker(workerUrl);
|
|
worker.onmessage = function (event) {
|
|
var Bi = event.data[0],
|
|
Bslice = event.data[1];
|
|
worksDone++;
|
|
|
|
if (i < p) {
|
|
worker.postMessage([N, r, p, B, i++]);
|
|
}
|
|
|
|
var length = Bslice.length,
|
|
destPos = Bi * 128 * r,
|
|
srcPos = 0;
|
|
while (length--) {
|
|
B[destPos++] = Bslice[srcPos++];
|
|
}
|
|
|
|
if (worksDone == p) {
|
|
callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
|
|
}
|
|
};
|
|
return worker;
|
|
};
|
|
var workers = [makeWorker(), makeWorker()];
|
|
workers[0].postMessage([N, r, p, B, i++]);
|
|
if (p > 1) {
|
|
workers[1].postMessage([N, r, p, B, i++]);
|
|
}
|
|
} catch (e) {
|
|
window.setTimeout(function () {
|
|
scryptCore();
|
|
callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
|
|
}, 0);
|
|
}
|
|
|
|
// using this function to enclose everything needed to create a worker (but also invokable directly for synchronous use)
|
|
function scryptCore() {
|
|
var XY = [],
|
|
V = [];
|
|
|
|
if (typeof B === 'undefined') {
|
|
onmessage = function (event) {
|
|
var data = event.data;
|
|
var N = data[0],
|
|
r = data[1],
|
|
p = data[2],
|
|
B = data[3],
|
|
i = data[4];
|
|
|
|
var Bslice = [];
|
|
arraycopy32(B, i * 128 * r, Bslice, 0, 128 * r);
|
|
smix(Bslice, 0, r, N, V, XY);
|
|
|
|
postMessage([i, Bslice]);
|
|
};
|
|
} else {
|
|
for (var i = 0; i < p; i++) {
|
|
smix(B, i * 128 * r, r, N, V, XY);
|
|
}
|
|
}
|
|
|
|
function smix(B, Bi, r, N, V, XY) {
|
|
var Xi = 0;
|
|
var Yi = 128 * r;
|
|
var i;
|
|
|
|
arraycopy32(B, Bi, XY, Xi, Yi);
|
|
|
|
for (i = 0; i < N; i++) {
|
|
arraycopy32(XY, Xi, V, i * Yi, Yi);
|
|
blockmix_salsa8(XY, Xi, Yi, r);
|
|
}
|
|
|
|
for (i = 0; i < N; i++) {
|
|
var j = integerify(XY, Xi, r) & (N - 1);
|
|
blockxor(V, j * Yi, XY, Xi, Yi);
|
|
blockmix_salsa8(XY, Xi, Yi, r);
|
|
}
|
|
|
|
arraycopy32(XY, Xi, B, Bi, Yi);
|
|
}
|
|
|
|
function blockmix_salsa8(BY, Bi, Yi, r) {
|
|
var X = [];
|
|
var i;
|
|
|
|
arraycopy32(BY, Bi + (2 * r - 1) * 64, X, 0, 64);
|
|
|
|
for (i = 0; i < 2 * r; i++) {
|
|
blockxor(BY, i * 64, X, 0, 64);
|
|
salsa20_8(X);
|
|
arraycopy32(X, 0, BY, Yi + (i * 64), 64);
|
|
}
|
|
|
|
for (i = 0; i < r; i++) {
|
|
arraycopy32(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64);
|
|
}
|
|
|
|
for (i = 0; i < r; i++) {
|
|
arraycopy32(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64);
|
|
}
|
|
}
|
|
|
|
function R(a, b) {
|
|
return (a << b) | (a >>> (32 - b));
|
|
}
|
|
|
|
function salsa20_8(B) {
|
|
var B32 = new Array(32);
|
|
var x = new Array(32);
|
|
var i;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
B32[i] = (B[i * 4 + 0] & 0xff) << 0;
|
|
B32[i] |= (B[i * 4 + 1] & 0xff) << 8;
|
|
B32[i] |= (B[i * 4 + 2] & 0xff) << 16;
|
|
B32[i] |= (B[i * 4 + 3] & 0xff) << 24;
|
|
}
|
|
|
|
arraycopy(B32, 0, x, 0, 16);
|
|
|
|
for (i = 8; i > 0; i -= 2) {
|
|
x[4] ^= R(x[0] + x[12], 7);
|
|
x[8] ^= R(x[4] + x[0], 9);
|
|
x[12] ^= R(x[8] + x[4], 13);
|
|
x[0] ^= R(x[12] + x[8], 18);
|
|
x[9] ^= R(x[5] + x[1], 7);
|
|
x[13] ^= R(x[9] + x[5], 9);
|
|
x[1] ^= R(x[13] + x[9], 13);
|
|
x[5] ^= R(x[1] + x[13], 18);
|
|
x[14] ^= R(x[10] + x[6], 7);
|
|
x[2] ^= R(x[14] + x[10], 9);
|
|
x[6] ^= R(x[2] + x[14], 13);
|
|
x[10] ^= R(x[6] + x[2], 18);
|
|
x[3] ^= R(x[15] + x[11], 7);
|
|
x[7] ^= R(x[3] + x[15], 9);
|
|
x[11] ^= R(x[7] + x[3], 13);
|
|
x[15] ^= R(x[11] + x[7], 18);
|
|
x[1] ^= R(x[0] + x[3], 7);
|
|
x[2] ^= R(x[1] + x[0], 9);
|
|
x[3] ^= R(x[2] + x[1], 13);
|
|
x[0] ^= R(x[3] + x[2], 18);
|
|
x[6] ^= R(x[5] + x[4], 7);
|
|
x[7] ^= R(x[6] + x[5], 9);
|
|
x[4] ^= R(x[7] + x[6], 13);
|
|
x[5] ^= R(x[4] + x[7], 18);
|
|
x[11] ^= R(x[10] + x[9], 7);
|
|
x[8] ^= R(x[11] + x[10], 9);
|
|
x[9] ^= R(x[8] + x[11], 13);
|
|
x[10] ^= R(x[9] + x[8], 18);
|
|
x[12] ^= R(x[15] + x[14], 7);
|
|
x[13] ^= R(x[12] + x[15], 9);
|
|
x[14] ^= R(x[13] + x[12], 13);
|
|
x[15] ^= R(x[14] + x[13], 18);
|
|
}
|
|
|
|
for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i];
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
var bi = i * 4;
|
|
B[bi + 0] = (B32[i] >> 0 & 0xff);
|
|
B[bi + 1] = (B32[i] >> 8 & 0xff);
|
|
B[bi + 2] = (B32[i] >> 16 & 0xff);
|
|
B[bi + 3] = (B32[i] >> 24 & 0xff);
|
|
}
|
|
}
|
|
|
|
function blockxor(S, Si, D, Di, len) {
|
|
var i = len >> 6;
|
|
while (i--) {
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
D[Di++] ^= S[Si++];
|
|
}
|
|
}
|
|
|
|
function integerify(B, bi, r) {
|
|
var n;
|
|
|
|
bi += (2 * r - 1) * 64;
|
|
|
|
n = (B[bi + 0] & 0xff) << 0;
|
|
n |= (B[bi + 1] & 0xff) << 8;
|
|
n |= (B[bi + 2] & 0xff) << 16;
|
|
n |= (B[bi + 3] & 0xff) << 24;
|
|
|
|
return n;
|
|
}
|
|
|
|
function arraycopy(src, srcPos, dest, destPos, length) {
|
|
while (length--) {
|
|
dest[destPos++] = src[srcPos++];
|
|
}
|
|
}
|
|
|
|
function arraycopy32(src, srcPos, dest, destPos, length) {
|
|
var i = length >> 5;
|
|
while (i--) {
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
dest[destPos++] = src[srcPos++];
|
|
}
|
|
}
|
|
} // scryptCore
|
|
}; // window.Crypto_scrypt
|
|
})();
|
|
</script>
|
|
|
|
<!-- bitTrx.js -->
|
|
<script>
|
|
(function () {
|
|
|
|
var bitjs = window.bitjs = function () {};
|
|
|
|
/* public vars */
|
|
//bitjs.pub = 0x23; // flochange - changed the prefix to FLO Mainnet PublicKey Prefix 0x23
|
|
//bitjs.priv = 0xa3; //flochange - changed the prefix to FLO Mainnet Private key prefix 0xa3
|
|
bitjs.pub = 0x73; // flochange - changed the prefix to FLO Testnet PublicKey Prefix 0x23
|
|
bitjs.priv = 0xef; //flochange - changed the prefix to FLO Testnet Private key prefix 0xa3
|
|
bitjs.compressed = false;
|
|
|
|
/* provide a privkey and return an WIF */
|
|
bitjs.privkey2wif = function (h) {
|
|
var r = Crypto.util.hexToBytes(h);
|
|
|
|
if (bitjs.compressed == true) {
|
|
r.push(0x01);
|
|
}
|
|
|
|
r.unshift(bitjs.priv);
|
|
var hash = Crypto.SHA256(Crypto.SHA256(r, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
var checksum = hash.slice(0, 4);
|
|
|
|
return B58.encode(r.concat(checksum));
|
|
}
|
|
|
|
/* convert a wif key back to a private key */
|
|
bitjs.wif2privkey = function (wif) {
|
|
var compressed = false;
|
|
var decode = B58.decode(wif);
|
|
var key = decode.slice(0, decode.length - 4);
|
|
key = key.slice(1, key.length);
|
|
if (key.length >= 33 && key[key.length - 1] == 0x01) {
|
|
key = key.slice(0, key.length - 1);
|
|
compressed = true;
|
|
}
|
|
return {
|
|
'privkey': Crypto.util.bytesToHex(key),
|
|
'compressed': compressed
|
|
};
|
|
}
|
|
|
|
/* convert a wif to a pubkey */
|
|
bitjs.wif2pubkey = function (wif) {
|
|
var compressed = bitjs.compressed;
|
|
var r = bitjs.wif2privkey(wif);
|
|
bitjs.compressed = r['compressed'];
|
|
var pubkey = bitjs.newPubkey(r['privkey']);
|
|
bitjs.compressed = compressed;
|
|
return {
|
|
'pubkey': pubkey,
|
|
'compressed': r['compressed']
|
|
};
|
|
}
|
|
|
|
/* convert a wif to a address */
|
|
bitjs.wif2address = function (wif) {
|
|
var r = bitjs.wif2pubkey(wif);
|
|
return {
|
|
'address': bitjs.pubkey2address(r['pubkey']),
|
|
'compressed': r['compressed']
|
|
};
|
|
}
|
|
|
|
/* generate a public key from a private key */
|
|
bitjs.newPubkey = function (hash) {
|
|
var privateKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(hash));
|
|
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
|
|
|
var curvePt = curve.getG().multiply(privateKeyBigInt);
|
|
var x = curvePt.getX().toBigInteger();
|
|
var y = curvePt.getY().toBigInteger();
|
|
|
|
var publicKeyBytes = EllipticCurve.integerToBytes(x, 32);
|
|
publicKeyBytes = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32));
|
|
publicKeyBytes.unshift(0x04);
|
|
|
|
if (bitjs.compressed == true) {
|
|
var publicKeyBytesCompressed = EllipticCurve.integerToBytes(x, 32)
|
|
if (y.isEven()) {
|
|
publicKeyBytesCompressed.unshift(0x02)
|
|
} else {
|
|
publicKeyBytesCompressed.unshift(0x03)
|
|
}
|
|
return Crypto.util.bytesToHex(publicKeyBytesCompressed);
|
|
} else {
|
|
return Crypto.util.bytesToHex(publicKeyBytes);
|
|
}
|
|
}
|
|
|
|
/* provide a public key and return address */
|
|
bitjs.pubkey2address = function (h, byte) {
|
|
var r = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(h), {
|
|
asBytes: true
|
|
}));
|
|
r.unshift(byte || bitjs.pub);
|
|
var hash = Crypto.SHA256(Crypto.SHA256(r, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
});
|
|
var checksum = hash.slice(0, 4);
|
|
return B58.encode(r.concat(checksum));
|
|
}
|
|
|
|
bitjs.transaction = function () {
|
|
var btrx = {};
|
|
btrx.version = 2; //flochange look at this version
|
|
btrx.inputs = [];
|
|
btrx.outputs = [];
|
|
btrx.locktime = 0;
|
|
btrx.floData = ""; //flochange .. look at this
|
|
|
|
|
|
btrx.addinput = function (txid, index, scriptPubKey, sequence) {
|
|
var o = {};
|
|
o.outpoint = {
|
|
'hash': txid,
|
|
'index': index
|
|
};
|
|
//o.script = []; Signature and Public Key should be added after singning
|
|
o.script = Crypto.util.hexToBytes(scriptPubKey); //push previous output pubkey script
|
|
o.sequence = sequence || ((btrx.locktime == 0) ? 4294967295 : 0);
|
|
return this.inputs.push(o);
|
|
}
|
|
|
|
btrx.addoutput = function (address, value) {
|
|
var o = {};
|
|
var buf = [];
|
|
var addrDecoded = btrx.addressDecode(address);
|
|
o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10);
|
|
buf.push(118); //OP_DUP
|
|
buf.push(169); //OP_HASH160
|
|
buf.push(addrDecoded.length);
|
|
buf = buf.concat(addrDecoded); // address in bytes
|
|
buf.push(136); //OP_EQUALVERIFY
|
|
buf.push(172); // OP_CHECKSIG
|
|
o.script = buf;
|
|
return this.outputs.push(o);
|
|
}
|
|
|
|
|
|
btrx.addflodata = function (txcomments) { // flochange - this whole function needs to be done
|
|
this.floData = txcomments;
|
|
return this.floData; //flochange .. returning the txcomments -- check if the function return will assign
|
|
}
|
|
|
|
|
|
// Only standard addresses
|
|
btrx.addressDecode = function (address) {
|
|
var bytes = B58.decode(address);
|
|
var front = bytes.slice(0, bytes.length - 4);
|
|
var back = bytes.slice(bytes.length - 4);
|
|
var checksum = Crypto.SHA256(Crypto.SHA256(front, {
|
|
asBytes: true
|
|
}), {
|
|
asBytes: true
|
|
}).slice(0, 4);
|
|
if (checksum + "" == back + "") {
|
|
return front.slice(1);
|
|
}
|
|
}
|
|
|
|
/* generate the transaction hash to sign from a transaction input */
|
|
btrx.transactionHash = function (index, sigHashType) {
|
|
|
|
var clone = bitjs.clone(this);
|
|
var shType = sigHashType || 1;
|
|
|
|
/* black out all other ins, except this one */
|
|
for (var i = 0; i < clone.inputs.length; i++) {
|
|
if (index != i) {
|
|
clone.inputs[i].script = [];
|
|
}
|
|
}
|
|
|
|
|
|
if ((clone.inputs) && clone.inputs[index]) {
|
|
|
|
/* SIGHASH : For more info on sig hashs see https://en.bitcoin.it/wiki/OP_CHECKSIG
|
|
and https://bitcoin.org/en/developer-guide#signature-hash-type */
|
|
|
|
if (shType == 1) {
|
|
//SIGHASH_ALL 0x01
|
|
|
|
} else if (shType == 2) {
|
|
//SIGHASH_NONE 0x02
|
|
clone.outputs = [];
|
|
for (var i = 0; i < clone.inputs.length; i++) {
|
|
if (index != i) {
|
|
clone.inputs[i].sequence = 0;
|
|
}
|
|
}
|
|
|
|
} else if (shType == 3) {
|
|
|
|
//SIGHASH_SINGLE 0x03
|
|
clone.outputs.length = index + 1;
|
|
|
|
for (var i = 0; i < index; i++) {
|
|
clone.outputs[i].value = -1;
|
|
clone.outputs[i].script = [];
|
|
}
|
|
|
|
for (var i = 0; i < clone.inputs.length; i++) {
|
|
if (index != i) {
|
|
clone.inputs[i].sequence = 0;
|
|
}
|
|
}
|
|
|
|
} else if (shType >= 128) {
|
|
//SIGHASH_ANYONECANPAY 0x80
|
|
clone.inputs = [clone.inputs[index]];
|
|
|
|
if (shType == 129) {
|
|
// SIGHASH_ALL + SIGHASH_ANYONECANPAY
|
|
|
|
} else if (shType == 130) {
|
|
// SIGHASH_NONE + SIGHASH_ANYONECANPAY
|
|
clone.outputs = [];
|
|
|
|
} else if (shType == 131) {
|
|
// SIGHASH_SINGLE + SIGHASH_ANYONECANPAY
|
|
clone.outputs.length = index + 1;
|
|
for (var i = 0; i < index; i++) {
|
|
clone.outputs[i].value = -1;
|
|
clone.outputs[i].script = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
var buffer = Crypto.util.hexToBytes(clone.serialize());
|
|
buffer = buffer.concat(bitjs.numToBytes(parseInt(shType), 4));
|
|
var hash = Crypto.SHA256(buffer, {
|
|
asBytes: true
|
|
});
|
|
var r = Crypto.util.bytesToHex(Crypto.SHA256(hash, {
|
|
asBytes: true
|
|
}));
|
|
return r;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* generate a signature from a transaction hash */
|
|
btrx.transactionSig = function (index, wif, sigHashType, txhash) {
|
|
|
|
function serializeSig(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;
|
|
}
|
|
|
|
var shType = sigHashType || 1;
|
|
var hash = txhash || Crypto.util.hexToBytes(this.transactionHash(index, shType));
|
|
|
|
if (hash) {
|
|
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var key = bitjs.wif2privkey(wif);
|
|
var priv = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(key['privkey']));
|
|
var n = curve.getN();
|
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
|
var badrs = 0
|
|
do {
|
|
var k = this.deterministicK(wif, hash, badrs);
|
|
var G = curve.getG();
|
|
var Q = G.multiply(k);
|
|
var r = Q.getX().toBigInteger().mod(n);
|
|
var s = k.modInverse(n).multiply(e.add(priv.multiply(r))).mod(n);
|
|
badrs++
|
|
} while (r.compareTo(BigInteger.ZERO) <= 0 || s.compareTo(BigInteger.ZERO) <= 0);
|
|
|
|
// Force lower s values per BIP62
|
|
var halfn = n.shiftRight(1);
|
|
if (s.compareTo(halfn) > 0) {
|
|
s = n.subtract(s);
|
|
};
|
|
|
|
var sig = serializeSig(r, s);
|
|
sig.push(parseInt(shType, 10));
|
|
|
|
return Crypto.util.bytesToHex(sig);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// https://tools.ietf.org/html/rfc6979#section-3.2
|
|
btrx.deterministicK = function (wif, hash, badrs) {
|
|
// if r or s were invalid when this function was used in signing,
|
|
// we do not want to actually compute r, s here for efficiency, so,
|
|
// we can increment badrs. explained at end of RFC 6979 section 3.2
|
|
|
|
// wif is b58check encoded wif privkey.
|
|
// hash is byte array of transaction digest.
|
|
// badrs is used only if the k resulted in bad r or s.
|
|
|
|
// some necessary things out of the way for clarity.
|
|
badrs = badrs || 0;
|
|
var key = bitjs.wif2privkey(wif);
|
|
var x = Crypto.util.hexToBytes(key['privkey'])
|
|
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var N = curve.getN();
|
|
|
|
// Step: a
|
|
// hash is a byteArray of the message digest. so h1 == hash in our case
|
|
|
|
// Step: b
|
|
var v = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1
|
|
];
|
|
|
|
// Step: c
|
|
var k = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0
|
|
];
|
|
|
|
// Step: d
|
|
k = Crypto.HMAC(Crypto.SHA256, v.concat([0]).concat(x).concat(hash), k, {
|
|
asBytes: true
|
|
});
|
|
|
|
// Step: e
|
|
v = Crypto.HMAC(Crypto.SHA256, v, k, {
|
|
asBytes: true
|
|
});
|
|
|
|
// Step: f
|
|
k = Crypto.HMAC(Crypto.SHA256, v.concat([1]).concat(x).concat(hash), k, {
|
|
asBytes: true
|
|
});
|
|
|
|
// Step: g
|
|
v = Crypto.HMAC(Crypto.SHA256, v, k, {
|
|
asBytes: true
|
|
});
|
|
|
|
// Step: h1
|
|
var T = [];
|
|
|
|
// Step: h2 (since we know tlen = qlen, just copy v to T.)
|
|
v = Crypto.HMAC(Crypto.SHA256, v, k, {
|
|
asBytes: true
|
|
});
|
|
T = v;
|
|
|
|
// Step: h3
|
|
var KBigInt = BigInteger.fromByteArrayUnsigned(T);
|
|
|
|
// loop if KBigInt is not in the range of [1, N-1] or if badrs needs incrementing.
|
|
var i = 0
|
|
while (KBigInt.compareTo(N) >= 0 || KBigInt.compareTo(BigInteger.ZERO) <= 0 || i <
|
|
badrs) {
|
|
k = Crypto.HMAC(Crypto.SHA256, v.concat([0]), k, {
|
|
asBytes: true
|
|
});
|
|
v = Crypto.HMAC(Crypto.SHA256, v, k, {
|
|
asBytes: true
|
|
});
|
|
v = Crypto.HMAC(Crypto.SHA256, v, k, {
|
|
asBytes: true
|
|
});
|
|
T = v;
|
|
KBigInt = BigInteger.fromByteArrayUnsigned(T);
|
|
i++
|
|
};
|
|
|
|
return KBigInt;
|
|
};
|
|
|
|
/* sign a "standard" input */
|
|
btrx.signinput = function (index, wif, sigHashType) {
|
|
var key = bitjs.wif2pubkey(wif);
|
|
var shType = sigHashType || 1;
|
|
var signature = this.transactionSig(index, wif, shType);
|
|
var buf = [];
|
|
var sigBytes = Crypto.util.hexToBytes(signature);
|
|
buf.push(sigBytes.length);
|
|
buf = buf.concat(sigBytes);
|
|
var pubKeyBytes = Crypto.util.hexToBytes(key['pubkey']);
|
|
buf.push(pubKeyBytes.length);
|
|
buf = buf.concat(pubKeyBytes);
|
|
this.inputs[index].script = buf;
|
|
return true;
|
|
}
|
|
|
|
/* sign inputs */
|
|
btrx.sign = function (wif, sigHashType) {
|
|
var shType = sigHashType || 1;
|
|
for (var i = 0; i < this.inputs.length; i++) {
|
|
this.signinput(i, wif, shType);
|
|
}
|
|
return this.serialize();
|
|
}
|
|
|
|
|
|
/* serialize a transaction */
|
|
btrx.serialize = function () {
|
|
var buffer = [];
|
|
buffer = buffer.concat(bitjs.numToBytes(parseInt(this.version), 4));
|
|
|
|
buffer = buffer.concat(bitjs.numToVarInt(this.inputs.length));
|
|
for (var i = 0; i < this.inputs.length; i++) {
|
|
var txin = this.inputs[i];
|
|
buffer = buffer.concat(Crypto.util.hexToBytes(txin.outpoint.hash).reverse());
|
|
buffer = buffer.concat(bitjs.numToBytes(parseInt(txin.outpoint.index), 4));
|
|
var scriptBytes = txin.script;
|
|
buffer = buffer.concat(bitjs.numToVarInt(scriptBytes.length));
|
|
buffer = buffer.concat(scriptBytes);
|
|
buffer = buffer.concat(bitjs.numToBytes(parseInt(txin.sequence), 4));
|
|
|
|
}
|
|
buffer = buffer.concat(bitjs.numToVarInt(this.outputs.length));
|
|
|
|
for (var i = 0; i < this.outputs.length; i++) {
|
|
var txout = this.outputs[i];
|
|
buffer = buffer.concat(bitjs.numToBytes(txout.value, 8));
|
|
var scriptBytes = txout.script;
|
|
buffer = buffer.concat(bitjs.numToVarInt(scriptBytes.length));
|
|
buffer = buffer.concat(scriptBytes);
|
|
}
|
|
|
|
buffer = buffer.concat(bitjs.numToBytes(parseInt(this.locktime), 4));
|
|
flohex = ascii_to_hexa(this.floData);
|
|
floDataCount = this.floData.length;
|
|
|
|
//flochange -- creating unique data character count logic for floData. This string is prefixed before actual floData string in Raw Transaction
|
|
if (floDataCount <= 16) {
|
|
floDataCountString = floDataCount.toString(16);
|
|
floDataCountString = "0" + floDataCountString;
|
|
} else if (floDataCount < 253) {
|
|
floDataCountString = floDataCount.toString(16);
|
|
} else if (floDataCount <= 1023) {
|
|
floDataCountAdjusted = (floDataCount - 253) + parseInt("0xfd00fd");
|
|
floDataCountStringAdjusted = floDataCountAdjusted.toString(16);
|
|
floDataCountString = floDataCountStringAdjusted.substr(0, 2) +
|
|
floDataCountStringAdjusted.substr(4, 2) + floDataCountStringAdjusted.substr(2,
|
|
2);
|
|
} else {
|
|
floDataCountString = "Character Limit Exceeded";
|
|
}
|
|
return Crypto.util.bytesToHex(buffer) + floDataCountString + flohex; // flochange -- Addition of floDataCountString and floData in serialization
|
|
}
|
|
return btrx;
|
|
}
|
|
|
|
bitjs.numToBytes = function (num, bytes) {
|
|
if (typeof bytes === "undefined") bytes = 8;
|
|
if (bytes == 0) {
|
|
return [];
|
|
} else if (num == -1) {
|
|
return Crypto.util.hexToBytes("ffffffffffffffff");
|
|
} else {
|
|
return [num % 256].concat(bitjs.numToBytes(Math.floor(num / 256), bytes - 1));
|
|
}
|
|
}
|
|
|
|
bitjs.numToByteArray = function (num) {
|
|
if (num <= 256) {
|
|
return [num];
|
|
} else {
|
|
return [num % 256].concat(bitjs.numToByteArray(Math.floor(num / 256)));
|
|
}
|
|
}
|
|
|
|
bitjs.numToVarInt = function (num) {
|
|
if (num < 253) {
|
|
return [num];
|
|
} else if (num < 65536) {
|
|
return [253].concat(bitjs.numToBytes(num, 2));
|
|
} else if (num < 4294967296) {
|
|
return [254].concat(bitjs.numToBytes(num, 4));
|
|
} else {
|
|
return [255].concat(bitjs.numToBytes(num, 8));
|
|
}
|
|
}
|
|
|
|
bitjs.bytesToNum = function (bytes) {
|
|
if (bytes.length == 0) return 0;
|
|
else return bytes[0] + 256 * bitjs.bytesToNum(bytes.slice(1));
|
|
}
|
|
|
|
/* clone an object */
|
|
bitjs.clone = function (obj) {
|
|
if (obj == null || typeof (obj) != 'object') return obj;
|
|
var temp = new obj.constructor();
|
|
|
|
for (var key in obj) {
|
|
if (obj.hasOwnProperty(key)) {
|
|
temp[key] = bitjs.clone(obj[key]);
|
|
}
|
|
}
|
|
return temp;
|
|
}
|
|
|
|
var B58 = bitjs.Base58 = {
|
|
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/,
|
|
base: BigInteger.valueOf(58),
|
|
|
|
/**
|
|
* 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 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.
|
|
for (var i = 0; i < input.length; i++) {
|
|
if (input[i] == 0x00) {
|
|
chars.unshift(B58.alphabet[0]);
|
|
} else break;
|
|
}
|
|
|
|
return chars.join('');
|
|
},
|
|
|
|
/**
|
|
* Convert a base58-encoded string to a byte array.
|
|
*
|
|
* Written by Mike Hearn for BitcoinJ.
|
|
* Copyright (c) 2011 Google Inc.
|
|
*
|
|
* Ported to JavaScript by Stefan Thomas.
|
|
*/
|
|
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
|
|
if (input[i] == "1") leadingZerosNum++;
|
|
else leadingZerosNum = 0;
|
|
}
|
|
var bytes = bi.toByteArrayUnsigned();
|
|
|
|
// Add leading zeros
|
|
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
|
|
|
return bytes;
|
|
}
|
|
}
|
|
return bitjs;
|
|
|
|
})();
|
|
</script>
|
|
|
|
<!-- Shamir's secret (https://github.com/amper5and/secrets.js) -->
|
|
<script>
|
|
// secrets.js - by Alexander Stetsyuk - released under MIT License
|
|
(function (exports, global) {
|
|
var defaults = {
|
|
bits: 8, // default number of bits
|
|
radix: 16, // work with HEX by default
|
|
minBits: 3,
|
|
maxBits: 20, // this permits 1,048,575 shares, though going this high is NOT recommended in JS!
|
|
|
|
bytesPerChar: 2,
|
|
maxBytesPerChar: 6, // Math.pow(256,7) > Math.pow(2,53)
|
|
|
|
// Primitive polynomials (in decimal form) for Galois Fields GF(2^n), for 2 <= n <= 30
|
|
// The index of each term in the array corresponds to the n for that polynomial
|
|
// i.e. to get the polynomial for n=16, use primitivePolynomials[16]
|
|
primitivePolynomials: [null, null, 1, 3, 3, 5, 3, 3, 29, 17, 9, 5, 83, 27, 43, 3, 45, 9, 39, 39,
|
|
9, 5, 3, 33, 27, 9, 71, 39, 9, 5, 83
|
|
],
|
|
|
|
// warning for insecure PRNG
|
|
warning: 'WARNING:\nA secure random number generator was not found.\nUsing Math.random(), which is NOT cryptographically strong!'
|
|
};
|
|
|
|
// Protected settings object
|
|
var config = {};
|
|
|
|
/** @expose **/
|
|
exports.getConfig = function () {
|
|
return {
|
|
'bits': config.bits,
|
|
'unsafePRNG': config.unsafePRNG
|
|
};
|
|
};
|
|
|
|
function init(bits) {
|
|
if (bits && (typeof bits !== 'number' || bits % 1 !== 0 || bits < defaults.minBits || bits >
|
|
defaults.maxBits)) {
|
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' +
|
|
defaults.maxBits + ', inclusive.')
|
|
}
|
|
|
|
config.radix = defaults.radix;
|
|
config.bits = bits || defaults.bits;
|
|
config.size = Math.pow(2, config.bits);
|
|
config.max = config.size - 1;
|
|
|
|
// Construct the exp and log tables for multiplication.
|
|
var logs = [],
|
|
exps = [],
|
|
x = 1,
|
|
primitive = defaults.primitivePolynomials[config.bits];
|
|
for (var i = 0; i < config.size; i++) {
|
|
exps[i] = x;
|
|
logs[x] = i;
|
|
x <<= 1;
|
|
if (x >= config.size) {
|
|
x ^= primitive;
|
|
x &= config.max;
|
|
}
|
|
}
|
|
|
|
config.logs = logs;
|
|
config.exps = exps;
|
|
};
|
|
|
|
/** @expose **/
|
|
exports.init = init;
|
|
|
|
function isInited() {
|
|
if (!config.bits || !config.size || !config.max || !config.logs || !config.exps || config.logs.length !==
|
|
config.size || config.exps.length !== config.size) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Returns a pseudo-random number generator of the form function(bits){}
|
|
// which should output a random string of 1's and 0's of length `bits`
|
|
function getRNG() {
|
|
var randomBits, crypto;
|
|
|
|
function construct(bits, arr, radix, size) {
|
|
var str = '',
|
|
i = 0,
|
|
len = arr.length - 1;
|
|
while (i < len || (str.length < bits)) {
|
|
str += padLeft(parseInt(arr[i], radix).toString(2), size);
|
|
i++;
|
|
}
|
|
str = str.substr(-bits);
|
|
if ((str.match(/0/g) || []).length === str.length) { // all zeros?
|
|
return null;
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// node.js crypto.randomBytes()
|
|
if (typeof require === 'function' && (crypto = require('crypto')) && (randomBits = crypto[
|
|
'randomBytes'])) {
|
|
return function (bits) {
|
|
var bytes = Math.ceil(bits / 8),
|
|
str = null;
|
|
|
|
while (str === null) {
|
|
str = construct(bits, randomBits(bytes).toString('hex'), 16, 4);
|
|
}
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// browsers with window.crypto.getRandomValues()
|
|
if (global['crypto'] && typeof global['crypto']['getRandomValues'] === 'function' && typeof global[
|
|
'Uint32Array'] === 'function') {
|
|
crypto = global['crypto'];
|
|
return function (bits) {
|
|
var elems = Math.ceil(bits / 32),
|
|
str = null,
|
|
arr = new global['Uint32Array'](elems);
|
|
|
|
while (str === null) {
|
|
crypto['getRandomValues'](arr);
|
|
str = construct(bits, arr, 10, 32);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// A totally insecure RNG!!! (except in Safari)
|
|
// Will produce a warning every time it is called.
|
|
config.unsafePRNG = true;
|
|
warn();
|
|
|
|
var bitsPerNum = 32;
|
|
var max = Math.pow(2, bitsPerNum) - 1;
|
|
return function (bits) {
|
|
var elems = Math.ceil(bits / bitsPerNum);
|
|
var arr = [],
|
|
str = null;
|
|
while (str === null) {
|
|
for (var i = 0; i < elems; i++) {
|
|
arr[i] = Math.floor(Math.random() * max + 1);
|
|
}
|
|
str = construct(bits, arr, 10, bitsPerNum);
|
|
}
|
|
return str;
|
|
};
|
|
};
|
|
|
|
// Warn about using insecure rng.
|
|
// Called when Math.random() is being used.
|
|
function warn() {
|
|
global['console']['warn'](defaults.warning);
|
|
if (typeof global['alert'] === 'function' && config.alert) {
|
|
global['alert'](defaults.warning);
|
|
}
|
|
}
|
|
|
|
// Set the PRNG to use. If no RNG function is supplied, pick a default using getRNG()
|
|
/** @expose **/
|
|
exports.setRNG = function (rng, alert) {
|
|
if (!isInited()) {
|
|
this.init();
|
|
}
|
|
config.unsafePRNG = false;
|
|
rng = rng || getRNG();
|
|
|
|
// test the RNG (5 times)
|
|
if (typeof rng !== 'function' || typeof rng(config.bits) !== 'string' || !parseInt(rng(config.bits),
|
|
2) || rng(config.bits).length > config.bits || rng(config.bits).length < config.bits) {
|
|
throw new Error(
|
|
"Random number generator is invalid. Supply an RNG of the form function(bits){} that returns a string containing 'bits' number of random 1's and 0's."
|
|
)
|
|
} else {
|
|
config.rng = rng;
|
|
}
|
|
config.alert = !!alert;
|
|
|
|
return !!config.unsafePRNG;
|
|
};
|
|
|
|
function isSetRNG() {
|
|
return typeof config.rng === 'function';
|
|
};
|
|
|
|
// Generates a random bits-length number string using the PRNG
|
|
/** @expose **/
|
|
exports.random = function (bits) {
|
|
if (!isSetRNG()) {
|
|
this.setRNG();
|
|
}
|
|
|
|
if (typeof bits !== 'number' || bits % 1 !== 0 || bits < 2) {
|
|
throw new Error('Number of bits must be an integer greater than 1.')
|
|
}
|
|
|
|
if (config.unsafePRNG) {
|
|
warn();
|
|
}
|
|
return bin2hex(config.rng(bits));
|
|
}
|
|
|
|
// Divides a `secret` number String str expressed in radix `inputRadix` (optional, default 16)
|
|
// into `numShares` shares, each expressed in radix `outputRadix` (optional, default to `inputRadix`),
|
|
// requiring `threshold` number of shares to reconstruct the secret.
|
|
// Optionally, zero-pads the secret to a length that is a multiple of padLength before sharing.
|
|
/** @expose **/
|
|
exports.share = function (secret, numShares, threshold, padLength, withoutPrefix) {
|
|
if (!isInited()) {
|
|
this.init();
|
|
}
|
|
if (!isSetRNG()) {
|
|
this.setRNG();
|
|
}
|
|
|
|
padLength = padLength || 0;
|
|
|
|
if (typeof secret !== 'string') {
|
|
throw new Error('Secret must be a string.');
|
|
}
|
|
if (typeof numShares !== 'number' || numShares % 1 !== 0 || numShares < 2) {
|
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max +
|
|
'), inclusive.')
|
|
}
|
|
if (numShares > config.max) {
|
|
var neededBits = Math.ceil(Math.log(numShares + 1) / Math.LN2);
|
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max +
|
|
'), inclusive. To create ' + numShares + ' shares, use at least ' + neededBits +
|
|
' bits.')
|
|
}
|
|
if (typeof threshold !== 'number' || threshold % 1 !== 0 || threshold < 2) {
|
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' +
|
|
config.max + '), inclusive.');
|
|
}
|
|
if (threshold > config.max) {
|
|
var neededBits = Math.ceil(Math.log(threshold + 1) / Math.LN2);
|
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' +
|
|
config.max + '), inclusive. To use a threshold of ' + threshold +
|
|
', use at least ' + neededBits + ' bits.');
|
|
}
|
|
if (typeof padLength !== 'number' || padLength % 1 !== 0) {
|
|
throw new Error('Zero-pad length must be an integer greater than 1.');
|
|
}
|
|
|
|
if (config.unsafePRNG) {
|
|
warn();
|
|
}
|
|
|
|
secret = '1' + hex2bin(secret); // append a 1 so that we can preserve the correct number of leading zeros in our secret
|
|
secret = split(secret, padLength);
|
|
var x = new Array(numShares),
|
|
y = new Array(numShares);
|
|
for (var i = 0, len = secret.length; i < len; i++) {
|
|
var subShares = this._getShares(secret[i], numShares, threshold);
|
|
for (var j = 0; j < numShares; j++) {
|
|
x[j] = x[j] || subShares[j].x.toString(config.radix);
|
|
y[j] = padLeft(subShares[j].y.toString(2)) + (y[j] ? y[j] : '');
|
|
}
|
|
}
|
|
var padding = config.max.toString(config.radix).length;
|
|
if (withoutPrefix) {
|
|
for (var i = 0; i < numShares; i++) {
|
|
x[i] = bin2hex(y[i]);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < numShares; i++) {
|
|
x[i] = config.bits.toString(36).toUpperCase() + padLeft(x[i], padding) + bin2hex(y[i]);
|
|
}
|
|
}
|
|
|
|
return x;
|
|
};
|
|
|
|
// This is the basic polynomial generation and evaluation function
|
|
// for a `config.bits`-length secret (NOT an arbitrary length)
|
|
// Note: no error-checking at this stage! If `secrets` is NOT
|
|
// a NUMBER less than 2^bits-1, the output will be incorrect!
|
|
/** @expose **/
|
|
exports._getShares = function (secret, numShares, threshold) {
|
|
var shares = [];
|
|
var coeffs = [secret];
|
|
|
|
for (var i = 1; i < threshold; i++) {
|
|
coeffs[i] = parseInt(config.rng(config.bits), 2);
|
|
}
|
|
for (var i = 1, len = numShares + 1; i < len; i++) {
|
|
shares[i - 1] = {
|
|
x: i,
|
|
y: horner(i, coeffs)
|
|
}
|
|
}
|
|
return shares;
|
|
};
|
|
|
|
// Polynomial evaluation at `x` using Horner's Method
|
|
// TODO: this can possibly be sped up using other methods
|
|
// NOTE: fx=fx * x + coeff[i] -> exp(log(fx) + log(x)) + coeff[i],
|
|
// so if fx===0, just set fx to coeff[i] because
|
|
// using the exp/log form will result in incorrect value
|
|
function horner(x, coeffs) {
|
|
var logx = config.logs[x];
|
|
var fx = 0;
|
|
for (var i = coeffs.length - 1; i >= 0; i--) {
|
|
if (fx === 0) {
|
|
fx = coeffs[i];
|
|
continue;
|
|
}
|
|
fx = config.exps[(logx + config.logs[fx]) % config.max] ^ coeffs[i];
|
|
}
|
|
return fx;
|
|
};
|
|
|
|
function inArray(arr, val) {
|
|
for (var i = 0, len = arr.length; i < len; i++) {
|
|
if (arr[i] === val) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
function processShare(share) {
|
|
|
|
var bits = parseInt(share[0], 36);
|
|
if (bits && (typeof bits !== 'number' || bits % 1 !== 0 || bits < defaults.minBits || bits >
|
|
defaults.maxBits)) {
|
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' +
|
|
defaults.maxBits + ', inclusive.')
|
|
}
|
|
|
|
var max = Math.pow(2, bits) - 1;
|
|
var idLength = max.toString(config.radix).length;
|
|
|
|
var id = parseInt(share.substr(1, idLength), config.radix);
|
|
if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > max) {
|
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
|
}
|
|
share = share.substr(idLength + 1);
|
|
if (!share.length) {
|
|
throw new Error('Invalid share: zero-length share.')
|
|
}
|
|
return {
|
|
'bits': bits,
|
|
'id': id,
|
|
'value': share
|
|
};
|
|
};
|
|
|
|
/** @expose **/
|
|
exports._processShare = processShare;
|
|
|
|
// Protected method that evaluates the Lagrange interpolation
|
|
// polynomial at x=`at` for individual config.bits-length
|
|
// segments of each share in the `shares` Array.
|
|
// Each share is expressed in base `inputRadix`. The output
|
|
// is expressed in base `outputRadix'
|
|
function combine(at, shares) {
|
|
var setBits, share, x = [],
|
|
y = [],
|
|
result = '',
|
|
idx;
|
|
|
|
for (var i = 0, len = shares.length; i < len; i++) {
|
|
share = processShare(shares[i]);
|
|
if (typeof setBits === 'undefined') {
|
|
setBits = share['bits'];
|
|
} else if (share['bits'] !== setBits) {
|
|
throw new Error('Mismatched shares: Different bit settings.')
|
|
}
|
|
|
|
if (config.bits !== setBits) {
|
|
init(setBits);
|
|
}
|
|
|
|
if (inArray(x, share['id'])) { // repeated x value?
|
|
continue;
|
|
}
|
|
|
|
idx = x.push(share['id']) - 1;
|
|
share = split(hex2bin(share['value']));
|
|
for (var j = 0, len2 = share.length; j < len2; j++) {
|
|
y[j] = y[j] || [];
|
|
y[j][idx] = share[j];
|
|
}
|
|
}
|
|
|
|
for (var i = 0, len = y.length; i < len; i++) {
|
|
result = padLeft(lagrange(at, x, y[i]).toString(2)) + result;
|
|
}
|
|
|
|
if (at === 0) { // reconstructing the secret
|
|
var idx = result.indexOf('1'); //find the first 1
|
|
return bin2hex(result.slice(idx + 1));
|
|
} else { // generating a new share
|
|
return bin2hex(result);
|
|
}
|
|
};
|
|
|
|
// Combine `shares` Array into the original secret
|
|
/** @expose **/
|
|
exports.combine = function (shares) {
|
|
return combine(0, shares);
|
|
};
|
|
|
|
// Generate a new share with id `id` (a number between 1 and 2^bits-1)
|
|
// `id` can be a Number or a String in the default radix (16)
|
|
/** @expose **/
|
|
exports.newShare = function (id, shares) {
|
|
if (typeof id === 'string') {
|
|
id = parseInt(id, config.radix);
|
|
}
|
|
|
|
var share = processShare(shares[0]);
|
|
var max = Math.pow(2, share['bits']) - 1;
|
|
|
|
if (typeof id !== 'number' || id % 1 !== 0 || id < 1 || id > max) {
|
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
|
}
|
|
|
|
var padding = max.toString(config.radix).length;
|
|
return config.bits.toString(36).toUpperCase() + padLeft(id.toString(config.radix), padding) +
|
|
combine(id, shares);
|
|
};
|
|
|
|
// Evaluate the Lagrange interpolation polynomial at x = `at`
|
|
// using x and y Arrays that are of the same length, with
|
|
// corresponding elements constituting points on the polynomial.
|
|
function lagrange(at, x, y) {
|
|
var sum = 0,
|
|
product,
|
|
i, j;
|
|
|
|
for (var i = 0, len = x.length; i < len; i++) {
|
|
if (!y[i]) {
|
|
continue;
|
|
}
|
|
|
|
product = config.logs[y[i]];
|
|
for (var j = 0; j < len; j++) {
|
|
if (i === j) {
|
|
continue;
|
|
}
|
|
if (at === x[j]) { // happens when computing a share that is in the list of shares used to compute it
|
|
product = -1; // fix for a zero product term, after which the sum should be sum^0 = sum, not sum^1
|
|
break;
|
|
}
|
|
product = (product + config.logs[at ^ x[j]] - config.logs[x[i] ^ x[j]] + config.max /* to make sure it's not negative */ ) %
|
|
config.max;
|
|
}
|
|
|
|
sum = product === -1 ? sum : sum ^ config.exps[product]; // though exps[-1]= undefined and undefined ^ anything = anything in chrome, this behavior may not hold everywhere, so do the check
|
|
}
|
|
return sum;
|
|
};
|
|
|
|
/** @expose **/
|
|
exports._lagrange = lagrange;
|
|
|
|
// Splits a number string `bits`-length segments, after first
|
|
// optionally zero-padding it to a length that is a multiple of `padLength.
|
|
// Returns array of integers (each less than 2^bits-1), with each element
|
|
// representing a `bits`-length segment of the input string from right to left,
|
|
// i.e. parts[0] represents the right-most `bits`-length segment of the input string.
|
|
function split(str, padLength) {
|
|
if (padLength) {
|
|
str = padLeft(str, padLength)
|
|
}
|
|
var parts = [];
|
|
for (var i = str.length; i > config.bits; i -= config.bits) {
|
|
parts.push(parseInt(str.slice(i - config.bits, i), 2));
|
|
}
|
|
parts.push(parseInt(str.slice(0, i), 2));
|
|
return parts;
|
|
};
|
|
|
|
// Pads a string `str` with zeros on the left so that its length is a multiple of `bits`
|
|
function padLeft(str, bits) {
|
|
bits = bits || config.bits
|
|
var missing = str.length % bits;
|
|
return (missing ? new Array(bits - missing + 1).join('0') : '') + str;
|
|
};
|
|
|
|
function hex2bin(str) {
|
|
var bin = '',
|
|
num;
|
|
for (var i = str.length - 1; i >= 0; i--) {
|
|
num = parseInt(str[i], 16)
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid hex character.')
|
|
}
|
|
bin = padLeft(num.toString(2), 4) + bin;
|
|
}
|
|
return bin;
|
|
}
|
|
|
|
function bin2hex(str) {
|
|
var hex = '',
|
|
num;
|
|
str = padLeft(str, 4);
|
|
for (var i = str.length; i >= 4; i -= 4) {
|
|
num = parseInt(str.slice(i - 4, i), 2);
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid binary character.')
|
|
}
|
|
hex = num.toString(16) + hex;
|
|
}
|
|
return hex;
|
|
}
|
|
|
|
// Converts a given UTF16 character string to the HEX representation.
|
|
// Each character of the input string is represented by
|
|
// `bytesPerChar` bytes in the output string.
|
|
/** @expose **/
|
|
exports.str2hex = function (str, bytesPerChar) {
|
|
if (typeof str !== 'string') {
|
|
throw new Error('Input must be a character string.');
|
|
}
|
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
|
|
|
if (typeof bytesPerChar !== 'number' || bytesPerChar % 1 !== 0 || bytesPerChar < 1 ||
|
|
bytesPerChar > defaults.maxBytesPerChar) {
|
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar +
|
|
', inclusive.')
|
|
}
|
|
|
|
var hexChars = 2 * bytesPerChar;
|
|
var max = Math.pow(16, hexChars) - 1;
|
|
var out = '',
|
|
num;
|
|
for (var i = 0, len = str.length; i < len; i++) {
|
|
num = str[i].charCodeAt();
|
|
if (isNaN(num)) {
|
|
throw new Error('Invalid character: ' + str[i]);
|
|
} else if (num > max) {
|
|
var neededBytes = Math.ceil(Math.log(num + 1) / Math.log(256));
|
|
throw new Error('Invalid character code (' + num +
|
|
'). Maximum allowable is 256^bytes-1 (' + max +
|
|
'). To convert this character, use at least ' + neededBytes + ' bytes.')
|
|
} else {
|
|
out = padLeft(num.toString(16), hexChars) + out;
|
|
}
|
|
}
|
|
return out;
|
|
};
|
|
|
|
// Converts a given HEX number string to a UTF16 character string.
|
|
/** @expose **/
|
|
exports.hex2str = function (str, bytesPerChar) {
|
|
if (typeof str !== 'string') {
|
|
throw new Error('Input must be a hexadecimal string.');
|
|
}
|
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
|
|
|
if (typeof bytesPerChar !== 'number' || bytesPerChar % 1 !== 0 || bytesPerChar < 1 ||
|
|
bytesPerChar > defaults.maxBytesPerChar) {
|
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar +
|
|
', inclusive.')
|
|
}
|
|
|
|
var hexChars = 2 * bytesPerChar;
|
|
var out = '';
|
|
str = padLeft(str, hexChars);
|
|
for (var i = 0, len = str.length; i < len; i += hexChars) {
|
|
out = String.fromCharCode(parseInt(str.slice(i, i + hexChars), 16)) + out;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
// by default, initialize without an RNG
|
|
exports.init();
|
|
})(typeof module !== 'undefined' && module['exports'] ? module['exports'] : (window['shamirSecretShare'] = {}),
|
|
typeof global !== 'undefined' ? global : window);
|
|
</script>
|
|
<script language="JavaScript">
|
|
(function (ellipticCurveType) {
|
|
|
|
//Defining Elliptic Encryption Object
|
|
var ellipticEncryption = window.ellipticCurveEncryption = function () { };
|
|
|
|
ellipticEncryption.rng = new SecureRandom();
|
|
|
|
|
|
|
|
ellipticEncryption.getCurveParameters = function(curveName) {
|
|
|
|
//Default is secp256k1
|
|
curveName = typeof curveName !== 'undefined' ? curveName : "secp256k1";
|
|
|
|
var c = EllipticCurve.getSECCurveByName(curveName);
|
|
var curveDetails = {Q:"",A:"",B:"",GX:"",GY:"",N:""};
|
|
|
|
curveDetails.Q = c.getCurve().getQ().toString();
|
|
curveDetails.A = c.getCurve().getA().toBigInteger().toString();
|
|
curveDetails.B = c.getCurve().getB().toBigInteger().toString();
|
|
curveDetails.GX = c.getG().getX().toBigInteger().toString();
|
|
curveDetails.GY = c.getG().getY().toBigInteger().toString();
|
|
curveDetails.N = c.getN().toString();
|
|
|
|
return curveDetails;
|
|
|
|
}
|
|
|
|
ellipticEncryption.selectedCurve = ellipticEncryption.getCurveParameters(ellipticCurveType);
|
|
|
|
|
|
ellipticEncryption.get_curve = function() {
|
|
return new EllipticCurve.CurveFp(new BigInteger(this.selectedCurve.Q),
|
|
new BigInteger(this.selectedCurve.A),
|
|
new BigInteger(this.selectedCurve.B));
|
|
}
|
|
|
|
ellipticEncryption.get_G = function(curve) {
|
|
return new EllipticCurve.PointFp(curve,
|
|
curve.fromBigInteger(new BigInteger(this.selectedCurve.GX)),
|
|
curve.fromBigInteger(new BigInteger(this.selectedCurve.GY)));
|
|
}
|
|
|
|
|
|
ellipticEncryption.pick_rand = function() {
|
|
var n = new BigInteger(this.selectedCurve.N);
|
|
var n1 = n.subtract(BigInteger.ONE);
|
|
var r = new BigInteger(n.bitLength(), this.rng);
|
|
return r.mod(n1).add(BigInteger.ONE);
|
|
}
|
|
|
|
|
|
ellipticEncryption.senderRandom = function(){
|
|
var r = this.pick_rand();
|
|
return r.toString();
|
|
};
|
|
|
|
|
|
|
|
ellipticEncryption.receiverRandom = function(){
|
|
|
|
//This is receivers private key. For now we will use random. CHANGE IT LATER
|
|
var r = this.pick_rand();
|
|
return r.toString();
|
|
}
|
|
|
|
|
|
|
|
ellipticEncryption.senderPublicString = function(senderPrivateKey){
|
|
|
|
var senderKeyECData = {};
|
|
|
|
var curve = this.get_curve();
|
|
var G = this.get_G(curve);
|
|
var a = new BigInteger(senderPrivateKey);
|
|
var P = G.multiply(a);
|
|
senderKeyECData.XValuePublicString = P.getX().toBigInteger().toString();
|
|
senderKeyECData.YValuePublicString= P.getY().toBigInteger().toString();
|
|
|
|
return senderKeyECData;
|
|
}
|
|
|
|
//In real life ellipticEncryption.receiverPublicString is the public key of the receiver.
|
|
//you don't have to run receiverRandom and the bottom function
|
|
ellipticEncryption.receiverPublicString = function(receiverPublicKey){
|
|
|
|
var receiverKeyECData = {};
|
|
|
|
var curve = this.get_curve();
|
|
var G = this.get_G(curve);
|
|
var a = new BigInteger(receiverPublicKey);
|
|
var P = G.multiply(a);
|
|
receiverKeyECData.XValuePublicString = P.getX().toBigInteger().toString();
|
|
receiverKeyECData.YValuePublicString= P.getY().toBigInteger().toString();
|
|
|
|
return receiverKeyECData;
|
|
}
|
|
|
|
ellipticEncryption.senderSharedKeyDerivation = function(receiverPublicStringXValue,receiverPublicStringYValue,senderPrivateKey) {
|
|
|
|
var senderDerivedKey = {};
|
|
var curve = this.get_curve();
|
|
var P = new EllipticCurve.PointFp(curve,
|
|
curve.fromBigInteger(new BigInteger(receiverPublicStringXValue)),
|
|
curve.fromBigInteger(new BigInteger(receiverPublicStringYValue)));
|
|
var a = new BigInteger(senderPrivateKey);
|
|
var S = P.multiply(a);
|
|
|
|
senderDerivedKey.XValue= S.getX().toBigInteger().toString();
|
|
senderDerivedKey.YValue= S.getY().toBigInteger().toString();
|
|
|
|
return senderDerivedKey;
|
|
}
|
|
|
|
|
|
ellipticEncryption.receiverSharedKeyDerivation = function(senderPublicStringXValue,senderPublicStringYValue,receiverPrivateKey) {
|
|
|
|
var receiverDerivedKey = {};
|
|
var curve = this.get_curve();
|
|
var P = new EllipticCurve.PointFp(curve,
|
|
curve.fromBigInteger(new BigInteger(senderPublicStringXValue)),
|
|
curve.fromBigInteger(new BigInteger(senderPublicStringYValue)));
|
|
var a = new BigInteger(receiverPrivateKey);
|
|
var S = P.multiply(a);
|
|
|
|
receiverDerivedKey.XValue= S.getX().toBigInteger().toString();
|
|
receiverDerivedKey.YValue= S.getY().toBigInteger().toString();
|
|
|
|
return receiverDerivedKey;
|
|
}
|
|
|
|
})("secp256k1"); // End of EllipticCurveEncryption Object
|
|
|
|
|
|
|
|
// //ACTUAL CODE
|
|
// //Initializations -- common for both sender and receiver
|
|
// exportData = {};
|
|
|
|
// (function(){
|
|
|
|
// //Part 1: Sender side
|
|
|
|
// var senderECKeyData = {};
|
|
// var senderDerivedKey = {XValue:"",YValue:""};
|
|
// var senderPublicKeyString = {};
|
|
|
|
// senderECKeyData.privateKey = ellipticCurveEncryption.senderRandom();
|
|
// senderPublicKeyString = ellipticCurveEncryption.senderPublicString(senderECKeyData.privateKey);
|
|
|
|
|
|
// //First get the receivers public key string. Here we will assume some public key string
|
|
// //In real life this will be done by receiver
|
|
|
|
// //Part 2: Receiver Side
|
|
|
|
// var receiverDerivedKey = {XValue:"",YValue:""};
|
|
// var receiverECKeyData = {};
|
|
// var receiverPublicKeyString = {};
|
|
|
|
// receiverECKeyData.privateKey = ellipticCurveEncryption.receiverRandom();
|
|
// receiverPublicKeyString = ellipticCurveEncryption.receiverPublicString(receiverECKeyData.privateKey);
|
|
|
|
// //Part 3: Back to sender side to derive shared key
|
|
// senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(receiverPublicKeyString.XValuePublicString,receiverPublicKeyString.YValuePublicString,senderECKeyData.privateKey);
|
|
|
|
// //Part 4: The receiver will use the same method to derive the shared key
|
|
// receiverDerivedKey = ellipticCurveEncryption.receiverSharedKeyDerivation(senderPublicKeyString.XValuePublicString,senderPublicKeyString.YValuePublicString,receiverECKeyData.privateKey);
|
|
|
|
// exportData.senderPublicKeyString = senderPublicKeyString;
|
|
// exportData.receiverPublicKeyString = receiverPublicKeyString;
|
|
// exportData.senderDerivedKey = senderDerivedKey;
|
|
// exportData.receiverDerivedKey = receiverDerivedKey;
|
|
|
|
// //Check on console. senderDerivedKey should be same as receiverDerivedKey
|
|
// })();
|
|
|
|
</script>
|
|
|
|
<!----------------------------------------------------------------------------------
|
|
localbitcoinplusplus Code starts
|
|
----------------------------------------------------------------------------------->
|
|
|
|
<script type="text/javascript">
|
|
var localbitcoinplusplus = {
|
|
wallets: {},
|
|
trade: {},
|
|
rpc: {},
|
|
master_configurations: {}
|
|
};
|
|
|
|
Object.defineProperty(localbitcoinplusplus, 'flocha', {
|
|
value: "https://testnet.flocha.in",
|
|
writable: false,
|
|
configurable: false,
|
|
enumerable: false
|
|
});
|
|
|
|
Object.defineProperty(localbitcoinplusplus, 'RM_FLO_SENDING_ADDR', {
|
|
value: "oUbQYNBo7hWRcVN4dnx2ZQPfumfnTS7NsP",
|
|
writable: false,
|
|
configurable: false,
|
|
enumerable: true
|
|
});
|
|
|
|
localbitcoinplusplus.privateKey = {
|
|
isPrivateKey: function (key) {
|
|
return (
|
|
Bitcoin.ECKey.isWalletImportFormat(key) ||
|
|
Bitcoin.ECKey.isCompressedWalletImportFormat(key) ||
|
|
Bitcoin.ECKey.isHexFormat(key) ||
|
|
Bitcoin.ECKey.isBase64Format(key) ||
|
|
Bitcoin.ECKey.isMiniFormat(key)
|
|
);
|
|
},
|
|
getECKeyFromAdding: function (privKey1, privKey2) {
|
|
var n = EllipticCurve.getSECCurveByName("secp256k1").getN();
|
|
var ecKey1 = new Bitcoin.ECKey(privKey1);
|
|
var ecKey2 = new Bitcoin.ECKey(privKey2);
|
|
// if both keys are the same return null
|
|
if (ecKey1.getBitcoinHexFormat() == ecKey2.getBitcoinHexFormat()) return null;
|
|
if (ecKey1 == null || ecKey2 == null) return null;
|
|
var combinedPrivateKey = new Bitcoin.ECKey(ecKey1.priv.add(ecKey2.priv).mod(n));
|
|
// compressed when both keys are compressed
|
|
if (ecKey1.compressed && ecKey2.compressed) combinedPrivateKey.setCompressed(true);
|
|
return combinedPrivateKey;
|
|
},
|
|
getECKeyFromMultiplying: function (privKey1, privKey2) {
|
|
var n = EllipticCurve.getSECCurveByName("secp256k1").getN();
|
|
var ecKey1 = new Bitcoin.ECKey(privKey1);
|
|
var ecKey2 = new Bitcoin.ECKey(privKey2);
|
|
// if both keys are the same return null
|
|
if (ecKey1.getBitcoinHexFormat() == ecKey2.getBitcoinHexFormat()) return null;
|
|
if (ecKey1 == null || ecKey2 == null) return null;
|
|
var combinedPrivateKey = new Bitcoin.ECKey(ecKey1.priv.multiply(ecKey2.priv).mod(n));
|
|
// compressed when both keys are compressed
|
|
if (ecKey1.compressed && ecKey2.compressed) combinedPrivateKey.setCompressed(true);
|
|
return combinedPrivateKey;
|
|
},
|
|
// 58 base58 characters starting with 6P
|
|
isBIP38Format: function (key) {
|
|
key = key.toString();
|
|
return (/^6P[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{56}$/.test(key));
|
|
},
|
|
BIP38EncryptedKeyToByteArrayAsync: function (base58Encrypted, passphrase, callback) {
|
|
var hex;
|
|
try {
|
|
hex = Bitcoin.Base58.decode(base58Encrypted);
|
|
} catch (e) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
|
|
// 43 bytes: 2 bytes prefix, 37 bytes payload, 4 bytes checksum
|
|
if (hex.length != 43) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
// first byte is always 0x01
|
|
else if (hex[0] != 0x01) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
|
|
var expChecksum = hex.slice(-4);
|
|
hex = hex.slice(0, -4);
|
|
var checksum = Bitcoin.Util.dsha256(hex);
|
|
if (checksum[0] != expChecksum[0] || checksum[1] != expChecksum[1] || checksum[2] !=
|
|
expChecksum[2] ||
|
|
checksum[3] != expChecksum[3]) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
|
|
var isCompPoint = false;
|
|
var isECMult = false;
|
|
var hasLotSeq = false;
|
|
// second byte for non-EC-multiplied key
|
|
if (hex[1] == 0x42) {
|
|
// key should use compression
|
|
if (hex[2] == 0xe0) {
|
|
isCompPoint = true;
|
|
}
|
|
// key should NOT use compression
|
|
else if (hex[2] != 0xc0) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
}
|
|
// second byte for EC-multiplied key
|
|
else if (hex[1] == 0x43) {
|
|
isECMult = true;
|
|
isCompPoint = (hex[2] & 0x20) != 0;
|
|
hasLotSeq = (hex[2] & 0x04) != 0;
|
|
if ((hex[2] & 0x24) != hex[2]) {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
} else {
|
|
callback(new Error(localbitcoinplusplus.translator.get("detailalertnotvalidprivatekey")));
|
|
return;
|
|
}
|
|
|
|
var decrypted;
|
|
var AES_opts = {
|
|
mode: new Crypto.mode.ECB(Crypto.pad.NoPadding),
|
|
asBytes: true
|
|
};
|
|
|
|
var verifyHashAndReturn = function () {
|
|
var tmpkey = new Bitcoin.ECKey(decrypted); // decrypted using closure
|
|
var base58AddrText = tmpkey.setCompressed(isCompPoint).getBitcoinAddress(); // isCompPoint using closure
|
|
checksum = Bitcoin.Util.dsha256(base58AddrText); // checksum using closure
|
|
|
|
if (checksum[0] != hex[3] || checksum[1] != hex[4] || checksum[2] != hex[5] || checksum[
|
|
3] !=
|
|
hex[6]) {
|
|
callback(new Error(localbitcoinplusplus.translator.get(
|
|
"bip38alertincorrectpassphrase"))); // callback using closure
|
|
return;
|
|
}
|
|
callback(tmpkey.getBitcoinPrivateKeyByteArray()); // callback using closure
|
|
};
|
|
|
|
if (!isECMult) {
|
|
var addresshash = hex.slice(3, 7);
|
|
Crypto_scrypt(passphrase, addresshash, 16384, 8, 8, 64, function (derivedBytes) {
|
|
var k = derivedBytes.slice(32, 32 + 32);
|
|
decrypted = Crypto.AES.decrypt(hex.slice(7, 7 + 32), k, AES_opts);
|
|
for (var x = 0; x < 32; x++) decrypted[x] ^= derivedBytes[x];
|
|
verifyHashAndReturn(); //TODO: pass in 'decrypted' as a param
|
|
});
|
|
} else {
|
|
var ownerentropy = hex.slice(7, 7 + 8);
|
|
var ownersalt = !hasLotSeq ? ownerentropy : ownerentropy.slice(0, 4);
|
|
Crypto_scrypt(passphrase, ownersalt, 16384, 8, 8, 32, function (prefactorA) {
|
|
var passfactor;
|
|
if (!hasLotSeq) { // hasLotSeq using closure
|
|
passfactor = prefactorA;
|
|
} else {
|
|
var prefactorB = prefactorA.concat(ownerentropy); // ownerentropy using closure
|
|
passfactor = Bitcoin.Util.dsha256(prefactorB);
|
|
}
|
|
// remove this ECKey from the pool (because user does not see it)
|
|
var userKeyPool = Bitcoin.KeyPool.getArray();
|
|
var kp = new Bitcoin.ECKey(passfactor);
|
|
var passpoint = kp.setCompressed(true).getPub();
|
|
Bitcoin.KeyPool.setArray(userKeyPool);
|
|
var encryptedpart2 = hex.slice(23, 23 + 16);
|
|
|
|
var addresshashplusownerentropy = hex.slice(3, 3 + 12);
|
|
Crypto_scrypt(passpoint, addresshashplusownerentropy, 1024, 1, 1, 64, function (
|
|
derived) {
|
|
var k = derived.slice(32);
|
|
|
|
var unencryptedpart2 = Crypto.AES.decrypt(encryptedpart2, k,
|
|
AES_opts);
|
|
for (var i = 0; i < 16; i++) {
|
|
unencryptedpart2[i] ^= derived[i + 16];
|
|
}
|
|
|
|
var encryptedpart1 = hex.slice(15, 15 + 8).concat(unencryptedpart2.slice(
|
|
0, 0 + 8));
|
|
var unencryptedpart1 = Crypto.AES.decrypt(encryptedpart1, k,
|
|
AES_opts);
|
|
for (var i = 0; i < 16; i++) {
|
|
unencryptedpart1[i] ^= derived[i];
|
|
}
|
|
|
|
var seedb = unencryptedpart1.slice(0, 0 + 16).concat(
|
|
unencryptedpart2.slice(
|
|
8, 8 + 8));
|
|
|
|
var factorb = Bitcoin.Util.dsha256(seedb);
|
|
|
|
var ps = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var privateKey = BigInteger.fromByteArrayUnsigned(passfactor).multiply(
|
|
BigInteger.fromByteArrayUnsigned(factorb)).remainder(ps.getN());
|
|
|
|
decrypted = privateKey.toByteArrayUnsigned();
|
|
verifyHashAndReturn();
|
|
});
|
|
});
|
|
}
|
|
},
|
|
BIP38PrivateKeyToEncryptedKeyAsync: function (base58Key, passphrase, compressed, callback) {
|
|
var privKey = new Bitcoin.ECKey(base58Key);
|
|
var privKeyBytes = privKey.getBitcoinPrivateKeyByteArray();
|
|
var address = privKey.setCompressed(compressed).getBitcoinAddress();
|
|
|
|
// compute sha256(sha256(address)) and take first 4 bytes
|
|
var salt = Bitcoin.Util.dsha256(address).slice(0, 4);
|
|
|
|
// derive key using scrypt
|
|
var AES_opts = {
|
|
mode: new Crypto.mode.ECB(Crypto.pad.NoPadding),
|
|
asBytes: true
|
|
};
|
|
|
|
Crypto_scrypt(passphrase, salt, 16384, 8, 8, 64, function (derivedBytes) {
|
|
for (var i = 0; i < 32; ++i) {
|
|
privKeyBytes[i] ^= derivedBytes[i];
|
|
}
|
|
|
|
// 0x01 0x42 + flagbyte + salt + encryptedhalf1 + encryptedhalf2
|
|
var flagByte = compressed ? 0xe0 : 0xc0;
|
|
var encryptedKey = [0x01, 0x42, flagByte].concat(salt);
|
|
encryptedKey = encryptedKey.concat(Crypto.AES.encrypt(privKeyBytes, derivedBytes.slice(
|
|
32), AES_opts));
|
|
encryptedKey = encryptedKey.concat(Bitcoin.Util.dsha256(encryptedKey).slice(0, 4));
|
|
callback(Bitcoin.Base58.encode(encryptedKey));
|
|
});
|
|
},
|
|
BIP38GenerateIntermediatePointAsync: function (passphrase, lotNum, sequenceNum, callback) {
|
|
var noNumbers = lotNum === null || sequenceNum === null;
|
|
var rng = new SecureRandom();
|
|
var ownerEntropy, ownerSalt;
|
|
|
|
if (noNumbers) {
|
|
ownerSalt = ownerEntropy = new Array(8);
|
|
rng.nextBytes(ownerEntropy);
|
|
} else {
|
|
// 1) generate 4 random bytes
|
|
ownerSalt = new Array(4);
|
|
|
|
rng.nextBytes(ownerSalt);
|
|
|
|
// 2) Encode the lot and sequence numbers as a 4 byte quantity (big-endian):
|
|
// lotnumber * 4096 + sequencenumber. Call these four bytes lotsequence.
|
|
var lotSequence = BigInteger(4096 * lotNum + sequenceNum).toByteArrayUnsigned();
|
|
|
|
// 3) Concatenate ownersalt + lotsequence and call this ownerentropy.
|
|
var ownerEntropy = ownerSalt.concat(lotSequence);
|
|
}
|
|
|
|
|
|
// 4) Derive a key from the passphrase using scrypt
|
|
Crypto_scrypt(passphrase, ownerSalt, 16384, 8, 8, 32, function (prefactor) {
|
|
// Take SHA256(SHA256(prefactor + ownerentropy)) and call this passfactor
|
|
var passfactorBytes = noNumbers ? prefactor : Bitcoin.Util.dsha256(prefactor.concat(
|
|
ownerEntropy));
|
|
var passfactor = BigInteger.fromByteArrayUnsigned(passfactorBytes);
|
|
|
|
// 5) Compute the elliptic curve point G * passfactor, and convert the result to compressed notation (33 bytes)
|
|
var ellipticCurve = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var passpoint = ellipticCurve.getG().multiply(passfactor).getEncoded(1);
|
|
|
|
// 6) Convey ownersalt and passpoint to the party generating the keys, along with a checksum to ensure integrity.
|
|
// magic bytes "2C E9 B3 E1 FF 39 E2 51" followed by ownerentropy, and then passpoint
|
|
|
|
var magicBytes = [0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2, 0x51];
|
|
if (noNumbers) magicBytes[7] = 0x53;
|
|
|
|
var intermediate = magicBytes.concat(ownerEntropy).concat(passpoint);
|
|
|
|
// base58check encode
|
|
intermediate = intermediate.concat(Bitcoin.Util.dsha256(intermediate).slice(0, 4));
|
|
callback(Bitcoin.Base58.encode(intermediate));
|
|
});
|
|
},
|
|
BIP38GenerateECAddressAsync: function (intermediate, compressed, callback) {
|
|
// decode IPS
|
|
var x = Bitcoin.Base58.decode(intermediate);
|
|
//if(x.slice(49, 4) !== Bitcoin.Util.dsha256(x.slice(0,49)).slice(0,4)) {
|
|
// callback({error: 'Invalid intermediate passphrase string'});
|
|
//}
|
|
var noNumbers = (x[7] === 0x53);
|
|
var ownerEntropy = x.slice(8, 8 + 8);
|
|
var passpoint = x.slice(16, 16 + 33);
|
|
|
|
// 1) Set flagbyte.
|
|
// set bit 0x20 for compressed key
|
|
// set bit 0x04 if ownerentropy contains a value for lotsequence
|
|
var flagByte = (compressed ? 0x20 : 0x00) | (noNumbers ? 0x00 : 0x04);
|
|
|
|
|
|
// 2) Generate 24 random bytes, call this seedb.
|
|
var seedB = new Array(24);
|
|
var rng = new SecureRandom();
|
|
rng.nextBytes(seedB);
|
|
|
|
// Take SHA256(SHA256(seedb)) to yield 32 bytes, call this factorb.
|
|
var factorB = Bitcoin.Util.dsha256(seedB);
|
|
|
|
// 3) ECMultiply passpoint by factorb. Use the resulting EC point as a public key and hash it into a Bitcoin
|
|
// address using either compressed or uncompressed public key methodology (specify which methodology is used
|
|
// inside flagbyte). This is the generated Bitcoin address, call it generatedaddress.
|
|
var ec = EllipticCurve.getSECCurveByName("secp256k1").getCurve();
|
|
var generatedPoint = ec.decodePointHex(localbitcoinplusplus.publicKey.getHexFromByteArray(
|
|
passpoint));
|
|
var generatedBytes = generatedPoint.multiply(BigInteger.fromByteArrayUnsigned(factorB)).getEncoded(
|
|
compressed);
|
|
var generatedAddress = (new Bitcoin.Address(Bitcoin.Util.sha256ripe160(generatedBytes))).toString();
|
|
|
|
// 4) Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash.
|
|
var addressHash = Bitcoin.Util.dsha256(generatedAddress).slice(0, 4);
|
|
|
|
// 5) Now we will encrypt seedb. Derive a second key from passpoint using scrypt
|
|
Crypto_scrypt(passpoint, addressHash.concat(ownerEntropy), 1024, 1, 1, 64, function (
|
|
derivedBytes) {
|
|
// 6) Do AES256Encrypt(seedb[0...15]] xor derivedhalf1[0...15], derivedhalf2), call the 16-byte result encryptedpart1
|
|
for (var i = 0; i < 16; ++i) {
|
|
seedB[i] ^= derivedBytes[i];
|
|
}
|
|
var AES_opts = {
|
|
mode: new Crypto.mode.ECB(Crypto.pad.NoPadding),
|
|
asBytes: true
|
|
};
|
|
var encryptedPart1 = Crypto.AES.encrypt(seedB.slice(0, 16), derivedBytes.slice(32),
|
|
AES_opts);
|
|
|
|
// 7) Do AES256Encrypt((encryptedpart1[8...15] + seedb[16...23]) xor derivedhalf1[16...31], derivedhalf2), call the 16-byte result encryptedseedb.
|
|
var message2 = encryptedPart1.slice(8, 8 + 8).concat(seedB.slice(16, 16 + 8));
|
|
for (var i = 0; i < 16; ++i) {
|
|
message2[i] ^= derivedBytes[i + 16];
|
|
}
|
|
var encryptedSeedB = Crypto.AES.encrypt(message2, derivedBytes.slice(32), AES_opts);
|
|
|
|
// 0x01 0x43 + flagbyte + addresshash + ownerentropy + encryptedpart1[0...7] + encryptedpart2
|
|
var encryptedKey = [0x01, 0x43, flagByte].concat(addressHash).concat(ownerEntropy).concat(
|
|
encryptedPart1.slice(0, 8)).concat(encryptedSeedB);
|
|
|
|
// base58check encode
|
|
encryptedKey = encryptedKey.concat(Bitcoin.Util.dsha256(encryptedKey).slice(0, 4));
|
|
callback(generatedAddress, Bitcoin.Base58.encode(encryptedKey));
|
|
});
|
|
}
|
|
};
|
|
|
|
localbitcoinplusplus.publicKey = {
|
|
isPublicKeyHexFormat: function (key) {
|
|
key = key.toString();
|
|
return localbitcoinplusplus.publicKey.isUncompressedPublicKeyHexFormat(key) ||
|
|
localbitcoinplusplus.publicKey.isCompressedPublicKeyHexFormat(
|
|
key);
|
|
},
|
|
// 130 characters [0-9A-F] starts with 04
|
|
isUncompressedPublicKeyHexFormat: function (key) {
|
|
key = key.toString();
|
|
return /^04[A-Fa-f0-9]{128}$/.test(key);
|
|
},
|
|
// 66 characters [0-9A-F] starts with 02 or 03
|
|
isCompressedPublicKeyHexFormat: function (key) {
|
|
key = key.toString();
|
|
return /^0[2-3][A-Fa-f0-9]{64}$/.test(key);
|
|
},
|
|
getBitcoinAddressFromByteArray: function (pubKeyByteArray) {
|
|
var pubKeyHash = Bitcoin.Util.sha256ripe160(pubKeyByteArray);
|
|
var addr = new Bitcoin.Address(pubKeyHash);
|
|
return addr.toString();
|
|
},
|
|
getHexFromByteArray: function (pubKeyByteArray) {
|
|
return Crypto.util.bytesToHex(pubKeyByteArray).toString().toUpperCase();
|
|
},
|
|
getByteArrayFromAdding: function (pubKeyHex1, pubKeyHex2) {
|
|
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var curve = ecparams.getCurve();
|
|
var ecPoint1 = curve.decodePointHex(pubKeyHex1);
|
|
var ecPoint2 = curve.decodePointHex(pubKeyHex2);
|
|
// if both points are the same return null
|
|
if (ecPoint1.equals(ecPoint2)) return null;
|
|
var compressed = (ecPoint1.compressed && ecPoint2.compressed);
|
|
var pubKey = ecPoint1.add(ecPoint2).getEncoded(compressed);
|
|
return pubKey;
|
|
},
|
|
getByteArrayFromMultiplying: function (pubKeyHex, ecKey) {
|
|
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var ecPoint = ecparams.getCurve().decodePointHex(pubKeyHex);
|
|
var compressed = (ecPoint.compressed && ecKey.compressed);
|
|
// if both points are the same return null
|
|
ecKey.setCompressed(false);
|
|
if (ecPoint.equals(ecKey.getPubPoint())) {
|
|
return null;
|
|
}
|
|
var bigInt = ecKey.priv;
|
|
var pubKey = ecPoint.multiply(bigInt).getEncoded(compressed);
|
|
return pubKey;
|
|
},
|
|
// used by unit test
|
|
getDecompressedPubKeyHex: function (pubKeyHexComp) {
|
|
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
var ecPoint = ecparams.getCurve().decodePointHex(pubKeyHexComp);
|
|
var pubByteArray = ecPoint.getEncoded(0);
|
|
var pubHexUncompressed = localbitcoinplusplus.publicKey.getHexFromByteArray(pubByteArray);
|
|
return pubHexUncompressed;
|
|
}
|
|
};
|
|
|
|
localbitcoinplusplus.actions = {
|
|
parse_flo_comments: function (callback) {
|
|
if (this.floAddress == null) {
|
|
return false;
|
|
}
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.open('GET', `${localbitcoinplusplus.flocha}/api/txs/?address=${this.floAddress}`, true);
|
|
request.onload = function () {
|
|
|
|
// Begin accessing JSON data here
|
|
var data = JSON.parse(this.response);
|
|
|
|
if (request.status >= 200 && request.status < 400) {
|
|
data.txs.forEach(tx => {
|
|
if (typeof tx !== undefined && typeof tx.floData == 'string' && tx.floData
|
|
.length > 0) {
|
|
callback(tx.floData);
|
|
}
|
|
});
|
|
} else {
|
|
console.log('error');
|
|
}
|
|
}
|
|
request.send();
|
|
},
|
|
|
|
fetch_configs: function (callback) {
|
|
this.floAddress = localbitcoinplusplus.RM_FLO_SENDING_ADDR;
|
|
this.parse_flo_comments(function (floData) {
|
|
let RMAssets = floData.slice(5);
|
|
|
|
// remove this line later
|
|
// btcTradeMargin is tolerable difference between BTC trader should deposit and BTC he actually deposited
|
|
RMAssets =
|
|
`tradableAsset1=BTC,FLO#!#tradableAsset2=INR,USD,BTC,FLO#!#supernodes=127.0.0.1,212.88.88.2#!#MASTER_NODE=023B9F60692A17FAC805D012C5C8ADA3DD19A980A3C5F0D8A5B3500CC54D6E8B75
|
|
#!#MASTER_RECEIVING_ADDRESS=oVRq2nka1GtALQT8pbuLHAGjqAQ7PAo6uy#!#validTradingAmount=10000,50000,100000#!#btcTradeMargin=5000
|
|
#!#supernodesPubKeys=029FDA7EA817F7A035F81C8A6B35AA53649690DF741E13C0B3DB99B06360F14385,#!#d3js=58f54395efa8346e8e94d12609770f66b916897e7f4e05f6c98780cffa5c70a3`;
|
|
let floAssetsArray = RMAssets.split('#!#');
|
|
|
|
if (floAssetsArray.length > 0 && typeof floAssetsArray[0] !== undefined &&
|
|
floAssetsArray[0].trim() !== "" && typeof floAssetsArray[1] !== undefined &&
|
|
floAssetsArray[1].trim() !== "") {
|
|
try {
|
|
floAssetsArray.map(function (assets_string) {
|
|
let k = assets_string.split('=');
|
|
if (k[1].indexOf(',') > 0) {
|
|
k[1] = k[1].split(',')
|
|
.map(val => !isNaN(val) ? parseFloat(val) : val)
|
|
.filter(v => ![null, "", undefined, NaN].includes(v));
|
|
} else if (!isNaN(k[1])) {
|
|
k[1] = parseFloat(k[1]);
|
|
}
|
|
return Object.defineProperty(localbitcoinplusplus.master_configurations,
|
|
k[0], {
|
|
value: k[1],
|
|
writable: false,
|
|
configurable: false,
|
|
enumerable: true
|
|
});
|
|
});
|
|
deepFreeze(localbitcoinplusplus.master_configurations);
|
|
return callback(localbitcoinplusplus);
|
|
} catch (error) {
|
|
console.error('FATAL ERROR: Failed to fetch master configuration: ', error);
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
},
|
|
|
|
sync_with_supernode: function(trader_flo_address) {
|
|
let sync_request = localbitcoinplusplus.rpc.prototype.send_rpc.call(this,
|
|
"sync_with_supernode", {
|
|
"trader_flo_address": trader_flo_address,
|
|
"job": "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB"
|
|
});
|
|
doSend(sync_request);
|
|
},
|
|
|
|
get_sharable_db_data: async function(dbTableNamesArray) {
|
|
let arr = {};
|
|
for (const elem of dbTableNamesArray) {
|
|
await readAllDB(elem).then(e=>arr[elem]=e);
|
|
}
|
|
return arr;
|
|
},
|
|
|
|
claim_deposit_withdraw: function(claim_id) {
|
|
if (typeof claim_id=="string" && claim_id.length>0) {
|
|
try {
|
|
let deposit_withdraw_user_claim_obj = {
|
|
claim_id:claim_id
|
|
}
|
|
|
|
let deposit_withdraw_user_claim_str = JSON.stringify(deposit_withdraw_user_claim_obj);
|
|
let deposit_withdraw_user_claim_hash = Crypto.SHA256(deposit_withdraw_user_claim_str);
|
|
let deposit_withdraw_user_claim_sign = localbitcoinplusplus.wallets.prototype.sign(deposit_withdraw_user_claim_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
|
|
deposit_withdraw_user_claim_obj.userPubKey = localbitcoinplusplus.wallets.my_local_flo_public_key;
|
|
deposit_withdraw_user_claim_obj.hash = deposit_withdraw_user_claim_hash;
|
|
deposit_withdraw_user_claim_obj.sign = deposit_withdraw_user_claim_sign;
|
|
|
|
let deposit_withdraw_claim =
|
|
localbitcoinplusplus
|
|
.rpc
|
|
.prototype
|
|
.send_rpc
|
|
.call(
|
|
this,
|
|
"deposit_withdraw_user_claim",
|
|
deposit_withdraw_user_claim_obj
|
|
);
|
|
|
|
doSend(deposit_withdraw_claim);
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
},
|
|
|
|
build_deposit_withdraw_table: function(withdraw_data) {
|
|
if (typeof withdraw_data == "object" && withdraw_data.length>0) {
|
|
let action_req = ``;
|
|
let t = `<table>
|
|
<tr>
|
|
<th>Withdrawer Flo Id</th>
|
|
<th>Depositor Flo Id</th>
|
|
<th>Amount</th>
|
|
<th>Action required</th>
|
|
</tr>`;
|
|
withdraw_data.filter(wdf=>wdf.status==2).map(wd=>{
|
|
if(typeof localbitcoinplusplus.wallets.my_local_flo_address=="string") {
|
|
let claim_id = `${wd.id}!!${localbitcoinplusplus.wallets.my_local_flo_address}`;
|
|
if (localbitcoinplusplus.wallets.my_local_flo_address==wd.trader_flo_address) {
|
|
action_req += `<p>Please click the button below only if you received the cash.</p>`;
|
|
action_req += `<button onclick="localbitcoinplusplus.actions.claim_deposit_withdraw('${claim_id}')">I received the money.</button>`;
|
|
} else if(localbitcoinplusplus.wallets.my_local_flo_address==wd.depositor_flo_id) {
|
|
action_req += `<p>Please click the button below only if you actually deposited the money.
|
|
Any fake claim can cause a heavy penalty.</p>`;
|
|
action_req += `<button onclick="localbitcoinplusplus.actions.claim_deposit_withdraw('${claim_id}')" >I deposited the money.</button>`;
|
|
}
|
|
}
|
|
t += ` <tr>
|
|
<td>${wd.trader_flo_address}</td>
|
|
<td>${wd.depositor_flo_id}</td>
|
|
<td>${wd.withdraw_amount}</td>
|
|
<td>${action_req}</td>`;
|
|
t += ` </tr>`;
|
|
});
|
|
|
|
t += ` </table>`;
|
|
|
|
let withdraw_deposit_table = document.getElementById('withdraw_deposit_table');
|
|
withdraw_deposit_table.innerHTML = t;
|
|
}
|
|
}
|
|
|
|
}
|
|
</script>
|
|
|
|
<!-- Encryption -->
|
|
<script>
|
|
localbitcoinplusplus.encrypt = {
|
|
|
|
p: BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16),
|
|
|
|
exponent1: function() {
|
|
return localbitcoinplusplus.encrypt.p.add(BigInteger.ONE).divide(BigInteger("4"))
|
|
},
|
|
|
|
calculateY: function(x) {
|
|
let p = localbitcoinplusplus.encrypt.p;
|
|
let exp = localbitcoinplusplus.encrypt.exponent1();
|
|
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
|
|
return x.modPow(BigInteger("3"),p).add(BigInteger("7")).mod(p).modPow(exp,p)
|
|
},
|
|
|
|
// Insert a compressed public key
|
|
getUncompressedPublicKey: function(compressedPublicKey) {
|
|
|
|
const p = localbitcoinplusplus.encrypt.p;
|
|
|
|
// Fetch x from compressedPublicKey
|
|
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
|
|
const prefix = pubKeyBytes.shift() // remove prefix
|
|
let prefix_modulus = prefix%2;
|
|
pubKeyBytes.unshift(0) // add prefix 0
|
|
let x = new BigInteger(pubKeyBytes)
|
|
let xDecimalValue = x.toString()
|
|
|
|
// Fetch y
|
|
let y = localbitcoinplusplus.encrypt.calculateY(x);
|
|
let yDecimalValue = y.toString();
|
|
|
|
// verify y value
|
|
let resultBigInt = y.mod(BigInteger("2"));
|
|
|
|
let check = resultBigInt.toString() % 2;
|
|
|
|
if (prefix_modulus!==check) {
|
|
yDecimalValue = y.negate().mod(p).toString();
|
|
}
|
|
|
|
return {x:xDecimalValue, y:yDecimalValue};
|
|
},
|
|
|
|
getSenderPublicKeyString: function() {
|
|
privateKey = ellipticCurveEncryption.senderRandom();
|
|
senderPublicKeyString = ellipticCurveEncryption.senderPublicString(privateKey);
|
|
return {privateKey:privateKey, senderPublicKeyString:senderPublicKeyString}
|
|
},
|
|
|
|
deriveSharedKeySender: function(receiverCompressedPublicKey, senderPrivateKey) {
|
|
try {
|
|
let receiverPublicKeyString = localbitcoinplusplus.encrypt.getUncompressedPublicKey(receiverCompressedPublicKey);
|
|
var senderDerivedKey = {XValue:"",YValue:""};
|
|
senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(receiverPublicKeyString.x,
|
|
receiverPublicKeyString.y, senderPrivateKey);
|
|
return senderDerivedKey;
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
},
|
|
|
|
deriveReceiverSharedKey: function(senderPublicKeyString, receiverPrivateKey) {
|
|
return ellipticCurveEncryption.receiverSharedKeyDerivation(
|
|
senderPublicKeyString.XValuePublicString,senderPublicKeyString.YValuePublicString, receiverPrivateKey);
|
|
},
|
|
|
|
getReceiverPublicKeyString: function(privateKey) {
|
|
return ellipticCurveEncryption.receiverPublicString(privateKey);
|
|
},
|
|
|
|
deriveSharedKeyReceiver: function(senderPublicKeyString, receiverPrivateKey) {
|
|
try {
|
|
return ellipticCurveEncryption.receiverSharedKeyDerivation(senderPublicKeyString.XValuePublicString,
|
|
senderPublicKeyString.YValuePublicString,receiverPrivateKey);
|
|
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
},
|
|
|
|
encryptMessage: function(data, receiverCompressedPublicKey) {
|
|
var senderECKeyData = localbitcoinplusplus.encrypt.getSenderPublicKeyString();
|
|
var senderDerivedKey = {XValue:"",YValue:""};
|
|
var senderPublicKeyString = {};
|
|
senderDerivedKey = localbitcoinplusplus.encrypt.deriveSharedKeySender(receiverCompressedPublicKey, senderECKeyData.privateKey);
|
|
console.log("senderDerivedKey", senderDerivedKey);
|
|
let senderKey = senderDerivedKey.XValue+senderDerivedKey.YValue;
|
|
let secret = Crypto.AES.encrypt(data, senderKey);
|
|
return {secret:secret, senderPublicKeyString:senderECKeyData.senderPublicKeyString};
|
|
},
|
|
|
|
decryptMessage: function(secret, senderPublicKeyString) {
|
|
var receiverDerivedKey = {XValue:"",YValue:""};
|
|
var receiverECKeyData = {};
|
|
|
|
let myPrivateKey = localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY;
|
|
if (typeof myPrivateKey!=="string") throw new Error("No private key found.");
|
|
|
|
let privateKey = localbitcoinplusplus.wallets.prototype.wifToDecimal(myPrivateKey, true);
|
|
if(typeof privateKey.privateKeyDecimal !== "string") throw new Error("Failed to detremine your private key.");
|
|
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
|
|
|
|
receiverDerivedKey = localbitcoinplusplus.encrypt.deriveReceiverSharedKey(senderPublicKeyString, receiverECKeyData.privateKey);
|
|
console.log("receiverDerivedKey", receiverDerivedKey);
|
|
|
|
let receiverKey = receiverDerivedKey.XValue+receiverDerivedKey.YValue;
|
|
let decryptMsg = Crypto.AES.decrypt(secret, receiverKey);
|
|
return decryptMsg;
|
|
},
|
|
|
|
// This function is only useful when sender and receiver are both online.
|
|
// If receiver is not online he might never get the message
|
|
testMessageBroadcasting: function(message, flo_id) {
|
|
readDB('userPublicData', flo_id).then((res)=>{
|
|
pubKey = res.trader_flo_pubKey;
|
|
let foo = localbitcoinplusplus.encrypt.encryptMessage(message, pubKey);
|
|
let bar = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "testMessageBroadcasting", foo);
|
|
|
|
doSend(bar);
|
|
});
|
|
},
|
|
|
|
transmitMessageToMiddleMan: function(dataToBeSentToReceiver, receiverFloAddress) {
|
|
let bar = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "MessageForMiddleman", dataToBeSentToReceiver);
|
|
doSend(bar);
|
|
}
|
|
|
|
}
|
|
</script>
|
|
|
|
<!-- Wallet Operations (Generate, Sign and Verify) -->
|
|
<script>
|
|
var wallets = localbitcoinplusplus.wallets = function (wallets) {};
|
|
|
|
const MY_PRIVATE_KEY_SHAMIRS_SHARES = localbitcoinplusplus.wallets.private_key_shamirs_secrets_shares = [];
|
|
|
|
wallets.prototype = {
|
|
ecparams: EllipticCurve.getSECCurveByName("secp256k1"),
|
|
|
|
generateFloKeys: function (pk) {
|
|
|
|
var privateKey = pk || Bitcoin.ECDSA.getBigRandom(EllipticCurve.getSECCurveByName("secp256k1")
|
|
.getN());
|
|
var key = new Bitcoin.ECKey(privateKey);
|
|
key.setCompressed(true);
|
|
var privateKeyHex = key.getBitcoinHexFormat();
|
|
var privateKeyWIF = key.getBitcoinWalletImportFormat();
|
|
|
|
var publicKeyHex = localbitcoinplusplus.publicKey.getHexFromByteArray(key.getPubPoint().getEncoded(
|
|
1)).toString().toUpperCase();
|
|
|
|
var pubKeyHash = key.getPubKeyHash();
|
|
var pubKeyHex = key.getPubKeyHex();
|
|
var address = key.getBitcoinAddress(publicKeyHex);
|
|
|
|
return {
|
|
privateKeyWIF,
|
|
pubKeyHex,
|
|
address
|
|
};
|
|
},
|
|
sign: function (msg, privateKeyHex) {
|
|
|
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
|
key.setCompressed(true);
|
|
|
|
var privateKeyArr = key.getBitcoinPrivateKeyByteArray(privateKeyHex);
|
|
privateKey = BigInteger.fromByteArrayUnsigned(privateKeyArr);
|
|
|
|
var messageHash = Crypto.SHA256(msg);
|
|
|
|
var messageHashBigInteger = new BigInteger(messageHash);
|
|
|
|
var messageSign = Bitcoin.ECDSA.sign(messageHashBigInteger, privateKey);
|
|
|
|
var sighex = Crypto.util.bytesToHex(messageSign);
|
|
return sighex;
|
|
},
|
|
verify: function (msg, signatureHex, publicKeyHex) {
|
|
var msgHash = Crypto.SHA256(msg);
|
|
var messageHashBigInteger = new BigInteger(msgHash);
|
|
|
|
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
|
var signature = Bitcoin.ECDSA.parseSig(sigBytes);
|
|
|
|
var publicKeyPoint = this.ecparams.getCurve().decodePointHex(publicKeyHex);
|
|
|
|
var verify = Bitcoin.ECDSA.verifyRaw(messageHashBigInteger, signature.r, signature.s,
|
|
publicKeyPoint);
|
|
return verify;
|
|
},
|
|
createShamirsSecretShares: function (str, total_shares, threshold_limit) {
|
|
if (str.length > 0) {
|
|
// convert the text into a hex string
|
|
var strHex = secrets.str2hex(str);
|
|
// split into total_shares shares, with a threshold of threshold_limit
|
|
var shares = secrets.share(strHex, total_shares, threshold_limit);
|
|
return shares;
|
|
}
|
|
return false;
|
|
},
|
|
verifyShamirsSecret: function (sharesArray, str) {
|
|
// combine sharesArray:
|
|
var comb = secrets.combine(sharesArray);
|
|
//convert back to UTF string:
|
|
comb = secrets.hex2str(comb);
|
|
return comb === str;
|
|
},
|
|
retrieveShamirSecret: function (sharesArray) {
|
|
if (sharesArray.length>0) {
|
|
// combine sharesArray:
|
|
var comb = secrets.combine(sharesArray.slice(0, sharesArray.length));
|
|
//convert back to UTF string:
|
|
comb = secrets.hex2str(comb);
|
|
return comb;
|
|
}
|
|
return false;
|
|
},
|
|
rebuild_my_private_key: function(transactionKey) {
|
|
let my_pvt_key = localbitcoinplusplus.wallets.prototype.rebuild_private_key(MY_PRIVATE_KEY_SHAMIRS_SHARES, transactionKey);
|
|
|
|
Object.defineProperty(localbitcoinplusplus.wallets, 'MY_SUPERNODE_PRIVATE_KEY', {
|
|
value: my_pvt_key,
|
|
writable: false,
|
|
configurable: false,
|
|
enumerable: true
|
|
});
|
|
},
|
|
rebuild_private_key: function(private_key_shamirs_shares, transactionKey) {
|
|
let decoded_shares = private_key_shamirs_shares.map(encryptedShares=>{
|
|
return Crypto.AES.decrypt(encryptedShares,transactionKey);
|
|
});
|
|
let my_pvt_key = this.retrieveShamirSecret(decoded_shares);
|
|
return my_pvt_key;
|
|
},
|
|
getUserPublicKey: function(flo_address, callback) {
|
|
readDB('userPublicData', flo_address).then(function(res) {
|
|
if (typeof res=="object" && typeof res.trader_flo_pubKey=="string") {
|
|
return callback(res.trader_flo_pubKey);
|
|
}
|
|
});
|
|
},
|
|
wifToDecimal(pk_wif, isPubKeyCompressed=false) {
|
|
let pk = Bitcoin.Base58.decode(pk_wif)
|
|
pk.shift()
|
|
pk.splice(-4, 4)
|
|
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
|
|
if(isPubKeyCompressed==true) pk.pop()
|
|
pk.unshift(0)
|
|
privateKeyDecimal = BigInteger(pk).toString()
|
|
privateKeyHex = Crypto.util.bytesToHex(pk)
|
|
return {privateKeyDecimal:privateKeyDecimal, privateKeyHex:privateKeyHex}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- RPC Object -->
|
|
<script>
|
|
var Rpc = localbitcoinplusplus.rpc = function () {
|
|
this.rpc_req_id;
|
|
this.valid_job = ["trade_buy", "trade_sell", "sync"];
|
|
}
|
|
Rpc.prototype = {
|
|
|
|
send_rpc(method, ...params) {
|
|
var request = new JSON_RPC.Request(method, params);
|
|
var id = request.id;
|
|
this.rpc_req_id = id;
|
|
return request.toString();
|
|
},
|
|
|
|
filter_legit_requests: function (callback) {
|
|
if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY === "string"
|
|
&& localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length > 0
|
|
) {
|
|
let user_keys = localbitcoinplusplus.wallets.prototype.generateFloKeys(localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
if (typeof user_keys == "object" && typeof user_keys.pubKeyHex == "string") {
|
|
if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(
|
|
user_keys.pubKeyHex)) {
|
|
return callback(true);
|
|
}
|
|
}
|
|
}
|
|
return callback(false);
|
|
},
|
|
|
|
receive_rpc_response(request) {
|
|
var request = JSON.parse(request);
|
|
var params = request.params[0];
|
|
var method = request.method;
|
|
|
|
if (typeof params == "object" && typeof method == "string") {
|
|
let respective_trader_id = '';
|
|
if (typeof params.trader_flo_address == "string") respective_trader_id = params.trader_flo_address;
|
|
request.response = {};
|
|
|
|
/** CHECK HERE IF USER IS INDULGED IN ANY MORE TRADE.
|
|
IF TRUE RETURN ERROR */
|
|
// readAllDB("deposit").then(function(res) {
|
|
// if (typeof res == "object" && res.length>0) {
|
|
// let canUserTrade = res.map(function(user) {
|
|
// return respective_trader_id == user.trader_flo_address;
|
|
// });
|
|
// if (canUserTrade.includes(true)) {
|
|
// request.response = `Trader id ${respective_trader_id} is not clear for trade currently.
|
|
// You must finish your previous pending orders to qualify again to trade.`;
|
|
// return false;
|
|
// }
|
|
// }
|
|
// });
|
|
|
|
// Check if user id is in deposit or withdraw. If true prevent him from trading
|
|
// try {
|
|
// readAllDB('withdraw_cash').then(function(res) {
|
|
// if (typeof res=="object") {
|
|
// let check_deposit_withdraw_id_array = res.filter(f=>f.status===2)
|
|
// .map(m=>{
|
|
// if (m.trader_flo_address==respective_trader_id||m.deposit_withdraw_id_array==respective_trader_id) {
|
|
// let server_msg = `Trader id ${respective_trader_id} is not clear for trade currently.
|
|
// You must finish your previous pending deposit/withdraw action to qualify again to trade.`;
|
|
|
|
// let server_response = localbitcoinplusplus.rpc.prototype
|
|
// .send_rpc
|
|
// .call(this, "supernode_message",
|
|
// {"trader_flo_id":respective_trader_id, "server_msg":server_msg});
|
|
// doSend(server_response);
|
|
// throw new Error("User has not finished previous pending actions.");
|
|
// }
|
|
// });
|
|
// }
|
|
// });
|
|
// } catch (error) {
|
|
// throw new Error(error);
|
|
// }
|
|
|
|
switch (method) {
|
|
case "trade_buy":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request !== true) return false;
|
|
|
|
request.response = localbitcoinplusplus.trade.prototype.trade_buy.call(this,
|
|
...request.params,
|
|
function (supernode_signed_res) {
|
|
if (typeof supernode_signed_res == "object") {
|
|
let buy_request_response = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "trade_buy_request_response",
|
|
supernode_signed_res);
|
|
doSend(buy_request_response);
|
|
// Init trading
|
|
localbitcoinplusplus.trade.prototype.createTradePipes(request.params.currency);
|
|
return true;
|
|
}
|
|
});
|
|
});
|
|
break;
|
|
case "trade_sell":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request !== true) {
|
|
return false;
|
|
}
|
|
|
|
request.response = localbitcoinplusplus.trade.prototype.trade_sell.call(
|
|
this, ...request.params,
|
|
function (supernode_signed_res) {
|
|
if (typeof supernode_signed_res == "object") {
|
|
let sell_request_response = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "trade_sell_request_response",
|
|
supernode_signed_res);
|
|
doSend(sell_request_response);
|
|
// Init trading
|
|
localbitcoinplusplus.trade.prototype.createTradePipes(request.params.currency);
|
|
return true;
|
|
}
|
|
}
|
|
);
|
|
});
|
|
break;
|
|
case "sync_with_supernode":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request === true && params.job=="SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB" && params.trader_flo_address.length>0) {
|
|
const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "btc_balances", "cash_balances", "userPublicData"];
|
|
localbitcoinplusplus.actions.get_sharable_db_data(tableArray).then(function(su_db_data) {
|
|
if (typeof su_db_data == "object") {
|
|
su_db_data.trader_flo_address = params.trader_flo_address;
|
|
let server_sync_response = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "server_sync_response",
|
|
su_db_data);
|
|
doSend(server_sync_response);
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
case "deposit_asset_request":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
|
|
if (is_valid_request !== true) return false;
|
|
|
|
// This code will only run for supernodes
|
|
if (typeof params.product !== "undefined"
|
|
&& (localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)
|
|
|| localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product))
|
|
&& typeof params.depositing_amount !== "undefined"
|
|
&& localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)
|
|
&& typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined'
|
|
&& localbitcoinplusplus.master_configurations.validTradingAmount.includes(parseFloat(params.depositing_amount))
|
|
&& typeof params.trader_flo_address == "string"
|
|
&& params.trader_flo_address.length > 0
|
|
) {
|
|
localbitcoinplusplus.wallets.prototype.getUserPublicKey(params.trader_flo_address,
|
|
function(requester_public_key) {
|
|
if (requester_public_key==undefined||requester_public_key==null) {
|
|
throw new Error('Failed to get public key of the user.');
|
|
}
|
|
params.depositor_public_key = requester_public_key;
|
|
|
|
if (params.product == "BTC") {
|
|
/**************************************************************************
|
|
// YOU HAVE TO PROVIDE BTC KEYS HERE. CHANGE IT LATER
|
|
****************************************************************************/
|
|
let generate_btc_keys_for_requester = localbitcoinplusplus.wallets.prototype
|
|
.generateFloKeys.call();
|
|
|
|
/************************************************************************************
|
|
// Need to do: Super Node saves the private keys and sends the BTC address to the requester
|
|
// THIS IS VERY DANGEROUS STEP BCOZ BTC PRIVATE KEY DATA CAN BE LEAKED HERE IF ANYTHING GOES WRONG
|
|
*****************************************************************************/
|
|
params.id = helper_functions.unique_id();
|
|
params.status = 1;
|
|
params.btc_address = generate_btc_keys_for_requester.address;
|
|
|
|
/***************************************************
|
|
GET EQUIVALENT BTC HERE IN TERMS OF ORDERED CASH I.E 10K, 50K...
|
|
******************************************************/
|
|
params.bitcoinToBePaid = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(
|
|
params.depositing_amount, params.currency);
|
|
|
|
let receivedTradeInfo = {...params};
|
|
|
|
if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=="undefined") {
|
|
throw new Error('Failed to determine Super node signing key.');
|
|
}
|
|
|
|
readDB("localbitcoinUser", "00-01").then(function (su_data) {
|
|
if (typeof su_data == "object" && typeof su_data.myLocalFLOPublicKey ==
|
|
"string" &&
|
|
su_data.myLocalFLOPublicKey.length > 0 &&
|
|
localbitcoinplusplus.master_configurations.supernodesPubKeys
|
|
.includes(su_data.myLocalFLOPublicKey)) {
|
|
|
|
let receivedTradeInfoHash = Crypto.SHA256(JSON.stringify(
|
|
receivedTradeInfo));
|
|
|
|
receivedTradeInfo["depositDataHash"] =
|
|
receivedTradeInfoHash;
|
|
receivedTradeInfo["order_validator_sign"] =
|
|
localbitcoinplusplus.wallets.prototype.sign(
|
|
receivedTradeInfoHash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY
|
|
);
|
|
receivedTradeInfo["order_validator_public_key"] =
|
|
su_data.myLocalFLOPublicKey;
|
|
|
|
try {
|
|
const this_btc_pvt_key = generate_btc_keys_for_requester.privateKeyWIF;
|
|
const this_btc_tx_key = Crypto.util.randomBytes(64);
|
|
const this_btc_pvt_key_shamirs_secret = localbitcoinplusplus.wallets.prototype.createShamirsSecretShares(this_btc_pvt_key, 10, 5);
|
|
if (typeof this_btc_pvt_key_shamirs_secret=="object" && this_btc_pvt_key_shamirs_secret.length>0) {
|
|
addDB("deposit", receivedTradeInfo);
|
|
|
|
// Send the address to the requester
|
|
let deposit_response_object = {
|
|
error: false,
|
|
method: "deposit_asset_request_response",
|
|
msg: `Please send the Bitcoin to ${generate_btc_keys_for_requester.address}.`,
|
|
data: receivedTradeInfo
|
|
};
|
|
let deposit_request_response = localbitcoinplusplus
|
|
.rpc.prototype.send_rpc
|
|
.call(this,
|
|
"deposit_asset_request_response",
|
|
deposit_response_object);
|
|
doSend(deposit_request_response);
|
|
|
|
let this_btc_pvt_key_shamirs_secret_array = this_btc_pvt_key_shamirs_secret.map(chunks=>{
|
|
let chunk_ids = Crypto.util.bytesToHex(Crypto.util.randomBytes(64));
|
|
let chunk_array = {
|
|
"id": chunk_ids,
|
|
"privateKeyChunks": Crypto.AES.encrypt(chunks, this_btc_tx_key)
|
|
};
|
|
return chunk_array;
|
|
});
|
|
|
|
// Send chunks of private keys to other supernodes
|
|
this_btc_pvt_key_shamirs_secret_array.map(shares=>{
|
|
let store_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "store_shamirs_secret_pvtkey_shares", shares);
|
|
doSend(store_pvtkey_req);
|
|
}
|
|
);
|
|
|
|
if (typeof localbitcoinplusplus.wallets.my_local_flo_address == "string"
|
|
&& typeof localbitcoinplusplus.wallets.my_local_flo_public_key == "string"
|
|
&& typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object"
|
|
&& localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) {
|
|
try {
|
|
let this_btc_pvt_key_shamirs_secret__id_array = this_btc_pvt_key_shamirs_secret_array.map(i=>i.id);
|
|
let btc_private_key_shamirs_id = this_btc_pvt_key_shamirs_secret__id_array;
|
|
let supernode_transaction_key = this_btc_tx_key;
|
|
const system_btc_reserves_private_keys_object = {
|
|
id: helper_functions.unique_id(),
|
|
btc_address: params.btc_address,
|
|
balance: null,
|
|
trader_flo_address: params.trader_flo_address,
|
|
btc_private_key_shamirs_id: btc_private_key_shamirs_id,
|
|
supernode_transaction_key: supernode_transaction_key
|
|
}
|
|
addDB("system_btc_reserves_private_keys", system_btc_reserves_private_keys_object);
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
// Send the address to the requester
|
|
let deposit_response_object = {
|
|
error: false,
|
|
method: "deposit_asset_request_response",
|
|
msg: `Please send the Bitcoin to ${generate_btc_keys_for_requester.address}.`,
|
|
data: receivedTradeInfo
|
|
};
|
|
|
|
let deposit_request_response = localbitcoinplusplus
|
|
.rpc.prototype.send_rpc
|
|
.call(this,
|
|
"deposit_asset_request_response",
|
|
deposit_response_object);
|
|
doSend(deposit_request_response);
|
|
return true;
|
|
}
|
|
});
|
|
|
|
return false;
|
|
|
|
} else if (!localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)) {
|
|
params.id = helper_functions.unique_id();
|
|
params.status = 1;
|
|
let receivedTradeInfo = { ...params };
|
|
|
|
readDB("localbitcoinUser", "00-01").then(function (su_data) {
|
|
if (typeof su_data == "object" && typeof su_data.myLocalFLOPublicKey ==
|
|
"string" &&
|
|
su_data.myLocalFLOPublicKey.length > 0 &&
|
|
localbitcoinplusplus.master_configurations.supernodesPubKeys
|
|
.includes(su_data.myLocalFLOPublicKey)) {
|
|
let receivedTradeInfoHash = Crypto.SHA256(JSON.stringify(receivedTradeInfo));
|
|
|
|
receivedTradeInfo["depositDataHash"] = receivedTradeInfoHash;
|
|
receivedTradeInfo["order_validator_sign"] = localbitcoinplusplus.wallets.prototype.sign(
|
|
receivedTradeInfoHash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY
|
|
);
|
|
receivedTradeInfo["order_validator_public_key"] = su_data.myLocalFLOPublicKey;
|
|
|
|
// YOU NEED TO DETERMINE A BANK ACCOUNT HERE IF NO ONE IS WITHDRAWING
|
|
try {
|
|
addDB("deposit", receivedTradeInfo);
|
|
readDBbyIndex("withdraw_cash", "status", 1).then(
|
|
function (
|
|
withdrawers_list) {
|
|
if (typeof withdrawers_list == "object") {
|
|
if (withdrawers_list.length > 0) {
|
|
withdrawers_list.filter(wd=>wd.product == params.currency).map(
|
|
function (withdrawer) {
|
|
if (withdrawer.withdraw_amount == params.depositing_amount
|
|
&& withdrawer.product == params.currency
|
|
) {
|
|
withdrawer.status = 2; // A depositor has been asked to deposit money
|
|
withdrawer.depositor_found_at = + new Date();
|
|
withdrawer.depositor_flo_id = receivedTradeInfo.trader_flo_address;
|
|
updateinDB("withdraw_cash", withdrawer, withdrawer.trader_flo_address);
|
|
|
|
receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account
|
|
updateinDB("deposit", receivedTradeInfo, receivedTradeInfo.trader_flo_address);
|
|
|
|
let withdrawer_bank_account = withdrawer.receivinAddress;
|
|
|
|
let deposit_response_object = {
|
|
error: false,
|
|
method: "deposit_asset_request_response",
|
|
msg: `Plese send the money to following bank address: "${withdrawer_bank_account}"`,
|
|
data: receivedTradeInfo,
|
|
withdrawer_data: withdrawer
|
|
};
|
|
let deposit_request_response =
|
|
localbitcoinplusplus
|
|
.rpc
|
|
.prototype
|
|
.send_rpc
|
|
.call(
|
|
this,
|
|
"deposit_asset_request_response",
|
|
deposit_response_object
|
|
);
|
|
|
|
doSend(deposit_request_response);
|
|
return true;
|
|
} else {
|
|
console.warning("Deposit request failed: We could not find a withdrawer.");
|
|
}
|
|
});
|
|
} else {
|
|
//No one is withdrawing so provide your bank details
|
|
let deposit_response_object = {
|
|
error: false,
|
|
method: "deposit_asset_request_response",
|
|
msg: `Plese send the money to following bank address: "System determined bank".`,
|
|
data: receivedTradeInfo
|
|
};
|
|
let deposit_request_response =
|
|
localbitcoinplusplus.rpc
|
|
.prototype.send_rpc
|
|
.call(this,
|
|
"deposit_asset_request_response",
|
|
deposit_response_object
|
|
);
|
|
|
|
receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account
|
|
updateinDB("deposit", receivedTradeInfo, receivedTradeInfo.trader_flo_address);
|
|
|
|
doSend(deposit_request_response);
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Deposit request failed: We could not find a withdrawer. Come again later.");
|
|
throw new Error(error);
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
} else {
|
|
console.log("deposit asset request error");
|
|
}
|
|
|
|
});
|
|
break;
|
|
case "withdraw_request_method":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
|
|
if (is_valid_request !== true) return false;
|
|
|
|
if (typeof params.product !== "undefined"
|
|
&& (localbitcoinplusplus.master_configurations.tradableAsset1.include(params.product)
|
|
|| localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product)) &&
|
|
typeof params.withdrawing_amount !== "undefined" &&
|
|
typeof localbitcoinplusplus.master_configurations.validTradingAmount !==
|
|
'undefined' &&
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.includes(
|
|
parseFloat(params.withdrawing_amount)) &&
|
|
typeof params.trader_flo_address == "string" && params.trader_flo_address
|
|
.length > 0 &&
|
|
typeof params.receivinAddress == "string" && params.receivinAddress.length >
|
|
0 && typeof params.currency !== "undefined"
|
|
) {
|
|
params.id = helper_functions.unique_id();
|
|
params.status = 1;
|
|
if (params.product == "BTC") {
|
|
// Check how much Bitcoins the user can withdraw
|
|
readDB("btc_balances", params.trader_flo_address).then(function (
|
|
btc_balance_res) {
|
|
if (typeof btc_balance_res == "object" && typeof btc_balance_res
|
|
.trader_flo_address == "string" &&
|
|
typeof btc_balance_res.btc_balance == "number" &&
|
|
btc_balance_res.btc_balance > 0) {
|
|
let withdrawer_btc_balance = parseFloat(btc_balance_res.btc_balance);
|
|
let withdrawing_btc_amount_in_cash = parseFloat(params.withdrawing_amount);
|
|
if(!localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)) {
|
|
params.currency = "USD";
|
|
}
|
|
let eqBTC = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(
|
|
withdrawing_btc_amount_in_cash, params.currency);
|
|
eqBTC = parseFloat(eqBTC).toFixed(8);
|
|
let withdrawer_new_btc_balance = withdrawer_btc_balance - eqBTC;
|
|
if (withdrawer_btc_balance > 0 &&
|
|
withdrawing_btc_amount_in_cash > 0 &&
|
|
eqBTC > 0 && eqBTC <= withdrawer_btc_balance) {
|
|
|
|
// Now details of Bitcoins can be sent to withdrawer
|
|
|
|
/****************************************************************************
|
|
***********IMPORTANT: CHANGE RECEIVING ADDRESS TO BTC THAN FLO HERE**********
|
|
***********AND DO SOMETHING ABOUT PRIVATE KEY BELOW**************************
|
|
****************************************************************************/
|
|
let sum_total_btc = 0;
|
|
let valid_utxo_list = [];
|
|
let receiverBTCAddress = params.receivinAddress
|
|
.trim();
|
|
|
|
readAllDB("deposit").then(function (deposit_list) {
|
|
if (typeof deposit_list == "object" &&
|
|
deposit_list.length > 0) {
|
|
deposit_list = deposit_list.filter(
|
|
deposits => deposits.status == 2
|
|
&& deposits.product == "BTC");
|
|
for (const dl in deposit_list) {
|
|
if (deposit_list.hasOwnProperty(dl)) {
|
|
const deposit_dl = deposit_list[dl];
|
|
sum_total_btc += parseFloat(deposit_dl.bitcoinToBePaid);
|
|
|
|
if (eqBTC <= sum_total_btc) {
|
|
valid_utxo_list.push(deposit_dl);
|
|
break;
|
|
} else {
|
|
valid_utxo_list.push(deposit_dl);
|
|
}
|
|
}
|
|
}
|
|
let valid_btc_list = valid_utxo_list.map(deposit_arr => {
|
|
deposit_arr.status = 3
|
|
updateinDB("deposit", deposit_arr, deposit_arr.trader_flo_address);
|
|
|
|
// save the address and id in a table
|
|
let withdraw_id = helper_functions.unique_id();
|
|
const withdraw_btc_order_object = {
|
|
id: withdraw_id,
|
|
trader_flo_address: params.trader_flo_address,
|
|
utxo_addr: deposit_arr.btc_address,
|
|
receiverBTCAddress: params.receivinAddress,
|
|
receiverBTCEquivalentInCash: withdrawing_btc_amount_in_cash,
|
|
change_adress:deposit_arr.btc_address,
|
|
timestamp: + new Date()
|
|
}
|
|
addDB('withdraw_btc', withdraw_btc_order_object);
|
|
return {withdraw_id:withdraw_id, deposited_btc_address:deposit_arr.btc_address};
|
|
});
|
|
|
|
// doSend btc_private_key_shamirs_id from system_btc_reserves_private_keys
|
|
valid_btc_list.map(vbl=>{
|
|
readDBbyIndex('system_btc_reserves_private_keys', 'btc_address', vbl.deposited_btc_address).then(function(res) {
|
|
let retrieve_pvtkey_req_id = res[0].id;
|
|
res[0].btc_private_key_shamirs_id.map(bpks=>{
|
|
let retrieve_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "send_back_shamirs_secret_btc_pvtkey",
|
|
{ retrieve_pvtkey_req_id:retrieve_pvtkey_req_id,
|
|
chunk_val:bpks,
|
|
withdraw_id:vbl.withdraw_id
|
|
});
|
|
doSend(retrieve_pvtkey_req);
|
|
});
|
|
});
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
} else {
|
|
// Return error to the requester
|
|
return {
|
|
error: true,
|
|
method: "withdrawal_request_response",
|
|
data: "Withdrawal request failed: You are trying to withdraw more Bitcoins than you have."
|
|
};
|
|
}
|
|
} else {
|
|
// Return error to the requester
|
|
return {
|
|
error: true,
|
|
method: "withdrawal_request_response",
|
|
data: `Withdrawal request failed: You don't seem to have any Bitcoin balance in the system yet.
|
|
Please buy some Bitcoins to withdraw.`
|
|
};
|
|
}
|
|
});
|
|
} else if (!localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)) {
|
|
// Check if there's no already a withdraw cash order of this user
|
|
/*ONLY DELETE A WITHDRAW ORDER WHEN A DEPOSITOR HAS CONFIRMED DEPOSIT
|
|
AND RECEIVER HAS CONFIRMED WITHDRAW*/
|
|
|
|
// Check how much Cash user can withdraw
|
|
readDB("cash_balances", params.trader_flo_address).then(function (
|
|
cash_balances_res) {
|
|
if (typeof cash_balances_res == "object" && typeof cash_balances_res
|
|
.trader_flo_address == "string" &&
|
|
typeof cash_balances_res.cash_balance == "number" &&
|
|
cash_balances_res.cash_balance > 0) {
|
|
let withdrawer_cash_balance = parseFloat(cash_balances_res.cash_balance);
|
|
let withdrawing_cash_amount = parseFloat(params.withdrawing_amount);
|
|
let bank_details = params.receivinAddress.trim();
|
|
|
|
if (withdrawer_cash_balance > 0 &&
|
|
withdrawing_cash_amount > 0 &&
|
|
withdrawer_cash_balance >=
|
|
withdrawing_cash_amount) {
|
|
// Add it to cash withdrawal table
|
|
let withdraw_request_db_object = {
|
|
id: helper_functions.unique_id(),
|
|
trader_flo_address: params.trader_flo_address,
|
|
withdraw_amount: withdrawing_cash_amount,
|
|
receivinAddress: bank_details,
|
|
status: 1 // withdraw request called
|
|
}
|
|
|
|
readDB("localbitcoinUser", "00-01").then(function (
|
|
su_data) {
|
|
if (typeof su_data == "object" &&
|
|
typeof su_data.myLocalFLOPublicKey ==
|
|
"string" &&
|
|
su_data.myLocalFLOPublicKey.length >
|
|
0 && localbitcoinplusplus.master_configurations
|
|
.supernodesPubKeys.includes(
|
|
su_data.myLocalFLOPublicKey
|
|
)) {
|
|
|
|
let
|
|
withdraw_request_db_object_hash =
|
|
Crypto.SHA256(JSON.stringify(
|
|
withdraw_request_db_object
|
|
));
|
|
withdraw_request_db_object[
|
|
"withdrawDataHash"] =
|
|
withdraw_request_db_object_hash;
|
|
withdraw_request_db_object[
|
|
"order_validator_sign"] =
|
|
localbitcoinplusplus.wallets
|
|
.prototype
|
|
.sign(
|
|
withdraw_request_db_object_hash,
|
|
localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY
|
|
);
|
|
withdraw_request_db_object[
|
|
"order_validator_public_key"
|
|
] = su_data.myLocalFLOPublicKey;
|
|
|
|
try {
|
|
// add the request to supernode db
|
|
addDB("withdraw_cash",
|
|
withdraw_request_db_object
|
|
);
|
|
// return back the response to client
|
|
let
|
|
withdrawal_request_response =
|
|
localbitcoinplusplus.rpc
|
|
.prototype.send_rpc
|
|
.call(this,
|
|
"withdrawal_request_response",
|
|
withdraw_request_db_object
|
|
);
|
|
doSend(withdrawal_request_response);
|
|
return true;
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
});
|
|
|
|
} else {
|
|
// Return error to the requester
|
|
console.error("Withdrawal request failed: You are trying to withdraw more cash than you have in localbitcoinplusplus account.");
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
console.log("withdraw request error");
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "retrieve_shamirs_secret_btc_pvtkey":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request !== true) return false;
|
|
if (typeof params.btc_private_key_array !== "string" || typeof params.retrieve_pvtkey_req_id !== "number") return false;
|
|
|
|
let btc_private_key_str = params.btc_private_key_array;
|
|
let retrieve_pvtkey_req_id = params.retrieve_pvtkey_req_id;
|
|
let withdraw_id = params.withdraw_id;
|
|
|
|
try {
|
|
let btc_private_key_object = JSON.parse(btc_private_key_str);
|
|
let btc_pk_shares_array = btc_private_key_object.map(pkChunks=>{
|
|
if(typeof pkChunks.private_key_chunk !== "undefined") return pkChunks.private_key_chunk.privateKeyChunks;
|
|
}).filter(val => val !== undefined);
|
|
|
|
readDB('withdraw_btc', withdraw_id).then(function(withdraw_res) {
|
|
if (typeof withdraw_res == "object") {
|
|
readDB('system_btc_reserves_private_keys', retrieve_pvtkey_req_id).then(function(btc_reserves) {
|
|
if (typeof btc_reserves == "object") {
|
|
let transaction_key = btc_reserves.supernode_transaction_key;
|
|
if (transaction_key.length>0) {
|
|
let btc_private_key = localbitcoinplusplus.wallets.prototype.rebuild_private_key(btc_pk_shares_array, transaction_key);
|
|
localbitcoinplusplus.trade.prototype.sendTransaction(withdraw_res.utxo_addr, btc_private_key, withdraw_res.receiverBTCAddress,
|
|
withdraw_res.receiverBTCEquivalentInCash, withdraw_res.currency, withdraw_res.change_adress, function(res) {
|
|
console.log(res);
|
|
if (typeof res == "string" && res.length>0) {
|
|
try {
|
|
let msg = `Transaction Id for your withdrawn BTC: ${res.txid.result}`;
|
|
writeToScreen(msg);
|
|
alert(msg);
|
|
return true;
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
|
|
// Check if there's BTC left in deposited BTC. If yes update its status to 2 else delete it
|
|
|
|
/***********************************************************************************************************************************
|
|
*******************CHECK ACTUAL BTC BALANCE HERE THROUGH AN API AND UPDATE DEPOSIT TABLE****************************************************
|
|
************************************************************************************************************************************/
|
|
|
|
|
|
|
|
readDBbyIndex('deposit', 'btc_address', withdraw_res.utxo_addr).then(function(deposit_arr) {
|
|
if (typeof deposit_arr=="object") {
|
|
let eqBTC = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(withdraw_res.receiverBTCEquivalentInCash, withdraw_res.currency);
|
|
eqBTC = parseFloat(eqBTC);
|
|
deposit_arr.bitcoinToBePaid -= eqBTC;
|
|
|
|
if (deposit_arr.bitcoinToBePaid > 0) {
|
|
// update deposits in db
|
|
deposit_arr.status = 2; // UTXO ready to be used again
|
|
updateinDB(
|
|
"deposit",
|
|
deposit_arr,
|
|
deposit_arr
|
|
.trader_flo_address
|
|
);
|
|
|
|
} else {
|
|
// delete entry in deposits in db
|
|
removeinDB
|
|
(
|
|
"deposit",
|
|
deposit_arr
|
|
.trader_flo_address
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
});
|
|
break;
|
|
|
|
case "superNodeSignedAddUserPublicData":
|
|
if (typeof params=="object" && typeof params.data=="object") {
|
|
if (typeof params.su_pubKey=="string" && localbitcoinplusplus
|
|
.master_configurations.supernodesPubKeys.includes(params.su_pubKey)) {
|
|
let res_data_obj = {
|
|
trader_flo_address: params.data.trader_flo_address,
|
|
trader_flo_pubKey: params.data.trader_flo_pubKey,
|
|
trader_status: params.data.trader_status,
|
|
timestamp: params.data.timestamp
|
|
};
|
|
let res_data_hash = Crypto.SHA256(JSON.stringify(res_data_obj));
|
|
let res_data_verification = localbitcoinplusplus.wallets.prototype
|
|
.verify(res_data_hash, params.sign, params.su_pubKey);
|
|
if ((res_data_verification==true) && res_data_hash==params.data_hash) {
|
|
addDB('userPublicData', params.data);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
alert("Unknown method called for execution.");
|
|
break;
|
|
}
|
|
}
|
|
return request.toString(); // return to client
|
|
},
|
|
|
|
parse_server_rpc_response(request) {
|
|
var request = JSON_RPC.parse(request);
|
|
var response;
|
|
if (request.id == this.rpc_req_id) { // the request ID is maintained
|
|
response = request.response; // if successful
|
|
}
|
|
console.log("res:", response);
|
|
return response;
|
|
}
|
|
|
|
}
|
|
</script>
|
|
|
|
<!-- Trade Object -->
|
|
<script>
|
|
/*Initiate trade level with 0*/
|
|
var Trade = localbitcoinplusplus.trade = function processTrade() {
|
|
this.errors = [];
|
|
this.level = 0; // default
|
|
this.order_type = null;
|
|
this.valid_order_type = ["buy", "sell"];
|
|
this.product = null;
|
|
this.currency = null;
|
|
this.buy_price = null;
|
|
this.buyer_public_key = null;
|
|
this.buyer_key_signature = null;
|
|
this.order_validator_public_key = null;
|
|
this.rpc_job = null;
|
|
this.floAddress = null;
|
|
this.user_flo_address = null;
|
|
this.sorted_trade_list = [];
|
|
const DEFAULT_FIAT_CURRENCY = "USD";
|
|
|
|
Object.defineProperty(localbitcoinplusplus.trade, 'user_preffered_currency', {
|
|
get: function() {
|
|
return typeof localbitcoinplusplus.wallets.my_preferred_trade_currency !== "undefined"
|
|
? localbitcoinplusplus.wallets.my_preferred_trade_currency:DEFAULT_FIAT_CURRENCY;
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
Trade.prototype = {
|
|
|
|
getTrustLevel(){return this.level},
|
|
|
|
setTrustLevel(level) {
|
|
if (typeof level === "number" && level === parseInt(level, 10) && level > 0 && level < 6) {
|
|
this.level = level;
|
|
}
|
|
},
|
|
|
|
validate_order(order_type, user_flo_address, product, currency, buy_price) {
|
|
|
|
if (this.valid_order_type.indexOf(order_type) >= 0) {
|
|
this.order_type = order_type;
|
|
} else {
|
|
this.errors.push("Inavlid trade type value.");
|
|
}
|
|
if (user_flo_address.trim() !== "") {
|
|
this.user_flo_address = user_flo_address;
|
|
} else {
|
|
this.errors.push("No receiving BTC or Bank address provided.");
|
|
}
|
|
if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.tradableAsset1.indexOf(product) >= 0) {
|
|
this.product = product;
|
|
} else {
|
|
this.errors.push("Invalid trading product.");
|
|
}
|
|
if (typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.tradableAsset2.indexOf(currency) >= 0) {
|
|
this.currency = currency;
|
|
} else {
|
|
this.errors.push("Invalid exchanging currency or asset.");
|
|
}
|
|
if (localbitcoinplusplus.master_configurations.validTradingAmount.includes(buy_price)) {
|
|
this.buy_price = buy_price;
|
|
} else {
|
|
this.errors.push("Invalid buying price. Please place a valid buy amount.");
|
|
}
|
|
|
|
if (this.errors.length > 0) {
|
|
return this.errors;
|
|
}
|
|
return true;
|
|
},
|
|
place_order(order_type, user_flo_address, product, currency, buy_price) {
|
|
var is_valid_order = this.validate_order(order_type, user_flo_address, product,
|
|
currency, buy_price);
|
|
if (is_valid_order === true) {
|
|
|
|
this.rpc_job = 'trade_' + this.order_type;
|
|
|
|
let newOrderDataObj = {
|
|
"order_type": this.order_type,
|
|
"trader_flo_address": this.user_flo_address,
|
|
"product": this.product,
|
|
"currency": this.currency,
|
|
"buy_price": this.buy_price
|
|
}
|
|
|
|
let placeNewOrder = localbitcoinplusplus.rpc.prototype.send_rpc.call(this, this
|
|
.rpc_job, newOrderDataObj);
|
|
return placeNewOrder;
|
|
|
|
} else if (typeof is_valid_order == "object") {
|
|
var err;
|
|
for (err = 0; err < is_valid_order.length; err++) {
|
|
alert(is_valid_order[err]);
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
trade_buy(params, callback) {
|
|
|
|
for (var key in params) {
|
|
if (params.hasOwnProperty(key)) {
|
|
if (typeof key == undefined || key.trim() == "" || key == null) {
|
|
throw new Error("Incomplete or invalid request!");
|
|
}
|
|
}
|
|
}
|
|
if (params.order_type != "buy" || !localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)
|
|
|| !localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)
|
|
|| params.currency==params.product) {
|
|
throw new Error("Invalid buy request.");
|
|
}
|
|
|
|
//Check buyer's fiat balance
|
|
readDB("cash_balances", params.trader_flo_address).then(function (res) {
|
|
if (typeof res !== "undefined" && typeof res.cash_balance == "number" && !isNaN(res.cash_balance)) {
|
|
let buyer_cash_balance = parseFloat(res.cash_balance);
|
|
let buy_price_btc = parseFloat(params.buy_price);
|
|
if (buyer_cash_balance < buy_price_btc) {
|
|
throw new Error("Insufficient balance.");
|
|
}
|
|
// calculate equivalent BTC for x amount of Cash
|
|
let eqBTC = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(buy_price_btc, params.currency);
|
|
eqBTC = parseFloat(eqBTC);
|
|
if (typeof eqBTC == "number" && eqBTC > 0) {
|
|
|
|
let res_btc;
|
|
|
|
// supernode data query
|
|
readDB('localbitcoinUser', '00-01').then(function (user_data) {
|
|
if (typeof user_data == "object" && typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY ==
|
|
"string" && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length >
|
|
0) {
|
|
try {
|
|
// Add buy oder
|
|
params['id'] = helper_functions.unique_id();
|
|
|
|
let hashed_data = Crypto.SHA256(JSON.stringify(params));
|
|
// Signing of the data by Supernode
|
|
let signed_data = localbitcoinplusplus.wallets.prototype
|
|
.sign(hashed_data, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
|
|
params["data_hash"] = hashed_data;
|
|
params["supernode_sign"] = signed_data;
|
|
params["supernodePubKey"] = user_data.myLocalFLOPublicKey;
|
|
params["status"] = 1;
|
|
|
|
addDB("buyOrders", params);
|
|
|
|
// Send data for further action
|
|
callback(params);
|
|
} catch (error) {
|
|
console.error(error);
|
|
callback(false);
|
|
}
|
|
}
|
|
});
|
|
callback(false);
|
|
} else {
|
|
throw new Error("Failed to fetch current BTC price.");
|
|
}
|
|
}
|
|
});
|
|
},
|
|
trade_sell(params, callback) {
|
|
for (var key in params) {
|
|
if (params.hasOwnProperty(key)) {
|
|
if (typeof key == "undefined" || key.trim() == "" || key == null) {
|
|
throw new Error("Incomplete or invalid request!");
|
|
}
|
|
}
|
|
}
|
|
if (params.order_type != "sell"
|
|
|| !localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)
|
|
|| !localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)
|
|
|| params.currency==params.product) {
|
|
throw new Error("Invalid sell request.");
|
|
}
|
|
|
|
// Check BTC balance of the seller
|
|
readDB("btc_balances", params.trader_flo_address).then(function (res) {
|
|
if (typeof res !== "undefined" && typeof res.trader_flo_address == "string" && res.trader_flo_address
|
|
.length > 0 &&
|
|
typeof res.btc_balance == "number" && res.btc_balance > 0) {
|
|
let seller_btc_balance = parseFloat(res.btc_balance);
|
|
let sell_price_in_inr = parseFloat(params.buy_price);
|
|
let eqBTC = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(sell_price_in_inr, params.currency);
|
|
eqBTC = parseFloat(eqBTC);
|
|
if (typeof eqBTC == "number" && eqBTC > 0) {
|
|
|
|
if (seller_btc_balance < eqBTC) {
|
|
throw new Error("Insufficient BTC balance.");
|
|
}
|
|
|
|
// supernode data query
|
|
readDB('localbitcoinUser', '00-01').then(function (user_data) {
|
|
if (typeof user_data == "object" && typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY ==
|
|
"string" && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length > 0) {
|
|
|
|
//Add cash balance
|
|
params['id'] = helper_functions.unique_id();
|
|
|
|
let hashed_data = Crypto.SHA256(JSON.stringify(params));
|
|
// Signing of the data by Supernode
|
|
let signed_data = localbitcoinplusplus.wallets.prototype
|
|
.sign(hashed_data, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
|
|
params["data_hash"] = hashed_data;
|
|
params["supernode_sign"] = signed_data;
|
|
params["supernodePubKey"] = user_data.myLocalFLOPublicKey;
|
|
params["status"] = 1;
|
|
|
|
addDB("sellOrders", params);
|
|
|
|
callback(params);
|
|
}
|
|
});
|
|
callback(false);
|
|
} else {
|
|
throw new Error("Failed to fetch cuurent BTC price.");
|
|
}
|
|
} else {
|
|
throw new Error("Failed to read BTC balance from DB.");
|
|
}
|
|
});
|
|
},
|
|
depositAsset(assetType, amount, userFLOaddress) {
|
|
if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 == 'undefined' ||
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset2 == 'undefined' ||
|
|
(!localbitcoinplusplus.master_configurations.tradableAsset1.includes(assetType) &&
|
|
!localbitcoinplusplus.master_configurations.tradableAsset2.includes(assetType))) {
|
|
throw new Error("Invalid asset error");
|
|
} else if (parseFloat(amount) <= 0) {
|
|
throw new Error("Invalid amount error.");
|
|
} else if (userFLOaddress.length < 0) {
|
|
throw new Error("User address required.");
|
|
}
|
|
let currency = localbitcoinplusplus.trade.user_preffered_currency;
|
|
let deposit_request_object = {
|
|
trader_flo_address: userFLOaddress,
|
|
depositing_amount: amount,
|
|
currency:currency,
|
|
depositor_key_signature: null,
|
|
depositor_public_key: null,
|
|
operation_type: "deposit",
|
|
order_validator_public_key: null,
|
|
product: assetType
|
|
}
|
|
|
|
let deposit_request = localbitcoinplusplus.rpc.prototype.send_rpc.call(this,
|
|
"deposit_asset_request", deposit_request_object);
|
|
doSend(deposit_request);
|
|
},
|
|
withdrawAsset(assetType, amount, receivinAddress, userFLOaddress, callback) {
|
|
if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 == 'undefined' ||
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset2 == 'undefined' ||
|
|
!localbitcoinplusplus.master_configurations.tradableAsset1.includes(assetType) ||
|
|
!localbitcoinplusplus.master_configurations.tradableAsset2.includes(assetType)) {
|
|
throw new Error("Invalid asset error");
|
|
} else if (parseFloat(amount) <= 0) {
|
|
throw new Error("Invalid amount error.");
|
|
} else if (userFLOaddress.length < 0) {
|
|
throw new Error("User address required.");
|
|
} else if (receivinAddress.trim() == "") {
|
|
throw new Error("You must provide a vaid Bitcoin address to receive funds.");
|
|
}
|
|
|
|
let withdraw_request_object = {
|
|
trader_flo_address: userFLOaddress,
|
|
receivinAddress: receivinAddress,
|
|
withdrawing_amount: amount,
|
|
wihdrawer_key_signature: null,
|
|
wihdrawer_public_key: null,
|
|
operation_type: "withdraw",
|
|
order_validator_public_key: null,
|
|
product: assetType,
|
|
currency: localbitcoinplusplus.wallets.my_preferred_trade_currency
|
|
}
|
|
|
|
let withdraw_request = localbitcoinplusplus.rpc.prototype.send_rpc.call(this,
|
|
"withdraw_request_method", withdraw_request_object);
|
|
doSend(withdraw_request);
|
|
},
|
|
calculateBTCEquivalentOfCash(btc_buy_price, currency="USD") {
|
|
if (localbitcoinplusplus.master_configurations.validTradingAmount.includes(btc_buy_price)) {
|
|
if(!localbitcoinplusplus.master_configurations.tradableAsset2.includes(currency)) return false;
|
|
let current_btc_price = localbitcoinplusplus.trade.prototype.get_current_btc_price_in_fiat(currency);
|
|
if (typeof current_btc_price=="object" && current_btc_price.rate > 0) {
|
|
return parseFloat(btc_buy_price / current_btc_price.rate).toFixed(8);
|
|
}
|
|
}
|
|
throw new Error("Failed to calculate BTC equivalent of cash.");
|
|
},
|
|
get_current_btc_price_in_fiat(currency_code) {
|
|
return localbitcoinplusplus.trade[`current_btc_price_in_${currency_code}`];
|
|
},
|
|
async resolve_current_btc_price_in_fiat(currency_code) {
|
|
let today = + new Date();
|
|
let last_update_of_fiat_price_obj = localbitcoinplusplus.trade.prototype.get_current_btc_price_in_fiat(currency_code);
|
|
if(typeof last_update_of_fiat_price_obj!=="object"
|
|
|| (today-last_update_of_fiat_price_obj.timestamp>3600000)) {
|
|
last_update_of_fiat_price_obj = await localbitcoinplusplus.trade.prototype.set_current_btc_price_in_fiat(currency_code);
|
|
return last_update_of_fiat_price_obj;
|
|
} else {
|
|
return last_update_of_fiat_price_obj;
|
|
}
|
|
},
|
|
async set_current_btc_price_in_fiat(currency_code) {
|
|
const url = `https://api.coindesk.com/v1/bpi/currentprice/${currency_code}.json`;
|
|
const res = await helper_functions.ajaxGet(url);
|
|
if (typeof res == "object" && typeof res.bpi =="object") {
|
|
try {
|
|
let new_price = res.bpi[`${currency_code}`].rate_float;
|
|
await Object.defineProperty(localbitcoinplusplus.trade,
|
|
`current_btc_price_in_${currency_code}`, {
|
|
value: {rate:new_price,
|
|
timestamp: + new Date()},
|
|
writable: true,
|
|
configurable: false,
|
|
enumerable: true
|
|
});
|
|
return localbitcoinplusplus.trade[`current_btc_price_in_${currency_code}`];
|
|
} catch (error) {
|
|
console.error(error);
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
sendTransaction(utxo_addr, utxo_addr_wif, receiver_address, receiving_amount, receiving_amount_currency="USD", change_adress, callback) {
|
|
let url = `${localbitcoinplusplus.flocha}/api/addr/${utxo_addr}/utxo`;
|
|
helper_functions.ajaxGet(url).then(res=>{
|
|
|
|
if (res.length > 0) {
|
|
try {
|
|
if (!localbitcoinplusplus.master_configurations.validTradingAmount.includes(receiving_amount)) {
|
|
throw new Error('Invalid amount');
|
|
}
|
|
let btc_eq_receiving_amount = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(receiving_amount, receiving_amount_currency);
|
|
btc_eq_receiving_amount = parseFloat(btc_eq_receiving_amount).toFixed(8);
|
|
let utxo_list = JSON.parse(res);
|
|
let trx = bitjs.transaction();
|
|
let sum = 0;
|
|
|
|
for (var key in utxo_list) {
|
|
if (utxo_list[key].confirmations > 0) {
|
|
var obj = utxo_list[key];
|
|
sum += obj.satoshis;
|
|
|
|
if (btc_eq_receiving_amount <= sum) {
|
|
trx.addinput(obj.txid, obj.vout, obj.scriptPubKey);
|
|
break;
|
|
} else {
|
|
trx.addinput(obj.txid, obj.vout, obj.scriptPubKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
sum = parseFloat(sum / 100000000).toFixed(8);
|
|
|
|
let change_amount = sum - btc_eq_receiving_amount - 0.00006060;
|
|
|
|
trx.addoutput(receiver_address, btc_eq_receiving_amount);
|
|
trx.addoutput(change_adress, change_amount);
|
|
var sendFloData =
|
|
`localbitcoinpluslus tx: Send ${btc_eq_receiving_amount} satoshis to ${receiver_address}.`; //flochange adding place for flodata -- need a validation of 1024 chars
|
|
trx.addflodata(sendFloData); // flochange .. create this function
|
|
|
|
try {
|
|
console.log(trx);
|
|
|
|
let signedTxHash = trx.sign(utxo_addr_wif, 1); //SIGHASH_ALL DEFAULT 1
|
|
console.log(signedTxHash);
|
|
|
|
var http = new XMLHttpRequest();
|
|
var tx_send_url = `${localbitcoinplusplus.flocha}/api/tx/send`;
|
|
var params = `{"rawtx":"${signedTxHash}"}`;
|
|
http.open('POST', tx_send_url, true);
|
|
http.setRequestHeader('Content-type', 'application/json');
|
|
http.onreadystatechange = function () { //Call a function when the state changes.
|
|
if (http.readyState == 4 && http.status == 200) {
|
|
console.log(http.responseText);
|
|
callback(http.responseText);
|
|
}
|
|
}
|
|
http.send(params);
|
|
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
/*Finds the best buy sell id match for a trade*/
|
|
createTradePipes(trading_currency="USD") {
|
|
try {
|
|
readAllDB("sellOrders").then(function (sellOrdersList) {
|
|
if (sellOrdersList.length > 0) {
|
|
sellOrdersList = sellOrdersList.filter(sellOrder=>sellOrder.currency==trading_currency);
|
|
readAllDB("buyOrders").then(function (buyOrdersList) {
|
|
if (buyOrdersList.length > 0) {
|
|
buyOrdersList = buyOrdersList.filter(buyOrder=>buyOrder.currency==trading_currency);
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.map(
|
|
li => {
|
|
eval(
|
|
`buy${li} = buyOrdersList.filter(buyOrder=>buyOrder.buy_price==li)`
|
|
);
|
|
eval(
|
|
`sell${li} = sellOrdersList.filter(sellOrder=>sellOrder.buy_price==li)`
|
|
);
|
|
eval(
|
|
`buysell${li} = {"buy":buy${li}, "sell":sell${li}}`
|
|
);
|
|
eval(
|
|
`buysellArray${li} = Object.entries(buysell${li}).map(([key, value]) => ({key,value}))`
|
|
);
|
|
eval(`buyPipe = buysellArray${li}[0]`);
|
|
eval(`sellPipe = buysellArray${li}[1]`);
|
|
eval(
|
|
`n = buyPipe.value.length < sellPipe.value.length ? buyPipe.value.length : sellPipe.value.length`
|
|
);
|
|
|
|
if (buyPipe.value.length > 0 && sellPipe.value.length >
|
|
0) {
|
|
for (let i = 0; i < n; i++) {
|
|
localbitcoinplusplus.trade.prototype.launchTrade(
|
|
buyPipe.value[i], sellPipe.value[i],
|
|
function (supernode_res) {
|
|
if (typeof supernode_res == "object") {
|
|
let server_res =
|
|
localbitcoinplusplus.rpc
|
|
.prototype.send_rpc.call(
|
|
this,
|
|
"trade_balance_updates",
|
|
supernode_res);
|
|
doSend(server_res);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
},
|
|
launchTrade(buyPipeObj, sellPipeObj, callback) {
|
|
if (buyPipeObj.order_type == "buy" && sellPipeObj.order_type == "sell" &&
|
|
buyPipeObj.buy_price == sellPipeObj.buy_price
|
|
&& buyPipeObj.currency == sellPipeObj.currency
|
|
) {
|
|
// Check buyer's cash balance
|
|
readDB("cash_balances", buyPipeObj.trader_flo_address).then(function (buyPipeCashRes) {
|
|
if (typeof buyPipeCashRes == "object" && typeof buyPipeCashRes.cash_balance ==
|
|
"number") {
|
|
let buyer_cash_balance = parseFloat(buyPipeCashRes.cash_balance);
|
|
let buy_price_btc = parseFloat(buyPipeObj.buy_price);
|
|
if (buyer_cash_balance < buy_price_btc) {
|
|
throw new Error("Insufficient cash balance of buyer.");
|
|
}
|
|
// calculate equivalent BTC for x amount of Cash
|
|
let eqBTCBuyer = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(buy_price_btc, buyPipeObj.currency);
|
|
|
|
if (!isNaN(eqBTCBuyer) && eqBTCBuyer != "" && eqBTCBuyer != undefined) {
|
|
eqBTCBuyer = parseFloat(eqBTCBuyer);
|
|
}
|
|
|
|
// Check seller's BTC balance
|
|
readDB("btc_balances", sellPipeObj.trader_flo_address).then(function (sellPipeBTCRes) {
|
|
if (typeof sellPipeBTCRes == "object" && typeof sellPipeBTCRes.btc_balance ==
|
|
"number") {
|
|
let seller_btc_balance = parseFloat(sellPipeBTCRes.btc_balance)
|
|
.toFixed(8);
|
|
let sell_price_in_inr = parseFloat(sellPipeObj.buy_price);
|
|
let eqBTCSeller = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash(
|
|
sell_price_in_inr, buy_price_btc.currency);
|
|
if (!isNaN(eqBTCSeller) && eqBTCSeller != "" && eqBTCSeller !=
|
|
undefined) {
|
|
eqBTCSeller = parseFloat(eqBTCSeller);
|
|
if (seller_btc_balance < eqBTCSeller) {
|
|
throw new Error("Insufficient BTC balance of seller.");
|
|
}
|
|
|
|
// Increase buyer's BTC balance
|
|
let buyerBTCResponseObject;
|
|
readDB("btc_balances", buyPipeObj.trader_flo_address).then(
|
|
function (buyPipeBTCRes) {
|
|
if (typeof buyPipeBTCRes == "object" && typeof buyPipeBTCRes
|
|
.btc_balance == "number") {
|
|
buyPipeBTCRes.btc_balance = parseFloat(
|
|
buyPipeBTCRes.btc_balance) +
|
|
eqBTCBuyer;
|
|
buyerBTCResponseObject = buyPipeBTCRes;
|
|
} else {
|
|
// The user bought BTC for first time
|
|
buyerBTCResponseObject = {
|
|
trader_flo_address: buyPipeObj.trader_flo_address,
|
|
btc_balance: eqBTCBuyer
|
|
}
|
|
}
|
|
|
|
// Descrease buyer cash balance
|
|
let buyer_new_cash_balance = buyer_cash_balance -
|
|
buy_price_btc;
|
|
|
|
let buyerCashResponseObject = {
|
|
trader_flo_address: buyPipeObj.trader_flo_address,
|
|
cash_balance: buyer_new_cash_balance
|
|
}
|
|
|
|
// Increase seller's Cash balance
|
|
let sellerCashResponseObject;
|
|
readDB("cash_balances", sellPipeObj.trader_flo_address).then(
|
|
function (sellPipeCashRes) {
|
|
if (typeof sellPipeCashRes ==
|
|
"object" && typeof sellPipeCashRes
|
|
.cash_balance == "number" &&
|
|
!isNaN(sellPipeCashRes.cash_balance)
|
|
) {
|
|
sellPipeCashRes.cash_balance =
|
|
parseFloat(sellPipeCashRes.cash_balance) +
|
|
sell_price_in_inr;
|
|
sellerCashResponseObject =
|
|
sellPipeCashRes;
|
|
} else {
|
|
// User got cash for the first time
|
|
sellerCashResponseObject = {
|
|
trader_flo_address: sellPipeObj
|
|
.trader_flo_address,
|
|
cash_balance: sell_price_in_inr
|
|
}
|
|
}
|
|
|
|
// Decrease seller BTC balance
|
|
let new_seller_btc_balance =
|
|
seller_btc_balance -
|
|
eqBTCSeller;
|
|
let sellerBTCResponseObject = {
|
|
trader_flo_address: sellPipeObj
|
|
.trader_flo_address,
|
|
btc_balance: new_seller_btc_balance
|
|
}
|
|
|
|
// supernode data query
|
|
readDB('localbitcoinUser', '00-01').then(
|
|
function (user_data) {
|
|
if (typeof user_data ==
|
|
"object" && typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY ==
|
|
"string" &&
|
|
localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY
|
|
.length > 0) {
|
|
// Delete orders
|
|
try {
|
|
removeinDB(
|
|
"buyOrders",
|
|
buyPipeObj
|
|
.id);
|
|
removeinDB(
|
|
"sellOrders",
|
|
sellPipeObj
|
|
.id);
|
|
} catch (error) {
|
|
callback(false);
|
|
throw new Error(
|
|
error);
|
|
}
|
|
|
|
// Update balances
|
|
try {
|
|
updateinDB(
|
|
"cash_balances",
|
|
buyerCashResponseObject,
|
|
buyPipeObj
|
|
.trader_flo_address
|
|
);
|
|
updateinDB(
|
|
"cash_balances",
|
|
sellerCashResponseObject,
|
|
sellPipeObj
|
|
.trader_flo_address
|
|
);
|
|
updateinDB(
|
|
"btc_balances",
|
|
buyerBTCResponseObject,
|
|
buyPipeObj
|
|
.trader_flo_address
|
|
);
|
|
updateinDB(
|
|
"btc_balances",
|
|
sellerBTCResponseObject,
|
|
sellPipeObj
|
|
.trader_flo_address
|
|
);
|
|
} catch (error) {
|
|
callback(false);
|
|
throw new Error(
|
|
error);
|
|
}
|
|
|
|
// Prepare response
|
|
let trade_infos = {
|
|
"buy_order_id": buyPipeObj
|
|
.id,
|
|
"sell_order_id": sellPipeObj
|
|
.id,
|
|
"buyer_flo_id": buyPipeObj
|
|
.trader_flo_address,
|
|
"seller_flo_id": sellPipeObj
|
|
.trader_flo_address
|
|
}
|
|
|
|
let trade_infos_str =
|
|
JSON.stringify(
|
|
trade_infos
|
|
);
|
|
let
|
|
buyerCashResponseObjectStr =
|
|
JSON.stringify(
|
|
buyerCashResponseObject
|
|
);
|
|
let
|
|
sellerCashResponseObjectStr =
|
|
JSON.stringify(
|
|
sellerCashResponseObject
|
|
);
|
|
let
|
|
buyerBTCResponseObjectStr =
|
|
JSON.stringify(
|
|
buyerBTCResponseObject
|
|
);
|
|
let
|
|
sellerBTCResponseObjectStr =
|
|
JSON.stringify(
|
|
sellerBTCResponseObject
|
|
);
|
|
|
|
let res_str =
|
|
`${trade_infos_str}${buyerCashResponseObjectStr}${sellerCashResponseObjectStr}${buyerBTCResponseObjectStr}${sellerBTCResponseObjectStr}`;
|
|
|
|
let hashed_data =
|
|
Crypto.SHA256(
|
|
res_str);
|
|
|
|
// Signing of the data by Supernode
|
|
let signed_data =
|
|
localbitcoinplusplus
|
|
.wallets.prototype
|
|
.sign(
|
|
hashed_data,
|
|
localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY
|
|
);
|
|
|
|
let
|
|
response_for_client = {
|
|
"trade_infos": trade_infos,
|
|
"buyer_cash_data": buyerCashResponseObject,
|
|
"seller_cash_data": sellerCashResponseObject,
|
|
"buyer_btc_data": buyerBTCResponseObject,
|
|
"seller_btc_data": sellerBTCResponseObject,
|
|
"data_hash": hashed_data,
|
|
"supernode_sign": signed_data,
|
|
"supernodePubKey": user_data
|
|
.myLocalFLOPublicKey
|
|
}
|
|
callback(
|
|
response_for_client
|
|
);
|
|
return true;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
callback(false);
|
|
}
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<!-- Helper functions -->
|
|
<script>
|
|
let helper_functions = {
|
|
// AJAX Get
|
|
ajaxGet: async function (url) {
|
|
try {
|
|
//await the response of the fetch call
|
|
let response = await fetch(url);
|
|
//proceed once the first promise is resolved.
|
|
let data = await response.json()
|
|
//proceed only when the second promise is resolved
|
|
return data;
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
},
|
|
|
|
//AJAX Post
|
|
ajaxPost: function (url = ``, data = {}) {
|
|
return fetch(url, {
|
|
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
|
mode: "cors", // no-cors, cors, *same-origin
|
|
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
|
|
credentials: "same-origin", // include, *same-origin, omit
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
// "Content-Type": "application/x-www-form-urlencoded",
|
|
},
|
|
redirect: "follow", // manual, *follow, error
|
|
referrer: "no-referrer", // no-referrer, *client
|
|
body: JSON.stringify(data), // body data type must match "Content-Type" header
|
|
})
|
|
.then(response => response.json()); // parses response to JSON
|
|
},
|
|
|
|
// Create unique id
|
|
unique_id: function () {
|
|
return `${+new Date()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
},
|
|
|
|
}
|
|
</script>
|
|
|
|
<!-- JSON RPC Library Starts (https://github.com/oliver-moran/json-rpc) -->
|
|
<script language="javascript" type="text/javascript">
|
|
var JSON_RPC = {};
|
|
|
|
var id = 0,
|
|
callbacks = {};
|
|
|
|
/**
|
|
* Constructs a new JSON-RPC Request
|
|
* @param method A String containing the name of the method to be invoked.
|
|
* @param params (optional) A Structured value that holds the parameter values to be used during the invocation of the method.
|
|
*/
|
|
JSON_RPC.Request = function (method, params) {
|
|
this.jsonrpc = "2.0";
|
|
this.method = method;
|
|
if (typeof params !== "undefined") {
|
|
this.params = params;
|
|
}
|
|
|
|
this.id = id++;
|
|
};
|
|
|
|
// Implements getters and setters for the result of a JSON-RPC Request.
|
|
// The result may be an any Object or primitive
|
|
Object.defineProperty(JSON_RPC.Request.prototype, "result", {
|
|
get: function () {
|
|
return this._result;
|
|
},
|
|
set: function (result) {
|
|
delete this.method; // remove the method name
|
|
delete this.params; // remove the params
|
|
delete this.error; // remove error state if it exists
|
|
this._result = result;
|
|
}
|
|
});
|
|
|
|
// Implements getters and setters for the error state of a JSON-RPC Request.
|
|
// Error should be a JSON_RPC.Error object
|
|
Object.defineProperty(JSON_RPC.Request.prototype, "error", {
|
|
get: function () {
|
|
return this._error;
|
|
},
|
|
set: function (error) {
|
|
delete this.method; // remove the method name
|
|
delete this.params; // remove the params
|
|
delete this.result; // remove result state if it exists
|
|
this._error = error;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Returns a String representation of a JSON-RPC Request
|
|
* @returns A JSON String
|
|
*/
|
|
JSON_RPC.Request.prototype.toString = function () {
|
|
var rpc = {
|
|
jsonrpc: this.jsonrpc,
|
|
id: this.id
|
|
};
|
|
|
|
if (this.method !== undefined) rpc.method = this.method;
|
|
if (this.params !== undefined) rpc.params = this.params;
|
|
if (this.result !== undefined) rpc.result = this.result;
|
|
if (this.error !== undefined) rpc.error = this.error;
|
|
|
|
return JSON.stringify(rpc);
|
|
};
|
|
|
|
/**
|
|
* Constructs a new JSON-RPC Notification
|
|
* @param method A String containing the name of the method to be invoked.
|
|
* @param params (optional) A Structured value that holds the parameter values to be used during the invocation of the method.
|
|
*/
|
|
JSON_RPC.Notification = function (method, params) {
|
|
this.jsonrpc = "2.0";
|
|
this.method = method;
|
|
if (typeof params !== "undefined") {
|
|
this.params = params;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a String representation of a JSON-RPC Notification
|
|
* @returns A JSON String
|
|
*/
|
|
JSON_RPC.Notification.prototype.toString = function () {
|
|
var rpc = {
|
|
jsonrpc: this.jsonrpc,
|
|
method: this.method,
|
|
params: this.params
|
|
};
|
|
|
|
return JSON.stringify(rpc);
|
|
};
|
|
|
|
/**
|
|
* Constructs a new JSON-RPC Errror object
|
|
* @params code A Number that indicates the error type that occurred. -32768 to -32000 are reserved.
|
|
* @param message (optional) A String providing a short description of the error.
|
|
* @param data (optional) A Primitive or Structured value that contains additional information about the error.
|
|
*/
|
|
JSON_RPC.Error = function (code, message, data) {
|
|
this.code = code;
|
|
if (typeof message == "string") this.message = message;
|
|
if (data !== undefined) this.data = data;
|
|
};
|
|
|
|
// stock errors
|
|
JSON_RPC.PARSE_ERROR = new JSON_RPC.Error(-32700,
|
|
"An error occurred on the server while parsing the JSON text.");
|
|
JSON_RPC.INVALID_REQUEST = new JSON_RPC.Error(-32600, "The JSON sent is not a valid Request object.");
|
|
JSON_RPC.METHOD_NOT_FOUND = new JSON_RPC.Error(-32601, "The method does not exist / is not available.");
|
|
JSON_RPC.INVALID_PARAMS = new JSON_RPC.Error(-32602, "Invalid method parameter(s).");
|
|
JSON_RPC.INTERNAL_ERROR = new JSON_RPC.Error(-32603, "Internal JSON-RPC error.");
|
|
|
|
/**
|
|
* Parses a JSON-RPC string and converts to a JSON-RPC object or an Array of such strings.
|
|
* @params rpc A String or Array to parse to a JSON-RPC object.
|
|
*/
|
|
JSON_RPC.parse = function (rpc) {
|
|
// batch?
|
|
if (rpc.constructor === Array) {
|
|
var arr = [];
|
|
rpc.forEach(function (el) {
|
|
arr.push(JSON_RPC.parse(el));
|
|
});
|
|
return arr;
|
|
}
|
|
|
|
// parsable?
|
|
var rpc;
|
|
try {
|
|
rpc = JSON.parse(rpc);
|
|
} catch (err) {
|
|
var obj = new JSON_RPC.Request();
|
|
obj.result = JSON_RPC.PARSE_ERROR;
|
|
obj.id = null;
|
|
return obj;
|
|
}
|
|
|
|
// 2.0?
|
|
if (rpc.jsonrpc !== "2.0") {
|
|
var obj = new JSON_RPC.Request();
|
|
obj.result = JSON_RPC.INVALID_REQUEST;
|
|
obj.id = null;
|
|
return obj;
|
|
}
|
|
|
|
// request or notification?
|
|
var obj = (rpc.id === undefined) ?
|
|
new JSON_RPC.Notification(rpc.method, rpc.params) :
|
|
new JSON_RPC.Request(rpc.method, rpc.params);
|
|
// have an ID?
|
|
if (rpc.id !== undefined) obj.id = rpc.id;
|
|
// is it a result?
|
|
if (rpc.result !== undefined) obj.result = rpc.result;
|
|
// is it a error?
|
|
if (rpc.error !== undefined) {
|
|
obj.error = new JSON_RPC.Error(
|
|
rpc.error.code,
|
|
rpc.error.message,
|
|
rpc.error.data
|
|
);
|
|
}
|
|
|
|
// parsed :-)
|
|
return obj;
|
|
};
|
|
|
|
/* JSON RPC Library Ends */
|
|
|
|
/*******************************************************
|
|
Custom Localbitcoin++ JSON-RPC code starts here
|
|
*********************************************************/
|
|
// Start building all functions here
|
|
|
|
/* Custom JSON-RPC code ends */
|
|
|
|
/* Websocket Code Starts here */
|
|
|
|
//var wsUri = "ws://localhost:9000/";
|
|
var wsUri = "ws://ranchimall786.duckdns.org:9000/";
|
|
var output;
|
|
|
|
function init() {
|
|
output = document.getElementById("output_div");
|
|
testWebSocket();
|
|
}
|
|
|
|
function testWebSocket() {
|
|
websocket = new WebSocket(wsUri);
|
|
websocket.onopen = function (evt) {
|
|
onOpen(evt)
|
|
};
|
|
websocket.onclose = function (evt) {
|
|
onClose(evt)
|
|
};
|
|
websocket.onmessage = function (evt) {
|
|
onMessage(evt)
|
|
};
|
|
websocket.onerror = function (evt) {
|
|
onError(evt)
|
|
};
|
|
}
|
|
|
|
function onOpen(evt) {
|
|
writeToScreen("CONNECTED");
|
|
doSend("Intial Hello Message: WebSocket rocks");
|
|
}
|
|
|
|
function onClose(evt) {
|
|
writeToScreen("DISCONNECTED");
|
|
}
|
|
|
|
function onMessage(evt) {
|
|
var response = evt.data;
|
|
var res_pos = response.indexOf('{');
|
|
if (res_pos >= 0) {
|
|
var res = response.substr(res_pos);
|
|
try {
|
|
|
|
var res_obj = JSON.parse(res);
|
|
|
|
if (typeof res_obj.method !== undefined) {
|
|
let response_from_sever;
|
|
|
|
switch (res_obj.method) {
|
|
case "supernode_message":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let received_resp = res_obj.params[0];
|
|
try {
|
|
if (received_resp.trader_flo_id.length>0 && received_resp.server_msg.length>0) {
|
|
readDB("localbitcoinUser", "00-01").then(function(res) {
|
|
if (typeof res=="object" && res.myLocalFLOAddress.length>0) {
|
|
if (res.myLocalFLOAddress===received_resp.trader_flo_id) {
|
|
writeToScreen(received_resp.server_msg);
|
|
alert(received_resp.server_msg);
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
break;
|
|
case "trade_buy":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
break;
|
|
case "trade_buy_request_response":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let buyOrders_data = res_obj.params[0];
|
|
|
|
if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" &&
|
|
localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(
|
|
buyOrders_data.supernodePubKey)) {
|
|
let isDataSignedBySuperNode = localbitcoinplusplus.wallets.prototype
|
|
.verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign,
|
|
buyOrders_data.supernodePubKey);
|
|
if (isDataSignedBySuperNode === true) {
|
|
// Add buy order
|
|
addDB("buyOrders", buyOrders_data);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "trade_sell":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
break;
|
|
case "trade_sell_request_response":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let sellOrders_data = res_obj.params[0];
|
|
if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" &&
|
|
localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(
|
|
sellOrders_data
|
|
.supernodePubKey)) {
|
|
let isDataSignedBySuperNode = localbitcoinplusplus.wallets.prototype
|
|
.verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign,
|
|
sellOrders_data.supernodePubKey);
|
|
if (isDataSignedBySuperNode === true) {
|
|
// Add buy order
|
|
addDB("sellOrders", sellOrders_data);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "sync_with_supernode":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
break;
|
|
case "server_sync_response":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let su_db_data = res_obj.params[0];
|
|
if (typeof localbitcoinplusplus.wallets.my_local_flo_address!=="string"
|
|
|| su_db_data.trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return false;
|
|
|
|
(async function() {
|
|
for (let tableStoreName in su_db_data) {
|
|
// skip loop if the property is from prototype
|
|
if (tableStoreName=='trader_flo_address' || !su_db_data.hasOwnProperty(tableStoreName)) continue;
|
|
|
|
try {
|
|
let obj = su_db_data[tableStoreName];
|
|
if (["btc_balances", "cash_balances", "userPublicData"].includes(tableStoreName)) {
|
|
if (obj.length>0) {
|
|
for (var prop in obj) {
|
|
if(!obj.hasOwnProperty(prop)) continue;
|
|
await updateinDB(tableStoreName, obj[prop], obj[prop].trader_flo_address);
|
|
}
|
|
}
|
|
} else {
|
|
let resdbdata = await removeAllinDB(tableStoreName);
|
|
if (resdbdata!==false) {
|
|
if (obj.length>0) {
|
|
for (var prop in obj) {
|
|
if(!obj.hasOwnProperty(prop)) continue;
|
|
await addDB(resdbdata, obj[prop]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
})()
|
|
|
|
// Pass data to build_deposit_withdraw_table function
|
|
try {
|
|
localbitcoinplusplus.actions.build_deposit_withdraw_table(su_db_data.withdraw_cash);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
|
|
}
|
|
break;
|
|
case "deposit_asset_request":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
case "deposit_asset_request_response":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && typeof res_obj
|
|
.params[0].data == "object") {
|
|
let resp = res_obj.params[0];
|
|
if (localbitcoinplusplus.wallets.prototype
|
|
.verify(resp.data.depositDataHash, resp.data.order_validator_sign, resp.data.order_validator_public_key)
|
|
) {
|
|
addDB('deposit', resp.data);
|
|
if (typeof resp.withdrawer_data=="object") {
|
|
updateinDB("withdraw_cash", resp.withdrawer_data, resp.withdrawer_data.trader_flo_address);
|
|
}
|
|
readDB("localbitcoinUser", "00-01").then(function (user) {
|
|
if (typeof user == "object" && user.myLocalFLOAddress == resp.data.trader_flo_address) {
|
|
let counterTraderAccountAddress =
|
|
`<p><strong>Please pay the amount to following address:</strong></p>
|
|
<p>${resp.msg}</p>`;
|
|
let asset_boxx = document.getElementById("asset_box");
|
|
asset_boxx.insertAdjacentHTML('beforeend', counterTraderAccountAddress);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
case "withdraw_request_method":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
doSend(JSON.stringify(response_from_sever)); // send response to client
|
|
break;
|
|
case "withdrawal_request_response":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
if (localbitcoinplusplus.wallets.prototype
|
|
.verify(res_obj.params[0].withdrawDataHash, res_obj.params[0].order_validator_sign,
|
|
res_obj.params[0].order_validator_public_key)) {
|
|
addDB('withdraw_cash', res_obj.params[0]);
|
|
}
|
|
}
|
|
break;
|
|
case "trade_balance_updates":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
const trade_balance_res = res_obj.params[0];
|
|
// Verify data
|
|
let trade_info_str = JSON.stringify(trade_balance_res.trade_infos);
|
|
let buyer_cash_data_str = JSON.stringify(trade_balance_res.buyer_cash_data);
|
|
let seller_cash_data_str = JSON.stringify(trade_balance_res.seller_cash_data);
|
|
let buyer_btc_data_str = JSON.stringify(trade_balance_res.buyer_btc_data);
|
|
let seller_btc_data_str = JSON.stringify(trade_balance_res.seller_btc_data);
|
|
|
|
let res_str =
|
|
`${trade_info_str}${buyer_cash_data_str}${seller_cash_data_str}${buyer_btc_data_str}${seller_btc_data_str}`;
|
|
let hashed_data = Crypto.SHA256(res_str);
|
|
|
|
if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(
|
|
trade_balance_res.supernodePubKey)) {
|
|
if (localbitcoinplusplus.wallets.prototype.verify(hashed_data,
|
|
trade_balance_res.supernode_sign, trade_balance_res.supernodePubKey)) {
|
|
// Delete orders in clients DB
|
|
try {
|
|
removeinDB("buyOrders", trade_balance_res.trade_infos.buy_order_id);
|
|
removeinDB("sellOrders", trade_balance_res.trade_infos.sell_order_id);
|
|
} catch (error) {
|
|
callback(false);
|
|
throw new Error(error);
|
|
}
|
|
|
|
// Update balances in clients DB
|
|
try {
|
|
updateinDB("cash_balances", trade_balance_res.buyer_cash_data,
|
|
trade_balance_res.trade_infos.buyer_flo_id);
|
|
updateinDB("cash_balances", trade_balance_res.seller_cash_data,
|
|
trade_balance_res.trade_infos.seller_flo_id);
|
|
updateinDB("btc_balances", trade_balance_res.buyer_btc_data,
|
|
trade_balance_res.trade_infos.buyer_flo_id);
|
|
updateinDB("btc_balances", trade_balance_res.seller_btc_data,
|
|
trade_balance_res.trade_infos.seller_flo_id);
|
|
} catch (error) {
|
|
callback(false);
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "store_shamirs_secret_pvtkey_shares":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
addDB("supernode_private_key_chunks", res_obj.params[0]);
|
|
}
|
|
break;
|
|
case "send_back_shamirs_secret_supernode_pvtkey":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val).then(function(res) {
|
|
let send_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "retrieve_shamirs_secret_supernode_pvtkey",
|
|
{private_key_chunk:res});
|
|
doSend(send_pvtkey_req);
|
|
});
|
|
}
|
|
break;
|
|
case "retrieve_shamirs_secret_supernode_pvtkey":
|
|
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object"
|
|
&& typeof res_obj.params[0].private_key_chunk=="object"
|
|
&& typeof localbitcoinplusplus.wallets.supernode_transaction_key == "object") {
|
|
let share = res_obj.params[0].private_key_chunk.privateKeyChunks;
|
|
if (typeof share !== "undefined" && !MY_PRIVATE_KEY_SHAMIRS_SHARES.includes(share)) {
|
|
MY_PRIVATE_KEY_SHAMIRS_SHARES.push(share);
|
|
}
|
|
if (MY_PRIVATE_KEY_SHAMIRS_SHARES.length==5) {
|
|
localbitcoinplusplus.wallets.prototype.rebuild_my_private_key(localbitcoinplusplus.wallets.supernode_transaction_key);
|
|
}
|
|
}
|
|
break;
|
|
case "send_back_shamirs_secret_btc_pvtkey":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val).then(function(res) {
|
|
let send_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "retrieve_shamirs_secret_btc_pvtkey",
|
|
{
|
|
retrieve_pvtkey_req_id:res_obj.params[0].retrieve_pvtkey_req_id,
|
|
private_key_chunk:res,
|
|
withdraw_id:res_obj.params[0].withdraw_id
|
|
});
|
|
doSend(send_pvtkey_req);
|
|
});
|
|
}
|
|
break;
|
|
case "retrieve_shamirs_secret_btc_pvtkey":
|
|
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object"
|
|
&& typeof res_obj.params[0].private_key_chunk=="object"
|
|
&& typeof res_obj.params[0].retrieve_pvtkey_req_id=="string"
|
|
&& typeof res_obj.params[0].withdraw_id=="string") {
|
|
let shamirs_shares_response = res_obj.params[0];
|
|
let retrieve_pvtkey_req_id = res_obj.params[0].retrieve_pvtkey_req_id;
|
|
let withdraw_id = res_obj.params[0].withdraw_id;
|
|
eval(`if (typeof btc_pvt_arr${retrieve_pvtkey_req_id}=="undefined") btc_pvt_arr${retrieve_pvtkey_req_id} = [];
|
|
btc_pvt_arr${retrieve_pvtkey_req_id}.push(shamirs_shares_response);
|
|
if (btc_pvt_arr${retrieve_pvtkey_req_id}.length===localbitcoinplusplus.master_configurations.ShamirsMaxShares) {
|
|
delete res_obj.params[0].private_key_chunk;
|
|
res_obj.params[0].btc_private_key_array = JSON.stringify(btc_pvt_arr${retrieve_pvtkey_req_id});
|
|
localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this, JSON.stringify(res_obj));
|
|
}
|
|
`);
|
|
}
|
|
break;
|
|
case "deposit_withdraw_user_claim":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request !== true) return false;
|
|
if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=="undefined") throw new Error("Supernode Private Keys is undefind.");
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let user_claim_request = res_obj.params[0];
|
|
let user_claim_id = user_claim_request.claim_id.split('!!');
|
|
let withdraw_order_id = user_claim_id[0];
|
|
let user_id = user_claim_id[1];
|
|
|
|
let deposit_withdraw_user_claim_obj = {
|
|
claim_id:user_claim_request.claim_id
|
|
}
|
|
|
|
let deposit_withdraw_user_claim_str = JSON.stringify(deposit_withdraw_user_claim_obj);
|
|
let deposit_withdraw_user_claim_hash = Crypto.SHA256(deposit_withdraw_user_claim_str);
|
|
|
|
if (deposit_withdraw_user_claim_hash==user_claim_request.hash &&
|
|
localbitcoinplusplus.wallets.prototype.verify(deposit_withdraw_user_claim_hash,
|
|
user_claim_request.sign, user_claim_request.userPubKey)) {
|
|
//If the request is valid, find out if the requester is depositor or withdrawer
|
|
|
|
readDB("withdraw_cash", withdraw_order_id).then(function(withdraw_data) {
|
|
if (typeof withdraw_data=="object") {
|
|
if (withdraw_data.depositor_flo_id==user_id) {
|
|
// Depositor claimed to deposit the cash
|
|
withdraw_data.status = 3;
|
|
updateinDB('withdraw_cash', withdraw_data, withdraw_data.id);
|
|
let update_withdraw_cash_obj_data = {
|
|
depositor_claim:withdraw_data
|
|
};
|
|
let update_withdraw_cash_obj_data_str = JSON.stringify(update_withdraw_cash_obj_data);
|
|
let update_withdraw_cash_obj_data_hash = Crypto.SHA256(update_withdraw_cash_obj_data_str);
|
|
let update_withdraw_cash_obj_data_sign = localbitcoinplusplus.wallets.prototype
|
|
.sign(update_withdraw_cash_obj_data_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
update_withdraw_cash_obj_data.hash = update_withdraw_cash_obj_data_hash;
|
|
update_withdraw_cash_obj_data.sign = update_withdraw_cash_obj_data_sign;
|
|
update_withdraw_cash_obj_data.publicKey = localbitcoinplusplus.wallets.my_local_flo_public_key;
|
|
let update_withdraw_cash_obj = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "update_all_withdraw_cash_depositor_claim",
|
|
update_withdraw_cash_obj_data);
|
|
doSend(update_withdraw_cash_obj);
|
|
} else if (withdraw_data.trader_flo_address==user_id) {
|
|
// Withdrawer confirmed the payment
|
|
readDBbyIndex('cash_balances', 'trader_flo_address', withdraw_data.depositor_flo_id).then(function(depositor_cash_data) {
|
|
if (typeof depositor_cash_data=="object") {
|
|
if (depositor_cash_data.length==0) {
|
|
depositor_cash_data = {cash_balance:0, trader_flo_address:withdraw_data.depositor_flo_id, currency:localbitcoinplusplus.trade.user_preffered_currency};
|
|
addDB('cash_balances', depositor_cash_data);
|
|
}
|
|
depositor_cash_data.cash_balance += parseFloat(withdraw_data.withdraw_amount);
|
|
updateinDB('cash_balances', depositor_cash_data);
|
|
removeByIndex('deposit', 'trader_flo_address', depositor_cash_data.trader_flo_address);
|
|
removeinDB('withdraw_cash', withdraw_data.id);
|
|
|
|
let update_cash_balance_obj = {
|
|
depositor_cash_data:depositor_cash_data
|
|
}
|
|
let update_cash_balance_str = JSON.stringify(update_cash_balance_obj);
|
|
let update_cash_balance_hash = Crypto.SHA256(update_cash_balance_str);
|
|
let update_cash_balance_sign = localbitcoinplusplus.wallets.prototype
|
|
.sign(update_cash_balance_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
|
|
update_cash_balance_obj.publicKey = localbitcoinplusplus.wallets.my_local_flo_public_key;
|
|
update_cash_balance_obj.sign = update_cash_balance_sign;
|
|
update_cash_balance_obj.hash = update_cash_balance_hash;
|
|
update_cash_balance_obj.withdraw_id = withdraw_data.id;
|
|
|
|
let update_cash_balance_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "update_all_deposit_withdraw_success",
|
|
update_cash_balance_obj);
|
|
doSend(update_cash_balance_req);
|
|
}
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
break;
|
|
case "update_all_withdraw_cash_depositor_claim":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let depositor_claim_response_object = res_obj.params[0];
|
|
let update_withdraw_cash_obj_data_res = {
|
|
depositor_claim:depositor_claim_response_object.depositor_claim
|
|
};
|
|
let update_withdraw_cash_obj_data_res_str = JSON.stringify(update_withdraw_cash_obj_data_res);
|
|
let depositor_claim_response_data_hash = Crypto.SHA256(update_withdraw_cash_obj_data_res_str);
|
|
let depositor_claim_response_object_verification = localbitcoinplusplus.wallets.prototype
|
|
.verify(depositor_claim_response_data_hash, depositor_claim_response_object.sign, depositor_claim_response_object.publicKey);
|
|
|
|
if ((depositor_claim_response_data_hash==depositor_claim_response_object.hash) && (depositor_claim_response_object_verification==true)) {
|
|
updateinDB('withdraw_cash', depositor_claim_response_object.depositor_claim, depositor_claim_response_object.depositor_claim.id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
break;
|
|
case "update_all_deposit_withdraw_success":
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let withdraw_success_response = res_obj.params[0];
|
|
let update_cash_balance_obj_res = {
|
|
depositor_cash_data:withdraw_success_response.depositor_cash_data
|
|
}
|
|
let update_cash_balance_obj_res_str = JSON.stringify(update_cash_balance_obj_res);
|
|
let update_cash_balance_obj_res_hash = Crypto.SHA256(update_cash_balance_obj_res_str);
|
|
let update_cash_balance_obj_res_verification = localbitcoinplusplus.wallets.prototype
|
|
.verify(update_cash_balance_obj_res_hash, withdraw_success_response.sign, withdraw_success_response.publicKey);
|
|
|
|
if ((update_cash_balance_obj_res_hash==withdraw_success_response.hash) && update_cash_balance_obj_res_verification==true) {
|
|
updateinDB('cash_balances', withdraw_success_response.depositor_cash_data);
|
|
removeByIndex('deposit', 'trader_flo_address', withdraw_success_response.depositor_cash_data.trader_flo_address);
|
|
removeinDB('withdraw_cash', withdraw_success_response.withdraw_id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case "add_user_public_data":
|
|
localbitcoinplusplus.rpc.prototype.filter_legit_requests(function (is_valid_request) {
|
|
if (is_valid_request !== true) return false;
|
|
|
|
if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") {
|
|
let req_data = res_obj.params[0].public_data;
|
|
try {
|
|
let flo_address = bitjs.pubkey2address(req_data.trader_flo_pubKey);
|
|
|
|
if (flo_address==req_data.trader_flo_address && req_data.trader_flo_address.length>0) {
|
|
|
|
let public_req_object = {
|
|
trader_flo_address: req_data.trader_flo_address,
|
|
trader_flo_pubKey: req_data.trader_flo_pubKey,
|
|
trader_status: 0,
|
|
timestamp: + new Date()
|
|
}
|
|
|
|
addDB('userPublicData', public_req_object);
|
|
|
|
let public_req_object_str = JSON.stringify(public_req_object);
|
|
let public_req_object_hash = Crypto.SHA256(public_req_object_str);
|
|
let public_req_object_sign = localbitcoinplusplus.wallets.prototype.sign(public_req_object_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY);
|
|
|
|
let userPublicDataResponseObject = {
|
|
data: public_req_object,
|
|
data_hash: public_req_object_hash,
|
|
sign: public_req_object_sign,
|
|
su_pubKey: localbitcoinplusplus.wallets.my_local_flo_public_key
|
|
}
|
|
|
|
let send_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "superNodeSignedAddUserPublicData",
|
|
userPublicDataResponseObject);
|
|
|
|
doSend(send_pvtkey_req);
|
|
|
|
}
|
|
} catch (error) {
|
|
throw new Error('Invalid public key and flo address combination.');
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "superNodeSignedAddUserPublicData":
|
|
response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this,
|
|
JSON.stringify(res_obj));
|
|
doSend(JSON.stringify(response_from_sever)); // send response to client
|
|
break;
|
|
|
|
|
|
case "testMessageBroadcasting":
|
|
console.log(res_obj);
|
|
try {
|
|
let data = res_obj.params[0];
|
|
let msg = localbitcoinplusplus.encrypt.decryptMessage(data.secret, data.senderPublicKeyString);
|
|
console.log(msg);
|
|
|
|
} catch (error) {
|
|
console.error(error);
|
|
|
|
}
|
|
break;
|
|
|
|
case "MessageForMiddleman":
|
|
console.log(res_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
return;
|
|
}
|
|
}
|
|
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data + '</span>');
|
|
}
|
|
|
|
function onError(evt) {
|
|
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
|
|
}
|
|
|
|
function doSend(message) {
|
|
writeToScreen("SENT: " + message);
|
|
websocket.send(message);
|
|
}
|
|
|
|
function SendToSpecificAddress(to, message) {
|
|
writeToScreen("SENT: " + message);
|
|
websocket.send(JSON.stringify({
|
|
to: to,
|
|
data: message
|
|
}));
|
|
}
|
|
|
|
function writeToScreen(message) {
|
|
var pre = document.createElement("p");
|
|
pre.style.wordWrap = "break-word";
|
|
pre.innerHTML = message;
|
|
output.appendChild(pre);
|
|
}
|
|
|
|
window.addEventListener("load", init, false);
|
|
|
|
/* Websocket Code Ends Here*/
|
|
</script>
|
|
|
|
<!-- Indexed DB -->
|
|
<script>
|
|
//prefixes of implementation that we want to test
|
|
window.indexedDB = window.indexedDB || window.mozIndexedDB ||
|
|
window.webkitIndexedDB || window.msIndexedDB;
|
|
|
|
//prefixes of window.IDB objects
|
|
window.IDBTransaction = window.IDBTransaction ||
|
|
window.webkitIDBTransaction || window.msIDBTransaction;
|
|
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange ||
|
|
window.msIDBKeyRange
|
|
|
|
if (!window.indexedDB) {
|
|
window.alert("Your browser doesn't support a stable version of IndexedDB.")
|
|
}
|
|
|
|
let localbitcoinplusplusObj = {
|
|
id: "00-01",
|
|
myLocalFLOAddress: "",
|
|
myLocalFLOPublicKey: "",
|
|
myAddressTrustLevel: 1,
|
|
preferredTradeCurrency: "USD"
|
|
};
|
|
|
|
const userPublicData = {
|
|
trader_flo_address: null,
|
|
trader_flo_pubKey: null,
|
|
trader_status: null,
|
|
timestamp: null
|
|
};
|
|
|
|
const deposit = {
|
|
id: "",
|
|
trader_flo_address: null,
|
|
depositing_amount: 0,
|
|
depositor_key_signature: null,
|
|
depositor_public_key: null,
|
|
operation_type: null,
|
|
order_validator_public_key: null,
|
|
product: null,
|
|
status: 0
|
|
}
|
|
|
|
const withdraw_cash = {
|
|
id: "",
|
|
trader_flo_address: null,
|
|
withdraw_amount: null,
|
|
currency: null,
|
|
receivinAddress: null,
|
|
status: null,
|
|
depositor_found_at: null
|
|
}
|
|
|
|
const btc_balances = {
|
|
trader_flo_address: null,
|
|
btc_balance: null
|
|
}
|
|
|
|
const cash_balances = {
|
|
trader_flo_address: null,
|
|
cash_balance: null,
|
|
currency: null
|
|
}
|
|
|
|
const system_btc_reserves_private_keys = {
|
|
id: '',
|
|
btc_address: null,
|
|
balance: null,
|
|
trader_flo_address: null,
|
|
btc_private_key_shamirs_id: null,
|
|
supernode_transaction_key: null
|
|
}
|
|
|
|
const my_supernode_private_key_chunks = {
|
|
id: '',
|
|
supernode_transaction_key: null
|
|
}
|
|
|
|
const supernode_private_key_chunks = {
|
|
id: '',
|
|
privateKeyChunks: null
|
|
}
|
|
|
|
const withdraw_btc = {
|
|
id: '',
|
|
trader_flo_address: null,
|
|
utxo_addr: null,
|
|
receiverBTCAddress: null,
|
|
receiverBTCEquivalentInCash: null,
|
|
currency: null,
|
|
fiat_currency: null,
|
|
change_adress:null,
|
|
timestamp: null
|
|
}
|
|
|
|
const d3js = {
|
|
version: null,
|
|
filehash: null,
|
|
content: null
|
|
}
|
|
|
|
var db;
|
|
const DBName = "localbitcoinDB";
|
|
var request = window.indexedDB.open(DBName, 1);
|
|
|
|
request.onerror = function (event) {
|
|
//https://stackoverflow.com/questions/13972385/invalidstateerror-while-opening-indexeddb-in-firefox
|
|
event.preventDefault();
|
|
var error = event.target.error;
|
|
console.log("IndexedDB database open error:", error.name, error.message);
|
|
};
|
|
|
|
request.onsuccess = function (event) {
|
|
db = request.result;
|
|
runInitialDBOperations();
|
|
};
|
|
|
|
request.onupgradeneeded = function (event) {
|
|
var db = event.target.result;
|
|
if (!db.objectStoreNames.contains('localbitcoinUser')) {
|
|
var objectStore = db.createObjectStore("localbitcoinUser", {
|
|
keyPath: "id"
|
|
});
|
|
objectStore.createIndex('myLocalFLOAddress', 'myLocalFLOAddress', {
|
|
unique: false
|
|
});
|
|
objectStore.put(localbitcoinplusplusObj);
|
|
}
|
|
if (!db.objectStoreNames.contains('buyOrders')) {
|
|
var objectStore = db.createObjectStore("buyOrders", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('sellOrders')) {
|
|
var objectStore = db.createObjectStore("sellOrders", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('deposit')) {
|
|
var objectStore = db.createObjectStore("deposit", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: false
|
|
});
|
|
objectStore.createIndex('btc_address', 'btc_address', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('withdraw_cash')) {
|
|
var objectStore = db.createObjectStore("withdraw_cash", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: true
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('btc_balances')) {
|
|
var objectStore = db.createObjectStore("btc_balances", {
|
|
keyPath: 'trader_flo_address'
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('cash_balances')) {
|
|
var objectStore = db.createObjectStore("cash_balances", {
|
|
keyPath: 'trader_flo_address'
|
|
});
|
|
objectStore.createIndex('currency', 'currency', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('my_supernode_private_key_chunks')) {
|
|
var objectStore = db.createObjectStore("my_supernode_private_key_chunks", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('supernode_transaction_key', 'supernode_transaction_key', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('supernode_private_key_chunks')) {
|
|
var objectStore = db.createObjectStore("supernode_private_key_chunks", {
|
|
keyPath: 'id'
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('system_btc_reserves_private_keys')) {
|
|
var objectStore = db.createObjectStore("system_btc_reserves_private_keys", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: false
|
|
});
|
|
objectStore.createIndex('btc_address', 'btc_address', {
|
|
unique: false
|
|
});
|
|
objectStore.createIndex('supernode_transaction_key', 'supernode_transaction_key', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('withdraw_btc')) {
|
|
var objectStore = db.createObjectStore("withdraw_btc", {
|
|
keyPath: 'id'
|
|
});
|
|
objectStore.createIndex('trader_flo_address', 'trader_flo_address', {
|
|
unique: false
|
|
});
|
|
objectStore.createIndex('utxo_addr', 'utxo_addr', {
|
|
unique: false
|
|
});
|
|
objectStore.createIndex('receiverBTCAddress', 'receiverBTCAddress', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('userPublicData')) {
|
|
var objectStore = db.createObjectStore("userPublicData", {
|
|
keyPath: 'trader_flo_address'
|
|
});
|
|
objectStore.createIndex('trader_flo_pubKey', 'trader_flo_pubKey', {
|
|
unique: true
|
|
});
|
|
objectStore.createIndex('trader_status', 'trader_status', {
|
|
unique: false
|
|
});
|
|
}
|
|
if (!db.objectStoreNames.contains('d3js')) {
|
|
var objectStore = db.createObjectStore("d3js", {
|
|
keyPath: 'filehash'
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
function readDB(tablename, id) {
|
|
return new Promise((resolve, reject)=>{
|
|
var transaction = db.transaction([tablename]);
|
|
var objectStore = transaction.objectStore(tablename);
|
|
var request = objectStore.get(id);
|
|
|
|
request.onerror = function (event) {
|
|
reject("Unable to retrieve data from database!");
|
|
};
|
|
|
|
request.onsuccess = function (event) {
|
|
if (request.result) {
|
|
resolve(request.result);
|
|
} else {
|
|
resolve();
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
function readDBbyIndex(tablename, index, indexValue) {
|
|
return new Promise((resolve, reject)=>{
|
|
var transaction = db.transaction([tablename]);
|
|
var objectStore = transaction.objectStore(tablename);
|
|
let response = [];
|
|
objectStore.openCursor().onerror = function (event) {
|
|
console.error("Error fetching data");
|
|
reject(event);
|
|
};
|
|
objectStore.openCursor().onsuccess = function (event) {
|
|
let cursor = event.target.result;
|
|
if (cursor) {
|
|
if (cursor.value[index] == indexValue) {
|
|
response.push(cursor.value);
|
|
}
|
|
cursor.continue();
|
|
} else {
|
|
resolve(response);
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
|
|
function readAllDB(tablename) {
|
|
return new Promise((resolve, reject) => {
|
|
let response = [];
|
|
var objectStore = db.transaction(tablename).objectStore(tablename);
|
|
objectStore.openCursor().onsuccess = function (event) {
|
|
let cursor = event.target.result;
|
|
if (cursor) {
|
|
response.push(cursor.value);
|
|
cursor.continue();
|
|
} else {
|
|
resolve(response);
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
async function addDB(tablename, dbObject) {
|
|
try {
|
|
let request = db.transaction([tablename], "readwrite")
|
|
let store = request.objectStore(tablename)
|
|
await store.add(dbObject);
|
|
await request.complete;
|
|
console.info("Data added in "+tablename);
|
|
return dbObject;
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
}
|
|
|
|
async function updateinDB(tablename, Obj, key) {
|
|
try {
|
|
var request = db.transaction([tablename], "readwrite")
|
|
let store = request.objectStore(tablename)
|
|
await store.put(Obj);
|
|
await request.complete;
|
|
return Obj;
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
}
|
|
|
|
async function removeinDB(tablename, id) {
|
|
try {
|
|
var request = db.transaction([tablename], "readwrite")
|
|
let store = request.objectStore(tablename)
|
|
await store.delete(id);
|
|
await request.complete;
|
|
return id;
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
}
|
|
|
|
function removeByIndex(tablename, indexName, indexValue) {
|
|
return new Promise((resolve, reject)=>{
|
|
var request = db.transaction([tablename], "readwrite")
|
|
.objectStore(tablename);
|
|
var index = request.index(indexName);
|
|
var request = index.openCursor(IDBKeyRange.only(indexValue));
|
|
request.onsuccess = function() {
|
|
var cursor = request.result;
|
|
if (cursor) {
|
|
cursor.delete();
|
|
cursor.continue();
|
|
} else {
|
|
resolve(true);
|
|
}
|
|
};
|
|
request.onerror = function(e) {
|
|
reject(e);
|
|
}
|
|
})
|
|
}
|
|
|
|
async function removeAllinDB(tablename) {
|
|
try {
|
|
var request = db.transaction([tablename], "readwrite")
|
|
var objectStore = request.objectStore(tablename);
|
|
var objectStoreRequest = await objectStore.clear();
|
|
await request.complete;
|
|
console.info("All the data entry has been removed from your database "+tablename);
|
|
return tablename;
|
|
} catch (error) {
|
|
return new Error(error);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- Initialization of objects -->
|
|
<script>
|
|
var RM_WALLET = new localbitcoinplusplus.wallets;
|
|
var RM_TRADE = new localbitcoinplusplus.trade;
|
|
var RM_RPC = new localbitcoinplusplus.rpc;
|
|
|
|
// Fetch configs from Master Key
|
|
try {
|
|
var rm_configs = localbitcoinplusplus.actions.fetch_configs(function (...fetch_configs_res) {
|
|
dataBaseOperations();
|
|
});
|
|
} catch (error) {
|
|
throw new Error(`Failed to fetch configurations: ${error}`);
|
|
}
|
|
</script>
|
|
|
|
<!-- Database operations -->
|
|
<script>
|
|
// These DB functions run as soon as th page and indexed db loads
|
|
let runInitialDBOperations = function() {
|
|
try {
|
|
readDB('d3js', "58f54395efa8346e8e94d12609770f66b916897e7f4e05f6c98780cffa5c70a3").then(fileContent=>{
|
|
if (typeof fileContent=="object" && typeof fileContent.content=="string") {
|
|
var oScript = document.createElement("script");
|
|
var oScriptText = document.createTextNode(fileContent.content);
|
|
oScript.appendChild(oScriptText);
|
|
document.body.appendChild(oScript);
|
|
} else {
|
|
console.warn("Failed to load d3.js");
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
// localbitcoinUser Databse
|
|
const dataBaseOperations = function () {
|
|
let ask_flo_addr_btn = document.getElementById('ask_flo_addr_btn');
|
|
ask_flo_addr_btn.addEventListener('click', function () {
|
|
let ask_flo_addr = document.getElementById('ask_flo_addr');
|
|
let ask_flo_addr_val = ask_flo_addr.value.trim();
|
|
|
|
if (ask_flo_addr_val == null || typeof ask_flo_addr_val == undefined || ask_flo_addr_val ==
|
|
"") {
|
|
throw new Error('Empty or invalid FLO address.');
|
|
}
|
|
|
|
try {
|
|
readDB("localbitcoinUser", "00-01").then(function (idbData) {
|
|
if (typeof idbData.myLocalFLOPublicKey == undefined || idbData.myLocalFLOPublicKey
|
|
.trim() == '') {
|
|
let user_pvt_key = prompt("Please Enter your private key");
|
|
if (user_pvt_key.trim() !== "") {
|
|
|
|
let newKeys = RM_WALLET.generateFloKeys(user_pvt_key);
|
|
|
|
if (typeof newKeys == 'object' && typeof newKeys.address !==
|
|
undefined) {
|
|
localbitcoinplusplusObj.myLocalFLOAddress = newKeys.address;
|
|
localbitcoinplusplusObj.myLocalFLOPublicKey = newKeys.pubKeyHex;
|
|
localbitcoinplusplusObj.preferredTradeCurrency = "USD";
|
|
updateinDB("localbitcoinUser", localbitcoinplusplusObj, "00-01");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Declare the user flo address
|
|
const MY_LOCAL_FLO_ADDRESS = localbitcoinplusplus.wallets.my_local_flo_address = idbData.myLocalFLOAddress;
|
|
const MY_LOCAL_FLO_PUBLIC_KEY = localbitcoinplusplus.wallets.my_local_flo_public_key = idbData.myLocalFLOPublicKey;
|
|
const MY_PREFFERED_TRADE_CURRENCY = localbitcoinplusplus.wallets.my_preferred_trade_currency
|
|
= typeof idbData.preferredTradeCurrency !=="undefined" ? idbData.preferredTradeCurrency:"USD";
|
|
|
|
readDB('userPublicData', MY_LOCAL_FLO_ADDRESS).then(function(pubic_data_response) {
|
|
if (typeof pubic_data_response !== "object") {
|
|
let user_public_data_object = {
|
|
trader_flo_address: MY_LOCAL_FLO_ADDRESS,
|
|
trader_flo_pubKey: MY_LOCAL_FLO_PUBLIC_KEY,
|
|
trader_status: 0,
|
|
timestamp: + new Date()
|
|
}
|
|
let add_user_public_data_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "add_user_public_data",
|
|
{public_data:user_public_data_object});
|
|
doSend(add_user_public_data_req);
|
|
}
|
|
});
|
|
|
|
// rebuild private key
|
|
let supernode_transaction_key_arr = [];
|
|
readAllDB("my_supernode_private_key_chunks").then(function(chunks) {
|
|
if (typeof chunks == "object" && chunks.length>0) {
|
|
let txKey = chunks.map(chunk=>{
|
|
let retrieve_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "send_back_shamirs_secret_supernode_pvtkey",
|
|
{chunk_val:chunk.id});
|
|
doSend(retrieve_pvtkey_req);
|
|
supernode_transaction_key_arr.push(chunk.supernode_transaction_key);
|
|
return supernode_transaction_key_arr;
|
|
}).filter(function (e, i, c) {
|
|
return c.indexOf(e) === i;
|
|
});
|
|
const TRANSACTION_KEY = localbitcoinplusplus.wallets.supernode_transaction_key = txKey[0][0];
|
|
}
|
|
});
|
|
|
|
localbitcoinplusplus.actions.sync_with_supernode(MY_LOCAL_FLO_ADDRESS);
|
|
|
|
//localbitcoinuserdiv
|
|
document.getElementById("localbitcoinuserdiv").innerHTML = `<p>Address: ${idbData.myLocalFLOAddress}<p>`;
|
|
|
|
/* Give user the facillity to trade */
|
|
|
|
const tradebox = document.getElementById("tradebox");
|
|
|
|
const selectListCrypto = document.createElement("select");
|
|
selectListCrypto.id = "selectListCrypto";
|
|
tradebox.appendChild(selectListCrypto);
|
|
|
|
const selectListFiat = document.createElement("select");
|
|
selectListFiat.id = "selectListFiat";
|
|
tradebox.appendChild(selectListFiat);
|
|
|
|
const selectListAmount = document.createElement('select');
|
|
selectListAmount.id = "selectListAmount";
|
|
tradebox.appendChild(selectListAmount);
|
|
|
|
const trade_buy_button = document.createElement('button');
|
|
const trade_buy_button_text = document.createTextNode("BUY");
|
|
trade_buy_button.appendChild(trade_buy_button_text);
|
|
|
|
const trade_sell_button = document.createElement('button');
|
|
const trade_sell_button_text = document.createTextNode("SELL");
|
|
trade_sell_button.appendChild(trade_sell_button_text);
|
|
|
|
tradebox.appendChild(selectListCrypto);
|
|
tradebox.appendChild(selectListFiat);
|
|
tradebox.appendChild(selectListAmount);
|
|
tradebox.appendChild(trade_buy_button);
|
|
tradebox.appendChild(trade_sell_button);
|
|
|
|
localbitcoinplusplus.master_configurations.tradableAsset1.map(cryptos=>{
|
|
let option = document.createElement("option");
|
|
option.value = cryptos;
|
|
option.text = cryptos;
|
|
selectListCrypto.appendChild(option);
|
|
});
|
|
localbitcoinplusplus.master_configurations.tradableAsset2.map(fiat=>{
|
|
let option = document.createElement("option");
|
|
option.value = fiat;
|
|
option.text = fiat;
|
|
selectListFiat.appendChild(option);
|
|
});
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.map(amount=>{
|
|
let option = document.createElement("option");
|
|
option.value = amount;
|
|
option.text = amount;
|
|
selectListAmount.appendChild(option);
|
|
});
|
|
|
|
trade_buy_button.onclick = function (event) {
|
|
if (typeof idbData.myLocalFLOAddress !== "string" || idbData.myLocalFLOAddress
|
|
.trim() == "") {
|
|
throw new Error(
|
|
"You must have a BTC address to receive Bitcoin. No Bitcoin address found in database."
|
|
);
|
|
}
|
|
let buytrade = RM_TRADE.place_order("buy", idbData.myLocalFLOAddress,
|
|
selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value));
|
|
doSend(buytrade);
|
|
}
|
|
|
|
trade_sell_button.onclick = function (event) {
|
|
if (typeof idbData.myLocalFLOAddress == undefined || idbData.myLocalFLOAddress
|
|
.trim() == "") {
|
|
throw new Error(
|
|
"You must have a FLO address to trade. No such address found in database."
|
|
);
|
|
}
|
|
let selltrade = RM_TRADE.place_order("sell", idbData.myLocalFLOAddress,
|
|
selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value));
|
|
doSend(selltrade);
|
|
}
|
|
|
|
// Deposit / Withdraw asset
|
|
depositWithdrawAsset(idbData.myLocalFLOAddress);
|
|
|
|
// Upload files to DB
|
|
uploadFileToDB();
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
throw new Error(
|
|
"ERROR: Failed to initialise the localbitcoinUser database. You are unable to trade at the moment."
|
|
);
|
|
}
|
|
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<!-- Deposit/Withdraw asset -->
|
|
<script>
|
|
const depositWithdrawAsset = function (userFLOaddress) {
|
|
let asset_box = document.getElementById("asset_box");
|
|
|
|
// Create a select input for asset type
|
|
let assetTypeInput = document.createElement('select');
|
|
assetTypeInput.id = "select_assets_type";
|
|
asset_box.appendChild(assetTypeInput);
|
|
if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' &&
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined') {
|
|
let assetTypeSelectArray1 = JSON.parse(JSON.stringify(localbitcoinplusplus.master_configurations.tradableAsset1));
|
|
let assetTypeSelectArray2 = JSON.parse(JSON.stringify(localbitcoinplusplus.master_configurations.tradableAsset2));
|
|
let assetTypeSelectArray = assetTypeSelectArray1.concat(assetTypeSelectArray2)
|
|
.filter((item, pos, finalArray)=>finalArray.indexOf(item) == pos
|
|
);
|
|
assetTypeSelectArray.unshift("Select Asset Type");
|
|
for (var i = 0; i < assetTypeSelectArray.length; i++) {
|
|
var option = document.createElement("option");
|
|
option.value = assetTypeSelectArray[i];
|
|
option.text = assetTypeSelectArray[i];
|
|
assetTypeInput.appendChild(option);
|
|
}
|
|
}
|
|
|
|
// Create a select input for trade amount
|
|
let tradeAmountSelect = document.createElement('select');
|
|
tradeAmountSelect.id = "trade_amount_select";
|
|
asset_box.appendChild(tradeAmountSelect);
|
|
if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.length > 0) {
|
|
let tradeAmountSelectArray = JSON.parse(JSON.stringify(localbitcoinplusplus.master_configurations.validTradingAmount));
|
|
tradeAmountSelectArray.unshift("Select Asset Amount");
|
|
for (var i = 0; i < tradeAmountSelectArray.length; i++) {
|
|
var option = document.createElement("option");
|
|
option.value = tradeAmountSelectArray[i];
|
|
option.text = tradeAmountSelectArray[i];
|
|
tradeAmountSelect.appendChild(option);
|
|
}
|
|
}
|
|
|
|
// Create a deposit and withdraw button
|
|
let depositAssetButton = document.createElement('button');
|
|
let depositAssetButtonText = document.createTextNode('Deposit');
|
|
depositAssetButton.appendChild(depositAssetButtonText);
|
|
let withdrawAssetButton = document.createElement('button');
|
|
let withdrawAssetButtonText = document.createTextNode('Withdraw');
|
|
withdrawAssetButton.appendChild(withdrawAssetButtonText);
|
|
asset_box.appendChild(depositAssetButton);
|
|
asset_box.appendChild(withdrawAssetButton);
|
|
|
|
depositAssetButton.addEventListener('click', function () {
|
|
let asset_type = assetTypeInput.value;
|
|
let tradeAmount = parseFloat(tradeAmountSelect.value);
|
|
if (typeof userFLOaddress == undefined || userFLOaddress.trim().length < 1) {
|
|
throw new Error("Invalid or empty user FLO address.");
|
|
}
|
|
if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount) &&
|
|
(typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.tradableAsset1.includes(asset_type) ||
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.tradableAsset2.includes(asset_type))
|
|
) {
|
|
RM_TRADE.depositAsset(asset_type, tradeAmount, userFLOaddress);
|
|
} else {
|
|
throw new Error("Error while depositing your address.");
|
|
}
|
|
});
|
|
|
|
withdrawAssetButton.addEventListener('click', function (params) {
|
|
let receivinAddress = prompt("Please enter a Bitcoin address or full bank details.");
|
|
if (receivinAddress.trim == "") {
|
|
alert(
|
|
"You must specify either a Bitcoin address to withdraw Bitcoin or your bank detail to withdraw cash."
|
|
);
|
|
return false;
|
|
}
|
|
|
|
let asset_type = assetTypeInput.value;
|
|
|
|
let tradeAmount = parseFloat(tradeAmountSelect.value);
|
|
if (typeof userFLOaddress == undefined || userFLOaddress.trim().length < 1) {
|
|
throw new Error("Invalid or empty user FLO address.");
|
|
}
|
|
if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount) &&
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' &&
|
|
typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' &&
|
|
localbitcoinplusplus.master_configurations.tradableAsset1
|
|
.concat(localbitcoinplusplus.master_configurations.tradableAsset2).includes(asset_type)) {
|
|
RM_TRADE.withdrawAsset(asset_type, tradeAmount, receivinAddress, userFLOaddress);
|
|
} else {
|
|
throw new Error("Error while depositing your address.");
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<!-- Generate new keys -->
|
|
<script>
|
|
(function () {
|
|
let new_flo_keys_ul = document.getElementById('new_flo_keys_ul');
|
|
let new_flo_keys_button = document.getElementById('new_flo_keys');
|
|
new_flo_keys_button.onclick = function () {
|
|
if (typeof RM_WALLET == "object") {
|
|
let new_flo_keys = RM_WALLET.generateFloKeys();
|
|
let new_key_li = '';
|
|
Object.keys(new_flo_keys).forEach(function (key) {
|
|
new_key_li += `<li><strong>${key}</strong>: ${new_flo_keys[key]}</li>`;
|
|
});
|
|
new_flo_keys_ul.insertAdjacentHTML('beforeend', new_key_li);
|
|
}
|
|
}
|
|
})();
|
|
|
|
// Distribute the private keys
|
|
const register_as_supernode_div = document.createElement('div');
|
|
|
|
const register_as_supernode_input = document.createElement('input');
|
|
register_as_supernode_input.type = 'text';
|
|
|
|
let register_as_supernode_btn = document.createElement("button");
|
|
let register_as_supernode_btn_text = document.createTextNode("Shamir Secretify My Private Key");
|
|
register_as_supernode_btn.appendChild(register_as_supernode_btn_text);
|
|
register_as_supernode_btn.onclick = function() {
|
|
|
|
const users_entered_private_key = register_as_supernode_input.value;
|
|
|
|
if (users_entered_private_key.length<0) {
|
|
throw new Error("Private key is empty.");
|
|
}
|
|
|
|
let publicKey_for_users_entered_private_key;
|
|
try {
|
|
publicKey_for_users_entered_private_key = RM_WALLET.generateFloKeys(users_entered_private_key).pubKeyHex;
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
// let su_list = localbitcoinplusplus.master_configurations.supernodesPubKeys;
|
|
|
|
// if (!su_list.includes(publicKey_for_users_entered_private_key)) {
|
|
// throw new Error(`The public key ${publicKey_for_users_entered_private_key} is not registered as supernode yet.`);
|
|
// }
|
|
|
|
let pvt_key_shamirs_secret_shares = localbitcoinplusplus.wallets.prototype.createShamirsSecretShares(users_entered_private_key, 10, 5);
|
|
if (typeof pvt_key_shamirs_secret_shares=="object" && pvt_key_shamirs_secret_shares.length>0) {
|
|
|
|
// Add suprnode's own private keys to DB
|
|
let supernode_transaction_key = Crypto.util.randomBytes(64);
|
|
let pvt_key_shamirs_secret_shares_array = pvt_key_shamirs_secret_shares.map(chunks=>{
|
|
let chunk_ids = Crypto.util.bytesToHex(Crypto.util.randomBytes(64));
|
|
let chunk_array = {
|
|
"id": chunk_ids,
|
|
"privateKeyChunks": Crypto.AES.encrypt(chunks, supernode_transaction_key)
|
|
};
|
|
try {
|
|
addDB("my_supernode_private_key_chunks", {id:chunk_ids, supernode_transaction_key:supernode_transaction_key});
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
return chunk_array;
|
|
});
|
|
|
|
// Send chunks of privat keys to other supernodes
|
|
pvt_key_shamirs_secret_shares_array.map(shares=>{
|
|
let store_pvtkey_req = localbitcoinplusplus.rpc.prototype
|
|
.send_rpc
|
|
.call(this, "store_shamirs_secret_pvtkey_shares",
|
|
shares);
|
|
doSend(store_pvtkey_req);
|
|
}
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
register_as_supernode_div.appendChild(register_as_supernode_input);
|
|
register_as_supernode_div.appendChild(register_as_supernode_btn);
|
|
document.body.appendChild(register_as_supernode_div);
|
|
|
|
</script>
|
|
|
|
<!-- Misc functions -->
|
|
<script>
|
|
function rand(a, b, multiple) {
|
|
return Math.floor(Math.random(a, b) * multiple);
|
|
}
|
|
|
|
/*https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze*/
|
|
function deepFreeze(object) {
|
|
// Retrieve the property names defined on object
|
|
var propNames = Object.getOwnPropertyNames(object);
|
|
|
|
// Freeze properties before freezing self
|
|
for (let name of propNames) {
|
|
let value = object[name];
|
|
|
|
object[name] = value && typeof value === "object" ?
|
|
deepFreeze(value) : value;
|
|
}
|
|
return Object.freeze(object);
|
|
}
|
|
|
|
function randomNoRepeats(array) {
|
|
var copy = array.slice(0);
|
|
return function() {
|
|
if (copy.length < 1) { copy = array.slice(0); }
|
|
var index = Math.floor(Math.random() * copy.length);
|
|
var item = copy[index];
|
|
copy.splice(index, 1);
|
|
return item;
|
|
};
|
|
}
|
|
|
|
function duplicatesInArray(arr) {
|
|
arr.reduce(function(acc, el, i, arr) {
|
|
if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc;
|
|
}, []);
|
|
}
|
|
|
|
/* Function to load files to db */
|
|
function readBlob(version) {
|
|
|
|
var files = document.getElementById('upload_file_db').files;
|
|
|
|
if (!files.length) return('Please select a file!');
|
|
|
|
if (isNaN(version)) throw new Error('Please provide a valid version number for thhis file.');
|
|
|
|
var file = files[0];
|
|
var start = 0;
|
|
var stop = file.size - 1;
|
|
|
|
var reader = new FileReader();
|
|
|
|
// If we use onloadend, we need to check the readyState.
|
|
reader.onloadend = function(evt) {
|
|
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
|
let data = evt.target.result;
|
|
let hash = Crypto.SHA256(data);
|
|
addDB("d3js", {
|
|
content: data,
|
|
filehash: hash,
|
|
version: version
|
|
});
|
|
};
|
|
};
|
|
|
|
var blob = file.slice(start, stop + 1);
|
|
reader.readAsBinaryString(blob);
|
|
}
|
|
|
|
function uploadFileToDB() {
|
|
|
|
const fileUploadDiv = document.createElement('div');
|
|
const dbFile = document.createElement('input');
|
|
dbFile.setAttribute("type", "file");
|
|
dbFile.setAttribute("id", "upload_file_db");
|
|
const readBytesButtons = document.createElement("button");
|
|
readBytesButtons.setAttribute("id", "uploadFileButton");
|
|
readBytesButtons.innerHTML = "Upload File";
|
|
|
|
fileUploadDiv.appendChild(dbFile);
|
|
fileUploadDiv.appendChild(readBytesButtons);
|
|
document.body.appendChild(fileUploadDiv);
|
|
|
|
document.querySelector('#uploadFileButton').addEventListener('click', function(evt) {
|
|
if (evt.target.tagName.toLowerCase() == 'button') {
|
|
let version_num = prompt("Enter version number for this file.", 1);
|
|
readBlob(version_num);
|
|
}
|
|
}, false);
|
|
|
|
}
|
|
|
|
//Function to check current balance of a BTC address
|
|
//trader_flo_address, BTCAddress, bitcoinToBePaid
|
|
function validateDepositedBTCBalance(trader_deposits) {
|
|
try {
|
|
//let url = `https://blockchain.info/q/addressbalance/${BTCAddress}?confirmations=6`;
|
|
let url = `https://testnet.flocha.in/api/addr/${trader_deposits.btc_address}/balance`;
|
|
helper_functions.ajaxGet(url).then(balance=> {
|
|
if (!isNaN(balance) && parseFloat(balance) > 0) {
|
|
balance = parseFloat(balance);
|
|
|
|
/************************ Case of dispute *****************/
|
|
if(0) {
|
|
//if (trader_deposits.bitcoinToBePaid - balance > localbitcoinplusplus.master_configurations.btcTradeMargin) {
|
|
console.log(trader_deposits.bitcoinToBePaid - balance, localbitcoinplusplus.master_configurations
|
|
.btcTradeMargin);
|
|
|
|
trader_deposits.status = 4; // User sent less BTC than he should #Disputed
|
|
updateinDB("deposit", trader_deposits, trader_flo_address);
|
|
} else {
|
|
//Deposit successful. Update user balance and status to 2. Its Private key can be
|
|
// now given to a random trader
|
|
trader_deposits.status = 2;
|
|
updateinDB("deposit", trader_deposits, trader_deposits.trader_flo_address);
|
|
|
|
readDBbyIndex('system_btc_reserves_private_keys', 'btc_address', trader_deposits.btc_address).then(function(reserve_res) {
|
|
if (typeof reserve_res=="object") {
|
|
reserve_res.balance = balance;
|
|
updateinDB('system_btc_reserves_private_keys', reserve_res, reserve_res.id);
|
|
}
|
|
});
|
|
|
|
let updatedBTCbalances = {
|
|
trader_flo_address: trader_deposits.trader_flo_address,
|
|
btc_balance: balance
|
|
}
|
|
readDB('btc_balances', trader_deposits.trader_flo_address).then(function (res_btc_balances) {
|
|
if (typeof res_btc_balances == "object" && typeof res_btc_balances.result ==
|
|
"object" && typeof res_btc_balances.result.btc_balance=="number") {
|
|
updatedBTCbalances.btc_balance += parseFloat(res_btc_balances.result.btc_balance);
|
|
}
|
|
// Update BTC balance of user in btc_balances
|
|
updateinDB("btc_balances", updatedBTCbalances, trader_deposits.btc_address);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
setInterval(function () {
|
|
readDBbyIndex("deposit", 'status', 1).then(function (res) {
|
|
res.map(function (deposit_trade) {
|
|
if (deposit_trade.product == "BTC") {
|
|
validateDepositedBTCBalance(deposit_trade);
|
|
}
|
|
});
|
|
});
|
|
}, 600000);
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |