mirror of https://github.com/embarklabs/embark.git
feat(@embark/whisper): Add Whisper client config
Add option in communication config to choose which Whisper client to use. Because Parity’s implementation of Whisper is not compatible with Whisper v6, and therefore web3.js in its current form, the following changes have been made: 1. remove any functionality associated with launching a Parity Whisper process. 2. Warn the user when the Parity Whisper client has been opted for in the communication config. 3. Return an error for API calls when Parity Whisper client has been opted for in the communication config. 4. Update Cockpit’s Communication module to show errors returned from API calls. 5. Update the messaging configuration documentation for the new communication client option.
This commit is contained in:
parent
446197baff
commit
bd4b110a78
|
@ -4,6 +4,7 @@ module.exports = {
|
|||
enabled: true,
|
||||
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
|
||||
available_providers: ["whisper"], // Array of available providers
|
||||
client: "geth"
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
|
@ -35,12 +36,12 @@ module.exports = {
|
|||
// "embark run custom_name"
|
||||
//custom_name: {
|
||||
//}
|
||||
// Use this section when you need a specific symmetric or private keys in whisper
|
||||
/*
|
||||
,keys: {
|
||||
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
|
||||
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
|
||||
}
|
||||
*/
|
||||
// Use this section when you need a specific symmetric or private keys in whisper
|
||||
/*
|
||||
,keys: {
|
||||
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
|
||||
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
|
||||
}
|
||||
*/
|
||||
//}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ module.exports = {
|
|||
enabled: true,
|
||||
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
|
||||
available_providers: ["whisper"], // Array of available providers
|
||||
client: "geth"
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
|
@ -36,11 +37,11 @@ module.exports = {
|
|||
// "embark run custom_name"
|
||||
//custom_name: {
|
||||
//}
|
||||
// Use this section when you need a specific symmetric or private keys in whisper
|
||||
/*
|
||||
,keys: {
|
||||
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
|
||||
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
|
||||
}
|
||||
*/
|
||||
// Use this section when you need a specific symmetric or private keys in whisper
|
||||
/*
|
||||
,keys: {
|
||||
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
|
||||
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import PropTypes from "prop-types";
|
||||
import React, {Component} from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
|
@ -13,6 +13,7 @@ import {
|
|||
ListGroup,
|
||||
ListGroupItem
|
||||
} from 'reactstrap';
|
||||
import Error from "./Error";
|
||||
|
||||
class Communication extends Component {
|
||||
constructor(props) {
|
||||
|
@ -61,6 +62,7 @@ class Communication extends Component {
|
|||
return (
|
||||
<Row className="justify-content-md-center">
|
||||
<Col xs="12" sm="9" lg="9">
|
||||
{this.props.error && this.props.error.error && <Error error={this.props.error.error} />}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<strong>Listen to channel</strong>
|
||||
|
@ -147,7 +149,8 @@ Communication.propTypes = {
|
|||
sendMessage: PropTypes.func,
|
||||
listenToMessages: PropTypes.func,
|
||||
subscriptions: PropTypes.array,
|
||||
channels: PropTypes.object
|
||||
channels: PropTypes.object,
|
||||
error: PropTypes.object
|
||||
};
|
||||
|
||||
export default Communication;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Alert } from 'reactstrap';
|
|||
import {messageSend, messageListen, versions} from "../actions";
|
||||
import Communication from "../components/Communication";
|
||||
import PageHead from "../components/PageHead";
|
||||
import {getMessages, getMessageChannels, isOldWeb3, isWeb3Enabled} from "../reducers/selectors";
|
||||
import { getMessages, getMessageChannels, isOldWeb3, isWeb3Enabled, getMessagesError } from "../reducers/selectors";
|
||||
|
||||
class CommunicationContainer extends Component {
|
||||
componentDidMount() {
|
||||
|
@ -37,9 +37,11 @@ class CommunicationContainer extends Component {
|
|||
<React.Fragment>
|
||||
<PageHead title="Communication" description="Interact with the decentralised communication protocols configured for Embark (ie Whisper)" />
|
||||
<Communication listenToMessages={(channel) => this.listenToChannel(channel)}
|
||||
sendMessage={(channel, message) => this.sendMessage(channel, message)}
|
||||
channels={this.props.messages}
|
||||
subscriptions={this.props.messageChannels}/>
|
||||
sendMessage={(channel, message) => this.sendMessage(channel, message)}
|
||||
channels={this.props.messages}
|
||||
subscriptions={this.props.messageChannels}
|
||||
error={this.props.error}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -60,7 +62,8 @@ CommunicationContainer.propTypes = {
|
|||
isWeb3Enabled: PropTypes.bool,
|
||||
messages: PropTypes.object,
|
||||
messageChannels: PropTypes.array,
|
||||
fetchVersions: PropTypes.func
|
||||
fetchVersions: PropTypes.func,
|
||||
error: PropTypes.object
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
@ -68,7 +71,8 @@ function mapStateToProps(state) {
|
|||
messages: getMessages(state),
|
||||
messageChannels: getMessageChannels(state),
|
||||
isOldWeb3: isOldWeb3(state),
|
||||
isWeb3Enabled: isWeb3Enabled(state)
|
||||
isWeb3Enabled: isWeb3Enabled(state),
|
||||
error: getMessagesError(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,12 @@ export function getMessages(state) {
|
|||
return messages;
|
||||
}
|
||||
|
||||
export function getMessagesError(state) {
|
||||
return {
|
||||
error: state.errorEntities.messages
|
||||
};
|
||||
}
|
||||
|
||||
export function getFiddleDeploy(state) {
|
||||
return {
|
||||
data: last(state.entities.fiddleDeploys),
|
||||
|
|
|
@ -571,7 +571,10 @@ export function *listenToMessages(action) {
|
|||
const channel = yield call(createChannel, socket);
|
||||
while (true) {
|
||||
const message = yield take(channel);
|
||||
yield put(actions.messageListen.success([{channel: action.messageChannels[0], message: message.data, time: message.time}]));
|
||||
if (message.error) {
|
||||
return yield put(actions.messageListen.failure(message.error));
|
||||
}
|
||||
yield put(actions.messageListen.success([{ channel: action.messageChannels[0], message: message.data, time: message.time }]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -567,6 +567,7 @@ export class Config {
|
|||
enabled: true,
|
||||
provider: "whisper",
|
||||
available_providers: ["whisper"],
|
||||
client: "geth",
|
||||
connection: {
|
||||
host: defaultHost,
|
||||
port: 8547,
|
||||
|
|
|
@ -18,7 +18,7 @@ class Whisper {
|
|||
this.webSocketsChannels = {};
|
||||
this.modulesPath = dappPath(embark.config.embarkConfig.generationDir, constants.dappArtifacts.symlinkDir);
|
||||
|
||||
if (!this.communicationConfig.enabled || this.blockchainConfig.client !== constants.blockchain.clients.geth) {
|
||||
if (!this.communicationConfig.enabled || this.communicationConfig.client !== constants.blockchain.clients.geth) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import EmbarkJS from "embarkjs";
|
||||
import EmbarkJSWhisper from "embarkjs-whisper";
|
||||
|
||||
class API {
|
||||
export const PARITY_WHISPER_ERROR = "Parity's implementation of Whisper is not compatible with Whisper v6 (and therefore web3.js). Try changing the communication config to use '{client: \"geth\"}' instead.";
|
||||
|
||||
export class Api {
|
||||
|
||||
constructor(embark) {
|
||||
this.embark = embark;
|
||||
|
@ -25,13 +27,8 @@ class API {
|
|||
this.embark.registerAPICall(
|
||||
"post",
|
||||
"/embark-api/communication/sendMessage",
|
||||
(req, res) => {
|
||||
EmbarkJS.Messages.sendMessage({ topic: req.body.topic, data: req.body.message }, (err, result) => {
|
||||
if (err) {
|
||||
return res.status(500).send({ error: err });
|
||||
}
|
||||
res.send(result);
|
||||
});
|
||||
(_req, res) => {
|
||||
res.status(500).send({ error: PARITY_WHISPER_ERROR });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -39,13 +36,8 @@ class API {
|
|||
this.embark.registerAPICall(
|
||||
"ws",
|
||||
"/embark-api/communication/listenTo/:topic",
|
||||
(ws, req) => {
|
||||
EmbarkJS.Messages.listenTo({ topic: req.params.topic }).subscribe(data => {
|
||||
ws.send(JSON.stringify(data));
|
||||
});
|
||||
(ws, _req) => {
|
||||
ws.send(JSON.stringify({ error: PARITY_WHISPER_ERROR }));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = API;
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
const WebSocket = require("ws");
|
||||
const http = require("http");
|
||||
|
||||
const LIVENESS_CHECK = JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'web3_clientVersion',
|
||||
params:[],
|
||||
id:42
|
||||
});
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
const parseAndRespond = (data, cb) => {
|
||||
let resp;
|
||||
try {
|
||||
resp = JSON.parse(data);
|
||||
if (resp.error) {
|
||||
return cb(resp.error);
|
||||
}
|
||||
} catch (e) {
|
||||
return cb("Version data is not valid JSON");
|
||||
}
|
||||
if (!resp || !resp.result) {
|
||||
return cb("No version returned");
|
||||
}
|
||||
const result = resp.result.replace("//", "/");
|
||||
const [_, version, __] = result.split("/");
|
||||
cb(null, version);
|
||||
};
|
||||
|
||||
const rpc = (host, port, cb) => {
|
||||
const options = {
|
||||
hostname: host, // TODO(andremedeiros): get from config
|
||||
port, // TODO(andremedeiros): get from config
|
||||
method: "POST",
|
||||
timeout: 1000,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": Buffer.byteLength(LIVENESS_CHECK)
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = "";
|
||||
res.on("data", (chunk) => { data += chunk; });
|
||||
res.on("end", () => parseAndRespond(data, cb));
|
||||
});
|
||||
req.on("error", (e) => cb(e));
|
||||
req.write(LIVENESS_CHECK);
|
||||
req.end();
|
||||
};
|
||||
|
||||
const ws = (host, port, cb) => {
|
||||
const conn = new WebSocket("ws://" + host + ":" + port);
|
||||
conn.on("message", (data) => {
|
||||
parseAndRespond(data, cb);
|
||||
conn.close();
|
||||
});
|
||||
conn.on("open", () => conn.send(LIVENESS_CHECK));
|
||||
conn.on("error", (e) => cb(e));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
ws,
|
||||
rpc
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
import { __ } from "embark-i18n";
|
||||
import { dappPath, canonicalHost, defaultHost } from "embark-utils";
|
||||
const constants = require("embark-core/constants");
|
||||
const API = require("./api.js");
|
||||
import { ws, rpc } from "./check.js";
|
||||
const { Api, PARITY_WHISPER_ERROR } = require("./api.js");
|
||||
|
||||
class Whisper {
|
||||
constructor(embark, _options) {
|
||||
|
@ -16,71 +15,33 @@ class Whisper {
|
|||
this.webSocketsChannels = {};
|
||||
this.modulesPath = dappPath(embark.config.embarkConfig.generationDir, constants.dappArtifacts.symlinkDir);
|
||||
|
||||
if (!this.communicationConfig.enabled || this.blockchainConfig.client !== constants.blockchain.clients.parity) {
|
||||
if (!this.communicationConfig.enabled || this.communicationConfig.client !== constants.blockchain.clients.parity) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.api = new API(embark);
|
||||
this.whisperNodes = {};
|
||||
|
||||
this.events.request("embarkjs:plugin:register", "messages", "whisper", "embarkjs-whisper");
|
||||
this.events.request("embarkjs:console:register", "messages", "whisper", "embarkjs-whisper");
|
||||
|
||||
this.events.request("communication:node:register", "whisper", (readyCb) => {
|
||||
this.events.request("processes:register", "communication", {
|
||||
launchFn: (cb) => {
|
||||
this.startWhisperNode(cb);
|
||||
},
|
||||
stopFn: (cb) => {
|
||||
this.stopWhisperNode(cb);
|
||||
}
|
||||
});
|
||||
|
||||
this.events.request("processes:launch", "communication", (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error launching whisper process: ${err.message || err}`);
|
||||
}
|
||||
readyCb();
|
||||
});
|
||||
this.logger.warn(PARITY_WHISPER_ERROR);
|
||||
readyCb();
|
||||
|
||||
this.registerServiceCheck();
|
||||
});
|
||||
|
||||
this.events.on("communication:started", () => {
|
||||
this.api = new API(embark);
|
||||
this.api = new Api(embark);
|
||||
this.api.registerAPICalls();
|
||||
this.connectEmbarkJSProvider();
|
||||
});
|
||||
}
|
||||
|
||||
_getNodeState(err, version, cb) {
|
||||
if (err) return cb({ name: "Whisper node not found", status: "off" });
|
||||
|
||||
let nodeName = "Parity";
|
||||
let versionNumber = version.split("-")[0];
|
||||
let name = nodeName + " " + versionNumber + " (Whisper)";
|
||||
return cb({ name, status: "on" });
|
||||
}
|
||||
|
||||
registerServiceCheck() {
|
||||
this.events.request("services:register", "Whisper", (cb) => {
|
||||
const { host, port, type } = this.communicationConfig.connection;
|
||||
if (type === "ws") {
|
||||
return ws(host, port, (err, version) => this._getNodeState(err, version, cb));
|
||||
}
|
||||
rpc(host, port, (err, version) => this._getNodeState(err, version, cb));
|
||||
|
||||
}, 5000, "off");
|
||||
}
|
||||
|
||||
startWhisperNode(callback) {
|
||||
this.logger.info(`Whisper node has already been started with the Parity blockchain.`);
|
||||
callback();
|
||||
}
|
||||
|
||||
stopWhisperNode(cb) {
|
||||
this.logger.warn(`Cannot stop Whisper process as it has been started with the Parity blockchain.`);
|
||||
cb();
|
||||
cb({ name: "Whisper Parity", status: "off" });
|
||||
}, 1000 * 60 * 30, "off");
|
||||
}
|
||||
|
||||
// esline-disable-next-line complexity
|
||||
|
|
|
@ -28,4 +28,5 @@ Option | Type: `default` | Value
|
|||
`enabled` | boolean: `true/false` | To enable or completely disable communication support
|
||||
`provider` | string: `whisper` | Desired provider to automatically connect to in the dapp.
|
||||
`available_providers` | array: `["whisper"]` | List of communication platforms to be supported in the dapp. This will affect what's available with the EmbarkJS library in the dapp so if you don't need Whisper for example, removing it from this will considerably reduce the file size of the generated JS code.
|
||||
`client` | string: `geth/parity` | Desired Whisper client for Embark to start. **NOTE:** Parity's implementation of Whisper does not currently adhere to Whisper v6 standards and thus is not supported by `web3.js`.
|
||||
|
||||
|
|
Loading…
Reference in New Issue