├── .gitignore
├── LICENSE
├── README.md
├── app
├── index.js
├── package.json
├── swarm.js
└── yarn.lock
├── assets
├── icon.icns
└── icon.ico
├── config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── polyfills.js
├── webpack.config.dev.js
├── webpack.config.prod.js
└── webpackDevServer.config.js
├── docs
└── architecture.png
├── electron-builder.json
├── package.json
├── prettier.config.js
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── scripts
├── build.js
├── build_binaries.sh
├── start.js
└── test.js
├── src
├── Onyx.js
├── colors.js
├── components
│ ├── AddContactModal.js
│ ├── App.js
│ ├── Avatar.js
│ ├── CertSelectionModal.js
│ ├── ColoredLine.js
│ ├── Contact.js
│ ├── Conversation.js
│ ├── CreateChannelModal.js
│ ├── EditProfile.js
│ ├── FileSelector.js
│ ├── Form
│ │ ├── Button.js
│ │ └── TextInput.js
│ ├── Icon.js
│ ├── LeftNav
│ │ ├── ContactListLabel.js
│ │ ├── ConversationTitle.js
│ │ ├── Profile.js
│ │ └── SectionTitle.js
│ ├── Loader.js
│ ├── MainframeBar.js
│ ├── Modal.js
│ ├── NoStakeIcon.js
│ ├── NodeConnectionView.js
│ ├── Text.js
│ ├── UserProfile.js
│ ├── UserProfileModal.js
│ └── icons
│ │ ├── arrow-right.svg
│ │ ├── camera_icon.svg
│ │ ├── checkmark.svg
│ │ ├── circled-cross.svg
│ │ ├── download.svg
│ │ ├── file-dark.svg
│ │ ├── file-red.svg
│ │ ├── file.svg
│ │ ├── flash-blue.svg
│ │ ├── flash-gray.svg
│ │ ├── flash-red.svg
│ │ ├── generic-file.svg
│ │ ├── mainframe-icon.svg
│ │ ├── mainframe-logo.svg
│ │ ├── mask-blue.svg
│ │ ├── mask-gray.svg
│ │ ├── mask-red.svg
│ │ ├── pdf.svg
│ │ ├── plus.svg
│ │ ├── red-close.svg
│ │ ├── rolling.svg
│ │ └── see-qr.svg
├── constants.js
├── data
│ ├── Apollo.js
│ ├── Electron.js
│ ├── Navigation.js
│ └── Store.js
├── fonts
│ ├── Muli-Bold.woff2
│ ├── Muli-Regular.woff2
│ ├── Muli-SemiBold.woff2
│ ├── Poppins-Medium.woff2
│ ├── Poppins-Regular.woff2
│ └── Poppins-SemiBold.woff2
├── graphql
│ ├── fragments.js
│ └── mutations.js
├── index.css
├── index.js
└── styles.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /dist
3 | /tmp
4 | /bin
5 | /app/build
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 ThusFresh Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # onyx
2 |
3 | Decentralized messaging application using PSS.
4 |
5 | ## Introduction
6 |
7 | Onyx is a taster for our next phase of development on a fully-decentralized & incentivized network. It relies on a secure messaging protocol in the Ethereum core called [PSS](https://github.com/ethersphere/go-ethereum/tree/swarm-network-rewrite/swarm/pss).
8 |
9 | ## Installation
10 |
11 | To install Onyx, download and install the latest release binaries for your platform from our [releases page](https://github.com/MainframeHQ/onyx/releases).
12 |
13 | When you first install Onyx, you will be required to stake a Mainframe token (MFT) in order to connect and communicate with other nodes. Onyx guides you through this process. You will first need to create a wallet and make sure it has at least 1 MFT and a little ETH to submit the transaction with. This can be done on [MyCrypto](https://mycrypto.com). You will be required to perform two transactions on the ethereum network.
14 |
15 | ### Step 1 - Approve deposit of 1 MFT
16 |
17 | Once you have the wallet ready, open the Onyx application and click the button entitled "Step 1 - Approve deposit of 1 MFT." This will open MyCrypto with the required transaction data pre-loaded. Before you submit the approval transaction, make sure you set the gas price high enough for the transaction to go through in a timely manner. This depends on network congestion. [ETH Gas Station](https://ethgasstation.info) is a good place to go for current average gas prices.
18 |
19 |
20 | ### Step 2 - Deposit 1 MFT and whitelist node
21 |
22 | After submitting the transaction, wait for it to be processed. Then click "Step 2 - Deposit 1 MFT and whitelist node." NOTE: do not alter the auto-generated address in the grey box. This will submit another transaction to MyCrypto, this time to perform the actual deposit so that your node can be allowed to use the Mainframe network.
23 |
24 | ### Step 3 - Restart local node
25 |
26 | After the transaction is complete, click on "Restart local node." You should now be able to use Onyx and connect with other Onyx users.
27 |
28 | ## Known issues
29 |
30 | This application is an alpha product and is currently suitable for testing purposes only. As such, there is no guarantee of:
31 |
32 | * **Security**: Our codebase is not fully tested. We authenticate both the client app and the mailboxing service and use TLS between the two, but any intruder who succeeded in accessing a remotely-installed service could read your messages, as they are stored in plaintext. Messages are transmitted via [PSS](https://github.com/ethersphere/go-ethereum/tree/pss/swarm/pss), which is intended to be highly secure but is still beta software.
33 | * **Reliability**: PSS does not provide deliverability guarantees. When remotely installed, however, the [onyx-server](https://github.com/MainframeHQ/onyx-server) is designed to store messages sent to you while you are offline. As long as PSS delivers them successfully to your mailboxing service, they should be waiting for you when you open your desktop or mobile app again. If you are running in the default mode, which runs the mailbox service only locally, any messages sent to you while your app is not running will be lost.
34 | * **Performance**: We have not sufficiently tested this version for large-scale use. All messages are stored in a global state file that gets updated with each new message that is received. We anticipate that this will not scale well. The message store was created quickly for the alpha, and will require a more robust implementation in our next phase of development.
35 |
36 | PSS & Swarm are currently in rapid development and as such, the build of swarm that we're currently using (to have a working PSS implementation) has the following known limitations from what is expected in the next release of swarm:
37 |
38 | * **Network Formation**: The network discovery protocol is not currently usable which means that the nodes in the network cannot optimise their routing tables to ensure good routing of messages. In order to ensure routing despite this, all "client" nodes are configured to connect to the network through the same permanent bootnode.
39 | * **File Storage**: The file storage functionality is not currently usable which means that files uploaded to swarm would not be fetchable later. In order to retain the file upload feature, we are running a separate swarm gateway on https://onyx-storage.mainframe.com using a different build of swarm with usable file storage.
40 |
41 | ## Get in touch!
42 |
43 | Although this release is not officially supported, we really want to hear your feedback. If you become aware of a bug or have a great idea about a feature that would make Onyx more awesome, please submit an issue on our [issues page](https://github.com/MainframeHQ/onyx/issues).
44 |
45 | ## Architecture
46 |
47 | Onyx is a privacy-focused messaging application that combines the desired features of today's best messaging tools while also maintaining the highest level of security and user sovereignty. The product consists of front-end client apps (mobile and desktop) that connect to a p2p networking node. This node can be run on the user's desktop (inside the Onyx electron wrapper) or deployed to the cloud.
48 |
49 | 
50 |
51 | An Onyx node consists of a p2p node with a messaging layer and various services necessary for storing messages and managing contacts. It makes use of the [Swarm](https://github.com/ethersphere/swarm) distributed storage platform and [PSS](https://github.com/ethersphere/go-ethereum/tree/pss/swarm/pss) secure messaging protocol for message delivery and file storage. Later development milestones will include more rich messaging features, and incentivization for reliable message delivery.
52 |
53 | PSS is a connectionless communication protocol that provides dark routing capabilities in addition to conventional cryptography. A configurable level of per-message routing information allows senders to choose how specific they wish to be about whom their message is addressed to. By omitting or partially omitting the recipient’s address, messages are delivered to all matching addresses, thus increasing the difficulty of identifying both sender and receiver amidst numerous duplicate messages, or of targeting specific nodes for attack or disruption. These features enable extremely secure communications. Given networks of sufficient size, dark routing makes it virtually impossible to detect messaging activity between specific nodes. The only reliable means of disrupting this communication is to disable general Internet access.
54 |
55 | ## Development
56 |
57 | After you pull this repository, to install all the dependencies run
58 |
59 | ```
60 | yarn
61 | ```
62 |
63 | The app requires an [onyx-server](https://github.com/MainframeHQ/onyx-server) instance to connect to - you will be prompted when you state the app. This can be run separately, following the instructions in that repository, or can be run locally inside the electron process. To run locally, binaries for geth and swarm are required, and can be built from a known good version for the local architecture using the yarn task:
64 |
65 | ```
66 | yarn build:binaries
67 | ```
68 |
69 | Run
70 |
71 | ```
72 | yarn start
73 | ```
74 |
75 | to start the development server for the frontend. It will serve it on `localhost:3000`.
76 |
77 | Once ready, you can start the electron app with
78 |
79 | ```
80 | yarn electron
81 | ```
82 |
83 | ### Project structure
84 |
85 | * `app`: electron app
86 | * `assets`: build assets (app icons)
87 | * `dist`: app builds
88 | * `public`: assets that will be added to the `build` folder
89 | * `src`: source code
90 |
91 | ### Releases
92 |
93 | To build a release for the local architecture, run
94 |
95 | ```
96 | yarn build:binaries
97 | yarn dist
98 | ```
99 |
100 | To cross-compile for MacOS, Windows, Linux, run
101 |
102 | ```
103 | yarn build:binaries:mwl
104 | yarn dist -mwl
105 | ```
106 |
107 | ## Contributing
108 |
109 | Thanks for your interest in our project! Feel free to examine our list of potential enhancements on our [issues page](https://github.com/MainframeHQ/onyx/issues) and help us implement them. Feel free to submit an feature request or bug report yourself as well.
110 |
111 | ## Acknowledgments
112 |
113 | Mainframe wishes to thank the Swarm core team, including Viktor Trón, Louis Holbrook, and Lewis Marshall, as well as the JAAK team, who helped us test the group messaging features for our presentation at Devcon3 on November 4, 2017 in Cancun, Mexico.
114 |
115 | ## License
116 |
117 | [MIT](LICENSE)
118 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow, dialog, ipcMain, Menu, shell } = require('electron')
2 | const fetch = require('node-fetch')
3 | const { appReady, is } = require('electron-util')
4 | const Store = require('electron-store')
5 | const getPort = require('get-port')
6 | const createOnyxServer = require('onyx-server').default
7 | const StaticServer = require('static-server')
8 | const path = require('path')
9 | const querystring = require('querystring')
10 |
11 | const { config, version } = require(path.join(__dirname, 'package.json'))
12 | const swarm = require('./swarm')
13 |
14 | const VERSION_CHECK_URL = 'https://mainframe.com/onyx-version.json'
15 |
16 | const USE_TESTNET = process.env.USE_TESTNET || false
17 |
18 | const SWARM_WS_URL =
19 | process.env.SWARM_WS_URL ||
20 | (config && config.swarmWsUrl) ||
21 | 'ws://localhost:8546'
22 | const SWARM_HTTP_URL =
23 | process.env.SWARM_HTTP_URL ||
24 | (config && config.swarmHttpUrl) ||
25 | 'https://onyx-staking-storage.mainframe.com'
26 |
27 | const checkUpdates = () => {
28 | fetch(VERSION_CHECK_URL)
29 | .then(res => {
30 | if (res.ok) {
31 | return res.json()
32 | } else {
33 | throw new Error('Error loading version')
34 | }
35 | })
36 | .then(data => {
37 | if (data.versions.includes(version)) {
38 | dialog.showMessageBox(
39 | mainWindow,
40 | {
41 | type: 'warning',
42 | title: data.title || 'Update Available',
43 | message:
44 | data.description || 'A new version of Onyx has been released!',
45 | cancelId: 0,
46 | buttons: ['Cancel', data.button || 'Download'],
47 | },
48 | async index => {
49 | if (index === 1) {
50 | shell.openExternal(
51 | data.url || 'https://github.com/MainframeHQ/onyx/releases',
52 | )
53 | }
54 | },
55 | )
56 | }
57 | })
58 | .catch(() => {
59 | console.warn('Error fetching updates data')
60 | })
61 | }
62 |
63 | let appServer, mainWindow
64 |
65 | const shouldQuit = app.makeSingleInstance(() => {
66 | if (mainWindow) {
67 | if (mainWindow.isMinimized()) mainWindow.restore()
68 | mainWindow.focus()
69 | }
70 | })
71 |
72 | if (shouldQuit) {
73 | app.quit()
74 | }
75 |
76 | const store = new Store({ name: is.development ? 'onyx-dev' : 'onyx' })
77 |
78 | const menu = Menu.buildFromTemplate([
79 | {
80 | label: is.macos ? app.getName() : 'File',
81 | submenu: [
82 | {
83 | label: 'v' + app.getVersion(),
84 | enabled: false,
85 | },
86 | {
87 | type: 'separator',
88 | },
89 | {
90 | label: 'Reset',
91 | click: (item, focusedWindow) => {
92 | dialog.showMessageBox(
93 | focusedWindow,
94 | {
95 | type: 'warning',
96 | message:
97 | 'Resetting the application will clear ALL data and your local Swarm identity. Are you sure you want to continue?',
98 | cancelId: 0,
99 | buttons: ['Cancel', 'Confirm'],
100 | },
101 | async index => {
102 | if (index === 1) {
103 | store.delete('wsUrl')
104 | store.delete('certs')
105 | await swarm.reset()
106 | start()
107 | }
108 | },
109 | )
110 | },
111 | },
112 | {
113 | label: 'Quit',
114 | accelerator: 'CmdOrCtrl+Q',
115 | click: () => {
116 | app.quit()
117 | },
118 | },
119 | ],
120 | },
121 | {
122 | label: 'Edit',
123 | submenu: [
124 | { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
125 | { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
126 | { type: 'separator' },
127 | { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
128 | { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
129 | { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
130 | {
131 | label: 'Select All',
132 | accelerator: 'CmdOrCtrl+A',
133 | selector: 'selectAll:',
134 | },
135 | ],
136 | },
137 | {
138 | label: 'View',
139 | submenu: [
140 | {
141 | label: 'Reload',
142 | accelerator: 'CmdOrCtrl+R',
143 | click: (item, focusedWindow) => {
144 | if (focusedWindow) focusedWindow.reload()
145 | },
146 | },
147 | {
148 | label: 'Toggle Developer Tools',
149 | accelerator: is.macos ? 'Alt+Command+I' : 'Ctrl+Shift+I',
150 | click: (item, focusedWindow) => {
151 | if (focusedWindow) focusedWindow.toggleDevTools()
152 | },
153 | },
154 | { type: 'separator' },
155 | { role: 'togglefullscreen' },
156 | { role: 'resetzoom' },
157 | { role: 'zoomin' },
158 | { role: 'zoomout' },
159 | ],
160 | },
161 | ])
162 |
163 | const createMainWindow = async url => {
164 | await appReady
165 |
166 | Menu.setApplicationMenu(menu)
167 |
168 | mainWindow = new BrowserWindow({ width: 800, height: 600, show: false })
169 | mainWindow.loadURL(url)
170 |
171 | mainWindow.on('ready-to-show', () => {
172 | mainWindow.show()
173 | checkUpdates()
174 | })
175 | mainWindow.on('closed', () => {
176 | swarm.stop()
177 | app.quit()
178 | mainWindow = null
179 | })
180 | }
181 |
182 | const startAppServer = async () => {
183 | const appPort = await getPort()
184 | const appServer = new StaticServer({
185 | rootPath: path.join(__dirname, 'build'),
186 | port: appPort,
187 | })
188 |
189 | return new Promise(resolve => {
190 | appServer.start(() => {
191 | app.on('quit', () => {
192 | appServer.stop()
193 | })
194 | resolve(appServer)
195 | })
196 | })
197 | }
198 |
199 | const startLocalOnyxServer = async () => {
200 | const port = await getPort()
201 | await createOnyxServer({
202 | wsUrl: SWARM_WS_URL,
203 | httpUrl: SWARM_HTTP_URL,
204 | port,
205 | store,
206 | unsecure: true,
207 | testNet: USE_TESTNET,
208 | })
209 | return port
210 | }
211 |
212 | const start = async () => {
213 | let appPort
214 | if (is.development) {
215 | appPort = 3000
216 | } else {
217 | if (appServer == null) {
218 | appServer = await startAppServer()
219 | }
220 | appPort = appServer.port
221 | }
222 | let appUrl = `http://localhost:${appPort}`
223 | let urlParams = { testNet: USE_TESTNET }
224 | let nodeAddress
225 |
226 | const stakeRequiredError = 'You need to stake Mainframe tokens for your node'
227 |
228 | const storedWsUrl = store.get('wsUrl')
229 | if (storedWsUrl) {
230 | if (storedWsUrl === 'local') {
231 | let errorMsg
232 |
233 | try {
234 | await swarm.setup()
235 | } catch (e) {
236 | console.log(e.stack)
237 | errorMsg =
238 | 'There was an error setting up the Geth account\nDebug: ' +
239 | e.toString()
240 | }
241 |
242 | if (!errorMsg) {
243 | try {
244 | await swarm.start()
245 | } catch (e) {
246 | console.log(e.stack)
247 | if (e.message.includes('No stake found')) {
248 | nodeAddress = e.message
249 | .split(' ')
250 | .splice(-1)[0]
251 | .trim()
252 | errorMsg = stakeRequiredError
253 | } else {
254 | errorMsg =
255 | 'There was an error starting the local Swarm node, you may want to check if you have a Swarm node running already, or if the port 30399 is in use\nDebug: ' +
256 | e.toString()
257 | }
258 | }
259 | }
260 |
261 | if (!errorMsg) {
262 | try {
263 | const serverPort = await startLocalOnyxServer(appPort)
264 | urlParams.wsUrl = `ws://localhost:${serverPort}/graphql`
265 | } catch (e) {
266 | console.log(e.stack)
267 | swarm.stop()
268 | if (e.message.startsWith('Missing stake')) {
269 | nodeAddress = e.address
270 | errorMsg = stakeRequiredError
271 | } else {
272 | errorMsg =
273 | 'There was an error starting the local GraphQL server, you may want to check you have a Swarm node WebSocket interface listening on ' +
274 | SWARM_WS_URL +
275 | '\nDebug: ' +
276 | e.toString()
277 | }
278 | }
279 | }
280 |
281 | if (errorMsg) {
282 | urlParams = {
283 | address: nodeAddress,
284 | wsUrl: storedWsUrl,
285 | connectionError: errorMsg,
286 | testNet: USE_TESTNET,
287 | }
288 | }
289 | } else {
290 | // Use stored remote server url
291 | let domain
292 | if (storedWsUrl.indexOf('://') !== -1) {
293 | domain = storedWsUrl.split('/')[2]
294 | } else if (storedWsUrl.indexOf('/') !== -1) {
295 | domain = storedWsUrl.split('/')[0]
296 | }
297 | urlParams.wsUrl = storedWsUrl
298 | if (!domain) {
299 | urlParams.connectionError = 'Invalid ws url'
300 | }
301 | }
302 | }
303 |
304 | if (urlParams != null) {
305 | appUrl += '?' + querystring.stringify(urlParams)
306 | }
307 |
308 | if (mainWindow == null) {
309 | createMainWindow(appUrl)
310 | } else {
311 | mainWindow.loadURL(appUrl)
312 | }
313 |
314 | app.on('activate', () => {
315 | if (mainWindow == null) {
316 | createMainWindow(appUrl)
317 | }
318 | })
319 |
320 | app.on(
321 | 'certificate-error',
322 | (event, webContents, url, error, certificate, callback) => {
323 | console.warn('cert error: ', error)
324 | },
325 | )
326 | }
327 |
328 | ipcMain.on('onSetWsUrl', (e, url) => {
329 | store.set('wsUrl', url)
330 | start()
331 | })
332 |
333 | start()
334 |
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Onyx",
3 | "version": "0.5.2",
4 | "description": "Decentralized messaging application based on PSS",
5 | "main": "index.js",
6 | "author": "Mainframe",
7 | "dependencies": {
8 | "electron-store": "^1.3.0",
9 | "electron-util": "^0.8.0",
10 | "execa": "^0.10.0",
11 | "fs-extra": "^5.0.0",
12 | "get-port": "^3.2.0",
13 | "mkdirp": "^0.5.1",
14 | "node-fetch": "^2.1.2",
15 | "onyx-server": "^0.5.0",
16 | "static-server": "^3.0.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/swarm.js:
--------------------------------------------------------------------------------
1 | const { app } = require('electron')
2 | const { is } = require('electron-util')
3 | const execa = require('execa')
4 | const {
5 | createWriteStream,
6 | ensureDir,
7 | pathExists,
8 | readdir,
9 | readJson,
10 | remove,
11 | writeFile,
12 | } = require('fs-extra')
13 | const path = require('path')
14 | const os = require('os')
15 |
16 | const platform = {
17 | darwin: 'mac',
18 | linux: 'linux',
19 | win32: 'win',
20 | }[os.platform()]
21 |
22 | const getBinPath = is.development
23 | ? name => path.join(app.getAppPath(), '..', 'bin', `${name}-${platform}`)
24 | : name => path.join(process.resourcesPath, 'bin', name)
25 |
26 | const dataDir = path.join(app.getPath('userData'), 'data')
27 | const pwdPath = path.join(dataDir, 'pwd')
28 | const keystorePath = path.join(dataDir, 'keystore')
29 | const logPath = path.join(dataDir, 'node.log')
30 | const gethPath = getBinPath('geth')
31 | const swarmPath = getBinPath('swarm')
32 |
33 | let proc
34 |
35 | const setup = async () => {
36 | if (await pathExists(keystorePath)) {
37 | console.log('keystore exists, skip setup')
38 | return
39 | }
40 |
41 | await ensureDir(dataDir)
42 | await writeFile(pwdPath, 'secret')
43 | await execa(gethPath, [
44 | '--datadir',
45 | dataDir,
46 | '--password',
47 | pwdPath,
48 | 'account',
49 | 'new',
50 | ])
51 | }
52 |
53 | const start = async () => {
54 | const keystoreFiles = await readdir(keystorePath)
55 | const keyFilePath = path.join(keystorePath, keystoreFiles[0])
56 | const keystore = await readJson(keyFilePath)
57 |
58 | return new Promise((resolve, reject) => {
59 | proc = execa(swarmPath, [
60 | '--datadir',
61 | dataDir,
62 | '--password',
63 | pwdPath,
64 | '--bzzaccount',
65 | keystore.address,
66 | '--verbosity',
67 | '4',
68 | '--bzznetworkid',
69 | '1000',
70 | '--bootnodes',
71 | 'enode://b3417a20b07104ce2f948d7458fc03c73ad5918ae6be24eaf840322c70ffa8d0f59473139bef8ef0b4caffe7bc99018ab3686b1a757d73c1a7bfd880d2b7e7ef@52.31.117.198:30399,enode://762d482f6a400c89210be4d180b192dd5f921ca9f1a42a1651293f242613874f3e4e22589be582e0837c816c0c5366c00c32b7760ca345d65eb9ed75897db8c0@54.153.70.43:30399,enode://fc8d3eb2d5cfe4ed05c9e722518c895e006456448a49c2915e30beffd1ddb1ee17cd12abd550f1745d8b17060bf6c46e822e051ae4deca0e21d7fa7a15bb4c43@13.113.67.30:30399',
72 | '--ws',
73 | '--wsorigins',
74 | '*',
75 | '--ens-api',
76 | 'https://mainnet.infura.io/55HkPWVAJQjGH4ucvfW9',
77 | '--nosync',
78 | ])
79 |
80 | proc.catch(error => {
81 | console.error('Failed to start Swarm node: ', error.stderr)
82 | reject(error.stderr)
83 | })
84 |
85 | proc.stderr.pipe(createWriteStream(logPath))
86 |
87 | proc.stdout.on('data', data => {
88 | const dataStr = data.toString()
89 | if (dataStr.toLowerCase().indexOf('fatal:') !== -1) {
90 | const error = new Error(`Swarm error: ${dataStr}`)
91 | console.log(error)
92 | reject(error)
93 | }
94 | })
95 |
96 | proc.stderr.on('data', data => {
97 | if (
98 | data
99 | .toString()
100 | .toLowerCase()
101 | .indexOf('websocket endpoint opened') !== -1
102 | ) {
103 | console.log('Swarm node started')
104 | resolve()
105 | }
106 | })
107 | })
108 | }
109 |
110 | const stop = () => {
111 | return proc
112 | ? new Promise(resolve => {
113 | proc.once('exit', resolve)
114 | proc.kill()
115 | proc = undefined
116 | })
117 | : Promise.resolve()
118 | }
119 |
120 | const reset = async () => {
121 | await stop()
122 | await remove(dataDir)
123 | await setup()
124 | }
125 |
126 | module.exports = { reset, setup, start, stop }
127 |
--------------------------------------------------------------------------------
/assets/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mainframe-Archive/onyx/9c9caeab89fb3f5b8454023a5465f428590126f3/assets/icon.icns
--------------------------------------------------------------------------------
/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mainframe-Archive/onyx/9c9caeab89fb3f5b8454023a5465f428590126f3/assets/icon.ico
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 |
7 | // Make sure that including paths.js after env.js will read .env variables.
8 | delete require.cache[require.resolve('./paths')];
9 |
10 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | var dotenvFiles = [
19 | `${paths.dotenv}.${NODE_ENV}.local`,
20 | `${paths.dotenv}.${NODE_ENV}`,
21 | // Don't include `.env.local` for `test` environment
22 | // since normally you expect tests to produce the same
23 | // results for everyone
24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 | paths.dotenv,
26 | ].filter(Boolean);
27 |
28 | // Load environment variables from .env* files. Suppress warnings using silent
29 | // if this file is missing. dotenv will never modify any environment variables
30 | // that have already been set.
31 | // https://github.com/motdotla/dotenv
32 | dotenvFiles.forEach(dotenvFile => {
33 | if (fs.existsSync(dotenvFile)) {
34 | require('dotenv').config({
35 | path: dotenvFile,
36 | });
37 | }
38 | });
39 |
40 | // We support resolving modules according to `NODE_PATH`.
41 | // This lets you use absolute paths in imports inside large monorepos:
42 | // https://github.com/facebookincubator/create-react-app/issues/253.
43 | // It works similar to `NODE_PATH` in Node itself:
44 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
45 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
46 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
47 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
48 | // We also resolve them to make sure all tools using them work consistently.
49 | const appDirectory = fs.realpathSync(process.cwd());
50 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
51 | .split(path.delimiter)
52 | .filter(folder => folder && !path.isAbsolute(folder))
53 | .map(folder => path.resolve(appDirectory, folder))
54 | .join(path.delimiter);
55 |
56 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
57 | // injected into the application via DefinePlugin in Webpack configuration.
58 | const REACT_APP = /^REACT_APP_/i;
59 |
60 | function getClientEnvironment(publicUrl) {
61 | const raw = Object.keys(process.env)
62 | .filter(key => REACT_APP.test(key))
63 | .reduce(
64 | (env, key) => {
65 | env[key] = process.env[key];
66 | return env;
67 | },
68 | {
69 | // Useful for determining whether we’re running in production mode.
70 | // Most importantly, it switches React into the correct mode.
71 | NODE_ENV: process.env.NODE_ENV || 'development',
72 | // Useful for resolving the correct path to static assets in `public`.
73 | // For example,
.
74 | // This should only be used as an escape hatch. Normally you would put
75 | // images into the `src` and `import` them in code to get their paths.
76 | PUBLIC_URL: publicUrl,
77 | }
78 | );
79 | // Stringify all values so we can feed into Webpack DefinePlugin
80 | const stringified = {
81 | 'process.env': Object.keys(raw).reduce((env, key) => {
82 | env[key] = JSON.stringify(raw[key]);
83 | return env;
84 | }, {}),
85 | };
86 |
87 | return { raw, stringified };
88 | }
89 |
90 | module.exports = getClientEnvironment;
91 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const path = require('path')
4 | const fs = require('fs')
5 | const url = require('url')
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd())
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath)
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/')
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1)
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`
20 | } else {
21 | return path
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right