updated monero_utils to support calls which are safe over IPC to an electron remote from renderer proc's require

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

@ -1,3 +1,2 @@
node_modules/*
local_modules/cryptonote_utils/**

@ -497,6 +497,9 @@ module.exports = function(options)
{
const instance = new MyMoneroCoreBridge(thisModule);
resolve(instance);
}).catch(function(e) {
console.error("Error loading MyMoneroCoreCpp:", e);
reject(e);
});
});
};

@ -0,0 +1,75 @@
// 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";
//
// NOTE: You will never need to require this file directly. See / use monero_utils.js.
//
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"
}
var coreBridge_instance = null;
const local_fns = {};
const fn_names = require('./__bridged_fns_spec').bridgedFn_names;
for (const i in fn_names) {
const name = fn_names[i]
local_fns[name] = function()
{
if (coreBridge_instance === null) {
throw "Expected coreBridge_instance to have been loaded by the time " + name + " was called"
}
//
// Note how we're not getting ret.err_msg and using it to 'throw'
// here… we're just passing it back over from remote as JSON
//
return coreBridge_instance[name].apply(coreBridge_instance, 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
}
}
//
// Cannot export a promise, though, because this must be safe for IPC to electron 'remote'...
local_fns.isReady = false;
//
module.exports = local_fns;
//
//
const coreBridgeLoading_promise = require("./MyMoneroCoreBridge")({});
coreBridgeLoading_promise.then(function(this__coreBridge_instance)
{
coreBridge_instance = this__coreBridge_instance;
//
local_fns.isReady = true;
//
});
coreBridgeLoading_promise.catch(function(e)
{
console.log("Error: ", e);
// this may be insufficient… being able to throw would be nice
});

@ -0,0 +1,47 @@
// 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.
//
// This file is here merely to share configuration
//
exports.bridgedFn_names =
[
"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"
];

@ -28,8 +28,6 @@
//
"use strict";
//
const async = require("async");
//
const monero_config = require("./monero_config");
const monero_utils_promise = require('./monero_utils')
const monero_amount_format_utils = require("./monero_amount_format_utils");
@ -259,6 +257,7 @@ function SendFunds(
[targetDescription], // requires a list of descriptions - but SendFunds was
// not written with multiple target support as MyMonero does not yet support it
nettype,
monero_utils,
function(err, moneroReady_targetDescriptions) {
if (err) {
__trampolineFor_err_withErr(err);
@ -737,69 +736,69 @@ function new_moneroReadyTargetDescriptions_fromTargetDescriptions(
monero_openalias_utils,
targetDescriptions,
nettype,
monero_utils,
fn, // fn: (err, moneroReady_targetDescriptions) -> Void
// TODO: remove this fn - this is a sync method now
) {
// parse & normalize the target descriptions by mapping them to currency (Monero)-ready addresses & amounts
// some pure function declarations for the map we'll do on targetDescriptions
async.mapSeries(
targetDescriptions,
function(targetDescription, cb) {
if (!targetDescription.address && !targetDescription.amount) {
// PSNote: is this check rigorous enough?
const errStr =
"Please supply a target address and a target amount.";
const err = new Error(errStr);
cb(err);
return;
}
const targetDescription_address = targetDescription.address;
const targetDescription_amount = "" + targetDescription.amount; // we are converting it to a string here because parseMoney expects a string
// now verify/parse address and amount
if (
monero_openalias_utils.DoesStringContainPeriodChar_excludingAsXMRAddress_qualifyingAsPossibleOAAddress(
targetDescription_address,
) == true
) {
throw "You must resolve this OA address to a Monero address before calling SendFunds";
}
// otherwise this should be a normal, single Monero public address
try {
monero_utils.decode_address(targetDescription_address, 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;
}
// amount:
var moneroReady_amountToSend; // possibly need this ; here for the JS parser
try {
moneroReady_amountToSend = monero_amount_format_utils.parseMoney(
targetDescription_amount,
);
} catch (e) {
const errStr =
"Couldn't parse amount " +
targetDescription_amount +
": " +
e;
const err = new Error(errStr);
cb(err);
return;
}
cb(null, {
address: targetDescription_address,
amount: moneroReady_amountToSend,
});
},
function(err, moneroReady_targetDescriptions) {
fn(err, moneroReady_targetDescriptions);
},
);
//
const moneroReady_targetDescriptions = [];
for (var i = 0 ; i < targetDescriptions.length ; i++) {
const targetDescription = targetDescriptions[i];
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);
fn(err);
return;
}
const targetDescription_address = targetDescription.address;
const targetDescription_amount = "" + targetDescription.amount; // we are converting it to a string here because parseMoney expects a string
// now verify/parse address and amount
if (
monero_openalias_utils.DoesStringContainPeriodChar_excludingAsXMRAddress_qualifyingAsPossibleOAAddress(
targetDescription_address,
) == true
) {
throw "You must resolve this OA address to a Monero address before calling SendFunds";
}
// otherwise this should be a normal, single Monero public address
try {
monero_utils.decode_address(targetDescription_address, nettype); // verify that the address is valid
} catch (e) {
const errStr =
"Couldn't decode address " +
targetDescription_address +
": " +
e;
const err = new Error(errStr);
fn(err);
return;
}
// amount:
var moneroReady_amountToSend; // possibly need this ; here for the JS parser
try {
moneroReady_amountToSend = monero_amount_format_utils.parseMoney(
targetDescription_amount,
);
} catch (e) {
const errStr =
"Couldn't parse amount " +
targetDescription_amount +
": " +
e;
const err = new Error(errStr);
fn(err);
return;
}
moneroReady_targetDescriptions.push({
address: targetDescription_address,
amount: moneroReady_amountToSend,
});
}
fn(null, moneroReady_targetDescriptions);
}
function __randomIndex(list) {
return Math.floor(Math.random() * list.length);

@ -28,11 +28,88 @@
//
"use strict";
//
const promise = require('./MyMoneroCoreBridge')({}) // this returns a promise
promise.catch(function(e)
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 wants_electronRemote = (ENVIRONMENT_IS_NODE&&ENVIRONMENT_IS_WEB)/*this may become insufficient*/
|| (typeof window !== 'undefined' && window.IsElectronRendererProcess == true);
//
const moneroUtils_promise = new Promise(function(resolve, reject)
{
console.log("Error: ", e);
// this may be insufficient… being able to throw would be nice
function _didLoad(coreBridge_instance)
{
if (coreBridge_instance == null) {
throw "Unable to make coreBridge_instance"
}
const local_fns = {};
const fn_names = require('./__bridged_fns_spec').bridgedFn_names;
for (const i in fn_names) {
const name = fn_names[i]
local_fns[name] = function()
{
const retVal = coreBridge_instance[name].apply(coreBridge_instance, arguments); // called on the cached value
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;
}
}
resolve(local_fns);
}
if (wants_electronRemote) {
// 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.
const remoteModule = require('electron').remote.require(
"../mymonero_core_js/monero_utils/__IPCSafe_remote_monero_utils"
);
// Oftentimes this will be ready right away.. somehow.. but just in case.. the readiness
// state promise behavior should be preserved by the following codepath...
var _try;
function __retryAfter(attemptN)
{
console.warn("Checking remote module readiness again after a few ms...")
setTimeout(function()
{
_try(attemptN + 1)
}, 10)
}
_try = function(attemptN)
{
if (attemptN > 10000) {
throw "Expected remote module to be ready"
}
if (remoteModule.isReady) {
_didLoad(remoteModule);
} else {
__retryAfter(attemptN)
}
}
_try(0)
} else {
const coreBridgeLoading_promise = require('./MyMoneroCoreBridge')({}); // this returns a promise
coreBridgeLoading_promise.catch(function(e)
{
console.error("Error: ", e);
// this may be insufficient… being able to throw would be nice
reject(e);
});
coreBridgeLoading_promise.then(_didLoad);
}
});
//
module.exports = promise
//
// Since we actually are constructing bridge functions we technically have the export ready
// synchronously but that would lose the ability to wait until the core bridge is actually ready.
//
// TODO: in future, possibly return function which takes options instead to support better env.
//
module.exports = moneroUtils_promise;
Loading…
Cancel
Save