Merge branch 'master' into check-updates

This commit is contained in:
Jaco Greeff 2016-12-14 14:37:38 +01:00
commit 2588aea6b2
38 changed files with 506 additions and 154 deletions

2
Cargo.lock generated
View File

@ -1316,7 +1316,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/ethcore/js-precompiled.git#a8f4db707c521f71f093b62e3173b98144569c4e"
source = "git+https://github.com/ethcore/js-precompiled.git#175003ae159b126302fd1a90dd875dc86d7adba0"
dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -10,14 +10,15 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x118c30",
"eip150Transition": "0x2625a0",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"homesteadTransition": 1150000,
"eip150Transition": 2500000,
"eip155Transition": 3000000,
"eip160Transition": 3000000,
"ecip1010PauseTransition": 3000000,
"ecip1010ContinueTransition": 5000000,
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff",
"ecip1010PauseTransition": "0x2dc6c0",
"ecip1010ContinueTransition": "0x4c4b40"
"eip161dTransition": "0x7fffffffffffffff"
}
}
},

View File

@ -9,12 +9,15 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
"homesteadTransition": "0x789b0",
"eip150Transition": "0x1b34d8",
"eip155Transition": 1885000,
"eip160Transition": 1885000,
"eip161abcTransition": 1885000,
"eip161dTransition": 1885000
"homesteadTransition": 494000,
"eip150Transition": 1783000,
"eip155Transition": 1915000,
"eip160Transition": 1915000,
"ecip1010PauseTransition": 1915000,
"ecip1010ContinueTransition": 3415000,
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
}
}
},

@ -1 +1 @@
Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be
Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8

View File

@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "0.2.121",
"version": "0.2.122",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",

View File

@ -0,0 +1,33 @@
// 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 <http://www.gnu.org/licenses/>.
import { stringify } from 'querystring';
export const postToServer = (query, isTestnet = false) => {
const port = isTestnet ? 28443 : 18443;
query = stringify(query);
return fetch(`https://email-verification.parity.io:${port}/?` + query, {
method: 'POST', mode: 'cors', cache: 'no-store'
})
.then((res) => {
return res.json().then((data) => {
if (res.ok) {
return data.message;
}
throw new Error(data.message || 'unknown error');
});
});
};

View File

@ -0,0 +1,23 @@
// 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 <http://www.gnu.org/licenses/>.
import React from 'react';
export default (
<ul>
<li>todo</li>
</ul>
);

View File

@ -15,17 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { stringify } from 'querystring';
import React from 'react';
export const termsOfService = (
<ul>
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
<li>You pay a fee for the cost of this service using the account you want to verify.</li>
<li>Your phone number is transmitted to a third party US SMS verification service Twilio for the sole purpose of the SMS verification. You consent to this use. Twilios privacy policy is here: <a href={ 'https://www.twilio.com/legal/privacy/developer' }>https://www.twilio.com/legal/privacy/developer</a>.</li>
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
</ul>
);
export const postToServer = (query, isTestnet = false) => {
const port = isTestnet ? 8443 : 443;

View File

@ -0,0 +1,27 @@
// 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 <http://www.gnu.org/licenses/>.
import React from 'react';
export default (
<ul>
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
<li>You pay a fee for the cost of this service using the account you want to verify.</li>
<li>Your phone number is transmitted to a third party US SMS verification service Twilio for the sole purpose of the SMS verification. You consent to this use. Twilios privacy policy is here: <a href={ 'https://www.twilio.com/legal/privacy/developer' }>https://www.twilio.com/legal/privacy/developer</a>.</li>
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
</ul>
);

View File

@ -0,0 +1 @@
[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"reverse","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"},{"name":"_puzzle","type":"bytes32"},{"name":"_emailHash","type":"bytes32"}],"name":"puzzle","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_emailHash","type":"bytes32"}],"name":"request","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"emailHash","type":"bytes32"}],"name":"Requested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":true,"name":"emailHash","type":"bytes32"},{"indexed":false,"name":"puzzle","type":"bytes32"}],"name":"Puzzled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Confirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}]

View File

@ -19,6 +19,7 @@ import basiccoin from './basiccoin.json';
import basiccoinmanager from './basiccoinmanager.json';
import dappreg from './dappreg.json';
import eip20 from './eip20.json';
import emailverification from './email-verification.json';
import gavcoin from './gavcoin.json';
import githubhint from './githubhint.json';
import owned from './owned.json';
@ -34,6 +35,7 @@ export {
basiccoinmanager,
dappreg,
eip20,
emailverification,
gavcoin,
githubhint,
owned,

View File

@ -19,7 +19,7 @@ import Registry from './registry';
import SignatureReg from './signaturereg';
import TokenReg from './tokenreg';
import GithubHint from './githubhint';
import * as smsVerification from './sms-verification';
import * as verification from './verification';
import BadgeReg from './badgereg';
let instance = null;
@ -58,7 +58,11 @@ export default class Contracts {
}
get smsVerification () {
return smsVerification;
return verification;
}
get emailVerification () {
return verification;
}
static create (api) {

View File

@ -15,10 +15,6 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.list li {
padding: .1em 0;
}
.spacing {
margin-top: 1.5em;
}

View File

@ -25,41 +25,33 @@ import { fromWei } from '~/api/util/wei';
import { Form, Input } from '~/ui';
import { nullableProptype } from '~/util/proptypes';
import { termsOfService } from '../../../3rdparty/sms-verification';
import smsTermsOfService from '~/3rdparty/sms-verification/terms-of-service';
import emailTermsOfService from '~/3rdparty/email-verification/terms-of-service';
import { howSMSVerificationWorks, howEmailVerificationWorks } from '../how-it-works';
import styles from './gatherData.css';
export default class GatherData extends Component {
static propTypes = {
fee: React.PropTypes.instanceOf(BigNumber),
isNumberValid: PropTypes.bool.isRequired,
method: PropTypes.string.isRequired,
fields: PropTypes.array.isRequired,
isVerified: nullableProptype(PropTypes.bool.isRequired),
hasRequested: nullableProptype(PropTypes.bool.isRequired),
setNumber: PropTypes.func.isRequired,
setConsentGiven: PropTypes.func.isRequired
}
render () {
const { isNumberValid, isVerified } = this.props;
const { method, isVerified } = this.props;
const termsOfService = method === 'email' ? emailTermsOfService : smsTermsOfService;
const howItWorks = method === 'email' ? howEmailVerificationWorks : howSMSVerificationWorks;
return (
<Form>
<p>The following steps will let you prove that you control both an account and a phone number.</p>
<ol className={ styles.list }>
<li>You send a verification request to a specific contract.</li>
<li>Our server puts a puzzle into this contract.</li>
<li>The code you receive via SMS is the solution to this puzzle.</li>
</ol>
{ howItWorks }
{ this.renderFee() }
{ this.renderCertified() }
{ this.renderRequested() }
<Input
label={ 'phone number in international format' }
hint={ 'the SMS will be sent to this number' }
error={ isNumberValid ? null : 'invalid number' }
disabled={ isVerified }
onChange={ this.numberOnChange }
onSubmit={ this.numberOnSubmit }
/>
{ this.renderFields() }
<Checkbox
className={ styles.spacing }
label={ 'I agree to the terms and conditions below.' }
@ -136,12 +128,28 @@ export default class GatherData extends Component {
);
}
numberOnSubmit = (value) => {
this.props.setNumber(value);
}
renderFields () {
const { isVerified, fields } = this.props;
numberOnChange = (_, value) => {
this.props.setNumber(value);
const rendered = fields.map((field) => {
const onChange = (_, v) => {
field.onChange(v);
};
const onSubmit = field.onChange;
return (
<Input
key={ field.key }
label={ field.label }
hint={ field.hint }
error={ field.error }
disabled={ isVerified }
onChange={ onChange }
onSubmit={ onSubmit }
/>
);
});
return (<div>{rendered}</div>);
}
consentOnChange = (_, consentGiven) => {

View File

@ -20,20 +20,25 @@ import { Form, Input } from '~/ui';
export default class QueryCode extends Component {
static propTypes = {
number: PropTypes.string.isRequired,
receiver: PropTypes.string.isRequired,
hint: PropTypes.string,
isCodeValid: PropTypes.bool.isRequired,
setCode: PropTypes.func.isRequired
}
static defaultProps = {
hint: 'Enter the code you received.'
}
render () {
const { number, isCodeValid } = this.props;
const { receiver, hint, isCodeValid } = this.props;
return (
<Form>
<p>The verification code has been sent to { number }.</p>
<p>The verification code has been sent to { receiver }.</p>
<Input
label={ 'verification code' }
hint={ 'Enter the code you received via SMS.' }
hint={ hint }
error={ isCodeValid ? null : 'invalid code' }
onChange={ this.onChange }
onSubmit={ this.onSubmit }

View File

@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react';
import { nullableProptype } from '~/util/proptypes';
import TxHash from '~/ui/TxHash';
import {
POSTING_REQUEST, POSTED_REQUEST, REQUESTING_SMS
POSTING_REQUEST, POSTED_REQUEST, REQUESTING_CODE
} from '../store';
import styles from './sendRequest.css';
@ -45,9 +45,9 @@ export default class SendRequest extends Component {
</div>
);
case REQUESTING_SMS:
case REQUESTING_CODE:
return (
<p>Requesting an SMS from the Parity server and waiting for the puzzle to be put into the contract.</p>
<p>Requesting a code from the Parity server and waiting for the puzzle to be put into the contract.</p>
);
default:

View File

@ -0,0 +1,70 @@
// 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 <http://www.gnu.org/licenses/>.
import { observable, computed, action } from 'mobx';
import { sha3 } from '~/api/util/sha3';
import EmailVerificationABI from '~/contracts/abi/email-verification.json';
import VerificationStore, {
LOADING, QUERY_DATA, QUERY_CODE, POSTED_CONFIRMATION, DONE
} from './store';
import { postToServer } from '../../3rdparty/email-verification';
export default class EmailVerificationStore extends VerificationStore {
@observable email = '';
@computed get isEmailValid () {
// See https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
return this.email && this.email.indexOf('@') >= 0;
}
@computed get isStepValid () {
if (this.step === DONE) {
return true;
}
if (this.error) {
return false;
}
switch (this.step) {
case LOADING:
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
case QUERY_DATA:
return this.isEmailValid && this.consentGiven;
case QUERY_CODE:
return this.requestTx && this.isCodeValid === true;
case POSTED_CONFIRMATION:
return !!this.confirmationTx;
default:
return false;
}
}
constructor (api, account, isTestnet) {
super(api, EmailVerificationABI, 'emailverification3', account, isTestnet);
}
requestValues = () => [ sha3(this.email) ]
@action setEmail = (email) => {
this.email = email;
}
requestCode = () => {
const { email, account, isTestnet } = this;
return postToServer({ email, address: account }, isTestnet);
}
}

View File

@ -0,0 +1,41 @@
// 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 <http://www.gnu.org/licenses/>.
import React from 'react';
import styles from './verification.css';
export const howSMSVerificationWorks = (
<div>
<p>The following steps will let you prove that you control both an account and a phone number.</p>
<ol className={ styles.list }>
<li>You send a verification request to a specific contract.</li>
<li>Our server puts a puzzle into this contract.</li>
<li>The code you receive via SMS is the solution to this puzzle.</li>
</ol>
</div>
);
export const howEmailVerificationWorks = (
<div>
<p>The following steps will let you prove that you control both an account and an e-mail address.</p>
<ol className={ styles.list }>
<li>You send a verification request to a specific contract.</li>
<li>Our server puts a puzzle into this contract.</li>
<li>The code you receive via e-mail is the solution to this puzzle.</li>
</ol>
</div>
);

View File

@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './SMSVerification';
export default from './verification';

View File

@ -0,0 +1,67 @@
// 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 <http://www.gnu.org/licenses/>.
import { observable, computed, action } from 'mobx';
import phone from 'phoneformat.js';
import SMSVerificationABI from '~/contracts/abi/sms-verification.json';
import VerificationStore, {
LOADING, QUERY_DATA, QUERY_CODE, POSTED_CONFIRMATION, DONE
} from './store';
import { postToServer } from '../../3rdparty/sms-verification';
export default class SMSVerificationStore extends VerificationStore {
@observable number = '';
@computed get isNumberValid () {
return phone.isValidNumber(this.number);
}
@computed get isStepValid () {
if (this.step === DONE) {
return true;
}
if (this.error) {
return false;
}
switch (this.step) {
case LOADING:
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
case QUERY_DATA:
return this.isNumberValid && this.consentGiven;
case QUERY_CODE:
return this.requestTx && this.isCodeValid === true;
case POSTED_CONFIRMATION:
return !!this.confirmationTx;
default:
return false;
}
}
constructor (api, account, isTestnet) {
super(api, SMSVerificationABI, 'smsverification', account, isTestnet);
}
@action setNumber = (number) => {
this.number = number;
}
requestCode = () => {
const { number, account, isTestnet } = this;
return postToServer({ number, address: account }, isTestnet);
}
}

View File

@ -14,21 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { observable, computed, autorun, action } from 'mobx';
import phone from 'phoneformat.js';
import { observable, autorun, action } from 'mobx';
import { sha3 } from '~/api/util/sha3';
import Contract from '~/api/contract';
import Contracts from '~/contracts';
import { checkIfVerified, checkIfRequested, awaitPuzzle } from '~/contracts/sms-verification';
import { postToServer } from '~/3rdparty/sms-verification';
import { checkIfVerified, checkIfRequested, awaitPuzzle } from '~/contracts/verification';
import { checkIfTxFailed, waitForConfirmations } from '~/util/tx';
export const LOADING = 'fetching-contract';
export const QUERY_DATA = 'query-data';
export const POSTING_REQUEST = 'posting-request';
export const POSTED_REQUEST = 'posted-request';
export const REQUESTING_SMS = 'requesting-sms';
export const REQUESTING_CODE = 'requesting-code';
export const QUERY_CODE = 'query-code';
export const POSTING_CONFIRMATION = 'posting-confirmation';
export const POSTED_CONFIRMATION = 'posted-confirmation';
@ -43,56 +41,30 @@ export default class VerificationStore {
@observable isVerified = null;
@observable hasRequested = null;
@observable consentGiven = false;
@observable number = '';
@observable requestTx = null;
@observable code = '';
@observable isCodeValid = null;
@observable confirmationTx = null;
@computed get isNumberValid () {
return phone.isValidNumber(this.number);
}
@computed get isStepValid () {
if (this.step === DONE) {
return true;
}
if (this.error) {
return false;
}
switch (this.step) {
case LOADING:
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
case QUERY_DATA:
return this.isNumberValid && this.consentGiven;
case QUERY_CODE:
return this.requestTx && this.isCodeValid === true;
case POSTED_CONFIRMATION:
return !!this.confirmationTx;
default:
return false;
}
}
constructor (api, account, isTestnet) {
constructor (api, abi, name, account, isTestnet) {
this.api = api;
this.account = account;
this.isTestnet = isTestnet;
this.step = LOADING;
Contracts.create(api).registry.getContract('smsverification')
.then((contract) => {
this.contract = contract;
Contracts.get().badgeReg.fetchCertifier(name)
.then(({ address }) => {
this.contract = new Contract(api, abi).at(address);
this.load();
})
.catch((err) => {
console.error('error', err);
this.error = 'Failed to fetch the contract: ' + err.message;
});
autorun(() => {
if (this.error) {
console.error('sms verification: ' + this.error);
console.error('verification: ' + this.error);
}
});
}
@ -135,10 +107,6 @@ export default class VerificationStore {
});
}
@action setNumber = (number) => {
this.number = number;
}
@action setConsentGiven = (consentGiven) => {
this.consentGiven = consentGiven;
}
@ -166,19 +134,22 @@ export default class VerificationStore {
});
}
requestValues = () => []
@action sendRequest = () => {
const { api, account, contract, fee, number, hasRequested } = this;
const { api, account, contract, fee, hasRequested } = this;
const request = contract.functions.find((fn) => fn.name === 'request');
const options = { from: account, value: fee.toString() };
const values = this.requestValues();
let chain = Promise.resolve();
if (!hasRequested) {
this.step = POSTING_REQUEST;
chain = request.estimateGas(options, [])
chain = request.estimateGas(options, values)
.then((gas) => {
options.gas = gas.mul(1.2).toFixed(0);
return request.postTransaction(options, []);
return request.postTransaction(options, values);
})
.then((handle) => {
// TODO: The "request rejected" error doesn't have any property to
@ -200,18 +171,15 @@ export default class VerificationStore {
chain
.then(() => {
return api.parity.netChain();
})
.then((chain) => {
this.step = REQUESTING_SMS;
return postToServer({ number, address: account }, this.isTestnet);
this.step = REQUESTING_CODE;
return this.requestCode();
})
.then(() => awaitPuzzle(api, contract, account))
.then(() => {
this.step = QUERY_CODE;
})
.catch((err) => {
this.error = 'Failed to request a confirmation SMS: ' + err.message;
this.error = 'Failed to request a confirmation code: ' + err.message;
});
}

View File

@ -0,0 +1,25 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
.noSpacing {
margin-top: 0;
margin-bottom: 0;
}
.list li {
padding: .1em 0;
}

View File

@ -20,12 +20,27 @@ import DoneIcon from 'material-ui/svg-icons/action/done-all';
import CancelIcon from 'material-ui/svg-icons/content/clear';
import { Button, IdentityIcon, Modal } from '~/ui';
import RadioButtons from '~/ui/Form/RadioButtons';
import { nullableProptype } from '~/util/proptypes';
import styles from './verification.css';
const methods = {
sms: {
label: 'SMS Verification', key: 0, value: 'sms',
description: (<p className={ styles.noSpacing }>It will be stored on the blockchain that you control a phone number (not <em>which</em>).</p>)
},
email: {
label: 'E-mail Verification', key: 1, value: 'email',
description: (<p className={ styles.noSpacing }>The hash of the e-mail address you prove control over will be stored on the blockchain.</p>)
}
};
import {
LOADING,
QUERY_DATA,
POSTING_REQUEST, POSTED_REQUEST,
REQUESTING_SMS, QUERY_CODE,
REQUESTING_CODE, QUERY_CODE,
POSTING_CONFIRMATION, POSTED_CONFIRMATION,
DONE
} from './store';
@ -37,34 +52,44 @@ import SendConfirmation from './SendConfirmation';
import Done from './Done';
@observer
export default class SMSVerification extends Component {
export default class Verification extends Component {
static propTypes = {
store: PropTypes.any.isRequired,
store: nullableProptype(PropTypes.object.isRequired),
account: PropTypes.string.isRequired,
onSelectMethod: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired
}
static phases = { // mapping (store steps -> steps)
[LOADING]: 0,
[QUERY_DATA]: 1,
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_SMS]: 2,
[LOADING]: 1, [QUERY_DATA]: 1,
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_CODE]: 2,
[QUERY_CODE]: 3,
[POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4,
[DONE]: 5
}
state = {
method: 'sms'
};
render () {
const phase = SMSVerification.phases[this.props.store.step];
const { error, isStepValid } = this.props.store;
const { store } = this.props;
let phase = 0; let error = false; let isStepValid = true;
if (store) {
phase = Verification.phases[store.step];
error = store.error;
isStepValid = store.isStepValid;
}
return (
<Modal
actions={ this.renderDialogActions(phase, error, isStepValid) }
title='verify your account via SMS'
title='verify your account'
visible
current={ phase }
steps={ ['Prepare', 'Enter Data', 'Request', 'Enter Code', 'Confirm', 'Done!'] }
waiting={ error ? [] : [ 0, 2, 4 ] }
steps={ ['Method', 'Enter Data', 'Request', 'Enter Code', 'Confirm', 'Done!'] }
waiting={ error ? [] : [ 2, 4 ] }
>
{ this.renderStep(phase, error) }
</Modal>
@ -101,6 +126,13 @@ export default class SMSVerification extends Component {
let action = () => {};
switch (phase) {
case 0:
action = () => {
const { onSelectMethod } = this.props;
const { method } = this.state;
onSelectMethod(method);
};
break;
case 1:
action = store.sendRequest;
break;
@ -133,26 +165,58 @@ export default class SMSVerification extends Component {
return (<p>{ error }</p>);
}
const { method } = this.state;
if (phase === 0) {
const values = Object.values(methods);
const value = values.findIndex((v) => v.value === method);
return (
<RadioButtons
value={ value < 0 ? 0 : value }
values={ values }
onChange={ this.selectMethod }
/>
);
}
const {
step,
fee, number, isNumberValid, isVerified, hasRequested,
fee, isVerified, hasRequested,
requestTx, isCodeValid, confirmationTx,
setCode
} = this.props.store;
switch (phase) {
case 0:
return (
<p>Loading SMS Verification.</p>
);
case 1:
const { setNumber, setConsentGiven } = this.props.store;
if (step === LOADING) {
return (<p>Loading verification data.</p>);
}
const { setConsentGiven } = this.props.store;
const fields = [];
if (method === 'sms') {
fields.push({
key: 'number',
label: 'phone number in international format',
hint: 'the SMS will be sent to this number',
error: this.props.store.isNumberValid ? null : 'invalid number',
onChange: this.props.store.setNumber
});
} else if (method === 'email') {
fields.push({
key: 'email',
label: 'email address',
hint: 'the code will be sent to this address',
error: this.props.store.isEmailValid ? null : 'invalid email',
onChange: this.props.store.setEmail
});
}
return (
<GatherData
fee={ fee } isNumberValid={ isNumberValid }
isVerified={ isVerified } hasRequested={ hasRequested }
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
method={ method } fields={ fields }
fee={ fee } isVerified={ isVerified } hasRequested={ hasRequested }
setConsentGiven={ setConsentGiven }
/>
);
@ -162,9 +226,19 @@ export default class SMSVerification extends Component {
);
case 3:
let receiver, hint;
if (method === 'sms') {
receiver = this.props.store.number;
hint = 'Enter the code you received via SMS.';
} else if (method === 'email') {
receiver = this.props.store.email;
hint = 'Enter the code you received via e-mail.';
}
return (
<QueryCode
number={ number } fee={ fee } isCodeValid={ isCodeValid }
receiver={ receiver }
hint={ hint }
isCodeValid={ isCodeValid }
setCode={ setCode }
/>
);
@ -183,4 +257,8 @@ export default class SMSVerification extends Component {
return null;
}
}
selectMethod = (choice, i) => {
this.setState({ method: choice.value });
}
}

View File

@ -26,7 +26,7 @@ import FirstRun from './FirstRun';
import LoadContract from './LoadContract';
import SaveContract from './SaveContract';
import Shapeshift from './Shapeshift';
import SMSVerification from './SMSVerification';
import Verification from './Verification';
import Transfer from './Transfer';
import PasswordManager from './PasswordManager';
import UpgradeParity from './UpgradeParity';
@ -45,7 +45,7 @@ export {
LoadContract,
SaveContract,
Shapeshift,
SMSVerification,
Verification,
Transfer,
PasswordManager,
UpgradeParity,

View File

@ -23,7 +23,7 @@ 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, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '~/modals';
import { EditMeta, DeleteAccount, Shapeshift, Verification, Transfer, PasswordManager } from '~/modals';
import { Actionbar, Button, Page } from '~/ui';
import shapeshiftBtn from '~/../assets/images/shapeshift-btn.png';
@ -32,7 +32,8 @@ import Header from './Header';
import Transactions from './Transactions';
import { setVisibleAccounts } from '~/redux/providers/personalActions';
import VerificationStore from '~/modals/SMSVerification/store';
import SMSVerificationStore from '~/modals/Verification/sms-store';
import EmailVerificationStore from '~/modals/Verification/email-store';
import styles from './account.css';
@ -72,15 +73,6 @@ class Account extends Component {
if (prevAddress !== nextAddress) {
this.setVisibleAccounts(nextProps);
}
const { isTestnet } = nextProps;
if (typeof isTestnet === 'boolean' && !this.state.verificationStore) {
const { api } = this.context;
const { address } = nextProps.params;
this.setState({
verificationStore: new VerificationStore(api, address, isTestnet)
});
}
}
componentWillUnmount () {
@ -228,8 +220,9 @@ class Account extends Component {
const { address } = this.props.params;
return (
<SMSVerification
<Verification
store={ store } account={ address }
onSelectMethod={ this.selectVerificationMethod }
onClose={ this.onVerificationClose }
/>
);
@ -303,6 +296,22 @@ class Account extends Component {
this.setState({ showVerificationDialog: true });
}
selectVerificationMethod = (name) => {
const { isTestnet } = this.props;
if (typeof isTestnet !== 'boolean' || this.state.verificationStore) return;
const { api } = this.context;
const { address } = this.props.params;
let verificationStore = null;
if (name === 'sms') {
verificationStore = new SMSVerificationStore(api, address, isTestnet);
} else if (name === 'email') {
verificationStore = new EmailVerificationStore(api, address, isTestnet);
}
this.setState({ verificationStore });
}
onVerificationClose = () => {
this.setState({ showVerificationDialog: false });
}

View File

@ -49,7 +49,7 @@ impl str::FromStr for SpecType {
let spec = match s {
"frontier" | "homestead" | "mainnet" => SpecType::Mainnet,
"frontier-dogmatic" | "homestead-dogmatic" | "classic" => SpecType::Classic,
"morden" | "testnet" => SpecType::Testnet,
"morden" | "testnet" | "classic-testnet" => SpecType::Testnet,
"ropsten" => SpecType::Ropsten,
"olympic" => SpecType::Olympic,
"expanse" => SpecType::Expanse,
@ -288,6 +288,8 @@ mod tests {
assert_eq!(SpecType::Testnet, "morden".parse().unwrap());
assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap());
assert_eq!(SpecType::Olympic, "olympic".parse().unwrap());
assert_eq!(SpecType::Classic, "classic".parse().unwrap());
assert_eq!(SpecType::Testnet, "classic-testnet".parse().unwrap());
}
#[test]