lots of moving things around; implemented promise constructor for monero_utils (which is actually MyMoneroCoreBridge, a new class to replace cnUtil).. this was chosen after trying both promises directly in the functions and async/await in one or two decent patterns in order to avoid the requirement that integrators support async/await and to retain the option for calls to monero_utils to remain synchronous - added src/module-post as a post-js in order to fix emscripten .then infinite loop ... this PR does not include any kind of support for calling from electron renderer to electron main - that is, the code to detect electron renderer, then intercept ret.err_msg and throw only if not in electron renderer, and relay ret.err_msg otherwise has been removed for the moment.. will likely be added back

pull/41/head
Paul Shapiro 6 years ago
parent 4a789ffcfe
commit ef830ad28a

3
.gitignore vendored

@ -5,9 +5,6 @@ coverage
build
tests/emjs/MyMoneroCoreCpp.wasm
tests/emjs/MyMoneroCoreCpp.js
contrib/boost-sdk/bjam
contrib/boost-sdk/b2
contrib/boost-sdk/project-config.jam

@ -1,5 +1,5 @@
cryptonote_utils/MyMoneroCoreCpp.js
cryptonote_utils/MyMoneroCoreCpp.wasm
monero_utils/MyMoneroCoreCpp.js
monero_utils/MyMoneroCoreCpp.wasm
package.json
yarn.lock

@ -181,6 +181,7 @@ set(
-Oz \
--llvm-lto 1 \
-s WASM=1 \
--post-js ${CMAKE_CURRENT_LIST_DIR}/src/module-post.js \
-s \"BINARYEN_METHOD='native-wasm'\" \
-s NO_DYNAMIC_EXECUTION=1 \
-s \"BINARYEN_TRAP_MODE='clamp'\" \

@ -29,10 +29,10 @@ There is also a chain of build scripts which is capable of building a JS module
### Contents
* `monero_utils` contains Monero- and MyMonero-specific implementations, wrappers, and declarations.
* `monero_utils` contains Monero- and MyMonero-specific implementations, wrappers, and declarations, and the MyMonero JS and wasm implementations for the underlying cryptography behind Monero.
* `monero_utils/MyMoneroCoreCpp.(js,wasm)` are produced by transpiling Monero core C++ code to JS via Emscripten (See *Building MyMoneroCoreCpp*). A Module instance is managed by `monero_utils/MyMoneroCoreBridge.js`.
* A ready-made entrypoint for interacting with `MyMoneroCoreBridge` is located at `monero_utils/monero_utils.js` with usage `require("./monero_utils/monero_utils").then(function(monero_utils) { })`
* `cryptonote_utils` contains the MyMonero JS implementations for the underlying cryptography behind Monero.
* `cryptonote_utils/MyMoneroCoreCpp.(js,wasm)` are produced by transpiling Monero core C++ code to JS via Emscripten (See *Building MyMoneroCoreCpp*). A Module instance is managed by `cryptonote_utils/cryptonote_utils.js`.
* This readme is located at `README.md`, and the license is located at `LICENSE.txt`.
@ -44,7 +44,7 @@ There is also a chain of build scripts which is capable of building a JS module
## Building MyMoneroCoreCpp from Scratch
There's no need to build cryptonote_utils/MyMoneroCoreCpp as a build is provided, but if you were for example interested in adding a C++ function, you could use the information in this section to transpile it to JS.
There's no need to build monero_utils/MyMoneroCoreCpp as a build is provided, but if you were for example interested in adding a C++ function, you could use the information in this section to transpile it to JS.
### Repository Setup

@ -1,5 +1,5 @@
#!/bin/sh
bin/build-emcpp.sh &&
cp build/MyMoneroCoreCpp.js cryptonote_utils/ &&
cp build/MyMoneroCoreCpp.wasm cryptonote_utils/
cp build/MyMoneroCoreCpp.js monero_utils/ &&
cp build/MyMoneroCoreCpp.wasm monero_utils/

@ -39,6 +39,7 @@ function Parsed_AddressInfo__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
) {
// -> returnValuesByKey
const total_received = new JSBigInt(data.total_received || 0);
@ -61,6 +62,7 @@ function Parsed_AddressInfo__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
if (spent_output.key_image !== key_image) {
// console.log('💬 Output used as mixin (' + spent_output.key_image + '/' + key_image + ')')
@ -96,6 +98,7 @@ function Parsed_AddressInfo__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
) {
// -> returnValuesByKey
const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(
@ -108,6 +111,7 @@ function Parsed_AddressInfo__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
}
function Parsed_AddressInfo(
@ -117,6 +121,7 @@ function Parsed_AddressInfo(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn, // (err?, returnValuesByKey) -> Void
) {
const returnValuesByKey = Parsed_AddressInfo__sync(
@ -126,6 +131,7 @@ function Parsed_AddressInfo(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
fn(null, returnValuesByKey);
}
@ -135,6 +141,7 @@ function Parsed_AddressInfo__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
) {
// -> returnValuesByKey
@ -145,6 +152,7 @@ function Parsed_AddressInfo__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
);
}
@ -160,6 +168,7 @@ function Parsed_AddressTransactions(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn, // (err?, returnValuesByKey) -> Void
) {
const returnValuesByKey = Parsed_AddressTransactions__sync(
@ -169,6 +178,7 @@ function Parsed_AddressTransactions(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
fn(null, returnValuesByKey);
}
@ -179,6 +189,7 @@ function Parsed_AddressTransactions__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
) {
const account_scanned_height = data.scanned_height || 0;
const account_scanned_block_height = data.scanned_block_height || 0;
@ -200,6 +211,7 @@ function Parsed_AddressTransactions__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
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 + ')')
@ -280,6 +292,7 @@ function Parsed_AddressTransactions__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
) {
const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(
address,
@ -291,6 +304,7 @@ function Parsed_AddressTransactions__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
}
function Parsed_AddressTransactions__keyImageManaged(
@ -299,6 +313,7 @@ function Parsed_AddressTransactions__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
) {
Parsed_AddressTransactions(
@ -308,6 +323,7 @@ function Parsed_AddressTransactions__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
);
}
@ -323,6 +339,7 @@ function Parsed_UnspentOuts(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn, // (err?, returnValuesByKey)
) {
const returnValuesByKey = Parsed_UnspentOuts__sync(
@ -332,6 +349,7 @@ function Parsed_UnspentOuts(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
fn(null, returnValuesByKey);
}
@ -342,6 +360,7 @@ function Parsed_UnspentOuts__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils
) {
const data_outputs = data.outputs;
const finalized_unspentOutputs = data.outputs || []; // to finalize:
@ -397,6 +416,7 @@ function Parsed_UnspentOuts__sync(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils
);
if (
key_image ===
@ -439,6 +459,7 @@ function Parsed_UnspentOuts__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
) {
const keyImageCache = monero_keyImage_cache_utils.Lazy_KeyImageCacheForWalletWith(
address,
@ -450,6 +471,7 @@ function Parsed_UnspentOuts__sync__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
);
}
function Parsed_UnspentOuts__keyImageManaged(
@ -458,6 +480,7 @@ function Parsed_UnspentOuts__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
) {
Parsed_UnspentOuts(
@ -467,6 +490,7 @@ function Parsed_UnspentOuts__keyImageManaged(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
fn,
);
}

@ -31,7 +31,7 @@
// 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_utils_promise = require("./monero_utils/monero_utils"); // NOTE: This is actually a promise. Call .then(function(monero_utils) { }) to actually use
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");

@ -31,156 +31,78 @@
// 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 nettype_utils = require("./nettype");
const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger;
const nettype_utils = require("../cryptonote_utils/nettype");
const monero_config = require('./monero_config');
const currency_amount_format_utils = require("../cryptonote_utils/money_format_utils")(monero_config);
//
var cnUtil = function(currencyConfig)
function ret_val_boolstring_to_bool(boolstring)
{
const currency_amount_format_utils = require("../cryptonote_utils/money_format_utils")(currencyConfig)
//
this._CNCrypto = undefined; // undefined -> cause 'early' calls to CNCrypto to throw exception
const ENVIRONMENT_IS_WEB = typeof window==="object";
const ENVIRONMENT_IS_WORKER = typeof importScripts==="function";
const ENVIRONMENT_IS_NODE = typeof process==="object" && process.browser !== true && typeof require==="function" && ENVIRONMENT_IS_WORKER == false; // we want this to be true for Electron but not for a WebView
const ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
var _CNCrypto_template =
{
locateFile: function(filename, scriptDirectory)
{
if (currencyConfig["locateFile"]) {
return currencyConfig["locateFile"](filename, scriptDirectory)
}
var this_scriptDirectory = scriptDirectory
const lastChar = this_scriptDirectory.charAt(this_scriptDirectory.length - 1)
if (lastChar == "/") {
this_scriptDirectory = this_scriptDirectory.substring(0, this_scriptDirectory.length - 1) // remove trailing "/"
}
const scriptDirectory_pathComponents = this_scriptDirectory.split("/")
const lastPathComponent = scriptDirectory_pathComponents[scriptDirectory_pathComponents.length - 1]
var pathTo_cryptonoteUtilsDir; // add trailing slash to this
if (lastPathComponent == "cryptonote_utils") { // typical node or electron-main process
pathTo_cryptonoteUtilsDir = scriptDirectory_pathComponents.join("/") + "/"
} else if (ENVIRONMENT_IS_WEB) { // this will still match on electron-renderer, so the path must be patched up…
if (typeof __dirname !== undefined && __dirname !== "/") { // looks like node running in browser.. assuming Electron-renderer
// have to check != "/" b/c webpack (I think) replaces __dirname
pathTo_cryptonoteUtilsDir = "file://" + __dirname + "/" // prepending "file://" because it's going to try to stream it
} else { // actual web browser
pathTo_cryptonoteUtilsDir = this_scriptDirectory + "/mymonero_core_js/cryptonote_utils/" // this works for the MyMonero browser build, and is quite general, at least
}
} else {
throw "Undefined pathTo_cryptonoteUtilsDir. Please pass locateFile() to cryptonote_utils init."
}
const fullPath = pathTo_cryptonoteUtilsDir + filename
//
return fullPath
}
if (typeof boolstring !== "string") {
throw "ret_val_boolstring_to_bool expected string input"
}
// if (ENVIRONMENT_IS_WEB && ENVIRONMENT_IS_NODE) { // that means it's probably electron-renderer
// const fs = require("fs");
// const path = require("path");
// const filepath = path.normalize(path.join(__dirname, "MyMoneroCoreCpp.wasm"));
// const wasmBinary = fs.readFileSync(filepath)
// console.log("wasmBinary", wasmBinary)
// _CNCrypto_template["wasmBinary"] = wasmBinary
// }
this._CNCrypto = undefined;
var loaded_CNCrypto = this.loaded_CNCrypto = function()
{ // CAUTION: calling this method blocks until _CNCrypto is loaded
if (typeof this._CNCrypto === 'undefined' || !this._CNCrypto) {
throw "You must call OnceModuleReady to wait for _CNCrypto to be ready"
}
return this._CNCrypto;
if (boolstring === "true") {
return true
} else if (boolstring === "false") {
return false
}
this.moduleReadyFns = [] // initial (gets set to undefined once Module ready)
this.OnceModuleReady = function(fn)
{
if (this._CNCrypto == null) {
if (typeof this.moduleReadyFns == 'undefined' || !this.moduleReadyFns) {
throw "Expected moduleReadyFns"
}
this.moduleReadyFns.push(fn)
} else {
fn(Module)
}
}
require("./MyMoneroCoreCpp")(_CNCrypto_template).then(function(thisModule)
{
this._CNCrypto = thisModule
{
for (let fn of this.moduleReadyFns) {
fn(thisModule)
}
}
this.moduleReadyFns = [] // flash/free
});
//
function ret_val_boolstring_to_bool(boolstring)
{
if (typeof boolstring !== "string") {
throw "ret_val_boolstring_to_bool expected string input"
}
if (boolstring === "true") {
return true
} else if (boolstring === "false") {
return false
}
throw "ret_val_boolstring_to_bool given illegal input"
}
function api_safe_wordset_name(wordset_name)
throw "ret_val_boolstring_to_bool given illegal input"
}
function api_safe_wordset_name(wordset_name)
{
return wordset_name.charAt(0).toUpperCase() + wordset_name.substr(1) // capitalizes first letter
}
//
class MyMoneroCoreBridge
{
constructor(Module)
{
return wordset_name.charAt(0).toUpperCase() + wordset_name.substr(1) // capitalizes first letter
this.Module = Module;
}
//
var config = {}; // shallow copy of initConfig
for (var key in currencyConfig) {
config[key] = currencyConfig[key];
}
//
this.is_subaddress = function(addr, nettype) {
is_subaddress(addr, nettype) {
const args =
{
address: addr,
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.is_subaddress(args_str);
const ret_string = this.Module.is_subaddress(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
}
return ret_val_boolstring_to_bool(ret.retVal);
};
}
this.is_integrated_address = function(addr, nettype) {
is_integrated_address(addr, nettype) {
const args =
{
address: addr,
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.is_integrated_address(args_str);
const ret_string = this.Module.is_integrated_address(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
}
return ret_val_boolstring_to_bool(ret.retVal);
};
}
this.new_payment_id = function() {
new_payment_id() {
const args = {};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.new_payment_id(args_str);
const ret_string = this.Module.new_payment_id(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
}
return ret.retVal;
};
}
this.new__int_addr_from_addr_and_short_pid = function(
new__int_addr_from_addr_and_short_pid(
address,
short_pid,
nettype
@ -195,29 +117,27 @@ var cnUtil = function(currencyConfig)
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.new_integrated_address(args_str);
const ret_string = this.Module.new_integrated_address(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
}
return ret.retVal;
};
}
this.new_fake_address_for_rct_tx = function(nettype)
new_fake_address_for_rct_tx(nettype)
{ // TODO: possibly support sending random_scalar from JS to emscripten to avoid emscripten random
const args = { nettype_string: nettype_utils.nettype_to_API_string(nettype) };
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.new_fake_address_for_rct_tx(args_str);
const ret_string = this.Module.new_fake_address_for_rct_tx(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
}
return ret.retVal;
};
}
this.decode_address = function(address, nettype)
decode_address(address, nettype)
{
const args =
{
@ -225,8 +145,7 @@ var cnUtil = function(currencyConfig)
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.decode_address(args_str);
const ret_string = this.Module.decode_address(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
@ -237,9 +156,9 @@ var cnUtil = function(currencyConfig)
intPaymentId: ret.paymentID_string, // may be undefined
isSubaddress: ret_val_boolstring_to_bool(ret.isSubaddress)
}
};
}
this.newly_created_wallet = function(
newly_created_wallet(
locale_language_code,
nettype
) {
@ -249,8 +168,7 @@ var cnUtil = function(currencyConfig)
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.newly_created_wallet(args_str);
const ret_string = this.Module.newly_created_wallet(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
@ -265,17 +183,16 @@ var cnUtil = function(currencyConfig)
pub_spendKey_string: ret.pub_spendKey_string,
sec_spendKey_string: ret.sec_spendKey_string
};
};
}
this.are_equal_mnemonics = function(a, b) {
are_equal_mnemonics(a, b) {
const args =
{
a: a,
b: b
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.are_equal_mnemonics(args_str);
const ret_string = this.Module.are_equal_mnemonics(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
@ -283,7 +200,7 @@ var cnUtil = function(currencyConfig)
return ret_val_boolstring_to_bool(ret.retVal);
}
this.mnemonic_from_seed = function(
mnemonic_from_seed(
seed_string,
wordset_name
) {
@ -293,16 +210,15 @@ var cnUtil = function(currencyConfig)
wordset_name: api_safe_wordset_name(wordset_name)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.mnemonic_from_seed(args_str);
const ret_string = this.Module.mnemonic_from_seed(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg } // TODO: maybe return this somehow
}
return ret.retVal;
};
}
this.seed_and_keys_from_mnemonic = function(
seed_and_keys_from_mnemonic(
mnemonic_string,
nettype
) {
@ -312,8 +228,7 @@ var cnUtil = function(currencyConfig)
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.seed_and_keys_from_mnemonic(args_str);
const ret_string = this.Module.seed_and_keys_from_mnemonic(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
@ -327,9 +242,9 @@ var cnUtil = function(currencyConfig)
pub_spendKey_string: ret.pub_spendKey_string,
sec_spendKey_string: ret.sec_spendKey_string
};
};
}
this.validate_components_for_login = function(
validate_components_for_login(
address_string,
sec_viewKey_string,
sec_spendKey_string,
@ -345,8 +260,7 @@ var cnUtil = function(currencyConfig)
nettype_string: nettype_utils.nettype_to_API_string(nettype)
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.validate_components_for_login(args_str);
const ret_string = this.Module.validate_components_for_login(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg }
@ -357,9 +271,9 @@ var cnUtil = function(currencyConfig)
pub_viewKey_string: ret.pub_viewKey_string,
pub_spendKey_string: ret.pub_spendKey_string
};
};
}
this.generate_key_image = function(
generate_key_image(
tx_pub,
view_sec,
spend_pub,
@ -387,16 +301,15 @@ var cnUtil = function(currencyConfig)
out_index: "" + output_index
};
const args_str = JSON.stringify(args);
const CNCrypto = loaded_CNCrypto();
const ret_string = CNCrypto.generate_key_image(args_str);
const ret_string = this.Module.generate_key_image(args_str);
const ret = JSON.parse(ret_string);
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
return { err_msg: ret.err_msg };
}
return ret.retVal;
};
}
this.create_signed_transaction = function(
create_signed_transaction(
from_address_string,
sec_keys,
to_address_string,
@ -428,7 +341,7 @@ var cnUtil = function(currencyConfig)
);
}
this.create_signed_transaction__nonIPCsafe = function( // you can use this function to pass JSBigInts
create_signed_transaction__nonIPCsafe( // you can use this function to pass JSBigInts
from_address_string,
sec_keys,
to_address_string,
@ -517,7 +430,7 @@ var cnUtil = function(currencyConfig)
args.payment_id_string = payment_id;
}
const args_str = JSON.stringify(args);
const ret_string = loaded_CNCrypto().create_transaction(args_str);
const ret_string = this.Module.create_transaction(args_str);
const ret = JSON.parse(ret_string);
//
if (typeof ret.err_msg !== 'undefined' && ret.err_msg) {
@ -528,14 +441,62 @@ var cnUtil = function(currencyConfig)
tx_hash: ret.tx_hash,
tx_key: ret.tx_key
};
};
function assert(stmt, val) {
if (!stmt) {
throw "assert failed" + (val !== undefined ? ": " + val : "");
}
}
return this;
};
exports.cnUtil = cnUtil;
}
//
module.exports = function(options)
{
options = options || {}
//
return new Promise(function(resolve) {
const ENVIRONMENT_IS_WEB = typeof window==="object";
const ENVIRONMENT_IS_WORKER = typeof importScripts==="function";
const ENVIRONMENT_IS_NODE = typeof process==="object" && process.browser !== true && typeof require==="function" && ENVIRONMENT_IS_WORKER == false; // we want this to be true for Electron but not for a WebView
const ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
var _Module_template =
{
locateFile: function(filename, scriptDirectory)
{
// if (options["locateFile"]) {
// return options["locateFile"](filename, scriptDirectory)
// }
var this_scriptDirectory = scriptDirectory
const lastChar = this_scriptDirectory.charAt(this_scriptDirectory.length - 1)
if (lastChar == "/") {
this_scriptDirectory = this_scriptDirectory.substring(0, this_scriptDirectory.length - 1) // remove trailing "/"
}
const scriptDirectory_pathComponents = this_scriptDirectory.split("/")
const lastPathComponent = scriptDirectory_pathComponents[scriptDirectory_pathComponents.length - 1]
var pathTo_cryptonoteUtilsDir; // add trailing slash to this
if (lastPathComponent == "monero_utils") { // typical node or electron-main process
pathTo_cryptonoteUtilsDir = scriptDirectory_pathComponents.join("/") + "/"
} else if (ENVIRONMENT_IS_WEB) { // this will still match on electron-renderer, so the path must be patched up…
if (typeof __dirname !== undefined && __dirname !== "/") { // looks like node running in browser.. assuming Electron-renderer
// have to check != "/" b/c webpack (I think) replaces __dirname
pathTo_cryptonoteUtilsDir = "file://" + __dirname + "/" // prepending "file://" because it's going to try to stream it
} else { // actual web browser
pathTo_cryptonoteUtilsDir = this_scriptDirectory + "/mymonero_core_js/monero_utils/" // this works for the MyMonero browser build, and is quite general, at least
}
} else {
throw "Undefined pathTo_cryptonoteUtilsDir. Please pass locateFile() to cryptonote_utils init."
}
const fullPath = pathTo_cryptonoteUtilsDir + filename
//
return fullPath
}
}
// if (ENVIRONMENT_IS_WEB && ENVIRONMENT_IS_NODE) { // that means it's probably electron-renderer
// const fs = require("fs");
// const path = require("path");
// const filepath = path.normalize(path.join(__dirname, "MyMoneroCoreCpp.wasm"));
// const wasmBinary = fs.readFileSync(filepath)
// console.log("wasmBinary", wasmBinary)
// _Module_template["wasmBinary"] = wasmBinary
// }
require("./MyMoneroCoreCpp")(_Module_template).ready.then(function(thisModule)
{
const instance = new MyMoneroCoreBridge(thisModule);
resolve(instance);
});
});
};

File diff suppressed because one or more lines are too long

@ -1,49 +0,0 @@
// 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
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
"use strict";
//
const ENVIRONMENT_IS_WEB = typeof window==="object";
const ENVIRONMENT_IS_WORKER = typeof importScripts==="function";
const ENVIRONMENT_IS_NODE = typeof process==="object" && process.browser !== true && typeof require==="function" && ENVIRONMENT_IS_WORKER == false; // we want this to be true for Electron but not for a WebView
if (!ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_WEB) {
throw "Not expecting this module to be included in this environment: non-node or web"
}
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 local_fns = {};
const fn_names = require('./bridged_fns_spec').cryptonote_utils_bridge_fn_interface_names;
for (const i in fn_names) {
const name = fn_names[i]
local_fns[name] = function()
{
return monero_cryptonote_utils_instance[name].apply(null, arguments); // We are not intercepting the err_msg here -- because we will kill the remote fn call if we throw -- so we'll let the renderer-side throw
}
}
module.exports = local_fns;

@ -1,72 +0,0 @@
// 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
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
"use strict";
//
const ENVIRONMENT_IS_WEB = typeof window==="object";
const ENVIRONMENT_IS_WORKER = typeof importScripts==="function";
const ENVIRONMENT_IS_NODE = typeof process==="object" && process.browser !== true && typeof require==="function" && ENVIRONMENT_IS_WORKER == false; // we want this to be true for Electron but not for a WebView
var isElectronRenderer = (ENVIRONMENT_IS_NODE&&ENVIRONMENT_IS_WEB)/*this may become insufficient*/
|| (typeof window !== 'undefined' && window.IsElectronRendererProcess == true);
//
var monero_cryptonote_utils_instance = null;
if (isElectronRenderer) {
// Require file again except on the main process ...
// this avoids a host of issues running wasm on the renderer side,
// for right now until we can load such files raw w/o unsafe-eval
// script-src CSP. makes calls synchronous. if that is a perf problem
// we can make API async.
//
// Resolves relative to the entrypoint of the main process.
monero_cryptonote_utils_instance = require('electron').remote.require("../mymonero_core_js/monero_utils/_monero_utils_i_nonthrowing")
} else {
const monero_config = require("./monero_config");
const cryptonote_utils = require("../cryptonote_utils/cryptonote_utils").cnUtil;
monero_cryptonote_utils_instance = cryptonote_utils(monero_config);
}
if (monero_cryptonote_utils_instance == null) {
throw "Unable to make monero_cryptonote_utils_instance"
}
const local_fns = {};
const fn_names = require('./bridged_fns_spec').cryptonote_utils_bridge_fn_interface_names;
for (const i in fn_names) {
const name = fn_names[i]
local_fns[name] = function()
{
const retVal = monero_cryptonote_utils_instance[name].apply(null, arguments);
if (typeof retVal === "object") {
const err_msg = retVal.err_msg
if (typeof err_msg !== 'undefined' && err_msg) {
throw err_msg; // because we can't throw from electron remote w/o killing fn call
// ... and because parsing out this err_msg everywhere is sorta inefficient
}
}
return retVal;
}
}
module.exports = local_fns;

@ -28,8 +28,6 @@
//
"use strict";
//
const monero_utils = require("./monero_cryptonote_utils_instance");
//
const Lazy_KeyImage = function(
mutable_keyImagesByCacheKey, // pass a mutable JS dictionary
tx_pub_key,
@ -38,6 +36,7 @@ const Lazy_KeyImage = function(
view_key__private,
spend_key__public,
spend_key__private,
monero_utils // must pass this so this fn can remain synchronous
) {
var cache_index = tx_pub_key + ":" + public_address + ":" + out_index;
const cached__key_image = mutable_keyImagesByCacheKey[cache_index];

@ -29,7 +29,6 @@
"use strict";
//
const monero_config = require("./monero_config");
const monero_utils = require("./monero_cryptonote_utils_instance");
//
const URITypes = {
addressAsFirstPathComponent: 1,
@ -90,7 +89,7 @@ function New_RequestFunds_URI(args) {
}
exports.New_RequestFunds_URI = New_RequestFunds_URI;
//
function New_ParsedPayload_FromPossibleRequestURIString(string, nettype) {
function New_ParsedPayload_FromPossibleRequestURIString(string, nettype, monero_utils/*pass this so this fn remains sync*/) {
// throws; -> {}
//
// detect no-scheme moneroAddr and possible OA addr - if has no monero: prefix

@ -31,7 +31,7 @@
const async = require("async");
//
const monero_config = require("./monero_config");
const monero_utils = require("./monero_cryptonote_utils_instance");
const monero_utils_promise = require('./monero_utils')
const monero_amount_format_utils = require("./monero_amount_format_utils");
const monero_paymentID_utils = require("./monero_paymentID_utils");
const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger;
@ -213,21 +213,14 @@ function SendFunds(
// err
// )
) {
const mixin = fixedMixin();
var isRingCT = true;
var sweeping = isSweep_orZeroWhenAmount === true; // rather than, say, undefined
//
// some callback trampoline function declarations…
function __trampolineFor_success(
moneroReady_targetDescription_address,
sentAmount,
final__payment_id,
tx_hash,
tx_fee,
tx_key,
mixin,
) {
success_fn(
monero_utils_promise.then(function(monero_utils)
{
const mixin = fixedMixin();
var isRingCT = true;
var sweeping = isSweep_orZeroWhenAmount === true; // rather than, say, undefined
//
// some callback trampoline function declarations…
function __trampolineFor_success(
moneroReady_targetDescription_address,
sentAmount,
final__payment_id,
@ -235,498 +228,508 @@ function SendFunds(
tx_fee,
tx_key,
mixin,
);
}
function __trampolineFor_err_withErr(err) {
failWithErr_fn(err);
}
function __trampolineFor_err_withStr(errStr) {
const err = new Error(errStr);
console.error(errStr);
failWithErr_fn(err);
}
//
// 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,
};
new_moneroReadyTargetDescriptions_fromTargetDescriptions(
monero_openalias_utils,
[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) {
if (err) {
__trampolineFor_err_withErr(err);
return;
}
const invalidOrZeroDestination_errStr =
"You need to enter a valid destination";
if (moneroReady_targetDescriptions.length === 0) {
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr);
return;
}
const moneroReady_targetDescription =
moneroReady_targetDescriptions[0];
if (
moneroReady_targetDescription === null ||
typeof moneroReady_targetDescription === "undefined"
) {
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr);
return;
}
_proceedTo_prepareToSendFundsTo_moneroReady_targetDescription(
moneroReady_targetDescription,
) {
success_fn(
moneroReady_targetDescription_address,
sentAmount,
final__payment_id,
tx_hash,
tx_fee,
tx_key,
mixin,
);
},
);
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_amount_format_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;
}
function __trampolineFor_err_withErr(err) {
failWithErr_fn(err);
}
function __trampolineFor_err_withStr(errStr) {
const err = new Error(errStr);
console.error(errStr);
failWithErr_fn(err);
}
//
// Derive/finalize some values…
var final__payment_id = payment_id;
var final__pid_encrypt = false; // we don't want to encrypt payment ID unless we find an integrated one
var address__decode_result;
try {
address__decode_result = monero_utils.decode_address(
moneroReady_targetDescription_address,
nettype,
// 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,
};
new_moneroReadyTargetDescriptions_fromTargetDescriptions(
monero_openalias_utils,
[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) {
if (err) {
__trampolineFor_err_withErr(err);
return;
}
const invalidOrZeroDestination_errStr =
"You need to enter a valid destination";
if (moneroReady_targetDescriptions.length === 0) {
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr);
return;
}
const moneroReady_targetDescription =
moneroReady_targetDescriptions[0];
if (
moneroReady_targetDescription === null ||
typeof moneroReady_targetDescription === "undefined"
) {
__trampolineFor_err_withStr(invalidOrZeroDestination_errStr);
return;
}
_proceedTo_prepareToSendFundsTo_moneroReady_targetDescription(
moneroReady_targetDescription,
);
},
);
function _proceedTo_prepareToSendFundsTo_moneroReady_targetDescription(
moneroReady_targetDescription,
) {
var moneroReady_targetDescription_address =
moneroReady_targetDescription.address;
var moneroReady_targetDescription_amount =
moneroReady_targetDescription.amount;
//
var totalAmountWithoutFee_JSBigInt = new JSBigInt(0).add(
moneroReady_targetDescription_amount,
);
} catch (e) {
__trampolineFor_err_withStr(
typeof e === "string" ? e : e.toString(),
console.log(
"💬 Total to send, before fee: " + sweeping
? "all"
: monero_amount_format_utils.formatMoney(totalAmountWithoutFee_JSBigInt),
);
return;
}
if (payment_id) {
if (address__decode_result.intPaymentId) {
const errStr =
"Payment ID must be blank when using an Integrated Address";
if (!sweeping && totalAmountWithoutFee_JSBigInt.compare(0) <= 0) {
const errStr = "The amount you've entered is too low";
__trampolineFor_err_withStr(errStr);
return;
} else if (
monero_utils.is_subaddress(
}
//
// Derive/finalize some values…
var final__payment_id = payment_id;
var final__pid_encrypt = false; // we don't want to encrypt payment ID unless we find an integrated one
var address__decode_result;
try {
address__decode_result = monero_utils.decode_address(
moneroReady_targetDescription_address,
nettype,
)
);
} catch (e) {
__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;
}
}
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 =
"Payment ID must be blank when using a Subaddress";
const errStr = "Invalid payment ID.";
__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(
//
_proceedTo_getUnspentOutsUsableForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
) === false
) {
const errStr = "Invalid payment ID.";
__trampolineFor_err_withStr(errStr);
return;
final__pid_encrypt,
);
}
//
_proceedTo_getUnspentOutsUsableForMixin(
function _proceedTo_getUnspentOutsUsableForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
);
}
function _proceedTo_getUnspentOutsUsableForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id, // non-existent or valid
final__pid_encrypt, // true or false
) {
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.fetchingLatestBalance,
);
hostedMoneroAPIClient.UnspentOuts(
wallet__public_address,
wallet__private_keys.view,
wallet__public_keys.spend,
wallet__private_keys.spend,
mixin,
sweeping,
function(err, unspentOuts, unusedOuts, dynamic_feePerKB_JSBigInt) {
if (err) {
__trampolineFor_err_withErr(err);
return;
}
console.log(
"Received dynamic per kb fee",
monero_amount_format_utils.formatMoneySymbol(dynamic_feePerKB_JSBigInt),
);
_proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
dynamic_feePerKB_JSBigInt,
);
},
);
}
function _proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
dynamic_feePerKB_JSBigInt,
) {
// status: constructing transaction…
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),
);
// ^-- 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(
final__payment_id, // non-existent or valid
final__pid_encrypt, // true or false
) {
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.fetchingLatestBalance,
);
hostedMoneroAPIClient.UnspentOuts(
wallet__public_address,
wallet__private_keys.view,
wallet__public_keys.spend,
wallet__private_keys.spend,
mixin,
sweeping,
function(err, unspentOuts, unusedOuts, dynamic_feePerKB_JSBigInt) {
if (err) {
__trampolineFor_err_withErr(err);
return;
}
console.log(
"Received dynamic per kb fee",
monero_amount_format_utils.formatMoneySymbol(dynamic_feePerKB_JSBigInt),
);
_proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
dynamic_feePerKB_JSBigInt,
);
},
);
}
function _proceedTo_constructFundTransferListAndSendFundsByUsingUnusedUnspentOutsForMixin(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
feePerKB_JSBigInt, // obtained from server, so passed in
network_minimumFee,
);
}
function __reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
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,
);
//
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_amount_format_utils.formatMoneySymbol(totalAmountIncludingFees),
dynamic_feePerKB_JSBigInt,
) {
// status: constructing transaction…
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),
);
// ^-- now we're going to try using this minimum fee but the codepath has to be able to be re-entered if we find after constructing the whole tx that it is larger in kb than the minimum fee we're attempting to send it off with
__reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
feePerKB_JSBigInt, // obtained from server, so passed in
network_minimumFee,
);
}
const usableOutputsAndAmounts = _outputsAndAmountToUseForMixin(
totalAmountIncludingFees,
function __reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
isRingCT,
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 newNeededFee = calculate_fee(
feePerKB_JSBigInt,
estimateRctSize(usingOuts.length, mixin, 2),
fee_multiplier_for_priority(simple_priority),
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,
);
// 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;
}
//
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) {
/*
// When/if sending to multiple destinations supported, uncomment and port this:
if (dsts.length !== 1) {
deferred.reject("Sweeping to multiple accounts is not allowed");
return;
}
*/
totalAmountWithoutFee_JSBigInt = usingOutsAmount.subtract(
newNeededFee,
);
if (totalAmountWithoutFee_JSBigInt.compare(0) < 1) {
const errStr = `Your spendable balance is too low. Have ${monero_amount_format_utils.formatMoney(
usingOutsAmount,
)} ${
monero_config.coinSymbol
} spendable, need ${monero_amount_format_utils.formatMoney(
newNeededFee,
)} ${monero_config.coinSymbol}.`;
__trampolineFor_err_withStr(errStr);
return;
}
totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(
newNeededFee,
);
totalAmountIncludingFees = new JSBigInt("18450000000000000000"); //~uint64 max
console.log("Balance required: all");
} else {
totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(
newNeededFee,
attemptAt_network_minimumFee,
); /*.add(hostingService_chargeAmount) NOTE service fee removed for now */
console.log(
"Balance required: " +
monero_amount_format_utils.formatMoneySymbol(totalAmountIncludingFees),
);
// 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_amount_format_utils.formatMoney(out.amount) +
" - " +
JSON.stringify(out),
}
const usableOutputsAndAmounts = _outputsAndAmountToUseForMixin(
totalAmountIncludingFees,
unusedOuts,
isRingCT,
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 newNeededFee = calculate_fee(
feePerKB_JSBigInt,
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;
}
if (sweeping) {
/*
// When/if sending to multiple destinations supported, uncomment and port this:
if (dsts.length !== 1) {
deferred.reject("Sweeping to multiple accounts is not allowed");
return;
}
*/
totalAmountWithoutFee_JSBigInt = usingOutsAmount.subtract(
newNeededFee,
);
// and recalculate invalidated values
newNeededFee = calculate_fee(
feePerKB_JSBigInt,
estimateRctSize(
usingOuts.length,
mixin,
2,
),
fee_multiplier_for_priority(simple_priority),
if (totalAmountWithoutFee_JSBigInt.compare(0) < 1) {
const errStr = `Your spendable balance is too low. Have ${monero_amount_format_utils.formatMoney(
usingOutsAmount,
)} ${
monero_config.coinSymbol
} spendable, need ${monero_amount_format_utils.formatMoney(
newNeededFee,
)} ${monero_config.coinSymbol}.`;
__trampolineFor_err_withStr(errStr);
return;
}
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_amount_format_utils.formatMoney(out.amount) +
" - " +
JSON.stringify(out),
);
// and recalculate invalidated values
newNeededFee = calculate_fee(
feePerKB_JSBigInt,
estimateRctSize(
usingOuts.length,
mixin,
2,
),
fee_multiplier_for_priority(simple_priority),
);
totalAmountIncludingFees = totalAmountWithoutFee_JSBigInt.add(
newNeededFee,
);
}
}
console.log(
"New fee: " +
monero_amount_format_utils.formatMoneySymbol(newNeededFee) +
" for " +
usingOuts.length +
" inputs",
);
attemptAt_network_minimumFee = newNeededFee;
}
console.log(
"New fee: " +
monero_amount_format_utils.formatMoneySymbol(newNeededFee) +
" for " +
usingOuts.length +
" inputs",
"~ Balance required: " +
monero_amount_format_utils.formatMoneySymbol(totalAmountIncludingFees),
);
attemptAt_network_minimumFee = newNeededFee;
}
console.log(
"~ Balance required: " +
monero_amount_format_utils.formatMoneySymbol(totalAmountIncludingFees),
);
// Now we can validate available balance with usingOutsAmount (TODO? maybe this check can be done before selecting outputs?)
const usingOutsAmount_comparedTo_totalAmount = usingOutsAmount.compare(
totalAmountIncludingFees,
);
if (usingOutsAmount_comparedTo_totalAmount < 0) {
const errStr = `Your spendable balance is too low. Have ${monero_amount_format_utils.formatMoney(
usingOutsAmount,
)} ${
monero_config.coinSymbol
} spendable, need ${monero_amount_format_utils.formatMoney(
totalAmountIncludingFees,
)} ${monero_config.coinSymbol}.`;
__trampolineFor_err_withStr(errStr);
return;
}
//
// Must calculate change..
var changeAmount = JSBigInt("0"); // to initialize
if (usingOutsAmount_comparedTo_totalAmount > 0) {
if (sweeping) {
throw "Unexpected usingOutsAmount_comparedTo_totalAmount > 0 && sweeping";
}
changeAmount = usingOutsAmount.subtract(
// 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,
);
}
console.log("Calculated changeAmount:", changeAmount);
//
// 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);
});
//
function __createTxAndAttemptToSend(mix_outs) {
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.constructingTransaction,
);
function printDsts(dsts)
{
for (var i = 0; i < dsts.length; i++) {
console.log(dsts[i].address + ": " + monero_amount_format_utils.formatMoneyFull(dsts[i].amount))
}
}
var create_transaction__retVals;
try {
create_transaction__retVals = monero_utils.create_signed_transaction(
wallet__public_address,
wallet__private_keys,
target_address,
usingOuts,
mix_outs,
mixin,
totalAmountWithoutFee_JSBigInt.toString(), // even though it's in dsts, sending it directly as core C++ takes it
changeAmount.toString(),
attemptAt_network_minimumFee.toString(), // must serialize for IPC
final__payment_id,
0,
isRingCT,
nettype,
);
console.log("got back", create_transaction__retVals)
} catch (e) {
var errStr;
if (e) {
errStr = typeof e == "string" ? e : e.toString();
} else {
errStr = "Failed to create transaction with unknown error.";
}
if (usingOutsAmount_comparedTo_totalAmount < 0) {
const errStr = `Your spendable balance is too low. Have ${monero_amount_format_utils.formatMoney(
usingOutsAmount,
)} ${
monero_config.coinSymbol
} spendable, need ${monero_amount_format_utils.formatMoney(
totalAmountIncludingFees,
)} ${monero_config.coinSymbol}.`;
__trampolineFor_err_withStr(errStr);
return;
}
console.log("created tx: ", JSON.stringify(create_transaction__retVals));
//
if (typeof create_transaction__retVals.err_msg !== 'undefined' && create_transaction__retVals.err_msg) {
// actually not expecting this! but just in case..
__trampolineFor_err_withStr(create_transaction__retVals.err_msg);
return;
// Must calculate change..
var changeAmount = JSBigInt("0"); // to initialize
if (usingOutsAmount_comparedTo_totalAmount > 0) {
if (sweeping) {
throw "Unexpected usingOutsAmount_comparedTo_totalAmount > 0 && sweeping";
}
changeAmount = usingOutsAmount.subtract(
totalAmountIncludingFees,
);
}
var serialized_signedTx = create_transaction__retVals.signed_serialized_tx;
var tx_hash = create_transaction__retVals.tx_hash;
var tx_key = create_transaction__retVals.tx_key;
console.log("tx serialized: " + serialized_signedTx);
console.log("Tx hash: " + tx_hash);
console.log("Tx key: " + tx_key);
console.log("Calculated changeAmount:", changeAmount);
//
// work out per-kb fee for transaction and verify that it's enough
var txBlobBytes = serialized_signedTx.length / 2;
var numKB = Math.floor(txBlobBytes / 1024);
if (txBlobBytes % 1024) {
numKB++;
}
console.log(
txBlobBytes +
" bytes <= " +
numKB +
" KB (current fee: " +
monero_amount_format_utils.formatMoneyFull(attemptAt_network_minimumFee) +
")",
);
const feeActuallyNeededByNetwork = calculate_fee__kb(
feePerKB_JSBigInt,
numKB,
fee_multiplier_for_priority(simple_priority),
// first, grab RandomOuts, then enter __createTx
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.fetchingDecoyOutputs,
);
// if we need a higher fee
if (
feeActuallyNeededByNetwork.compare(
attemptAt_network_minimumFee,
) > 0
hostedMoneroAPIClient.RandomOuts(usingOuts, mixin, function(
err,
amount_outs,
) {
if (err) {
__trampolineFor_err_withErr(err);
return;
}
__createTxAndAttemptToSend(amount_outs);
});
//
function __createTxAndAttemptToSend(mix_outs) {
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.constructingTransaction,
);
function printDsts(dsts)
{
for (var i = 0; i < dsts.length; i++) {
console.log(dsts[i].address + ": " + monero_amount_format_utils.formatMoneyFull(dsts[i].amount))
}
}
var create_transaction__retVals;
try {
create_transaction__retVals = monero_utils.create_signed_transaction(
wallet__public_address,
wallet__private_keys,
target_address,
usingOuts,
mix_outs,
mixin,
totalAmountWithoutFee_JSBigInt.toString(), // even though it's in dsts, sending it directly as core C++ takes it
changeAmount.toString(),
attemptAt_network_minimumFee.toString(), // must serialize for IPC
final__payment_id,
0,
isRingCT,
nettype,
);
console.log("got back", create_transaction__retVals)
} catch (e) {
var errStr;
if (e) {
errStr = typeof e == "string" ? e : e.toString();
} else {
errStr = "Failed to create transaction with unknown error.";
}
__trampolineFor_err_withStr(errStr);
return;
}
console.log("created tx: ", JSON.stringify(create_transaction__retVals));
//
if (typeof create_transaction__retVals.err_msg !== 'undefined' && create_transaction__retVals.err_msg) {
// actually not expecting this! but just in case..
__trampolineFor_err_withStr(create_transaction__retVals.err_msg);
return;
}
var serialized_signedTx = create_transaction__retVals.signed_serialized_tx;
var tx_hash = create_transaction__retVals.tx_hash;
var tx_key = create_transaction__retVals.tx_key;
console.log("tx serialized: " + serialized_signedTx);
console.log("Tx hash: " + tx_hash);
console.log("Tx key: " + tx_key);
//
// work out per-kb fee for transaction and verify that it's enough
var txBlobBytes = serialized_signedTx.length / 2;
var numKB = Math.floor(txBlobBytes / 1024);
if (txBlobBytes % 1024) {
numKB++;
}
console.log(
"💬 Need to reconstruct the tx with enough of a network fee. Previous fee: " +
monero_amount_format_utils.formatMoneyFull(
attemptAt_network_minimumFee,
) +
" New fee: " +
monero_amount_format_utils.formatMoneyFull(
feeActuallyNeededByNetwork,
),
txBlobBytes +
" bytes <= " +
numKB +
" KB (current fee: " +
monero_amount_format_utils.formatMoneyFull(attemptAt_network_minimumFee) +
")",
);
// this will update status back to .calculatingFee
__reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
moneroReady_targetDescription_address,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
final__pid_encrypt,
unusedOuts,
const feeActuallyNeededByNetwork = calculate_fee__kb(
feePerKB_JSBigInt,
feeActuallyNeededByNetwork, // we are re-entering this codepath after changing this feeActuallyNeededByNetwork
numKB,
fee_multiplier_for_priority(simple_priority),
);
//
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_amount_format_utils.formatMoney(final_networkFee),
);
// status: submitting…
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.submittingTransaction,
);
hostedMoneroAPIClient.SubmitSerializedSignedTransaction(
wallet__public_address,
wallet__private_keys.view,
serialized_signedTx,
function(err) {
if (err) {
__trampolineFor_err_withStr(
"Something unexpected occurred when submitting your transaction: " +
err,
);
return;
}
const tx_fee = final_networkFee; /*.add(hostingService_chargeAmount) NOTE: Service charge removed to reduce bloat for now */
__trampolineFor_success(
// 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_amount_format_utils.formatMoneyFull(
attemptAt_network_minimumFee,
) +
" New fee: " +
monero_amount_format_utils.formatMoneyFull(
feeActuallyNeededByNetwork,
),
);
// this will update status back to .calculatingFee
__reenterable_constructFundTransferListAndSendFunds_findingLowestNetworkFee(
moneroReady_targetDescription_address,
sweeping
? parseFloat(
monero_amount_format_utils.formatMoneyFull(
totalAmountWithoutFee_JSBigInt,
),
)
: amount,
totalAmountWithoutFee_JSBigInt,
final__payment_id,
tx_hash,
tx_fee,
tx_key,
mixin,
); // 🎉
},
);
final__pid_encrypt,
unusedOuts,
feePerKB_JSBigInt,
feeActuallyNeededByNetwork, // we are re-entering this codepath after changing this feeActuallyNeededByNetwork
);
//
return;
}
//
// generated with correct per-kb fee
const final_networkFee = attemptAt_network_minimumFee; // just to make things clear
console.log(
"💬 Successful tx generation, submitting tx. Going with final_networkFee of ",
monero_amount_format_utils.formatMoney(final_networkFee),
);
// status: submitting…
preSuccess_nonTerminal_statusUpdate_fn(
SendFunds_ProcessStep_Code.submittingTransaction,
);
hostedMoneroAPIClient.SubmitSerializedSignedTransaction(
wallet__public_address,
wallet__private_keys.view,
serialized_signedTx,
function(err) {
if (err) {
__trampolineFor_err_withStr(
"Something unexpected occurred when submitting your transaction: " +
err,
);
return;
}
const tx_fee = final_networkFee; /*.add(hostingService_chargeAmount) NOTE: Service charge removed to reduce bloat for now */
__trampolineFor_success(
moneroReady_targetDescription_address,
sweeping
? parseFloat(
monero_amount_format_utils.formatMoneyFull(
totalAmountWithoutFee_JSBigInt,
),
)
: amount,
final__payment_id,
tx_hash,
tx_fee,
tx_key,
mixin,
); // 🎉
},
);
}
}
}
});
}
exports.SendFunds = SendFunds;
//

@ -26,24 +26,13 @@
// 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.
//
// This file is here merely to share configuration
//
exports.cryptonote_utils_bridge_fn_interface_names =
[
"OnceModuleReady",
//
"is_subaddress",
"is_integrated_address",
"new_payment_id",
"new__int_addr_from_addr_and_short_pid",
"new_fake_address_for_rct_tx",
"decode_address",
"newly_created_wallet",
"are_equal_mnemonics",
"mnemonic_from_seed",
"seed_and_keys_from_mnemonic",
"validate_components_for_login",
"generate_key_image",
"create_signed_transaction",
"create_signed_transaction__nonIPCsafe"
];
"use strict";
//
const promise = require('./MyMoneroCoreBridge')({}) // this returns a promise
promise.catch(function(e)
{
console.log("Error: ", e);
// this may be insufficient… being able to throw would be nice
});
//
module.exports = promise

@ -42,7 +42,7 @@
"verbose": true,
"coveragePathIgnorePatterns": [
"node_modules",
"cryptonote_utils/MyMoneroCoreCpp.js",
"monero_utils/MyMoneroCoreCpp.js",
"tests/emjs/*"
]
}

@ -0,0 +1,9 @@
Module['ready'] = new Promise(function (resolve, reject) {
delete Module['then']
Module['onAbort'] = function (what) {
reject(what)
}
addOnPostRun(function () {
resolve(Module)
})
})

@ -40,31 +40,37 @@ var nettype = mymonero.nettype_utils.network_type.MAINNET;
describe("cryptonote_utils tests", function() {
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);
async function test() {
var decoded = (await mymonero.monero_utils_promise).decode_address(
"49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr",
nettype,
);
var expected = {
spend:
"d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534",
view:
"576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7",
};
assert.deepEqual(decoded, expected);
}
test()
});
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);
async function test() {
var decoded = (await mymonero.monero_utils_promise).decode_address(
"4KYcX9yTizXfvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNd3mZpgrjXBKMx9ee7c",
nettype,
);
var expected = {
spend:
"d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534",
view:
"576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7",
intPaymentId: "83eab71fbee84eb9",
};
assert.deepEqual(decoded, expected);
}
test()
});
});

@ -28,12 +28,16 @@
"use strict";
//
const monero_utils = require("../../monero_utils/monero_cryptonote_utils_instance");
console.time("Load module")
monero_utils.OnceModuleReady(function(Module)
{
async function tests()
{
const monero_utils = await require("../monero_utils/monero_utils")
//
const Module = monero_utils.Module;
console.timeEnd("Load module")
//
//
setTimeout(function() {
console.time("create_transaction")
const args_str = '{"nettype_string":"MAINNET","from_address_string":"43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg","sec_viewKey_string":"7bea1907940afdd480eff7c4bcadb478a0fbb626df9e3ed74ae801e18f53e104","sec_spendKey_string":"4e6d43cd03812b803c6f3206689f5fcc910005fc7e91d50d79b0776dbefcd803","to_address_string":"43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg","payment_id_string":"b79f8efc81f58f67","unlock_time":"0","sending_amount":"10000000000","change_amount":"112832250000","fee_amount":"2167750000","outputs":[{"amount":"125000000000","public_key":"596fa47b6b3905269503435099a05e3ede54564026c93cbe5285e2df074c7118","rct":"920ee8d99299f304d17fdb104720d1f62be0b03383c7bb466ff39c6a264d80d616ce1eccd6c4de1cc0fba87e463f2e0c373146c475e8a1517f36e7a37351d50034688cc8cb528c14188cae45d89b313d444e583c9d68a32cb80938a5e2aa200b","global_index":"6451664","index":"0","tx_pub_key":"0a86e588dc67ca11993737e003a9e60c57174a663a47495e3b1d764f486fc88f"}],"mix_outs":[{"amount":"0","outputs":[{"global_index":"5260585","public_key":"da77082624fce921891c4fb80a1e7076a6714ca8c9fc547311737926a0b85a46","rct":"bb227b27e36b7f3e695dffb641c29bb60bfd991accdb5ef4b580c9acd48c16b6"},{"global_index":"1929918","public_key":"8c983e7053d7a1dc9de8ac00468bcf11836a787d712dc0c02bd54a3ee00a55e8","rct":"8dec45867644d1a76aafe4487292d7cf401302e6bbbb99a61c2f3b6cef4f4f34"},{"global_index":"3921094","public_key":"0133219bd5e247eef51003921ec792784c41fc34289c703e9326d46f78d9b10a","rct":"75082f4ce31904acba4af37699c28d8d4f0f74fdf63b1e4a8069ebed50df3220"},{"global_index":"6627106","public_key":"daef1663dd1084bd7fe585c3d493480ee1c4cefb93254eac5855afdf38f662b1","rct":"1d96763c5bc3300090c286705b7d544f02c185d9be8c32baac6bbfb8e0d0d283"},{"global_index":"3308654","public_key":"ae135f58762b1133667002538f8c353a1869db815aa686e2544b5243c2d2212f","rct":"15046b93bb181189f2917eed38173202fbbb9cdbfcf3d1bc3e432df999ae1b1c"},{"global_index":"1972531","public_key":"39e44fa88d684d71762c40eb64ac80ddc694b74a99ac445667bf433536c09c8f","rct":"66a42d0e8123768b392ad4a230759258d9156fab1aea00a19b041832326aca0a"},{"global_index":"3274424","public_key":"a89b91648645ba6f32e214ba5720f5387376e5a144e698d5d5d1ebac971de349","rct":"815a6b1da6fc6a3bd791c4342782381cf948ee822ac9da7149f1b3717e0266d2"}]}]}'
@ -155,5 +159,5 @@ monero_utils.OnceModuleReady(function(Module)
console.timeEnd("generate_key_image")
console.log("generate_key_image ret", ret_string)
})
})
}
tests()

@ -9,11 +9,18 @@ var private_key =
var nettype = mymonero.nettype_utils.network_type.MAINNET;
mymonero.monero_utils.OnceModuleReady(function(Module)
var monero_utils;
async function t1()
{
var decoded = mymonero.monero_utils.decode_address(
"49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr",
nettype,
);
console.log("decoded", decoded)
})
try {
var decoded = (await mymonero.monero_utils_promise).decode_address(
"49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr",
nettype,
);
console.log("decoded", decoded)
} catch (e) {
console.log(e)
}
}
t1()

@ -40,7 +40,7 @@ module.exports = function(wallaby) {
],
filesWithNoCoverageCalculated: [
"cryptonote_utils/MyMoneroCoreCpp.js",
"monero_utils/MyMoneroCoreCpp.js",
],
tests: ["./tests/**/*spec.js"],

Loading…
Cancel
Save