diff --git a/.eslintrc.json b/.eslintrc.json index d2f4de5..c8287fa 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,40 +1,31 @@ { - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "node": true - }, - "globals": { - "LocalFileSystem": false, - "cordova": false, - "device": false - }, - "extends": "eslint:recommended", - "rules": { - "indent": [ - "off", - "tab" - ], - "linebreak-style": [ - "error", - "unix" - ], - "no-unused-vars": [ - "off", - { - "vars": "all", - "args": "after-used", - "ignoreRestSiblings": false - } - ], - "no-console": [ - "warn", - { "allow": [ "warn", "error" ] } - ], - "no-extra-semi": [ "warn" ], - "no-redeclare": [ "warn" ], - // "no-unreachable": [ "warn" ], - "no-inner-declarations": [ "warn" ] - } -} \ No newline at end of file + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "globals": { + "LocalFileSystem": false, + "cordova": false, + "device": false + }, + "extends": "eslint:recommended", + "rules": { + "indent": ["off", "tab"], + "linebreak-style": ["error", "unix"], + "no-unused-vars": [ + "off", + { + "vars": "all", + "args": "after-used", + "ignoreRestSiblings": false + } + ], + "no-console": ["warn", { "allow": ["warn", "error"] }], + "no-extra-semi": ["warn"], + "no-redeclare": ["warn"], + // "no-unreachable": [ "warn" ], + "no-inner-declarations": ["warn"] + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..dfff9a5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +cryptonote_utils/biginteger.js +cryptonote_utils/sha3.js +cryptonote_utils/nacl-fast-cn.js +cryptonote_utils/mnemonic.js +cryptonote_utils/cryptonote_crypto_EMSCRIPTEN.js +cryptonote_utils/crc32.js +package.json +yarn.lock + + diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b6cd36c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 80, + "singleQuote": false, + "useTabs": true, + "semi": true, + "tabWidth": 4, + "trailingComma": "all" +} diff --git a/cryptonote_utils/cryptonote_base58.js b/cryptonote_utils/cryptonote_base58.js index 0744805..7386e48 100644 --- a/cryptonote_utils/cryptonote_base58.js +++ b/cryptonote_utils/cryptonote_base58.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,15 +26,14 @@ // 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 +const JSBigInt = require("./biginteger").BigInteger; -var cnBase58 = (function () -{ +var cnBase58 = (function() { var b58 = {}; // - var alphabet_str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + var alphabet_str = + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; var alphabet = []; for (var i = 0; i < alphabet_str.length; i++) { alphabet.push(alphabet_str.charCodeAt(i)); @@ -88,25 +87,25 @@ var cnBase58 = (function () 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"; + 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; } @@ -124,7 +123,7 @@ var cnBase58 = (function () return res; } - b58.encode_block = function (data, buf, index) { + b58.encode_block = function(data, buf, index) { if (data.length < 1 || data.length > full_encoded_block_size) { throw "Invalid block length: " + data.length; } @@ -143,14 +142,16 @@ var cnBase58 = (function () return buf; }; - b58.encode = function (hex) { + 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_size = + full_block_count * full_encoded_block_size + + encoded_block_sizes[last_block_size]; var res = new Uint8Array(res_size); var i; @@ -158,15 +159,29 @@ var cnBase58 = (function () 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); + 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) + 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) { + b58.decode_block = function(data, buf, index) { if (data.length < 1 || data.length > full_encoded_block_size) { throw "Invalid block length: " + data.length; } @@ -190,31 +205,52 @@ var cnBase58 = (function () 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)) { + 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) { + 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); + 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_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); + 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); + 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); }; @@ -222,4 +258,4 @@ var cnBase58 = (function () return b58; })(); -exports.cnBase58 = cnBase58 \ No newline at end of file +exports.cnBase58 = cnBase58; diff --git a/cryptonote_utils/cryptonote_utils.js b/cryptonote_utils/cryptonote_utils.js index e9d2572..245e011 100644 --- a/cryptonote_utils/cryptonote_utils.js +++ b/cryptonote_utils/cryptonote_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -29,23 +29,22 @@ // Original Author: Lucas Jones // Modified to remove jQuery dep and support modular inclusion of deps by Paul Shapiro (2016) // Modified to add RingCT support by luigi1111 (2017) -// +// // v--- These should maybe be injected into a context and supplied to currencyConfig for future platforms -const JSBigInt = require('./biginteger').BigInteger -const cnBase58 = require('./cryptonote_base58').cnBase58 -const CNCrypto = require('./cryptonote_crypto_EMSCRIPTEN') -const mnemonic = require('./mnemonic') -const nacl = require('./nacl-fast-cn') -const sha3 = require('./sha3') -const nettype_utils = require('./nettype') - -var cnUtil = function(currencyConfig) -{ - var config = {} // shallow copy of initConfig +const JSBigInt = require("./biginteger").BigInteger; +const cnBase58 = require("./cryptonote_base58").cnBase58; +const CNCrypto = require("./cryptonote_crypto_EMSCRIPTEN"); +const mnemonic = require("./mnemonic"); +const nacl = require("./nacl-fast-cn"); +const sha3 = require("./sha3"); +const nettype_utils = require("./nettype"); + +var cnUtil = function(currencyConfig) { + var config = {}; // shallow copy of initConfig for (var key in currencyConfig) { - config[key] = currencyConfig[key] + config[key] = currencyConfig[key]; } - + config.coinUnits = new JSBigInt(10).pow(config.coinUnitPlaces); var HASH_STATE_BYTES = 200; @@ -61,14 +60,14 @@ var cnUtil = function(currencyConfig) var RCTTypeSimple = 2; var TX_EXTRA_NONCE_MAX_COUNT = 255; var TX_EXTRA_TAGS = { - PADDING: '00', - PUBKEY: '01', - NONCE: '02', - MERGE_MINING: '03' + PADDING: "00", + PUBKEY: "01", + NONCE: "02", + MERGE_MINING: "03", }; var TX_EXTRA_NONCE_TAGS = { - PAYMENT_ID: '00', - ENCRYPTED_PAYMENT_ID: '01' + PAYMENT_ID: "00", + ENCRYPTED_PAYMENT_ID: "01", }; var KEY_SIZE = 32; var STRUCT_SIZES = { @@ -80,61 +79,102 @@ var cnUtil = function(currencyConfig) EC_POINT: 32, KEY_IMAGE: 32, GE_DSMP: 160 * 8, // ge_cached * 8 - SIGNATURE: 64 // ec_scalar * 2 + SIGNATURE: 64, // ec_scalar * 2 }; //RCT vars var H = "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"; //base H for amounts - var l = JSBigInt("7237005577332262213973186563042994240857116359379907606001950938285454250989"); //curve order (not RCT specific) + var l = JSBigInt( + "7237005577332262213973186563042994240857116359379907606001950938285454250989", + ); //curve order (not RCT specific) var I = "0100000000000000000000000000000000000000000000000000000000000000"; //identity element var Z = "0000000000000000000000000000000000000000000000000000000000000000"; //zero scalar //H2 object to speed up some operations - var H2 = ["8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94", "8faa448ae4b3e2bb3d4d130909f55fcd79711c1c83cdbccadd42cbe1515e8712", - "12a7d62c7791654a57f3e67694ed50b49a7d9e3fc1e4c7a0bde29d187e9cc71d", "789ab9934b49c4f9e6785c6d57a498b3ead443f04f13df110c5427b4f214c739", - "771e9299d94f02ac72e38e44de568ac1dcb2edc6edb61f83ca418e1077ce3de8", "73b96db43039819bdaf5680e5c32d741488884d18d93866d4074a849182a8a64", - "8d458e1c2f68ebebccd2fd5d379f5e58f8134df3e0e88cad3d46701063a8d412", "09551edbe494418e81284455d64b35ee8ac093068a5f161fa6637559177ef404", - "d05a8866f4df8cee1e268b1d23a4c58c92e760309786cdac0feda1d247a9c9a7", "55cdaad518bd871dd1eb7bc7023e1dc0fdf3339864f88fdd2de269fe9ee1832d", - "e7697e951a98cfd5712b84bbe5f34ed733e9473fcb68eda66e3788df1958c306", "f92a970bae72782989bfc83adfaa92a4f49c7e95918b3bba3cdc7fe88acc8d47", - "1f66c2d491d75af915c8db6a6d1cb0cd4f7ddcd5e63d3ba9b83c866c39ef3a2b", "3eec9884b43f58e93ef8deea260004efea2a46344fc5965b1a7dd5d18997efa7", - "b29f8f0ccb96977fe777d489d6be9e7ebc19c409b5103568f277611d7ea84894", "56b1f51265b9559876d58d249d0c146d69a103636699874d3f90473550fe3f2c", - "1d7a36575e22f5d139ff9cc510fa138505576b63815a94e4b012bfd457caaada", "d0ac507a864ecd0593fa67be7d23134392d00e4007e2534878d9b242e10d7620", - "f6c6840b9cf145bb2dccf86e940be0fc098e32e31099d56f7fe087bd5deb5094", "28831a3340070eb1db87c12e05980d5f33e9ef90f83a4817c9f4a0a33227e197", - "87632273d629ccb7e1ed1a768fa2ebd51760f32e1c0b867a5d368d5271055c6e", "5c7b29424347964d04275517c5ae14b6b5ea2798b573fc94e6e44a5321600cfb", - "e6945042d78bc2c3bd6ec58c511a9fe859c0ad63fde494f5039e0e8232612bd5", "36d56907e2ec745db6e54f0b2e1b2300abcb422e712da588a40d3f1ebbbe02f6", - "34db6ee4d0608e5f783650495a3b2f5273c5134e5284e4fdf96627bb16e31e6b", "8e7659fb45a3787d674ae86731faa2538ec0fdf442ab26e9c791fada089467e9", - "3006cf198b24f31bb4c7e6346000abc701e827cfbb5df52dcfa42e9ca9ff0802", "f5fd403cb6e8be21472e377ffd805a8c6083ea4803b8485389cc3ebc215f002a", - "3731b260eb3f9482e45f1c3f3b9dcf834b75e6eef8c40f461ea27e8b6ed9473d", "9f9dab09c3f5e42855c2de971b659328a2dbc454845f396ffc053f0bb192f8c3", - "5e055d25f85fdb98f273e4afe08464c003b70f1ef0677bb5e25706400be620a5", "868bcf3679cb6b500b94418c0b8925f9865530303ae4e4b262591865666a4590", - "b3db6bd3897afbd1df3f9644ab21c8050e1f0038a52f7ca95ac0c3de7558cb7a", "8119b3a059ff2cac483e69bcd41d6d27149447914288bbeaee3413e6dcc6d1eb", - "10fc58f35fc7fe7ae875524bb5850003005b7f978c0c65e2a965464b6d00819c", "5acd94eb3c578379c1ea58a343ec4fcff962776fe35521e475a0e06d887b2db9", - "33daf3a214d6e0d42d2300a7b44b39290db8989b427974cd865db011055a2901", "cfc6572f29afd164a494e64e6f1aeb820c3e7da355144e5124a391d06e9f95ea", - "d5312a4b0ef615a331f6352c2ed21dac9e7c36398b939aec901c257f6cbc9e8e", "551d67fefc7b5b9f9fdbf6af57c96c8a74d7e45a002078a7b5ba45c6fde93e33", - "d50ac7bd5ca593c656928f38428017fc7ba502854c43d8414950e96ecb405dc3", "0773e18ea1be44fe1a97e239573cfae3e4e95ef9aa9faabeac1274d3ad261604", - "e9af0e7ca89330d2b8615d1b4137ca617e21297f2f0ded8e31b7d2ead8714660", "7b124583097f1029a0c74191fe7378c9105acc706695ed1493bb76034226a57b", - "ec40057b995476650b3db98e9db75738a8cd2f94d863b906150c56aac19caa6b", "01d9ff729efd39d83784c0fe59c4ae81a67034cb53c943fb818b9d8ae7fc33e5", - "00dfb3c696328c76424519a7befe8e0f6c76f947b52767916d24823f735baf2e", "461b799b4d9ceea8d580dcb76d11150d535e1639d16003c3fb7e9d1fd13083a8", - "ee03039479e5228fdc551cbde7079d3412ea186a517ccc63e46e9fcce4fe3a6c", "a8cfb543524e7f02b9f045acd543c21c373b4c9b98ac20cec417a6ddb5744e94", - "932b794bf89c6edaf5d0650c7c4bad9242b25626e37ead5aa75ec8c64e09dd4f", "16b10c779ce5cfef59c7710d2e68441ea6facb68e9b5f7d533ae0bb78e28bf57", - "0f77c76743e7396f9910139f4937d837ae54e21038ac5c0b3fd6ef171a28a7e4", "d7e574b7b952f293e80dde905eb509373f3f6cd109a02208b3c1e924080a20ca", - "45666f8c381e3da675563ff8ba23f83bfac30c34abdde6e5c0975ef9fd700cb9", "b24612e454607eb1aba447f816d1a4551ef95fa7247fb7c1f503020a7177f0dd", - "7e208861856da42c8bb46a7567f8121362d9fb2496f131a4aa9017cf366cdfce", "5b646bff6ad1100165037a055601ea02358c0f41050f9dfe3c95dccbd3087be0", - "746d1dccfed2f0ff1e13c51e2d50d5324375fbd5bf7ca82a8931828d801d43ab", "cb98110d4a6bb97d22feadbc6c0d8930c5f8fc508b2fc5b35328d26b88db19ae", - "60b626a033b55f27d7676c4095eababc7a2c7ede2624b472e97f64f96b8cfc0e", "e5b52bc927468df71893eb8197ef820cf76cb0aaf6e8e4fe93ad62d803983104", - "056541ae5da9961be2b0a5e895e5c5ba153cbb62dd561a427bad0ffd41923199", "f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888"]; + var H2 = [ + "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94", + "8faa448ae4b3e2bb3d4d130909f55fcd79711c1c83cdbccadd42cbe1515e8712", + "12a7d62c7791654a57f3e67694ed50b49a7d9e3fc1e4c7a0bde29d187e9cc71d", + "789ab9934b49c4f9e6785c6d57a498b3ead443f04f13df110c5427b4f214c739", + "771e9299d94f02ac72e38e44de568ac1dcb2edc6edb61f83ca418e1077ce3de8", + "73b96db43039819bdaf5680e5c32d741488884d18d93866d4074a849182a8a64", + "8d458e1c2f68ebebccd2fd5d379f5e58f8134df3e0e88cad3d46701063a8d412", + "09551edbe494418e81284455d64b35ee8ac093068a5f161fa6637559177ef404", + "d05a8866f4df8cee1e268b1d23a4c58c92e760309786cdac0feda1d247a9c9a7", + "55cdaad518bd871dd1eb7bc7023e1dc0fdf3339864f88fdd2de269fe9ee1832d", + "e7697e951a98cfd5712b84bbe5f34ed733e9473fcb68eda66e3788df1958c306", + "f92a970bae72782989bfc83adfaa92a4f49c7e95918b3bba3cdc7fe88acc8d47", + "1f66c2d491d75af915c8db6a6d1cb0cd4f7ddcd5e63d3ba9b83c866c39ef3a2b", + "3eec9884b43f58e93ef8deea260004efea2a46344fc5965b1a7dd5d18997efa7", + "b29f8f0ccb96977fe777d489d6be9e7ebc19c409b5103568f277611d7ea84894", + "56b1f51265b9559876d58d249d0c146d69a103636699874d3f90473550fe3f2c", + "1d7a36575e22f5d139ff9cc510fa138505576b63815a94e4b012bfd457caaada", + "d0ac507a864ecd0593fa67be7d23134392d00e4007e2534878d9b242e10d7620", + "f6c6840b9cf145bb2dccf86e940be0fc098e32e31099d56f7fe087bd5deb5094", + "28831a3340070eb1db87c12e05980d5f33e9ef90f83a4817c9f4a0a33227e197", + "87632273d629ccb7e1ed1a768fa2ebd51760f32e1c0b867a5d368d5271055c6e", + "5c7b29424347964d04275517c5ae14b6b5ea2798b573fc94e6e44a5321600cfb", + "e6945042d78bc2c3bd6ec58c511a9fe859c0ad63fde494f5039e0e8232612bd5", + "36d56907e2ec745db6e54f0b2e1b2300abcb422e712da588a40d3f1ebbbe02f6", + "34db6ee4d0608e5f783650495a3b2f5273c5134e5284e4fdf96627bb16e31e6b", + "8e7659fb45a3787d674ae86731faa2538ec0fdf442ab26e9c791fada089467e9", + "3006cf198b24f31bb4c7e6346000abc701e827cfbb5df52dcfa42e9ca9ff0802", + "f5fd403cb6e8be21472e377ffd805a8c6083ea4803b8485389cc3ebc215f002a", + "3731b260eb3f9482e45f1c3f3b9dcf834b75e6eef8c40f461ea27e8b6ed9473d", + "9f9dab09c3f5e42855c2de971b659328a2dbc454845f396ffc053f0bb192f8c3", + "5e055d25f85fdb98f273e4afe08464c003b70f1ef0677bb5e25706400be620a5", + "868bcf3679cb6b500b94418c0b8925f9865530303ae4e4b262591865666a4590", + "b3db6bd3897afbd1df3f9644ab21c8050e1f0038a52f7ca95ac0c3de7558cb7a", + "8119b3a059ff2cac483e69bcd41d6d27149447914288bbeaee3413e6dcc6d1eb", + "10fc58f35fc7fe7ae875524bb5850003005b7f978c0c65e2a965464b6d00819c", + "5acd94eb3c578379c1ea58a343ec4fcff962776fe35521e475a0e06d887b2db9", + "33daf3a214d6e0d42d2300a7b44b39290db8989b427974cd865db011055a2901", + "cfc6572f29afd164a494e64e6f1aeb820c3e7da355144e5124a391d06e9f95ea", + "d5312a4b0ef615a331f6352c2ed21dac9e7c36398b939aec901c257f6cbc9e8e", + "551d67fefc7b5b9f9fdbf6af57c96c8a74d7e45a002078a7b5ba45c6fde93e33", + "d50ac7bd5ca593c656928f38428017fc7ba502854c43d8414950e96ecb405dc3", + "0773e18ea1be44fe1a97e239573cfae3e4e95ef9aa9faabeac1274d3ad261604", + "e9af0e7ca89330d2b8615d1b4137ca617e21297f2f0ded8e31b7d2ead8714660", + "7b124583097f1029a0c74191fe7378c9105acc706695ed1493bb76034226a57b", + "ec40057b995476650b3db98e9db75738a8cd2f94d863b906150c56aac19caa6b", + "01d9ff729efd39d83784c0fe59c4ae81a67034cb53c943fb818b9d8ae7fc33e5", + "00dfb3c696328c76424519a7befe8e0f6c76f947b52767916d24823f735baf2e", + "461b799b4d9ceea8d580dcb76d11150d535e1639d16003c3fb7e9d1fd13083a8", + "ee03039479e5228fdc551cbde7079d3412ea186a517ccc63e46e9fcce4fe3a6c", + "a8cfb543524e7f02b9f045acd543c21c373b4c9b98ac20cec417a6ddb5744e94", + "932b794bf89c6edaf5d0650c7c4bad9242b25626e37ead5aa75ec8c64e09dd4f", + "16b10c779ce5cfef59c7710d2e68441ea6facb68e9b5f7d533ae0bb78e28bf57", + "0f77c76743e7396f9910139f4937d837ae54e21038ac5c0b3fd6ef171a28a7e4", + "d7e574b7b952f293e80dde905eb509373f3f6cd109a02208b3c1e924080a20ca", + "45666f8c381e3da675563ff8ba23f83bfac30c34abdde6e5c0975ef9fd700cb9", + "b24612e454607eb1aba447f816d1a4551ef95fa7247fb7c1f503020a7177f0dd", + "7e208861856da42c8bb46a7567f8121362d9fb2496f131a4aa9017cf366cdfce", + "5b646bff6ad1100165037a055601ea02358c0f41050f9dfe3c95dccbd3087be0", + "746d1dccfed2f0ff1e13c51e2d50d5324375fbd5bf7ca82a8931828d801d43ab", + "cb98110d4a6bb97d22feadbc6c0d8930c5f8fc508b2fc5b35328d26b88db19ae", + "60b626a033b55f27d7676c4095eababc7a2c7ede2624b472e97f64f96b8cfc0e", + "e5b52bc927468df71893eb8197ef820cf76cb0aaf6e8e4fe93ad62d803983104", + "056541ae5da9961be2b0a5e895e5c5ba153cbb62dd561a427bad0ffd41923199", + "f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888", + ]; //begin rct new functions //creates a Pedersen commitment from an amount (in scalar form) and a mask //C = bG + aH where b = mask, a = amount - function commit(amount, mask){ - if (!valid_hex(mask) || mask.length !== 64 || !valid_hex(amount) || amount.length !== 64){ + function commit(amount, mask) { + if ( + !valid_hex(mask) || + mask.length !== 64 || + !valid_hex(amount) || + amount.length !== 64 + ) { throw "invalid amount or mask!"; } var C = this.ge_double_scalarmult_base_vartime(amount, H, mask); return C; } - function zeroCommit(amount){ - if (!valid_hex(amount) || amount.length !== 64){ + function zeroCommit(amount) { + if (!valid_hex(amount) || amount.length !== 64) { throw "invalid amount!"; } var C = this.ge_double_scalarmult_base_vartime(amount, H, I); @@ -145,8 +185,8 @@ var cnUtil = function(currencyConfig) var first = this.hash_to_scalar(key); var second = this.hash_to_scalar(first); return { - "mask": this.sc_sub(ecdh.mask, first), - "amount": this.sc_sub(ecdh.amount, second) + mask: this.sc_sub(ecdh.mask, first), + amount: this.sc_sub(ecdh.amount, second), }; }; @@ -154,25 +194,27 @@ var cnUtil = function(currencyConfig) var first = this.hash_to_scalar(key); var second = this.hash_to_scalar(first); return { - "mask": this.sc_add(ecdh.mask, first), - "amount": this.sc_add(ecdh.amount, second) + mask: this.sc_add(ecdh.mask, first), + amount: this.sc_add(ecdh.amount, second), }; }; //switch byte order for hex string - function swapEndian(hex){ - if (hex.length % 2 !== 0){return "length must be a multiple of 2!";} + function swapEndian(hex) { + if (hex.length % 2 !== 0) { + return "length must be a multiple of 2!"; + } var data = ""; - for (var i=1; i <= hex.length / 2; i++){ + for (var i = 1; i <= hex.length / 2; i++) { data += hex.substr(0 - 2 * i, 2); } return data; } //switch byte order charwise - function swapEndianC(string){ + function swapEndianC(string) { var data = ""; - for (var i=1; i <= string.length; i++){ + for (var i = 1; i <= string.length; i++) { data += string.substr(0 - i, 1); } return data; @@ -180,46 +222,61 @@ var cnUtil = function(currencyConfig) //for most uses you'll also want to swapEndian after conversion //mainly to convert integer "scalars" to usable hexadecimal strings - function d2h(integer){ - if (typeof integer !== "string" && integer.toString().length > 15){throw "integer should be entered as a string for precision";} + function d2h(integer) { + if (typeof integer !== "string" && integer.toString().length > 15) { + throw "integer should be entered as a string for precision"; + } var padding = ""; - for (i = 0; i < 63; i++){ + for (i = 0; i < 63; i++) { padding += "0"; } - return (padding + JSBigInt(integer).toString(16).toLowerCase()).slice(-64); + return ( + padding + + JSBigInt(integer) + .toString(16) + .toLowerCase() + ).slice(-64); } //integer (string) to scalar - function d2s(integer){ + function d2s(integer) { return swapEndian(d2h(integer)); } //scalar to integer (string) - function s2d(scalar){ + function s2d(scalar) { return JSBigInt.parse(swapEndian(scalar), 16).toString(); } //convert integer string to 64bit "binary" little-endian string - function d2b(integer){ - if (typeof integer !== "string" && integer.toString().length > 15){throw "integer should be entered as a string for precision";} + function d2b(integer) { + if (typeof integer !== "string" && integer.toString().length > 15) { + throw "integer should be entered as a string for precision"; + } var padding = ""; - for (i = 0; i < 63; i++){ + for (i = 0; i < 63; i++) { padding += "0"; } var a = new JSBigInt(integer); - if (a.toString(2).length > 64){throw "amount overflows uint64!";} + if (a.toString(2).length > 64) { + throw "amount overflows uint64!"; + } return swapEndianC((padding + a.toString(2)).slice(-64)); } //convert integer string to 64bit base 4 little-endian string - function d2b4(integer){ - if (typeof integer !== "string" && integer.toString().length > 15){throw "integer should be entered as a string for precision";} + function d2b4(integer) { + if (typeof integer !== "string" && integer.toString().length > 15) { + throw "integer should be entered as a string for precision"; + } var padding = ""; - for (i = 0; i < 31; i++){ + for (i = 0; i < 31; i++) { padding += "0"; } var a = new JSBigInt(integer); - if (a.toString(2).length > 64){throw "amount overflows uint64!";} + if (a.toString(2).length > 64) { + throw "amount overflows uint64!"; + } return swapEndianC((padding + a.toString(4)).slice(-32)); } //end rct new functions @@ -231,11 +288,19 @@ var cnUtil = function(currencyConfig) //simple exclusive or function for two hex inputs this.hex_xor = function(hex1, hex2) { - if (!hex1 || !hex2 || hex1.length !== hex2.length || hex1.length % 2 !== 0 || hex2.length % 2 !== 0){throw "Hex string(s) is/are invalid!";} + if ( + !hex1 || + !hex2 || + hex1.length !== hex2.length || + hex1.length % 2 !== 0 || + hex2.length % 2 !== 0 + ) { + throw "Hex string(s) is/are invalid!"; + } var bin1 = hextobin(hex1); var bin2 = hextobin(hex2); var xor = new Uint8Array(bin1.length); - for (i = 0; i < xor.length; i++){ + for (i = 0; i < xor.length; i++) { xor[i] = bin1[i] ^ bin2[i]; } return bintohex(xor); @@ -275,7 +340,7 @@ var cnUtil = function(currencyConfig) this.encode_varint = function(i) { i = new JSBigInt(i); - var out = ''; + var out = ""; // While i >= b10000000 while (i.compare(0x80) >= 0) { // out.append i & b01111111 | b10000000 @@ -293,7 +358,7 @@ var cnUtil = function(currencyConfig) } var mem = CNCrypto._malloc(64); CNCrypto.HEAPU8.set(input, mem); - CNCrypto.ccall('sc_reduce', 'void', ['number'], [mem]); + CNCrypto.ccall("sc_reduce", "void", ["number"], [mem]); var output = CNCrypto.HEAPU8.subarray(mem, mem + 64); CNCrypto._free(mem); return bintohex(output); @@ -306,7 +371,7 @@ var cnUtil = function(currencyConfig) } var mem = CNCrypto._malloc(32); CNCrypto.HEAPU8.set(input, mem); - CNCrypto.ccall('sc_reduce32', 'void', ['number'], [mem]); + CNCrypto.ccall("sc_reduce32", "void", ["number"], [mem]); var output = CNCrypto.HEAPU8.subarray(mem, mem + 32); CNCrypto._free(mem); return bintohex(output); @@ -392,28 +457,37 @@ var cnUtil = function(currencyConfig) }; this.pubkeys_to_string = function(spend, view, nettype) { - var prefix = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype)); + var prefix = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype), + ); var data = prefix + spend + view; var checksum = this.cn_fast_hash(data); - return cnBase58.encode(data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2)); + return cnBase58.encode( + data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2), + ); }; this.new__int_addr_from_addr_and_short_pid = function( address, short_pid, - nettype - ) { // throws + nettype, + ) { + // throws let decoded_address = this.decode_address( address, // TODO/FIXME: not super happy about having to decode just to re-encode… this was a quick hack - nettype + nettype, ); // throws if (!short_pid || short_pid.length != 16) { throw "expected valid short_pid"; } - var prefix = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype)); - var data = prefix + decoded_address.spend + decoded_address.view + short_pid; + var prefix = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype), + ); + var data = + prefix + decoded_address.spend + decoded_address.view + short_pid; var checksum = this.cn_fast_hash(data); - var encodable__data = data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2); + var encodable__data = + data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2); // return cnBase58.encode(encodable__data); }; @@ -424,8 +498,8 @@ var cnUtil = function(currencyConfig) var sec = this.sc_reduce32(seed); var pub = this.sec_key_to_pub(sec); return { - 'sec': sec, - 'pub': pub + sec: sec, + pub: pub, }; }; @@ -471,7 +545,11 @@ var cnUtil = function(currencyConfig) keys.spend = this.generate_keys(first); var second = this.cn_fast_hash(first); keys.view = this.generate_keys(second); - keys.public_addr = this.pubkeys_to_string(keys.spend.pub, keys.view.pub, nettype); + keys.public_addr = this.pubkeys_to_string( + keys.spend.pub, + keys.view.pub, + nettype, + ); return keys; }; @@ -483,58 +561,81 @@ var cnUtil = function(currencyConfig) first = seed; } var spend = this.generate_keys(first); - var prefix = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype)); + var prefix = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype), + ); return cnBase58.encode(prefix + spend.pub).slice(0, 44); }; - + this.decode_address = function(address, nettype) { var dec = cnBase58.decode(address); - var expectedPrefix = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype)); - var expectedPrefixInt = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype)); - var expectedPrefixSub = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype)); + var expectedPrefix = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype), + ); + var expectedPrefixInt = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype), + ); + var expectedPrefixSub = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype), + ); var prefix = dec.slice(0, expectedPrefix.length); - if (prefix !== expectedPrefix && prefix !== expectedPrefixInt && prefix !== expectedPrefixSub) { + if ( + prefix !== expectedPrefix && + prefix !== expectedPrefixInt && + prefix !== expectedPrefixSub + ) { throw "Invalid address prefix"; } dec = dec.slice(expectedPrefix.length); var spend = dec.slice(0, 64); var view = dec.slice(64, 128); - if (prefix === expectedPrefixInt){ - var intPaymentId = dec.slice(128, 128 + (INTEGRATED_ID_SIZE * 2)); - var checksum = dec.slice(128 + (INTEGRATED_ID_SIZE * 2), 128 + (INTEGRATED_ID_SIZE * 2) + (ADDRESS_CHECKSUM_SIZE * 2)); - var expectedChecksum = this.cn_fast_hash(prefix + spend + view + intPaymentId).slice(0, ADDRESS_CHECKSUM_SIZE * 2); + if (prefix === expectedPrefixInt) { + var intPaymentId = dec.slice(128, 128 + INTEGRATED_ID_SIZE * 2); + var checksum = dec.slice( + 128 + INTEGRATED_ID_SIZE * 2, + 128 + INTEGRATED_ID_SIZE * 2 + ADDRESS_CHECKSUM_SIZE * 2, + ); + var expectedChecksum = this.cn_fast_hash( + prefix + spend + view + intPaymentId, + ).slice(0, ADDRESS_CHECKSUM_SIZE * 2); } else { - var checksum = dec.slice(128, 128 + (ADDRESS_CHECKSUM_SIZE * 2)); - var expectedChecksum = this.cn_fast_hash(prefix + spend + view).slice(0, ADDRESS_CHECKSUM_SIZE * 2); + var checksum = dec.slice(128, 128 + ADDRESS_CHECKSUM_SIZE * 2); + var expectedChecksum = this.cn_fast_hash( + prefix + spend + view, + ).slice(0, ADDRESS_CHECKSUM_SIZE * 2); } if (checksum !== expectedChecksum) { throw "Invalid checksum"; } - if (intPaymentId){ + if (intPaymentId) { return { spend: spend, view: view, - intPaymentId: intPaymentId + intPaymentId: intPaymentId, }; } else { return { spend: spend, - view: view + view: view, }; } }; - + this.is_subaddress = function(addr, nettype) { var decoded = cnBase58.decode(addr); - var subaddressPrefix = this.encode_varint(nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype)); + var subaddressPrefix = this.encode_varint( + nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype), + ); var prefix = decoded.slice(0, subaddressPrefix.length); - return (prefix === subaddressPrefix); + return prefix === subaddressPrefix; }; this.valid_keys = function(view_pub, view_sec, spend_pub, spend_sec) { var expected_view_pub = this.sec_key_to_pub(view_sec); var expected_spend_pub = this.sec_key_to_pub(spend_sec); - return (expected_spend_pub === spend_pub) && (expected_view_pub === view_pub); + return ( + expected_spend_pub === spend_pub && expected_view_pub === view_pub + ); }; this.hash_to_scalar = function(buf) { @@ -584,7 +685,7 @@ var cnUtil = function(currencyConfig) this.derivation_to_scalar = function(derivation, output_index) { var buf = ""; - if (derivation.length !== (STRUCT_SIZES.EC_POINT * 2)) { + if (derivation.length !== STRUCT_SIZES.EC_POINT * 2) { throw "Invalid derivation length!"; } buf += derivation; @@ -601,13 +702,23 @@ var cnUtil = function(currencyConfig) throw "Invalid input length!"; } var scalar_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); - var scalar_b = hextobin(this.derivation_to_scalar(derivation, out_index)); + var scalar_b = hextobin( + this.derivation_to_scalar(derivation, out_index), + ); CNCrypto.HEAPU8.set(scalar_b, scalar_m); var base_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.HEAPU8.set(hextobin(sec), base_m); var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); - CNCrypto.ccall("sc_add", "void", ["number", "number", "number"], [derived_m, base_m, scalar_m]); - var res = CNCrypto.HEAPU8.subarray(derived_m, derived_m + STRUCT_SIZES.EC_SCALAR); + CNCrypto.ccall( + "sc_add", + "void", + ["number", "number", "number"], + [derived_m, base_m, scalar_m], + ); + var res = CNCrypto.HEAPU8.subarray( + derived_m, + derived_m + STRUCT_SIZES.EC_SCALAR, + ); CNCrypto._free(scalar_m); CNCrypto._free(base_m); CNCrypto._free(derived_m); @@ -659,12 +770,18 @@ var cnUtil = function(currencyConfig) throw "Invalid input length!"; } var s = this.derivation_to_scalar(derivation, out_index); - return bintohex(nacl.ll.ge_add(hextobin(pub), hextobin(this.ge_scalarmult_base(s)))); + return bintohex( + nacl.ll.ge_add(hextobin(pub), hextobin(this.ge_scalarmult_base(s))), + ); }; - + // D' = P - Hs(aR|i)G - this.derive_subaddress_public_key = function(output_key, derivation, out_index) { - if(output_key.length !== 64 || derivation.length !== 64) { + this.derive_subaddress_public_key = function( + output_key, + derivation, + out_index, + ) { + if (output_key.length !== 64 || derivation.length !== 64) { throw "Invalid input length!"; } var scalar = this.derivation_to_scalar(derivation, out_index); @@ -673,7 +790,7 @@ var cnUtil = function(currencyConfig) }; this.hash_to_ec = function(key) { - if (key.length !== (KEY_SIZE * 2)) { + if (key.length !== KEY_SIZE * 2) { throw "Invalid input length"; } var h_m = CNCrypto._malloc(HASH_SIZE); @@ -682,9 +799,24 @@ var cnUtil = function(currencyConfig) var res_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3); var hash = hextobin(this.cn_fast_hash(key, KEY_SIZE)); CNCrypto.HEAPU8.set(hash, h_m); - CNCrypto.ccall("ge_fromfe_frombytes_vartime", "void", ["number", "number"], [point_m, h_m]); - CNCrypto.ccall("ge_mul8", "void", ["number", "number"], [point2_m, point_m]); - CNCrypto.ccall("ge_p1p1_to_p3", "void", ["number", "number"], [res_m, point2_m]); + CNCrypto.ccall( + "ge_fromfe_frombytes_vartime", + "void", + ["number", "number"], + [point_m, h_m], + ); + CNCrypto.ccall( + "ge_mul8", + "void", + ["number", "number"], + [point2_m, point_m], + ); + CNCrypto.ccall( + "ge_p1p1_to_p3", + "void", + ["number", "number"], + [res_m, point2_m], + ); var res = CNCrypto.HEAPU8.subarray(res_m, res_m + STRUCT_SIZES.GE_P3); CNCrypto._free(h_m); CNCrypto._free(point_m); @@ -695,7 +827,7 @@ var cnUtil = function(currencyConfig) //returns a 32 byte point via "ge_p3_tobytes" rather than a 160 byte "p3", otherwise same as above; this.hash_to_ec_2 = function(key) { - if (key.length !== (KEY_SIZE * 2)) { + if (key.length !== KEY_SIZE * 2) { throw "Invalid input length"; } var h_m = CNCrypto._malloc(HASH_SIZE); @@ -705,10 +837,30 @@ var cnUtil = function(currencyConfig) var hash = hextobin(this.cn_fast_hash(key, KEY_SIZE)); var res2_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.HEAPU8.set(hash, h_m); - CNCrypto.ccall("ge_fromfe_frombytes_vartime", "void", ["number", "number"], [point_m, h_m]); - CNCrypto.ccall("ge_mul8", "void", ["number", "number"], [point2_m, point_m]); - CNCrypto.ccall("ge_p1p1_to_p3", "void", ["number", "number"], [res_m, point2_m]); - CNCrypto.ccall("ge_p3_tobytes", "void", ["number", "number"], [res2_m, res_m]); + CNCrypto.ccall( + "ge_fromfe_frombytes_vartime", + "void", + ["number", "number"], + [point_m, h_m], + ); + CNCrypto.ccall( + "ge_mul8", + "void", + ["number", "number"], + [point2_m, point_m], + ); + CNCrypto.ccall( + "ge_p1p1_to_p3", + "void", + ["number", "number"], + [res_m, point2_m], + ); + CNCrypto.ccall( + "ge_p3_tobytes", + "void", + ["number", "number"], + [res2_m, res_m], + ); var res = CNCrypto.HEAPU8.subarray(res2_m, res2_m + KEY_SIZE); CNCrypto._free(h_m); CNCrypto._free(point_m); @@ -734,9 +886,22 @@ var cnUtil = function(currencyConfig) var point_b = hextobin(this.hash_to_ec(pub)); CNCrypto.HEAPU8.set(point_b, point_m); var image_m = CNCrypto._malloc(STRUCT_SIZES.KEY_IMAGE); - CNCrypto.ccall("ge_scalarmult", "void", ["number", "number", "number"], [point2_m, sec_m, point_m]); - CNCrypto.ccall("ge_tobytes", "void", ["number", "number"], [image_m, point2_m]); - var res = CNCrypto.HEAPU8.subarray(image_m, image_m + STRUCT_SIZES.KEY_IMAGE); + CNCrypto.ccall( + "ge_scalarmult", + "void", + ["number", "number", "number"], + [point2_m, sec_m, point_m], + ); + CNCrypto.ccall( + "ge_tobytes", + "void", + ["number", "number"], + [image_m, point2_m], + ); + var res = CNCrypto.HEAPU8.subarray( + image_m, + image_m + STRUCT_SIZES.KEY_IMAGE, + ); CNCrypto._free(pub_m); CNCrypto._free(sec_m); CNCrypto._free(point_m); @@ -745,7 +910,13 @@ var cnUtil = function(currencyConfig) return bintohex(res); }; - this.generate_key_image = function(tx_pub, view_sec, spend_pub, spend_sec, output_index) { + this.generate_key_image = function( + tx_pub, + view_sec, + spend_pub, + spend_sec, + output_index, + ) { if (tx_pub.length !== 64) { throw "Invalid tx_pub length"; } @@ -759,40 +930,75 @@ var cnUtil = function(currencyConfig) throw "Invalid spend_sec length"; } var recv_derivation = this.generate_key_derivation(tx_pub, view_sec); - var ephemeral_pub = this.derive_public_key(recv_derivation, output_index, spend_pub); - var ephemeral_sec = this.derive_secret_key(recv_derivation, output_index, spend_sec); + var ephemeral_pub = this.derive_public_key( + recv_derivation, + output_index, + spend_pub, + ); + var ephemeral_sec = this.derive_secret_key( + recv_derivation, + output_index, + spend_sec, + ); var k_image = this.generate_key_image_2(ephemeral_pub, ephemeral_sec); return { ephemeral_pub: ephemeral_pub, - key_image: k_image + key_image: k_image, }; }; - this.generate_key_image_helper_rct = function(keys, tx_pub_key, out_index, enc_mask) { - var recv_derivation = this.generate_key_derivation(tx_pub_key, keys.view.sec); + this.generate_key_image_helper_rct = function( + keys, + tx_pub_key, + out_index, + enc_mask, + ) { + var recv_derivation = this.generate_key_derivation( + tx_pub_key, + keys.view.sec, + ); if (!recv_derivation) throw "Failed to generate key image"; - var mask = enc_mask ? sc_sub(enc_mask, hash_to_scalar(derivation_to_scalar(recv_derivation, out_index))) : I; //decode mask, or d2s(1) if no mask - var ephemeral_pub = this.derive_public_key(recv_derivation, out_index, keys.spend.pub); + var mask = enc_mask + ? sc_sub( + enc_mask, + hash_to_scalar( + derivation_to_scalar(recv_derivation, out_index), + ), + ) + : I; //decode mask, or d2s(1) if no mask + var ephemeral_pub = this.derive_public_key( + recv_derivation, + out_index, + keys.spend.pub, + ); if (!ephemeral_pub) throw "Failed to generate key image"; - var ephemeral_sec = this.derive_secret_key(recv_derivation, out_index, keys.spend.sec); + var ephemeral_sec = this.derive_secret_key( + recv_derivation, + out_index, + keys.spend.sec, + ); var image = this.generate_key_image_2(ephemeral_pub, ephemeral_sec); return { in_ephemeral: { pub: ephemeral_pub, sec: ephemeral_sec, - mask: mask + mask: mask, }, - image: image + image: image, }; }; //curve and scalar functions; split out to make their host functions cleaner and more readable //inverts X coordinate -- this seems correct ^_^ -luigi1111 this.ge_neg = function(point) { - if (point.length !== 64){ - throw "expected 64 char hex string"; + if (point.length !== 64) { + throw "expected 64 char hex string"; } - return point.slice(0,62) + ((parseInt(point.slice(62,63), 16) + 8) % 16).toString(16) + point.slice(63,64); + return ( + point.slice(0, 62) + + ((parseInt(point.slice(62, 63), 16) + 8) % 16).toString(16) + + point.slice(63, 64) + ); }; //adds two points together, order does not matter @@ -852,8 +1058,16 @@ var cnUtil = function(currencyConfig) CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m); CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m); var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); - CNCrypto.ccall("sc_add", "void", ["number", "number", "number"], [derived_m, scalar1_m, scalar2_m]); - var res = CNCrypto.HEAPU8.subarray(derived_m, derived_m + STRUCT_SIZES.EC_SCALAR); + CNCrypto.ccall( + "sc_add", + "void", + ["number", "number", "number"], + [derived_m, scalar1_m, scalar2_m], + ); + var res = CNCrypto.HEAPU8.subarray( + derived_m, + derived_m + STRUCT_SIZES.EC_SCALAR, + ); CNCrypto._free(scalar1_m); CNCrypto._free(scalar2_m); CNCrypto._free(derived_m); @@ -870,8 +1084,16 @@ var cnUtil = function(currencyConfig) CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m); CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m); var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); - CNCrypto.ccall("sc_sub", "void", ["number", "number", "number"], [derived_m, scalar1_m, scalar2_m]); - var res = CNCrypto.HEAPU8.subarray(derived_m, derived_m + STRUCT_SIZES.EC_SCALAR); + CNCrypto.ccall( + "sc_sub", + "void", + ["number", "number", "number"], + [derived_m, scalar1_m, scalar2_m], + ); + var res = CNCrypto.HEAPU8.subarray( + derived_m, + derived_m + STRUCT_SIZES.EC_SCALAR, + ); CNCrypto._free(scalar1_m); CNCrypto._free(scalar2_m); CNCrypto._free(derived_m); @@ -883,12 +1105,24 @@ var cnUtil = function(currencyConfig) if (scalar1.length !== 64 || scalar2.length !== 64) { throw "Invalid input length!"; } - return d2s(JSBigInt(s2d(scalar1)).multiply(JSBigInt(s2d(scalar2))).remainder(l).toString()); + return d2s( + JSBigInt(s2d(scalar1)) + .multiply(JSBigInt(s2d(scalar2))) + .remainder(l) + .toString(), + ); }; //res = c - (ab) mod l; argument names copied from the signature implementation this.sc_mulsub = function(sigc, sec, k) { - if (k.length !== KEY_SIZE * 2 || sigc.length !== KEY_SIZE * 2 || sec.length !== KEY_SIZE * 2 || !this.valid_hex(k) || !this.valid_hex(sigc) || !this.valid_hex(sec)) { + if ( + k.length !== KEY_SIZE * 2 || + sigc.length !== KEY_SIZE * 2 || + sec.length !== KEY_SIZE * 2 || + !this.valid_hex(k) || + !this.valid_hex(sigc) || + !this.valid_hex(sec) + ) { throw "bad scalar"; } var sec_m = CNCrypto._malloc(KEY_SIZE); @@ -899,7 +1133,12 @@ var cnUtil = function(currencyConfig) CNCrypto.HEAPU8.set(hextobin(k), k_m); var res_m = CNCrypto._malloc(KEY_SIZE); - CNCrypto.ccall("sc_mulsub", "void", ["number", "number", "number", "number"], [res_m, sigc_m, sec_m, k_m]); + CNCrypto.ccall( + "sc_mulsub", + "void", + ["number", "number", "number", "number"], + [res_m, sigc_m, sec_m, k_m], + ); res = CNCrypto.HEAPU8.subarray(res_m, res_m + KEY_SIZE); CNCrypto._free(k_m); CNCrypto._free(sec_m); @@ -941,7 +1180,13 @@ var cnUtil = function(currencyConfig) if (c.length !== 64 || P.length !== 64 || r.length !== 64) { throw "Invalid input length!"; } - return bintohex(nacl.ll.ge_double_scalarmult_base_vartime(hextobin(c), hextobin(P), hextobin(r))); + return bintohex( + nacl.ll.ge_double_scalarmult_base_vartime( + hextobin(c), + hextobin(P), + hextobin(r), + ), + ); }; //res = a * Hp(B) + c*D @@ -979,14 +1224,24 @@ var cnUtil = function(currencyConfig) };*/ this.ge_double_scalarmult_postcomp_vartime = function(r, P, c, I) { - if (c.length !== 64 || P.length !== 64 || r.length !== 64 || I.length !== 64) { + if ( + c.length !== 64 || + P.length !== 64 || + r.length !== 64 || + I.length !== 64 + ) { throw "Invalid input length!"; } var Pb = this.hash_to_ec_2(P); - return bintohex(nacl.ll.ge_double_scalarmult_postcomp_vartime(hextobin(r), hextobin(Pb), hextobin(c), hextobin(I))); + return bintohex( + nacl.ll.ge_double_scalarmult_postcomp_vartime( + hextobin(r), + hextobin(Pb), + hextobin(c), + hextobin(I), + ), + ); }; - - //begin RCT functions @@ -996,70 +1251,78 @@ var cnUtil = function(currencyConfig) //size: ring size //nrings: number of rings //extensible borromean signatures - this.genBorromean = function(xv, pm, iv, size, nrings){ - if (xv.length !== nrings){ - throw "wrong xv length " + xv.length; - } - if (pm.length !== size){ - throw "wrong pm size " + pm.length; - } - for (var i = 0; i < pm.length; i++){ - if (pm[i].length !== nrings){ - throw "wrong pm[" + i + "] length " + pm[i].length; + this.genBorromean = function(xv, pm, iv, size, nrings) { + if (xv.length !== nrings) { + throw "wrong xv length " + xv.length; } + if (pm.length !== size) { + throw "wrong pm size " + pm.length; } - if (iv.length !== nrings){ - throw "wrong iv length " + iv.length; + for (var i = 0; i < pm.length; i++) { + if (pm[i].length !== nrings) { + throw "wrong pm[" + i + "] length " + pm[i].length; + } } - for (var i = 0; i < iv.length; i++){ - if (iv[i] >= size){ - throw "bad indices value at: " + i + ": " + iv[i]; + if (iv.length !== nrings) { + throw "wrong iv length " + iv.length; } + for (var i = 0; i < iv.length; i++) { + if (iv[i] >= size) { + throw "bad indices value at: " + i + ": " + iv[i]; + } } //signature struct var bb = { - s: [], - ee: "" + s: [], + ee: "", }; //signature pubkey matrix var L = []; //add needed sub vectors (1 per ring size) - for (var i = 0; i < size; i++){ - bb.s[i] = []; - L[i] = []; + for (var i = 0; i < size; i++) { + bb.s[i] = []; + L[i] = []; } //compute starting at the secret index to the last row var index; var alpha = []; - for (var i = 0; i < nrings; i++){ - index = parseInt(iv[i]); - alpha[i] = random_scalar(); - L[index][i] = ge_scalarmult_base(alpha[i]); - for (var j = index + 1; j < size; j++){ - bb.s[j][i] = random_scalar(); - var c = hash_to_scalar(L[j-1][i]); - L[j][i] = ge_double_scalarmult_base_vartime(c, pm[j][i], bb.s[j][i]); - } + for (var i = 0; i < nrings; i++) { + index = parseInt(iv[i]); + alpha[i] = random_scalar(); + L[index][i] = ge_scalarmult_base(alpha[i]); + for (var j = index + 1; j < size; j++) { + bb.s[j][i] = random_scalar(); + var c = hash_to_scalar(L[j - 1][i]); + L[j][i] = ge_double_scalarmult_base_vartime( + c, + pm[j][i], + bb.s[j][i], + ); + } } //hash last row to create ee var ltemp = ""; - for (var i = 0; i < nrings; i++){ - ltemp += L[size-1][i]; + for (var i = 0; i < nrings; i++) { + ltemp += L[size - 1][i]; } bb.ee = hash_to_scalar(ltemp); //compute the rest from 0 to secret index - for (var i = 0; i < nrings; i++){ - var cc = bb.ee - for (var j = 0; j < iv[i]; j++){ - bb.s[j][i] = random_scalar(); - var LL = ge_double_scalarmult_base_vartime(cc, pm[j][i], bb.s[j][i]); - cc = hash_to_scalar(LL); - } - bb.s[j][i] = sc_mulsub(xv[i], cc, alpha[i]); + for (var i = 0; i < nrings; i++) { + var cc = bb.ee; + for (var j = 0; j < iv[i]; j++) { + bb.s[j][i] = random_scalar(); + var LL = ge_double_scalarmult_base_vartime( + cc, + pm[j][i], + bb.s[j][i], + ); + cc = hash_to_scalar(LL); + } + bb.s[j][i] = sc_mulsub(xv[i], cc, alpha[i]); } return bb; }; - + //proveRange //proveRange gives C, and mask such that \sumCi = C // c.f. http://eprint.iacr.org/2015/1098 section 5.1 @@ -1067,15 +1330,21 @@ var cnUtil = function(currencyConfig) // thus this proves that "amount" is in [0, s^n] (we assume s to be 4) (2 for now with v2 txes) // mask is a such that C = aG + bH, and b = amount //commitMaskObj = {C: commit, mask: mask} - this.proveRange = function(commitMaskObj, amount, nrings, enc_seed, exponent){ + this.proveRange = function( + commitMaskObj, + amount, + nrings, + enc_seed, + exponent, + ) { var size = 2; var C = I; //identity var mask = Z; //zero scalar var indices = d2b(amount); //base 2 for now var sig = { - Ci: [] - //exp: exponent //doesn't exist for now - } + Ci: [], + //exp: exponent //doesn't exist for now + }; /*payload stuff - ignore for now seeds = new Array(3); for (var i = 0; i < seeds.length; i++){ @@ -1085,34 +1354,34 @@ var cnUtil = function(currencyConfig) */ var ai = []; var PM = []; - for (var i = 0; i < size; i++){ - PM[i] = []; + for (var i = 0; i < size; i++) { + PM[i] = []; } var j; //start at index and fill PM left and right -- PM[0] holds Ci - for (i = 0; i < nrings; i++){ - ai[i] = random_scalar(); - j = indices[i]; - PM[j][i] = ge_scalarmult_base(ai[i]); - while (j > 0){ - j--; - PM[j][i] = ge_add(PM[j+1][i], H2[i]); //will need to use i*2 for base 4 (or different object) - } - j = indices[i]; - while (j < size - 1){ - j++; - PM[j][i] = ge_sub(PM[j-1][i], H2[i]); //will need to use i*2 for base 4 (or different object) - } - mask = sc_add(mask, ai[i]); + for (i = 0; i < nrings; i++) { + ai[i] = random_scalar(); + j = indices[i]; + PM[j][i] = ge_scalarmult_base(ai[i]); + while (j > 0) { + j--; + PM[j][i] = ge_add(PM[j + 1][i], H2[i]); //will need to use i*2 for base 4 (or different object) + } + j = indices[i]; + while (j < size - 1) { + j++; + PM[j][i] = ge_sub(PM[j - 1][i], H2[i]); //will need to use i*2 for base 4 (or different object) + } + mask = sc_add(mask, ai[i]); } /* * some more payload stuff here */ //copy commitments to sig and sum them to commitment - for (i = 0; i < nrings; i++){ - //if (i < nrings - 1) //for later version - sig.Ci[i] = PM[0][i]; - C = ge_add(C, PM[0][i]); + for (i = 0; i < nrings; i++) { + //if (i < nrings - 1) //for later version + sig.Ci[i] = PM[0][i]; + C = ge_add(C, PM[0][i]); } /* exponent stuff - ignore for now if (exponent){ @@ -1126,45 +1395,54 @@ var cnUtil = function(currencyConfig) commitMaskObj.mask = mask; return sig; }; - - - function array_hash_to_scalar(array){ - var buf = "" - for (var i = 0; i < array.length; i++){ - if (typeof array[i] !== "string"){throw "unexpected array element";} - buf += array[i]; + + function array_hash_to_scalar(array) { + var buf = ""; + for (var i = 0; i < array.length; i++) { + if (typeof array[i] !== "string") { + throw "unexpected array element"; + } + buf += array[i]; } return hash_to_scalar(buf); } - + // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // we presently only support matrices of 2 rows (pubkey, commitment) // this is a simplied MLSAG_Gen function to reflect that // because we don't want to force same secret column for all inputs - this.MLSAG_Gen = function(message, pk, xx, kimg, index){ + this.MLSAG_Gen = function(message, pk, xx, kimg, index) { var cols = pk.length; //ring size - if (index >= cols){throw "index out of range";} + if (index >= cols) { + throw "index out of range"; + } var rows = pk[0].length; //number of signature rows (always 2) - if (rows !== 2){throw "wrong row count";} - for (var i = 0; i < cols; i++){ - if (pk[i].length !== rows){throw "pk is not rectangular";} + if (rows !== 2) { + throw "wrong row count"; + } + for (var i = 0; i < cols; i++) { + if (pk[i].length !== rows) { + throw "pk is not rectangular"; + } } - if (xx.length !== rows){throw "bad xx size";} - + if (xx.length !== rows) { + throw "bad xx size"; + } + var c_old = ""; var alpha = []; - + var rv = { - ss: [], - cc: null + ss: [], + cc: null, }; - for (i = 0; i < cols; i++){ - rv.ss[i] = []; + for (i = 0; i < cols; i++) { + rv.ss[i] = []; } var toHash = []; //holds 6 elements: message, pubkey, dsRow L, dsRow R, commitment, ndsRow L toHash[0] = message; - + //secret index (pubkey section) alpha[0] = random_scalar(); //need to save alphas for later toHash[1] = pk[index][0]; //secret index pubkey @@ -1174,47 +1452,62 @@ var cnUtil = function(currencyConfig) alpha[1] = random_scalar(); toHash[4] = pk[index][1]; //secret index commitment toHash[5] = ge_scalarmult_base(alpha[1]); //ndsRow L - + c_old = array_hash_to_scalar(toHash); - + i = (index + 1) % cols; - if (i === 0){ - rv.cc = c_old; - } - while (i != index){ - rv.ss[i][0] = random_scalar(); //dsRow ss - rv.ss[i][1] = random_scalar(); //ndsRow ss - - //!secret index (pubkey section) - toHash[1] = pk[i][0]; - toHash[2] = ge_double_scalarmult_base_vartime(c_old, pk[i][0], rv.ss[i][0]); - toHash[3] = ge_double_scalarmult_postcomp_vartime(rv.ss[i][0], pk[i][0], c_old, kimg); - //!secret index (commitment section) - toHash[4] = pk[i][1]; - toHash[5] = ge_double_scalarmult_base_vartime(c_old, pk[i][1], rv.ss[i][1]); - c_old = array_hash_to_scalar(toHash); //hash to get next column c - i = (i + 1) % cols; - if (i === 0){ + if (i === 0) { rv.cc = c_old; } + while (i != index) { + rv.ss[i][0] = random_scalar(); //dsRow ss + rv.ss[i][1] = random_scalar(); //ndsRow ss + + //!secret index (pubkey section) + toHash[1] = pk[i][0]; + toHash[2] = ge_double_scalarmult_base_vartime( + c_old, + pk[i][0], + rv.ss[i][0], + ); + toHash[3] = ge_double_scalarmult_postcomp_vartime( + rv.ss[i][0], + pk[i][0], + c_old, + kimg, + ); + //!secret index (commitment section) + toHash[4] = pk[i][1]; + toHash[5] = ge_double_scalarmult_base_vartime( + c_old, + pk[i][1], + rv.ss[i][1], + ); + c_old = array_hash_to_scalar(toHash); //hash to get next column c + i = (i + 1) % cols; + if (i === 0) { + rv.cc = c_old; + } } - for (i = 0; i < rows; i++){ - rv.ss[index][i] = sc_mulsub(c_old, xx[i], alpha[i]); + for (i = 0; i < rows; i++) { + rv.ss[index][i] = sc_mulsub(c_old, xx[i], alpha[i]); } return rv; }; - + //prepares for MLSAG_Gen - this.proveRctMG = function(message, pubs, inSk, kimg, mask, Cout, index){ + this.proveRctMG = function(message, pubs, inSk, kimg, mask, Cout, index) { var cols = pubs.length; - if (cols < 3){throw "cols must be > 2 (mixin)";} + if (cols < 3) { + throw "cols must be > 2 (mixin)"; + } var xx = []; var PK = []; //fill pubkey matrix (copy destination, subtract commitments) - for (var i = 0; i < cols; i++){ - PK[i] = []; - PK[i][0] = pubs[i].dest; - PK[i][1] = ge_sub(pubs[i].mask, Cout); + for (var i = 0; i < cols; i++) { + PK[i] = []; + PK[i][0] = pubs[i].dest; + PK[i][1] = ge_sub(pubs[i].mask, Cout); } xx[0] = inSk.x; xx[1] = sc_sub(inSk.a, mask); @@ -1228,7 +1521,7 @@ var cnUtil = function(currencyConfig) var buf = serialize_range_proofs(rv); hashes += this.cn_fast_hash(buf); return this.cn_fast_hash(hashes); - } + }; function serialize_range_proofs(rv) { var buf = ""; @@ -1245,7 +1538,7 @@ var cnUtil = function(currencyConfig) } return buf; } - + //message is normal prefix hash //inSk is vector of x,a //kimg is vector of kimg @@ -1256,77 +1549,125 @@ var cnUtil = function(currencyConfig) //amountKeys is vector of scalars //indices is vector //txnFee is string - this.genRct = function(message, inSk, kimg, /*destinations, */inAmounts, outAmounts, mixRing, amountKeys, indices, txnFee){ - if (outAmounts.length !== amountKeys.length ){throw "different number of amounts/amount_keys";} - for (var i = 0; i < mixRing.length; i++){ - if (mixRing[i].length <= indices[i]){throw "bad mixRing/index size";} - } - if (mixRing.length !== inSk.length){throw "mismatched mixRing/inSk";} - if (inAmounts.length !== inSk.length){throw "mismatched inAmounts/inSk";} - if (indices.length !== inSk.length){throw "mismatched indices/inSk";} - + this.genRct = function( + message, + inSk, + kimg, + /*destinations, */ inAmounts, + outAmounts, + mixRing, + amountKeys, + indices, + txnFee, + ) { + if (outAmounts.length !== amountKeys.length) { + throw "different number of amounts/amount_keys"; + } + for (var i = 0; i < mixRing.length; i++) { + if (mixRing[i].length <= indices[i]) { + throw "bad mixRing/index size"; + } + } + if (mixRing.length !== inSk.length) { + throw "mismatched mixRing/inSk"; + } + if (inAmounts.length !== inSk.length) { + throw "mismatched inAmounts/inSk"; + } + if (indices.length !== inSk.length) { + throw "mismatched indices/inSk"; + } + rv = { - type: inSk.length === 1 ? RCTTypeFull : RCTTypeSimple, - message: message, - outPk: [], - p: { - rangeSigs: [], - MGs: [] - }, - ecdhInfo: [], - txnFee: txnFee.toString(), - pseudoOuts: [] + type: inSk.length === 1 ? RCTTypeFull : RCTTypeSimple, + message: message, + outPk: [], + p: { + rangeSigs: [], + MGs: [], + }, + ecdhInfo: [], + txnFee: txnFee.toString(), + pseudoOuts: [], }; - + var sumout = Z; var cmObj = { - C: null, - mask: null + C: null, + mask: null, }; var nrings = 64; //for base 2/current //compute range proofs, etc - for (i = 0; i < outAmounts.length; i++){ - var teststart = new Date().getTime(); - rv.p.rangeSigs[i] = this.proveRange(cmObj, outAmounts[i], nrings, 0, 0); - var testfinish = new Date().getTime() - teststart; - console.log("Time take for range proof " + i + ": " + testfinish); - rv.outPk[i] = cmObj.C; - sumout = sc_add(sumout, cmObj.mask); - rv.ecdhInfo[i] = this.encode_rct_ecdh({mask: cmObj.mask, amount: d2s(outAmounts[i])}, amountKeys[i]); + for (i = 0; i < outAmounts.length; i++) { + var teststart = new Date().getTime(); + rv.p.rangeSigs[i] = this.proveRange( + cmObj, + outAmounts[i], + nrings, + 0, + 0, + ); + var testfinish = new Date().getTime() - teststart; + console.log("Time take for range proof " + i + ": " + testfinish); + rv.outPk[i] = cmObj.C; + sumout = sc_add(sumout, cmObj.mask); + rv.ecdhInfo[i] = this.encode_rct_ecdh( + { mask: cmObj.mask, amount: d2s(outAmounts[i]) }, + amountKeys[i], + ); } //simple - if (rv.type === 2){ - var ai = []; - var sumpouts = Z; - //create pseudoOuts - for (i = 0; i < inAmounts.length - 1; i++){ - ai[i] = random_scalar(); - sumpouts = sc_add(sumpouts, ai[i]); + if (rv.type === 2) { + var ai = []; + var sumpouts = Z; + //create pseudoOuts + for (i = 0; i < inAmounts.length - 1; i++) { + ai[i] = random_scalar(); + sumpouts = sc_add(sumpouts, ai[i]); + rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]); + } + ai[i] = sc_sub(sumout, sumpouts); rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]); - } - ai[i] = sc_sub(sumout, sumpouts); - rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]); - var full_message = this.get_pre_mlsag_hash(rv); - for (i = 0; i < inAmounts.length; i++){ - rv.p.MGs.push(this.proveRctMG(full_message, mixRing[i], inSk[i], kimg[i], ai[i], rv.pseudoOuts[i], indices[i])); - } + var full_message = this.get_pre_mlsag_hash(rv); + for (i = 0; i < inAmounts.length; i++) { + rv.p.MGs.push( + this.proveRctMG( + full_message, + mixRing[i], + inSk[i], + kimg[i], + ai[i], + rv.pseudoOuts[i], + indices[i], + ), + ); + } } else { - var sumC = I; - //get sum of output commitments to use in MLSAG - for (i = 0; i < rv.outPk.length; i++){ - sumC = ge_add(sumC, rv.outPk[i]); - } - sumC = ge_add(sumC, ge_scalarmult(H, d2s(rv.txnFee))); - var full_message = this.get_pre_mlsag_hash(rv); - rv.p.MGs.push(this.proveRctMG(full_message, mixRing[0], inSk[0], kimg[0], sumout, sumC, indices[0])); + var sumC = I; + //get sum of output commitments to use in MLSAG + for (i = 0; i < rv.outPk.length; i++) { + sumC = ge_add(sumC, rv.outPk[i]); + } + sumC = ge_add(sumC, ge_scalarmult(H, d2s(rv.txnFee))); + var full_message = this.get_pre_mlsag_hash(rv); + rv.p.MGs.push( + this.proveRctMG( + full_message, + mixRing[0], + inSk[0], + kimg[0], + sumout, + sumC, + indices[0], + ), + ); } return rv; }; //end RCT functions - this.add_pub_key_to_extra = function(extra, pubkey) { if (pubkey.length !== 64) throw "Invalid pubkey length"; // Append pubkey tag and pubkey @@ -1336,16 +1677,18 @@ var cnUtil = function(currencyConfig) this.add_nonce_to_extra = function(extra, nonce) { // Append extra nonce - if ((nonce.length % 2) !== 0) { + if (nonce.length % 2 !== 0) { throw "Invalid extra nonce"; } - if ((nonce.length / 2) > TX_EXTRA_NONCE_MAX_COUNT) { - throw "Extra nonce must be at most " + TX_EXTRA_NONCE_MAX_COUNT + " bytes"; + if (nonce.length / 2 > TX_EXTRA_NONCE_MAX_COUNT) { + throw "Extra nonce must be at most " + + TX_EXTRA_NONCE_MAX_COUNT + + " bytes"; } // Add nonce tag extra += TX_EXTRA_TAGS.NONCE; // Encode length of nonce - extra += ('0' + (nonce.length / 2).toString(16)).slice(-2); + extra += ("0" + (nonce.length / 2).toString(16)).slice(-2); // Write nonce extra += nonce; return extra; @@ -1355,7 +1698,7 @@ var cnUtil = function(currencyConfig) if (payment_id.length !== 64 && payment_id.length !== 16) { throw "Invalid payment id"; } - var res = ''; + var res = ""; if (pid_encrypt) { res += TX_EXTRA_NONCE_TAGS.ENCRYPTED_PAYMENT_ID; } else { @@ -1368,7 +1711,9 @@ var cnUtil = function(currencyConfig) this.abs_to_rel_offsets = function(offsets) { if (offsets.length === 0) return offsets; for (var i = offsets.length - 1; i >= 1; --i) { - offsets[i] = new JSBigInt(offsets[i]).subtract(offsets[i - 1]).toString(); + offsets[i] = new JSBigInt(offsets[i]) + .subtract(offsets[i - 1]) + .toString(); } return offsets; }; @@ -1379,7 +1724,7 @@ var cnUtil = function(currencyConfig) }; this.get_tx_hash = function(tx) { - if (typeof(tx) === 'string') { + if (typeof tx === "string") { return this.cn_fast_hash(tx); } else { return this.cn_fast_hash(this.serialize_tx(tx)); @@ -1472,7 +1817,7 @@ var cnUtil = function(currencyConfig) var hash = this.cn_fast_hash(hashes); return { raw: buf, - hash: hash + hash: hash, }; }; @@ -1498,31 +1843,81 @@ var cnUtil = function(currencyConfig) return buf; }; - this.generate_ring_signature = function(prefix_hash, k_image, keys, sec, real_index) { + this.generate_ring_signature = function( + prefix_hash, + k_image, + keys, + sec, + real_index, + ) { if (k_image.length !== STRUCT_SIZES.KEY_IMAGE * 2) { throw "invalid key image length"; } if (sec.length !== KEY_SIZE * 2) { throw "Invalid secret key length"; } - if (prefix_hash.length !== HASH_SIZE * 2 || !this.valid_hex(prefix_hash)) { + if ( + prefix_hash.length !== HASH_SIZE * 2 || + !this.valid_hex(prefix_hash) + ) { throw "Invalid prefix hash"; } if (real_index >= keys.length || real_index < 0) { throw "real_index is invalid"; } - var _ge_tobytes = CNCrypto.cwrap("ge_tobytes", "void", ["number", "number"]); - var _ge_p3_tobytes = CNCrypto.cwrap("ge_p3_tobytes", "void", ["number", "number"]); - var _ge_scalarmult_base = CNCrypto.cwrap("ge_scalarmult_base", "void", ["number", "number"]); - var _ge_scalarmult = CNCrypto.cwrap("ge_scalarmult", "void", ["number", "number", "number"]); - var _sc_add = CNCrypto.cwrap("sc_add", "void", ["number", "number", "number"]); - var _sc_sub = CNCrypto.cwrap("sc_sub", "void", ["number", "number", "number"]); - var _sc_mulsub = CNCrypto.cwrap("sc_mulsub", "void", ["number", "number", "number", "number"]); + var _ge_tobytes = CNCrypto.cwrap("ge_tobytes", "void", [ + "number", + "number", + ]); + var _ge_p3_tobytes = CNCrypto.cwrap("ge_p3_tobytes", "void", [ + "number", + "number", + ]); + var _ge_scalarmult_base = CNCrypto.cwrap("ge_scalarmult_base", "void", [ + "number", + "number", + ]); + var _ge_scalarmult = CNCrypto.cwrap("ge_scalarmult", "void", [ + "number", + "number", + "number", + ]); + var _sc_add = CNCrypto.cwrap("sc_add", "void", [ + "number", + "number", + "number", + ]); + var _sc_sub = CNCrypto.cwrap("sc_sub", "void", [ + "number", + "number", + "number", + ]); + var _sc_mulsub = CNCrypto.cwrap("sc_mulsub", "void", [ + "number", + "number", + "number", + "number", + ]); var _sc_0 = CNCrypto.cwrap("sc_0", "void", ["number"]); - var _ge_double_scalarmult_base_vartime = CNCrypto.cwrap("ge_double_scalarmult_base_vartime", "void", ["number", "number", "number", "number"]); - var _ge_double_scalarmult_precomp_vartime = CNCrypto.cwrap("ge_double_scalarmult_precomp_vartime", "void", ["number", "number", "number", "number", "number"]); - var _ge_frombytes_vartime = CNCrypto.cwrap("ge_frombytes_vartime", "number", ["number", "number"]); - var _ge_dsm_precomp = CNCrypto.cwrap("ge_dsm_precomp", "void", ["number", "number"]); + var _ge_double_scalarmult_base_vartime = CNCrypto.cwrap( + "ge_double_scalarmult_base_vartime", + "void", + ["number", "number", "number", "number"], + ); + var _ge_double_scalarmult_precomp_vartime = CNCrypto.cwrap( + "ge_double_scalarmult_precomp_vartime", + "void", + ["number", "number", "number", "number", "number"], + ); + var _ge_frombytes_vartime = CNCrypto.cwrap( + "ge_frombytes_vartime", + "number", + ["number", "number"], + ); + var _ge_dsm_precomp = CNCrypto.cwrap("ge_dsm_precomp", "void", [ + "number", + "number", + ]); var buf_size = STRUCT_SIZES.EC_POINT * 2 * keys.length; var buf_m = CNCrypto._malloc(buf_size); @@ -1575,14 +1970,32 @@ var cnUtil = function(currencyConfig) CNCrypto.HEAPU8.set(hextobin(this.random_scalar()), sig_c(i)); CNCrypto.HEAPU8.set(hextobin(this.random_scalar()), sig_r(i)); CNCrypto.HEAPU8.set(hextobin(keys[i]), pub_m); - if (CNCrypto.ccall("ge_frombytes_vartime", "void", ["number", "number"], [tmp3_m, pub_m]) !== 0) { + if ( + CNCrypto.ccall( + "ge_frombytes_vartime", + "void", + ["number", "number"], + [tmp3_m, pub_m], + ) !== 0 + ) { throw "Failed to call ge_frombytes_vartime"; } - _ge_double_scalarmult_base_vartime(tmp2_m, sig_c(i), tmp3_m, sig_r(i)); + _ge_double_scalarmult_base_vartime( + tmp2_m, + sig_c(i), + tmp3_m, + sig_r(i), + ); _ge_tobytes(buf_a(i), tmp2_m); var ec = this.hash_to_ec(keys[i]); CNCrypto.HEAPU8.set(hextobin(ec), tmp3_m); - _ge_double_scalarmult_precomp_vartime(tmp2_m, sig_r(i), tmp3_m, sig_c(i), image_pre_m); + _ge_double_scalarmult_precomp_vartime( + tmp2_m, + sig_r(i), + tmp3_m, + sig_c(i), + image_pre_m, + ); _ge_tobytes(buf_b(i), tmp2_m); _sc_add(sum_m, sum_m, sig_c(i)); } @@ -1592,10 +2005,17 @@ var cnUtil = function(currencyConfig) CNCrypto.HEAPU8.set(hextobin(scalar), h_m); _sc_sub(sig_c(real_index), h_m, sum_m); _sc_mulsub(sig_r(real_index), sig_c(real_index), sec_m, k_m); - var sig_data = bintohex(CNCrypto.HEAPU8.subarray(sig_m, sig_m + sig_size)); + var sig_data = bintohex( + CNCrypto.HEAPU8.subarray(sig_m, sig_m + sig_size), + ); var sigs = []; for (var k = 0; k < keys.length; k++) { - sigs.push(sig_data.slice(STRUCT_SIZES.SIGNATURE * 2 * k, STRUCT_SIZES.SIGNATURE * 2 * (k + 1))); + sigs.push( + sig_data.slice( + STRUCT_SIZES.SIGNATURE * 2 * k, + STRUCT_SIZES.SIGNATURE * 2 * (k + 1), + ), + ); } CNCrypto._free(image_m); CNCrypto._free(image_unp_m); @@ -1612,18 +2032,35 @@ var cnUtil = function(currencyConfig) return sigs; }; - this.construct_tx = function(keys, sources, dsts, fee_amount, payment_id, pid_encrypt, realDestViewKey, unlock_time, rct, nettype) { + this.construct_tx = function( + keys, + sources, + dsts, + fee_amount, + payment_id, + pid_encrypt, + realDestViewKey, + unlock_time, + rct, + nettype, + ) { //we move payment ID stuff here, because we need txkey to encrypt var txkey = this.random_keypair(); console.log(txkey); - var extra = ''; + var extra = ""; if (payment_id) { if (pid_encrypt && payment_id.length !== INTEGRATED_ID_SIZE * 2) { - throw "payment ID must be " + INTEGRATED_ID_SIZE + " bytes to be encrypted!"; + throw "payment ID must be " + + INTEGRATED_ID_SIZE + + " bytes to be encrypted!"; } console.log("Adding payment id: " + payment_id); - if (pid_encrypt) { //get the derivation from our passed viewkey, then hash that + tail to get encryption key - var pid_key = this.cn_fast_hash(this.generate_key_derivation(realDestViewKey, txkey.sec) + ENCRYPTED_PAYMENT_ID_TAIL.toString(16)).slice(0, INTEGRATED_ID_SIZE * 2); + if (pid_encrypt) { + //get the derivation from our passed viewkey, then hash that + tail to get encryption key + var pid_key = this.cn_fast_hash( + this.generate_key_derivation(realDestViewKey, txkey.sec) + + ENCRYPTED_PAYMENT_ID_TAIL.toString(16), + ).slice(0, INTEGRATED_ID_SIZE * 2); console.log("Txkeys:", txkey, "Payment ID key:", pid_key); payment_id = this.hex_xor(payment_id, pid_key); } @@ -1636,7 +2073,7 @@ var cnUtil = function(currencyConfig) version: rct ? CURRENT_TX_VERSION : OLD_TX_VERSION, extra: extra, vin: [], - vout: [] + vout: [], }; if (rct) { tx.rct_signatures = {}; @@ -1647,24 +2084,36 @@ var cnUtil = function(currencyConfig) var in_contexts = []; var inputs_money = JSBigInt.ZERO; var i, j; - console.log('Sources: '); + console.log("Sources: "); //run the for loop twice to sort ins by key image //first generate key image and other construction data to sort it all in one go for (i = 0; i < sources.length; i++) { - console.log(i + ': ' + this.formatMoneyFull(sources[i].amount)); + console.log(i + ": " + this.formatMoneyFull(sources[i].amount)); if (sources[i].real_out >= sources[i].outputs.length) { throw "real index >= outputs.length"; } - var res = this.generate_key_image_helper_rct(keys, sources[i].real_out_tx_key, sources[i].real_out_in_tx, sources[i].mask); //mask will be undefined for non-rct - if (res.in_ephemeral.pub !== sources[i].outputs[sources[i].real_out].key) { + var res = this.generate_key_image_helper_rct( + keys, + sources[i].real_out_tx_key, + sources[i].real_out_in_tx, + sources[i].mask, + ); //mask will be undefined for non-rct + if ( + res.in_ephemeral.pub !== + sources[i].outputs[sources[i].real_out].key + ) { throw "in_ephemeral.pub != source.real_out.key"; } sources[i].key_image = res.image; sources[i].in_ephemeral = res.in_ephemeral; } //sort ins - sources.sort(function(a,b){ - return JSBigInt.parse(a.key_image, 16).compare(JSBigInt.parse(b.key_image, 16)) * -1 ; + sources.sort(function(a, b) { + return ( + JSBigInt.parse(a.key_image, 16).compare( + JSBigInt.parse(b.key_image, 16), + ) * -1 + ); }); //copy the sorted sources data to tx for (i = 0; i < sources.length; i++) { @@ -1678,7 +2127,9 @@ var cnUtil = function(currencyConfig) for (j = 0; j < sources[i].outputs.length; ++j) { input_to_key.key_offsets.push(sources[i].outputs[j].index); } - input_to_key.key_offsets = this.abs_to_rel_offsets(input_to_key.key_offsets); + input_to_key.key_offsets = this.abs_to_rel_offsets( + input_to_key.key_offsets, + ); tx.vin.push(input_to_key); } var outputs_money = JSBigInt.ZERO; @@ -1689,48 +2140,65 @@ var cnUtil = function(currencyConfig) throw "dst.amount < 0"; //amount can be zero if no change } dsts[i].keys = this.decode_address(dsts[i].address, nettype); - + // R = rD for subaddresses - if(this.is_subaddress(dsts[i].address, nettype)) { - if (typeof payment_id !== 'undefined' && payment_id) { // this could stand to be placed earlier in the function but we save repeating a little algo time this way - throw "Payment ID must not be supplied when sending to a subaddress" + if (this.is_subaddress(dsts[i].address, nettype)) { + if (typeof payment_id !== "undefined" && payment_id) { + // this could stand to be placed earlier in the function but we save repeating a little algo time this way + throw "Payment ID must not be supplied when sending to a subaddress"; } txkey.pub = this.ge_scalarmult(dsts[i].keys.spend, txkey.sec); } - + var out_derivation; - + // send change to ourselves - if(dsts[i].keys.view == keys.view.pub) { - out_derivation = this.generate_key_derivation(txkey.pub, keys.view.sec); - } - - else { - out_derivation = this.generate_key_derivation(dsts[i].keys.view, txkey.sec); + if (dsts[i].keys.view == keys.view.pub) { + out_derivation = this.generate_key_derivation( + txkey.pub, + keys.view.sec, + ); + } else { + out_derivation = this.generate_key_derivation( + dsts[i].keys.view, + txkey.sec, + ); } - + if (rct) { - amountKeys.push(this.derivation_to_scalar(out_derivation, out_index)); + amountKeys.push( + this.derivation_to_scalar(out_derivation, out_index), + ); } - var out_ephemeral_pub = this.derive_public_key(out_derivation, out_index, dsts[i].keys.spend); + var out_ephemeral_pub = this.derive_public_key( + out_derivation, + out_index, + dsts[i].keys.spend, + ); var out = { - amount: dsts[i].amount.toString() + amount: dsts[i].amount.toString(), }; // txout_to_key out.target = { type: "txout_to_key", - key: out_ephemeral_pub + key: out_ephemeral_pub, }; tx.vout.push(out); ++out_index; outputs_money = outputs_money.add(dsts[i].amount); } - + // add pub key to extra after we know whether to use R = rG or R = rD tx.extra = this.add_pub_key_to_extra(tx.extra, txkey.pub); - + if (outputs_money.add(fee_amount).compare(inputs_money) > 0) { - throw "outputs money (" + this.formatMoneyFull(outputs_money) + ") + fee (" + this.formatMoneyFull(fee_amount) + ") > inputs money (" + this.formatMoneyFull(inputs_money) + ")"; + throw "outputs money (" + + this.formatMoneyFull(outputs_money) + + ") + fee (" + + this.formatMoneyFull(fee_amount) + + ") > inputs money (" + + this.formatMoneyFull(inputs_money) + + ")"; } if (!rct) { for (i = 0; i < sources.length; ++i) { @@ -1738,11 +2206,17 @@ var cnUtil = function(currencyConfig) for (j = 0; j < sources[i].outputs.length; ++j) { src_keys.push(sources[i].outputs[j].key); } - var sigs = this.generate_ring_signature(this.get_tx_prefix_hash(tx), tx.vin[i].k_image, src_keys, - in_contexts[i].sec, sources[i].real_out); + var sigs = this.generate_ring_signature( + this.get_tx_prefix_hash(tx), + tx.vin[i].k_image, + src_keys, + in_contexts[i].sec, + sources[i].real_out, + ); tx.signatures.push(sigs); } - } else { //rct + } else { + //rct var txnFee = fee_amount; var keyimages = []; var inSk = []; @@ -1753,17 +2227,18 @@ var cnUtil = function(currencyConfig) keyimages.push(tx.vin[i].k_image); inSk.push({ x: in_contexts[i].sec, - a: in_contexts[i].mask + a: in_contexts[i].mask, }); inAmounts.push(tx.vin[i].amount); - if (in_contexts[i].mask !== I) {//if input is rct (has a valid mask), 0 out amount + if (in_contexts[i].mask !== I) { + //if input is rct (has a valid mask), 0 out amount tx.vin[i].amount = "0"; } mixRing[i] = []; for (j = 0; j < sources[i].outputs.length; j++) { mixRing[i].push({ dest: sources[i].outputs[j].key, - mask: sources[i].outputs[j].commit + mask: sources[i].outputs[j].commit, }); } indices.push(sources[i].real_out); @@ -1774,39 +2249,73 @@ var cnUtil = function(currencyConfig) tx.vout[i].amount = "0"; //zero out all rct outputs } var tx_prefix_hash = this.get_tx_prefix_hash(tx); - tx.rct_signatures = genRct(tx_prefix_hash, inSk, keyimages, /*destinations, */inAmounts, outAmounts, mixRing, amountKeys, indices, txnFee); - + tx.rct_signatures = genRct( + tx_prefix_hash, + inSk, + keyimages, + /*destinations, */ inAmounts, + outAmounts, + mixRing, + amountKeys, + indices, + txnFee, + ); } console.log(tx); return tx; }; - this.create_transaction = function(pub_keys, sec_keys, dsts, outputs, mix_outs, fake_outputs_count, fee_amount, payment_id, pid_encrypt, realDestViewKey, unlock_time, rct, nettype) { + this.create_transaction = function( + pub_keys, + sec_keys, + dsts, + outputs, + mix_outs, + fake_outputs_count, + fee_amount, + payment_id, + pid_encrypt, + realDestViewKey, + unlock_time, + rct, + nettype, + ) { unlock_time = unlock_time || 0; mix_outs = mix_outs || []; var i, j; if (dsts.length === 0) { - throw 'Destinations empty'; + throw "Destinations empty"; } if (mix_outs.length !== outputs.length && fake_outputs_count !== 0) { - throw 'Wrong number of mix outs provided (' + outputs.length + ' outputs, ' + mix_outs.length + ' mix outs)'; + throw "Wrong number of mix outs provided (" + + outputs.length + + " outputs, " + + mix_outs.length + + " mix outs)"; } for (i = 0; i < mix_outs.length; i++) { if ((mix_outs[i].outputs || []).length < fake_outputs_count) { - throw 'Not enough outputs to mix with'; + throw "Not enough outputs to mix with"; } } var keys = { view: { pub: pub_keys.view, - sec: sec_keys.view + sec: sec_keys.view, }, spend: { pub: pub_keys.spend, - sec: sec_keys.spend - } + sec: sec_keys.spend, + }, }; - if (!this.valid_keys(keys.view.pub, keys.view.sec, keys.spend.pub, keys.spend.sec)) { + if ( + !this.valid_keys( + keys.view.pub, + keys.view.sec, + keys.spend.pub, + keys.spend.sec, + ) + ) { throw "Invalid secret keys!"; } var needed_money = JSBigInt.ZERO; @@ -1818,14 +2327,14 @@ var cnUtil = function(currencyConfig) } var found_money = JSBigInt.ZERO; var sources = []; - console.log('Selected transfers: ', outputs); + console.log("Selected transfers: ", outputs); for (i = 0; i < outputs.length; ++i) { found_money = found_money.add(outputs[i].amount); if (found_money.compare(UINT64_MAX) !== -1) { throw "Input overflow!"; } var src = { - outputs: [] + outputs: [], }; src.amount = new JSBigInt(outputs[i].amount).toString(); if (mix_outs.length !== 0) { @@ -1834,21 +2343,26 @@ var cnUtil = function(currencyConfig) return new JSBigInt(a.global_index).compare(b.global_index); }); j = 0; - while ((src.outputs.length < fake_outputs_count) && (j < mix_outs[i].outputs.length)) { + while ( + src.outputs.length < fake_outputs_count && + j < mix_outs[i].outputs.length + ) { var out = mix_outs[i].outputs[j]; if (out.global_index === outputs[i].global_index) { - console.log('got mixin the same as output, skipping'); + console.log("got mixin the same as output, skipping"); j++; continue; } var oe = {}; oe.index = out.global_index.toString(); oe.key = out.public_key; - if (rct){ - if (out.rct){ - oe.commit = out.rct.slice(0,64); //add commitment from rct mix outs + if (rct) { + if (out.rct) { + oe.commit = out.rct.slice(0, 64); //add commitment from rct mix outs } else { - if (outputs[i].rct) {throw "mix rct outs missing commit";} + if (outputs[i].rct) { + throw "mix rct outs missing commit"; + } oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct mix input } } @@ -1857,18 +2371,23 @@ var cnUtil = function(currencyConfig) } } var real_oe = {}; - real_oe.index = new JSBigInt(outputs[i].global_index || 0).toString(); + real_oe.index = new JSBigInt( + outputs[i].global_index || 0, + ).toString(); real_oe.key = outputs[i].public_key; - if (rct){ + if (rct) { if (outputs[i].rct) { - real_oe.commit = outputs[i].rct.slice(0,64); //add commitment for real input + real_oe.commit = outputs[i].rct.slice(0, 64); //add commitment for real input } else { real_oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct input } } var real_index = src.outputs.length; for (j = 0; j < src.outputs.length; j++) { - if (new JSBigInt(real_oe.index).compare(src.outputs[j].index) < 0) { + if ( + new JSBigInt(real_oe.index).compare(src.outputs[j].index) < + 0 + ) { real_index = j; break; } @@ -1879,19 +2398,18 @@ var cnUtil = function(currencyConfig) // Real output entry index src.real_out = real_index; src.real_out_in_tx = outputs[i].index; - if (rct){ + if (rct) { if (outputs[i].rct) { - - src.mask = outputs[i].rct.slice(64,128); //encrypted + src.mask = outputs[i].rct.slice(64, 128); //encrypted } else { src.mask = null; //will be set by generate_key_image_helper_rct } } sources.push(src); } - console.log('sources: ', sources); + console.log("sources: ", sources); var change = { - amount: JSBigInt.ZERO + amount: JSBigInt.ZERO, }; var cmp = needed_money.compare(found_money); if (cmp < 0) { @@ -1900,9 +2418,24 @@ var cnUtil = function(currencyConfig) throw "early fee calculation != later"; } } else if (cmp > 0) { - throw "Need more money than found! (have: " + cnUtil.formatMoney(found_money) + " need: " + cnUtil.formatMoney(needed_money) + ")"; - } - return this.construct_tx(keys, sources, dsts, fee_amount, payment_id, pid_encrypt, realDestViewKey, unlock_time, rct, nettype); + throw "Need more money than found! (have: " + + cnUtil.formatMoney(found_money) + + " need: " + + cnUtil.formatMoney(needed_money) + + ")"; + } + return this.construct_tx( + keys, + sources, + dsts, + fee_amount, + payment_id, + pid_encrypt, + realDestViewKey, + unlock_time, + rct, + nettype, + ); }; this.estimateRctSize = function(inputs, mixin, outputs) { @@ -1910,18 +2443,18 @@ var cnUtil = function(currencyConfig) // tx prefix // first few bytes size += 1 + 6; - size += inputs * (1+6+(mixin+1)*3+32); // original C implementation is *2+32 but author advised to change 2 to 3 as key offsets are variable size and this constitutes a best guess + size += inputs * (1 + 6 + (mixin + 1) * 3 + 32); // original C implementation is *2+32 but author advised to change 2 to 3 as key offsets are variable size and this constitutes a best guess // vout - size += outputs * (6+32); + size += outputs * (6 + 32); // extra size += 40; // rct signatures // type size += 1; // rangeSigs - size += (2*64*32+32+64*32) * outputs; + size += (2 * 64 * 32 + 32 + 64 * 32) * outputs; // MGs - size += inputs * (32 * (mixin+1) + 32); + size += inputs * (32 * (mixin + 1) + 32); // mixRing - not serialized, can be reconstructed /* size += 2 * 32 * (mixin+1) * inputs; */ // pseudoOuts @@ -1952,48 +2485,58 @@ var cnUtil = function(currencyConfig) this.printDsts = function(dsts) { for (var i = 0; i < dsts.length; i++) { - console.log(dsts[i].address + ': ' + this.formatMoneyFull(dsts[i].amount)); + console.log( + dsts[i].address + ": " + this.formatMoneyFull(dsts[i].amount), + ); } }; this.formatMoneyFull = function(units) { units = units.toString(); - var symbol = units[0] === '-' ? '-' : ''; - if (symbol === '-') { + var symbol = units[0] === "-" ? "-" : ""; + if (symbol === "-") { units = units.slice(1); } var decimal; if (units.length >= config.coinUnitPlaces) { - decimal = units.substr(units.length - config.coinUnitPlaces, config.coinUnitPlaces); + decimal = units.substr( + units.length - config.coinUnitPlaces, + config.coinUnitPlaces, + ); } else { - decimal = padLeft(units, config.coinUnitPlaces, '0'); + decimal = padLeft(units, config.coinUnitPlaces, "0"); } - return symbol + (units.substr(0, units.length - config.coinUnitPlaces) || '0') + '.' + decimal; + return ( + symbol + + (units.substr(0, units.length - config.coinUnitPlaces) || "0") + + "." + + decimal + ); }; this.formatMoneyFullSymbol = function(units) { - return this.formatMoneyFull(units) + ' ' + config.coinSymbol; + return this.formatMoneyFull(units) + " " + config.coinSymbol; }; this.formatMoney = function(units) { - var f = trimRight(this.formatMoneyFull(units), '0'); - if (f[f.length - 1] === '.') { + var f = trimRight(this.formatMoneyFull(units), "0"); + if (f[f.length - 1] === ".") { return f.slice(0, f.length - 1); } return f; }; this.formatMoneySymbol = function(units) { - return this.formatMoney(units) + ' ' + config.coinSymbol; + return this.formatMoney(units) + " " + config.coinSymbol; }; this.parseMoney = function(str) { if (!str) return JSBigInt.ZERO; - var negative = str[0] === '-'; + var negative = str[0] === "-"; if (negative) { str = str.slice(1); } - var decimalIndex = str.indexOf('.'); + var decimalIndex = str.indexOf("."); if (decimalIndex == -1) { if (negative) { return JSBigInt.multiply(str, config.coinUnits).negate(); @@ -2004,11 +2547,21 @@ var cnUtil = function(currencyConfig) str = str.substr(0, decimalIndex + config.coinUnitPlaces + 1); } if (negative) { - return new JSBigInt(str.substr(0, decimalIndex)).exp10(config.coinUnitPlaces) - .add(new JSBigInt(str.substr(decimalIndex + 1)).exp10(decimalIndex + config.coinUnitPlaces - str.length + 1)).negate; - } - return new JSBigInt(str.substr(0, decimalIndex)).exp10(config.coinUnitPlaces) - .add(new JSBigInt(str.substr(decimalIndex + 1)).exp10(decimalIndex + config.coinUnitPlaces - str.length + 1)); + return new JSBigInt(str.substr(0, decimalIndex)) + .exp10(config.coinUnitPlaces) + .add( + new JSBigInt(str.substr(decimalIndex + 1)).exp10( + decimalIndex + config.coinUnitPlaces - str.length + 1, + ), + ).negate; + } + return new JSBigInt(str.substr(0, decimalIndex)) + .exp10(config.coinUnitPlaces) + .add( + new JSBigInt(str.substr(decimalIndex + 1)).exp10( + decimalIndex + config.coinUnitPlaces - str.length + 1, + ), + ); }; this.decompose_amount_into_digits = function(amount) { @@ -2027,7 +2580,7 @@ var cnUtil = function(currencyConfig) break; }*/ //check so we don't create 0s - if (amount[0] !== "0"){ + if (amount[0] !== "0") { var digit = amount[0]; while (digit.length < amount.length) { digit += "0"; @@ -2045,7 +2598,7 @@ var cnUtil = function(currencyConfig) for (var i = 0; i < dsts.length; i++) { out.push({ address: dsts[i].address, - amount: dsts[i].amount + amount: dsts[i].amount, }); } } else { @@ -2055,17 +2608,17 @@ var cnUtil = function(currencyConfig) if (digits[j].compare(0) > 0) { out.push({ address: dsts[i].address, - amount: digits[j] + amount: digits[j], }); } } } } - return out.sort(function(a,b){ + return out.sort(function(a, b) { return a["amount"] - b["amount"]; }); }; - + this.is_tx_unlocked = function(unlock_time, blockchain_height) { if (!config.maxBlockNumber) { throw "Max block number is not set in config!"; @@ -2087,26 +2640,42 @@ var cnUtil = function(currencyConfig) if (numBlocks <= 0) { return "Transaction is unlocked"; } - var unlock_prediction = moment().add(numBlocks * config.avgBlockTime, 'seconds'); - return "Will be unlocked in " + numBlocks + " blocks, ~" + unlock_prediction.fromNow(true) + ", " + unlock_prediction.calendar() + ""; + var unlock_prediction = moment().add( + numBlocks * config.avgBlockTime, + "seconds", + ); + return ( + "Will be unlocked in " + + numBlocks + + " blocks, ~" + + unlock_prediction.fromNow(true) + + ", " + + unlock_prediction.calendar() + + "" + ); } else { // unlock time is timestamp var current_time = Math.round(new Date().getTime() / 1000); var time_difference = unlock_time - current_time; - if(time_difference <= 0) { + if (time_difference <= 0) { return "Transaction is unlocked"; } var unlock_moment = moment(unlock_time * 1000); - return "Will be unlocked " + unlock_moment.fromNow() + ", " + unlock_moment.calendar(); + return ( + "Will be unlocked " + + unlock_moment.fromNow() + + ", " + + unlock_moment.calendar() + ); } }; function assert(stmt, val) { if (!stmt) { - throw "assert failed" + (val !== undefined ? ': ' + val : ''); + throw "assert failed" + (val !== undefined ? ": " + val : ""); } } return this; -} -exports.cnUtil = cnUtil +}; +exports.cnUtil = cnUtil; diff --git a/cryptonote_utils/nettype.js b/cryptonote_utils/nettype.js index 04f23fc..27ec4ff 100644 --- a/cryptonote_utils/nettype.js +++ b/cryptonote_utils/nettype.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,13 +26,12 @@ // 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" +"use strict"; -var network_type = -{ +var network_type = { MAINNET: 0, TESTNET: 1, - STAGENET: 2 + STAGENET: 2, }; exports.network_type = network_type; // @@ -48,49 +47,46 @@ var __STAGENET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 24; var __STAGENET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 25; var __STAGENET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 36; // -function cryptonoteBase58PrefixForStandardAddressOn(nettype) -{ - if (nettype == null || typeof nettype === 'undefined') { - console.warn("Unexpected nil nettype") +function cryptonoteBase58PrefixForStandardAddressOn(nettype) { + if (nettype == null || typeof nettype === "undefined") { + console.warn("Unexpected nil nettype"); } if (nettype == network_type.MAINNET) { return __MAINNET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; } else if (nettype == network_type.TESTNET) { - return __TESTNET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX + return __TESTNET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; } else if (nettype == network_type.STAGENET) { - return __STAGENET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX + return __STAGENET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; } - throw "Illegal nettype" + throw "Illegal nettype"; } -function cryptonoteBase58PrefixForIntegratedAddressOn(nettype) -{ - if (nettype == null || typeof nettype === 'undefined') { - console.warn("Unexpected nil nettype") +function cryptonoteBase58PrefixForIntegratedAddressOn(nettype) { + if (nettype == null || typeof nettype === "undefined") { + console.warn("Unexpected nil nettype"); } if (nettype == network_type.MAINNET) { return __MAINNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; } else if (nettype == network_type.TESTNET) { - return __TESTNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX + return __TESTNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; } else if (nettype == network_type.STAGENET) { - return __STAGENET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX + return __STAGENET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; } - throw "Illegal nettype" + throw "Illegal nettype"; } -function cryptonoteBase58PrefixForSubAddressOn(nettype) -{ - if (nettype == null || typeof nettype === 'undefined') { - console.warn("Unexpected nil nettype") +function cryptonoteBase58PrefixForSubAddressOn(nettype) { + if (nettype == null || typeof nettype === "undefined") { + console.warn("Unexpected nil nettype"); } if (nettype == network_type.MAINNET) { return __MAINNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; } else if (nettype == network_type.TESTNET) { - return __TESTNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX + return __TESTNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; } else if (nettype == network_type.STAGENET) { - return __STAGENET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX + return __STAGENET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; } - throw "Illegal nettype" + throw "Illegal nettype"; } // exports.cryptonoteBase58PrefixForStandardAddressOn = cryptonoteBase58PrefixForStandardAddressOn; exports.cryptonoteBase58PrefixForIntegratedAddressOn = cryptonoteBase58PrefixForIntegratedAddressOn; -exports.cryptonoteBase58PrefixForSubAddressOn = cryptonoteBase58PrefixForSubAddressOn; \ No newline at end of file +exports.cryptonoteBase58PrefixForSubAddressOn = cryptonoteBase58PrefixForSubAddressOn; diff --git a/hostAPI/net_service_utils.js b/hostAPI/net_service_utils.js index 2a7b6c4..97cc3f5 100644 --- a/hostAPI/net_service_utils.js +++ b/hostAPI/net_service_utils.js @@ -26,117 +26,131 @@ // 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" +"use strict"; // -const response_parser_utils = require('./response_parser_utils') -const JSBigInt = require('../cryptonote_utils/biginteger').BigInteger // important: grab defined export +const response_parser_utils = require("./response_parser_utils"); +const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger; // important: grab defined export // -function New_ParametersForWalletRequest(address, view_key__private) -{ +function New_ParametersForWalletRequest(address, view_key__private) { return { address: address, - view_key: view_key__private - } + view_key: view_key__private, + }; } -exports.New_ParametersForWalletRequest = New_ParametersForWalletRequest +exports.New_ParametersForWalletRequest = New_ParametersForWalletRequest; // function AddUserAgentParamters( parameters, - appUserAgent_product, - appUserAgent_version + appUserAgent_product, + appUserAgent_version, ) { // setting these on params instead of as header field User-Agent so as to retain all info found in User-Agent, such as platform… and these are set so server has option to control delivery - parameters["app_name"] = appUserAgent_product - parameters["app_version"] = appUserAgent_version + parameters["app_name"] = appUserAgent_product; + parameters["app_version"] = appUserAgent_version; } -exports.AddUserAgentParamters = AddUserAgentParamters +exports.AddUserAgentParamters = AddUserAgentParamters; // function HTTPRequest( request_conformant_module, // such as 'request' or 'xhr' .. TODO: consider switching to 'fetch' apiAddress_authority, // authority means [subdomain.]host.…[:…] with no trailing slash - endpointPath, - final_parameters, - fn -) { // fn: (err?, data?) -> new Request - if (typeof final_parameters == 'undefined' || final_parameters == null) { - throw "final_parameters must not be nil" + endpointPath, + final_parameters, + fn, +) { + // fn: (err?, data?) -> new Request + if (typeof final_parameters == "undefined" || final_parameters == null) { + throw "final_parameters must not be nil"; // return null } - const completeURL = _new_APIAddress_baseURLString(apiAddress_authority) + endpointPath - console.log("📡 " + completeURL) + const completeURL = + _new_APIAddress_baseURLString(apiAddress_authority) + endpointPath; + console.log("📡 " + completeURL); // - const request_options = _new_requestOptions_base("POST", completeURL, final_parameters) - const requestHandle = request_conformant_module( - request_options, - function(err_orProgressEvent, res, body) - { - _new_HTTPRequestHandlerFunctionCallingFn(fn)( // <- called manually instead of directly passed to request_conformant_module call to enable passing completeURL - completeURL, - err_orProgressEvent, res, body - ) - } - ) + const request_options = _new_requestOptions_base( + "POST", + completeURL, + final_parameters, + ); + const requestHandle = request_conformant_module(request_options, function( + err_orProgressEvent, + res, + body, + ) { + _new_HTTPRequestHandlerFunctionCallingFn(fn)( + // <- called manually instead of directly passed to request_conformant_module call to enable passing completeURL + completeURL, + err_orProgressEvent, + res, + body, + ); + }); // - return requestHandle + return requestHandle; } -exports.HTTPRequest = HTTPRequest +exports.HTTPRequest = HTTPRequest; // -function _new_APIAddress_baseURLString(apiAddress_authority) // authority means [subdomain.]host.…[:…] -{ - return "https" + "://" + apiAddress_authority + "/" +function _new_APIAddress_baseURLString( + apiAddress_authority, // authority means [subdomain.]host.…[:…] +) { + return "https" + "://" + apiAddress_authority + "/"; } -function _new_requestOptions_base(methodName, completeURL, json_parameters) -{ +function _new_requestOptions_base(methodName, completeURL, json_parameters) { return { method: methodName, url: completeURL, headers: { "Content-Type": "application/json", - "Accept": "application/json" + Accept: "application/json", }, json: json_parameters, useXDR: true, // CORS - withCredentials: true // CORS - } + withCredentials: true, // CORS + }; } -function _new_HTTPRequestHandlerFunctionCallingFn(fn) -{ - return function(completeURL, err_orProgressEvent, res, body) - { +function _new_HTTPRequestHandlerFunctionCallingFn(fn) { + return function(completeURL, err_orProgressEvent, res, body) { // err appears to actually be a ProgressEvent - var err = null - const statusCode = typeof res !== 'undefined' ? res.statusCode : -1 - if (statusCode == 0 || statusCode == -1) { // we'll treat 0 as a lack of internet connection.. unless there's a better way to make use of err_orProgressEvent which is apparently going to be typeof ProgressEvent here - err = new Error("Connection Failure") + var err = null; + const statusCode = typeof res !== "undefined" ? res.statusCode : -1; + if (statusCode == 0 || statusCode == -1) { + // we'll treat 0 as a lack of internet connection.. unless there's a better way to make use of err_orProgressEvent which is apparently going to be typeof ProgressEvent here + err = new Error("Connection Failure"); } else if (statusCode !== 200) { - const body_Error = body && typeof body == 'object' ? body.Error : undefined - const statusMessage = res && res.statusMessage ? res.statusMessage : undefined - if (typeof body_Error !== 'undefined' && body_Error) { - err = new Error(body_Error) - } else if (typeof statusMessage !== 'undefined' && statusMessage) { - err = new Error(statusMessage) + const body_Error = + body && typeof body == "object" ? body.Error : undefined; + const statusMessage = + res && res.statusMessage ? res.statusMessage : undefined; + if (typeof body_Error !== "undefined" && body_Error) { + err = new Error(body_Error); + } else if (typeof statusMessage !== "undefined" && statusMessage) { + err = new Error(statusMessage); } else { - err = new Error("Unknown " + statusCode + " error") + err = new Error("Unknown " + statusCode + " error"); } } if (err) { console.error("❌ " + err); // console.error("Body:", body) - fn(err, null) - return + fn(err, null); + return; } var json; - if (typeof body === 'string') { + if (typeof body === "string") { try { json = JSON.parse(body); } catch (e) { - console.error("❌ HostedMoneroAPIClient Error: Unable to parse json with exception:", e, "\nbody:", body); - fn(e, null) + console.error( + "❌ HostedMoneroAPIClient Error: Unable to parse json with exception:", + e, + "\nbody:", + body, + ); + fn(e, null); } } else { - json = body + json = body; } - console.log("✅ " + completeURL + " " + statusCode) - fn(null, json) - } -} \ No newline at end of file + console.log("✅ " + completeURL + " " + statusCode); + fn(null, json); + }; +} diff --git a/hostAPI/response_parser_utils.js b/hostAPI/response_parser_utils.js index da83f61..a7b4e35 100644 --- a/hostAPI/response_parser_utils.js +++ b/hostAPI/response_parser_utils.js @@ -26,11 +26,11 @@ // 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" +"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') +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( keyImage_cache, @@ -38,18 +38,19 @@ function Parsed_AddressInfo__sync( address, view_key__private, spend_key__public, - spend_key__private -) { // -> returnValuesByKey + 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 + 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 || [] + const spent_outputs = data.spent_outputs || []; // for (let spent_output of spent_outputs) { var key_image = monero_keyImage_cache_utils.Lazy_KeyImage( @@ -59,20 +60,23 @@ function Parsed_AddressInfo__sync( address, view_key__private, spend_key__public, - spend_key__private - ) + 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) + total_sent = new JSBigInt(total_sent).subtract(spent_output.amount); } } // - const ratesBySymbol = data.rates || {} // jic it's not there + const ratesBySymbol = data.rates || {}; // jic it's not there // - const returnValuesByKey = - { - total_received_String: total_received ? total_received.toString() : null, - locked_balance_String: locked_balance ? locked_balance.toString() : null, + 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, @@ -82,26 +86,29 @@ function Parsed_AddressInfo__sync( transaction_height: transaction_height, blockchain_height: blockchain_height, // - ratesBySymbol: ratesBySymbol - } - return returnValuesByKey + ratesBySymbol: ratesBySymbol, + }; + return returnValuesByKey; } function Parsed_AddressInfo__sync__keyImageManaged( data, address, view_key__private, spend_key__public, - spend_key__private -) { // -> returnValuesByKey - const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address) + spend_key__private, +) { + // -> returnValuesByKey + const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith( + address, + ); return Parsed_AddressInfo__sync( keyImageCache, data, address, view_key__private, spend_key__public, - spend_key__private - ) + spend_key__private, + ); } function Parsed_AddressInfo( keyImage_cache, @@ -110,7 +117,7 @@ function Parsed_AddressInfo( view_key__private, spend_key__public, spend_key__private, - fn // (err?, returnValuesByKey) -> Void + fn, // (err?, returnValuesByKey) -> Void ) { const returnValuesByKey = Parsed_AddressInfo__sync( keyImage_cache, @@ -118,9 +125,9 @@ function Parsed_AddressInfo( address, view_key__private, spend_key__public, - spend_key__private - ) - fn(null, returnValuesByKey) + spend_key__private, + ); + fn(null, returnValuesByKey); } function Parsed_AddressInfo__keyImageManaged( data, @@ -128,8 +135,9 @@ function Parsed_AddressInfo__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn -) { // -> returnValuesByKey + fn, +) { + // -> returnValuesByKey Parsed_AddressInfo( monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address), data, @@ -137,13 +145,13 @@ function Parsed_AddressInfo__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn - ) + fn, + ); } -exports.Parsed_AddressInfo = Parsed_AddressInfo -exports.Parsed_AddressInfo__keyImageManaged = Parsed_AddressInfo__keyImageManaged // in case you can't send a mutable key image cache dictionary -exports.Parsed_AddressInfo__sync__keyImageManaged = Parsed_AddressInfo__sync__keyImageManaged // in case you can't send a mutable key image cache dictionary -exports.Parsed_AddressInfo__sync = Parsed_AddressInfo__sync +exports.Parsed_AddressInfo = Parsed_AddressInfo; +exports.Parsed_AddressInfo__keyImageManaged = Parsed_AddressInfo__keyImageManaged; // in case you can't send a mutable key image cache dictionary +exports.Parsed_AddressInfo__sync__keyImageManaged = Parsed_AddressInfo__sync__keyImageManaged; // in case you can't send a mutable key image cache dictionary +exports.Parsed_AddressInfo__sync = Parsed_AddressInfo__sync; // function Parsed_AddressTransactions( keyImage_cache, @@ -152,7 +160,7 @@ function Parsed_AddressTransactions( view_key__private, spend_key__public, spend_key__private, - fn // (err?, returnValuesByKey) -> Void + fn, // (err?, returnValuesByKey) -> Void ) { const returnValuesByKey = Parsed_AddressTransactions__sync( keyImage_cache, @@ -160,9 +168,9 @@ function Parsed_AddressTransactions( address, view_key__private, spend_key__public, - spend_key__private - ) - fn(null, returnValuesByKey) + spend_key__private, + ); + fn(null, returnValuesByKey); } function Parsed_AddressTransactions__sync( keyImage_cache, @@ -170,15 +178,15 @@ function Parsed_AddressTransactions__sync( address, view_key__private, spend_key__public, - spend_key__private + 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 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 || [] + const transactions = data.transactions || []; // // TODO: rewrite this with more clarity if possible for (let i = 0; i < transactions.length; ++i) { @@ -191,80 +199,99 @@ function Parsed_AddressTransactions__sync( address, view_key__private, spend_key__public, - spend_key__private - ) + 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-- + 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 + 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 - const record__payment_id = transactions[i].payment_id - if (typeof record__payment_id !== 'undefined' && record__payment_id) { - if (record__payment_id.length == 16) { // short (encrypted) pid - if (transactions[i].approx_float_amount < 0) { // outgoing - delete transactions[i]["payment_id"] // need to filter these out .. because the server can't filter out short (encrypted) pids on outgoing txs + 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; + const record__payment_id = transactions[i].payment_id; + if (typeof record__payment_id !== "undefined" && record__payment_id) { + if (record__payment_id.length == 16) { + // short (encrypted) pid + if (transactions[i].approx_float_amount < 0) { + // outgoing + delete transactions[i]["payment_id"]; // need to filter these out .. because the server can't filter out short (encrypted) pids on outgoing txs } } } } - transactions.sort(function(a, b) - { + transactions.sort(function(a, b) { if (a.mempool == true) { if (b.mempool != true) { - return -1 // a first + return -1; // a first } // both mempool - fall back to .id compare } else if (b.mempool == true) { - return 1 // b first + return 1; // b first } - return b.id - a.id - }) + 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() + 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 = - { + 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 + serialized_transactions: transactions, + }; + return returnValuesByKey; } function Parsed_AddressTransactions__sync__keyImageManaged( data, address, view_key__private, spend_key__public, - spend_key__private + spend_key__private, ) { - const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address) + const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith( + address, + ); return Parsed_AddressTransactions__sync( keyImageCache, data, address, view_key__private, spend_key__public, - spend_key__private - ) + spend_key__private, + ); } function Parsed_AddressTransactions__keyImageManaged( data, @@ -272,7 +299,7 @@ function Parsed_AddressTransactions__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn + fn, ) { Parsed_AddressTransactions( monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address), @@ -281,13 +308,13 @@ function Parsed_AddressTransactions__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn - ) + fn, + ); } -exports.Parsed_AddressTransactions = Parsed_AddressTransactions -exports.Parsed_AddressTransactions__keyImageManaged = Parsed_AddressTransactions__keyImageManaged -exports.Parsed_AddressTransactions__sync = Parsed_AddressTransactions__sync -exports.Parsed_AddressTransactions__sync__keyImageManaged = Parsed_AddressTransactions__sync__keyImageManaged +exports.Parsed_AddressTransactions = Parsed_AddressTransactions; +exports.Parsed_AddressTransactions__keyImageManaged = Parsed_AddressTransactions__keyImageManaged; +exports.Parsed_AddressTransactions__sync = Parsed_AddressTransactions__sync; +exports.Parsed_AddressTransactions__sync__keyImageManaged = Parsed_AddressTransactions__sync__keyImageManaged; // function Parsed_UnspentOuts( keyImage_cache, @@ -296,7 +323,7 @@ function Parsed_UnspentOuts( view_key__private, spend_key__public, spend_key__private, - fn // (err?, returnValuesByKey) + fn, // (err?, returnValuesByKey) ) { const returnValuesByKey = Parsed_UnspentOuts__sync( keyImage_cache, @@ -304,9 +331,9 @@ function Parsed_UnspentOuts( address, view_key__private, spend_key__public, - spend_key__private - ) - fn(null, returnValuesByKey) + spend_key__private, + ); + fn(null, returnValuesByKey); } function Parsed_UnspentOuts__sync( keyImage_cache, @@ -314,33 +341,53 @@ function Parsed_UnspentOuts__sync( address, view_key__private, spend_key__public, - spend_key__private + spend_key__private, ) { - const data_outputs = data.outputs - const finalized_unspentOutputs = data.outputs || [] // to finalize: + 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 + 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" + 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" + 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.`) // NOTE: Looks like the i-- code below should exit earlier if this is necessary - continue + 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.`, + ); // NOTE: Looks like the i-- code below should exit earlier if this is necessary + 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 + 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( keyImage_cache, @@ -349,48 +396,61 @@ function Parsed_UnspentOuts__sync( address, view_key__private, spend_key__public, - spend_key__private - ) - if (key_image === finalized_unspentOutput_atI_beforeSplice.spend_key_images[j]) { + 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] + const finalized_unspentOutput_atI_afterSplice = + finalized_unspentOutputs[i]; if (finalized_unspentOutput_atI_afterSplice) { - j = finalized_unspentOutput_atI_afterSplice.spend_key_images.length; + 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( + "💬 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 = - { + const unusedOuts = finalized_unspentOutputs.slice(0); + const returnValuesByKey = { unspentOutputs: finalized_unspentOutputs, unusedOuts: unusedOuts, - per_kb_fee: data.per_kb_fee // String - } - return returnValuesByKey + per_kb_fee: data.per_kb_fee, // String + }; + return returnValuesByKey; } function Parsed_UnspentOuts__sync__keyImageManaged( data, address, view_key__private, spend_key__public, - spend_key__private + spend_key__private, ) { - const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address) + const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith( + address, + ); return Parsed_UnspentOuts__sync( keyImageCache, data, address, view_key__private, spend_key__public, - spend_key__private - ) + spend_key__private, + ); } function Parsed_UnspentOuts__keyImageManaged( data, @@ -398,7 +458,7 @@ function Parsed_UnspentOuts__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn + fn, ) { Parsed_UnspentOuts( monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(address), @@ -407,10 +467,10 @@ function Parsed_UnspentOuts__keyImageManaged( view_key__private, spend_key__public, spend_key__private, - fn - ) + fn, + ); } -exports.Parsed_UnspentOuts = Parsed_UnspentOuts -exports.Parsed_UnspentOuts__keyImageManaged = Parsed_UnspentOuts__keyImageManaged -exports.Parsed_UnspentOuts__sync = Parsed_UnspentOuts__sync -exports.Parsed_UnspentOuts__sync__keyImageManaged = Parsed_UnspentOuts__sync__keyImageManaged +exports.Parsed_UnspentOuts = Parsed_UnspentOuts; +exports.Parsed_UnspentOuts__keyImageManaged = Parsed_UnspentOuts__keyImageManaged; +exports.Parsed_UnspentOuts__sync = Parsed_UnspentOuts__sync; +exports.Parsed_UnspentOuts__sync__keyImageManaged = Parsed_UnspentOuts__sync__keyImageManaged; diff --git a/index.js b/index.js index c14b351..262a65c 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,23 +26,23 @@ // 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" +"use strict"; // // NOTE: The main downside to using an index.js file like this is that it will pull in all the code - rather than the consumer requiring code module-by-module // It's of course possible to construct your own stripped-down index.[custom name].js file for, e.g., special webpack bundling usages. -const mymonero_core_js = {} -mymonero_core_js.monero_utils = require('./monero_utils/monero_cryptonote_utils_instance') -mymonero_core_js.monero_wallet_utils = require('./monero_utils/monero_wallet_utils') -mymonero_core_js.monero_config = require('./monero_utils/monero_config') -mymonero_core_js.monero_txParsing_utils = require('./monero_utils/monero_txParsing_utils') -mymonero_core_js.monero_sendingFunds_utils = require('./monero_utils/monero_sendingFunds_utils') -mymonero_core_js.monero_requestURI_utils = require('./monero_utils/monero_requestURI_utils') -mymonero_core_js.monero_keyImage_cache_utils = require('./monero_utils/monero_keyImage_cache_utils') -mymonero_core_js.monero_wallet_locale = require('./monero_utils/monero_wallet_locale') -mymonero_core_js.monero_paymentID_utils = require('./monero_utils/monero_paymentID_utils') -mymonero_core_js.api_response_parser_utils = require('./hostAPI/response_parser_utils') -// -mymonero_core_js.nettype_utils = require('./cryptonote_utils/nettype_utils') -mymonero_core_js.JSBigInt = require('./cryptonote_utils/biginteger').BigInteger // so that it is available to a hypothetical consumer's language-bridging web context for constructing string arguments to the above modules -// -module.exports = mymonero_core_js \ No newline at end of file +const mymonero_core_js = {}; +mymonero_core_js.monero_utils = require("./monero_utils/monero_cryptonote_utils_instance"); +mymonero_core_js.monero_wallet_utils = require("./monero_utils/monero_wallet_utils"); +mymonero_core_js.monero_config = require("./monero_utils/monero_config"); +mymonero_core_js.monero_txParsing_utils = require("./monero_utils/monero_txParsing_utils"); +mymonero_core_js.monero_sendingFunds_utils = require("./monero_utils/monero_sendingFunds_utils"); +mymonero_core_js.monero_requestURI_utils = require("./monero_utils/monero_requestURI_utils"); +mymonero_core_js.monero_keyImage_cache_utils = require("./monero_utils/monero_keyImage_cache_utils"); +mymonero_core_js.monero_wallet_locale = require("./monero_utils/monero_wallet_locale"); +mymonero_core_js.monero_paymentID_utils = require("./monero_utils/monero_paymentID_utils"); +mymonero_core_js.api_response_parser_utils = require("./hostAPI/response_parser_utils"); +// +mymonero_core_js.nettype_utils = require("./cryptonote_utils/nettype_utils"); +mymonero_core_js.JSBigInt = require("./cryptonote_utils/biginteger").BigInteger; // so that it is available to a hypothetical consumer's language-bridging web context for constructing string arguments to the above modules +// +module.exports = mymonero_core_js; diff --git a/monero_utils/monero_config.js b/monero_utils/monero_config.js index 5a20b3e..924a65d 100644 --- a/monero_utils/monero_config.js +++ b/monero_utils/monero_config.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,12 +26,11 @@ // 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" +"use strict"; // -const JSBigInt = require('../cryptonote_utils/biginteger').BigInteger +const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger; // -module.exports = -{ +module.exports = { // Number of atomic units in one unit of currency. e.g. 12 => 10^12 = 1000000000000 coinUnitPlaces: 12, @@ -39,16 +38,16 @@ module.exports = txMinConfirms: 10, // Currency symbol - coinSymbol: 'XMR', + coinSymbol: "XMR", // OpenAlias prefix openAliasPrefix: "xmr", // Currency name - coinName: 'Monero', + coinName: "Monero", // Payment URI Prefix - coinUriPrefix: 'monero:', + coinUriPrefix: "monero:", // Prefix code for addresses addressPrefix: 18, // 18 => addresses start with "4" @@ -57,11 +56,11 @@ module.exports = // Dust threshold in atomic units // 2*10^9 used for choosing outputs/change - we decompose all the way down if the receiver wants now regardless of threshold - dustThreshold: new JSBigInt('2000000000'), - + dustThreshold: new JSBigInt("2000000000"), + // Maximum block number, used for tx unlock time maxBlockNumber: 500000000, // Average block time in seconds, used for unlock time estimation - avgBlockTime: 60 -} \ No newline at end of file + avgBlockTime: 60, +}; diff --git a/monero_utils/monero_cryptonote_utils_instance.js b/monero_utils/monero_cryptonote_utils_instance.js index e655059..b5a51c4 100644 --- a/monero_utils/monero_cryptonote_utils_instance.js +++ b/monero_utils/monero_cryptonote_utils_instance.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,10 +26,10 @@ // 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" +"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) +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 \ No newline at end of file +module.exports = monero_cryptonote_utils_instance; diff --git a/monero_utils/monero_keyImage_cache_utils.js b/monero_utils/monero_keyImage_cache_utils.js index 1b9a025..6f84ef9 100644 --- a/monero_utils/monero_keyImage_cache_utils.js +++ b/monero_utils/monero_keyImage_cache_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,70 +26,76 @@ // 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" +"use strict"; // -const monero_utils = require('./monero_cryptonote_utils_instance') +const monero_utils = require("./monero_cryptonote_utils_instance"); // const Lazy_KeyImage = function( - mutable_keyImagesByCacheKey, // pass a mutable JS dictionary - tx_pub_key, + mutable_keyImagesByCacheKey, // pass a mutable JS dictionary + tx_pub_key, out_index, public_address, view_key__private, spend_key__public, - spend_key__private + spend_key__private, ) { - var cache_index = tx_pub_key + ':' + public_address + ':' + out_index - const cached__key_image = mutable_keyImagesByCacheKey[cache_index] - if (typeof cached__key_image !== 'undefined' && cached__key_image !== null) { - return cached__key_image + var cache_index = tx_pub_key + ":" + public_address + ":" + out_index; + const cached__key_image = mutable_keyImagesByCacheKey[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 + out_index, + ).key_image; // cache: - mutable_keyImagesByCacheKey[cache_index] = key_image + mutable_keyImagesByCacheKey[cache_index] = key_image; // - return key_image -} -exports.Lazy_KeyImage = Lazy_KeyImage + return key_image; +}; +exports.Lazy_KeyImage = Lazy_KeyImage; // // // Managed caches - Can be used by apps which can't send a mutable_keyImagesByCacheKey -const __global_managed_keyImageCaches_by_walletId = {} -function _managedKeyImageCacheWalletIdForWalletWith(public_address) -{ // NOTE: making the assumption that public_address is unique enough to identify a wallet for caching.... +const __global_managed_keyImageCaches_by_walletId = {}; +function _managedKeyImageCacheWalletIdForWalletWith(public_address) { + // NOTE: making the assumption that public_address is unique enough to identify a wallet for caching.... // FIXME: with subaddresses, is that still the case? would we need to split them up by subaddr anyway? - if (public_address == "" || !public_address || typeof public_address == 'undefined') { - throw "managedKeyImageCacheIdentifierForWalletWith: Illegal public_address" + if ( + public_address == "" || + !public_address || + typeof public_address == "undefined" + ) { + throw "managedKeyImageCacheIdentifierForWalletWith: Illegal public_address"; } - return (""+public_address) + return "" + public_address; } -const Lazy_KeyImageCacheForWalletWith = function(public_address) -{ - var cacheId = _managedKeyImageCacheWalletIdForWalletWith(public_address) - var cache = __global_managed_keyImageCaches_by_walletId[cacheId] - if (typeof cache === 'undefined' || !cache) { - cache = {} - __global_managed_keyImageCaches_by_walletId[cacheId] = cache +const Lazy_KeyImageCacheForWalletWith = function(public_address) { + var cacheId = _managedKeyImageCacheWalletIdForWalletWith(public_address); + var cache = __global_managed_keyImageCaches_by_walletId[cacheId]; + if (typeof cache === "undefined" || !cache) { + cache = {}; + __global_managed_keyImageCaches_by_walletId[cacheId] = cache; } - return cache -} + return cache; +}; exports.Lazy_KeyImageCacheForWalletWith = Lazy_KeyImageCacheForWalletWith; // -const DeleteManagedKeyImagesForWalletWith = function(public_address) -{ // IMPORTANT: Ensure you call this method when you want to clear your wallet from - // memory or delete it, or else you could leak key images and public addresses. - const cacheId = _managedKeyImageCacheWalletIdForWalletWith(public_address) - delete __global_managed_keyImageCaches_by_walletId[cacheId] +const DeleteManagedKeyImagesForWalletWith = function(public_address) { + // IMPORTANT: Ensure you call this method when you want to clear your wallet from + // memory or delete it, or else you could leak key images and public addresses. + const cacheId = _managedKeyImageCacheWalletIdForWalletWith(public_address); + delete __global_managed_keyImageCaches_by_walletId[cacheId]; // - const cache = __global_managed_keyImageCaches_by_walletId[cacheId] - if (typeof cache !== 'undefined') { - throw "Key image cache still exists after deletion" + const cache = __global_managed_keyImageCaches_by_walletId[cacheId]; + if (typeof cache !== "undefined") { + throw "Key image cache still exists after deletion"; } -} -exports.DeleteManagedKeyImagesForWalletWith = DeleteManagedKeyImagesForWalletWith; \ No newline at end of file +}; +exports.DeleteManagedKeyImagesForWalletWith = DeleteManagedKeyImagesForWalletWith; diff --git a/monero_utils/monero_paymentID_utils.js b/monero_utils/monero_paymentID_utils.js index d0640f8..aca1519 100644 --- a/monero_utils/monero_paymentID_utils.js +++ b/monero_utils/monero_paymentID_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,22 +26,24 @@ // 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" +"use strict"; // -const monero_utils = require('./monero_cryptonote_utils_instance') +const monero_utils = require("./monero_cryptonote_utils_instance"); // // Note: long (64 char, plaintext) payment ids are deprecated. // -function New_Short_TransactionID() -{ - return monero_utils.rand_8() +function New_Short_TransactionID() { + return monero_utils.rand_8(); } exports.New_Short_TransactionID = New_Short_TransactionID; exports.New_TransactionID = New_Short_TransactionID; // -function IsValidPaymentIDOrNoPaymentID(payment_id__orNil) -{ - if (payment_id__orNil == null || payment_id__orNil == "" || typeof payment_id__orNil == "undefined") { +function IsValidPaymentIDOrNoPaymentID(payment_id__orNil) { + if ( + payment_id__orNil == null || + payment_id__orNil == "" || + typeof payment_id__orNil == "undefined" + ) { return true; // no pid } let payment_id = payment_id__orNil; @@ -55,31 +57,30 @@ function IsValidPaymentIDOrNoPaymentID(payment_id__orNil) } exports.IsValidPaymentIDOrNoPaymentID = IsValidPaymentIDOrNoPaymentID; // -function IsValidShortPaymentID(payment_id) -{ +function IsValidShortPaymentID(payment_id) { return IsValidPaymentIDOfLength(payment_id, 16); } exports.IsValidShortPaymentID = IsValidShortPaymentID; // -function IsValidLongPaymentID(payment_id) -{ +function IsValidLongPaymentID(payment_id) { return IsValidPaymentIDOfLength(payment_id, 64); } exports.IsValidLongPaymentID = IsValidLongPaymentID; // -function IsValidPaymentIDOfLength(payment_id, required_length) -{ +function IsValidPaymentIDOfLength(payment_id, required_length) { if (required_length != 16 && required_length != 64) { throw "unexpected IsValidPaymentIDOfLength required_length"; } let payment_id_length = payment_id.length; - if (payment_id_length !== required_length) { // new encrypted short + if (payment_id_length !== required_length) { + // new encrypted short return false; // invalid length } let pattern = RegExp("^[0-9a-fA-F]{" + required_length + "}$"); - if (pattern.test(payment_id) != true) { // not a valid required_length char pid + if (pattern.test(payment_id) != true) { + // not a valid required_length char pid return false; // then not valid - } + } return true; } exports.IsValidShortPaymentID = IsValidShortPaymentID; diff --git a/monero_utils/monero_requestURI_utils.js b/monero_utils/monero_requestURI_utils.js index 7c958da..c275f5f 100644 --- a/monero_utils/monero_requestURI_utils.js +++ b/monero_utils/monero_requestURI_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,126 +26,137 @@ // 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" +"use strict"; // -const monero_config = require('./monero_config') -const monero_utils = require('./monero_cryptonote_utils_instance') +const monero_config = require("./monero_config"); +const monero_utils = require("./monero_cryptonote_utils_instance"); // -const URITypes = -{ +const URITypes = { addressAsFirstPathComponent: 1, - addressAsAuthority: 2 + addressAsAuthority: 2, }; exports.URITypes = URITypes; // -function New_RequestFunds_URI( - args -) { // -> String? - const address = args.address +function New_RequestFunds_URI(args) { + // -> String? + const address = args.address; if (!address) { - throw "missing address" + throw "missing address"; // return null } - var mutable_uri = "" - mutable_uri += monero_config.coinUriPrefix + var mutable_uri = ""; + mutable_uri += monero_config.coinUriPrefix; { const uriType = args.uriType; if (uriType === URITypes.addressAsAuthority) { - mutable_uri += "//" // use for inserting a // so data detectors pick it up… + mutable_uri += "//"; // use for inserting a // so data detectors pick it up… } else if (uriType === URITypes.addressAsFirstPathComponent) { // nothing to do } else { - throw "Illegal args.uriType" + throw "Illegal args.uriType"; } } - mutable_uri += address - var isAppendingParam0 = true - function addParam(parameterName, value) - { - if (value == null || value == ""/*important*/ || typeof value === 'undefined') { - return + mutable_uri += address; + var isAppendingParam0 = true; + function addParam(parameterName, value) { + if ( + value == null || + value == "" /*important*/ || + typeof value === "undefined" + ) { + return; } - var conjunctionStr = "&" + var conjunctionStr = "&"; if (isAppendingParam0 === true) { - isAppendingParam0 = false - conjunctionStr = "?" + isAppendingParam0 = false; + conjunctionStr = "?"; } - mutable_uri += conjunctionStr - mutable_uri += parameterName + '=' + encodeURIComponent(value) + mutable_uri += conjunctionStr; + mutable_uri += parameterName + "=" + encodeURIComponent(value); } { - addParam('tx_amount', args.amount) - if ((args.amountCcySymbol || "").toLowerCase() != monero_config.coinSymbol.toLowerCase()) { - addParam('tx_amount_ccy', args.amountCcySymbol) + addParam("tx_amount", args.amount); + if ( + (args.amountCcySymbol || "").toLowerCase() != + monero_config.coinSymbol.toLowerCase() + ) { + addParam("tx_amount_ccy", args.amountCcySymbol); } - addParam('tx_description', args.description) - addParam('tx_payment_id', args.payment_id) - addParam('tx_message', args.message) + addParam("tx_description", args.description); + addParam("tx_payment_id", args.payment_id); + addParam("tx_message", args.message); } - return mutable_uri + return mutable_uri; } -exports.New_RequestFunds_URI = New_RequestFunds_URI +exports.New_RequestFunds_URI = New_RequestFunds_URI; // -function New_ParsedPayload_FromPossibleRequestURIString(string, nettype) -{ // throws; -> {} +function New_ParsedPayload_FromPossibleRequestURIString(string, nettype) { + // throws; -> {} // // detect no-scheme moneroAddr and possible OA addr - if has no monero: prefix if (string.indexOf(monero_config.coinUriPrefix) !== 0) { - const stringHasQMark = string.indexOf("?") !== -1 - if (stringHasQMark) { // fairly sure this is correct.. (just an extra failsafe/filter) - throw "Unrecognized URI format" + const stringHasQMark = string.indexOf("?") !== -1; + if (stringHasQMark) { + // fairly sure this is correct.. (just an extra failsafe/filter) + throw "Unrecognized URI format"; } - let couldBeOAAddress = string.indexOf(".") != -1 // contains period - would be nice to get this from DoesStringContainPeriodChar_excludingAsXMRAddress_qualifyingAsPossibleOAAddress so maybe mymonero_core_js should gain local_modules/OpenAlias + let couldBeOAAddress = string.indexOf(".") != -1; // contains period - would be nice to get this from DoesStringContainPeriodChar_excludingAsXMRAddress_qualifyingAsPossibleOAAddress so maybe mymonero_core_js should gain local_modules/OpenAlias if (couldBeOAAddress) { return { - address: string - } + address: string, + }; } - var address__decode_result; + var address__decode_result; try { - address__decode_result = monero_utils.decode_address(string, nettype) + address__decode_result = monero_utils.decode_address( + string, + nettype, + ); } catch (e) { - throw "No Monero request info" - return + throw "No Monero request info"; + return; } // then it looks like a monero address return { - address: string - } + address: string, + }; } - const uriString = string - const url = new URL(uriString) - const protocol = url.protocol + const uriString = string; + const url = new URL(uriString); + const protocol = url.protocol; if (protocol !== monero_config.coinUriPrefix) { - throw "Request URI has non-Monero protocol" + throw "Request URI has non-Monero protocol"; } - var target_address = url.pathname // var instead of const as have to finalize it + 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 === "" || + 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: + 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 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 + 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 + return payload; } -exports.New_ParsedPayload_FromPossibleRequestURIString = New_ParsedPayload_FromPossibleRequestURIString +exports.New_ParsedPayload_FromPossibleRequestURIString = New_ParsedPayload_FromPossibleRequestURIString; diff --git a/monero_utils/monero_sendingFunds_utils.js b/monero_utils/monero_sendingFunds_utils.js index 350b653..f0ddcc4 100644 --- a/monero_utils/monero_sendingFunds_utils.js +++ b/monero_utils/monero_sendingFunds_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,54 +26,78 @@ // 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" +"use strict"; // -const async = require('async') +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 JSBigInt = require('../cryptonote_utils/biginteger').BigInteger +const monero_config = require("./monero_config"); +const monero_utils = require("./monero_cryptonote_utils_instance"); +const monero_paymentID_utils = require("./monero_paymentID_utils"); +const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger; // -const hostAPI_net_service_utils = require('../hostAPI/net_service_utils') +const hostAPI_net_service_utils = require("../hostAPI/net_service_utils"); // -function _forkv7_minimumMixin() { return 6; } -function _mixinToRingsize(mixin) { return mixin + 1; } +function _forkv7_minimumMixin() { + return 6; +} +function _mixinToRingsize(mixin) { + return mixin + 1; +} // -function thisFork_minMixin() { return _forkv7_minimumMixin(); } -function thisFork_minRingSize() { return _mixinToRingsize(thisFork_minMixin()); } +function thisFork_minMixin() { + return _forkv7_minimumMixin(); +} +function thisFork_minRingSize() { + return _mixinToRingsize(thisFork_minMixin()); +} exports.thisFork_minMixin = thisFork_minMixin; exports.thisFork_minRingSize = thisFork_minRingSize; // -function fixedMixin() { return thisFork_minMixin(); /* using the monero app default to remove MM user identifiers */ } -function fixedRingsize() { return _mixinToRingsize(fixedMixin()); } +function fixedMixin() { + return thisFork_minMixin(); /* using the monero app default to remove MM user identifiers */ +} +function fixedRingsize() { + return _mixinToRingsize(fixedMixin()); +} exports.fixedMixin = fixedMixin; exports.fixedRingsize = fixedRingsize; // // -function default_priority() { return 1; } // aka .low +function default_priority() { + return 1; +} // aka .low exports.default_priority = default_priority; // // -function calculate_fee(fee_per_kb_JSBigInt, numberOf_bytes, fee_multiplier) -{ - const numberOf_kB_JSBigInt = new JSBigInt((numberOf_bytes + 1023.0) / 1024.0) // i.e. ceil +function calculate_fee(fee_per_kb_JSBigInt, numberOf_bytes, fee_multiplier) { + const numberOf_kB_JSBigInt = new JSBigInt( + (numberOf_bytes + 1023.0) / 1024.0, + ); // i.e. ceil // - return calculate_fee__kb(fee_per_kb_JSBigInt, numberOf_kB_JSBigInt, fee_multiplier) + return calculate_fee__kb( + fee_per_kb_JSBigInt, + numberOf_kB_JSBigInt, + fee_multiplier, + ); } -function calculate_fee__kb(fee_per_kb_JSBigInt, numberOf_kb, fee_multiplier) -{ - const numberOf_kB_JSBigInt = new JSBigInt(numberOf_kb) - const fee = fee_per_kb_JSBigInt.multiply(fee_multiplier).multiply(numberOf_kB_JSBigInt) +function calculate_fee__kb(fee_per_kb_JSBigInt, numberOf_kb, fee_multiplier) { + const numberOf_kB_JSBigInt = new JSBigInt(numberOf_kb); + const fee = fee_per_kb_JSBigInt + .multiply(fee_multiplier) + .multiply(numberOf_kB_JSBigInt); // - return fee + return fee; } const newer_multipliers = [1, 4, 20, 166]; function fee_multiplier_for_priority(priority__or0ForDefault) { - const final_priorityInt = !priority__or0ForDefault || priority__or0ForDefault == 0 - ? default_priority() - : priority__or0ForDefault; - if (final_priorityInt <= 0 || final_priorityInt > newer_multipliers.length) { + const final_priorityInt = + !priority__or0ForDefault || priority__or0ForDefault == 0 + ? default_priority() + : priority__or0ForDefault; + if ( + final_priorityInt <= 0 || + final_priorityInt > newer_multipliers.length + ) { throw "fee_multiplier_for_priority: simple_priority out of bounds"; } const priority_as_idx = final_priorityInt - 1; @@ -82,37 +106,44 @@ function fee_multiplier_for_priority(priority__or0ForDefault) { function EstimatedTransaction_networkFee( nonZero_mixin_int, feePerKB_JSBigInt, - simple_priority + simple_priority, ) { - const numberOf_inputs = 2 // this might change -- might select inputs - const numberOf_outputs = 1/*dest*/ + 1/*change*/ + 0/*no mymonero fee presently*/ + const numberOf_inputs = 2; // this might change -- might select inputs + const numberOf_outputs = + 1 /*dest*/ + 1 /*change*/ + 0; /*no mymonero fee presently*/ // TODO: update est tx size for bulletproofs // TODO: normalize est tx size fn naming - const estimated_txSize = monero_utils.estimateRctSize(numberOf_inputs, nonZero_mixin_int, numberOf_outputs) - const estimated_fee = calculate_fee(feePerKB_JSBigInt, estimated_txSize, fee_multiplier_for_priority(simple_priority)) + const estimated_txSize = monero_utils.estimateRctSize( + numberOf_inputs, + nonZero_mixin_int, + numberOf_outputs, + ); + const estimated_fee = calculate_fee( + feePerKB_JSBigInt, + estimated_txSize, + fee_multiplier_for_priority(simple_priority), + ); // - return estimated_fee + return estimated_fee; } -exports.EstimatedTransaction_networkFee = EstimatedTransaction_networkFee +exports.EstimatedTransaction_networkFee = EstimatedTransaction_networkFee; // -const SendFunds_ProcessStep_Code = -{ +const SendFunds_ProcessStep_Code = { fetchingLatestBalance: 1, calculatingFee: 2, fetchingDecoyOutputs: 3, // may get skipped if 0 mixin constructingTransaction: 4, // may go back to .calculatingFee - submittingTransaction: 5 -} -exports.SendFunds_ProcessStep_Code = SendFunds_ProcessStep_Code -const SendFunds_ProcessStep_MessageSuffix = -{ + submittingTransaction: 5, +}; +exports.SendFunds_ProcessStep_Code = SendFunds_ProcessStep_Code; +const SendFunds_ProcessStep_MessageSuffix = { 1: "Fetching latest balance.", 2: "Calculating fee.", 3: "Fetching decoy outputs.", 4: "Constructing transaction.", // may go back to .calculatingFee - 5: "Submitting transaction." -} -exports.SendFunds_ProcessStep_MessageSuffix = SendFunds_ProcessStep_MessageSuffix + 5: "Submitting transaction.", +}; +exports.SendFunds_ProcessStep_MessageSuffix = SendFunds_ProcessStep_MessageSuffix; // function SendFunds( target_address, // currency-ready wallet address, but not an OA address (resolve before calling) @@ -136,13 +167,13 @@ function SendFunds( // tx_hash?, // tx_fee? // ) - failWithErr_fn + failWithErr_fn, // failWithErr_fn: ( // err // ) ) { - var isRingCT = true - var sweeping = isSweep_orZeroWhenAmount === true // rather than, say, undefined + var isRingCT = true; + var sweeping = isSweep_orZeroWhenAmount === true; // rather than, say, undefined // // some callback trampoline function declarations… function __trampolineFor_success( @@ -150,119 +181,149 @@ function SendFunds( sentAmount, final__payment_id, tx_hash, - tx_fee + tx_fee, ) { success_fn( moneroReady_targetDescription_address, sentAmount, final__payment_id, tx_hash, - tx_fee - ) + tx_fee, + ); } - function __trampolineFor_err_withErr(err) - { - failWithErr_fn(err) + function __trampolineFor_err_withErr(err) { + failWithErr_fn(err); } - function __trampolineFor_err_withStr(errStr) - { - const err = new Error(errStr) - console.error(errStr) - failWithErr_fn(err) + function __trampolineFor_err_withStr(errStr) { + const err = new Error(errStr); + console.error(errStr); + failWithErr_fn(err); } if (mixin < thisFork_minMixin()) { - __trampolineFor_err_withStr("Ringsize is below the minimum.") + __trampolineFor_err_withStr("Ringsize is below the minimum."); return; } // // parse & normalize the target descriptions by mapping them to Monero addresses & amounts - var amount = sweeping ? 0 : amount_orZeroWhenSweep - const targetDescription = - { - address: target_address, - amount: amount - } + var amount = sweeping ? 0 : amount_orZeroWhenSweep; + const targetDescription = { + address: target_address, + amount: amount, + }; new_moneroReadyTargetDescriptions_fromTargetDescriptions( monero_openalias_utils, - [ targetDescription ], // requires a list of descriptions - but SendFunds was + [targetDescription], // requires a list of descriptions - but SendFunds was // not written with multiple target support as MyMonero does not yet support it nettype, - function(err, moneroReady_targetDescriptions) - { + function(err, moneroReady_targetDescriptions) { if (err) { - __trampolineFor_err_withErr(err) - return + __trampolineFor_err_withErr(err); + return; } - const invalidOrZeroDestination_errStr = "You need to enter a valid destination" + const invalidOrZeroDestination_errStr = + "You need to enter a valid destination"; if (moneroReady_targetDescriptions.length === 0) { - __trampolineFor_err_withStr(invalidOrZeroDestination_errStr) - return + __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 + 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 + _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: " + sweeping ? "all" : monero_utils.formatMoney(totalAmountWithoutFee_JSBigInt)); + var totalAmountWithoutFee_JSBigInt = new JSBigInt(0).add( + moneroReady_targetDescription_amount, + ); + console.log( + "💬 Total to send, before fee: " + sweeping + ? "all" + : monero_utils.formatMoney(totalAmountWithoutFee_JSBigInt), + ); if (!sweeping && totalAmountWithoutFee_JSBigInt.compare(0) <= 0) { - const errStr = "The amount you've entered is too low" - __trampolineFor_err_withStr(errStr) - return + 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; + 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, nettype) + address__decode_result = monero_utils.decode_address( + moneroReady_targetDescription_address, + nettype, + ); } catch (e) { - __trampolineFor_err_withStr(typeof e === 'string' ? e : e.toString()) - return + __trampolineFor_err_withStr( + typeof e === "string" ? e : e.toString(), + ); + return; } if (payment_id) { if (address__decode_result.intPaymentId) { - const errStr = "Payment ID must be blank when using an Integrated Address" - __trampolineFor_err_withStr(errStr) - return - } else if (monero_utils.is_subaddress(moneroReady_targetDescription_address, nettype)) { - const errStr = "Payment ID must be blank when using a Subaddress" - __trampolineFor_err_withStr(errStr) - return + const errStr = + "Payment ID must be blank when using an Integrated Address"; + __trampolineFor_err_withStr(errStr); + return; + } else if ( + monero_utils.is_subaddress( + moneroReady_targetDescription_address, + nettype, + ) + ) { + const errStr = + "Payment ID must be blank when using a Subaddress"; + __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 = "Invalid payment ID." - __trampolineFor_err_withStr(errStr) - return + 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 = "Invalid payment ID."; + __trampolineFor_err_withStr(errStr); + return; } // _proceedTo_getUnspentOutsUsableForMixin( moneroReady_targetDescription_address, totalAmountWithoutFee_JSBigInt, final__payment_id, - final__pid_encrypt - ) + final__pid_encrypt, + ); } function _proceedTo_getUnspentOutsUsableForMixin( moneroReady_targetDescription_address, totalAmountWithoutFee_JSBigInt, final__payment_id, // non-existent or valid - final__pid_encrypt // true or false + final__pid_encrypt, // true or false ) { - preSuccess_nonTerminal_statusUpdate_fn(SendFunds_ProcessStep_Code.fetchingLatestBalance) + preSuccess_nonTerminal_statusUpdate_fn( + SendFunds_ProcessStep_Code.fetchingLatestBalance, + ); hostedMoneroAPIClient.UnspentOuts( wallet__public_address, wallet__private_keys.view, @@ -270,27 +331,25 @@ function SendFunds( wallet__private_keys.spend, mixin, sweeping, - function( - err, - unspentOuts, - unusedOuts, - dynamic_feePerKB_JSBigInt - ) { + function(err, unspentOuts, unusedOuts, dynamic_feePerKB_JSBigInt) { if (err) { - __trampolineFor_err_withErr(err) - return + __trampolineFor_err_withErr(err); + return; } - console.log("Received dynamic per kb fee", monero_utils.formatMoneySymbol(dynamic_feePerKB_JSBigInt)) + console.log( + "Received dynamic per kb fee", + monero_utils.formatMoneySymbol(dynamic_feePerKB_JSBigInt), + ); _proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin( moneroReady_targetDescription_address, totalAmountWithoutFee_JSBigInt, final__payment_id, final__pid_encrypt, unusedOuts, - dynamic_feePerKB_JSBigInt - ) - } - ) + dynamic_feePerKB_JSBigInt, + ); + }, + ); } function _proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin( moneroReady_targetDescription_address, @@ -298,13 +357,17 @@ function SendFunds( final__payment_id, final__pid_encrypt, unusedOuts, - dynamic_feePerKB_JSBigInt + dynamic_feePerKB_JSBigInt, ) { // status: constructing transaction… - const feePerKB_JSBigInt = dynamic_feePerKB_JSBigInt + const feePerKB_JSBigInt = dynamic_feePerKB_JSBigInt; // Transaction will need at least 1KB fee (or 13KB for RingCT) - const network_minimumTXSize_kb = /*isRingCT ? */13/* : 1*/ - const network_minimumFee = calculate_fee__kb(feePerKB_JSBigInt, network_minimumTXSize_kb, fee_multiplier_for_priority(simple_priority)) + const network_minimumTXSize_kb = /*isRingCT ? */ 13; /* : 1*/ + const network_minimumFee = calculate_fee__kb( + feePerKB_JSBigInt, + network_minimumTXSize_kb, + fee_multiplier_for_priority(simple_priority), + ); // ^-- 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, @@ -313,9 +376,9 @@ function SendFunds( final__pid_encrypt, unusedOuts, feePerKB_JSBigInt, // obtained from server, so passed in - network_minimumFee - ) - } + network_minimumFee, + ); + } function __reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee( moneroReady_targetDescription_address, totalAmountWithoutFee_JSBigInt, @@ -323,36 +386,44 @@ function SendFunds( final__pid_encrypt, unusedOuts, feePerKB_JSBigInt, - passedIn_attemptAt_network_minimumFee - ) { // Now we need to establish some values for balance validation and to construct the transaction - preSuccess_nonTerminal_statusUpdate_fn(SendFunds_ProcessStep_Code.calculatingFee) + passedIn_attemptAt_network_minimumFee, + ) { + // Now we need to establish some values for balance validation and to construct the transaction + preSuccess_nonTerminal_statusUpdate_fn( + SendFunds_ProcessStep_Code.calculatingFee, + ); // - var attemptAt_network_minimumFee = passedIn_attemptAt_network_minimumFee // we may change this if isRingCT + 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; if (sweeping) { totalAmountIncludingFees = new JSBigInt("18450000000000000000"); //~uint64 max console.log("Balance required: all"); } else { - totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(attemptAt_network_minimumFee)/*.add(hostingService_chargeAmount) NOTE service fee removed for now */ - console.log("Balance required: " + monero_utils.formatMoneySymbol(totalAmountIncludingFees)); + totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add( + attemptAt_network_minimumFee, + ); /*.add(hostingService_chargeAmount) NOTE service fee removed for now */ + console.log( + "Balance required: " + + monero_utils.formatMoneySymbol(totalAmountIncludingFees), + ); } const usableOutputsAndAmounts = _outputsAndAmountToUseForMixin( totalAmountIncludingFees, unusedOuts, isRingCT, - sweeping - ) + sweeping, + ); // 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 (/*usingOuts.length > 1 &&*/ isRingCT) { + var usingOuts = usableOutputsAndAmounts.usingOuts; + var usingOutsAmount = usableOutputsAndAmounts.usingOutsAmount; + var remaining_unusedOuts = usableOutputsAndAmounts.remaining_unusedOuts; // this is a copy of the pre-mutation usingOuts + if (/*usingOuts.length > 1 &&*/ isRingCT) { var newNeededFee = calculate_fee( - feePerKB_JSBigInt, - monero_utils.estimateRctSize(usingOuts.length, mixin, 2), - fee_multiplier_for_priority(simple_priority) - ) + feePerKB_JSBigInt, + monero_utils.estimateRctSize(usingOuts.length, mixin, 2), + fee_multiplier_for_priority(simple_priority), + ); // if newNeededFee < neededFee, use neededFee instead (should only happen on the 2nd or later times through (due to estimated fee being too low)) if (newNeededFee.compare(attemptAt_network_minimumFee) < 0) { newNeededFee = attemptAt_network_minimumFee; @@ -365,189 +436,299 @@ function SendFunds( return; } */ - totalAmountWithoutFee_JSBigInt = usingOutsAmount.subtract(newNeededFee); + totalAmountWithoutFee_JSBigInt = usingOutsAmount.subtract( + newNeededFee, + ); if (totalAmountWithoutFee_JSBigInt.compare(0) < 1) { - const errStr = `Your spendable balance is too low. Have ${monero_utils.formatMoney(usingOutsAmount)} ${monero_config.coinSymbol} spendable, need ${monero_utils.formatMoney(newNeededFee)} ${monero_config.coinSymbol}.` - __trampolineFor_err_withStr(errStr) - return + const errStr = `Your spendable balance is too low. Have ${monero_utils.formatMoney( + usingOutsAmount, + )} ${ + monero_config.coinSymbol + } spendable, need ${monero_utils.formatMoney( + newNeededFee, + )} ${monero_config.coinSymbol}.`; + __trampolineFor_err_withStr(errStr); + return; } - totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(newNeededFee) - } else { - totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(newNeededFee); + totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add( + newNeededFee, + ); + } else { + 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) - console.log("Using output: " + monero_utils.formatMoney(out.amount) + " - " + JSON.stringify(out)); + while ( + usingOutsAmount.compare(totalAmountIncludingFees) < 0 && + remaining_unusedOuts.length > 0 + ) { + const out = _popAndReturnRandomElementFromList( + remaining_unusedOuts, + ); + console.log( + "Using output: " + + monero_utils.formatMoney(out.amount) + + " - " + + JSON.stringify(out), + ); // and recalculate invalidated values newNeededFee = calculate_fee( - feePerKB_JSBigInt, - monero_utils.estimateRctSize(usingOuts.length, mixin, 2), - fee_multiplier_for_priority(simple_priority) - ) - totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(newNeededFee); + feePerKB_JSBigInt, + monero_utils.estimateRctSize( + usingOuts.length, + mixin, + 2, + ), + fee_multiplier_for_priority(simple_priority), + ); + totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add( + newNeededFee, + ); } - } - console.log("New fee: " + monero_utils.formatMoneySymbol(newNeededFee) + " for " + usingOuts.length + " inputs") - attemptAt_network_minimumFee = 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)) + 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) + const usingOutsAmount_comparedTo_totalAmount = usingOutsAmount.compare( + totalAmountIncludingFees, + ); if (usingOutsAmount_comparedTo_totalAmount < 0) { - const errStr = `Your spendable balance is too low. Have ${monero_utils.formatMoney(usingOutsAmount)} ${monero_config.coinSymbol} spendable, need ${monero_utils.formatMoney(totalAmountIncludingFees)} ${monero_config.coinSymbol}.` - __trampolineFor_err_withStr(errStr) - return + const errStr = `Your spendable balance is too low. Have ${monero_utils.formatMoney( + usingOutsAmount, + )} ${ + monero_config.coinSymbol + } spendable, need ${monero_utils.formatMoney( + totalAmountIncludingFees, + )} ${monero_config.coinSymbol}.`; + __trampolineFor_err_withStr(errStr); + return; } // Now we can put together the list of fund transfers we need to perform - const fundTransferDescriptions = [] // to build… + const fundTransferDescriptions = []; // to build… // I. the actual transaction the user is asking to do - fundTransferDescriptions.push({ + fundTransferDescriptions.push({ address: moneroReady_targetDescription_address, - amount: totalAmountWithoutFee_JSBigInt - }) + 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": + // III. some amount of the total outputs will likely need to be returned to the user as "change": if (usingOutsAmount_comparedTo_totalAmount > 0) { if (sweeping) { - throw "Unexpected usingOutsAmount_comparedTo_totalAmount > 0 && sweeping" + throw "Unexpected usingOutsAmount_comparedTo_totalAmount > 0 && sweeping"; } - 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) + 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 + amount: changeAmount, + }); + } else { + // pre-ringct // do not give ourselves change < dust threshold - var changeAmountDivRem = changeAmount.divRem(monero_config.dustThreshold) - console.log("💬 changeAmountDivRem", changeAmountDivRem) + 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)") + 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) + 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 - }) + amount: usableChange, + }); } } - } else if (usingOutsAmount_comparedTo_totalAmount == 0) { + } else if (usingOutsAmount_comparedTo_totalAmount == 0) { // this should always fire when sweeping - 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(), nettype).public_addr - console.log("Sending 0 XMR to a fake address to keep tx uniform (no change exists): " + fakeAddress) + 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(), + nettype, + ).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 - }) + amount: 0, + }); } } - console.log("fundTransferDescriptions so far", fundTransferDescriptions) + console.log( + "fundTransferDescriptions so far", + fundTransferDescriptions, + ); if (mixin < 0 || isNaN(mixin)) { - __trampolineFor_err_withStr("Invalid mixin") - return + __trampolineFor_err_withStr("Invalid mixin"); + return; } - if (mixin > 0) { // first, grab RandomOuts, then enter __createTx - preSuccess_nonTerminal_statusUpdate_fn(SendFunds_ProcessStep_Code.fetchingDecoyOutputs) - hostedMoneroAPIClient.RandomOuts( - usingOuts, - mixin, - function(err, amount_outs) - { - if (err) { - __trampolineFor_err_withErr(err) - return - } - __createTxAndAttemptToSend(amount_outs) + if (mixin > 0) { + // first, grab RandomOuts, then enter __createTx + preSuccess_nonTerminal_statusUpdate_fn( + SendFunds_ProcessStep_Code.fetchingDecoyOutputs, + ); + hostedMoneroAPIClient.RandomOuts(usingOuts, mixin, function( + err, + amount_outs, + ) { + if (err) { + __trampolineFor_err_withErr(err); + return; } - ) - return - } else { // mixin === 0: -- PSNOTE: is that even allowed? - __createTxAndAttemptToSend() + __createTxAndAttemptToSend(amount_outs); + }); + return; + } else { + // mixin === 0: -- PSNOTE: is that even allowed? + __createTxAndAttemptToSend(); } - function __createTxAndAttemptToSend(mix_outs) - { - preSuccess_nonTerminal_statusUpdate_fn(SendFunds_ProcessStep_Code.constructingTransaction) + function __createTxAndAttemptToSend(mix_outs) { + preSuccess_nonTerminal_statusUpdate_fn( + SendFunds_ProcessStep_Code.constructingTransaction, + ); var signedTx; try { - console.log('Destinations: ') - monero_utils.printDsts(fundTransferDescriptions) + console.log("Destinations: "); + monero_utils.printDsts(fundTransferDescriptions); // - var realDestViewKey // need to get viewkey for encrypting here, because of splitting and sorting + 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, nettype).view - console.log("got realDestViewKey" , realDestViewKey) + realDestViewKey = monero_utils.decode_address( + moneroReady_targetDescription_address, + nettype, + ).view; + console.log("got realDestViewKey", realDestViewKey); } var splitDestinations = monero_utils.decompose_tx_destinations( - fundTransferDescriptions, - isRingCT - ) - console.log('Decomposed destinations:') - monero_utils.printDsts(splitDestinations) + 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, + wallet__public_keys, + wallet__private_keys, + splitDestinations, + usingOuts, + mix_outs, + mixin, + attemptAt_network_minimumFee, + final__payment_id, + final__pid_encrypt, + realDestViewKey, 0, isRingCT, - nettype - ) + nettype, + ); } catch (e) { var errStr; if (e) { - errStr = typeof e == "string" ? e : e.toString() + errStr = typeof e == "string" ? e : e.toString(); } else { - errStr = "Failed to create transaction with unknown error." + errStr = "Failed to create transaction with unknown error."; } - __trampolineFor_err_withStr(errStr) - return + __trampolineFor_err_withStr(errStr); + return; } - console.log("signed tx: ", JSON.stringify(signedTx)) + 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) + 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 + 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) + 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) + var txBlobBytes = serialized_signedTx.length / 2; + var numKB = Math.floor(txBlobBytes / 1024); if (txBlobBytes % 1024) { - numKB++ + numKB++; } - console.log(txBlobBytes + " bytes <= " + numKB + " KB (current fee: " + monero_utils.formatMoneyFull(attemptAt_network_minimumFee) + ")") - const feeActuallyNeededByNetwork = calculate_fee__kb(feePerKB_JSBigInt, numKB, fee_multiplier_for_priority(simple_priority)) + console.log( + txBlobBytes + + " bytes <= " + + numKB + + " KB (current fee: " + + monero_utils.formatMoneyFull(attemptAt_network_minimumFee) + + ")", + ); + const feeActuallyNeededByNetwork = calculate_fee__kb( + feePerKB_JSBigInt, + numKB, + fee_multiplier_for_priority(simple_priority), + ); // 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)) + 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, + ), + ); // this will update status back to .calculatingFee __reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee( moneroReady_targetDescription_address, @@ -556,146 +737,190 @@ function SendFunds( final__pid_encrypt, unusedOuts, feePerKB_JSBigInt, - feeActuallyNeededByNetwork // we are re-entering this codepath after changing this feeActuallyNeededByNetwork - ) + feeActuallyNeededByNetwork, // we are re-entering this codepath after changing this feeActuallyNeededByNetwork + ); // - return + 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)) + 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… - preSuccess_nonTerminal_statusUpdate_fn(SendFunds_ProcessStep_Code.submittingTransaction) + preSuccess_nonTerminal_statusUpdate_fn( + SendFunds_ProcessStep_Code.submittingTransaction, + ); hostedMoneroAPIClient.SubmitSerializedSignedTransaction( wallet__public_address, wallet__private_keys.view, serialized_signedTx, - function(err) - { + function(err) { if (err) { - __trampolineFor_err_withStr("Something unexpected occurred when submitting your transaction: " + err) - return + __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 */ + const tx_fee = final_networkFee; /*.add(hostingService_chargeAmount) NOTE: Service charge removed to reduce bloat for now */ __trampolineFor_success( moneroReady_targetDescription_address, - sweeping - ? parseFloat(monero_utils.formatMoneyFull(totalAmountWithoutFee_JSBigInt)) + sweeping + ? parseFloat( + monero_utils.formatMoneyFull( + totalAmountWithoutFee_JSBigInt, + ), + ) : amount, final__payment_id, tx_hash, - tx_fee - ) // 🎉 - } - ) + tx_fee, + ); // 🎉 + }, + ); } } } -exports.SendFunds = SendFunds +exports.SendFunds = SendFunds; // -function new_moneroReadyTargetDescriptions_fromTargetDescriptions( +function new_moneroReadyTargetDescriptions_fromTargetDescriptions( monero_openalias_utils, targetDescriptions, nettype, - fn -) // fn: (err, moneroReady_targetDescriptions) -> Void -{ // parse & normalize the target descriptions by mapping them to currency (Monero)-ready addresses & amounts + 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 + 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 + 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" + 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, nettype) // verify that the address is valid + monero_utils.decode_address(targetDescription_address, nettype); // 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 + 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) + 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 + const errStr = + "Couldn't parse amount " + + targetDescription_amount + + ": " + + e; + const err = new Error(errStr); + cb(err); + return; } - cb(null, { + cb(null, { address: targetDescription_address, - amount: moneroReady_amountToSend - }) + amount: moneroReady_amountToSend, + }); }, - function(err, moneroReady_targetDescriptions) - { - fn(err, moneroReady_targetDescriptions) - } - ) + function(err, moneroReady_targetDescriptions) { + fn(err, moneroReady_targetDescriptions); + }, + ); } // -function __randomIndex(list) -{ +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) +function _popAndReturnRandomElementFromList(list) { + var idx = __randomIndex(list); + var val = list[idx]; + list.splice(idx, 1); // - return val + return val; } function _outputsAndAmountToUseForMixin( target_amount, unusedOuts, isRingCT, - sweeping + sweeping, ) { - 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 + 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_JSBigInt = new JSBigInt(out.amount) - if (out_amount_JSBigInt.compare(monero_config.dustThreshold) < 0) { // amount is dusty.. + const out_amount_JSBigInt = new JSBigInt(out.amount); + if (out_amount_JSBigInt.compare(monero_config.dustThreshold) < 0) { + // amount is dusty.. if (sweeping == false) { - console.log("Not sweeping, and found a dusty (though maybe mixable) output... skipping it!") - continue + console.log( + "Not sweeping, and found a dusty (though maybe mixable) output... skipping it!", + ); + continue; } - if (!out.rct || typeof out.rct === 'undefined') { - console.log("Sweeping, and found a dusty but unmixable (non-rct) output... skipping it!") - continue + if (!out.rct || typeof out.rct === "undefined") { + console.log( + "Sweeping, and found a dusty but unmixable (non-rct) output... skipping it!", + ); + continue; } else { - console.log("Sweeping and found a dusty but mixable (rct) amount... keeping it!") + console.log( + "Sweeping and found a dusty but mixable (rct) amount... keeping it!", + ); } } - toFinalize_usingOuts.push(out) - toFinalize_usingOutsAmount = toFinalize_usingOutsAmount.add(out_amount_JSBigInt) - console.log("Using output: " + monero_utils.formatMoney(out_amount_JSBigInt) + " - " + JSON.stringify(out)) + toFinalize_usingOuts.push(out); + toFinalize_usingOutsAmount = toFinalize_usingOutsAmount.add( + out_amount_JSBigInt, + ); + console.log( + "Using output: " + + monero_utils.formatMoney(out_amount_JSBigInt) + + " - " + + JSON.stringify(out), + ); } return { usingOuts: toFinalize_usingOuts, usingOutsAmount: toFinalize_usingOutsAmount, - remaining_unusedOuts: remaining_unusedOuts - } -} \ No newline at end of file + remaining_unusedOuts: remaining_unusedOuts, + }; +} diff --git a/monero_utils/monero_txParsing_utils.js b/monero_utils/monero_txParsing_utils.js index a71bbd2..7be647b 100644 --- a/monero_utils/monero_txParsing_utils.js +++ b/monero_utils/monero_txParsing_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,25 +26,25 @@ // 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" +"use strict"; // -const monero_config = require('./monero_config') -const monero_utils = require('./monero_cryptonote_utils_instance') +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 +function IsTransactionConfirmed(tx, blockchain_height) { + return blockchain_height - tx.height > monero_config.txMinConfirms; } -exports.IsTransactionConfirmed = IsTransactionConfirmed +exports.IsTransactionConfirmed = IsTransactionConfirmed; // -function IsTransactionUnlocked(tx, blockchain_height) -{ - return monero_utils.is_tx_unlocked(tx.unlock_time || 0, blockchain_height) +function IsTransactionUnlocked(tx, blockchain_height) { + return monero_utils.is_tx_unlocked(tx.unlock_time || 0, blockchain_height); } -exports.IsTransactionUnlocked = IsTransactionUnlocked +exports.IsTransactionUnlocked = IsTransactionUnlocked; // -function TransactionLockedReason(tx, blockchain_height) -{ - return monero_utils.tx_locked_reason(tx.unlock_time || 0, blockchain_height) +function TransactionLockedReason(tx, blockchain_height) { + return monero_utils.tx_locked_reason( + tx.unlock_time || 0, + blockchain_height, + ); } -exports.TransactionLockedReason = TransactionLockedReason \ No newline at end of file +exports.TransactionLockedReason = TransactionLockedReason; diff --git a/monero_utils/monero_wallet_locale.js b/monero_utils/monero_wallet_locale.js index ca8caa5..f789c49 100644 --- a/monero_utils/monero_wallet_locale.js +++ b/monero_utils/monero_wallet_locale.js @@ -26,22 +26,24 @@ // 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 +"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 + return monero_wallet_utils.DefaultWalletMnemonicWordsetName; // which would be .English } -exports.MnemonicWordsetNameWithLocale = MnemonicWordsetNameWithLocale \ No newline at end of file +exports.MnemonicWordsetNameWithLocale = MnemonicWordsetNameWithLocale; diff --git a/monero_utils/monero_wallet_utils.js b/monero_utils/monero_wallet_utils.js index 38eb67b..867b050 100644 --- a/monero_utils/monero_wallet_utils.js +++ b/monero_utils/monero_wallet_utils.js @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, 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 @@ -26,305 +26,352 @@ // 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" +"use strict"; // -const mnemonic = require('../cryptonote_utils/mnemonic') -const monero_utils = require('./monero_cryptonote_utils_instance') -const monero_config = require('./monero_config') +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) +// +const wordsetNamesByWordsetName = {}; +const allWordsetNames = Object.keys(mnemonic.mn_words); for (let wordsetName of allWordsetNames) { - wordsetNamesByWordsetName[wordsetName] = wordsetName + wordsetNamesByWordsetName[wordsetName] = wordsetName; } -exports.WordsetNamesByWordsetName = wordsetNamesByWordsetName -exports.AllWordsetNames = allWordsetNames +exports.WordsetNamesByWordsetName = wordsetNamesByWordsetName; +exports.AllWordsetNames = allWordsetNames; // // // Mnemonic wordset utilities - Comparison // TODO: perhaps move this to mnemonic.js -function AreEqualMnemonics( - a, - b, - a__wordsetName, - b__wordsetName -) { +function AreEqualMnemonics(a, b, a__wordsetName, b__wordsetName) { if (a__wordsetName !== b__wordsetName) { - return false + return false; } - const wordsetName = a__wordsetName - const wordset = mnemonic.mn_words[wordsetName] - const prefix_len = wordset.prefix_len + const wordsetName = a__wordsetName; + const wordset = mnemonic.mn_words[wordsetName]; + const prefix_len = wordset.prefix_len; // since mnemonics can be entered with only the first N letters, we must check equality of mnemonics by prefix - let a__mnemonicString_words = a.split(" ") - let b__mnemonicString_words = b.split(" ") + let a__mnemonicString_words = a.split(" "); + let b__mnemonicString_words = b.split(" "); if (a__mnemonicString_words.length != b__mnemonicString_words.length) { - return false + return false; } - let numberOf_mnemonicString_words = a__mnemonicString_words.length - for (var i = 0 ; i < numberOf_mnemonicString_words ; i++) { - let a__word = a__mnemonicString_words[i] - let b__word = b__mnemonicString_words[i] + let numberOf_mnemonicString_words = a__mnemonicString_words.length; + for (var i = 0; i < numberOf_mnemonicString_words; i++) { + let a__word = a__mnemonicString_words[i]; + let b__word = b__mnemonicString_words[i]; // ... We're assuming that a and b are already valid mneminics - const a_prefix = a__word.slice(0, prefix_len) - const b_prefix = b__word.slice(0, prefix_len) + const a_prefix = a__word.slice(0, prefix_len); + const b_prefix = b__word.slice(0, prefix_len); if (a_prefix !== b_prefix) { - return false + return false; } } - return true + return true; } -exports.AreEqualMnemonics = AreEqualMnemonics +exports.AreEqualMnemonics = AreEqualMnemonics; // //////////////////////////////////////////////////////////////////////////////// // Mnemonic wordset utilities - Wordset name detection by mnemonic contents // TODO: perhaps move this to mnemonic.js -function WordsetNameAccordingToMnemonicString(mnemonicString) // throws -{ - const mnemonicString_words = mnemonicString.split(' ') +function WordsetNameAccordingToMnemonicString( + mnemonicString, // throws +) { + const mnemonicString_words = mnemonicString.split(" "); if (mnemonicString_words.length == 0) { - throw "Invalid mnemonic" + throw "Invalid mnemonic"; } - var wholeMnemonicSuspectedAsWordsetNamed = null // to derive + var wholeMnemonicSuspectedAsWordsetNamed = null; // to derive for (let mnemonicString_word of mnemonicString_words) { - var thisWordIsInWordsetNamed = null // to derive + var thisWordIsInWordsetNamed = null; // to derive for (let wordsetName of allWordsetNames) { - if (wordsetName === 'electrum') { - continue // skip because it conflicts with 'english' + if (wordsetName === "electrum") { + continue; // skip because it conflicts with 'english' } - const wordset = mnemonic.mn_words[wordsetName] - const prefix_len = wordset.prefix_len + const wordset = mnemonic.mn_words[wordsetName]; + const prefix_len = wordset.prefix_len; if (mnemonicString_word.length < prefix_len) { - throw "Please enter more than " + (prefix_len-1) + " letters per word" + throw "Please enter more than " + + (prefix_len - 1) + + " letters per word"; } - const wordsetWords = wordset.words + const wordsetWords = wordset.words; for (let wordsetWord of wordsetWords) { - if (wordsetWord.indexOf(mnemonicString_word) == 0) { // we can safely check prefix b/c we've checked mnemonicString_word is of at least min length - thisWordIsInWordsetNamed = wordsetName - break // done looking; exit interior then exterior loops + if (wordsetWord.indexOf(mnemonicString_word) == 0) { + // we can safely check prefix b/c we've checked mnemonicString_word is of at least min length + thisWordIsInWordsetNamed = wordsetName; + break; // done looking; exit interior then exterior loops } } - if (thisWordIsInWordsetNamed != null) { // just found - break // also exit + if (thisWordIsInWordsetNamed != null) { + // just found + break; // also exit } // haven't found it yet; keep looking } - if (thisWordIsInWordsetNamed === null) { // didn't find this word in any of the mnemonic wordsets - throw "Unrecognized mnemonic language" + 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 + 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" + if (wholeMnemonicSuspectedAsWordsetNamed === null) { + // this might be redundant, but for logical rigor…… + throw "Unrecognized mnemonic language"; } // - return wholeMnemonicSuspectedAsWordsetNamed + return wholeMnemonicSuspectedAsWordsetNamed; } -exports.WordsetNameAccordingToMnemonicString = WordsetNameAccordingToMnemonicString +exports.WordsetNameAccordingToMnemonicString = WordsetNameAccordingToMnemonicString; // // //////////////////////////////////////////////////////////////////////////////// // Mnemonic wordset utilities - By locale -// -const mnemonicWordsetNamesByAppLocaleNames = -{ +// +const mnemonicWordsetNamesByAppLocaleNames = { English: "english", Japanese: "japanese", Spanish: "spanish", - Portuguese: "portuguese" + Portuguese: "portuguese", // NOTE: no support for 'electrum' wordset here -} -exports.MnemonicWordsetNamesByAppLocaleNames = mnemonicWordsetNamesByAppLocaleNames +}; +exports.MnemonicWordsetNamesByAppLocaleNames = mnemonicWordsetNamesByAppLocaleNames; // -exports.DefaultWalletMnemonicWordsetName = mnemonicWordsetNamesByAppLocaleNames.English +exports.DefaultWalletMnemonicWordsetName = + mnemonicWordsetNamesByAppLocaleNames.English; // // //////////////////////////////////////////////////////////////////////////////// // Wallet creation: -// -function NewlyCreatedWallet(mnemonic_wordsetName, nettype) -{ - 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, nettype) +// +function NewlyCreatedWallet(mnemonic_wordsetName, nettype) { + 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, nettype); // return { seed: seed, mnemonicString: mnemonicString, - keys: keys - } + keys: keys, + }; } -exports.NewlyCreatedWallet = NewlyCreatedWallet +exports.NewlyCreatedWallet = NewlyCreatedWallet; // // //////////////////////////////////////////////////////////////////////////////// // Wallet login: -// -function MnemonicStringFromSeed(account_seed, mnemonic_wordsetName) -{ - const mnemonicString = mnemonic.mn_encode(account_seed, mnemonic_wordsetName) +// +function MnemonicStringFromSeed(account_seed, mnemonic_wordsetName) { + const mnemonicString = mnemonic.mn_encode( + account_seed, + mnemonic_wordsetName, + ); // - return mnemonicString + return mnemonicString; } -exports.MnemonicStringFromSeed = MnemonicStringFromSeed +exports.MnemonicStringFromSeed = MnemonicStringFromSeed; // -function SeedAndKeysFromMnemonic_sync(mnemonicString, mnemonic_wordsetName, nettype) -{ // -> {err_str?, seed?, keys?} - mnemonicString = mnemonicString.toLowerCase() || "" +function SeedAndKeysFromMnemonic_sync( + mnemonicString, + mnemonic_wordsetName, + nettype, +) { + // -> {err_str?, seed?, keys?} + mnemonicString = mnemonicString.toLowerCase() || ""; try { - var seed = null - var keys = null + var seed = null; + var keys = null; switch (mnemonic_wordsetName) { - case 'english': + case "english": try { - seed = mnemonic.mn_decode(mnemonicString) + 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") + seed = mnemonic.mn_decode(mnemonicString, "electrum"); } catch (ee) { - throw e + throw e; } } - break + break; default: - seed = mnemonic.mn_decode(mnemonicString, mnemonic_wordsetName) - break + seed = mnemonic.mn_decode(mnemonicString, mnemonic_wordsetName); + break; } if (seed === null) { - return { err_str: "Unable to derive seed", seed: null, keys: null } + return { err_str: "Unable to derive seed", seed: null, keys: null }; } - keys = monero_utils.create_address(seed, nettype) + keys = monero_utils.create_address(seed, nettype); if (keys === null) { - return { err_str: "Unable to derive keys from seed", seed: seed, keys: null } + return { + err_str: "Unable to derive keys from seed", + seed: seed, + keys: null, + }; } - return { err_str: null, seed: seed, keys: keys } + 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 } + console.error("Invalid mnemonic!"); + return { + err_str: typeof e === "string" ? e : "" + e, + seed: null, + keys: null, + }; } } -exports.SeedAndKeysFromMnemonic_sync = SeedAndKeysFromMnemonic_sync +exports.SeedAndKeysFromMnemonic_sync = SeedAndKeysFromMnemonic_sync; -function SeedAndKeysFromMnemonic(mnemonicString, mnemonic_wordsetName, nettype, fn) // made available via callback not because it's async but for convenience -{ // fn: (err?, seed?, keys?) - const payload = SeedAndKeysFromMnemonic_sync(mnemonicString, mnemonic_wordsetName, nettype) - const err = payload.err_str ? new Error(payload.err_str) : null - const seed = payload.seed - const keys = payload.keys - fn(err, seed, keys) +function SeedAndKeysFromMnemonic( + mnemonicString, + mnemonic_wordsetName, + nettype, + fn, // made available via callback not because it's async but for convenience +) { + // fn: (err?, seed?, keys?) + const payload = SeedAndKeysFromMnemonic_sync( + mnemonicString, + mnemonic_wordsetName, + nettype, + ); + 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 +exports.SeedAndKeysFromMnemonic = SeedAndKeysFromMnemonic; // function VerifiedComponentsForLogIn_sync( - address, + address, nettype, - view_key, - spend_key_orUndefinedForViewOnly, - seed_orUndefined, - wasAGeneratedWallet + view_key, + spend_key_orUndefinedForViewOnly, + seed_orUndefined, + wasAGeneratedWallet, ) { - var spend_key = typeof spend_key_orUndefinedForViewOnly !== 'undefined' && spend_key_orUndefinedForViewOnly != null && spend_key_orUndefinedForViewOnly != "" ? spend_key_orUndefinedForViewOnly : null + var spend_key = + typeof spend_key_orUndefinedForViewOnly !== "undefined" && + spend_key_orUndefinedForViewOnly != null && + spend_key_orUndefinedForViewOnly != "" + ? spend_key_orUndefinedForViewOnly + : null; var isInViewOnlyMode = spend_key == null; - if (!view_key || view_key.length !== 64 || (isInViewOnlyMode ? false : spend_key.length !== 64)) { - return { err_str: "invalid secret key length" } + 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" } + 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, nettype) + public_keys = monero_utils.decode_address(address, nettype); } catch (e) { - return { err_str: "invalid address" } + return { err_str: "invalid address" }; } var expected_view_pub; try { - expected_view_pub = monero_utils.sec_key_to_pub(view_key) + expected_view_pub = monero_utils.sec_key_to_pub(view_key); } catch (e) { - return { err_str: "invalid view key" } + return { err_str: "invalid view key" }; } - var expected_spend_pub + var expected_spend_pub; if (spend_key.length === 64) { try { - expected_spend_pub = monero_utils.sec_key_to_pub(spend_key) + expected_spend_pub = monero_utils.sec_key_to_pub(spend_key); } catch (e) { - return { err_str: "invalid spend key" } + return { err_str: "invalid spend key" }; } } if (public_keys.view !== expected_view_pub) { - return { err_str: "invalid view key" } + return { err_str: "invalid view key" }; } - if (!isInViewOnlyMode && (public_keys.spend !== expected_spend_pub)) { - return { err_str: "invalid spend key" } + if (!isInViewOnlyMode && public_keys.spend !== expected_spend_pub) { + return { err_str: "invalid spend key" }; } - const private_keys = - { + const private_keys = { view: view_key, - spend: spend_key - } + spend: spend_key, + }; var account_seed = null; // default - if (typeof seed_orUndefined !== 'undefined' && seed_orUndefined && seed_orUndefined.length != 0) { + if ( + typeof seed_orUndefined !== "undefined" && + seed_orUndefined && + seed_orUndefined.length != 0 + ) { var expected_account; try { - expected_account = monero_utils.create_address(seed_orUndefined, nettype) + expected_account = monero_utils.create_address( + seed_orUndefined, + nettype, + ); } catch (e) { - return { err_str: "invalid seed" } + return { err_str: "invalid seed" }; } - if (expected_account.view.sec !== view_key || + if ( + expected_account.view.sec !== view_key || expected_account.spend.sec !== spend_key || - expected_account.public_addr !== address) { - return { err_str: "invalid seed" } + expected_account.public_addr !== address + ) { + return { err_str: "invalid seed" }; } - account_seed = seed_orUndefined + account_seed = seed_orUndefined; } - const payload = - { + 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 + isInViewOnlyMode: isInViewOnlyMode, + }; + return payload; } -exports.VerifiedComponentsForLogIn_sync = VerifiedComponentsForLogIn_sync +exports.VerifiedComponentsForLogIn_sync = VerifiedComponentsForLogIn_sync; // function VerifiedComponentsForLogIn( - address, + address, nettype, - view_key, - spend_key_orUndefinedForViewOnly, - seed_orUndefined, + view_key, + spend_key_orUndefinedForViewOnly, + seed_orUndefined, wasAGeneratedWallet, - fn -) { // fn: (err?, address, account_seed, public_keys, private_keys, isInViewOnlyMode) -> Void + fn, +) { + // fn: (err?, address, account_seed, public_keys, private_keys, isInViewOnlyMode) -> Void const payload = VerifiedComponentsForLogIn_sync( - address, + address, nettype, - view_key, - spend_key_orUndefinedForViewOnly, - seed_orUndefined, - wasAGeneratedWallet - ) + 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 - ) + payload.isInViewOnlyMode, + ); } -exports.VerifiedComponentsForLogIn = VerifiedComponentsForLogIn \ No newline at end of file +exports.VerifiedComponentsForLogIn = VerifiedComponentsForLogIn; diff --git a/package.json b/package.json index 2d61572..caffeb3 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "type": "git", "url": "git+https://github.com/mymonero/mymonero-core-js.git" }, + "scripts": { + "format": "find . -name '*.js*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override" + }, "keywords": [ "monero", "mymonero", diff --git a/tests/cryptonote_utils.js b/tests/cryptonote_utils.js index 6fc05d5..caf9403 100644 --- a/tests/cryptonote_utils.js +++ b/tests/cryptonote_utils.js @@ -1,61 +1,120 @@ -"use strict" -const mymonero = require('mymonero-core-js'); -const assert = require('assert'); +"use strict"; +const mymonero = require("mymonero-core-js"); +const assert = require("assert"); -var public_key = "904e49462268d771cc1649084c35aa1296bfb214880fe2e7f373620a3e2ba597"; -var private_key = "52aa4c69b93b780885c9d7f51e6fd5795904962c61a2e07437e130784846f70d"; +var public_key = + "904e49462268d771cc1649084c35aa1296bfb214880fe2e7f373620a3e2ba597"; +var private_key = + "52aa4c69b93b780885c9d7f51e6fd5795904962c61a2e07437e130784846f70d"; var nettype = mymonero.nettype_utils.network_type.MAINNNET; -describe('cryptonote_utils tests', function() { - - it('is valid hex', function() { +describe("cryptonote_utils tests", function() { + it("is valid hex", function() { var valid = mymonero.monero_utils.valid_hex(private_key); assert.strictEqual(valid, true); }); - - it('fast hash / keccak-256', function() { - var hash = mymonero.monero_utils.cn_fast_hash(private_key , private_key.length); - assert.equal(hash, "64997ff54f0d82ee87d51e971a0329d4315481eaeb4ad2403c65d5843480c414"); + + it("fast hash / keccak-256", function() { + var hash = mymonero.monero_utils.cn_fast_hash( + private_key, + private_key.length, + ); + assert.equal( + hash, + "64997ff54f0d82ee87d51e971a0329d4315481eaeb4ad2403c65d5843480c414", + ); }); - - it('generate key derivation', function() { - var derivation = mymonero.monero_utils.generate_key_derivation(public_key, private_key); - assert.equal(derivation, "591c749f1868c58f37ec3d2a9d2f08e7f98417ac4f8131e3a57c1fd71273ad00"); + + it("generate key derivation", function() { + var derivation = mymonero.monero_utils.generate_key_derivation( + public_key, + private_key, + ); + assert.equal( + derivation, + "591c749f1868c58f37ec3d2a9d2f08e7f98417ac4f8131e3a57c1fd71273ad00", + ); }); - - it('decode mainnet primary address', function() { - var decoded = mymonero.monero_utils.decode_address("49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr", nettype); - var expected = { spend: "d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534", view: "576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7" } + + it("decode mainnet primary address", function() { + var decoded = mymonero.monero_utils.decode_address( + "49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr", + nettype, + ); + var expected = { + spend: + "d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534", + view: + "576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7", + }; assert.deepEqual(decoded, expected); }); - - it('decode mainnet integrated address', function() { - var decoded = mymonero.monero_utils.decode_address("4KYcX9yTizXfvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNd3mZpgrjXBKMx9ee7c", nettype); - var expected = { spend: "d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534", view: "576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7", intPaymentId: "83eab71fbee84eb9" } + + it("decode mainnet integrated address", function() { + var decoded = mymonero.monero_utils.decode_address( + "4KYcX9yTizXfvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNd3mZpgrjXBKMx9ee7c", + nettype, + ); + var expected = { + spend: + "d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534", + view: + "576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7", + intPaymentId: "83eab71fbee84eb9", + }; assert.deepEqual(decoded, expected); }); - - it('hash_to_scalar', function() { + + it("hash_to_scalar", function() { var scalar = mymonero.monero_utils.hash_to_scalar(private_key); - assert.equal(scalar, "77c5899835aa6f96b13827f43b094abf315481eaeb4ad2403c65d5843480c404"); + assert.equal( + scalar, + "77c5899835aa6f96b13827f43b094abf315481eaeb4ad2403c65d5843480c404", + ); }); - - it('derivation_to_scalar', function() { - var derivation = mymonero.monero_utils.generate_key_derivation(public_key, private_key); + + it("derivation_to_scalar", function() { + var derivation = mymonero.monero_utils.generate_key_derivation( + public_key, + private_key, + ); var scalar = mymonero.monero_utils.derivation_to_scalar(derivation, 1); - assert.equal(scalar, "201ce3c258e09eeb6132ec266d24ee1ca957828f384ce052d5bc217c2c55160d"); - }); - - it('derive public key', function() { - var derivation = mymonero.monero_utils.generate_key_derivation(public_key, private_key); - var output_key = mymonero.monero_utils.derive_public_key(derivation, 1, public_key); - assert.equal(output_key, "da26518ddb54cde24ccfc59f36df13bbe9bdfcb4ef1b223d9ab7bef0a50c8be3"); - }); - - it('derive subaddress public key', function() { - var derivation = mymonero.monero_utils.generate_key_derivation(public_key, private_key); - var subaddress_public_key = mymonero.monero_utils.derive_subaddress_public_key(public_key, derivation, 1); - assert.equal(subaddress_public_key, "dfc9e4a0039e913204c1c0f78e954a7ec7ce291d8ffe88265632f0da9d8de1be") + assert.equal( + scalar, + "201ce3c258e09eeb6132ec266d24ee1ca957828f384ce052d5bc217c2c55160d", + ); + }); + + it("derive public key", function() { + var derivation = mymonero.monero_utils.generate_key_derivation( + public_key, + private_key, + ); + var output_key = mymonero.monero_utils.derive_public_key( + derivation, + 1, + public_key, + ); + assert.equal( + output_key, + "da26518ddb54cde24ccfc59f36df13bbe9bdfcb4ef1b223d9ab7bef0a50c8be3", + ); + }); + + it("derive subaddress public key", function() { + var derivation = mymonero.monero_utils.generate_key_derivation( + public_key, + private_key, + ); + var subaddress_public_key = mymonero.monero_utils.derive_subaddress_public_key( + public_key, + derivation, + 1, + ); + assert.equal( + subaddress_public_key, + "dfc9e4a0039e913204c1c0f78e954a7ec7ce291d8ffe88265632f0da9d8de1be", + ); }); }); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..cd51a01 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + dependencies: + lodash "^4.17.10" + +lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +prettier@^1.13.5: + version "1.13.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.5.tgz#7ae2076998c8edce79d63834e9b7b09fead6bfd0"