Skip to content

Commit

Permalink
Merge pull request #5 from rixombea/master
Browse files Browse the repository at this point in the history
another batch of updates
  • Loading branch information
RocksteadyTC committed Sep 14, 2018
2 parents badc3c7 + 7252596 commit be69658
Show file tree
Hide file tree
Showing 39 changed files with 2,023 additions and 1,854 deletions.
4 changes: 3 additions & 1 deletion .gitignore
@@ -1,3 +1,5 @@
service.log
node_modules/*
tbin/*
.vscode/*
.vscode/*
build/*
77 changes: 43 additions & 34 deletions README.md
@@ -1,54 +1,63 @@
# WalletShell

![Overview Tab](https://image.ibb.co/d9ovhH/overview.png "Overview Tab")

This is a GUI wallet for TurtleCoin made using Electron, this means it's written in JavaScript, HTML and CSS.
It is meant to be able to work on Windows, Linux and MacOS, however so far I've only been able to test it on Windows.

As of now, this wallet contains the basic functions required to operate with TurtleCoin, this includes:
* Load a wallet file
* Generate a new wallet container file
* Import an existing wallet using keys
* Show private keys
* See balance and transactions
* Send TurtleCoin
It is meant to be able to work on Windows, Linux and MacOS, however so far we've only been able to test it on Linux & Windows.

There is still plenty of room for improvements and features, so I will gladly accept help from anyone who is capable of lending a hand. Right now the code is still in beta phase with some rough edges and should be polished in order to make a proper release.
![WalletShell Screens](https://raw.githubusercontent.com/rixombea/turtle-wallet-electron/wssx/sc/wssc.gif "WalletShell Screens")

### How to Use
### Features:
This wallet contains the basic functions required to manage your TurtleCoin wallet:
* Basic tasks: Open an existing wallet file, create new wallet file, import an existing wallet using keys or mnemonic seed
* Wallet operations: display wallet balance, list transactions, send new transaction, display/export private keys & mnemonic seed
* Address book: store and label your contact's wallet address, searchable and can be looked up during sending new transaction
* UI/Misc: Provides up-to-date public node address or specify your own local node, able to specify start scan height when importing wallet for faster sync, incoming transaction notification, minimize/close to system tray.

Once you got WalletShell running (see the sections below), you will need to specify the path to walletd.exe in the Settings tab (the cog icon in the top left). Walletd comes with the turtlecoin .zip, and will not work with a versions below 0.3.1.
There is still plenty of room for improvements and features, so we will gladly accept help from anyone who is capable of lending a hand.

Now you can switch back to the Overview and click the 'Load Wallet' button to start using WalletShell. If you don't have a wallet file you can generate a new one with the 'Create Wallet' button or import one using your private keys with the 'Import Wallet' button.
### Notes

### How to Launch
WalletShell relies on `turtle-service` to manage wallet container & rpc communication.

You need to have `Node.js` and `npm` installed to launch WalletShell. You can download the Node installer from https://nodejs.org/ and it comes bundled with `npm`. The versions I used are `v9.4.0` for Node and `5.8.0` for npm however it might work with older versions.
WalletShell release packaged includes ready to use `turtle-service` binary, which is unmodified copy TurtleCoin release archive.

Once you have Node installed and this repository downloaded to some folder, `cd` into that folder and use the command `npm install`. This will install the dependencies of WalletShell to a `node_modules` folder that you need in order to run the wallet. The dependencies are:
On first launch, WalletShell will try to detect location/path of bundled `turtle-service` binary, but if it's failed, can set path to the `turtle-service` binary on the Settings tab.

```
"devDependencies": {
"electron": "^1.8.4"
},
"dependencies": {
"electron-settings": "^3.1.4",
"jayson": "^2.0.5"
}
```
If you don't trust the bundled `turtle-service` file, you can compare the sha256 sum against one from the official release, or just download and use binary from official TurtleCoin release, which you can download here: https://github.com/turtlecoin/turtlecoin/releases. Then, make sure to update your `turtle-service` path setting.

### Download & Run WalletShell

* Download latest packaged release for your platform here: https://github.com/rixombea/turtle-wallet-electron/releases

Now all you need to do is type `npm start` and the wallet should launch.
* Extract downloaded file
* Open/Run the wallet executable, located in the extracted directory:
* GNU/Linux: `walletshell-<version>-linux/walletshell`
* Windows: `walletshell-<version>-windows/walletshell.exe`
* macOS: ?? `walletshell-<version>-osx/walletshell.app/Contents/MacOs/walletshell` ??

### How to Build

You can build it manually following the [Application Distribution](https://electronjs.org/docs/tutorial/application-distribution) guide in the Electron documentation.
### Build
You need to have `Node.js` and `npm` installed, go to https://nodejs.org and find out how to get it installed on your platform.

However the easiest way I have tried so far is using a npm package called `electron-packager`. In order to do so you need to install it globally by typing `npm install electron-packager -g`.
Once you have Node installed:
```
# clone the repo
git clone https://github.com/turtlecoin/turtle-wallet-electron
cd turtle-wallet-electron
# install dependencies
npm install
# build GNU/Linux package
npm run linpack
Now you can build WalletShell by typing:
# build Windows package
npm run winpack
# build OSX package
npm run osxpack
```

`electron-packager <path_source> --out <path_out> --asar`
Find the binary in a ready to package folder for each platform inside `build` subdirectory.

Where `<path_source>` is the path to your folder and `<path_out>` is the path to the folder where the app is going to be generated.

Using that command will generate a build for the platform and architecture you are currently using. You can use the `--platform=<platform>` and `--arch=<arch>` optional flags if you want to build it for something else. You can check https://github.com/electron-userland/electron-packager for extra info regarding this tool.
121 changes: 61 additions & 60 deletions main.js
@@ -1,39 +1,31 @@
const {app, BrowserWindow, dialog, Tray, Menu} = require('electron');
const {app, BrowserWindow, dialog, Tray, Menu, NativeImage} = require('electron');
const path = require('path');
const url = require('url');
//const ipc = require('electron').ipcMain;
const https = require('https');
const os = require('os');
const platform = require('os').platform();
const crypto = require('crypto');
const fs = require('fs');
const Store = require('electron-store');
const settings = new Store({name: 'Settings'});

const platform = os.platform;
const defaultFallbackNodes = [
'127.0.0.1:11898',
const IS_DEBUG = (process.argv[1] === 'debug' || process.argv[2] === 'debug');
const SERVICE_FILENAME = (platform === 'win32' ? 'turtle-service.exe' : 'turtle-service' );
const DEFAULT_SERVICE_BIN = path.join(process.resourcesPath, SERVICE_FILENAME);
const DEFAULT_TITLE = 'TurtleCoin Wallet';
const PUBLIC_NODES_URL = 'https://raw.githubusercontent.com/turtlecoin/turtlecoin-nodes-json/master/turtlecoin-nodes.json';
const FALLBACK_NODES = [
'public.turtlenode.io:11898',
'public.turtlenode.net:11898',
];
const defaultTitle = 'TurtleCoin Wallet';
const isDebug = (process.argv[1] === 'debug' || process.argv[2] === 'debug');
const publicNodesUrl = 'https://raw.githubusercontent.com/turtlecoin/turtlecoin-nodes-json/master/turtlecoin-nodes.json';

const binFilename = (platform === 'win32' ? 'turtle-service.exe' : 'turtle-service' );
const defaultServiceBin = path.join(process.resourcesPath, binFilename);
console.log(`default turtle-service bin: ${defaultServiceBin}`);

// default settings
var DEFAULT_SETTINGS = {
service_bin: defaultServiceBin,
const DEFAULT_SETTINGS = {
service_bin: DEFAULT_SERVICE_BIN,
service_host: '127.0.0.1',
service_port: 8070,
service_password: crypto.randomBytes(32).toString('hex'),
daemon_host: '127.0.0.1',
daemon_port: 11898,
pubnodes_date: null,
pubnodes_data: defaultFallbackNodes,
pubnodes_custom: [],
pubnodes_data: FALLBACK_NODES,
pubnodes_custom: ['127.0.0.1:11898'],
tray_minimize: false,
tray_close: false
}
Expand All @@ -42,10 +34,24 @@ let win;
app.prompExit = true;
app.needToExit = false;

let trayIcon = path.join(__dirname,'src/assets/tray_24x24.png');
if(platform === 'darwin'){
trayIcon = path.join(__dirname,'src/assets/tray.icns');
}else if(platform === 'win32'){
trayIcon = path.join(__dirname,'src/assets/tray.ico');
}

let trayIconHide = path.join(__dirname,'src/assets/trayon_24x24.png');
if(platform === 'darwin'){
trayIconHide = path.join(__dirname,'src/assets/trayon.icns');
}else if(platform === 'win32'){
trayIconHide = path.join(__dirname,'src/assets/trayon.ico');
}

function createWindow () {
// Create the browser window.
win = new BrowserWindow({
title: defaultTitle,
title: DEFAULT_TITLE,
icon: path.join(__dirname,'src/assets/walletshell_icon.png'),
frame: true,//frame: false,
width: 800,
Expand All @@ -56,34 +62,30 @@ function createWindow () {
backgroundColor: '#02853e',
});

const tray = new Tray(path.join(__dirname,'src/assets/trtl_tray.png'));
tray.setTitle(defaultTitle);
tray.setToolTip('Slow and steady wins the race!');
let contextMenu = Menu.buildFromTemplate([
{ label: 'Minimize to tray', click: () => { win.hide(); }},
{ label: 'Quit', click: ()=> {
// if(!win.isVisible()) win.show();
// if(win.isMinimized()) win.restore();
// win.focus();
app.needToExit = true;
win.close();
}
}
]);
// linux default;
const tray = new Tray(trayIcon);
tray.setPressedImage(trayIconHide);
tray.setTitle(DEFAULT_TITLE);
tray.setToolTip('Slow and steady wins the race!');
tray.setContextMenu(contextMenu);

tray.on('click', () => {
win.isVisible() ? win.hide() : win.show();
});

win.on('show', () => {
tray.setHighlightMode('always');
tray.setImage(trayIcon);
contextMenu = Menu.buildFromTemplate([
{ label: 'Minimize to tray', click: () => { win.hide();} },
{ label: 'Quit', click: ()=> {
// if(!win.isVisible()) win.show();
// if(win.isMinimized()) win.restore();
// win.focus();
app.needToExit = true;
win.close();
}
Expand All @@ -94,12 +96,11 @@ function createWindow () {

win.on('hide', () => {
tray.setHighlightMode('never');
tray.setImage(trayIconHide);

contextMenu = Menu.buildFromTemplate([
{ label: 'Restore', click: () => { win.show();} },
{ label: 'Quit', click: ()=> {
// if(!win.isVisible()) win.show();
// if(win.isMinimized()) win.restore();
// win.focus();
app.needToExit = true;
win.close();
}
Expand All @@ -123,12 +124,12 @@ function createWindow () {
}));

// open devtools
if(isDebug ) win.webContents.openDevTools();
if(IS_DEBUG ) win.webContents.openDevTools();

// show windosw
win.once('ready-to-show', () => {
win.show();
win.setTitle(defaultTitle);
win.setTitle(DEFAULT_TITLE);
})

win.on('close', (e) => {
Expand All @@ -137,14 +138,14 @@ function createWindow () {
win.hide();
}else if(app.prompExit){
e.preventDefault();
let msg = 'Are you sure?';
if(wsession.loadedWalletAddress !== ''){
msg = 'Close your wallet and exit?';
}

let msg = 'Are you sure want to exit?';
if(wsession.loadedWalletAddress !== '') msg = 'Are you sure want to close your wallet and exit?';

dialog.showMessageBox({
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
title: 'Exit Confirmation',
message: msg
}, function (response) {
if (response === 0) {
Expand All @@ -161,22 +162,24 @@ function createWindow () {
win.on('closed', () => {
win = null;
});

win.setMenu(null);

// misc handler
win.webContents.on('crashed', (event, killed) => {
if(isDebug) console.log('webcontent was crashed', event, killed);
// todo: prompt to restart
if(IS_DEBUG) console.log('webcontent was crashed', event, killed);
});

win.on('unresponsive', (even) => {
if(isDebug) console.log('unresponsive!');
// todo: prompt to restart
if(IS_DEBUG) console.log('unresponsive!');
});
}

function storeNodeList(pnodes){
pnodes = pnodes || settings.get('pubnodes_data');
if( pnodes.hasOwnProperty('nodes')){
global.wsession.nodeChoices = ['127.0.0.1:11898'];
pnodes.nodes.forEach(element => {
let item = `${element.url}:${element.port}`;
global.wsession.nodeChoices.push(item);
Expand All @@ -186,7 +189,7 @@ function storeNodeList(pnodes){
}

function doNodeListUpdate(){
https.get(publicNodesUrl, (res) => {
https.get(PUBLIC_NODES_URL, (res) => {
var result = '';
res.setEncoding('utf8');

Expand All @@ -199,13 +202,13 @@ function doNodeListUpdate(){
var pnodes = JSON.parse(result);
let today = new Date();
storeNodeList(pnodes);
if(isDebug) console.log('nodelist has been updated');
if(IS_DEBUG) console.log('nodelist has been updated');
let mo = (today.getMonth()+1);
settings.set('pubnodes_date', `${today.getFullYear()}-${mo}-${today.getDate()}`);
}catch(e){
if(isDebug) console.log('failed to parse json data', e);
if(isDebug) console.log('type of input', typeof d);
if(isDebug) console.log(JSON.stringify(d));
if(IS_DEBUG) console.log('failed to parse json data', e);
if(IS_DEBUG) console.log('type of input', typeof d);
if(IS_DEBUG) console.log(JSON.stringify(d));
storeNodeList();
}
});
Expand All @@ -226,6 +229,7 @@ function initSettings(){
const silock = app.requestSingleInstanceLock()
app.on('second-instance', (commandLine, workingDirectory) => {
if (win) {
if (!win.isVisible()) win.show();
if (win.isMinimized()) win.restore();
win.focus();
}
Expand All @@ -245,18 +249,19 @@ app.on('ready', () => {
txLen: 0,
txLastHash: '',
txLastTimestamp: '',
txNew: [],
tmpPath: app.getPath('temp'),
dataPath: app.getPath('userData'),
nodeFee: null,
nodeChoices: settings.get('pubnodes_data'),
servicePath: settings.get('service_bin'),
configUpdated: false,
uiStateChanged: false,
defaultTitle: defaultTitle,
debug: isDebug
defaultTitle: DEFAULT_TITLE,
debug: IS_DEBUG
};

if(isDebug) console.log('Running in debug mode');
if(IS_DEBUG) console.log('Running in debug mode');

let today = new Date();
let last_checked = new Date(settings.get('pubnodes_date'));
Expand All @@ -272,10 +277,6 @@ app.on('ready', () => {
createWindow();
});

// app.on('before-quit', () => {
// connection.terminateWallet();
// })

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
Expand All @@ -290,20 +291,20 @@ app.on('activate', () => {
});

process.on('uncaughtException', function (err) {
if(isDebug) console.log(err);
if(IS_DEBUG) console.log(err);
process.exit(1);
});

process.on('beforeExit', (code) => {
if(isDebug) console.log(`beforeExit code: ${code}`);
if(IS_DEBUG) console.log(`beforeExit code: ${code}`);
});

process.on('exit', (code) => {
if(isDebug) console.log(`exit with code: ${code}`);
if(IS_DEBUG) console.log(`exit with code: ${code}`);
});

process.on('warning', (warning) => {
if(isDebug){
if(IS_DEBUG){
console.warn(warning.name);
console.warn(warning.message);
console.warn(warning.code);
Expand Down

0 comments on commit be69658

Please sign in to comment.