Key derivation in Worker (#4071)
* Add Signer Key Derivation in Service Worker * Several fixes throughout the UI * Hint for external account // working Worker * Add Worker state change * PR Grumbles
This commit is contained in:
parent
ec4b4cfbf2
commit
40f0ee004f
@ -133,7 +133,7 @@ export default class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testPassword = (password) => {
|
testPassword = (password) => {
|
||||||
this.setBusy(false);
|
this.setBusy(true);
|
||||||
|
|
||||||
return this._api.parity
|
return this._api.parity
|
||||||
.testPassword(this.address, password || this.validatePassword)
|
.testPassword(this.address, password || this.validatePassword)
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import PromiseWorker from 'promise-worker';
|
|
||||||
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
|
|
||||||
|
|
||||||
let workerRegistration;
|
|
||||||
|
|
||||||
// Setup the Service Worker
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
workerRegistration = runtime
|
|
||||||
.register()
|
|
||||||
.then(() => navigator.serviceWorker.ready)
|
|
||||||
.then((registration) => {
|
|
||||||
const _worker = registration.active;
|
|
||||||
_worker.controller = registration.active;
|
|
||||||
const worker = new PromiseWorker(_worker);
|
|
||||||
|
|
||||||
return worker;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
workerRegistration = Promise.reject('Service Worker is not available in your browser.');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setWorker (worker) {
|
|
||||||
return {
|
|
||||||
type: 'setWorker',
|
|
||||||
worker
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setError (error) {
|
|
||||||
return {
|
|
||||||
type: 'setError',
|
|
||||||
error
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupWorker () {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
|
|
||||||
if (state.compiler.worker) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
workerRegistration
|
|
||||||
.then((worker) => {
|
|
||||||
dispatch(setWorker(worker));
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('sw', error);
|
|
||||||
dispatch(setWorker(null));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
@ -22,7 +22,7 @@ export Status from './status';
|
|||||||
export apiReducer from './apiReducer';
|
export apiReducer from './apiReducer';
|
||||||
export balancesReducer from './balancesReducer';
|
export balancesReducer from './balancesReducer';
|
||||||
export blockchainReducer from './blockchainReducer';
|
export blockchainReducer from './blockchainReducer';
|
||||||
export compilerReducer from './compilerReducer';
|
export workerReducer from './workerReducer';
|
||||||
export imagesReducer from './imagesReducer';
|
export imagesReducer from './imagesReducer';
|
||||||
export personalReducer from './personalReducer';
|
export personalReducer from './personalReducer';
|
||||||
export signerReducer from './signerReducer';
|
export signerReducer from './signerReducer';
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import * as actions from './signerActions';
|
import * as actions from './signerActions';
|
||||||
|
|
||||||
import { inHex } from '~/api/format/input';
|
import { inHex } from '~/api/format/input';
|
||||||
import { Wallet } from '../../util/wallet';
|
import { Signer } from '../../util/signer';
|
||||||
|
|
||||||
export default class SignerMiddleware {
|
export default class SignerMiddleware {
|
||||||
constructor (api) {
|
constructor (api) {
|
||||||
@ -58,6 +58,7 @@ export default class SignerMiddleware {
|
|||||||
promise
|
promise
|
||||||
.then((txHash) => {
|
.then((txHash) => {
|
||||||
console.log('confirmRequest', id, txHash);
|
console.log('confirmRequest', id, txHash);
|
||||||
|
|
||||||
if (!txHash) {
|
if (!txHash) {
|
||||||
store.dispatch(actions.errorConfirmRequest({ id, err: 'Unable to confirm.' }));
|
store.dispatch(actions.errorConfirmRequest({ id, err: 'Unable to confirm.' }));
|
||||||
return;
|
return;
|
||||||
@ -73,33 +74,49 @@ export default class SignerMiddleware {
|
|||||||
|
|
||||||
// Sign request in-browser
|
// Sign request in-browser
|
||||||
const transaction = payload.sendTransaction || payload.signTransaction;
|
const transaction = payload.sendTransaction || payload.signTransaction;
|
||||||
|
|
||||||
if (wallet && transaction) {
|
if (wallet && transaction) {
|
||||||
(transaction.nonce.isZero()
|
const noncePromise = transaction.nonce.isZero()
|
||||||
? this._api.parity.nextNonce(transaction.from)
|
? this._api.parity.nextNonce(transaction.from)
|
||||||
: Promise.resolve(transaction.nonce)
|
: Promise.resolve(transaction.nonce);
|
||||||
).then(nonce => {
|
|
||||||
let txData = {
|
|
||||||
to: inHex(transaction.to),
|
|
||||||
nonce: inHex(transaction.nonce.isZero() ? nonce : transaction.nonce),
|
|
||||||
gasPrice: inHex(transaction.gasPrice),
|
|
||||||
gasLimit: inHex(transaction.gas),
|
|
||||||
value: inHex(transaction.value),
|
|
||||||
data: inHex(transaction.data)
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
const { worker } = store.getState().worker;
|
||||||
// NOTE: Derving the key takes significant amount of time,
|
|
||||||
// make sure to display some kind of "in-progress" state.
|
|
||||||
const signer = Wallet.fromJson(wallet, password);
|
|
||||||
const rawTx = signer.signTransaction(txData);
|
|
||||||
|
|
||||||
handlePromise(this._api.signer.confirmRequestRaw(id, rawTx));
|
const signerPromise = worker && worker._worker.state === 'activated'
|
||||||
} catch (error) {
|
? worker
|
||||||
console.error(error);
|
.postMessage({
|
||||||
|
action: 'getSignerSeed',
|
||||||
|
data: { wallet, password }
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
const seed = Buffer.from(result.data);
|
||||||
|
return new Signer(seed);
|
||||||
|
})
|
||||||
|
: Signer.fromJson(wallet, password);
|
||||||
|
|
||||||
|
// NOTE: Derving the key takes significant amount of time,
|
||||||
|
// make sure to display some kind of "in-progress" state.
|
||||||
|
return Promise
|
||||||
|
.all([ signerPromise, noncePromise ])
|
||||||
|
.then(([ signer, nonce ]) => {
|
||||||
|
const txData = {
|
||||||
|
to: inHex(transaction.to),
|
||||||
|
nonce: inHex(transaction.nonce.isZero() ? nonce : transaction.nonce),
|
||||||
|
gasPrice: inHex(transaction.gasPrice),
|
||||||
|
gasLimit: inHex(transaction.gas),
|
||||||
|
value: inHex(transaction.value),
|
||||||
|
data: inHex(transaction.data)
|
||||||
|
};
|
||||||
|
|
||||||
|
return signer.signTransaction(txData);
|
||||||
|
})
|
||||||
|
.then((rawTx) => {
|
||||||
|
return handlePromise(this._api.signer.confirmRequestRaw(id, rawTx));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
store.dispatch(actions.errorConfirmRequest({ id, err: error.message }));
|
store.dispatch(actions.errorConfirmRequest({ id, err: error.message }));
|
||||||
}
|
});
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePromise(this._api.signer.confirmRequest(id, { gas, gasPrice }, password));
|
handlePromise(this._api.signer.confirmRequest(id, { gas, gasPrice }, password));
|
||||||
|
@ -125,12 +125,13 @@ export default class Status {
|
|||||||
this._store.dispatch(statusCollection(status));
|
this._store.dispatch(statusCollection(status));
|
||||||
this._status = status;
|
this._status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextTimeout();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('_pollStatus', error);
|
console.error('_pollStatus', error);
|
||||||
|
nextTimeout();
|
||||||
});
|
});
|
||||||
|
|
||||||
nextTimeout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
68
js/src/redux/providers/worker.js
Normal file
68
js/src/redux/providers/worker.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import PromiseWorker from 'promise-worker';
|
||||||
|
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
|
||||||
|
|
||||||
|
import { setWorker } from './workerActions';
|
||||||
|
|
||||||
|
function getWorker () {
|
||||||
|
// Setup the Service Worker
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
return runtime
|
||||||
|
.register()
|
||||||
|
.then(() => navigator.serviceWorker.ready)
|
||||||
|
.then((registration) => {
|
||||||
|
const worker = registration.active;
|
||||||
|
worker.controller = registration.active;
|
||||||
|
|
||||||
|
return new PromiseWorker(worker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject('Service Worker is not available in your browser.');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupWorker = (store) => {
|
||||||
|
const { dispatch, getState } = store;
|
||||||
|
|
||||||
|
const state = getState();
|
||||||
|
const stateWorker = state.worker.worker;
|
||||||
|
|
||||||
|
if (stateWorker !== undefined && !(stateWorker && stateWorker._worker.state === 'redundant')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorker()
|
||||||
|
.then((worker) => {
|
||||||
|
if (worker) {
|
||||||
|
worker._worker.addEventListener('statechange', (event) => {
|
||||||
|
console.warn('worker state changed to', worker._worker.state);
|
||||||
|
|
||||||
|
// Re-install the new Worker
|
||||||
|
if (worker._worker.state === 'redundant') {
|
||||||
|
setupWorker(store);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setWorker(worker));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('sw', error);
|
||||||
|
dispatch(setWorker(null));
|
||||||
|
});
|
||||||
|
};
|
29
js/src/redux/providers/workerActions.js
Normal file
29
js/src/redux/providers/workerActions.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export function setWorker (worker) {
|
||||||
|
return {
|
||||||
|
type: 'setWorker',
|
||||||
|
worker
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setError (error) {
|
||||||
|
return {
|
||||||
|
type: 'setError',
|
||||||
|
error
|
||||||
|
};
|
||||||
|
}
|
@ -24,7 +24,7 @@ const initialState = {
|
|||||||
export default handleActions({
|
export default handleActions({
|
||||||
setWorker (state, action) {
|
setWorker (state, action) {
|
||||||
const { worker } = action;
|
const { worker } = action;
|
||||||
return Object.assign({}, state, { worker });
|
return Object.assign({}, state, { worker: worker || null });
|
||||||
},
|
},
|
||||||
|
|
||||||
setError (state, action) {
|
setError (state, action) {
|
@ -19,7 +19,7 @@ import { routerReducer } from 'react-router-redux';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
apiReducer, balancesReducer, blockchainReducer,
|
apiReducer, balancesReducer, blockchainReducer,
|
||||||
compilerReducer, imagesReducer, personalReducer,
|
workerReducer, imagesReducer, personalReducer,
|
||||||
signerReducer, statusReducer as nodeStatusReducer,
|
signerReducer, statusReducer as nodeStatusReducer,
|
||||||
snackbarReducer, walletReducer
|
snackbarReducer, walletReducer
|
||||||
} from './providers';
|
} from './providers';
|
||||||
@ -40,12 +40,12 @@ export default function () {
|
|||||||
balances: balancesReducer,
|
balances: balancesReducer,
|
||||||
certifications: certificationsReducer,
|
certifications: certificationsReducer,
|
||||||
blockchain: blockchainReducer,
|
blockchain: blockchainReducer,
|
||||||
compiler: compilerReducer,
|
|
||||||
images: imagesReducer,
|
images: imagesReducer,
|
||||||
nodeStatus: nodeStatusReducer,
|
nodeStatus: nodeStatusReducer,
|
||||||
personal: personalReducer,
|
personal: personalReducer,
|
||||||
signer: signerReducer,
|
signer: signerReducer,
|
||||||
snackbar: snackbarReducer,
|
snackbar: snackbarReducer,
|
||||||
wallet: walletReducer
|
wallet: walletReducer,
|
||||||
|
worker: workerReducer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import initMiddleware from './middleware';
|
|||||||
import initReducers from './reducers';
|
import initReducers from './reducers';
|
||||||
|
|
||||||
import { load as loadWallet } from './providers/walletActions';
|
import { load as loadWallet } from './providers/walletActions';
|
||||||
|
import { setupWorker } from './providers/worker';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Balances as BalancesProvider,
|
Balances as BalancesProvider,
|
||||||
@ -43,6 +44,7 @@ export default function (api, browserHistory) {
|
|||||||
new StatusProvider(store, api).start();
|
new StatusProvider(store, api).start();
|
||||||
|
|
||||||
store.dispatch(loadWallet(api));
|
store.dispatch(loadWallet(api));
|
||||||
|
setupWorker(store);
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import registerPromiseWorker from 'promise-worker/register';
|
import registerPromiseWorker from 'promise-worker/register';
|
||||||
|
import { Signer } from '~/util/signer';
|
||||||
import SolidityUtils from '~/util/solidity';
|
import SolidityUtils from '~/util/solidity';
|
||||||
|
|
||||||
const CACHE_NAME = 'parity-cache-v1';
|
const CACHE_NAME = 'parity-cache-v1';
|
||||||
@ -93,12 +94,21 @@ function handleMessage (message) {
|
|||||||
case 'setFiles':
|
case 'setFiles':
|
||||||
return setFiles(message.data);
|
return setFiles(message.data);
|
||||||
|
|
||||||
|
case 'getSignerSeed':
|
||||||
|
return getSignerSeed(message.data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn(`unknown action "${message.action}"`);
|
console.warn(`unknown action "${message.action}"`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSignerSeed (data) {
|
||||||
|
console.log('deriving seed from service-worker');
|
||||||
|
const { wallet, password } = data;
|
||||||
|
return Signer.getSeed(wallet, password);
|
||||||
|
}
|
||||||
|
|
||||||
function compile (data) {
|
function compile (data) {
|
||||||
const { build } = data;
|
const { build } = data;
|
||||||
|
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
line-height: 1.75em;
|
||||||
|
}
|
||||||
|
|
||||||
.details,
|
.details,
|
||||||
.gasDetails {
|
.gasDetails {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
|
@ -196,7 +196,7 @@ class MethodDecoding extends Component {
|
|||||||
: text.slice(0, 50) + '...';
|
: text.slice(0, 50) + '...';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={ styles.details }>
|
||||||
<span>with the </span>
|
<span>with the </span>
|
||||||
<span
|
<span
|
||||||
onClick={ this.toggleInputType }
|
onClick={ this.toggleInputType }
|
||||||
|
@ -24,9 +24,26 @@ import { sha3 } from '~/api/util/sha3';
|
|||||||
|
|
||||||
// Adapted from https://github.com/kvhnuke/etherwallet/blob/mercury/app/scripts/myetherwallet.js
|
// Adapted from https://github.com/kvhnuke/etherwallet/blob/mercury/app/scripts/myetherwallet.js
|
||||||
|
|
||||||
export class Wallet {
|
export class Signer {
|
||||||
|
|
||||||
static fromJson (json, password) {
|
static fromJson (json, password) {
|
||||||
|
return Signer
|
||||||
|
.getSeed(json, password)
|
||||||
|
.then((seed) => {
|
||||||
|
return new Signer(seed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSeed (json, password) {
|
||||||
|
try {
|
||||||
|
const seed = Signer.getSyncSeed(json, password);
|
||||||
|
return Promise.resolve(seed);
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSyncSeed (json, password) {
|
||||||
if (json.version !== 3) {
|
if (json.version !== 3) {
|
||||||
throw new Error('Only V3 wallets are supported');
|
throw new Error('Only V3 wallets are supported');
|
||||||
}
|
}
|
||||||
@ -43,15 +60,17 @@ export class Wallet {
|
|||||||
if (kdfparams.prf !== 'hmac-sha256') {
|
if (kdfparams.prf !== 'hmac-sha256') {
|
||||||
throw new Error('Unsupported parameters to PBKDF2');
|
throw new Error('Unsupported parameters to PBKDF2');
|
||||||
}
|
}
|
||||||
|
|
||||||
derivedKey = pbkdf2Sync(pwd, salt, kdfparams.c, kdfparams.dklen, 'sha256');
|
derivedKey = pbkdf2Sync(pwd, salt, kdfparams.c, kdfparams.dklen, 'sha256');
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unsupported key derivation scheme');
|
throw new Error('Unsupported key derivation scheme');
|
||||||
}
|
}
|
||||||
|
|
||||||
const ciphertext = Buffer.from(json.crypto.ciphertext, 'hex');
|
const ciphertext = Buffer.from(json.crypto.ciphertext, 'hex');
|
||||||
let mac = sha3(Buffer.concat([derivedKey.slice(16, 32), ciphertext]));
|
const mac = sha3(Buffer.concat([derivedKey.slice(16, 32), ciphertext]));
|
||||||
|
|
||||||
if (mac !== inHex(json.crypto.mac)) {
|
if (mac !== inHex(json.crypto.mac)) {
|
||||||
throw new Error('Key derivation failed - possibly wrong passphrase');
|
throw new Error('Key derivation failed - possibly wrong password');
|
||||||
}
|
}
|
||||||
|
|
||||||
const decipher = createDecipheriv(
|
const decipher = createDecipheriv(
|
||||||
@ -59,6 +78,7 @@ export class Wallet {
|
|||||||
derivedKey.slice(0, 16),
|
derivedKey.slice(0, 16),
|
||||||
Buffer.from(json.crypto.cipherparams.iv, 'hex')
|
Buffer.from(json.crypto.cipherparams.iv, 'hex')
|
||||||
);
|
);
|
||||||
|
|
||||||
let seed = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
let seed = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
||||||
|
|
||||||
while (seed.length < 32) {
|
while (seed.length < 32) {
|
||||||
@ -66,7 +86,7 @@ export class Wallet {
|
|||||||
seed = Buffer.concat([nullBuff, seed]);
|
seed = Buffer.concat([nullBuff, seed]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Wallet(seed);
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (seed) {
|
constructor (seed) {
|
@ -77,13 +77,28 @@ class TransactionPendingFormConfirm extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPasswordHint () {
|
||||||
|
const { account } = this.props;
|
||||||
|
const accountHint = account && account.meta && account.meta.passwordHint;
|
||||||
|
|
||||||
|
if (accountHint) {
|
||||||
|
return accountHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { wallet } = this.state;
|
||||||
|
const walletHint = wallet && wallet.meta && wallet.meta.passwordHint;
|
||||||
|
|
||||||
|
return walletHint || null;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, address, isSending } = this.props;
|
const { account, address, isSending } = this.props;
|
||||||
const { password, wallet, walletError } = this.state;
|
const { password, wallet, walletError } = this.state;
|
||||||
const isExternal = !account.uuid;
|
const isExternal = !account.uuid;
|
||||||
|
|
||||||
const passwordHint = account.meta && account.meta.passwordHint
|
const passwordHintText = this.getPasswordHint();
|
||||||
? (<div><span>(hint) </span>{ account.meta.passwordHint }</div>)
|
const passwordHint = passwordHintText
|
||||||
|
? (<div><span>(hint) </span>{ passwordHintText }</div>)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const isWalletOk = !isExternal || (walletError === null && wallet !== null);
|
const isWalletOk = !isExternal || (walletError === null && wallet !== null);
|
||||||
@ -170,12 +185,26 @@ class TransactionPendingFormConfirm extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeySelect = (event) => {
|
onKeySelect = (event) => {
|
||||||
|
// Check that file have been selected
|
||||||
|
if (event.target.files.length === 0) {
|
||||||
|
return this.setState({
|
||||||
|
wallet: null,
|
||||||
|
walletError: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
|
|
||||||
fileReader.onload = (e) => {
|
fileReader.onload = (e) => {
|
||||||
try {
|
try {
|
||||||
const wallet = JSON.parse(e.target.result);
|
const wallet = JSON.parse(e.target.result);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (wallet && typeof wallet.meta === 'string') {
|
||||||
|
wallet.meta = JSON.parse(wallet.meta);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
wallet,
|
wallet,
|
||||||
walletError: null
|
walletError: null
|
||||||
|
@ -18,7 +18,6 @@ import React, { PropTypes, Component } from 'react';
|
|||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { MenuItem, Toggle } from 'material-ui';
|
import { MenuItem, Toggle } from 'material-ui';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import CircularProgress from 'material-ui/CircularProgress';
|
import CircularProgress from 'material-ui/CircularProgress';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
@ -32,8 +31,6 @@ import SendIcon from 'material-ui/svg-icons/content/send';
|
|||||||
import { Actionbar, ActionbarExport, ActionbarImport, Button, Editor, Page, Select, Input } from '~/ui';
|
import { Actionbar, ActionbarExport, ActionbarImport, Button, Editor, Page, Select, Input } from '~/ui';
|
||||||
import { DeployContract, SaveContract, LoadContract } from '~/modals';
|
import { DeployContract, SaveContract, LoadContract } from '~/modals';
|
||||||
|
|
||||||
import { setupWorker } from '~/redux/providers/compilerActions';
|
|
||||||
|
|
||||||
import WriteContractStore from './writeContractStore';
|
import WriteContractStore from './writeContractStore';
|
||||||
import styles from './writeContract.css';
|
import styles from './writeContract.css';
|
||||||
|
|
||||||
@ -42,7 +39,6 @@ class WriteContract extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
accounts: PropTypes.object.isRequired,
|
accounts: PropTypes.object.isRequired,
|
||||||
setupWorker: PropTypes.func.isRequired,
|
|
||||||
worker: PropTypes.object,
|
worker: PropTypes.object,
|
||||||
workerError: PropTypes.any
|
workerError: PropTypes.any
|
||||||
};
|
};
|
||||||
@ -55,8 +51,7 @@ class WriteContract extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { setupWorker, worker } = this.props;
|
const { worker } = this.props;
|
||||||
setupWorker();
|
|
||||||
|
|
||||||
if (worker !== undefined) {
|
if (worker !== undefined) {
|
||||||
this.store.setWorker(worker);
|
this.store.setWorker(worker);
|
||||||
@ -575,17 +570,10 @@ class WriteContract extends Component {
|
|||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { accounts } = state.personal;
|
const { accounts } = state.personal;
|
||||||
const { worker, error } = state.compiler;
|
const { worker, error } = state.worker;
|
||||||
return { accounts, worker, workerError: error };
|
return { accounts, worker, workerError: error };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return bindActionCreators({
|
|
||||||
setupWorker
|
|
||||||
}, dispatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps
|
||||||
mapDispatchToProps
|
|
||||||
)(WriteContract);
|
)(WriteContract);
|
||||||
|
Loading…
Reference in New Issue
Block a user