From 2b8bed434c498ff7d200376290a3f85bebe60f94 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 20 Nov 2016 16:17:57 +0100 Subject: [PATCH 01/25] RPC for deleting accounts. --- ethcore/src/account_provider.rs | 10 ++++++++-- rpc/src/v1/impls/parity_accounts.rs | 11 ++++++++++- rpc/src/v1/traits/parity_accounts.rs | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs index e906aefe9..917ae8b8b 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider.rs @@ -276,14 +276,20 @@ impl AccountProvider { } /// Returns `true` if the password for `account` is `password`. `false` if not. - pub fn test_password(&self, account: &Address, password: String) -> Result { - match self.sstore.sign(account, &password, &Default::default()) { + pub fn test_password(&self, account: &Address, password: &str) -> Result { + match self.sstore.sign(account, password, &Default::default()) { Ok(_) => Ok(true), Err(SSError::InvalidPassword) => Ok(false), Err(e) => Err(Error::SStore(e)), } } + /// Permanently removes an account. + pub fn kill_account(&self, account: &Address, password: &str) -> Result<(), Error> { + try!(self.sstore.remove_account(account, &password)); + Ok(()) + } + /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> { self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore) diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 2644c59e3..8229715ea 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -104,7 +104,7 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock let account: Address = account.into(); take_weak!(self.accounts) - .test_password(&account, password) + .test_password(&account, &password) .map_err(|e| errors::account("Could not fetch account info.", e)) } @@ -117,6 +117,15 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock .map_err(|e| errors::account("Could not fetch account info.", e)) } + fn kill_account(&self, account: RpcH160, password: String) -> Result { + try!(self.active()); + let account: Address = account.into(); + take_weak!(self.accounts) + .kill_account(&account, &password) + .map(|_| true) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + fn set_account_name(&self, addr: RpcH160, name: String) -> Result { try!(self.active()); let store = take_weak!(self.accounts); diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 0f62f59d1..29706d0b2 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -53,6 +53,11 @@ build_rpc_trait! { #[rpc(name = "parity_changePassword")] fn change_password(&self, H160, String, String) -> Result; + /// Permanently deletes an account. + /// Arguments: `account`, `password`. + #[rpc(name = "parity_killAccount")] + fn kill_account(&self, H160, String) -> Result; + /// Set an account's name. #[rpc(name = "parity_setAccountName")] fn set_account_name(&self, H160, String) -> Result; From 2d9369e5ba150899ae3b94d8878daae8a2fc09f9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 21 Nov 2016 11:03:18 +0100 Subject: [PATCH 02/25] Introduce test. --- rpc/src/v1/tests/mocked/parity_accounts.rs | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index c5ed4172e..c0982e60c 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -116,3 +116,27 @@ fn should_be_able_to_set_meta() { assert_eq!(res, Some(response)); } +#[test] +fn should_be_able_to_kill_account() { + let tester = setup(); + tester.accounts.new_account("password").unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":null},"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0x{}", "password"], "id": 1}}"#, address.hex()); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#; + let res = tester.io.handle_request_sync(request); + let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{}},\"id\":1}}"); + assert_eq!(res, Some(response)); +} + From 3db5e3e6279ba3f83539773fb591e020180a140c Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 22 Nov 2016 15:52:23 +0100 Subject: [PATCH 03/25] jsapi interface for parity_killAccount --- js/src/api/rpc/parity/parity.js | 5 +++++ js/src/jsonrpc/interfaces/parity.js | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index d14cc6554..ac16eb9b0 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -123,6 +123,11 @@ export default class Parity { .then((accounts) => (accounts || []).map(outAddress)); } + killAccount (account, password) { + return this._transport + .execute('parity_killAccount', inAddress(account), password); + } + listGethAccounts () { return this._transport .execute('parity_listGethAccounts') diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index a5717f502..d8d8a26c0 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -238,6 +238,24 @@ export default { } }, + killAccount: { + desc: 'Permanently an account', + params: [ + { + type: Address, + desc: 'The account to remove' + }, + { + type: String, + desc: 'Account password' + } + ], + returns: { + type: Boolean, + desc: 'true on success' + } + }, + listGethAccounts: { desc: 'Returns a list of the accounts available from Geth', params: [], From cce5b217ed14e6feeb78712d30e78f5430314113 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 22 Nov 2016 16:19:36 +0100 Subject: [PATCH 04/25] Delete account dialog --- js/src/modals/DeleteAccount/deleteAccount.css | 54 ++++++++ js/src/modals/DeleteAccount/deleteAccount.js | 127 ++++++++++++++++++ js/src/modals/DeleteAccount/index.js | 17 +++ js/src/modals/index.js | 2 + js/src/views/Account/account.js | 38 +++++- 5 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 js/src/modals/DeleteAccount/deleteAccount.css create mode 100644 js/src/modals/DeleteAccount/deleteAccount.js create mode 100644 js/src/modals/DeleteAccount/index.js diff --git a/js/src/modals/DeleteAccount/deleteAccount.css b/js/src/modals/DeleteAccount/deleteAccount.css new file mode 100644 index 000000000..9cd72c6ab --- /dev/null +++ b/js/src/modals/DeleteAccount/deleteAccount.css @@ -0,0 +1,54 @@ +/* Copyright 2015, 2016 Ethcore (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 . +*/ + +.body { + .hero { + padding-bottom: 1em; + } + + .info { + display: inline-block; + } + + .icon { + display: inline-block; + } + + .nameinfo { + display: inline-block; + text-align: left; + } + + .header { + text-transform: uppercase; + font-size: 1.25em; + padding-bottom: 0.25em; + } + + .address { + } + + .description { + padding-top: 1em; + font-size: 0.75em; + color: #aaa; + } + + .password { + padding: 1em 5em; + } +} diff --git a/js/src/modals/DeleteAccount/deleteAccount.js b/js/src/modals/DeleteAccount/deleteAccount.js new file mode 100644 index 000000000..1c6f48df5 --- /dev/null +++ b/js/src/modals/DeleteAccount/deleteAccount.js @@ -0,0 +1,127 @@ +// Copyright 2015, 2016 Ethcore (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 . + +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import { ConfirmDialog, IdentityIcon, IdentityName, Input } from '../../ui'; +import { newError } from '../../redux/actions'; + +import styles from './deleteAccount.css'; + +class DeleteAccount extends Component { + static contextTypes = { + api: PropTypes.object.isRequired, + router: PropTypes.object + } + + static propTypes = { + account: PropTypes.object.isRequired, + onClose: PropTypes.func, + newError: PropTypes.func + } + + state = { + password: '' + } + + render () { + const { account } = this.props; + const { password } = this.state; + + return ( + +
+ Are you sure you want to remove permanently delete the following account? +
+
+ +
+
+ +
+
+ { account.address } +
+
+
+
+ { account.meta.description } +
+
+ +
+
+ ); + } + + onChangePassword = (event, password) => { + this.setState({ password }); + } + + onDeleteConfirmed = () => { + const { api, router } = this.context; + const { account, newError } = this.props; + const { password } = this.state; + + api.parity + .killAccount(account.address, password) + .then((result) => { + if (result === true) { + router.push('/accounts'); + } else { + newError(new Error('Deletion failed.')); + } + + this.closeDeleteDialog(); + }) + .catch((error) => { + console.error('onDeleteConfirmed', error); + newError(new Error(`Deletion failed: ${error.message}`)); + this.closeDeleteDialog(); + }); + } + + closeDeleteDialog = () => { + this.props.onClose(); + } +} + +function mapStateToProps (state) { + return {}; +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ newError }, dispatch); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(DeleteAccount); diff --git a/js/src/modals/DeleteAccount/index.js b/js/src/modals/DeleteAccount/index.js new file mode 100644 index 000000000..5738c2704 --- /dev/null +++ b/js/src/modals/DeleteAccount/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 . + +export default from './deleteAccount'; diff --git a/js/src/modals/index.js b/js/src/modals/index.js index d0f8f7afe..06114159b 100644 --- a/js/src/modals/index.js +++ b/js/src/modals/index.js @@ -17,6 +17,7 @@ import AddAddress from './AddAddress'; import AddContract from './AddContract'; import CreateAccount from './CreateAccount'; +import DeleteAccount from './DeleteAccount'; import DeployContract from './DeployContract'; import EditMeta from './EditMeta'; import ExecuteContract from './ExecuteContract'; @@ -32,6 +33,7 @@ export { AddAddress, AddContract, CreateAccount, + DeleteAccount, DeployContract, EditMeta, ExecuteContract, diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index 86a76073e..b36f58618 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -17,12 +17,13 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import ActionDelete from 'material-ui/svg-icons/action/delete'; import ContentCreate from 'material-ui/svg-icons/content/create'; import ContentSend from 'material-ui/svg-icons/content/send'; import LockIcon from 'material-ui/svg-icons/action/lock'; import VerifyIcon from 'material-ui/svg-icons/action/verified-user'; -import { EditMeta, Shapeshift, SMSVerification, Transfer, PasswordManager } from '../../modals'; +import { EditMeta, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '../../modals'; import { Actionbar, Button, Page } from '../../ui'; import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png'; @@ -50,6 +51,7 @@ class Account extends Component { propName = null state = { + showDeleteDialog: false, showEditDialog: false, showFundDialog: false, showVerificationDialog: false, @@ -62,8 +64,8 @@ class Account extends Component { const { api } = this.context; const { address } = this.props.params; - const store = new VerificationStore(api, address); - this.setState({ verificationStore: store }); + const verificationStore = new VerificationStore(api, address); + this.setState({ verificationStore }); } render () { @@ -79,6 +81,7 @@ class Account extends Component { return (
+ { this.renderDeleteDialog(account) } { this.renderEditDialog(account) } { this.renderFundDialog() } { this.renderVerificationDialog() } @@ -131,7 +134,12 @@ class Account extends Component { key='passwordManager' icon={ } label='password' - onClick={ this.onPasswordClick } /> + onClick={ this.onPasswordClick } />, +