diff --git a/app/actions/AccountMixerActions.js b/app/actions/AccountMixerActions.js index 1363dd784c..1e8fc3dbac 100644 --- a/app/actions/AccountMixerActions.js +++ b/app/actions/AccountMixerActions.js @@ -1,4 +1,4 @@ -import { getAccountMixerService } from "wallet"; +import { getAccountMixerService, getDcrwalletGrpcKeyCert } from "wallet"; import Promise from "promise"; import * as sel from "selectors"; import * as wallet from "wallet"; @@ -17,12 +17,15 @@ export const getAccountMixerServiceAttempt = () => (dispatch, getState) => { const { daemon: { walletName } } = getState(); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); dispatch({ type: GETACCOUNTMIXERSERVICE_ATTEMPT }); return getAccountMixerService( sel.isTestNet(getState()), walletName, address, - port + port, + grpcCertAndKey, + grpcCertAndKey ) .then((accountMixerService) => dispatch({ accountMixerService, type: GETACCOUNTMIXERSERVICE_SUCCESS }) diff --git a/app/actions/ClientActions.js b/app/actions/ClientActions.js index 7c03081b35..40aada45d0 100644 --- a/app/actions/ClientActions.js +++ b/app/actions/ClientActions.js @@ -1,5 +1,6 @@ // @flow import * as wallet from "wallet"; +import { onAppReloadRequested, getDcrwalletGrpcKeyCert } from "wallet"; import * as sel from "selectors"; import eq from "lodash/fp/eq"; import { @@ -20,7 +21,6 @@ import { getAccountMixerServiceAttempt } from "./AccountMixerActions"; import { checkLnWallet } from "./LNActions"; import { push as pushHistory, goBack } from "connected-react-router"; import { getWalletCfg, getGlobalCfg } from "config"; -import { onAppReloadRequested } from "wallet"; import { clipboard } from "electron"; import { getStartupStats } from "./StatisticsActions"; import { getTokenAndInitialBatch } from "./GovernanceActions"; @@ -147,8 +147,9 @@ export const getWalletServiceAttempt = () => (dispatch, getState) => { daemon: { walletName } } = getState(); dispatch({ type: GETWALLETSERVICE_ATTEMPT }); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); wallet - .getWalletService(sel.isTestNet(getState()), walletName, address, port) + .getWalletService(sel.isTestNet(getState()), walletName, address, port, grpcCertAndKey, grpcCertAndKey) .then((walletService) => dispatch({ walletService, type: GETWALLETSERVICE_SUCCESS }) ) @@ -167,8 +168,9 @@ export const getTicketBuyerServiceAttempt = () => (dispatch, getState) => { daemon: { walletName } } = getState(); dispatch({ type: GETTICKETBUYERSERVICE_ATTEMPT }); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); wallet - .getTicketBuyerService(sel.isTestNet(getState()), walletName, address, port) + .getTicketBuyerService(sel.isTestNet(getState()), walletName, address, port, grpcCertAndKey, grpcCertAndKey) .then((ticketBuyerService) => { dispatch({ ticketBuyerService, type: GETTICKETBUYERSERVICE_SUCCESS }); }) @@ -495,8 +497,9 @@ export const getAgendaServiceAttempt = () => (dispatch, getState) => { daemon: { walletName } } = getState(); dispatch({ type: GETAGENDASERVICE_ATTEMPT }); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); wallet - .getAgendaService(sel.isTestNet(getState()), walletName, address, port) + .getAgendaService(sel.isTestNet(getState()), walletName, address, port, grpcCertAndKey, grpcCertAndKey) .then((agendaService) => { dispatch({ agendaService, type: GETAGENDASERVICE_SUCCESS }); setTimeout(() => { @@ -518,8 +521,9 @@ export const getVotingServiceAttempt = () => (dispatch, getState) => { daemon: { walletName } } = getState(); dispatch({ type: GETVOTINGSERVICE_ATTEMPT }); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); wallet - .getVotingService(sel.isTestNet(getState()), walletName, address, port) + .getVotingService(sel.isTestNet(getState()), walletName, address, port, grpcCertAndKey, grpcCertAndKey) .then((votingService) => dispatch({ votingService, type: GETVOTINGSERVICE_SUCCESS }) ) diff --git a/app/actions/VersionActions.js b/app/actions/VersionActions.js index 831cd04f0e..eb5c8fe7ed 100644 --- a/app/actions/VersionActions.js +++ b/app/actions/VersionActions.js @@ -1,6 +1,6 @@ // @flow import { loaderRequest, getWalletSeedService } from "./WalletLoaderActions"; -import { getVersionService, getVersionResponse } from "wallet"; +import { getVersionService, getVersionResponse, getDcrwalletGrpcKeyCert } from "wallet"; import { push as pushHistory } from "connected-react-router"; import { ipcRenderer } from "electron"; import { isTestNet } from "selectors"; @@ -19,12 +19,15 @@ export const getVersionServiceAttempt = () => (dispatch, getState) => const { daemon: { walletName } } = getState(); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); try { const versionService = await getVersionService( isTestNet(getState()), walletName, address, - port + port, + grpcCertAndKey, + grpcCertAndKey ); dispatch({ versionService, type: GETVERSIONSERVICE_SUCCESS }); await dispatch(getWalletRPCVersionAttempt(versionService)); diff --git a/app/actions/WalletLoaderActions.js b/app/actions/WalletLoaderActions.js index c459a44d37..daccf80d3d 100644 --- a/app/actions/WalletLoaderActions.js +++ b/app/actions/WalletLoaderActions.js @@ -5,7 +5,8 @@ import { openWallet, closeWallet, getStakePoolInfo, - rescanPoint + rescanPoint, + getDcrwalletGrpcKeyCert } from "wallet"; import * as wallet from "wallet"; import { rescanCancel, ticketBuyerCancel } from "./ControlActions"; @@ -47,11 +48,14 @@ export const loaderRequest = () => (dispatch, getState) => const { daemon: { walletName } } = getState(); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); const request = { isTestNet: isTestNet(getState()), walletName, address, - port + port, + cert: grpcCertAndKey, + key: grpcCertAndKey }; dispatch({ request, type: LOADER_ATTEMPT }); try { @@ -81,8 +85,9 @@ export const getWalletSeedService = () => (dispatch, getState) => { daemon: { walletName } } = getState(); dispatch({ type: GETWALLETSEEDSVC_ATTEMPT }); + const grpcCertAndKey = getDcrwalletGrpcKeyCert(); return wallet - .getSeedService(isTestNet(getState()), walletName, address, port) + .getSeedService(isTestNet(getState()), walletName, address, port, grpcCertAndKey, grpcCertAndKey) .then((seedService) => { dispatch({ seedService, type: GETWALLETSEEDSVC_SUCCESS }); }) diff --git a/app/main.development.js b/app/main.development.js index ef4db6e8bf..6e0f5feffa 100644 --- a/app/main.development.js +++ b/app/main.development.js @@ -41,7 +41,8 @@ import { getSelectedWallet, GetDcrlndPID, GetDcrlndCreds, - dropDCRDSocket + dropDCRDSocket, + getDcrwalletGrpcKeyCert } from "./main_dev/launch"; import { getAvailableWallets, @@ -479,6 +480,10 @@ ipcMain.on("get-dcrd-rpc-credentials", (event) => { event.returnValue = getDcrdRpcCredentials(); }); +ipcMain.on("get-dcrwallet-grpc-cert-key", (event) => { + event.returnValue = getDcrwalletGrpcKeyCert(); +}); + ipcMain.on("set-previous-wallet", (event, cfg) => { previousWallet = cfg; event.returnValue = true; diff --git a/app/main_dev/launch.js b/app/main_dev/launch.js index 4e25adfa3e..36722d3f83 100644 --- a/app/main_dev/launch.js +++ b/app/main_dev/launch.js @@ -43,9 +43,11 @@ let dcrdPID, dcrwPID, dcrlndPID; // windows-only stuff let dcrwPipeRx, dcrwPipeTx, dcrdPipeRx, dcrwTxStream; +// general data that needs to keep consistency while decrediton is running. let dcrwPort; let rpcuser, rpcpass, rpccert, rpchost, rpcport; let dcrlndCreds; +let dcrwalletGrpcKeyCert; let dcrdSocket, heightIsSynced, @@ -95,6 +97,16 @@ export const setSelectedWallet = (w) => { export const getSelectedWallet = () => selectedWallet; +export const getDcrwalletGrpcKeyCert = () => dcrwalletGrpcKeyCert; + +export const setDcrwalletGrpcKeyCert = (grpcKeyCert) => { + if (!Buffer.isBuffer(grpcKeyCert)) { + logger.log("error", "Error getting grpc key and cert from dcrwallet, " + + "grpc key and cert value: " + grpcKeyCert); + } + dcrwalletGrpcKeyCert = grpcKeyCert; +}; + export function closeDCRD() { if (dcrdPID === -1) { // process is not started by decrediton @@ -533,8 +545,10 @@ export const launchDCRWallet = ( : ""; let args = [confFile]; + // add needed dcrwallet flags args.push("--gaplimit=" + cfg.get("gaplimit")); - + args.push("--authtype=clientcert"); + args.push("--issueclientcert"); const dcrwExe = getExecutablePath("dcrwallet", argv.custombinpath); if (!fs.existsSync(dcrwExe)) { logger.log( @@ -565,6 +579,11 @@ export const launchDCRWallet = ( ); } } + if (mtype === "issuedclientcertificate") { + logger.log("info", "wallet grpc cert registered"); + // store dcrwallet grpc cert and key. + setDcrwalletGrpcKeyCert(payload); + } }); if (os.platform() == "win32") { diff --git a/app/middleware/grpc/client.js b/app/middleware/grpc/client.js index d620156011..4a313904fa 100644 --- a/app/middleware/grpc/client.js +++ b/app/middleware/grpc/client.js @@ -11,6 +11,8 @@ const getServiceClient = (clientClass) => ( walletPath, address, port, + grpcKey, + grpccert, cb ) => { const cert = getWalletCert(getWalletPath(isTestNet, walletPath)); @@ -20,19 +22,26 @@ const getServiceClient = (clientClass) => ( "Unable to load dcrwallet certificate. dcrwallet not running?" ); } - const creds = grpc.credentials.createSsl(cert); - const client = new clientClass(address + ":" + port, creds); - const deadline = new Date(); - const deadlineInSeconds = 30; - deadline.setSeconds(deadline.getSeconds() + deadlineInSeconds); - grpc.waitForClientReady(client, deadline, function (err) { - if (err) { - return cb(null, err); - } else { - return cb(client); - } - }); + try { + // dcrwallet sends the key and cert on the same payload after starting. + // So we can use the same value for both of them. + const creds = grpc.credentials.createSsl(cert, grpcKey, grpccert); + const client = new clientClass(address + ":" + port, creds); + + const deadline = new Date(); + const deadlineInSeconds = 30; + deadline.setSeconds(deadline.getSeconds() + deadlineInSeconds); + grpc.waitForClientReady(client, deadline, function (err) { + if (err) { + return cb(null, err); + } else { + return cb(client); + } + }); + } catch (err) { + return cb(null, err); + } }; export const getWalletService = getServiceClient(services.WalletServiceClient); diff --git a/app/wallet/daemon.js b/app/wallet/daemon.js index 07fd46b782..26b1ccb0f5 100644 --- a/app/wallet/daemon.js +++ b/app/wallet/daemon.js @@ -203,6 +203,8 @@ export const getAvailableWallets = log( logOptionNoResponseData() ); +export const getDcrwalletGrpcKeyCert = () => ipcRenderer.sendSync("get-dcrwallet-grpc-cert-key"); + export const reloadAllowedExternalRequests = log( () => Promise.resolve(ipcRenderer.sendSync("reload-allowed-external-request")), diff --git a/app/wallet/loader.js b/app/wallet/loader.js index d081cf9806..b77a83f6cb 100644 --- a/app/wallet/loader.js +++ b/app/wallet/loader.js @@ -16,9 +16,9 @@ import { } from "middleware/walletrpc/api_pb"; export const getLoader = log( - ({ isTestNet, walletName, address, port }) => + ({ isTestNet, walletName, address, port, cert, key }) => new Promise((resolve, reject) => - rpcLoader(isTestNet, walletName, address, port, (loader, error) => + rpcLoader(isTestNet, walletName, address, port, cert, key, (loader, error) => error ? reject(error) : resolve(loader) ) ), diff --git a/app/wallet/version.js b/app/wallet/version.js index ddce4d9583..33ac8a4647 100644 --- a/app/wallet/version.js +++ b/app/wallet/version.js @@ -4,9 +4,9 @@ const messages = require("../middleware/walletrpc/api_pb"); import { withLog as log } from "./index"; export const getVersionService = log( - (network, walletPath, address, port) => + (network, walletPath, address, port, grpckey, grpccert) => new Promise((resolve, reject) => - getService(network, walletPath, address, port, (versionService, error) => + getService(network, walletPath, address, port, grpckey, grpccert, (versionService, error) => error ? reject(error) : resolve(versionService) ) ),