initial commit + README

pull/5/head
Paul Shapiro 7 years ago
commit 1cd11fa63e

1
.gitignore vendored

@ -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…
Cancel
Save