commit
1cd11fa63e
@ -0,0 +1 @@
|
||||
.DS_Store
|
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
@ -0,0 +1,56 @@
|
||||
# MyMonero Core JS
|
||||
|
||||
![Logo](https://raw.githubusercontent.com/mymonero/mymonero-app-js/master/docs/assets/icon_100.png "Logo")
|
||||
|
||||
|
||||
### Info
|
||||
|
||||
1. Legal
|
||||
2. What's in This Repo?
|
||||
3. Library Roadmap
|
||||
4. Library API Documentation
|
||||
|
||||
### Contributing
|
||||
|
||||
1. QA
|
||||
2. Pull Requests
|
||||
|
||||
|
||||
## Legal
|
||||
|
||||
See `LICENSE.txt` for license.
|
||||
|
||||
All source code copyright © 2014-2017 by MyMonero. All rights reserved.
|
||||
|
||||
|
||||
## What's in This Repo?
|
||||
|
||||
This repository holds the Javascript source code for the basic cryptography which currently powers the official [MyMonero](https://www.mymonero.com) downloadable desktop and mobile apps.
|
||||
|
||||
### Contents
|
||||
|
||||
* `monero_utils` contains Monero- and MyMonero-specific implementations, wrappers, and declarations.
|
||||
|
||||
* `cryptonote_utils` contains the MyMonero JS implementations for the underlying cryptography behind Monero.
|
||||
|
||||
* This readme is located at `README.md`, and the license is located at `LICENSE.txt`.
|
||||
|
||||
## Library Roadmap
|
||||
|
||||
* Investigate replacing entire implementation with bindings to lightwallet API in official Monero core wallet C++
|
||||
|
||||
## Library API Documentation
|
||||
|
||||
*Coming soon*
|
||||
|
||||
# Contributing
|
||||
|
||||
## QA
|
||||
|
||||
Please submit any bugs as Issues unless they have already been reported.
|
||||
|
||||
Suggestions and feedback are very welcome!
|
||||
|
||||
## Pull Requests
|
||||
|
||||
We'll merge nearly anything constructive. Contributors welcome, and are credited in releases.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
|
||||
(function(exports)
|
||||
{
|
||||
var crc32 = (function () {
|
||||
'use strict';
|
||||
var crc32 = {};
|
||||
|
||||
crc32.Utf8Encode = function (string) {
|
||||
return unescape(encodeURIComponent(string));
|
||||
};
|
||||
|
||||
crc32.run = function (str) {
|
||||
var crc = new crc32.Type();
|
||||
crc.processString(str);
|
||||
return crc.checksum();
|
||||
};
|
||||
|
||||
crc32.table = [
|
||||
0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035,
|
||||
249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049,
|
||||
498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639,
|
||||
325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317,
|
||||
997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443,
|
||||
901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665,
|
||||
651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303,
|
||||
671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565,
|
||||
1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059,
|
||||
2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297,
|
||||
1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223,
|
||||
1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405,
|
||||
1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995,
|
||||
1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649,
|
||||
1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015,
|
||||
1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989,
|
||||
3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523,
|
||||
3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377,
|
||||
4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879,
|
||||
4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637,
|
||||
3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859,
|
||||
3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161,
|
||||
3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815,
|
||||
3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221,
|
||||
2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371,
|
||||
2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881,
|
||||
2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567,
|
||||
2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701,
|
||||
2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035,
|
||||
2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897,
|
||||
3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431,
|
||||
3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117
|
||||
];
|
||||
crc32.Type = function () {
|
||||
this.rem_ = 0xFFFFFFFF;
|
||||
this.checksum = function () {
|
||||
return ((this.rem_ ^ 0xFFFFFFFF) >>> 0);
|
||||
};
|
||||
this.processString = function (str) {
|
||||
str = crc32.Utf8Encode(str);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var byte_index = ((str.charCodeAt(i) ^ this.rem_) >>> 0) & 0xFF;
|
||||
this.rem_ = ((this.rem_ >>> 8) ^ crc32.table[byte_index]) >>> 0;
|
||||
}
|
||||
};
|
||||
return this;
|
||||
};
|
||||
|
||||
return crc32;
|
||||
})();
|
||||
exports.crc32 = crc32;
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
@ -0,0 +1,225 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
|
||||
|
||||
// v--- These should maybe be injected into context
|
||||
const JSBigInt = require('./biginteger').BigInteger
|
||||
|
||||
var cnBase58 = (function ()
|
||||
{
|
||||
var b58 = {};
|
||||
//
|
||||
var alphabet_str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
var alphabet = [];
|
||||
for (var i = 0; i < alphabet_str.length; i++) {
|
||||
alphabet.push(alphabet_str.charCodeAt(i));
|
||||
}
|
||||
var encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
|
||||
|
||||
var alphabet_size = alphabet.length;
|
||||
var full_block_size = 8;
|
||||
var full_encoded_block_size = 11;
|
||||
|
||||
var UINT64_MAX = new JSBigInt(2).pow(64);
|
||||
|
||||
function hextobin(hex) {
|
||||
if (hex.length % 2 !== 0) throw "Hex string has invalid length!";
|
||||
var res = new Uint8Array(hex.length / 2);
|
||||
for (var i = 0; i < hex.length / 2; ++i) {
|
||||
res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function bintohex(bin) {
|
||||
var out = [];
|
||||
for (var i = 0; i < bin.length; ++i) {
|
||||
out.push(("0" + bin[i].toString(16)).slice(-2));
|
||||
}
|
||||
return out.join("");
|
||||
}
|
||||
|
||||
function strtobin(str) {
|
||||
var res = new Uint8Array(str.length);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
res[i] = str.charCodeAt(i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function bintostr(bin) {
|
||||
var out = [];
|
||||
for (var i = 0; i < bin.length; i++) {
|
||||
out.push(String.fromCharCode(bin[i]));
|
||||
}
|
||||
return out.join("");
|
||||
}
|
||||
|
||||
function uint8_be_to_64(data) {
|
||||
if (data.length < 1 || data.length > 8) {
|
||||
throw "Invalid input length";
|
||||
}
|
||||
var res = JSBigInt.ZERO;
|
||||
var twopow8 = new JSBigInt(2).pow(8);
|
||||
var i = 0;
|
||||
switch (9 - data.length) {
|
||||
case 1:
|
||||
res = res.add(data[i++]);
|
||||
case 2:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 3:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 4:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 5:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 6:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 7:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
case 8:
|
||||
res = res.multiply(twopow8).add(data[i++]);
|
||||
break;
|
||||
default:
|
||||
throw "Impossible condition";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function uint64_to_8be(num, size) {
|
||||
var res = new Uint8Array(size);
|
||||
if (size < 1 || size > 8) {
|
||||
throw "Invalid input length";
|
||||
}
|
||||
var twopow8 = new JSBigInt(2).pow(8);
|
||||
for (var i = size - 1; i >= 0; i--) {
|
||||
res[i] = num.remainder(twopow8).toJSValue();
|
||||
num = num.divide(twopow8);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
b58.encode_block = function (data, buf, index) {
|
||||
if (data.length < 1 || data.length > full_encoded_block_size) {
|
||||
throw "Invalid block length: " + data.length;
|
||||
}
|
||||
var num = uint8_be_to_64(data);
|
||||
var i = encoded_block_sizes[data.length] - 1;
|
||||
// while num > 0
|
||||
while (num.compare(0) === 1) {
|
||||
var div = num.divRem(alphabet_size);
|
||||
// remainder = num % alphabet_size
|
||||
var remainder = div[1];
|
||||
// num = num / alphabet_size
|
||||
num = div[0];
|
||||
buf[index + i] = alphabet[remainder.toJSValue()];
|
||||
i--;
|
||||
}
|
||||
return buf;
|
||||
};
|
||||
|
||||
b58.encode = function (hex) {
|
||||
var data = hextobin(hex);
|
||||
if (data.length === 0) {
|
||||
return "";
|
||||
}
|
||||
var full_block_count = Math.floor(data.length / full_block_size);
|
||||
var last_block_size = data.length % full_block_size;
|
||||
var res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size];
|
||||
|
||||
var res = new Uint8Array(res_size);
|
||||
var i;
|
||||
for (i = 0; i < res_size; ++i) {
|
||||
res[i] = alphabet[0];
|
||||
}
|
||||
for (i = 0; i < full_block_count; i++) {
|
||||
res = b58.encode_block(data.subarray(i * full_block_size, i * full_block_size + full_block_size), res, i * full_encoded_block_size);
|
||||
}
|
||||
if (last_block_size > 0) {
|
||||
res = b58.encode_block(data.subarray(full_block_count * full_block_size, full_block_count * full_block_size + last_block_size), res, full_block_count * full_encoded_block_size)
|
||||
}
|
||||
return bintostr(res);
|
||||
};
|
||||
|
||||
b58.decode_block = function (data, buf, index) {
|
||||
if (data.length < 1 || data.length > full_encoded_block_size) {
|
||||
throw "Invalid block length: " + data.length;
|
||||
}
|
||||
|
||||
var res_size = encoded_block_sizes.indexOf(data.length);
|
||||
if (res_size <= 0) {
|
||||
throw "Invalid block size";
|
||||
}
|
||||
var res_num = new JSBigInt(0);
|
||||
var order = new JSBigInt(1);
|
||||
for (var i = data.length - 1; i >= 0; i--) {
|
||||
var digit = alphabet.indexOf(data[i]);
|
||||
if (digit < 0) {
|
||||
throw "Invalid symbol";
|
||||
}
|
||||
var product = order.multiply(digit).add(res_num);
|
||||
// if product > UINT64_MAX
|
||||
if (product.compare(UINT64_MAX) === 1) {
|
||||
throw "Overflow";
|
||||
}
|
||||
res_num = product;
|
||||
order = order.multiply(alphabet_size);
|
||||
}
|
||||
if (res_size < full_block_size && (new JSBigInt(2).pow(8 * res_size).compare(res_num) <= 0)) {
|
||||
throw "Overflow 2";
|
||||
}
|
||||
buf.set(uint64_to_8be(res_num, res_size), index);
|
||||
return buf;
|
||||
};
|
||||
|
||||
b58.decode = function (enc) {
|
||||
enc = strtobin(enc);
|
||||
if (enc.length === 0) {
|
||||
return "";
|
||||
}
|
||||
var full_block_count = Math.floor(enc.length / full_encoded_block_size);
|
||||
var last_block_size = enc.length % full_encoded_block_size;
|
||||
var last_block_decoded_size = encoded_block_sizes.indexOf(last_block_size);
|
||||
if (last_block_decoded_size < 0) {
|
||||
throw "Invalid encoded length";
|
||||
}
|
||||
var data_size = full_block_count * full_block_size + last_block_decoded_size;
|
||||
var data = new Uint8Array(data_size);
|
||||
for (var i = 0; i < full_block_count; i++) {
|
||||
data = b58.decode_block(enc.subarray(i * full_encoded_block_size, i * full_encoded_block_size + full_encoded_block_size), data, i * full_block_size);
|
||||
}
|
||||
if (last_block_size > 0) {
|
||||
data = b58.decode_block(enc.subarray(full_block_count * full_encoded_block_size, full_block_count * full_encoded_block_size + last_block_size), data, full_block_count * full_block_size);
|
||||
}
|
||||
return bintohex(data);
|
||||
};
|
||||
|
||||
return b58;
|
||||
})();
|
||||
|
||||
exports.cnBase58 = cnBase58
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,571 @@
|
||||
(function(nacl) {
|
||||
'use strict';
|
||||
|
||||
// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
|
||||
// Public domain.
|
||||
//
|
||||
// Implementation derived from TweetNaCl version 20140427.
|
||||
// See for details: http://tweetnacl.cr.yp.to/
|
||||
|
||||
// modified 2017 for some CN functions by luigi1111
|
||||
|
||||
var gf = function(init) {
|
||||
var i, r = new Float64Array(16);
|
||||
if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
|
||||
return r;
|
||||
};
|
||||
|
||||
// Pluggable, initialized in high-level API below.
|
||||
var randombytes = function(/* x, n */) { throw new Error('no PRNG'); };
|
||||
|
||||
var _0 = new Uint8Array(16);
|
||||
var _9 = new Uint8Array(32); _9[0] = 9;
|
||||
|
||||
var gf0 = gf(),
|
||||
gf1 = gf([1]),
|
||||
_121665 = gf([0xdb41, 1]),
|
||||
D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
|
||||
D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]),
|
||||
X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),
|
||||
Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),
|
||||
I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
|
||||
|
||||
function vn(x, xi, y, yi, n) {
|
||||
var i,d = 0;
|
||||
for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i];
|
||||
return (1 & ((d - 1) >>> 8)) - 1;
|
||||
}
|
||||
|
||||
function crypto_verify_16(x, xi, y, yi) {
|
||||
return vn(x,xi,y,yi,16);
|
||||
}
|
||||
|
||||
function crypto_verify_32(x, xi, y, yi) {
|
||||
return vn(x,xi,y,yi,32);
|
||||
}
|
||||
|
||||
function set25519(r, a) {
|
||||
var i;
|
||||
for (i = 0; i < 16; i++) r[i] = a[i]|0;
|
||||
}
|
||||
|
||||
function car25519(o) {
|
||||
var i, v, c = 1;
|
||||
for (i = 0; i < 16; i++) {
|
||||
v = o[i] + c + 65535;
|
||||
c = Math.floor(v / 65536);
|
||||
o[i] = v - c * 65536;
|
||||
}
|
||||
o[0] += c-1 + 37 * (c-1);
|
||||
}
|
||||
|
||||
function sel25519(p, q, b) {
|
||||
var t, c = ~(b-1);
|
||||
for (var i = 0; i < 16; i++) {
|
||||
t = c & (p[i] ^ q[i]);
|
||||
p[i] ^= t;
|
||||
q[i] ^= t;
|
||||
}
|
||||
}
|
||||
|
||||
function pack25519(o, n) {
|
||||
var i, j, b;
|
||||
var m = gf(), t = gf();
|
||||
for (i = 0; i < 16; i++) t[i] = n[i];
|
||||
car25519(t);
|
||||
car25519(t);
|
||||
car25519(t);
|
||||
for (j = 0; j < 2; j++) {
|
||||
m[0] = t[0] - 0xffed;
|
||||
for (i = 1; i < 15; i++) {
|
||||
m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
|
||||
m[i-1] &= 0xffff;
|
||||
}
|
||||
m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
|
||||
b = (m[15]>>16) & 1;
|
||||
m[14] &= 0xffff;
|
||||
sel25519(t, m, 1-b);
|
||||
}
|
||||
for (i = 0; i < 16; i++) {
|
||||
o[2*i] = t[i] & 0xff;
|
||||
o[2*i+1] = t[i]>>8;
|
||||
}
|
||||
}
|
||||
|
||||
function neq25519(a, b) {
|
||||
var c = new Uint8Array(32), d = new Uint8Array(32);
|
||||
pack25519(c, a);
|
||||
pack25519(d, b);
|
||||
return crypto_verify_32(c, 0, d, 0);
|
||||
}
|
||||
|
||||
function par25519(a) {
|
||||
var d = new Uint8Array(32);
|
||||
pack25519(d, a);
|
||||
return d[0] & 1;
|
||||
}
|
||||
|
||||
function unpack25519(o, n) {
|
||||
var i;
|
||||
for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
|
||||
o[15] &= 0x7fff;
|
||||
}
|
||||
|
||||
function A(o, a, b) {
|
||||
for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
|
||||
}
|
||||
|
||||
function Z(o, a, b) {
|
||||
for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
|
||||
}
|
||||
|
||||
function M(o, a, b) {
|
||||
var v, c,
|
||||
t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
|
||||
t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
|
||||
t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
|
||||
t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
|
||||
b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5],
|
||||
b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10],
|
||||
b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
|
||||
|
||||
v = a[0];
|
||||
t0 += v * b0; t1 += v * b1; t2 += v * b2; t3 += v * b3;
|
||||
t4 += v * b4; t5 += v * b5; t6 += v * b6; t7 += v * b7;
|
||||
t8 += v * b8; t9 += v * b9; t10 += v * b10; t11 += v * b11;
|
||||
t12 += v * b12; t13 += v * b13; t14 += v * b14; t15 += v * b15;
|
||||
v = a[1];
|
||||
t1 += v * b0; t2 += v * b1; t3 += v * b2; t4 += v * b3;
|
||||
t5 += v * b4; t6 += v * b5; t7 += v * b6; t8 += v * b7;
|
||||
t9 += v * b8; t10 += v * b9; t11 += v * b10; t12 += v * b11;
|
||||
t13 += v * b12; t14 += v * b13; t15 += v * b14; t16 += v * b15;
|
||||
v = a[2];
|
||||
t2 += v * b0; t3 += v * b1; t4 += v * b2; t5 += v * b3;
|
||||
t6 += v * b4; t7 += v * b5; t8 += v * b6; t9 += v * b7;
|
||||
t10 += v * b8; t11 += v * b9; t12 += v * b10; t13 += v * b11;
|
||||
t14 += v * b12; t15 += v * b13; t16 += v * b14; t17 += v * b15;
|
||||
v = a[3];
|
||||
t3 += v * b0; t4 += v * b1; t5 += v * b2; t6 += v * b3;
|
||||
t7 += v * b4; t8 += v * b5; t9 += v * b6; t10 += v * b7;
|
||||
t11 += v * b8; t12 += v * b9; t13 += v * b10; t14 += v * b11;
|
||||
t15 += v * b12; t16 += v * b13; t17 += v * b14; t18 += v * b15;
|
||||
v = a[4];
|
||||
t4 += v * b0; t5 += v * b1; t6 += v * b2; t7 += v * b3;
|
||||
t8 += v * b4; t9 += v * b5; t10 += v * b6; t11 += v * b7;
|
||||
t12 += v * b8; t13 += v * b9; t14 += v * b10; t15 += v * b11;
|
||||
t16 += v * b12; t17 += v * b13; t18 += v * b14; t19 += v * b15;
|
||||
v = a[5];
|
||||
t5 += v * b0; t6 += v * b1; t7 += v * b2; t8 += v * b3;
|
||||
t9 += v * b4; t10 += v * b5; t11 += v * b6; t12 += v * b7;
|
||||
t13 += v * b8; t14 += v * b9; t15 += v * b10; t16 += v * b11;
|
||||
t17 += v * b12; t18 += v * b13; t19 += v * b14; t20 += v * b15;
|
||||
v = a[6];
|
||||
t6 += v * b0; t7 += v * b1; t8 += v * b2; t9 += v * b3;
|
||||
t10 += v * b4; t11 += v * b5; t12 += v * b6; t13 += v * b7;
|
||||
t14 += v * b8; t15 += v * b9; t16 += v * b10; t17 += v * b11;
|
||||
t18 += v * b12; t19 += v * b13; t20 += v * b14; t21 += v * b15;
|
||||
v = a[7];
|
||||
t7 += v * b0; t8 += v * b1; t9 += v * b2; t10 += v * b3;
|
||||
t11 += v * b4; t12 += v * b5; t13 += v * b6; t14 += v * b7;
|
||||
t15 += v * b8; t16 += v * b9; t17 += v * b10; t18 += v * b11;
|
||||
t19 += v * b12; t20 += v * b13; t21 += v * b14; t22 += v * b15;
|
||||
v = a[8];
|
||||
t8 += v * b0; t9 += v * b1; t10 += v * b2; t11 += v * b3;
|
||||
t12 += v * b4; t13 += v * b5; t14 += v * b6; t15 += v * b7;
|
||||
t16 += v * b8; t17 += v * b9; t18 += v * b10; t19 += v * b11;
|
||||
t20 += v * b12; t21 += v * b13; t22 += v * b14; t23 += v * b15;
|
||||
v = a[9];
|
||||
t9 += v * b0; t10 += v * b1; t11 += v * b2; t12 += v * b3;
|
||||
t13 += v * b4; t14 += v * b5; t15 += v * b6; t16 += v * b7;
|
||||
t17 += v * b8; t18 += v * b9; t19 += v * b10; t20 += v * b11;
|
||||
t21 += v * b12; t22 += v * b13; t23 += v * b14; t24 += v * b15;
|
||||
v = a[10];
|
||||
t10 += v * b0; t11 += v * b1; t12 += v * b2; t13 += v * b3;
|
||||
t14 += v * b4; t15 += v * b5; t16 += v * b6; t17 += v * b7;
|
||||
t18 += v * b8; t19 += v * b9; t20 += v * b10; t21 += v * b11;
|
||||
t22 += v * b12; t23 += v * b13; t24 += v * b14; t25 += v * b15;
|
||||
v = a[11];
|
||||
t11 += v * b0; t12 += v * b1; t13 += v * b2; t14 += v * b3;
|
||||
t15 += v * b4; t16 += v * b5; t17 += v * b6; t18 += v * b7;
|
||||
t19 += v * b8; t20 += v * b9; t21 += v * b10; t22 += v * b11;
|
||||
t23 += v * b12; t24 += v * b13; t25 += v * b14; t26 += v * b15;
|
||||
v = a[12];
|
||||
t12 += v * b0; t13 += v * b1; t14 += v * b2; t15 += v * b3;
|
||||
t16 += v * b4; t17 += v * b5; t18 += v * b6; t19 += v * b7;
|
||||
t20 += v * b8; t21 += v * b9; t22 += v * b10; t23 += v * b11;
|
||||
t24 += v * b12; t25 += v * b13; t26 += v * b14; t27 += v * b15;
|
||||
v = a[13];
|
||||
t13 += v * b0; t14 += v * b1; t15 += v * b2; t16 += v * b3;
|
||||
t17 += v * b4; t18 += v * b5; t19 += v * b6; t20 += v * b7;
|
||||
t21 += v * b8; t22 += v * b9; t23 += v * b10; t24 += v * b11;
|
||||
t25 += v * b12; t26 += v * b13; t27 += v * b14; t28 += v * b15;
|
||||
v = a[14];
|
||||
t14 += v * b0; t15 += v * b1; t16 += v * b2; t17 += v * b3;
|
||||
t18 += v * b4; t19 += v * b5; t20 += v * b6; t21 += v * b7;
|
||||
t22 += v * b8; t23 += v * b9; t24 += v * b10; t25 += v * b11;
|
||||
t26 += v * b12; t27 += v * b13; t28 += v * b14; t29 += v * b15;
|
||||
v = a[15];
|
||||
t15 += v * b0; t16 += v * b1; t17 += v * b2; t18 += v * b3;
|
||||
t19 += v * b4; t20 += v * b5; t21 += v * b6; t22 += v * b7;
|
||||
t23 += v * b8; t24 += v * b9; t25 += v * b10; t26 += v * b11;
|
||||
t27 += v * b12; t28 += v * b13; t29 += v * b14; t30 += v * b15;
|
||||
|
||||
t0 += 38 * t16; t1 += 38 * t17; t2 += 38 * t18; t3 += 38 * t19;
|
||||
t4 += 38 * t20; t5 += 38 * t21; t6 += 38 * t22; t7 += 38 * t23;
|
||||
t8 += 38 * t24; t9 += 38 * t25; t10 += 38 * t26; t11 += 38 * t27;
|
||||
t12 += 38 * t28; t13 += 38 * t29; t14 += 38 * t30; // t15 left as is
|
||||
|
||||
// first car
|
||||
c = 1;
|
||||
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
|
||||
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
|
||||
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
|
||||
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
|
||||
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
|
||||
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
|
||||
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
|
||||
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
|
||||
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
|
||||
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
|
||||
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
|
||||
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
|
||||
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
|
||||
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
|
||||
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
|
||||
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
|
||||
t0 += c-1 + 37 * (c-1);
|
||||
|
||||
// second car
|
||||
c = 1;
|
||||
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
|
||||
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
|
||||
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
|
||||
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
|
||||
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
|
||||
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
|
||||
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
|
||||
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
|
||||
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
|
||||
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
|
||||
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
|
||||
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
|
||||
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
|
||||
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
|
||||
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
|
||||
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
|
||||
t0 += c-1 + 37 * (c-1);
|
||||
|
||||
o[ 0] = t0;
|
||||
o[ 1] = t1;
|
||||
o[ 2] = t2;
|
||||
o[ 3] = t3;
|
||||
o[ 4] = t4;
|
||||
o[ 5] = t5;
|
||||
o[ 6] = t6;
|
||||
o[ 7] = t7;
|
||||
o[ 8] = t8;
|
||||
o[ 9] = t9;
|
||||
o[10] = t10;
|
||||
o[11] = t11;
|
||||
o[12] = t12;
|
||||
o[13] = t13;
|
||||
o[14] = t14;
|
||||
o[15] = t15;
|
||||
}
|
||||
|
||||
function S(o, a) {
|
||||
M(o, a, a);
|
||||
}
|
||||
|
||||
function inv25519(o, i) {
|
||||
var c = gf();
|
||||
var a;
|
||||
for (a = 0; a < 16; a++) c[a] = i[a];
|
||||
for (a = 253; a >= 0; a--) {
|
||||
S(c, c);
|
||||
if(a !== 2 && a !== 4) M(c, c, i);
|
||||
}
|
||||
for (a = 0; a < 16; a++) o[a] = c[a];
|
||||
}
|
||||
|
||||
function pow2523(o, i) {
|
||||
var c = gf();
|
||||
var a;
|
||||
for (a = 0; a < 16; a++) c[a] = i[a];
|
||||
for (a = 250; a >= 0; a--) {
|
||||
S(c, c);
|
||||
if(a !== 1) M(c, c, i);
|
||||
}
|
||||
for (a = 0; a < 16; a++) o[a] = c[a];
|
||||
}
|
||||
|
||||
function add(p, q) {
|
||||
var a = gf(), b = gf(), c = gf(),
|
||||
d = gf(), e = gf(), f = gf(),
|
||||
g = gf(), h = gf(), t = gf();
|
||||
|
||||
Z(a, p[1], p[0]);
|
||||
Z(t, q[1], q[0]);
|
||||
M(a, a, t);
|
||||
A(b, p[0], p[1]);
|
||||
A(t, q[0], q[1]);
|
||||
M(b, b, t);
|
||||
M(c, p[3], q[3]);
|
||||
M(c, c, D2);
|
||||
M(d, p[2], q[2]);
|
||||
A(d, d, d);
|
||||
Z(e, b, a);
|
||||
Z(f, d, c);
|
||||
A(g, d, c);
|
||||
A(h, b, a);
|
||||
|
||||
M(p[0], e, f);
|
||||
M(p[1], h, g);
|
||||
M(p[2], g, f);
|
||||
M(p[3], e, h);
|
||||
}
|
||||
|
||||
function cswap(p, q, b) {
|
||||
var i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sel25519(p[i], q[i], b);
|
||||
}
|
||||
}
|
||||
|
||||
function pack(r, p) {
|
||||
var tx = gf(), ty = gf(), zi = gf();
|
||||
inv25519(zi, p[2]);
|
||||
M(tx, p[0], zi);
|
||||
M(ty, p[1], zi);
|
||||
pack25519(r, ty);
|
||||
r[31] ^= par25519(tx) << 7;
|
||||
}
|
||||
|
||||
function scalarmult(p, q, s) {
|
||||
var b, i;
|
||||
set25519(p[0], gf0);
|
||||
set25519(p[1], gf1);
|
||||
set25519(p[2], gf1);
|
||||
set25519(p[3], gf0);
|
||||
for (i = 255; i >= 0; --i) {
|
||||
b = (s[(i/8)|0] >> (i&7)) & 1;
|
||||
cswap(p, q, b);
|
||||
add(q, p);
|
||||
add(p, p);
|
||||
cswap(p, q, b);
|
||||
}
|
||||
}
|
||||
|
||||
function scalarbase(p, s) {
|
||||
var q = [gf(), gf(), gf(), gf()];
|
||||
set25519(q[0], X);
|
||||
set25519(q[1], Y);
|
||||
set25519(q[2], gf1);
|
||||
M(q[3], X, Y);
|
||||
scalarmult(p, q, s);
|
||||
}
|
||||
|
||||
//new functions for CN - scalar operations are handled externally
|
||||
// this only handles curve operations, except for Hp()
|
||||
|
||||
//why do we negate points when unpacking them???
|
||||
function ge_neg(pub) {
|
||||
pub[31] ^= 0x80;
|
||||
}
|
||||
|
||||
//res = s*G
|
||||
function ge_scalarmult_base(s) {
|
||||
var p = [gf(), gf(), gf(), gf()];
|
||||
scalarbase(p, s);
|
||||
var pk = new Uint8Array(32);
|
||||
pack(pk, p);
|
||||
return pk;
|
||||
}
|
||||
|
||||
//res = s*P
|
||||
function ge_scalarmult(P, s) {
|
||||
var p = [gf(), gf(), gf(), gf()],
|
||||
upk = [gf(), gf(), gf(), gf()],
|
||||
res = new Uint8Array(32);
|
||||
ge_neg(P);
|
||||
if (unpackneg(upk, P) !== 0) throw "non-0 error on point decode";
|
||||
scalarmult(p, upk, s);
|
||||
pack(res, p);
|
||||
return res;
|
||||
}
|
||||
|
||||
//res = c*P + r*G
|
||||
function ge_double_scalarmult_base_vartime(c, P, r) {
|
||||
var uP = [gf(), gf(), gf(), gf()],
|
||||
cP = [gf(), gf(), gf(), gf()],
|
||||
rG = [gf(), gf(), gf(), gf()],
|
||||
res = new Uint8Array(32);
|
||||
ge_neg(P);
|
||||
if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
|
||||
scalarmult(cP, uP, c);
|
||||
scalarbase(rG, r);
|
||||
add(rG, cP);
|
||||
pack(res, rG);
|
||||
return res;
|
||||
}
|
||||
|
||||
//name changed to reflect not using precomp; res = r*Pb + c*I
|
||||
function ge_double_scalarmult_postcomp_vartime(r, Pb, c, I) {
|
||||
var uPb = [gf(), gf(), gf(), gf()],
|
||||
uI = [gf(), gf(), gf(), gf()],
|
||||
cI = [gf(), gf(), gf(), gf()],
|
||||
rPb = [gf(), gf(), gf(), gf()],
|
||||
res = new Uint8Array(32);
|
||||
ge_neg(Pb);
|
||||
if (unpackneg(uPb, Pb) !== 0) throw "non-0 error on point decode";
|
||||
scalarmult(rPb, uPb, r);
|
||||
ge_neg(I);
|
||||
if (unpackneg(uI, I) !== 0) throw "non-0 error on point decode";
|
||||
scalarmult(cI, uI, c);
|
||||
add(rPb, cI);
|
||||
pack(res, rPb);
|
||||
return res;
|
||||
}
|
||||
|
||||
//res = P + Q
|
||||
function ge_add(P, Q) {
|
||||
var uP = [gf(), gf(), gf(), gf()],
|
||||
uQ = [gf(), gf(), gf(), gf()],
|
||||
res = new Uint8Array(32);
|
||||
ge_neg(P);
|
||||
ge_neg(Q);
|
||||
if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
|
||||
if (unpackneg(uQ, Q) !== 0) throw "non-0 error on point decode";
|
||||
add(uP, uQ);
|
||||
pack(res, uP);
|
||||
return res;
|
||||
}
|
||||
|
||||
var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
|
||||
|
||||
function modL(r, x) {
|
||||
var carry, i, j, k;
|
||||
for (i = 63; i >= 32; --i) {
|
||||
carry = 0;
|
||||
for (j = i - 32, k = i - 12; j < k; ++j) {
|
||||
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
|
||||
carry = (x[j] + 128) >> 8;
|
||||
x[j] -= carry * 256;
|
||||
}
|
||||
x[j] += carry;
|
||||
x[i] = 0;
|
||||
}
|
||||
carry = 0;
|
||||
for (j = 0; j < 32; j++) {
|
||||
x[j] += carry - (x[31] >> 4) * L[j];
|
||||
carry = x[j] >> 8;
|
||||
x[j] &= 255;
|
||||
}
|
||||
for (j = 0; j < 32; j++) x[j] -= carry * L[j];
|
||||
for (i = 0; i < 32; i++) {
|
||||
x[i+1] += x[i] >> 8;
|
||||
r[i] = x[i] & 255;
|
||||
}
|
||||
}
|
||||
|
||||
function reduce(r) {
|
||||
var x = new Float64Array(64), i;
|
||||
for (i = 0; i < 64; i++) x[i] = r[i];
|
||||
for (i = 0; i < 64; i++) r[i] = 0;
|
||||
modL(r, x);
|
||||
}
|
||||
|
||||
function unpackneg(r, p) {
|
||||
var t = gf(), chk = gf(), num = gf(),
|
||||
den = gf(), den2 = gf(), den4 = gf(),
|
||||
den6 = gf();
|
||||
|
||||
set25519(r[2], gf1);
|
||||
unpack25519(r[1], p);
|
||||
S(num, r[1]);
|
||||
M(den, num, D);
|
||||
Z(num, num, r[2]);
|
||||
A(den, r[2], den);
|
||||
|
||||
S(den2, den);
|
||||
S(den4, den2);
|
||||
M(den6, den4, den2);
|
||||
M(t, den6, num);
|
||||
M(t, t, den);
|
||||
|
||||
pow2523(t, t);
|
||||
M(t, t, num);
|
||||
M(t, t, den);
|
||||
M(t, t, den);
|
||||
M(r[0], t, den);
|
||||
|
||||
S(chk, r[0]);
|
||||
M(chk, chk, den);
|
||||
if (neq25519(chk, num)) M(r[0], r[0], I);
|
||||
|
||||
S(chk, r[0]);
|
||||
M(chk, chk, den);
|
||||
if (neq25519(chk, num)) return -1;
|
||||
|
||||
if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]);
|
||||
|
||||
M(r[3], r[0], r[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nacl.ll = {
|
||||
|
||||
ge_scalarmult_base: ge_scalarmult_base,
|
||||
ge_scalarmult: ge_scalarmult,
|
||||
ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime,
|
||||
ge_add: ge_add,
|
||||
ge_double_scalarmult_postcomp_vartime: ge_double_scalarmult_postcomp_vartime
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* High-level API */
|
||||
|
||||
function cleanup(arr) {
|
||||
for (var i = 0; i < arr.length; i++) arr[i] = 0;
|
||||
}
|
||||
|
||||
nacl.randomBytes = function(n) {
|
||||
var b = new Uint8Array(n);
|
||||
randombytes(b, n);
|
||||
return b;
|
||||
};
|
||||
|
||||
nacl.setPRNG = function(fn) {
|
||||
randombytes = fn;
|
||||
};
|
||||
|
||||
(function() {
|
||||
// Initialize PRNG if environment provides CSPRNG.
|
||||
// If not, methods calling randombytes will throw.
|
||||
var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null;
|
||||
if (crypto && crypto.getRandomValues) {
|
||||
// Browsers.
|
||||
var QUOTA = 65536;
|
||||
nacl.setPRNG(function(x, n) {
|
||||
var i, v = new Uint8Array(n);
|
||||
for (i = 0; i < n; i += QUOTA) {
|
||||
crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
|
||||
}
|
||||
for (i = 0; i < n; i++) x[i] = v[i];
|
||||
cleanup(v);
|
||||
});
|
||||
} else if (typeof require !== 'undefined') {
|
||||
// Node.js.
|
||||
crypto = require('crypto');
|
||||
if (crypto && crypto.randomBytes) {
|
||||
nacl.setPRNG(function(x, n) {
|
||||
var i, v = crypto.randomBytes(n);
|
||||
for (i = 0; i < n; i++) x[i] = v[i];
|
||||
cleanup(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {}));
|
@ -0,0 +1,473 @@
|
||||
/*
|
||||
* js-sha3 v0.5.1
|
||||
* https://github.com/emn178/js-sha3
|
||||
*
|
||||
* Copyright 2015, emn178@gmail.com
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
;(function(root, undefined) {
|
||||
'use strict';
|
||||
|
||||
var NODE_JS = typeof(module) != 'undefined';
|
||||
if(NODE_JS) {
|
||||
root = global;
|
||||
if(root.JS_SHA3_TEST) {
|
||||
root.navigator = { userAgent: 'Chrome'};
|
||||
}
|
||||
}
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
|
||||
var KECCAK_PADDING = [1, 256, 65536, 16777216];
|
||||
var PADDING = [6, 1536, 393216, 100663296];
|
||||
var SHIFT = [0, 8, 16, 24];
|
||||
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||
var BITS = [224, 256, 384, 512];
|
||||
var SHAKE_BITS = [128, 256];
|
||||
var OUTPUT_TYPES = ['hex', 'buffer', 'array'];
|
||||
|
||||
var createOutputMethod = function(bits, padding, outputType) {
|
||||
return function(message) {
|
||||
return new Keccak(bits, padding, bits).update(message)[outputType]();
|
||||
}
|
||||
};
|
||||
|
||||
var createShakeOutputMethod = function(bits, padding, outputType) {
|
||||
return function(message, outputBits) {
|
||||
return new Keccak(bits, padding, outputBits).update(message)[outputType]();
|
||||
}
|
||||
};
|
||||
|
||||
var createMethod = function(bits, padding) {
|
||||
var method = createOutputMethod(bits, padding, 'hex');
|
||||
method.create = function() {
|
||||
return new Keccak(bits, padding, bits);
|
||||
};
|
||||
method.update = function(message) {
|
||||
return method.create().update(message);
|
||||
};
|
||||
for(var i = 0;i < OUTPUT_TYPES.length;++i) {
|
||||
var type = OUTPUT_TYPES[i];
|
||||
method[type] = createOutputMethod(bits, padding, type);
|
||||
}
|
||||
return method;
|
||||
};
|
||||
|
||||
var createShakeMethod = function(bits, padding) {
|
||||
var method = createShakeOutputMethod(bits, padding, 'hex');
|
||||
method.create = function(outputBits) {
|
||||
return new Keccak(bits, padding, outputBits);
|
||||
};
|
||||
method.update = function(message, outputBits) {
|
||||
return method.create(outputBits).update(message);
|
||||
};
|
||||
for(var i = 0;i < OUTPUT_TYPES.length;++i) {
|
||||
var type = OUTPUT_TYPES[i];
|
||||
method[type] = createShakeOutputMethod(bits, padding, type);
|
||||
}
|
||||
return method;
|
||||
};
|
||||
|
||||
var algorithms = [
|
||||
{name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod},
|
||||
{name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod},
|
||||
{name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod}
|
||||
];
|
||||
|
||||
var methods = {};
|
||||
|
||||
for(var i = 0;i < algorithms.length;++i) {
|
||||
var algorithm = algorithms[i];
|
||||
var bits = algorithm.bits;
|
||||
var createMethod = algorithm.createMethod;
|
||||
for(var j = 0;j < bits.length;++j) {
|
||||
var method = algorithm.createMethod(bits[j], algorithm.padding);
|
||||
methods[algorithm.name +'_' + bits[j]] = method;
|
||||
}
|
||||
}
|
||||
|
||||
function Keccak(bits, padding, outputBits) {
|
||||
this.blocks = [];
|
||||
this.s = [];
|
||||
this.padding = padding;
|
||||
this.outputBits = outputBits;
|
||||
this.reset = true;
|
||||
this.block = 0;
|
||||
this.start = 0;
|
||||
this.blockCount = (1600 - (bits << 1)) >> 5;
|
||||
this.byteCount = this.blockCount << 2;
|
||||
this.outputBlocks = outputBits >> 5;
|
||||
this.extraBytes = (outputBits & 31) >> 3;
|
||||
|
||||
for(var i = 0;i < 50;++i) {
|
||||
this.s[i] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Keccak.prototype.update = function(message) {
|
||||
var notString = typeof(message) != 'string';
|
||||
if(notString && message.constructor == root.ArrayBuffer) {
|
||||
message = new Uint8Array(message);
|
||||
}
|
||||
var length = message.length, blocks = this.blocks, byteCount = this.byteCount,
|
||||
blockCount = this.blockCount, index = 0, s = this.s, i, code;
|
||||
|
||||
while(index < length) {
|
||||
if(this.reset) {
|
||||
this.reset = false;
|
||||
blocks[0] = this.block;
|
||||
for(i = 1;i < blockCount + 1;++i) {
|
||||
blocks[i] = 0;
|
||||
}
|
||||
}
|
||||
if(notString) {
|
||||
for (i = this.start;index < length && i < byteCount; ++index) {
|
||||
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
} else {
|
||||
for (i = this.start;index < length && i < byteCount; ++index) {
|
||||
code = message.charCodeAt(index);
|
||||
if (code < 0x80) {
|
||||
blocks[i >> 2] |= code << SHIFT[i++ & 3];
|
||||
} else if (code < 0x800) {
|
||||
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
||||
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.lastByteIndex = i;
|
||||
if(i >= byteCount) {
|
||||
this.start = i - byteCount;
|
||||
this.block = blocks[blockCount];
|
||||
for(i = 0;i < blockCount;++i) {
|
||||
s[i] ^= blocks[i];
|
||||
}
|
||||
f(s);
|
||||
this.reset = true;
|
||||
} else {
|
||||
this.start = i;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Keccak.prototype.finalize = function() {
|
||||
var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
|
||||
blocks[i >> 2] |= this.padding[i & 3];
|
||||
if(this.lastByteIndex == this.byteCount) {
|
||||
blocks[0] = blocks[blockCount];
|
||||
for(i = 1;i < blockCount + 1;++i) {
|
||||
blocks[i] = 0;
|
||||
}
|
||||
}
|
||||
blocks[blockCount - 1] |= 0x80000000;
|
||||
for(i = 0;i < blockCount;++i) {
|
||||
s[i] ^= blocks[i];
|
||||
}
|
||||
f(s);
|
||||
};
|
||||
|
||||
Keccak.prototype.toString = Keccak.prototype.hex = function() {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var hex = '', block;
|
||||
while(j < outputBlocks) {
|
||||
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
|
||||
block = s[i];
|
||||
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
|
||||
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
|
||||
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
|
||||
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
|
||||
}
|
||||
if(j % blockCount == 0) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
if(extraBytes) {
|
||||
block = s[i];
|
||||
if(extraBytes > 0) {
|
||||
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
|
||||
}
|
||||
if(extraBytes > 1) {
|
||||
hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
|
||||
}
|
||||
if(extraBytes > 2) {
|
||||
hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
|
||||
}
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
Keccak.prototype.buffer = function() {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var bytes = this.outputBits >> 3;
|
||||
var buffer;
|
||||
if(extraBytes) {
|
||||
buffer = new ArrayBuffer((outputBlocks + 1) << 2);
|
||||
} else {
|
||||
buffer = new ArrayBuffer(bytes);
|
||||
}
|
||||
var array = new Uint32Array(buffer);
|
||||
while(j < outputBlocks) {
|
||||
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
|
||||
array[j] = s[i];
|
||||
}
|
||||
if(j % blockCount == 0) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
if(extraBytes) {
|
||||
array[i] = s[i];
|
||||
buffer = buffer.slice(0, bytes);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
Keccak.prototype.digest = Keccak.prototype.array = function() {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var array = [], offset, block;
|
||||
while(j < outputBlocks) {
|
||||
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
|
||||
offset = j << 2;
|
||||
block = s[i];
|
||||
array[offset] = block & 0xFF;
|
||||
array[offset + 1] = (block >> 8) & 0xFF;
|
||||
array[offset + 2] = (block >> 16) & 0xFF;
|
||||
array[offset + 3] = (block >> 24) & 0xFF;
|
||||
}
|
||||
if(j % blockCount == 0) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
if(extraBytes) {
|
||||
offset = j << 2;
|
||||
block = s[i];
|
||||
if(extraBytes > 0) {
|
||||
array[offset] = block & 0xFF;
|
||||
}
|
||||
if(extraBytes > 1) {
|
||||
array[offset + 1] = (block >> 8) & 0xFF;
|
||||
}
|
||||
if(extraBytes > 2) {
|
||||
array[offset + 2] = (block >> 16) & 0xFF;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
var f = function(s) {
|
||||
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||
for(n = 0; n < 48; n += 2) {
|
||||
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||
|
||||
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||
s[0] ^= h;
|
||||
s[1] ^= l;
|
||||
s[10] ^= h;
|
||||
s[11] ^= l;
|
||||
s[20] ^= h;
|
||||
s[21] ^= l;
|
||||
s[30] ^= h;
|
||||
s[31] ^= l;
|
||||
s[40] ^= h;
|
||||
s[41] ^= l;
|
||||
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||
s[2] ^= h;
|
||||
s[3] ^= l;
|
||||
s[12] ^= h;
|
||||
s[13] ^= l;
|
||||
s[22] ^= h;
|
||||
s[23] ^= l;
|
||||
s[32] ^= h;
|
||||
s[33] ^= l;
|
||||
s[42] ^= h;
|
||||
s[43] ^= l;
|
||||
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||
s[4] ^= h;
|
||||
s[5] ^= l;
|
||||
s[14] ^= h;
|
||||
s[15] ^= l;
|
||||
s[24] ^= h;
|
||||
s[25] ^= l;
|
||||
s[34] ^= h;
|
||||
s[35] ^= l;
|
||||
s[44] ^= h;
|
||||
s[45] ^= l;
|
||||
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||
s[6] ^= h;
|
||||
s[7] ^= l;
|
||||
s[16] ^= h;
|
||||
s[17] ^= l;
|
||||
s[26] ^= h;
|
||||
s[27] ^= l;
|
||||
s[36] ^= h;
|
||||
s[37] ^= l;
|
||||
s[46] ^= h;
|
||||
s[47] ^= l;
|
||||
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||
s[8] ^= h;
|
||||
s[9] ^= l;
|
||||
s[18] ^= h;
|
||||
s[19] ^= l;
|
||||
s[28] ^= h;
|
||||
s[29] ^= l;
|
||||
s[38] ^= h;
|
||||
s[39] ^= l;
|
||||
s[48] ^= h;
|
||||
s[49] ^= l;
|
||||
|
||||
b0 = s[0];
|
||||
b1 = s[1];
|
||||
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||
|
||||
s[0] = b0 ^ (~b2 & b4);
|
||||
s[1] = b1 ^ (~b3 & b5);
|
||||
s[10] = b10 ^ (~b12 & b14);
|
||||
s[11] = b11 ^ (~b13 & b15);
|
||||
s[20] = b20 ^ (~b22 & b24);
|
||||
s[21] = b21 ^ (~b23 & b25);
|
||||
s[30] = b30 ^ (~b32 & b34);
|
||||
s[31] = b31 ^ (~b33 & b35);
|
||||
s[40] = b40 ^ (~b42 & b44);
|
||||
s[41] = b41 ^ (~b43 & b45);
|
||||
s[2] = b2 ^ (~b4 & b6);
|
||||
s[3] = b3 ^ (~b5 & b7);
|
||||
s[12] = b12 ^ (~b14 & b16);
|
||||
s[13] = b13 ^ (~b15 & b17);
|
||||
s[22] = b22 ^ (~b24 & b26);
|
||||
s[23] = b23 ^ (~b25 & b27);
|
||||
s[32] = b32 ^ (~b34 & b36);
|
||||
s[33] = b33 ^ (~b35 & b37);
|
||||
s[42] = b42 ^ (~b44 & b46);
|
||||
s[43] = b43 ^ (~b45 & b47);
|
||||
s[4] = b4 ^ (~b6 & b8);
|
||||
s[5] = b5 ^ (~b7 & b9);
|
||||
s[14] = b14 ^ (~b16 & b18);
|
||||
s[15] = b15 ^ (~b17 & b19);
|
||||
s[24] = b24 ^ (~b26 & b28);
|
||||
s[25] = b25 ^ (~b27 & b29);
|
||||
s[34] = b34 ^ (~b36 & b38);
|
||||
s[35] = b35 ^ (~b37 & b39);
|
||||
s[44] = b44 ^ (~b46 & b48);
|
||||
s[45] = b45 ^ (~b47 & b49);
|
||||
s[6] = b6 ^ (~b8 & b0);
|
||||
s[7] = b7 ^ (~b9 & b1);
|
||||
s[16] = b16 ^ (~b18 & b10);
|
||||
s[17] = b17 ^ (~b19 & b11);
|
||||
s[26] = b26 ^ (~b28 & b20);
|
||||
s[27] = b27 ^ (~b29 & b21);
|
||||
s[36] = b36 ^ (~b38 & b30);
|
||||
s[37] = b37 ^ (~b39 & b31);
|
||||
s[46] = b46 ^ (~b48 & b40);
|
||||
s[47] = b47 ^ (~b49 & b41);
|
||||
s[8] = b8 ^ (~b0 & b2);
|
||||
s[9] = b9 ^ (~b1 & b3);
|
||||
s[18] = b18 ^ (~b10 & b12);
|
||||
s[19] = b19 ^ (~b11 & b13);
|
||||
s[28] = b28 ^ (~b20 & b22);
|
||||
s[29] = b29 ^ (~b21 & b23);
|
||||
s[38] = b38 ^ (~b30 & b32);
|
||||
s[39] = b39 ^ (~b31 & b33);
|
||||
s[48] = b48 ^ (~b40 & b42);
|
||||
s[49] = b49 ^ (~b41 & b43);
|
||||
|
||||
s[0] ^= RC[n];
|
||||
s[1] ^= RC[n + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if(!root.JS_SHA3_TEST && NODE_JS) {
|
||||
module.exports = methods;
|
||||
} else if(root) {
|
||||
for(var key in methods) {
|
||||
root[key] = methods[key];
|
||||
}
|
||||
}
|
||||
}(this));
|
@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const JSBigInt = require('../cryptonote_utils/biginteger').BigInteger
|
||||
//
|
||||
module.exports =
|
||||
{
|
||||
// Number of atomic units in one unit of currency. e.g. 12 => 10^12 = 1000000000000
|
||||
coinUnitPlaces: 12,
|
||||
|
||||
// Minimum number of confirmations for a transaction to show as confirmed
|
||||
txMinConfirms: 10,
|
||||
|
||||
// Currency symbol
|
||||
coinSymbol: 'XMR',
|
||||
|
||||
// OpenAlias prefix
|
||||
openAliasPrefix: "xmr",
|
||||
|
||||
// Currency name
|
||||
coinName: 'Monero',
|
||||
|
||||
// Payment URI Prefix
|
||||
coinUriPrefix: 'monero:',
|
||||
|
||||
// Prefix code for addresses
|
||||
addressPrefix: 18, // 18 => addresses start with "4"
|
||||
integratedAddressPrefix: 19,
|
||||
|
||||
// Network per kb fee in atomic units
|
||||
feePerKB_JSBigInt: new JSBigInt('2000000000'), // 0.002
|
||||
|
||||
// Dust threshold in atomic units
|
||||
// 10^10 used for choosing outputs/change - we decompose all the way down if the receiver wants now regardless of threshold
|
||||
dustThreshold: new JSBigInt('10000000000'),
|
||||
|
||||
// Maximum block number, used for tx unlock time
|
||||
maxBlockNumber: 500000000,
|
||||
|
||||
// Average block time in seconds, used for unlock time estimation
|
||||
avgBlockTime: 60
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_config = require('./monero_config')
|
||||
const cryptonote_utils = require('../cryptonote_utils/cryptonote_utils').cnUtil
|
||||
const monero_cryptonote_utils_instance = cryptonote_utils(monero_config)
|
||||
//
|
||||
module.exports = monero_cryptonote_utils_instance
|
@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_utils = require('./monero_cryptonote_utils_instance')
|
||||
//
|
||||
var key_images = {}
|
||||
//
|
||||
var Lazy_KeyImage = function(
|
||||
tx_pub_key,
|
||||
out_index,
|
||||
public_address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
{
|
||||
var cache_index = tx_pub_key + ':' + public_address + ':' + out_index
|
||||
const cached__key_image = key_images[cache_index]
|
||||
if (typeof cached__key_image !== 'undefined' && cached__key_image !== null) {
|
||||
return cached__key_image
|
||||
}
|
||||
var key_image = monero_utils.generate_key_image(
|
||||
tx_pub_key,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private,
|
||||
out_index
|
||||
).key_image
|
||||
// cache:
|
||||
key_images[cache_index] = key_image
|
||||
//
|
||||
return key_image
|
||||
}
|
||||
exports.Lazy_KeyImage = Lazy_KeyImage
|
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_utils = require('./monero_cryptonote_utils_instance')
|
||||
//
|
||||
function New_TransactionID()
|
||||
{
|
||||
return monero_utils.rand_32()
|
||||
}
|
||||
exports.New_TransactionID = New_TransactionID
|
||||
//
|
||||
function IsValidPaymentIDOrNoPaymentID(payment_id)
|
||||
{
|
||||
if (payment_id) {
|
||||
if (payment_id.length !== 64 || !(/^[0-9a-fA-F]{64}$/.test(payment_id))) { // not a valid 64 char pid
|
||||
return false // then not valid
|
||||
}
|
||||
}
|
||||
return true // then either no pid or is a valid one
|
||||
}
|
||||
exports.IsValidPaymentIDOrNoPaymentID = IsValidPaymentIDOrNoPaymentID
|
@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_config = require('./monero_config')
|
||||
//
|
||||
function New_RequestFunds_URI(
|
||||
args
|
||||
)// -> String?
|
||||
{
|
||||
const address = args.address
|
||||
if (!address) {
|
||||
throw "missing address"
|
||||
// return null
|
||||
}
|
||||
var uri = monero_config.coinUriPrefix + "//" + address // inserting a // so data detectors pick it up… maybe remove if/after not necessary
|
||||
var isAppendingParam0 = true
|
||||
function addParam(parameterName, value)
|
||||
{
|
||||
if (value == null || value == ""/*important*/ || typeof value === 'undefined') {
|
||||
return
|
||||
}
|
||||
var conjunctionStr = "&"
|
||||
if (isAppendingParam0 === true) {
|
||||
isAppendingParam0 = false
|
||||
conjunctionStr = "?"
|
||||
}
|
||||
uri += conjunctionStr
|
||||
uri += parameterName + '=' + encodeURIComponent(value)
|
||||
}
|
||||
{
|
||||
addParam('tx_amount', args.amount)
|
||||
addParam('tx_description', args.description)
|
||||
addParam('tx_payment_id', args.payment_id)
|
||||
addParam('tx_message', args.message)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
exports.New_RequestFunds_URI = New_RequestFunds_URI
|
||||
//
|
||||
function New_ParsedPayload_FromRequestURIString(uriString)
|
||||
{ // throws; -> {}
|
||||
// TODO
|
||||
const url = new URL(uriString)
|
||||
const protocol = url.protocol
|
||||
if (protocol !== monero_config.coinUriPrefix) {
|
||||
throw "Request URI has non-Monero protocol"
|
||||
}
|
||||
var target_address = url.pathname // var instead of const as have to finalize it
|
||||
// it seems that if the URL has // in it, pathname will be empty, but host will contain the address instead
|
||||
if (target_address === "" || typeof target_address === 'undefined' || !target_address) {
|
||||
target_address = url.host || url.hostname
|
||||
}
|
||||
if (target_address.indexOf("//") == 0) {
|
||||
target_address = target_address.slice(0 + "//".length, target_address.length) // strip prefixing "//" in case URL had protocol:// instead of protocol:
|
||||
}
|
||||
const searchParams = url.searchParams // needs to be parsed it seems
|
||||
//
|
||||
const payload =
|
||||
{
|
||||
address: target_address
|
||||
}
|
||||
const keyPrefixToTrim = "tx_"
|
||||
const lengthOf_keyPrefixToTrim = keyPrefixToTrim.length
|
||||
searchParams.forEach(
|
||||
function(value, key)
|
||||
{
|
||||
var storeAt_key = key
|
||||
if (key.indexOf(keyPrefixToTrim) === 0) {
|
||||
storeAt_key = key.slice(lengthOf_keyPrefixToTrim, key.length)
|
||||
}
|
||||
payload["" + storeAt_key] = value
|
||||
}
|
||||
)
|
||||
//
|
||||
return payload
|
||||
}
|
||||
exports.New_ParsedPayload_FromRequestURIString = New_ParsedPayload_FromRequestURIString
|
@ -0,0 +1,652 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const async = require('async')
|
||||
//
|
||||
const monero_config = require('./monero_config')
|
||||
const monero_utils = require('./monero_cryptonote_utils_instance')
|
||||
const monero_paymentID_utils = require('./monero_paymentID_utils')
|
||||
const monero_openalias_utils = require('../OpenAlias/monero_openalias_utils')
|
||||
//
|
||||
const JSBigInt = require('../cryptonote_utils/biginteger').BigInteger
|
||||
//
|
||||
//
|
||||
// Fee calculation port from Monero baseline
|
||||
// https://github.com/monero-project/monero/blob/master/src/wallet/wallet2.cpp
|
||||
//
|
||||
const APPROXIMATE_INPUT_BYTES = 80 // used to choose when to stop adding outputs to a tx
|
||||
//
|
||||
function calculate_fee(fee_per_kb_JSBigInt, numberOf_bytes, fee_multiplier)
|
||||
{
|
||||
const numberOf_kB_JSBigInt = new JSBigInt((numberOf_bytes + 1023.0) / 1024.0)
|
||||
const fee = fee_per_kb_JSBigInt.multiply(fee_multiplier).multiply(numberOf_kB_JSBigInt)
|
||||
//
|
||||
return fee
|
||||
}
|
||||
//
|
||||
// Fee estimation for SendFunds
|
||||
function EstimatedTransaction_ringCT_networkFee(
|
||||
nonZero_mixin_int
|
||||
)
|
||||
{
|
||||
return EstimatedTransaction_networkFee(
|
||||
2, // this might change - might select inputs
|
||||
nonZero_mixin_int,
|
||||
3, // dest + change + mymonero fee
|
||||
true // to be sure
|
||||
)
|
||||
}
|
||||
exports.EstimatedTransaction_ringCT_networkFee = EstimatedTransaction_ringCT_networkFee
|
||||
//
|
||||
function EstimatedTransaction_networkFee(
|
||||
numberOf_inputs,
|
||||
nonZero_mixin_int,
|
||||
numberOf_outputs,
|
||||
doesUseRingCT_orTrue
|
||||
)
|
||||
{
|
||||
const doesUseRingCT = doesUseRingCT_orTrue === false ? false : true // default to true unless false
|
||||
const fee_per_kb_JSBigInt = monero_config.feePerKB_JSBigInt
|
||||
var estimated_txSize;
|
||||
if (doesUseRingCT) {
|
||||
estimated_txSize = EstimatedTransaction_ringCT_txSize(numberOf_inputs, nonZero_mixin_int, numberOf_outputs)
|
||||
} else {
|
||||
estimated_txSize = EstimatedTransaction_preRingCT_txSize(numberOf_inputs, nonZero_mixin_int)
|
||||
}
|
||||
const fee_multiplier = 1 // TODO: expose this
|
||||
const estimated_fee = calculate_fee(fee_per_kb_JSBigInt, estimated_txSize, fee_multiplier)
|
||||
//
|
||||
return estimated_fee
|
||||
}
|
||||
exports.EstimatedTransaction_networkFee = EstimatedTransaction_networkFee
|
||||
//
|
||||
function EstimatedTransaction_preRingCT_txSize(
|
||||
numberOf_inputs,
|
||||
nonZero_mixin_int
|
||||
)
|
||||
{
|
||||
const numberOf_fakeOuts = nonZero_mixin_int
|
||||
const size = numberOf_inputs * (numberOf_fakeOuts + 1) * APPROXIMATE_INPUT_BYTES //
|
||||
//
|
||||
return size
|
||||
}
|
||||
function EstimatedTransaction_ringCT_txSize(
|
||||
numberOf_inputs,
|
||||
mixin_int,
|
||||
numberOf_outputs
|
||||
)
|
||||
{
|
||||
var size = 0;
|
||||
// tx prefix
|
||||
// first few bytes
|
||||
size += 1 + 6;
|
||||
size += numberOf_inputs * (1+6+(mixin_int+1)*3+32); // original implementation is *2+32 but luigi1111 said change 2 to 3
|
||||
// vout
|
||||
size += numberOf_outputs * (6+32);
|
||||
// extra
|
||||
size += 40;
|
||||
// rct signatures
|
||||
// type
|
||||
size += 1;
|
||||
// rangeSigs
|
||||
size += (2*64*32+32+64*32) * numberOf_outputs;
|
||||
// MGs
|
||||
size += numberOf_inputs * (32 * (mixin_int+1) + 32);
|
||||
// mixRing - not serialized, can be reconstructed
|
||||
/* size += 2 * 32 * (mixin_int+1) * numberOf_inputs; */
|
||||
// pseudoOuts
|
||||
size += 32 * numberOf_inputs;
|
||||
// ecdhInfo
|
||||
size += 2 * 32 * numberOf_outputs;
|
||||
// outPk - only commitment is saved
|
||||
size += 32 * numberOf_outputs;
|
||||
// txnFee
|
||||
size += 4;
|
||||
// const logStr = `estimated rct tx size for ${numberOf_inputs} at mixin ${mixin_int} and ${numberOf_outputs} : ${size} (${((32 * numberOf_inputs/*+1*/) + 2 * 32 * (mixin_int+1) * numberOf_inputs + 32 * numberOf_outputs)}) saved)`
|
||||
// console.log(logStr)
|
||||
|
||||
return size;
|
||||
}
|
||||
//
|
||||
//
|
||||
// Actually sending funds
|
||||
//
|
||||
function SendFunds(
|
||||
isRingCT,
|
||||
target_address, // currency-ready wallet address, but not an OA address (resolve before calling)
|
||||
amount, // number
|
||||
wallet__public_address,
|
||||
wallet__private_keys,
|
||||
wallet__public_keys,
|
||||
hostedMoneroAPIClient,
|
||||
mixin,
|
||||
payment_id,
|
||||
success_fn,
|
||||
// success_fn: (
|
||||
// moneroReady_targetDescription_address?,
|
||||
// sentAmount?,
|
||||
// final__payment_id?,
|
||||
// tx_hash?,
|
||||
// tx_fee?
|
||||
// )
|
||||
failWithErr_fn
|
||||
// failWithErr_fn: (
|
||||
// err
|
||||
// )
|
||||
)
|
||||
{
|
||||
// arg sanitization
|
||||
mixin = parseInt(mixin)
|
||||
//
|
||||
// some callback trampoline function declarations…
|
||||
function __trampolineFor_success(
|
||||
moneroReady_targetDescription_address,
|
||||
sentAmount,
|
||||
final__payment_id,
|
||||
tx_hash,
|
||||
tx_fee
|
||||
)
|
||||
{
|
||||
success_fn(
|
||||
moneroReady_targetDescription_address,
|
||||
sentAmount,
|
||||
final__payment_id,
|
||||
tx_hash,
|
||||
tx_fee
|
||||
)
|
||||
}
|
||||
function __trampolineFor_err_withErr(err)
|
||||
{
|
||||
failWithErr_fn(err)
|
||||
}
|
||||
function __trampolineFor_err_withStr(errStr)
|
||||
{
|
||||
const err = new Error(errStr)
|
||||
console.error(errStr)
|
||||
failWithErr_fn(err)
|
||||
}
|
||||
// status: preparing to send funds…
|
||||
//
|
||||
// parse & normalize the target descriptions by mapping them to Monero addresses & amounts
|
||||
const targetDescription =
|
||||
{
|
||||
address: target_address,
|
||||
amount: amount
|
||||
}
|
||||
new_moneroReadyTargetDescriptions_fromTargetDescriptions(
|
||||
[ targetDescription ], // requires a list of descriptions - but SendFunds was
|
||||
// not written with multiple target support as MyMonero does not yet support it
|
||||
function(err, moneroReady_targetDescriptions)
|
||||
{
|
||||
if (err) {
|
||||
__trampolineFor_err_withErr(err)
|
||||
return
|
||||
}
|
||||
const invalidOrZeroDestination_errStr = "You need to enter a valid destination"
|
||||
if (moneroReady_targetDescriptions.length === 0) {
|
||||
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr)
|
||||
return
|
||||
}
|
||||
const moneroReady_targetDescription = moneroReady_targetDescriptions[0]
|
||||
if (moneroReady_targetDescription === null || typeof moneroReady_targetDescription === 'undefined') {
|
||||
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr)
|
||||
return
|
||||
}
|
||||
_proceedTo_prepareToSendFundsTo_moneroReady_targetDescription(moneroReady_targetDescription)
|
||||
}
|
||||
)
|
||||
function _proceedTo_prepareToSendFundsTo_moneroReady_targetDescription(moneroReady_targetDescription)
|
||||
{
|
||||
var moneroReady_targetDescription_address = moneroReady_targetDescription.address
|
||||
var moneroReady_targetDescription_amount = moneroReady_targetDescription.amount
|
||||
//
|
||||
var totalAmountWithoutFee_JSBigInt = (new JSBigInt(0)).add(moneroReady_targetDescription_amount)
|
||||
console.log("💬 Total to send, before fee: " + monero_utils.formatMoney(totalAmountWithoutFee_JSBigInt));
|
||||
if (totalAmountWithoutFee_JSBigInt.compare(0) <= 0) {
|
||||
const errStr = "The amount you've entered is too low"
|
||||
__trampolineFor_err_withStr(errStr)
|
||||
return
|
||||
}
|
||||
//
|
||||
// Derive/finalize some values…
|
||||
var final__payment_id = payment_id
|
||||
var final__pid_encrypt = false // we don't want to encrypt payment ID unless we find an integrated one
|
||||
var address__decode_result;
|
||||
try {
|
||||
address__decode_result = monero_utils.decode_address(moneroReady_targetDescription_address)
|
||||
} catch (e) {
|
||||
__trampolineFor_err_withStr(typeof e === 'string' ? e : e.toString())
|
||||
return
|
||||
}
|
||||
if (address__decode_result.intPaymentId && payment_id) {
|
||||
const errStr = "Payment ID field must be blank when using an Integrated Address"
|
||||
__trampolineFor_err_withStr(errStr)
|
||||
return
|
||||
}
|
||||
if (address__decode_result.intPaymentId) {
|
||||
final__payment_id = address__decode_result.intPaymentId
|
||||
final__pid_encrypt = true // we do want to encrypt if using an integrated address
|
||||
} else if (monero_paymentID_utils.IsValidPaymentIDOrNoPaymentID(final__payment_id) === false) {
|
||||
const errStr = "The payment ID you've entered is not valid"
|
||||
__trampolineFor_err_withStr(errStr)
|
||||
return
|
||||
}
|
||||
//
|
||||
_proceedTo_getUnspentOutsUsableForMixin(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt
|
||||
)
|
||||
}
|
||||
function _proceedTo_getUnspentOutsUsableForMixin(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id, // non-existent or valid
|
||||
final__pid_encrypt // true or false
|
||||
)
|
||||
{
|
||||
hostedMoneroAPIClient.UnspentOuts(
|
||||
wallet__public_address,
|
||||
wallet__private_keys.view,
|
||||
wallet__public_keys.spend,
|
||||
wallet__private_keys.spend,
|
||||
mixin,
|
||||
function(
|
||||
err,
|
||||
unspentOuts,
|
||||
unusedOuts
|
||||
)
|
||||
{
|
||||
if (err) {
|
||||
__trampolineFor_err_withErr(err)
|
||||
return
|
||||
}
|
||||
_proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
unusedOuts
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
function _proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
unusedOuts
|
||||
)
|
||||
{
|
||||
// status: constructing transaction…
|
||||
const feePerKB_JSBigInt = monero_config.feePerKB_JSBigInt
|
||||
// Transaction will need at least 1KB fee (13KB for RingCT)
|
||||
const network_minimumTXSize_kb = isRingCT ? 13 : 1
|
||||
var network_minimumFee = feePerKB_JSBigInt.multiply(network_minimumTXSize_kb)
|
||||
// ^-- now we're going to try using this minimum fee but the codepath has to be able to be re-entered if we find after constructing the whole tx that it is larger in kb than the minimum fee we're attempting to send it off with
|
||||
__reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
unusedOuts,
|
||||
network_minimumFee
|
||||
)
|
||||
}
|
||||
function __reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
unusedOuts,
|
||||
passedIn_attemptAt_network_minimumFee
|
||||
)
|
||||
{ // Now we need to establish some values for balance validation and to construct the transaction
|
||||
console.log("Entered re-enterable tx building codepath…", unusedOuts)
|
||||
var attemptAt_network_minimumFee = passedIn_attemptAt_network_minimumFee // we may change this if isRingCT
|
||||
// const hostingService_chargeAmount = hostedMoneroAPIClient.HostingServiceChargeFor_transactionWithNetworkFee(attemptAt_network_minimumFee)
|
||||
var totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(attemptAt_network_minimumFee)/*.add(hostingService_chargeAmount) NOTE service fee removed for now */
|
||||
const usableOutputsAndAmounts = _outputsAndAmountToUseForMixin(
|
||||
totalAmountIncludingFees,
|
||||
unusedOuts,
|
||||
isRingCT
|
||||
)
|
||||
// v-- now if RingCT compute fee as closely as possible before hand
|
||||
var usingOuts = usableOutputsAndAmounts.usingOuts
|
||||
var usingOutsAmount = usableOutputsAndAmounts.usingOutsAmount
|
||||
var remaining_unusedOuts = usableOutputsAndAmounts.remaining_unusedOuts // this is a copy of the pre-mutation usingOuts
|
||||
if (isRingCT) {
|
||||
if (usingOuts.length > 1) {
|
||||
var newNeededFee = new JSBigInt(Math.ceil(monero_utils.estimateRctSize(usingOuts.length, mixin, 2) / 1024)).multiply(monero_config.feePerKB_JSBigInt)
|
||||
totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(newNeededFee)
|
||||
// add outputs 1 at a time till we either have them all or can meet the fee
|
||||
while (usingOutsAmount.compare(totalAmountIncludingFees) < 0 && remaining_unusedOuts.length > 0) {
|
||||
const out = _popAndReturnRandomElementFromList(remaining_unusedOuts)
|
||||
usingOuts.push(out)
|
||||
usingOutsAmount = usingOutsAmount.add(out.amount)
|
||||
console.log("Using output: " + monero_utils.formatMoney(out.amount) + " - " + JSON.stringify(out))
|
||||
newNeededFee = new JSBigInt(
|
||||
Math.ceil(monero_utils.estimateRctSize(usingOuts.length, mixin, 2) / 1024)
|
||||
).multiply(monero_config.feePerKB_JSBigInt)
|
||||
totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(newNeededFee)
|
||||
}
|
||||
console.log("New fee: " + monero_utils.formatMoneySymbol(newNeededFee) + " for " + usingOuts.length + " inputs")
|
||||
attemptAt_network_minimumFee = newNeededFee
|
||||
}
|
||||
}
|
||||
console.log("~ Balance required: " + monero_utils.formatMoneySymbol(totalAmountIncludingFees))
|
||||
// Now we can validate available balance with usingOutsAmount (TODO? maybe this check can be done before selecting outputs?)
|
||||
const usingOutsAmount_comparedTo_totalAmount = usingOutsAmount.compare(totalAmountIncludingFees)
|
||||
if (usingOutsAmount_comparedTo_totalAmount < 0) {
|
||||
__trampolineFor_err_withStr(
|
||||
"Not enough spendable outputs / balance too low (have: "
|
||||
+ monero_utils.formatMoneyFull(usingOutsAmount)
|
||||
+ " need: "
|
||||
+ monero_utils.formatMoneyFull(totalAmountIncludingFees)
|
||||
+ ")"
|
||||
)
|
||||
return
|
||||
}
|
||||
// Now we can put together the list of fund transfers we need to perform
|
||||
const fundTransferDescriptions = [] // to build…
|
||||
// I. the actual transaction the user is asking to do
|
||||
fundTransferDescriptions.push({
|
||||
address: moneroReady_targetDescription_address,
|
||||
amount: totalAmountWithoutFee_JSBigInt
|
||||
})
|
||||
// II. the fee that the hosting provider charges
|
||||
// NOTE: The fee has been removed for RCT until a later date
|
||||
// fundTransferDescriptions.push({
|
||||
// address: hostedMoneroAPIClient.HostingServiceFeeDepositAddress(),
|
||||
// amount: hostingService_chargeAmount
|
||||
// })
|
||||
// III. some amount of the total outputs will likely need to be returned to the user as "change":
|
||||
if (usingOutsAmount_comparedTo_totalAmount > 0) {
|
||||
var changeAmount = usingOutsAmount.subtract(totalAmountIncludingFees)
|
||||
console.log("changeAmount" , changeAmount)
|
||||
if (isRingCT) { // for RCT we don't presently care about dustiness so add entire change amount
|
||||
console.log("Sending change of " + monero_utils.formatMoneySymbol(changeAmount) + " to " + wallet__public_address)
|
||||
fundTransferDescriptions.push({
|
||||
address: wallet__public_address,
|
||||
amount: changeAmount
|
||||
})
|
||||
} else { // pre-ringct
|
||||
// do not give ourselves change < dust threshold
|
||||
var changeAmountDivRem = changeAmount.divRem(monero_config.dustThreshold)
|
||||
console.log("💬 changeAmountDivRem", changeAmountDivRem)
|
||||
if (changeAmountDivRem[1].toString() !== "0") {
|
||||
// miners will add dusty change to fee
|
||||
console.log("💬 Miners will add change of " + monero_utils.formatMoneyFullSymbol(changeAmountDivRem[1]) + " to transaction fee (below dust threshold)")
|
||||
}
|
||||
if (changeAmountDivRem[0].toString() !== "0") {
|
||||
// send non-dusty change to our address
|
||||
var usableChange = changeAmountDivRem[0].multiply(monero_config.dustThreshold)
|
||||
console.log("💬 Sending change of " + monero_utils.formatMoneySymbol(usableChange) + " to " + wallet__public_address)
|
||||
fundTransferDescriptions.push({
|
||||
address: wallet__public_address,
|
||||
amount: usableChange
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (usingOutsAmount_comparedTo_totalAmount == 0) {
|
||||
if (isRingCT) { // then create random destination to keep 2 outputs always in case of 0 change
|
||||
var fakeAddress = monero_utils.create_address(monero_utils.random_scalar()).public_addr
|
||||
console.log("Sending 0 XMR to a fake address to keep tx uniform (no change exists): " + fakeAddress)
|
||||
fundTransferDescriptions.push({
|
||||
address: fakeAddress,
|
||||
amount: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log("fundTransferDescriptions so far", fundTransferDescriptions)
|
||||
if (mixin < 0 || isNaN(mixin)) {
|
||||
__trampolineFor_err_withStr("Invalid mixin")
|
||||
return
|
||||
}
|
||||
if (mixin > 0) { // first, grab RandomOuts, then enter __createTx
|
||||
hostedMoneroAPIClient.RandomOuts(
|
||||
usingOuts,
|
||||
mixin,
|
||||
function(err, amount_outs)
|
||||
{
|
||||
if (err) {
|
||||
__trampolineFor_err_withErr(err)
|
||||
return
|
||||
}
|
||||
__createTxAndAttemptToSend(amount_outs)
|
||||
}
|
||||
)
|
||||
return
|
||||
} else { // mixin === 0: -- PSNOTE: is that even allowed?
|
||||
__createTxAndAttemptToSend()
|
||||
}
|
||||
function __createTxAndAttemptToSend(mix_outs)
|
||||
{
|
||||
var signedTx;
|
||||
try {
|
||||
console.log('Destinations: ')
|
||||
monero_utils.printDsts(fundTransferDescriptions)
|
||||
//
|
||||
var realDestViewKey // need to get viewkey for encrypting here, because of splitting and sorting
|
||||
if (final__pid_encrypt) {
|
||||
realDestViewKey = monero_utils.decode_address(moneroReady_targetDescription_address).view
|
||||
console.log("got realDestViewKey" , realDestViewKey)
|
||||
}
|
||||
var splitDestinations = monero_utils.decompose_tx_destinations(
|
||||
fundTransferDescriptions,
|
||||
isRingCT
|
||||
)
|
||||
console.log('Decomposed destinations:')
|
||||
monero_utils.printDsts(splitDestinations)
|
||||
//
|
||||
signedTx = monero_utils.create_transaction(
|
||||
wallet__public_keys,
|
||||
wallet__private_keys,
|
||||
splitDestinations,
|
||||
usingOuts,
|
||||
mix_outs,
|
||||
mixin,
|
||||
attemptAt_network_minimumFee,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
realDestViewKey,
|
||||
0,
|
||||
isRingCT
|
||||
)
|
||||
} catch (e) {
|
||||
var errStr;
|
||||
if (e) {
|
||||
errStr = typeof e == "string" ? e : e.toString()
|
||||
} else {
|
||||
errStr = "Failed to create transaction with unknown error."
|
||||
}
|
||||
__trampolineFor_err_withStr(errStr)
|
||||
return
|
||||
}
|
||||
console.log("signed tx: ", JSON.stringify(signedTx))
|
||||
//
|
||||
var serialized_signedTx;
|
||||
var tx_hash;
|
||||
if (signedTx.version === 1) {
|
||||
serialized_signedTx = monero_utils.serialize_tx(signedTx)
|
||||
tx_hash = monero_utils.cn_fast_hash(serialized_signedTx)
|
||||
} else {
|
||||
const raw_tx_and_hash = monero_utils.serialize_rct_tx_with_hash(signedTx)
|
||||
serialized_signedTx = raw_tx_and_hash.raw
|
||||
tx_hash = raw_tx_and_hash.hash
|
||||
}
|
||||
console.log("tx serialized: " + serialized_signedTx)
|
||||
console.log("Tx hash: " + tx_hash)
|
||||
//
|
||||
// work out per-kb fee for transaction and verify that it's enough
|
||||
var txBlobBytes = serialized_signedTx.length / 2
|
||||
var numKB = Math.floor(txBlobBytes / 1024)
|
||||
if (txBlobBytes % 1024) {
|
||||
numKB++
|
||||
}
|
||||
console.log(txBlobBytes + " bytes <= " + numKB + " KB (current fee: " + monero_utils.formatMoneyFull(attemptAt_network_minimumFee) + ")")
|
||||
const feeActuallyNeededByNetwork = monero_config.feePerKB_JSBigInt.multiply(numKB)
|
||||
// if we need a higher fee
|
||||
if (feeActuallyNeededByNetwork.compare(attemptAt_network_minimumFee) > 0) {
|
||||
console.log("💬 Need to reconstruct the tx with enough of a network fee. Previous fee: " + monero_utils.formatMoneyFull(attemptAt_network_minimumFee) + " New fee: " + monero_utils.formatMoneyFull(feeActuallyNeededByNetwork))
|
||||
__reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
|
||||
moneroReady_targetDescription_address,
|
||||
totalAmountWithoutFee_JSBigInt,
|
||||
final__payment_id,
|
||||
final__pid_encrypt,
|
||||
unusedOuts,
|
||||
feeActuallyNeededByNetwork // we are re-entering this codepath after changing this feeActuallyNeededByNetwork
|
||||
)
|
||||
//
|
||||
return
|
||||
}
|
||||
//
|
||||
// generated with correct per-kb fee
|
||||
const final_networkFee = attemptAt_network_minimumFee // just to make things clear
|
||||
console.log("💬 Successful tx generation, submitting tx. Going with final_networkFee of ", monero_utils.formatMoney(final_networkFee))
|
||||
// status: submitting…
|
||||
hostedMoneroAPIClient.SubmitSerializedSignedTransaction(
|
||||
wallet__public_address,
|
||||
wallet__private_keys.view,
|
||||
serialized_signedTx,
|
||||
function(err)
|
||||
{
|
||||
if (err) {
|
||||
__trampolineFor_err_withStr("Something unexpected occurred when submitting your transaction:", err)
|
||||
return
|
||||
}
|
||||
const tx_fee = final_networkFee/*.add(hostingService_chargeAmount) NOTE: Service charge removed to reduce bloat for now */
|
||||
__trampolineFor_success(
|
||||
moneroReady_targetDescription_address,
|
||||
amount,
|
||||
final__payment_id,
|
||||
tx_hash,
|
||||
tx_fee
|
||||
) // 🎉
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.SendFunds = SendFunds
|
||||
//
|
||||
function new_moneroReadyTargetDescriptions_fromTargetDescriptions(
|
||||
targetDescriptions,
|
||||
fn
|
||||
) // fn: (err, moneroReady_targetDescriptions) -> Void
|
||||
{ // parse & normalize the target descriptions by mapping them to currency (Monero)-ready addresses & amounts
|
||||
// some pure function declarations for the map we'll do on targetDescriptions
|
||||
async.mapSeries(
|
||||
targetDescriptions,
|
||||
function(targetDescription, cb)
|
||||
{
|
||||
if (!targetDescription.address && !targetDescription.amount) { // PSNote: is this check rigorous enough?
|
||||
const errStr = "Please supply a target address and a target amount."
|
||||
const err = new Error(errStr)
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
const targetDescription_address = targetDescription.address
|
||||
const targetDescription_amount = "" + targetDescription.amount // we are converting it to a string here because parseMoney expects a string
|
||||
// now verify/parse address and amount
|
||||
if (monero_openalias_utils.DoesStringContainPeriodChar_excludingAsXMRAddress_qualifyingAsPossibleOAAddress(targetDescription_address) == true) {
|
||||
throw "You must resolve this OA address to a Monero address before calling SendFunds"
|
||||
}
|
||||
// otherwise this should be a normal, single Monero public address
|
||||
try {
|
||||
monero_utils.decode_address(targetDescription_address) // verify that the address is valid
|
||||
} catch (e) {
|
||||
const errStr = "Couldn't decode address " + targetDescription_address + ": " + e
|
||||
const err = new Error(errStr)
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
// amount:
|
||||
var moneroReady_amountToSend; // possibly need this ; here for the JS parser
|
||||
try {
|
||||
moneroReady_amountToSend = monero_utils.parseMoney(targetDescription_amount)
|
||||
} catch (e) {
|
||||
const errStr = "Couldn't parse amount " + targetDescription_amount + ": " + e
|
||||
const err = new Error(errStr)
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
cb(null, {
|
||||
address: targetDescription_address,
|
||||
amount: moneroReady_amountToSend
|
||||
})
|
||||
},
|
||||
function(err, moneroReady_targetDescriptions)
|
||||
{
|
||||
fn(err, moneroReady_targetDescriptions)
|
||||
}
|
||||
)
|
||||
}
|
||||
//
|
||||
function __randomIndex(list)
|
||||
{
|
||||
return Math.floor(Math.random() * list.length);
|
||||
}
|
||||
function _popAndReturnRandomElementFromList(list)
|
||||
{
|
||||
var idx = __randomIndex(list)
|
||||
var val = list[idx]
|
||||
list.splice(idx, 1)
|
||||
//
|
||||
return val
|
||||
}
|
||||
function _outputsAndAmountToUseForMixin(
|
||||
target_amount,
|
||||
unusedOuts,
|
||||
isRingCT
|
||||
)
|
||||
{
|
||||
console.log("Selecting outputs to use. target: " + monero_utils.formatMoney(target_amount))
|
||||
var toFinalize_usingOutsAmount = new JSBigInt(0)
|
||||
const toFinalize_usingOuts = []
|
||||
const remaining_unusedOuts = unusedOuts.slice() // take copy so as to prevent issue if we must re-enter tx building fn if fee too low after building
|
||||
while (toFinalize_usingOutsAmount.compare(target_amount) < 0 && remaining_unusedOuts.length > 0) {
|
||||
var out = _popAndReturnRandomElementFromList(remaining_unusedOuts)
|
||||
if (!isRingCT && out.rct) { // out.rct is set by the server
|
||||
continue; // skip rct outputs if not creating rct tx
|
||||
}
|
||||
const out_amount = out.amount
|
||||
toFinalize_usingOuts.push(out)
|
||||
toFinalize_usingOutsAmount = toFinalize_usingOutsAmount.add(out_amount)
|
||||
console.log("Using output: " + monero_utils.formatMoney(out_amount) + " - " + JSON.stringify(out))
|
||||
}
|
||||
return {
|
||||
usingOuts: toFinalize_usingOuts,
|
||||
usingOutsAmount: toFinalize_usingOutsAmount,
|
||||
remaining_unusedOuts: remaining_unusedOuts
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_config = require('./monero_config')
|
||||
const monero_utils = require('./monero_cryptonote_utils_instance')
|
||||
//
|
||||
function IsTransactionConfirmed(tx, blockchain_height)
|
||||
{
|
||||
return (blockchain_height - tx.height) > monero_config.txMinConfirms
|
||||
}
|
||||
exports.IsTransactionConfirmed = IsTransactionConfirmed
|
||||
//
|
||||
function IsTransactionUnlocked(tx, blockchain_height)
|
||||
{
|
||||
return monero_utils.is_tx_unlocked(tx.unlock_time || 0, blockchain_height)
|
||||
}
|
||||
exports.IsTransactionUnlocked = IsTransactionUnlocked
|
||||
//
|
||||
function TransactionLockedReason(tx, blockchain_height)
|
||||
{
|
||||
return monero_utils.tx_locked_reason(tx.unlock_time || 0, blockchain_height)
|
||||
}
|
||||
exports.TransactionLockedReason = TransactionLockedReason
|
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const monero_wallet_utils = require('./monero_wallet_utils')
|
||||
//
|
||||
function MnemonicWordsetNameWithLocale(currentLocale) // e.g. 'en'
|
||||
{
|
||||
const mnemonicWordsetNamesByAppLocaleNames = monero_wallet_utils.MnemonicWordsetNamesByAppLocaleNames
|
||||
if (currentLocale.indexOf('en') === 0) {
|
||||
return mnemonicWordsetNamesByAppLocaleNames.English
|
||||
} else if (currentLocale.indexOf('es') === 0) {
|
||||
return mnemonicWordsetNamesByAppLocaleNames.Spanish
|
||||
} else if (currentLocale.indexOf('pt') === 0) {
|
||||
return mnemonicWordsetNamesByAppLocaleNames.Portuguese
|
||||
} else if (currentLocale.indexOf('ja') === 0) {
|
||||
return mnemonicWordsetNamesByAppLocaleNames.Japanese
|
||||
}
|
||||
return monero_wallet_utils.DefaultWalletMnemonicWordsetName // which would be .English
|
||||
}
|
||||
exports.MnemonicWordsetNameWithLocale = MnemonicWordsetNameWithLocale
|
@ -0,0 +1,289 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
|
||||
"use strict"
|
||||
//
|
||||
const mnemonic = require('../cryptonote_utils/mnemonic')
|
||||
const monero_utils = require('./monero_cryptonote_utils_instance')
|
||||
const monero_config = require('./monero_config')
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Mnemonic wordset utilities - Exposing available names
|
||||
//
|
||||
const wordsetNamesByWordsetName = {}
|
||||
const allWordsetNames = Object.keys(mnemonic.mn_words)
|
||||
for (let wordsetName of allWordsetNames) {
|
||||
wordsetNamesByWordsetName[wordsetName] = wordsetName
|
||||
}
|
||||
exports.WordsetNamesByWordsetName = wordsetNamesByWordsetName
|
||||
exports.AllWordsetNames = allWordsetNames
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Mnemonic wordset utilities - Wordset name detection by mnemonic contents
|
||||
//
|
||||
function WordsetNameAccordingToMnemonicString(mnemonicString) // throws
|
||||
{
|
||||
const mnemonicString_words = mnemonicString.split(' ')
|
||||
if (mnemonicString_words.length == 0) {
|
||||
throw "Invalid mnemonic"
|
||||
}
|
||||
var wholeMnemonicSuspectedAsWordsetNamed = null // to derive
|
||||
for (let mnemonicString_word of mnemonicString_words) {
|
||||
var thisWordIsInWordsetNamed = null // to derive
|
||||
for (let wordsetName of allWordsetNames) {
|
||||
if (wordsetName === 'electrum') {
|
||||
continue // skip because it conflicts with 'english'
|
||||
}
|
||||
const wordsetWords = mnemonic.mn_words[wordsetName].words
|
||||
if (wordsetWords.indexOf(mnemonicString_word) !== -1) {
|
||||
thisWordIsInWordsetNamed = wordsetName
|
||||
break // done looking
|
||||
}
|
||||
// haven't found it yet
|
||||
}
|
||||
if (thisWordIsInWordsetNamed === null) { // didn't find this word in any of the mnemonic wordsets
|
||||
throw "Unrecognized mnemonic language"
|
||||
}
|
||||
if (wholeMnemonicSuspectedAsWordsetNamed === null) { // haven't found it yet
|
||||
wholeMnemonicSuspectedAsWordsetNamed = thisWordIsInWordsetNamed
|
||||
} else if (thisWordIsInWordsetNamed !== wholeMnemonicSuspectedAsWordsetNamed) {
|
||||
throw "Ambiguous mnemonic language" // multiple wordset names detected
|
||||
} else {
|
||||
// nothing to do but keep verifying the rest of the words that it's the same suspsected wordset
|
||||
}
|
||||
}
|
||||
if (wholeMnemonicSuspectedAsWordsetNamed === null) { // this might be redundant, but for logical rigor……
|
||||
throw "Unrecognized mnemonic language"
|
||||
}
|
||||
//
|
||||
return wholeMnemonicSuspectedAsWordsetNamed
|
||||
}
|
||||
exports.WordsetNameAccordingToMnemonicString = WordsetNameAccordingToMnemonicString
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Mnemonic wordset utilities - By locale
|
||||
//
|
||||
const mnemonicWordsetNamesByAppLocaleNames =
|
||||
{
|
||||
English: "english",
|
||||
Japanese: "japanese",
|
||||
Spanish: "spanish",
|
||||
Portuguese: "portuguese"
|
||||
// NOTE: no support for 'electrum' wordset here
|
||||
}
|
||||
exports.MnemonicWordsetNamesByAppLocaleNames = mnemonicWordsetNamesByAppLocaleNames
|
||||
//
|
||||
exports.DefaultWalletMnemonicWordsetName = mnemonicWordsetNamesByAppLocaleNames.English
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Wallet creation:
|
||||
//
|
||||
function NewlyCreatedWallet(mnemonic_wordsetName)
|
||||
{
|
||||
const seed = monero_utils.random_scalar() // to generate a 32-byte (25-word) but reduced seed
|
||||
const mnemonicString = mnemonic.mn_encode(seed, mnemonic_wordsetName)
|
||||
const keys = monero_utils.create_address(seed)
|
||||
//
|
||||
return {
|
||||
seed: seed,
|
||||
mnemonicString: mnemonicString,
|
||||
keys: keys
|
||||
}
|
||||
}
|
||||
exports.NewlyCreatedWallet = NewlyCreatedWallet
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Wallet login:
|
||||
//
|
||||
function MnemonicStringFromSeed(account_seed, mnemonic_wordsetName)
|
||||
{
|
||||
const mnemonicString = mnemonic.mn_encode(account_seed, mnemonic_wordsetName)
|
||||
//
|
||||
return mnemonicString
|
||||
}
|
||||
exports.MnemonicStringFromSeed = MnemonicStringFromSeed
|
||||
//
|
||||
function SeedAndKeysFromMnemonic_sync(mnemonicString, mnemonic_wordsetName)
|
||||
{ // -> {err_str?, seed?, keys?}
|
||||
mnemonicString = mnemonicString.toLowerCase() || ""
|
||||
try {
|
||||
var seed = null
|
||||
var keys = null
|
||||
switch (mnemonic_wordsetName) {
|
||||
case 'english':
|
||||
try {
|
||||
seed = mnemonic.mn_decode(mnemonicString)
|
||||
} catch (e) {
|
||||
// Try decoding as an electrum seed, on failure throw the original exception
|
||||
try {
|
||||
seed = mnemonic.mn_decode(mnemonicString, "electrum")
|
||||
} catch (ee) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
seed = mnemonic.mn_decode(mnemonicString, mnemonic_wordsetName)
|
||||
break
|
||||
}
|
||||
if (seed === null) {
|
||||
return { err_str: "Unable to derive seed", seed: null, keys: null }
|
||||
}
|
||||
keys = monero_utils.create_address(seed)
|
||||
if (keys === null) {
|
||||
return { err_str: "Unable to derive keys from seed", seed: seed, keys: null }
|
||||
}
|
||||
return { err_str: null, seed: seed, keys: keys }
|
||||
} catch (e) {
|
||||
console.error("Invalid mnemonic!")
|
||||
return { err_str: typeof e === 'string' ? e : ""+e, seed: null, keys: null }
|
||||
}
|
||||
}
|
||||
exports.SeedAndKeysFromMnemonic_sync = SeedAndKeysFromMnemonic_sync
|
||||
|
||||
function SeedAndKeysFromMnemonic(mnemonicString, mnemonic_wordsetName, fn) // made available via callback not because it's async but for convenience
|
||||
{ // fn: (err?, seed?, keys?)
|
||||
const payload = SeedAndKeysFromMnemonic_sync(mnemonicString, mnemonic_wordsetName)
|
||||
const err = payload.err_str ? new Error(payload.err_str) : null
|
||||
const seed = payload.seed
|
||||
const keys = payload.keys
|
||||
fn(err, seed, keys)
|
||||
}
|
||||
exports.SeedAndKeysFromMnemonic = SeedAndKeysFromMnemonic
|
||||
//
|
||||
function VerifiedComponentsForLogIn_sync(
|
||||
address,
|
||||
view_key,
|
||||
spend_key_orUndefinedForViewOnly,
|
||||
seed_orUndefined,
|
||||
wasAGeneratedWallet
|
||||
)
|
||||
{
|
||||
var spend_key
|
||||
if (typeof spend_key_orUndefinedForViewOnly === 'undefined' && (typeof seed_orUndefined === 'undefined' || seed_orUndefined === '') && wasAGeneratedWallet === false) {
|
||||
spend_key = ''
|
||||
} else {
|
||||
spend_key = spend_key_orUndefinedForViewOnly
|
||||
}
|
||||
const isInViewOnlyMode = (spend_key === '')
|
||||
if (!view_key || view_key.length !== 64 || (isInViewOnlyMode ? false : spend_key.length !== 64)) {
|
||||
return { err_str: "invalid secret key length" }
|
||||
}
|
||||
if (!monero_utils.valid_hex(view_key) || (isInViewOnlyMode ? false : !monero_utils.valid_hex(spend_key))) {
|
||||
return { err_str: "invalid hex formatting" }
|
||||
}
|
||||
var public_keys;
|
||||
try {
|
||||
public_keys = monero_utils.decode_address(address)
|
||||
} catch (e) {
|
||||
return { err_str: "invalid address" }
|
||||
}
|
||||
var expected_view_pub;
|
||||
try {
|
||||
expected_view_pub = monero_utils.sec_key_to_pub(view_key)
|
||||
} catch (e) {
|
||||
return { err_str: "invalid view key" }
|
||||
}
|
||||
var expected_spend_pub
|
||||
if (spend_key.length === 64) {
|
||||
try {
|
||||
expected_spend_pub = monero_utils.sec_key_to_pub(spend_key)
|
||||
} catch (e) {
|
||||
return { err_str: "invalid spend key" }
|
||||
}
|
||||
}
|
||||
if (public_keys.view !== expected_view_pub) {
|
||||
return { err_str: "invalid view key" }
|
||||
}
|
||||
if (!isInViewOnlyMode && (public_keys.spend !== expected_spend_pub)) {
|
||||
return { err_str: "invalid spend key" }
|
||||
}
|
||||
const private_keys =
|
||||
{
|
||||
view: view_key,
|
||||
spend: spend_key
|
||||
}
|
||||
var account_seed = null; // default
|
||||
if (typeof seed_orUndefined !== 'undefined' && seed_orUndefined && seed_orUndefined.length != 0) {
|
||||
var expected_account;
|
||||
try {
|
||||
expected_account = monero_utils.create_address(seed_orUndefined)
|
||||
} catch (e) {
|
||||
return { err_str: "invalid seed" }
|
||||
}
|
||||
if (expected_account.view.sec !== view_key ||
|
||||
expected_account.spend.sec !== spend_key ||
|
||||
expected_account.public_addr !== address) {
|
||||
return { err_str: "invalid seed" }
|
||||
}
|
||||
account_seed = seed_orUndefined
|
||||
}
|
||||
const payload =
|
||||
{
|
||||
err_str: null, // err
|
||||
address: address,
|
||||
account_seed: account_seed !== "" ? account_seed : null,
|
||||
public_keys: public_keys,
|
||||
private_keys: private_keys,
|
||||
isInViewOnlyMode: isInViewOnlyMode
|
||||
}
|
||||
return payload
|
||||
}
|
||||
exports.VerifiedComponentsForLogIn_sync = VerifiedComponentsForLogIn_sync
|
||||
//
|
||||
function VerifiedComponentsForLogIn(
|
||||
address,
|
||||
view_key,
|
||||
spend_key_orUndefinedForViewOnly,
|
||||
seed_orUndefined,
|
||||
wasAGeneratedWallet,
|
||||
fn
|
||||
)
|
||||
{ // fn: (err?, address, account_seed, public_keys, private_keys, isInViewOnlyMode) -> Void
|
||||
const payload = VerifiedComponentsForLogIn_sync(
|
||||
address,
|
||||
view_key,
|
||||
spend_key_orUndefinedForViewOnly,
|
||||
seed_orUndefined,
|
||||
wasAGeneratedWallet
|
||||
)
|
||||
fn(
|
||||
payload.err_str ? new Error(payload.err_str) : null,
|
||||
payload.address,
|
||||
payload.account_seed,
|
||||
payload.public_keys,
|
||||
payload.private_keys,
|
||||
payload.isInViewOnlyMode
|
||||
)
|
||||
}
|
||||
exports.VerifiedComponentsForLogIn = VerifiedComponentsForLogIn
|
@ -0,0 +1,277 @@
|
||||
// Copyright (c) 2014-2017, MyMonero.com
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
"use strict"
|
||||
//
|
||||
const JSBigInt = require('../cryptonote_utils/biginteger').BigInteger
|
||||
const monero_utils = require('../monero_utils/monero_cryptonote_utils_instance')
|
||||
const monero_keyImage_cache_utils = require('../monero_utils/monero_keyImage_cache_utils')
|
||||
//
|
||||
function Parsed_AddressInfo__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
) // -> returnValuesByKey
|
||||
{
|
||||
const total_received = new JSBigInt(data.total_received || 0);
|
||||
const locked_balance = new JSBigInt(data.locked_funds || 0);
|
||||
var total_sent = new JSBigInt(data.total_sent || 0) // will be modified in place
|
||||
//
|
||||
const account_scanned_tx_height = data.scanned_height || 0;
|
||||
const account_scanned_block_height = data.scanned_block_height || 0;
|
||||
const account_scan_start_height = data.start_height || 0;
|
||||
const transaction_height = data.transaction_height || 0;
|
||||
const blockchain_height = data.blockchain_height || 0;
|
||||
const spent_outputs = data.spent_outputs || []
|
||||
//
|
||||
for (let spent_output of spent_outputs) {
|
||||
var key_image = monero_keyImage_cache_utils.Lazy_KeyImage(
|
||||
spent_output.tx_pub_key,
|
||||
spent_output.out_index,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
if (spent_output.key_image !== key_image) {
|
||||
// console.log('💬 Output used as mixin (' + spent_output.key_image + '/' + key_image + ')')
|
||||
total_sent = new JSBigInt(total_sent).subtract(spent_output.amount)
|
||||
}
|
||||
}
|
||||
const returnValuesByKey =
|
||||
{
|
||||
total_received_String: total_received ? total_received.toString() : null,
|
||||
locked_balance_String: locked_balance ? locked_balance.toString() : null,
|
||||
total_sent_String: total_sent ? total_sent.toString() : null,
|
||||
// ^serialized JSBigInt
|
||||
spent_outputs: spent_outputs,
|
||||
account_scanned_tx_height: account_scanned_tx_height,
|
||||
account_scanned_block_height: account_scanned_block_height,
|
||||
account_scan_start_height: account_scan_start_height,
|
||||
transaction_height: transaction_height,
|
||||
blockchain_height: blockchain_height
|
||||
}
|
||||
return returnValuesByKey
|
||||
}
|
||||
function Parsed_AddressInfo(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private,
|
||||
fn // (err?, returnValuesByKey) -> Void
|
||||
)
|
||||
{
|
||||
const returnValuesByKey = Parsed_AddressInfo__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
fn(null, returnValuesByKey)
|
||||
}
|
||||
exports.Parsed_AddressInfo = Parsed_AddressInfo
|
||||
exports.Parsed_AddressInfo__sync = Parsed_AddressInfo__sync
|
||||
//
|
||||
function Parsed_AddressTransactions(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private,
|
||||
fn // (err?, returnValuesByKey) -> Void
|
||||
)
|
||||
{
|
||||
const returnValuesByKey = Parsed_AddressTransactions__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
fn(null, returnValuesByKey)
|
||||
}
|
||||
function Parsed_AddressTransactions__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
{
|
||||
const account_scanned_height = data.scanned_height || 0
|
||||
const account_scanned_block_height = data.scanned_block_height || 0
|
||||
const account_scan_start_height = data.start_height || 0
|
||||
const transaction_height = data.transaction_height || 0
|
||||
const blockchain_height = data.blockchain_height || 0
|
||||
//
|
||||
const transactions = data.transactions || []
|
||||
//
|
||||
// TODO: rewrite this with more clarity if possible
|
||||
for (let i = 0; i < transactions.length; ++i) {
|
||||
if ((transactions[i].spent_outputs || []).length > 0) {
|
||||
for (var j = 0; j < transactions[i].spent_outputs.length; ++j) {
|
||||
var key_image = monero_keyImage_cache_utils.Lazy_KeyImage(
|
||||
transactions[i].spent_outputs[j].tx_pub_key,
|
||||
transactions[i].spent_outputs[j].out_index,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
if (transactions[i].spent_outputs[j].key_image !== key_image) {
|
||||
// console.log('Output used as mixin, ignoring (' + transactions[i].spent_outputs[j].key_image + '/' + key_image + ')')
|
||||
transactions[i].total_sent = new JSBigInt(transactions[i].total_sent).subtract(transactions[i].spent_outputs[j].amount).toString()
|
||||
transactions[i].spent_outputs.splice(j, 1)
|
||||
j--
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new JSBigInt(transactions[i].total_received || 0).add(transactions[i].total_sent || 0).compare(0) <= 0) {
|
||||
transactions.splice(i, 1)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
transactions[i].amount = new JSBigInt(transactions[i].total_received || 0).subtract(transactions[i].total_sent || 0).toString()
|
||||
transactions[i].approx_float_amount = parseFloat(monero_utils.formatMoney(transactions[i].amount))
|
||||
transactions[i].timestamp = transactions[i].timestamp
|
||||
}
|
||||
transactions.sort(function(a, b)
|
||||
{
|
||||
return b.id - a.id
|
||||
})
|
||||
// prepare transactions to be serialized
|
||||
for (let transaction of transactions) {
|
||||
transaction.amount = transaction.amount.toString() // JSBigInt -> String
|
||||
if (typeof transaction.total_sent !== 'undefined' && transaction.total_sent !== null) {
|
||||
transaction.total_sent = transaction.total_sent.toString()
|
||||
}
|
||||
}
|
||||
// on the other side, we convert transactions timestamp to Date obj
|
||||
const returnValuesByKey =
|
||||
{
|
||||
account_scanned_height: account_scanned_height,
|
||||
account_scanned_block_height: account_scanned_block_height,
|
||||
account_scan_start_height: account_scan_start_height,
|
||||
transaction_height: transaction_height,
|
||||
blockchain_height: blockchain_height,
|
||||
serialized_transactions: transactions
|
||||
}
|
||||
return returnValuesByKey
|
||||
}
|
||||
exports.Parsed_AddressTransactions = Parsed_AddressTransactions
|
||||
exports.Parsed_AddressTransactions__sync = Parsed_AddressTransactions__sync
|
||||
//
|
||||
function Parsed_UnspentOuts(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private,
|
||||
fn // (err?, returnValuesByKey)
|
||||
)
|
||||
{
|
||||
const returnValuesByKey = Parsed_UnspentOuts__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
fn(null, returnValuesByKey)
|
||||
}
|
||||
function Parsed_UnspentOuts__sync(
|
||||
data,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
{
|
||||
const data_outputs = data.outputs
|
||||
const finalized_unspentOutputs = data.outputs || [] // to finalize:
|
||||
for (var i = 0; i < finalized_unspentOutputs.length; i++) {
|
||||
const unspent_output = finalized_unspentOutputs[i]
|
||||
if (unspent_output === null
|
||||
|| typeof unspent_output === 'undefined'
|
||||
|| !unspent_output // just preserving what was in the original code
|
||||
) {
|
||||
throw "unspent_output at index " + i + " was null"
|
||||
}
|
||||
const spend_key_images = unspent_output.spend_key_images
|
||||
if (spend_key_images === null || typeof spend_key_images === 'undefined') {
|
||||
throw "spend_key_images of unspent_output at index " + i + " was null"
|
||||
}
|
||||
for (var j = 0; j < spend_key_images.length; j++) {
|
||||
const finalized_unspentOutput_atI_beforeSplice = finalized_unspentOutputs[i]
|
||||
if (!finalized_unspentOutput_atI_beforeSplice || typeof finalized_unspentOutput_atI_beforeSplice === 'undefined') {
|
||||
console.warn(`This unspent output at i ${i} was literally undefined! Skipping.`)
|
||||
continue
|
||||
}
|
||||
const beforeSplice__tx_pub_key = finalized_unspentOutput_atI_beforeSplice.tx_pub_key
|
||||
const beforeSplice__index = finalized_unspentOutput_atI_beforeSplice.index
|
||||
if (typeof beforeSplice__tx_pub_key === 'undefined' || !beforeSplice__tx_pub_key) {
|
||||
console.warn("This unspent out was missing a tx_pub_key! Skipping.", finalized_unspentOutput_atI_beforeSplice)
|
||||
continue
|
||||
}
|
||||
var key_image = monero_keyImage_cache_utils.Lazy_KeyImage(
|
||||
beforeSplice__tx_pub_key,
|
||||
beforeSplice__index,
|
||||
address,
|
||||
view_key__private,
|
||||
spend_key__public,
|
||||
spend_key__private
|
||||
)
|
||||
if (key_image === finalized_unspentOutput_atI_beforeSplice.spend_key_images[j]) {
|
||||
// console.log("💬 Output was spent; key image: " + key_image + " amount: " + monero_utils.formatMoneyFull(finalized_unspentOutputs[i].amount));
|
||||
// Remove output from list
|
||||
finalized_unspentOutputs.splice(i, 1);
|
||||
const finalized_unspentOutput_atI_afterSplice = finalized_unspentOutputs[i]
|
||||
if (finalized_unspentOutput_atI_afterSplice) {
|
||||
j = finalized_unspentOutput_atI_afterSplice.spend_key_images.length;
|
||||
}
|
||||
i--;
|
||||
} else {
|
||||
console.log("💬 Output used as mixin (" + key_image + "/" + finalized_unspentOutputs[i].spend_key_images[j] + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("Unspent outs: " + JSON.stringify(finalized_unspentOutputs));
|
||||
const unusedOuts = finalized_unspentOutputs.slice(0)
|
||||
const returnValuesByKey =
|
||||
{
|
||||
unspentOutputs: finalized_unspentOutputs,
|
||||
unusedOuts: unusedOuts
|
||||
}
|
||||
return returnValuesByKey
|
||||
}
|
||||
exports.Parsed_UnspentOuts = Parsed_UnspentOuts
|
||||
exports.Parsed_UnspentOuts__sync = Parsed_UnspentOuts__sync
|
Loading…
Reference in new issue