hotfix: merge with develop

This commit is contained in:
George Lima 2018-12-07 10:17:30 -03:00
commit f552b92861
49 changed files with 16607 additions and 15662 deletions

View File

@ -1,11 +1,8 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-flow"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"
]
}
{
"presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"],
"plugins": [
"@babel/plugin-transform-regenerator",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"
]
}

View File

@ -1,47 +1,49 @@
{
"parser": "babel-eslint",
"extends": ["airbnb", "plugin:flowtype/recommended"],
"env": {
"browser": true,
"node": true,
"mocha": true,
"jest/globals": true
},
"plugins": ["flowtype", "jest"],
"settings": {
"flowtype": {
"onlyFilesWithFlowAnnotation": true
}
},
"rules": {
"jsx-quotes": ["error", "prefer-single"],
"import/prefer-default-export": ["off"],
"react/jsx-filename-extension": [1, { "extensions": [".js"] }],
"jsx-a11y/anchor-is-valid": [
"error",
{
"components": ["Link"],
"specialLink": ["to", "hrefLeft", "hrefRight"],
"aspects": ["noHref", "invalidHref", "preferButton"]
}
],
"jsx-a11y/no-autofocus": [
0,
{
"ignoreNonDOM": true
}
],
"max-len": [
"error",
{
"code": 120,
"tabWidth": 2,
"ignoreUrls": true,
"ignoreComments": true,
"ignoreStrings": true,
"ignorePattern": "<p[^>]*>.*?</p>",
"ignoreTrailingComments": true
}
]
}
}
{
"parser": "babel-eslint",
"extends": ["airbnb", "plugin:flowtype/recommended"],
"env": {
"browser": true,
"node": true,
"mocha": true,
"jest/globals": true
},
"plugins": ["flowtype", "jest"],
"settings": {
"flowtype": {
"onlyFilesWithFlowAnnotation": true
}
},
"rules": {
"jsx-quotes": ["error", "prefer-single"],
"import/prefer-default-export": ["off"],
"react/jsx-filename-extension": [1, { "extensions": [".js"] }],
"jsx-a11y/anchor-is-valid": [
"error",
{
"components": ["Link"],
"specialLink": ["to", "hrefLeft", "hrefRight"],
"aspects": ["noHref", "invalidHref", "preferButton"]
}
],
"jsx-a11y/no-autofocus": [
0,
{
"ignoreNonDOM": true
}
],
"max-len": [
"error",
{
"code": 120,
"tabWidth": 2,
"ignoreUrls": true,
"ignoreComments": true,
"ignoreStrings": true,
"ignorePattern": "<p[^>]*>.*?</p>",
"ignoreTrailingComments": true
}
],
"consistent-return": 0,
"react/destructuring-assignment": 0
}
}

View File

@ -10,6 +10,7 @@ import { SidebarComponent } from '../../app/components/sidebar';
describe('<Sidebar />', () => {
describe('render()', () => {
test('should render correctly', () => {
// $FlowFixMe
const { asFragment } = render(
<MemoryRouter>
<SidebarComponent />

54
app/components/Button.mdx Normal file
View File

@ -0,0 +1,54 @@
---
name: Button
---
import { Playground, PropsTable } from 'docz'
import { Button } from './button.js'
import { DoczWrapper } from '../theme.js'
# Button
<PropsTable of={Button} />
## Primary
<Playground>
<div style={{ backgroundColor: '#000', padding: '20px' }}>
<DoczWrapper>{() => <Button label="Click me!" />}</DoczWrapper>
</div>
</Playground>
## Secondary
<Playground>
<div style={{ backgroundColor: '#000', padding: '20px' }}>
<DoczWrapper>{() => <Button label="Click me!" onClick={() => alert('Clicked')} variant="secondary" />}</DoczWrapper>
</div>
</Playground>
## Primary Disabled
<Playground>
<div style={{ backgroundColor: '#000', padding: '20px' }}>
<DoczWrapper>{() => <Button label="Click me!" onClick={() => alert('Clicked')} disabled />}</DoczWrapper>
</div>
</Playground>
## Secondary Disabled
<Playground>
<div style={{ backgroundColor: '#000', padding: '20px' }}>
<DoczWrapper>
{() => <Button label="Click me!" onClick={() => alert('Clicked')} disabled variant="secondary" />}
</DoczWrapper>
</div>
</Playground>
## Link Button
<Playground>
<div style={{ backgroundColor: '#000', padding: '20px' }}>
<DoczWrapper>{() => <Button label="Click me!" isLink to="/my-route" />}</DoczWrapper>
</div>
</Playground>

26
app/components/Input.mdx Normal file
View File

@ -0,0 +1,26 @@
---
name: Input
---
import { Playground, PropsTable } from 'docz'
import { InputComponent } from './input.js'
import { DoczWrapper } from '../theme.js'
# Input
<PropsTable of={InputComponent} />
## Text Input
<Playground>
<DoczWrapper>{() => <InputComponent inputType="input" value="Hello World!" onChange={console.log} />}</DoczWrapper>
</Playground>
## Textarea
<Playground>
<DoczWrapper>
{() => <InputComponent inputType="textarea" value="I'm ZCash Electron Wallet" onChange={console.log} rows={10} />}
</DoczWrapper>
</Playground>

23
app/components/QRCode.mdx Normal file
View File

@ -0,0 +1,23 @@
---
name: QRCode
---
import { Playground, PropsTable } from 'docz'
import { QRCode } from './qrcode.js'
# QRCode
<PropsTable of={QRCode} />
## Basic usage
<Playground>
<QRCode value="https://astrocoders.com" />
</Playground>
## Custom size
<Playground>
<QRCode value="https://astrocoders.com" size={500} />
</Playground>

94
app/components/button.js Normal file
View File

@ -0,0 +1,94 @@
// @flow
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
/* eslint-disable import/no-extraneous-dependencies */
// $FlowFixMe
import { darken } from 'polished';
const defaultStyles = `
padding: 10px 30px;
font-family: ${
// $FlowFixMe
props => props.theme.fontFamily
};
font-weight: bold;
font-size: 0.9em;
cursor: pointer;
outline: none;
min-width: 100px;
border-radius: 100px;
transition: background-color 0.1s ease-in-out;
`;
const Primary = styled.button`
${defaultStyles};
background-color: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.secondary};
border: none;
&:hover {
background-color: ${props => darken(0.1, props.theme.colors.primary(props))};
}
&:disabled {
background-color: #3e3c42;
cursor: not-allowed;
opacity: 0.8;
}
`;
const Secondary = styled.button`
${defaultStyles};
background-color: Transparent;
color: ${props => props.theme.colors.secondary};
border: 2px solid #3e3c42;
&:hover {
border-color: ${props => props.theme.colors.primary};
}
&:disabled {
background-color: Transparent;
cursor: not-allowed;
color: #3e3c42;
&:hover {
border-color: #3e3c42;
}
}
`;
type Props = {
label: string,
onClick?: () => void,
to?: string,
variant?: 'primary' | 'secondary',
disabled?: boolean,
};
export const Button = ({
onClick, label, to, variant, disabled,
}: Props) => {
if (to && onClick) throw new Error('Should define either "to" or "onClick"');
const component = variant === 'primary' ? (
<Primary onClick={onClick} disabled={disabled}>
{label}
</Primary>
) : (
<Secondary onClick={onClick} disabled={disabled}>
{label}
</Secondary>
);
return to ? <Link to={String(to)}>{component}</Link> : component;
};
Button.defaultProps = {
to: null,
variant: 'primary',
onClick: null,
disabled: false,
};

53
app/components/input.js Normal file
View File

@ -0,0 +1,53 @@
// @flow
import React from 'react';
import styled from 'styled-components';
// TODO: Missing styles
const defaultStyles = `
padding: 10px;
width: 100%;
outline: none;
font-family: ${props => props.theme.fontFamily}
`;
const Input = styled.input.attrs({
type: 'text',
})`
${defaultStyles};
`;
const Textarea = styled.textarea`
${defaultStyles};
`;
type Props = {
inputType?: 'input' | 'textarea' | 'dropdown',
value: string,
onChange: string => void,
rows?: number,
disabled?: boolean,
type?: string,
};
export const InputComponent = ({ inputType, onChange, ...props }: Props) => {
const inputTypes = {
input: () => <Input onChange={evt => onChange(evt.target.value)} {...props} />,
textarea: () => <Textarea onChange={evt => onChange(evt.target.value)} {...props} />,
dropdown: () => null,
};
if (!Object.keys(inputTypes).find(key => key === inputType)) {
throw new Error(`Invalid input type: ${inputType}`);
}
return inputTypes[inputType]();
};
InputComponent.defaultProps = {
inputType: 'input',
rows: 4,
disabled: false,
type: 'text',
};

View File

@ -22,9 +22,5 @@ export const LayoutComponent = (props: Props) => {
// $FlowFixMe
const { children } = props; // eslint-disable-line
return (
<Layout>
{children}
</Layout>
);
return <Layout>{children}</Layout>;
};

15
app/components/qrcode.js Normal file
View File

@ -0,0 +1,15 @@
// @flow
import React from 'react';
import QR from 'qrcode.react';
type Props = {
value: string,
size?: number,
};
export const QRCode = ({ value, size }: Props) => <QR value={value} size={size} />;
QRCode.defaultProps = {
size: 128,
};

View File

@ -4,3 +4,4 @@ export const DASHBOARD_ROUTE = '/';
export const SEND_ROUTE = '/send';
export const RECEIVE_ROUTE = '/receive';
export const SETTINGS_ROUTE = '/settings';
export const CONSOLE_ROUTE = '/console';

View File

@ -1,10 +1,7 @@
// @flow
import {
DASHBOARD_ROUTE,
SEND_ROUTE,
RECEIVE_ROUTE,
SETTINGS_ROUTE,
DASHBOARD_ROUTE, SEND_ROUTE, RECEIVE_ROUTE, SETTINGS_ROUTE, CONSOLE_ROUTE,
} from './routes';
export const MENU_OPTIONS = [
@ -20,6 +17,10 @@ export const MENU_OPTIONS = [
label: 'Receive',
route: RECEIVE_ROUTE,
},
{
label: 'Console',
route: CONSOLE_ROUTE,
},
{
label: 'Settings',
route: SETTINGS_ROUTE,

View File

@ -9,25 +9,15 @@ import { createRootReducer } from './modules/reducer';
export const history = createBrowserHistory();
const shouldEnableDevTools = (
process.env.NODE_ENV !== 'production'
|| process.env.NODE_ENV !== 'staging'
) && window.devToolsExtension;
const shouldEnableDevTools = (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'staging') && window.devToolsExtension;
export const configureStore = (initialState: Object) => {
const middleware = applyMiddleware(
thunk,
routerMiddleware(history),
);
const middleware = applyMiddleware(thunk, routerMiddleware(history));
const enhancer = compose(
middleware,
shouldEnableDevTools ? window.devToolsExtension() : f => f,
);
return createStore(
createRootReducer(history),
initialState,
enhancer,
);
return createStore(createRootReducer(history), initialState, enhancer);
};

View File

@ -2,11 +2,11 @@
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import type { History } from 'react-router-dom';
import type { RouterHistory } from 'react-router-dom';
import todoReducer from './todo';
export const createRootReducer = (history: History) => combineReducers({
export const createRootReducer = (history: RouterHistory) => combineReducers({
todos: todoReducer,
router: connectRouter(history),
});

View File

@ -52,10 +52,7 @@ export const updateTodo = (id: string, text: string) => ({
const initialState = [];
// Reducers
export default (
state: Array<TodoType> = initialState,
action: Action,
): Array<TodoType> => {
export default (state: Array<TodoType> = initialState, action: Action): Array<TodoType> => {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];

View File

@ -1,5 +1,5 @@
// @flow
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { RouterComponent } from './router';
@ -9,7 +9,10 @@ const mapStateToProps = (state: AppState) => ({
todos: state.todos,
});
export const Router = withRouter(connect(
mapStateToProps,
null,
)(RouterComponent));
export const Router = compose(
withRouter,
connect(
mapStateToProps,
null,
),
)(RouterComponent);

View File

@ -10,12 +10,11 @@ import { SendView } from '../views/send';
import { ReceiveView } from '../views/receive';
import { SettingsView } from '../views/settings';
import { NotFoundView } from '../views/not-found';
import { ConsoleView } from '../views/console';
import { LayoutComponent } from '../components/layout';
import {
DASHBOARD_ROUTE,
SEND_ROUTE,
RECEIVE_ROUTE,
SETTINGS_ROUTE,
DASHBOARD_ROUTE, SEND_ROUTE, RECEIVE_ROUTE, SETTINGS_ROUTE, CONSOLE_ROUTE,
} from '../constants/routes';
export const RouterComponent = () => (
@ -25,23 +24,11 @@ export const RouterComponent = () => (
{/* $FlowFixMe */}
<LayoutComponent>
<Switch>
<Route
exact
path={DASHBOARD_ROUTE}
component={DashboardView}
/>
<Route
path={SEND_ROUTE}
component={SendView}
/>
<Route
path={RECEIVE_ROUTE}
component={ReceiveView}
/>
<Route
path={SETTINGS_ROUTE}
component={SettingsView}
/>
<Route exact path={DASHBOARD_ROUTE} component={DashboardView} />
<Route path={SEND_ROUTE} component={SendView} />
<Route path={RECEIVE_ROUTE} component={ReceiveView} />
<Route path={SETTINGS_ROUTE} component={SettingsView} />
<Route path={CONSOLE_ROUTE} component={ConsoleView} />
<Route component={NotFoundView} />
</Switch>
</LayoutComponent>

View File

@ -4,9 +4,9 @@ import { PureComponent } from 'react';
import { withRouter } from 'react-router-dom';
type Props = {
location: Object;
location: Object,
children: any,
}
};
class ScrollTop extends PureComponent<Props> {
componentDidUpdate(prevProps: Props) {

View File

@ -7,7 +7,7 @@ import { normalize } from 'polished'; // eslint-disable-line
import { DARK } from './constants/themes';
const darkOne = '#212124';
const darkOne = '#7B00DD';
const lightOne = '#ffffff';
const brandOne = '#624cda';
const brandTwo = '#a6ede2';
@ -17,13 +17,13 @@ const appTheme = {
fontFamily: 'PT Sans',
colors: {
primary: theme('mode', {
light: darkOne,
dark: lightOne,
}),
secondary: theme('mode', {
light: lightOne,
dark: darkOne,
}),
secondary: theme('mode', {
light: darkOne,
dark: lightOne,
}),
sidebarBg: brandOne,
sidebarItem: brandTwo,
sidebarItemActive: lightOne,
@ -36,11 +36,7 @@ const appTheme = {
/* eslint-disable react/prop-types */
// $FlowFixMe
export const DoczWrapper = ({ children }) => (
<ThemeProvider theme={appTheme}>
{children()}
</ThemeProvider>
);
export const DoczWrapper = ({ children }) => <ThemeProvider theme={appTheme}>{children()}</ThemeProvider>;
export const GlobalStyle = createGlobalStyle`${normalize()}`;

View File

@ -1,6 +1,6 @@
// @flow
type State = {| |};
type State = {||};
export type Action = { type: $Subtype<string>, payload: Object };
export type GetState = () => State;

View File

@ -4,5 +4,5 @@ export type TodoType = {
id: string,
text: string,
editing: boolean,
createdAt: number
createdAt: number,
};

39
app/views/console.js Normal file
View File

@ -0,0 +1,39 @@
// @flow
import React, { Component, Fragment } from 'react';
/* eslint-disable-next-line import/no-extraneous-dependencies */
import { ipcRenderer } from 'electron';
type Props = {};
type State = {
log: string | null,
};
export class ConsoleView extends Component<Props, State> {
state = {
log: null,
};
componentDidMount() {
ipcRenderer.on('zcashd-log', (event, message) => {
this.setState(() => ({
log: message,
}));
});
}
render() {
return (
<div className='dashboard'>
{this.state.log
&& this.state.log.split('\n').map(item => (
<Fragment key={`${item.slice(0, 10)}`}>
{item}
<br />
</Fragment>
))}
</div>
);
}
}

View File

@ -2,8 +2,4 @@
import React from 'react';
export const DashboardView = () => (
<div className='dashboard'>
dashboard
</div>
);
export const DashboardView = () => <div className='dashboard'>dashboard</div>;

View File

@ -2,8 +2,4 @@
import React from 'react';
export const NotFoundView = () => (
<div className='not found'>
not found
</div>
);
export const NotFoundView = () => <div className='not found'>not found</div>;

View File

@ -2,8 +2,4 @@
import React from 'react';
export const ReceiveView = () => (
<div className='send'>
receive
</div>
);
export const ReceiveView = () => <div className='send'>receive</div>;

View File

@ -2,8 +2,4 @@
import React from 'react';
export const SendView = () => (
<div className='send'>
send
</div>
);
export const SendView = () => <div className='send'>send</div>;

View File

@ -2,8 +2,4 @@
import React from 'react';
export const SettingsView = () => (
<div className='settings'>
settings
</div>
);
export const SettingsView = () => <div className='settings'>settings</div>;

View File

@ -20,25 +20,14 @@ type Props = {
export default (props: Props) => {
const {
addTodo,
todos,
deleteTodo,
toggleEdit,
updateTodo,
cancelUpdateTodo,
addTodo, todos, deleteTodo, toggleEdit, updateTodo, cancelUpdateTodo,
} = props;
return (
<div className='todo'>
<div className='todo__heading'>
<img
src={checklist}
alt='Testing File Loader'
className='todo__image'
/>
<h1 className='todo__header'>
Todo List App
</h1>
<img src={checklist} alt='Testing File Loader' className='todo__image' />
<h1 className='todo__header'>Todo List App</h1>
</div>
<TodoInput addTodo={addTodo} />
<TodoList

BIN
bin/linux/zcashd Executable file

Binary file not shown.

BIN
bin/mac/zcashd Executable file

Binary file not shown.

20
bin/win/first-run.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
IF NOT EXIST %AppData%\Zcash (
mkdir %AppData%\Zcash
)
IF NOT EXIST %AppData%\ZcashParams (
mkdir %AppData%\ZcashParams
)
IF NOT EXIST %AppData%\Zcash\zcash.conf (
(
echo addnode=mainnet.z.cash
echo rpcuser=username
echo rpcpassword=password%random%%random%
echo daemon=1
echo showmetrics=0
echo gen=0
) > %AppData%\Zcash\zcash.conf
)

BIN
bin/win/zcashd.exe Normal file

Binary file not shown.

209
bin/zcash-fetch-params Executable file
View File

@ -0,0 +1,209 @@
#!/bin/bash
set -eu
if [[ "$OSTYPE" == "darwin"* ]]; then
PARAMS_DIR="$HOME/Library/Application Support/ZcashParams"
else
PARAMS_DIR="$HOME/.zcash-params"
fi
SPROUT_PKEY_NAME='sprout-proving.key'
SPROUT_VKEY_NAME='sprout-verifying.key'
SAPLING_SPEND_NAME='sapling-spend.params'
SAPLING_OUTPUT_NAME='sapling-output.params'
SAPLING_SPROUT_GROTH16_NAME='sprout-groth16.params'
SPROUT_URL="https://z.cash/downloads"
SPROUT_IPFS="/ipfs/QmZKKx7Xup7LiAtFRhYsE1M7waXcv9ir9eCECyXAFGxhEo"
SHA256CMD="$(command -v sha256sum || echo shasum)"
SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')"
WGETCMD="$(command -v wget || echo '')"
IPFSCMD="$(command -v ipfs || echo '')"
CURLCMD="$(command -v curl || echo '')"
# fetch methods can be disabled with ZC_DISABLE_SOMETHING=1
ZC_DISABLE_WGET="${ZC_DISABLE_WGET:-}"
ZC_DISABLE_IPFS="${ZC_DISABLE_IPFS:-}"
ZC_DISABLE_CURL="${ZC_DISABLE_CURL:-}"
function fetch_wget {
if [ -z "$WGETCMD" ] || ! [ -z "$ZC_DISABLE_WGET" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (wget): $SPROUT_URL/$filename
EOF
wget \
--progress=dot:giga \
--output-document="$dlname" \
--continue \
--retry-connrefused --waitretry=3 --timeout=30 \
"$SPROUT_URL/$filename"
}
function fetch_ipfs {
if [ -z "$IPFSCMD" ] || ! [ -z "$ZC_DISABLE_IPFS" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (ipfs): $SPROUT_IPFS/$filename
EOF
ipfs get --output "$dlname" "$SPROUT_IPFS/$filename"
}
function fetch_curl {
if [ -z "$CURLCMD" ] || ! [ -z "$ZC_DISABLE_CURL" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (curl): $SPROUT_URL/$filename
EOF
curl \
--output "$dlname" \
-# -L -C - \
"$SPROUT_URL/$filename"
}
function fetch_failure {
cat >&2 <<EOF
Failed to fetch the Zcash zkSNARK parameters!
Try installing one of the following programs and make sure you're online:
* ipfs
* wget
* curl
EOF
exit 1
}
function fetch_params {
local filename="$1"
local output="$2"
local dlname="${output}.dl"
local expectedhash="$3"
if ! [ -f "$output" ]
then
for method in wget ipfs curl failure; do
if "fetch_$method" "$filename" "$dlname"; then
echo "Download successful!"
break
fi
done
"$SHA256CMD" $SHA256ARGS -c <<EOF
$expectedhash $dlname
EOF
# Check the exit code of the shasum command:
CHECKSUM_RESULT=$?
if [ $CHECKSUM_RESULT -eq 0 ]; then
mv -v "$dlname" "$output"
else
echo "Failed to verify parameter checksums!" >&2
exit 1
fi
fi
}
# Use flock to prevent parallel execution.
function lock() {
local lockfile=/tmp/fetch_params.lock
if [[ "$OSTYPE" == "darwin"* ]]; then
if shlock -f ${lockfile} -p $$; then
return 0
else
return 1
fi
else
# create lock file
eval "exec 200>$lockfile"
# acquire the lock
flock -n 200 \
&& return 0 \
|| return 1
fi
}
function exit_locked_error {
echo "Only one instance of fetch-params.sh can be run at a time." >&2
exit 1
}
function main() {
lock fetch-params.sh \
|| exit_locked_error
cat <<EOF
Zcash - fetch-params.sh
This script will fetch the Zcash zkSNARK parameters and verify their
integrity with sha256sum.
If they already exist locally, it will exit now and do nothing else.
EOF
# Now create PARAMS_DIR and insert a README if necessary:
if ! [ -d "$PARAMS_DIR" ]
then
mkdir -p "$PARAMS_DIR"
README_PATH="$PARAMS_DIR/README"
cat >> "$README_PATH" <<EOF
This directory stores common Zcash zkSNARK parameters. Note that it is
distinct from the daemon's -datadir argument because the parameters are
large and may be shared across multiple distinct -datadir's such as when
setting up test networks.
EOF
# This may be the first time the user's run this script, so give
# them some info, especially about bandwidth usage:
cat <<EOF
The parameters are currently just under 911MB in size, so plan accordingly
for your bandwidth constraints. If the files are already present and
have the correct sha256sum, no networking is used.
Creating params directory. For details about this directory, see:
$README_PATH
EOF
fi
cd "$PARAMS_DIR"
# Sprout parameters:
fetch_params "$SPROUT_PKEY_NAME" "$PARAMS_DIR/$SPROUT_PKEY_NAME" "8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7"
fetch_params "$SPROUT_VKEY_NAME" "$PARAMS_DIR/$SPROUT_VKEY_NAME" "4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82"
# Sapling parameters:
fetch_params "$SAPLING_SPEND_NAME" "$PARAMS_DIR/$SAPLING_SPEND_NAME" "8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13"
fetch_params "$SAPLING_OUTPUT_NAME" "$PARAMS_DIR/$SAPLING_OUTPUT_NAME" "2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4"
fetch_params "$SAPLING_SPROUT_GROTH16_NAME" "$PARAMS_DIR/$SAPLING_SPROUT_GROTH16_NAME" "b685d700c60328498fbde589c8c7c484c722b788b265b72af448a5bf0ee55b50"
}
main
rm -f /tmp/fetch_params.lock
exit 0

View File

@ -0,0 +1,24 @@
// @flow
import path from 'path';
import cp from 'child_process';
import getBinariesPath from './get-binaries-path';
import log from './logger';
export default (): Promise<*> => new Promise((resolve, reject) => {
const processName = path.join(getBinariesPath(), 'zcash-fetch-params');
const childProcess = cp.spawn(processName);
childProcess.stdout.on('data', data => log(data.toString()));
childProcess.stderr.on('data', data => log(data.toString()));
childProcess.on('error', reject);
childProcess.on('exit', (code, err) => {
if (code !== 0 || err) {
reject(new Error(err));
}
resolve();
});
});

View File

@ -0,0 +1,99 @@
// @flow
/* eslint-disable consistent-return */
import fs from 'fs';
import path from 'path';
import cp from 'child_process';
import crypto from 'crypto';
import util from 'util';
import eres from 'eres';
import got from 'got';
import Queue from 'p-queue';
// eslint-disable-next-line
import { app } from '../electron';
import getBinariesPath from './get-binaries-path';
import log from './logger';
const queue = new Queue({ concurrency: 1, autoStart: false });
const httpClient = got.extend({ baseUrl: 'https://z.cash/downloads/', retry: 3, useElectronNet: true });
const FILES: Array<{ name: string, hash: string }> = [
{ name: 'sprout-proving.key', hash: '8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7' },
{ name: 'sprout-verifying.key', hash: '4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82' },
{ name: 'sapling-spend.params', hash: '8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13' },
{ name: 'sapling-output.params', hash: '2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4' },
{ name: 'sprout-groth16.params', hash: 'b685d700c60328498fbde589c8c7c484c722b788b265b72af448a5bf0ee55b50' },
];
const checkSha256 = (pathToFile: string, expectedHash: string) => new Promise((resolve, reject) => {
fs.readFile(pathToFile, (err, file) => {
if (err) return reject(new Error(err));
const sum = crypto.createHash('sha256');
sum.update(file);
resolve(sum.digest('hex') === expectedHash);
});
});
const downloadFile = ({ file, pathToSave }): Promise<*> => new Promise((resolve, reject) => {
log(`Downloading ${file.name}...`);
httpClient
.stream(file.name)
.on('end', () => {
checkSha256(pathToSave, file.hash).then((isValid) => {
if (isValid) {
log(`SHA256 validation for file ${file.name} succeeded!`);
resolve(file.name);
} else {
reject(new Error(`SHA256 validation failed for file: ${file.name}`));
}
});
})
.on('error', err => reject(new Error(err)))
.pipe(fs.createWriteStream(pathToSave));
});
let missingDownloadParam = false;
export default (): Promise<*> => new Promise((resolve, reject) => {
const firstRunProcess = cp.spawn(path.join(getBinariesPath(), 'win', 'first-run.bat'));
firstRunProcess.stdout.on('data', data => log(data.toString()));
firstRunProcess.stderr.on('data', data => reject(data.toString()));
firstRunProcess.on('exit', async (code, err) => {
if (code !== 0 || err) return reject(new Error(err));
await Promise.all(
FILES.map(async (file) => {
const pathToSave = path.join(app.getPath('userData'), '..', 'ZcashParams', file.name);
const [cannotAccess] = await eres(util.promisify(fs.access)(pathToSave, fs.constants.F_OK));
if (cannotAccess) {
missingDownloadParam = true;
queue.add(() => downloadFile({ file, pathToSave }).then(() => log(`Download ${file.name} finished!`)));
} else {
const isValid = await checkSha256(pathToSave, file.hash);
if (isValid) {
log(`${file.name} already is in ${pathToSave}...`);
} else {
log(`File: ${file.name} failed in the SHASUM validation, downloading again...`);
queue.add(() => {
downloadFile({ file, pathToSave }).then(() => log(`Download ${file.name} finished!`));
});
}
}
}),
);
if (!missingDownloadParam) return resolve();
queue.onEmpty(resolve);
queue.start();
});
});

View File

@ -0,0 +1,8 @@
// @flow
export default () => Math.random()
.toString(36)
.substring(2, 15)
+ Math.random()
.toString(36)
.substring(2, 15);

View File

@ -0,0 +1,7 @@
// @flow
import path from 'path';
/* eslint-disable-next-line import/no-extraneous-dependencies */
import isDev from 'electron-is-dev';
// $FlowFixMe
export default () => (isDev ? path.join(__dirname, '..', '..', './bin') : path.join(process.resourcesPath, 'bin'));

View File

@ -0,0 +1,5 @@
// @flow
import os from 'os';
export default () => (os.platform() === 'win32' ? 'zcashd.exe' : 'zcashd');

View File

@ -0,0 +1,14 @@
// @flow
import os from 'os';
export default () => {
if (os.platform() === 'darwin') {
return 'mac';
}
if (os.platform() === 'win32') {
return 'win';
}
return 'linux';
};

4
config/daemon/logger.js Normal file
View File

@ -0,0 +1,4 @@
// @flow
/* eslint-disable-next-line no-console */
export default (...message: Array<*>) => console.log('[ZCash Daemon]', ...message);

View File

@ -0,0 +1,12 @@
// @flow
import os from 'os';
import log from './logger';
import fetchWindowsParams from './fetch-windows-params';
import runUnixFetchParams from './fetch-unix-params';
export default (): Promise<*> => {
log('Fetching params');
return os.platform() === 'win32' ? fetchWindowsParams() : runUnixFetchParams();
};

View File

@ -0,0 +1,104 @@
// @flow
import cp from 'child_process';
import path from 'path';
import os from 'os';
import processExists from 'process-exists';
/* eslint-disable import/no-extraneous-dependencies */
import isDev from 'electron-is-dev';
import type { ChildProcess } from 'child_process';
import eres from 'eres';
/* eslint-disable-next-line import/named */
import { mainWindow } from '../electron';
import getBinariesPath from './get-binaries-path';
import getOsFolder from './get-os-folder';
import getDaemonName from './get-daemon-name';
import fetchParams from './run-fetch-params';
import log from './logger';
import store from '../electron-store';
import generateRandomString from './generate-random-string';
const getDaemonOptions = ({ username, password }) => {
/*
-showmetrics
Show metrics on stdout
-metricsui
Set to 1 for a persistent metrics screen, 0 for sequential metrics
output
-metricsrefreshtime
Number of seconds between metrics refreshes
*/
const defaultOptions = [
'-showmetrics',
'--metricsui=0',
'-metricsrefreshtime=3',
`-rpcuser=${username}`,
`-rpcpassword=${password}`,
];
return isDev ? defaultOptions.concat('-testnet') : defaultOptions;
};
let resolved = false;
const runDaemon: () => Promise<?ChildProcess> = () => new Promise(async (resolve, reject) => {
const processName = path.join(getBinariesPath(), getOsFolder(), getDaemonName());
const [err] = await eres(fetchParams());
if (err) {
log('Something went wrong fetching params: ', err);
return reject(new Error(err));
}
log('Fetch Params finished!');
const [, isRunning] = await eres(processExists(processName));
if (isRunning) {
log('Already is running!');
return resolve();
}
const hasCredentials = store.has('rpcuser') && store.has('rpcpassword');
const rpcCredentials = hasCredentials
? {
username: store.get('rpcuser'),
password: store.get('rpcpassword'),
}
: {
username: generateRandomString(),
password: generateRandomString(),
};
if (!hasCredentials) {
store.set('rpcuser', rpcCredentials.username);
store.set('rpcpassword', rpcCredentials.password);
}
const childProcess = cp.spawn(processName, getDaemonOptions(rpcCredentials), {
stdio: ['ignore', 'pipe', 'pipe'],
});
childProcess.stdout.on('data', (data) => {
if (mainWindow) mainWindow.webContents.send('zcashd-log', data.toString());
if (!resolved) {
resolve(childProcess);
resolved = true;
}
});
childProcess.stderr.on('data', (data) => {
log(data.toString());
reject(new Error(data.toString()));
});
childProcess.on('error', reject);
if (os.platform() === 'win32') {
resolved = true;
resolve(childProcess);
}
});
export default runDaemon;

6
config/electron-store.js Normal file
View File

@ -0,0 +1,6 @@
// @flow
import Store from 'electron-store';
export default new Store({
encryptionKey: 'afr58kk5xg6tz5o4kmvmw',
});

View File

@ -1,4 +1,6 @@
// @flow
import '@babel/polyfill';
import path from 'path';
/* eslint-disable import/no-extraneous-dependencies */
@ -7,10 +9,14 @@ import { autoUpdater } from 'electron-updater';
import isDev from 'electron-is-dev';
/* eslint-enable import/no-extraneous-dependencies */
import type { BrowserWindow as BrowserWindowType } from 'electron';
import eres from 'eres';
import { registerDebugShortcut } from '../utils/debug-shortcut';
import runDaemon from './daemon/zcashd-child-process';
import zcashLog from './daemon/logger';
let mainWindow: BrowserWindowType;
let updateAvailable: boolean = false;
let zcashDaemon;
const showStatus = (text) => {
if (text === 'Update downloaded') updateAvailable = true;
@ -55,17 +61,33 @@ const createWindow = () => {
mainWindow.setVisibleOnAllWorkspaces(true);
registerDebugShortcut(app, mainWindow);
mainWindow.loadURL(isDev
? 'http://0.0.0.0:8080/'
: `file://${path.join(__dirname, '../build/index.html')}`);
mainWindow.loadURL(isDev ? 'http://0.0.0.0:8080/' : `file://${path.join(__dirname, '../build/index.html')}`);
exports.app = app;
exports.mainWindow = mainWindow;
};
app.on('ready', createWindow);
/* eslint-disable-next-line consistent-return */
app.on('ready', async () => {
createWindow();
const [err, proc] = await eres(runDaemon());
if (err || !proc) return zcashLog(err);
/* eslint-disable-next-line */
zcashLog(`ZCash Daemon running. PID: ${proc.pid}`);
zcashDaemon = proc;
});
app.on('activate', () => {
if (mainWindow === null) createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('before-quit', () => {
if (zcashDaemon) {
zcashLog('Graceful shutdown ZCash Daemon, this may take a few seconds.');
zcashDaemon.kill('SIGINT');
}
});

View File

@ -4,14 +4,13 @@ const autoprefixer = require('autoprefixer');
module.exports = {
entry: {
index: './app/index.js',
index: ['@babel/polyfill', './app/index.js'],
},
optimization: {
minimizer: [
new UglifyJSPlugin({ sourceMap: true }),
],
minimizer: [new UglifyJSPlugin({ sourceMap: true })],
},
devtool: 'cheap-module-source-map',
target: 'electron-renderer',
module: {
rules: [
{
@ -23,21 +22,28 @@ module.exports = {
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'postcss-loader',
ident: 'postcss',
options: {
plugins: () => [autoprefixer({
browsers: ['> 1%', 'not ie 11'],
})],
use: [
{
loader: 'style-loader',
},
}, {
loader: 'sass-loader',
}],
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
ident: 'postcss',
options: {
plugins: () => [
autoprefixer({
browsers: ['> 1%', 'not ie 11'],
}),
],
},
},
{
loader: 'sass-loader',
},
],
},
{
test: /\.(png|jpe?g|gif|svg)$/,
@ -53,13 +59,15 @@ module.exports = {
},
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/fonts/',
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/fonts/',
},
},
}],
],
},
],
},

View File

@ -5,4 +5,14 @@ export default {
title: 'Zcash Foundation',
description: 'Zcash Foundation User Interface Styleguide',
plugins: [css()],
htmlContext: {
head: {
links: [
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css?family=PT+Sans:400,700',
},
],
},
},
};

View File

@ -1,142 +1,149 @@
{
"name": "zec-react-wallet",
"version": "0.1.1",
"description": "Zcash Reference Wallet",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "concurrently \"cross-env BROWSER=none yarn dev\" \"wait-on http://0.0.0.0:8080 && yarn electron:dev\"",
"dev": "webpack-dev-server --config config/webpack-dev.config.js --mode development --open --hot",
"build": "rm -rf build && webpack --config config/webpack-prod.config.js --mode production --env.NODE_ENV=production",
"lint:precommit": "eslint ./app/",
"flow:precommit": "glow",
"flow:coverage": "flow-coverage-report -t html -i 'app/**/*.js' -x 'dist/*.js' --threshold 70",
"flow:report": "yarn flow:coverage && cd ./flow-coverage && open index.html",
"electron:dev": "electron -r @babel/register .",
"electron:prepare": "yarn icon:build && rm -rf dist && mkdir dist",
"electron:pack": "yarn electron:prepare && electron-builder --dir",
"electron:dist": "yarn electron:prepare && electron-builder",
"preelectron:prepare": "yarn build",
"icon:build": "./node_modules/.bin/electron-icon-maker --input=build-assets/icon.png --output=./build",
"docz:dev": "docz dev",
"docz:build": "docz build",
"test": "jest",
"test:watch": "jest --watch"
},
"author": {
"name": "André Neves",
"email": "andrerfneves@protonmail.com",
"url": "https://andrenev.es"
},
"private": true,
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"concurrently": "^4.1.0",
"cross-env": "^5.2.0",
"css-loader": "^1.0.1",
"docz": "^0.12.13",
"docz-plugin-css": "^0.11.0",
"electron": "^3.0.10",
"electron-builder": "^20.36.2",
"electron-icon-maker": "^0.0.4",
"electron-is-dev": "^1.0.1",
"electron-log": "^2.2.17",
"electron-positioner": "^4.1.0",
"electron-updater": "^4.0.4",
"eslint": "^5.8.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-flowtype": "^3.2.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^22.1.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^2.0.0",
"flow-bin": "^0.87.0",
"flow-coverage-report": "^0.6.0",
"flow-typed": "^2.5.1",
"glow": "^1.2.2",
"html-webpack-plugin": "^3.1.0",
"jest": "^23.6.0",
"jest-dom": "^2.1.1",
"node-sass": "^4.8.3",
"postcss-loader": "^3.0.0",
"pre-commit": "^1.2.2",
"react-testing-library": "^5.3.1",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"sass-loader": "^7.1.0",
"spectron": "^5.0.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.0.1",
"wait-on": "^3.2.0",
"webpack": "^4.4.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.1"
},
"dependencies": {
"@babel/register": "^7.0.0",
"autoprefixer": "^9.3.1",
"connected-react-router": "^5.0.1",
"got": "^9.3.2",
"history": "^4.7.2",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.2.2",
"redux": "^4.0.1",
"redux-thunk": "^2.2.0",
"styled-components": "^4.1.1",
"styled-theming": "^2.2.0",
"uuid": "^3.3.2"
},
"pre-commit": [
"lint:precommit",
"flow:precommit"
],
"build": {
"appId": "com.zcashfoundation",
"productName": "ZEC Wallet",
"asar": true,
"directories": {
"buildResources": "build",
"output": "dist"
},
"files": [
"./index.js",
"./build/**/*",
"./node_modules/**/*"
],
"linux": {
"icon": "./build/icons/png",
"target": [
"deb"
]
},
"mac": {
"category": "public.app-category.productivity",
"type": "distribution",
"target": [
"pkg",
"dmg"
]
}
},
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/__tests__/setup/jest.js",
"testPathIgnorePatterns": [
"<rootDir>/__tests__/setup/"
]
},
"resolutions": {
"babel-core": "7.0.0-bridge.0"
}
}
{
"name": "zec-react-wallet",
"version": "0.1.1",
"description": "Zcash Reference Wallet",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "concurrently \"cross-env BROWSER=none yarn dev\" \"wait-on http://0.0.0.0:8080 && yarn electron:dev\"",
"dev": "webpack-dev-server --config config/webpack-dev.config.js --mode development --open --hot",
"build": "rm -rf build && webpack --config config/webpack-prod.config.js --mode production --env.NODE_ENV=production",
"lint:precommit": "eslint ./app/",
"flow:precommit": "glow",
"flow:coverage": "flow-coverage-report -t html -i 'app/**/*.js' -x 'dist/*.js' --threshold 70",
"flow:report": "yarn flow:coverage && cd ./flow-coverage && open index.html",
"electron:dev": "electron -r @babel/register .",
"electron:prepare": "yarn icon:build && rm -rf dist && mkdir dist",
"electron:pack": "yarn electron:prepare && electron-builder --dir",
"electron:dist": "yarn electron:prepare && electron-builder",
"preelectron:prepare": "yarn build",
"icon:build": "./node_modules/.bin/electron-icon-maker --input=build-assets/icon.png --output=./build",
"docz:dev": "docz dev",
"docz:build": "docz build",
"test": "jest",
"test:watch": "jest --watch"
},
"author": {
"name": "André Neves",
"email": "andrerfneves@protonmail.com",
"url": "https://andrenev.es"
},
"private": true,
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-regenerator": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"concurrently": "^4.1.0",
"cross-env": "^5.2.0",
"css-loader": "^1.0.1",
"docz": "^0.12.13",
"docz-plugin-css": "^0.11.0",
"electron": "^3.0.10",
"electron-builder": "^20.36.2",
"electron-icon-maker": "^0.0.4",
"electron-is-dev": "^1.0.1",
"electron-log": "^2.2.17",
"electron-positioner": "^4.1.0",
"electron-updater": "^4.0.4",
"eslint": "^5.8.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-flowtype": "^3.2.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^22.1.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^2.0.0",
"flow-bin": "0.84.0",
"flow-coverage-report": "^0.6.0",
"flow-typed": "^2.5.1",
"glow": "^1.2.2",
"html-webpack-plugin": "^3.1.0",
"jest": "^23.6.0",
"jest-dom": "^2.1.1",
"node-sass": "^4.8.3",
"postcss-loader": "^3.0.0",
"pre-commit": "^1.2.2",
"react-testing-library": "^5.3.1",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"sass-loader": "^7.1.0",
"spectron": "^5.0.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.0.1",
"wait-on": "^3.2.0",
"webpack": "^4.4.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.1"
},
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@babel/register": "^7.0.0",
"autoprefixer": "^9.3.1",
"connected-react-router": "^5.0.1",
"electron-store": "^2.0.0",
"eres": "^1.0.1",
"got": "^9.3.2",
"history": "^4.7.2",
"p-queue": "^3.0.0",
"process-exists": "^3.1.0",
"qrcode.react": "^0.8.0",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.2.2",
"redux": "^4.0.1",
"redux-thunk": "^2.2.0",
"styled-components": "^4.1.1",
"styled-theming": "^2.2.0",
"uuid": "^3.3.2"
},
"pre-commit": [
"lint:precommit",
"flow:precommit"
],
"build": {
"appId": "com.zcashfoundation",
"productName": "ZEC Wallet",
"asar": true,
"directories": {
"buildResources": "build",
"output": "dist"
},
"files": [
"./index.js",
"./build/**/*",
"./node_modules/**/*"
],
"linux": {
"icon": "./build/icons/png",
"target": [
"deb"
]
},
"mac": {
"category": "public.app-category.productivity",
"type": "distribution",
"target": [
"pkg",
"dmg"
]
}
},
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/__tests__/setup/jest.js",
"testPathIgnorePatterns": [
"<rootDir>/__tests__/setup/"
]
},
"resolutions": {
"babel-core": "7.0.0-bridge.0"
}
}

View File

@ -6,6 +6,7 @@
<meta name="theme-color" content="#000000" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="shortcut icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=PT+Sans:400,700" rel="stylesheet" />
<title>Zcash Reference Wallet</title>
</head>

30795
yarn.lock

File diff suppressed because it is too large Load Diff