diff --git a/Cargo.lock b/Cargo.lock index 52906c509..46ddc5f07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,7 +1249,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#5e3b9629692c550811b228d68ca99d1461a4f6cb" +source = "git+https://github.com/ethcore/js-precompiled.git#b2513e92603b473799d653583bd86771e0063c08" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 97066e40c..9028c4801 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 97066e40ccd061f727deb5cd860e4d9135aa2551 +Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs index 103e750e6..c98d14027 100644 --- a/ethcrypto/src/lib.rs +++ b/ethcrypto/src/lib.rs @@ -34,16 +34,43 @@ pub const KEY_LENGTH: usize = 32; pub const KEY_ITERATIONS: usize = 10240; pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; +#[derive(PartialEq, Debug)] +pub enum ScryptError { + // log(N) < r / 16 + InvalidN, + // p <= (2^31-1 * 32)/(128 * r) + InvalidP, +} + +impl fmt::Display for ScryptError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let s = match *self { + ScryptError::InvalidN => "Invalid N argument of the scrypt encryption" , + ScryptError::InvalidP => "Invalid p argument of the scrypt encryption", + }; + + write!(f, "{}", s) + } +} + #[derive(PartialEq, Debug)] pub enum Error { Secp(SecpError), + Scrypt(ScryptError), InvalidMessage, } +impl From for Error { + fn from(err: ScryptError) -> Self { + Error::Scrypt(err) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { let s = match *self { Error::Secp(ref err) => err.to_string(), + Error::Scrypt(ref err) => err.to_string(), Error::InvalidMessage => "Invalid message".into(), }; @@ -80,13 +107,23 @@ pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec (Vec, Vec) { +pub fn derive_key_scrypt(password: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> Result<(Vec, Vec), Error> { + // sanity checks + let log_n = (32 - n.leading_zeros() - 1) as u8; + if log_n as u32 >= r * 16 { + return Err(Error::Scrypt(ScryptError::InvalidN)); + } + + if p as u64 > ((u32::max_value() as u64 - 1) * 32)/(128 * (r as u64)) { + return Err(Error::Scrypt(ScryptError::InvalidP)); + } + let mut derived_key = vec![0u8; KEY_LENGTH]; - let scrypt_params = ScryptParams::new(n.trailing_zeros() as u8, r, p); + let scrypt_params = ScryptParams::new(log_n, r, p); scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived_key); let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; - (derived_right_bits.to_vec(), derived_left_bits.to_vec()) + Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec())) } pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec { diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index 5dab35251..336e72875 100644 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -113,7 +113,7 @@ impl Crypto { let (derived_left_bits, derived_right_bits) = match self.kdf { Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c), - Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r), + Kdf::Scrypt(ref params) => try!(crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)), }; let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256(); diff --git a/ethstore/src/dir/disk.rs b/ethstore/src/dir/disk.rs index 6616ec15d..56b2c1ccb 100644 --- a/ethstore/src/dir/disk.rs +++ b/ethstore/src/dir/disk.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; use time; use ethkey::Address; use {json, SafeAccount, Error}; +use json::UUID; use super::KeyDirectory; const IGNORED_FILES: &'static [&'static str] = &["thumbs.db", "address_book.json"]; @@ -112,7 +113,7 @@ impl KeyDirectory for DiskDirectory { // build file path let filename = account.filename.as_ref().cloned().unwrap_or_else(|| { let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid."); - format!("UTC--{}Z--{:?}", timestamp, account.address) + format!("UTC--{}Z--{}", timestamp, UUID::from(account.id)) }); // update account filename diff --git a/js/package.json b/js/package.json index c90742c05..af56140b1 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.49", + "version": "0.2.52", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", @@ -139,6 +139,7 @@ "mobx-react": "^3.5.8", "mobx-react-devtools": "^4.2.9", "moment": "^2.14.1", + "phoneformat.js": "^1.0.3", "qs": "^6.3.0", "react": "^15.2.1", "react-ace": "^4.0.0", diff --git a/js/scripts/release.sh b/js/scripts/release.sh index 5e631cf98..3ff4a577c 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -68,11 +68,13 @@ if [ "$BRANCH" == "master" ]; then fi echo "*** Updating cargo parity-ui-precompiled#$PRECOMPILED_HASH" +git submodule update cargo update -p parity-ui-precompiled # --precise "$PRECOMPILED_HASH" echo "*** Committing updated files" -git add . +git add js +git add Cargo.lock git commit -m "[ci skip] js-precompiled $UTCDATE" git push origin HEAD:refs/heads/$BRANCH 2>$GITLOG diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js index 830ca0e21..4cd1c8a56 100644 --- a/js/src/api/format/input.js +++ b/js/src/api/format/input.js @@ -166,3 +166,11 @@ export function inTraceFilter (filterObject) { return filterObject; } + +export function inTraceType (whatTrace) { + if (isString(whatTrace)) { + return [whatTrace]; + } + + return whatTrace; +} diff --git a/js/src/api/format/input.spec.js b/js/src/api/format/input.spec.js index 219886d05..a22c8d131 100644 --- a/js/src/api/format/input.spec.js +++ b/js/src/api/format/input.spec.js @@ -16,7 +16,7 @@ import BigNumber from 'bignumber.js'; -import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions } from './input'; +import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions, inTraceType } from './input'; import { isAddress } from '../../../test/types'; describe('api/format/input', () => { @@ -242,4 +242,16 @@ describe('api/format/input', () => { }); }); }); + + describe('inTraceType', () => { + it('returns array of types as is', () => { + const types = ['vmTrace', 'trace', 'stateDiff']; + expect(inTraceType(types)).to.deep.equal(types); + }); + + it('formats single string type into array', () => { + const type = 'vmTrace'; + expect(inTraceType(type)).to.deep.equal([type]); + }); + }); }); diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 8461df20f..262a275a0 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -254,3 +254,25 @@ export function outTrace (trace) { return trace; } + +export function outTraces (traces) { + if (traces) { + return traces.map(outTrace); + } + + return traces; +} + +export function outTraceReplay (trace) { + if (trace) { + Object.keys(trace).forEach((key) => { + switch (key) { + case 'trace': + trace[key] = outTraces(trace[key]); + break; + } + }); + } + + return trace; +} diff --git a/js/src/api/rpc/trace/trace.e2e.js b/js/src/api/rpc/trace/trace.e2e.js index 1a0720927..88c0988f6 100644 --- a/js/src/api/rpc/trace/trace.e2e.js +++ b/js/src/api/rpc/trace/trace.e2e.js @@ -20,15 +20,25 @@ describe('ethapi.trace', () => { const ethapi = createHttpApi(); describe('block', () => { - it('returns the latest block', () => { - return ethapi.trace.block().then((block) => { - expect(block).to.be.ok; + it('returns the latest block traces', () => { + return ethapi.trace.block().then((traces) => { + expect(traces).to.be.ok; }); }); - it('returns a specified block', () => { - return ethapi.trace.block('0x65432').then((block) => { - expect(block).to.be.ok; + it('returns traces for a specified block', () => { + return ethapi.trace.block('0x65432').then((traces) => { + expect(traces).to.be.ok; + }); + }); + }); + + describe('replayTransaction', () => { + it('returns traces for a specific transaction', () => { + return ethapi.eth.getBlockByNumber().then((latestBlock) => { + return ethapi.trace.replayTransaction(latestBlock.transactions[0]).then((traces) => { + expect(traces).to.be.ok; + }); }); }); }); diff --git a/js/src/api/rpc/trace/trace.js b/js/src/api/rpc/trace/trace.js index 95fed4230..5c693c0b5 100644 --- a/js/src/api/rpc/trace/trace.js +++ b/js/src/api/rpc/trace/trace.js @@ -14,35 +14,53 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { inBlockNumber, inHex, inNumber16, inTraceFilter } from '../../format/input'; -import { outTrace } from '../../format/output'; +import { inBlockNumber, inData, inHex, inNumber16, inOptions, inTraceFilter, inTraceType } from '../../format/input'; +import { outTraces, outTraceReplay } from '../../format/output'; export default class Trace { constructor (transport) { this._transport = transport; } + block (blockNumber = 'latest') { + return this._transport + .execute('trace_block', inBlockNumber(blockNumber)) + .then(outTraces); + } + + call (options, blockNumber = 'latest', whatTrace = ['trace']) { + return this._transport + .execute('trace_call', inOptions(options), inBlockNumber(blockNumber), inTraceType(whatTrace)) + .then(outTraceReplay); + } + filter (filterObj) { return this._transport .execute('trace_filter', inTraceFilter(filterObj)) - .then(traces => traces.map(trace => outTrace(trace))); + .then(outTraces); } get (txHash, position) { return this._transport .execute('trace_get', inHex(txHash), inNumber16(position)) - .then(trace => outTrace(trace)); + .then(outTraces); + } + + rawTransaction (data, whatTrace = ['trace']) { + return this._transport + .execute('trace_rawTransaction', inData(data), inTraceType(whatTrace)) + .then(outTraceReplay); + } + + replayTransaction (txHash, whatTrace = ['trace']) { + return this._transport + .execute('trace_replayTransaction', txHash, inTraceType(whatTrace)) + .then(outTraceReplay); } transaction (txHash) { return this._transport .execute('trace_transaction', inHex(txHash)) - .then(traces => traces.map(trace => outTrace(trace))); - } - - block (blockNumber = 'latest') { - return this._transport - .execute('trace_block', inBlockNumber(blockNumber)) - .then(traces => traces.map(trace => outTrace(trace))); + .then(outTraces); } } diff --git a/js/src/contracts/abi/index.js b/js/src/contracts/abi/index.js index 80f49dc5b..a6a7f0783 100644 --- a/js/src/contracts/abi/index.js +++ b/js/src/contracts/abi/index.js @@ -23,6 +23,7 @@ import githubhint from './githubhint.json'; import owned from './owned.json'; import registry from './registry.json'; import signaturereg from './signaturereg.json'; +import smsverification from './sms-verification.json'; import tokenreg from './tokenreg.json'; import wallet from './wallet.json'; @@ -36,6 +37,7 @@ export { owned, registry, signaturereg, + smsverification, tokenreg, wallet }; diff --git a/js/src/contracts/abi/sms-verification.json b/js/src/contracts/abi/sms-verification.json new file mode 100644 index 000000000..400d22b44 --- /dev/null +++ b/js/src/contracts/abi/sms-verification.json @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"request","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"},{"name":"_puzzle","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":"_new","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes32"}],"name":"confirm","outputs":[],"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":"delegate","outputs":[{"name":"","type":"address"}],"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":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"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"}],"name":"Requested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"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"}] diff --git a/js/src/contracts/contracts.js b/js/src/contracts/contracts.js index a04321c7b..9d745762c 100644 --- a/js/src/contracts/contracts.js +++ b/js/src/contracts/contracts.js @@ -19,6 +19,7 @@ import Registry from './registry'; import SignatureReg from './signaturereg'; import TokenReg from './tokenreg'; import GithubHint from './githubhint'; +import smsVerification from './sms-verification'; let instance = null; @@ -54,6 +55,10 @@ export default class Contracts { return this._githubhint; } + get smsVerification () { + return smsVerification; + } + static create (api) { return new Contracts(api); } diff --git a/js/src/contracts/sms-verification.js b/js/src/contracts/sms-verification.js new file mode 100644 index 000000000..e93d57ffc --- /dev/null +++ b/js/src/contracts/sms-verification.js @@ -0,0 +1,52 @@ +// 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 { stringify } from 'querystring'; + +export const checkIfVerified = (contract, account) => { + return contract.instance.certified.call({}, [account]); +}; + +export const checkIfRequested = (contract, account) => { + return new Promise((resolve, reject) => { + contract.subscribe('Requested', { + fromBlock: 0, toBlock: 'pending' + }, (err, logs) => { + if (err) { + return reject(err); + } + const e = logs.find((l) => { + return l.type === 'mined' && l.params.who && l.params.who.value === account; + }); + resolve(e ? e.transactionHash : false); + }); + }); +}; + +export const postToServer = (query) => { + query = stringify(query); + return fetch('https://sms-verification.parity.io/?' + 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'); + }); + }); +}; diff --git a/js/src/jsonrpc/interfaces/trace.js b/js/src/jsonrpc/interfaces/trace.js index 3dc4451f0..efe45f34e 100644 --- a/js/src/jsonrpc/interfaces/trace.js +++ b/js/src/jsonrpc/interfaces/trace.js @@ -14,9 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { BlockNumber, Hash, Integer } from '../types'; +import { BlockNumber, Data, Hash, Integer } from '../types'; export default { + block: { + desc: 'Returns traces created at given block', + params: [ + { + type: BlockNumber, + desc: 'Integer block number, or \'latest\' for the last mined block or \'pending\', \'earliest\' for not yet mined transactions' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + + call: { + desc: 'Returns traces for a specific call', + params: [ + { + type: Object, + desc: 'Call options' + }, + { + type: BlockNumber, + desc: 'The blockNumber' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + filter: { desc: 'Returns traces matching given filter', params: [ @@ -49,6 +85,42 @@ export default { } }, + rawTransaction: { + desc: 'Traces a call to eth_sendRawTransaction without making the call, returning the traces', + params: [ + { + type: Data, + desc: 'Transaction data' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + + replayTransaction: { + desc: 'Replays a transaction, returning the traces', + params: [ + { + type: Hash, + desc: 'Transaction hash' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + transaction: { desc: 'Returns all traces of given transaction', params: [ @@ -61,19 +133,5 @@ export default { type: Array, desc: 'Traces of given transaction' } - }, - - block: { - desc: 'Returns traces created at given block', - params: [ - { - type: BlockNumber, - desc: 'Integer block number, or \'latest\' for the last mined block or \'pending\', \'earliest\' for not yet mined transactions' - } - ], - returns: { - type: Array, - desc: 'Block traces' - } } }; diff --git a/js/src/modals/SMSVerification/Done/done.css b/js/src/modals/SMSVerification/Done/done.css new file mode 100644 index 000000000..18c410f3e --- /dev/null +++ b/js/src/modals/SMSVerification/Done/done.css @@ -0,0 +1,31 @@ +/* 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 . +*/ +.spacing { + margin-top: 1.5em; +} + +.container { + margin-top: .5em; + display: flex; + align-items: center; +} + +.message { + margin-top: 0; + margin-bottom: 0; + margin-left: .5em; +} diff --git a/js/src/modals/SMSVerification/Done/done.js b/js/src/modals/SMSVerification/Done/done.js new file mode 100644 index 000000000..f3f6fa515 --- /dev/null +++ b/js/src/modals/SMSVerification/Done/done.js @@ -0,0 +1,31 @@ +// 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 } from 'react'; +import SuccessIcon from 'material-ui/svg-icons/navigation/check'; + +import styles from './done.css'; + +export default class Done extends Component { + render () { + return ( +
+ +

Congratulations, your account is verified!

+
+ ); + } +} diff --git a/js/src/modals/SMSVerification/Done/index.js b/js/src/modals/SMSVerification/Done/index.js new file mode 100644 index 000000000..549306dbd --- /dev/null +++ b/js/src/modals/SMSVerification/Done/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 './done'; diff --git a/js/src/modals/SMSVerification/GatherData/gatherData.css b/js/src/modals/SMSVerification/GatherData/gatherData.css new file mode 100644 index 000000000..680986981 --- /dev/null +++ b/js/src/modals/SMSVerification/GatherData/gatherData.css @@ -0,0 +1,49 @@ +/* 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 . +*/ + +.list li { + padding: .1em 0; +} + +.spacing { + margin-top: 1.5em; +} + +.container { + margin-top: .5em; + display: flex; + align-items: center; +} +.message { + margin-top: 0; + margin-bottom: 0; + margin-left: .5em; +} + +.terms { + line-height: 1.3; + opacity: .7; + + ul { + padding-left: 1.5em; + } + + li { + margin-top: .2em; + margin-bottom: .2em; + } +} diff --git a/js/src/modals/SMSVerification/GatherData/gatherData.js b/js/src/modals/SMSVerification/GatherData/gatherData.js new file mode 100644 index 000000000..f4036a3bc --- /dev/null +++ b/js/src/modals/SMSVerification/GatherData/gatherData.js @@ -0,0 +1,151 @@ +// 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 nullable from '../../../util/nullable-proptype'; +import BigNumber from 'bignumber.js'; +import { Checkbox } from 'material-ui'; +import InfoIcon from 'material-ui/svg-icons/action/info-outline'; +import SuccessIcon from 'material-ui/svg-icons/navigation/check'; +import ErrorIcon from 'material-ui/svg-icons/navigation/close'; + +import { fromWei } from '../../../api/util/wei'; +import { Form, Input } from '../../../ui'; + +import terms from '../terms-of-service'; +import styles from './gatherData.css'; + +export default class GatherData extends Component { + static propTypes = { + fee: React.PropTypes.instanceOf(BigNumber), + isNumberValid: PropTypes.bool.isRequired, + isVerified: nullable(PropTypes.bool.isRequired), + hasRequested: nullable(PropTypes.bool.isRequired), + setNumber: PropTypes.func.isRequired, + setConsentGiven: PropTypes.func.isRequired + } + + render () { + const { isNumberValid, isVerified } = this.props; + + return ( +
+

The following steps will let you prove that you control both an account and a phone number.

+
    +
  1. You send a verification request to a specific contract.
  2. +
  3. Our server puts a puzzle into this contract.
  4. +
  5. The code you receive via SMS is the solution to this puzzle.
  6. +
+ { this.renderFee() } + { this.renderCertified() } + { this.renderRequested() } + + +
{ terms }
+ + ); + } + + renderFee () { + const { fee } = this.props; + + if (!fee) { + return (

Fetching the fee…

); + } + return ( +
+ +

The fee is { fromWei(fee).toFixed(3) } ETH.

+
+ ); + } + + renderCertified () { + const { isVerified } = this.props; + + if (isVerified) { + return ( +
+ +

Your account is already verified.

+
+ ); + } else if (isVerified === false) { + return ( +
+ +

Your account is not verified yet.

+
+ ); + } + return ( +

Checking if your account is verified…

+ ); + } + + renderRequested () { + const { isVerified, hasRequested } = this.props; + + // If the account is verified, don't show that it has requested verification. + if (isVerified) { + return null; + } + + if (hasRequested) { + return ( +
+ +

You already requested verification.

+
+ ); + } + if (hasRequested === false) { + return ( +
+ +

You did not request verification yet.

+
+ ); + } + return ( +

Checking if you requested verification…

+ ); + } + + numberOnSubmit = (value) => { + this.props.setNumber(value); + } + + numberOnChange = (_, value) => { + this.props.setNumber(value); + } + + consentOnChange = (_, consentGiven) => { + this.props.setConsentGiven(consentGiven); + } +} diff --git a/js/src/modals/SMSVerification/GatherData/index.js b/js/src/modals/SMSVerification/GatherData/index.js new file mode 100644 index 000000000..1c03d3400 --- /dev/null +++ b/js/src/modals/SMSVerification/GatherData/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 './gatherData'; diff --git a/js/src/modals/SMSVerification/QueryCode/index.js b/js/src/modals/SMSVerification/QueryCode/index.js new file mode 100644 index 000000000..539c340f0 --- /dev/null +++ b/js/src/modals/SMSVerification/QueryCode/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 './queryCode'; diff --git a/js/src/modals/SMSVerification/QueryCode/queryCode.js b/js/src/modals/SMSVerification/QueryCode/queryCode.js new file mode 100644 index 000000000..9598fe358 --- /dev/null +++ b/js/src/modals/SMSVerification/QueryCode/queryCode.js @@ -0,0 +1,52 @@ +// 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 { Form, Input } from '../../../ui'; + +export default class QueryCode extends Component { + static propTypes = { + number: PropTypes.string.isRequired, + isCodeValid: PropTypes.bool.isRequired, + setCode: PropTypes.func.isRequired + } + + render () { + const { number, isCodeValid } = this.props; + + return ( +
+

The verification code has been sent to { number }.

+ +
+ ); + } + + onChange = (_, code) => { + this.props.setCode(code.trim()); + } + + onSubmit = (code) => { + this.props.setCode(code.trim()); + } +} diff --git a/js/src/modals/SMSVerification/SMSVerification.js b/js/src/modals/SMSVerification/SMSVerification.js new file mode 100644 index 000000000..4ec0b608d --- /dev/null +++ b/js/src/modals/SMSVerification/SMSVerification.js @@ -0,0 +1,176 @@ +// 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 { observer } from 'mobx-react'; +import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; +import ContentClear from 'material-ui/svg-icons/content/clear'; + +import { Button, IdentityIcon, Modal } from '../../ui'; + +import { + LOADING, + QUERY_DATA, + POSTING_REQUEST, POSTED_REQUEST, + REQUESTING_SMS, REQUESTED_SMS, + POSTING_CONFIRMATION, POSTED_CONFIRMATION, + DONE +} from './store'; + +import GatherData from './GatherData'; +import SendRequest from './SendRequest'; +import QueryCode from './QueryCode'; +import SendConfirmation from './SendConfirmation'; +import Done from './Done'; + +@observer +export default class SMSVerification extends Component { + static propTypes = { + store: PropTypes.any.isRequired, + account: PropTypes.string.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, + [REQUESTED_SMS]: 3, + [POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4, + [DONE]: 5 + } + + render () { + const phase = SMSVerification.phases[this.props.store.step]; + const { error, isStepValid } = this.props.store; + + return ( + + { this.renderStep(phase, error) } + + ); + } + + renderDialogActions (phase, error, isStepValid) { + const { store, account, onClose } = this.props; + + const cancel = ( +