Backporting to beta (#4175)
* verification: check if server is running (#4140) * verification: check if server is running See also ethcore/email-verification#67c6466 and ethcore/sms-verification#a585e42. * verification: show in the UI if server is running * verification: code style ✨, more i18n * fix i18n key * Optimized hash lookups (#4144) * Optimize hash comparison * Use libc * Ropsten fork detection (#4163) * Stop flickering + added loader in AddressSelector (#4149) * Stop UI flickering + added loader to AddressSelector #4103 * PR Grumbles * Add a password strength component (#4153) * Added new PasswordStrength Component * Added tests * PR Grumbles * icarus -> update, increase web timeout. (#4165) * icarus -> update, increase web timeout. * Fix estimate gas * Fix token images // Error in Contract Queries (#4169) * Fix dapps not loading (#4170) * Add secure to dappsreg * Remove trailing slash // fix dapps
This commit is contained in:
parent
5b30a61158
commit
65594b8865
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -390,6 +390,7 @@ version = "0.1.2"
|
||||
dependencies = [
|
||||
"bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -34,7 +34,7 @@ use endpoint::EndpointPath;
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||
|
||||
const FETCH_TIMEOUT: u64 = 30;
|
||||
const FETCH_TIMEOUT: u64 = 300;
|
||||
|
||||
pub enum ValidatorResponse {
|
||||
Local(LocalPageEndpoint),
|
||||
|
@ -24,7 +24,9 @@
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x3"
|
||||
"networkID" : "0x3",
|
||||
"forkBlock": 333922,
|
||||
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -908,7 +908,7 @@ impl BlockChainClient for Client {
|
||||
|
||||
Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm)
|
||||
.transact(&tx, options.clone())
|
||||
.map(|r| r.exception.is_some())
|
||||
.map(|r| r.exception.is_none())
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
|
@ -189,6 +189,7 @@
|
||||
"valid-url": "1.0.9",
|
||||
"validator": "6.2.0",
|
||||
"web3": "0.17.0-beta",
|
||||
"whatwg-fetch": "2.0.1"
|
||||
"whatwg-fetch": "2.0.1",
|
||||
"zxcvbn": "4.4.1"
|
||||
}
|
||||
}
|
||||
|
13
js/src/3rdparty/email-verification/index.js
vendored
13
js/src/3rdparty/email-verification/index.js
vendored
@ -16,6 +16,19 @@
|
||||
|
||||
import { stringify } from 'querystring';
|
||||
|
||||
export const isServerRunning = (isTestnet = false) => {
|
||||
const port = isTestnet ? 28443 : 18443;
|
||||
return fetch(`https://email-verification.parity.io:${port}/health`, {
|
||||
mode: 'cors', cache: 'no-store'
|
||||
})
|
||||
.then((res) => {
|
||||
return res.ok;
|
||||
})
|
||||
.catch(() => {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export const postToServer = (query, isTestnet = false) => {
|
||||
const port = isTestnet ? 28443 : 18443;
|
||||
query = stringify(query);
|
||||
|
13
js/src/3rdparty/sms-verification/index.js
vendored
13
js/src/3rdparty/sms-verification/index.js
vendored
@ -16,6 +16,19 @@
|
||||
|
||||
import { stringify } from 'querystring';
|
||||
|
||||
export const isServerRunning = (isTestnet = false) => {
|
||||
const port = isTestnet ? 8443 : 443;
|
||||
return fetch(`https://sms-verification.parity.io:${port}/health`, {
|
||||
mode: 'cors', cache: 'no-store'
|
||||
})
|
||||
.then((res) => {
|
||||
return res.ok;
|
||||
})
|
||||
.catch(() => {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export const postToServer = (query, isTestnet = false) => {
|
||||
const port = isTestnet ? 8443 : 443;
|
||||
query = stringify(query);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
module.exports = [
|
||||
{ name: 'basiccoin', entry: 'basiccoin.js', title: 'Basic Token Deployment' },
|
||||
{ name: 'dappreg', entry: 'dappreg.js', title: 'Dapp Registry' },
|
||||
{ name: 'dappreg', entry: 'dappreg.js', title: 'Dapp Registry', secure: true },
|
||||
{ name: 'githubhint', entry: 'githubhint.js', title: 'GitHub Hint', secure: true },
|
||||
{ name: 'localtx', entry: 'localtx.js', title: 'Local transactions Viewer', secure: true },
|
||||
{ name: 'registry', entry: 'registry.js', title: 'Registry' },
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import { IconButton } from 'material-ui';
|
||||
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
|
||||
import ActionAutorenew from 'material-ui/svg-icons/action/autorenew';
|
||||
|
||||
|
@ -88,12 +88,13 @@ export default class RecoveryPhrase extends Component {
|
||||
value={ password2 }
|
||||
onChange={ this.onEditPassword2 } />
|
||||
</div>
|
||||
<Checkbox
|
||||
className={ styles.checkbox }
|
||||
label='Key was created with Parity <1.4.5 on Windows'
|
||||
checked={ windowsPhrase }
|
||||
onCheck={ this.onToggleWindowsPhrase } />
|
||||
</div>
|
||||
<Checkbox
|
||||
className={ styles.checkbox }
|
||||
label='Key was created with Parity <1.4.5 on Windows'
|
||||
checked={ windowsPhrase }
|
||||
onCheck={ this.onToggleWindowsPhrase }
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ActionDone from 'material-ui/svg-icons/action/done';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
@ -100,44 +101,45 @@ export default class CreateAccount extends Component {
|
||||
switch (stage) {
|
||||
case 0:
|
||||
return (
|
||||
<CreationType
|
||||
onChange={ this.onChangeType } />
|
||||
<CreationType onChange={ this.onChangeType } />
|
||||
);
|
||||
|
||||
case 1:
|
||||
if (createType === 'fromNew') {
|
||||
return (
|
||||
<NewAccount
|
||||
onChange={ this.onChangeDetails } />
|
||||
<NewAccount onChange={ this.onChangeDetails } />
|
||||
);
|
||||
} else if (createType === 'fromGeth') {
|
||||
}
|
||||
|
||||
if (createType === 'fromGeth') {
|
||||
return (
|
||||
<NewGeth
|
||||
accounts={ accounts }
|
||||
onChange={ this.onChangeGeth } />
|
||||
onChange={ this.onChangeGeth }
|
||||
/>
|
||||
);
|
||||
} else if (createType === 'fromPhrase') {
|
||||
}
|
||||
|
||||
if (createType === 'fromPhrase') {
|
||||
return (
|
||||
<RecoveryPhrase
|
||||
onChange={ this.onChangeDetails } />
|
||||
<RecoveryPhrase onChange={ this.onChangeDetails } />
|
||||
);
|
||||
} else if (createType === 'fromRaw') {
|
||||
}
|
||||
|
||||
if (createType === 'fromRaw') {
|
||||
return (
|
||||
<RawKey
|
||||
onChange={ this.onChangeDetails } />
|
||||
<RawKey onChange={ this.onChangeDetails } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NewImport
|
||||
onChange={ this.onChangeWallet } />
|
||||
<NewImport onChange={ this.onChangeWallet } />
|
||||
);
|
||||
|
||||
case 2:
|
||||
if (createType === 'fromGeth') {
|
||||
return (
|
||||
<AccountDetailsGeth
|
||||
addresses={ this.state.gethAddresses } />
|
||||
<AccountDetailsGeth addresses={ this.state.gethAddresses } />
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,7 +147,8 @@ export default class CreateAccount extends Component {
|
||||
<AccountDetails
|
||||
address={ this.state.address }
|
||||
name={ this.state.name }
|
||||
phrase={ this.state.phrase } />
|
||||
phrase={ this.state.phrase }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -210,11 +213,14 @@ export default class CreateAccount extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Warning warning={
|
||||
<FormattedMessage
|
||||
id='createAccount.warning.insecurePassword'
|
||||
defaultMessage='It is recommended that a strong password be used to secure your accounts. Empty and trivial passwords are a security risk.' />
|
||||
} />
|
||||
<Warning
|
||||
warning={
|
||||
<FormattedMessage
|
||||
id='createAccount.warning.insecurePassword'
|
||||
defaultMessage='It is recommended that a strong password be used to secure your accounts. Empty and trivial passwords are a security risk.'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -371,7 +377,7 @@ export default class CreateAccount extends Component {
|
||||
}
|
||||
|
||||
onChangeDetails = (canCreate, { name, passwordHint, address, password, phrase, rawKey, windowsPhrase }) => {
|
||||
this.setState({
|
||||
const nextState = {
|
||||
canCreate,
|
||||
name,
|
||||
passwordHint,
|
||||
@ -380,7 +386,9 @@ export default class CreateAccount extends Component {
|
||||
phrase,
|
||||
windowsPhrase: windowsPhrase || false,
|
||||
rawKey
|
||||
});
|
||||
};
|
||||
|
||||
this.setState(nextState);
|
||||
}
|
||||
|
||||
onChangeRaw = (canCreate, rawKey) => {
|
||||
|
@ -23,7 +23,7 @@ import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { newError, openSnackbar } from '~/redux/actions';
|
||||
import { Button, Modal, IdentityName, IdentityIcon } from '~/ui';
|
||||
import { Button, Modal, IdentityName, IdentityIcon, PasswordStrength } from '~/ui';
|
||||
import Form, { Input } from '~/ui/Form';
|
||||
import { CancelIcon, CheckIcon, SendIcon } from '~/ui/Icons';
|
||||
|
||||
@ -120,7 +120,7 @@ class PasswordManager extends Component {
|
||||
}
|
||||
|
||||
renderPage () {
|
||||
const { busy, isRepeatValid, passwordHint } = this.store;
|
||||
const { busy, isRepeatValid, newPassword, passwordHint } = this.store;
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
@ -236,6 +236,8 @@ class PasswordManager extends Component {
|
||||
type='password' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PasswordStrength input={ newPassword } />
|
||||
</div>
|
||||
</Form>
|
||||
</Tab>
|
||||
|
@ -220,13 +220,27 @@ export default class TransferStore {
|
||||
}
|
||||
|
||||
@action _attachWalletOperation = (txhash) => {
|
||||
if (!txhash || /^(0x)?0*$/.test(txhash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ethSubscriptionId = null;
|
||||
|
||||
// Number of blocks left to look-up (unsub after 15 blocks if nothing)
|
||||
let nBlocksLeft = 15;
|
||||
|
||||
return this.api.subscribe('eth_blockNumber', () => {
|
||||
this.api.eth
|
||||
.getTransactionReceipt(txhash)
|
||||
.then((tx) => {
|
||||
if (nBlocksLeft <= 0) {
|
||||
this.api.unsubscribe(ethSubscriptionId);
|
||||
ethSubscriptionId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tx) {
|
||||
nBlocksLeft--;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -239,6 +253,10 @@ export default class TransferStore {
|
||||
this.operation = operations[0];
|
||||
}
|
||||
|
||||
this.api.unsubscribe(ethSubscriptionId);
|
||||
ethSubscriptionId = null;
|
||||
})
|
||||
.catch(() => {
|
||||
this.api.unsubscribe(ethSubscriptionId);
|
||||
ethSubscriptionId = null;
|
||||
});
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { Checkbox } from 'material-ui';
|
||||
import InfoIcon from 'material-ui/svg-icons/action/info-outline';
|
||||
@ -33,10 +34,11 @@ import styles from './gatherData.css';
|
||||
export default class GatherData extends Component {
|
||||
static propTypes = {
|
||||
fee: React.PropTypes.instanceOf(BigNumber),
|
||||
method: PropTypes.string.isRequired,
|
||||
fields: PropTypes.array.isRequired,
|
||||
isVerified: nullableProptype(PropTypes.bool.isRequired),
|
||||
hasRequested: nullableProptype(PropTypes.bool.isRequired),
|
||||
isServerRunning: nullableProptype(PropTypes.bool.isRequired),
|
||||
isVerified: nullableProptype(PropTypes.bool.isRequired),
|
||||
method: PropTypes.string.isRequired,
|
||||
setConsentGiven: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@ -48,13 +50,19 @@ export default class GatherData extends Component {
|
||||
return (
|
||||
<Form>
|
||||
{ howItWorks }
|
||||
{ this.renderServerRunning() }
|
||||
{ this.renderFee() }
|
||||
{ this.renderCertified() }
|
||||
{ this.renderRequested() }
|
||||
{ this.renderFields() }
|
||||
<Checkbox
|
||||
className={ styles.spacing }
|
||||
label={ 'I agree to the terms and conditions below.' }
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.termsOfService'
|
||||
defaultMessage='I agree to the terms and conditions below.'
|
||||
/>
|
||||
}
|
||||
disabled={ isVerified }
|
||||
onCheck={ this.consentOnChange }
|
||||
/>
|
||||
@ -63,6 +71,44 @@ export default class GatherData extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderServerRunning () {
|
||||
const { isServerRunning } = this.props;
|
||||
|
||||
if (isServerRunning) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isServerRunning.true'
|
||||
defaultMessage='The verification server is running.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (isServerRunning === false) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<ErrorIcon />
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isServerRunning.false'
|
||||
defaultMessage='The verification server is not running.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isServerRunning.pending'
|
||||
defaultMessage='Checking if the verification server is running…'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
renderFee () {
|
||||
const { fee } = this.props;
|
||||
|
||||
@ -72,7 +118,15 @@ export default class GatherData extends Component {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<InfoIcon />
|
||||
<p className={ styles.message }>The fee is { fromWei(fee).toFixed(3) } ETH.</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.fee'
|
||||
defaultMessage='The fee is {amount} ETH.'
|
||||
values={ {
|
||||
amount: fromWei(fee).toFixed(3)
|
||||
} }
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -84,19 +138,34 @@ export default class GatherData extends Component {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<ErrorIcon />
|
||||
<p className={ styles.message }>Your account is already verified.</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isVerified.true'
|
||||
defaultMessage='Your account is already verified.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (isVerified === false) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>Your account is not verified yet.</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isVerified.false'
|
||||
defaultMessage='Your account is not verified yet.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className={ styles.message }>Checking if your account is verified…</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.isVerified.pending'
|
||||
defaultMessage='Checking if your account is verified…'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -112,19 +181,34 @@ export default class GatherData extends Component {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<InfoIcon />
|
||||
<p className={ styles.message }>You already requested verification.</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.hasRequested.true'
|
||||
defaultMessage='You already requested verification.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (hasRequested === false) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>You did not request verification yet.</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.hasRequested.false'
|
||||
defaultMessage='You did not request verification yet.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className={ styles.message }>Checking if you requested verification…</p>
|
||||
<p className={ styles.message }>
|
||||
<FormattedMessage
|
||||
id='ui.verification.gatherData.hasRequested.pending'
|
||||
defaultMessage='Checking if you requested verification…'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ 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';
|
||||
import { isServerRunning, postToServer } from '../../3rdparty/email-verification';
|
||||
|
||||
const EMAIL_VERIFICATION = 7; // id in the `BadgeReg.sol` contract
|
||||
|
||||
@ -59,6 +59,10 @@ export default class EmailVerificationStore extends VerificationStore {
|
||||
super(api, EmailVerificationABI, EMAIL_VERIFICATION, account, isTestnet);
|
||||
}
|
||||
|
||||
isServerRunning = () => {
|
||||
return isServerRunning(this.isTestnet);
|
||||
}
|
||||
|
||||
requestValues = () => [ sha3.text(this.email) ]
|
||||
|
||||
@action setEmail = (email) => {
|
||||
|
@ -21,7 +21,7 @@ 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';
|
||||
import { isServerRunning, postToServer } from '../../3rdparty/sms-verification';
|
||||
|
||||
const SMS_VERIFICATION = 0; // id in the `BadgeReg.sol` contract
|
||||
|
||||
@ -58,6 +58,10 @@ export default class SMSVerificationStore extends VerificationStore {
|
||||
super(api, SMSVerificationABI, SMS_VERIFICATION, account, isTestnet);
|
||||
}
|
||||
|
||||
isServerRunning = () => {
|
||||
return isServerRunning(this.isTestnet);
|
||||
}
|
||||
|
||||
@action setNumber = (number) => {
|
||||
this.number = number;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ export default class VerificationStore {
|
||||
@observable fee = null;
|
||||
@observable isVerified = null;
|
||||
@observable hasRequested = null;
|
||||
@observable isServerRunning = null;
|
||||
@observable consentGiven = false;
|
||||
@observable requestTx = null;
|
||||
@observable code = '';
|
||||
@ -73,6 +74,14 @@ export default class VerificationStore {
|
||||
const { contract, account } = this;
|
||||
this.step = LOADING;
|
||||
|
||||
const isServerRunning = this.isServerRunning()
|
||||
.then((isRunning) => {
|
||||
this.isServerRunning = isRunning;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to check if server is running: ' + err.message;
|
||||
});
|
||||
|
||||
const fee = contract.instance.fee.call()
|
||||
.then((fee) => {
|
||||
this.fee = fee;
|
||||
@ -101,7 +110,7 @@ export default class VerificationStore {
|
||||
});
|
||||
|
||||
Promise
|
||||
.all([ fee, isVerified, hasRequested ])
|
||||
.all([ isServerRunning, fee, isVerified, hasRequested ])
|
||||
.then(() => {
|
||||
this.step = QUERY_DATA;
|
||||
});
|
||||
|
@ -94,10 +94,10 @@ class Verification extends Component {
|
||||
return (
|
||||
<Modal
|
||||
actions={ this.renderDialogActions(phase, error, isStepValid) }
|
||||
title='verify your account'
|
||||
visible
|
||||
current={ phase }
|
||||
steps={ ['Method', 'Enter Data', 'Request', 'Enter Code', 'Confirm', 'Done!'] }
|
||||
title='verify your account'
|
||||
visible
|
||||
waiting={ error ? [] : [ 2, 4 ] }
|
||||
>
|
||||
{ this.renderStep(phase, error) }
|
||||
@ -111,8 +111,9 @@ class Verification extends Component {
|
||||
|
||||
const cancel = (
|
||||
<Button
|
||||
key='cancel' label='Cancel'
|
||||
icon={ <CancelIcon /> }
|
||||
key='cancel'
|
||||
label='Cancel'
|
||||
onClick={ onClose }
|
||||
/>
|
||||
);
|
||||
@ -125,9 +126,10 @@ class Verification extends Component {
|
||||
<div>
|
||||
{ cancel }
|
||||
<Button
|
||||
key='done' label='Done'
|
||||
disabled={ !isStepValid }
|
||||
icon={ <DoneIcon /> }
|
||||
key='done'
|
||||
label='Done'
|
||||
onClick={ onClose }
|
||||
/>
|
||||
</div>
|
||||
@ -160,9 +162,15 @@ class Verification extends Component {
|
||||
<div>
|
||||
{ cancel }
|
||||
<Button
|
||||
key='next' label='Next'
|
||||
disabled={ !isStepValid }
|
||||
icon={ <IdentityIcon address={ account } button /> }
|
||||
icon={
|
||||
<IdentityIcon
|
||||
address={ account }
|
||||
button
|
||||
/>
|
||||
}
|
||||
key='next'
|
||||
label='Next'
|
||||
onClick={ action }
|
||||
/>
|
||||
</div>
|
||||
@ -180,16 +188,16 @@ class Verification extends Component {
|
||||
const value = values.findIndex((v) => v.value === method);
|
||||
return (
|
||||
<RadioButtons
|
||||
onChange={ this.selectMethod }
|
||||
value={ value < 0 ? 0 : value }
|
||||
values={ values }
|
||||
onChange={ this.selectMethod }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
step,
|
||||
fee, isVerified, hasRequested,
|
||||
isServerRunning, fee, isVerified, hasRequested,
|
||||
requestTx, isCodeValid, confirmationTx,
|
||||
setCode
|
||||
} = this.store;
|
||||
@ -223,15 +231,21 @@ class Verification extends Component {
|
||||
|
||||
return (
|
||||
<GatherData
|
||||
fee={ fee }
|
||||
hasRequested={ hasRequested }
|
||||
isServerRunning={ isServerRunning }
|
||||
isVerified={ isVerified }
|
||||
method={ method } fields={ fields }
|
||||
fee={ fee } isVerified={ isVerified } hasRequested={ hasRequested }
|
||||
setConsentGiven={ setConsentGiven }
|
||||
/>
|
||||
);
|
||||
|
||||
case 2:
|
||||
return (
|
||||
<SendRequest step={ step } tx={ requestTx } />
|
||||
<SendRequest
|
||||
step={ step }
|
||||
tx={ requestTx }
|
||||
/>
|
||||
);
|
||||
|
||||
case 3:
|
||||
@ -245,16 +259,19 @@ class Verification extends Component {
|
||||
}
|
||||
return (
|
||||
<QueryCode
|
||||
receiver={ receiver }
|
||||
hint={ hint }
|
||||
isCodeValid={ isCodeValid }
|
||||
receiver={ receiver }
|
||||
setCode={ setCode }
|
||||
/>
|
||||
);
|
||||
|
||||
case 4:
|
||||
return (
|
||||
<SendConfirmation step={ step } tx={ confirmationTx } />
|
||||
<SendConfirmation
|
||||
step={ step }
|
||||
tx={ confirmationTx }
|
||||
/>
|
||||
);
|
||||
|
||||
case 5:
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
transition: transform ease-out 0.1s;
|
||||
transform: scale(1);
|
||||
overflow: hidden;
|
||||
|
||||
&.copied {
|
||||
animation-duration: 0.25s;
|
||||
|
@ -62,11 +62,15 @@ class Balance extends Component {
|
||||
value = api.util.fromWei(balance.value).toFormat(3);
|
||||
}
|
||||
|
||||
let imagesrc = token.image;
|
||||
if (!imagesrc) {
|
||||
imagesrc = images[token.address]
|
||||
? `${api.dappsUrl}${images[token.address]}`
|
||||
: unknownImage;
|
||||
const imageurl = token.image || images[token.address];
|
||||
let imagesrc = unknownImage;
|
||||
|
||||
if (imageurl) {
|
||||
const host = /^(\/)?api/.test(imageurl)
|
||||
? api.dappsUrl
|
||||
: '';
|
||||
|
||||
imagesrc = `${host}${imageurl}`;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -15,6 +15,22 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.outerInput {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
right: 9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
box-sizing: border-box;
|
||||
appearance: textfield;
|
||||
@ -58,13 +74,13 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
margin: 1rem 2.5rem 0.25em;
|
||||
margin: 1rem 0.5rem 0.25em;
|
||||
color: rgba(255, 255, 255, 0.498039);
|
||||
}
|
||||
|
||||
.underline {
|
||||
position: relative;
|
||||
margin: 0 9rem 0 2.5rem;
|
||||
margin: 0 0.5rem 0 0.5rem;
|
||||
}
|
||||
|
||||
.empty {
|
||||
@ -78,7 +94,7 @@
|
||||
|
||||
.input {
|
||||
font-size: 1.5em;
|
||||
padding: 0 9rem 0.5em 2.5rem;
|
||||
padding: 0 9rem 0.5em 0.5rem;
|
||||
display: block;
|
||||
|
||||
padding-right: 6rem;
|
||||
@ -92,7 +108,7 @@
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
margin: 2rem 2rem 0;
|
||||
margin: 2rem 0 0;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
@ -107,8 +123,11 @@
|
||||
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
font-size: 1.5em;
|
||||
font-color: white;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.cards {
|
||||
|
@ -25,6 +25,7 @@ import TextFieldUnderline from 'material-ui/TextField/TextFieldUnderline';
|
||||
|
||||
import AccountCard from '~/ui/AccountCard';
|
||||
import InputAddress from '~/ui/Form/InputAddress';
|
||||
import Loading from '~/ui/Loading';
|
||||
import Portal from '~/ui/Portal';
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
import { validateAddress } from '~/util/validation';
|
||||
@ -130,7 +131,7 @@ class AddressSelect extends Component {
|
||||
const input = (
|
||||
<InputAddress
|
||||
accountsInfo={ accountsInfo }
|
||||
allowCopy={ allowCopy }
|
||||
allowCopy={ (disabled || readOnly) && allowCopy ? allowCopy : false }
|
||||
className={ className }
|
||||
disabled={ disabled || readOnly }
|
||||
error={ error }
|
||||
@ -182,17 +183,18 @@ class AddressSelect extends Component {
|
||||
<label className={ styles.label } htmlFor={ id }>
|
||||
{ label }
|
||||
</label>
|
||||
<input
|
||||
id={ id }
|
||||
className={ styles.input }
|
||||
placeholder={ ilHint }
|
||||
|
||||
onBlur={ this.handleInputBlur }
|
||||
onFocus={ this.handleInputFocus }
|
||||
onChange={ this.handleChange }
|
||||
|
||||
ref={ this.setInputRef }
|
||||
/>
|
||||
<div className={ styles.outerInput }>
|
||||
<input
|
||||
id={ id }
|
||||
className={ styles.input }
|
||||
placeholder={ ilHint }
|
||||
onBlur={ this.handleInputBlur }
|
||||
onFocus={ this.handleInputFocus }
|
||||
onChange={ this.handleChange }
|
||||
ref={ this.setInputRef }
|
||||
/>
|
||||
{ this.renderLoader() }
|
||||
</div>
|
||||
|
||||
<div className={ styles.underline }>
|
||||
<TextFieldUnderline
|
||||
@ -210,6 +212,19 @@ class AddressSelect extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderLoader () {
|
||||
if (!this.store.loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Loading
|
||||
className={ styles.loader }
|
||||
size={ 0.5 }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderCurrentInput () {
|
||||
const { inputValue } = this.state;
|
||||
|
||||
@ -304,7 +319,9 @@ class AddressSelect extends Component {
|
||||
|
||||
return (
|
||||
<div className={ styles.category } key={ `${key}_${index}` }>
|
||||
<div className={ styles.title }>{ label }</div>
|
||||
<div className={ styles.title }>
|
||||
<h3>{ label }</h3>
|
||||
</div>
|
||||
{ content }
|
||||
</div>
|
||||
);
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from 'react';
|
||||
import { observable, action } from 'mobx';
|
||||
import { observable, action, transaction } from 'mobx';
|
||||
import { flatMap, uniqBy } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
@ -26,6 +26,7 @@ const ZERO = /^(0x)?0*$/;
|
||||
|
||||
export default class AddressSelectStore {
|
||||
|
||||
@observable loading = false;
|
||||
@observable values = [];
|
||||
@observable registryValues = [];
|
||||
|
||||
@ -224,21 +225,28 @@ export default class AddressSelectStore {
|
||||
};
|
||||
});
|
||||
|
||||
// Registries Lookup
|
||||
this.registryValues = [];
|
||||
// Clear the previous results after 50ms
|
||||
// if still fetching
|
||||
const timeoutId = setTimeout(() => {
|
||||
transaction(() => {
|
||||
this.registryValues = [];
|
||||
this.loading = true;
|
||||
});
|
||||
}, 50);
|
||||
|
||||
const lookups = this.regLookups.map((regLookup) => regLookup(value));
|
||||
|
||||
Promise
|
||||
// Registries Lookup
|
||||
return Promise
|
||||
.all(lookups)
|
||||
.then((results) => {
|
||||
return results
|
||||
.filter((result) => result && !ZERO.test(result.address));
|
||||
})
|
||||
.then((results) => {
|
||||
results = uniqBy(results, (result) => result.address);
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
this.registryValues = results
|
||||
const registryValues = uniqBy(results, (result) => result.address)
|
||||
.map((result) => {
|
||||
const lowercaseAddress = result.address.toLowerCase();
|
||||
|
||||
@ -253,6 +261,11 @@ export default class AddressSelectStore {
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
transaction(() => {
|
||||
this.loading = false;
|
||||
this.registryValues = registryValues;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ class InputAddress extends Component {
|
||||
|
||||
const props = {};
|
||||
|
||||
if (!readOnly && !disabled) {
|
||||
if (!disabled) {
|
||||
props.focused = focused;
|
||||
}
|
||||
|
||||
|
17
js/src/ui/Form/PasswordStrength/index.js
Normal file
17
js/src/ui/Form/PasswordStrength/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 default from './passwordStrength';
|
31
js/src/ui/Form/PasswordStrength/passwordStrength.css
Normal file
31
js/src/ui/Form/PasswordStrength/passwordStrength.css
Normal file
@ -0,0 +1,31 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
.strength {
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.label {
|
||||
user-select: none;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.498039);
|
||||
}
|
125
js/src/ui/Form/PasswordStrength/passwordStrength.js
Normal file
125
js/src/ui/Form/PasswordStrength/passwordStrength.js
Normal file
@ -0,0 +1,125 @@
|
||||
// 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 React, { Component, PropTypes } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import { LinearProgress } from 'material-ui';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
|
||||
import styles from './passwordStrength.css';
|
||||
|
||||
const BAR_STYLE = {
|
||||
borderRadius: 1,
|
||||
height: 7,
|
||||
marginTop: '0.5em'
|
||||
};
|
||||
|
||||
export default class PasswordStrength extends Component {
|
||||
static propTypes = {
|
||||
input: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
strength: null
|
||||
};
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.updateStrength = debounce(this._updateStrength, 50, { leading: true });
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.updateStrength(this.props.input);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.input !== this.props.input) {
|
||||
this.updateStrength(nextProps.input);
|
||||
}
|
||||
}
|
||||
|
||||
_updateStrength (input = '') {
|
||||
const strength = zxcvbn(input);
|
||||
this.setState({ strength });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { strength } = this.state;
|
||||
|
||||
if (!strength) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { score, feedback } = strength;
|
||||
|
||||
// Score is between 0 and 4
|
||||
const value = score * 100 / 5 + 20;
|
||||
const color = this.getStrengthBarColor(score);
|
||||
|
||||
return (
|
||||
<div className={ styles.strength }>
|
||||
<label className={ styles.label }>
|
||||
<FormattedMessage
|
||||
id='ui.passwordStrength.label'
|
||||
defaultMessage='password strength'
|
||||
/>
|
||||
</label>
|
||||
<LinearProgress
|
||||
color={ color }
|
||||
mode='determinate'
|
||||
style={ BAR_STYLE }
|
||||
value={ value }
|
||||
/>
|
||||
<div className={ styles.feedback }>
|
||||
{ this.renderFeedback(feedback) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Note that the suggestions are in english, thus it wouldn't
|
||||
// make sense to add translations to surrounding words
|
||||
renderFeedback (feedback = {}) {
|
||||
const { suggestions = [] } = feedback;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{ suggestions.join(' ') }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getStrengthBarColor (score) {
|
||||
switch (score) {
|
||||
case 4:
|
||||
case 3:
|
||||
return 'lightgreen';
|
||||
|
||||
case 2:
|
||||
return 'yellow';
|
||||
|
||||
case 1:
|
||||
return 'orange';
|
||||
|
||||
default:
|
||||
return 'red';
|
||||
}
|
||||
}
|
||||
}
|
62
js/src/ui/Form/PasswordStrength/passwordStrength.spec.js
Normal file
62
js/src/ui/Form/PasswordStrength/passwordStrength.spec.js
Normal file
@ -0,0 +1,62 @@
|
||||
// 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import PasswordStrength from './passwordStrength';
|
||||
|
||||
const INPUT_A = 'l33t_test';
|
||||
const INPUT_B = 'Fu£dk;s$%kdlaOe9)_';
|
||||
const INPUT_NULL = '';
|
||||
|
||||
function render (props) {
|
||||
return shallow(
|
||||
<PasswordStrength { ...props } />
|
||||
).shallow();
|
||||
}
|
||||
|
||||
describe('ui/Form/PasswordStrength', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders', () => {
|
||||
expect(render({ input: INPUT_A })).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders a linear progress', () => {
|
||||
expect(render({ input: INPUT_A }).find('LinearProgress')).to.be.ok;
|
||||
});
|
||||
|
||||
describe('compute strength', () => {
|
||||
it('has low score with empty input', () => {
|
||||
expect(
|
||||
render({ input: INPUT_NULL }).find('LinearProgress').props().value
|
||||
).to.equal(20);
|
||||
});
|
||||
|
||||
it('has medium score', () => {
|
||||
expect(
|
||||
render({ input: INPUT_A }).find('LinearProgress').props().value
|
||||
).to.equal(60);
|
||||
});
|
||||
|
||||
it('has high score', () => {
|
||||
expect(
|
||||
render({ input: INPUT_B }).find('LinearProgress').props().value
|
||||
).to.equal(100);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -71,7 +71,9 @@ export default class TypedInput extends Component {
|
||||
const { isEth, value } = this.props;
|
||||
|
||||
if (typeof isEth === 'boolean' && value) {
|
||||
const ethValue = isEth ? fromWei(value) : value;
|
||||
// Remove formatting commas
|
||||
const sanitizedValue = typeof value === 'string' ? value.replace(/,/g, '') : value;
|
||||
const ethValue = isEth ? fromWei(sanitizedValue) : value;
|
||||
this.setState({ isEth, ethValue });
|
||||
}
|
||||
}
|
||||
@ -393,7 +395,9 @@ export default class TypedInput extends Component {
|
||||
return this.setState({ isEth: !isEth });
|
||||
}
|
||||
|
||||
const value = isEth ? toWei(ethValue) : fromWei(ethValue);
|
||||
// Remove formatting commas
|
||||
const sanitizedValue = typeof ethValue === 'string' ? ethValue.replace(/,/g, '') : ethValue;
|
||||
const value = isEth ? toWei(sanitizedValue) : fromWei(sanitizedValue);
|
||||
this.setState({ isEth: !isEth, ethValue: value }, () => {
|
||||
this.onEthValueChange(null, value);
|
||||
});
|
||||
|
@ -21,15 +21,22 @@ import styles from './loading.css';
|
||||
|
||||
export default class Loading extends Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
size: PropTypes.number
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
size: 2
|
||||
};
|
||||
|
||||
render () {
|
||||
const size = (this.props.size || 2) * 60;
|
||||
const { className, size } = this.props;
|
||||
const computedSize = size * 60;
|
||||
|
||||
return (
|
||||
<div className={ styles.loading }>
|
||||
<CircularProgress size={ size } />
|
||||
<div className={ [ styles.loading, className ].join(' ') }>
|
||||
<CircularProgress size={ computedSize } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -68,6 +68,9 @@ $top: 20vh;
|
||||
opacity: 0;
|
||||
z-index: -10;
|
||||
|
||||
padding: 1em;
|
||||
box-sizing: border-box;
|
||||
|
||||
* {
|
||||
min-width: 0;
|
||||
}
|
||||
@ -83,6 +86,7 @@ $top: 20vh;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
font-size: 4em;
|
||||
z-index: 100;
|
||||
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.25s;
|
||||
|
@ -43,6 +43,7 @@ import Modal, { Busy as BusyStep, Completed as CompletedStep } from './Modal';
|
||||
import muiTheme from './Theme';
|
||||
import Page from './Page';
|
||||
import ParityBackground from './ParityBackground';
|
||||
import PasswordStrength from './Form/PasswordStrength';
|
||||
import ShortenedHash from './ShortenedHash';
|
||||
import SignerIcon from './SignerIcon';
|
||||
import Tags from './Tags';
|
||||
@ -91,6 +92,7 @@ export {
|
||||
muiTheme,
|
||||
Page,
|
||||
ParityBackground,
|
||||
PasswordStrength,
|
||||
RadioButtons,
|
||||
ShortenedHash,
|
||||
Select,
|
||||
|
@ -43,7 +43,7 @@ module.exports = {
|
||||
index: './index.js'
|
||||
}),
|
||||
output: {
|
||||
publicPath: '/',
|
||||
// publicPath: '/',
|
||||
path: path.join(__dirname, '../', DEST),
|
||||
filename: '[name].[hash:10].js'
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ bigint = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
heapsize = "0.3"
|
||||
rand = "0.3.12"
|
||||
libc = "0.2"
|
||||
|
||||
[features]
|
||||
x64asm_arithmetic=[]
|
||||
|
@ -26,6 +26,7 @@ use rand::Rng;
|
||||
use rand::os::OsRng;
|
||||
use rustc_serialize::hex::{FromHex, FromHexError};
|
||||
use bigint::{Uint, U256};
|
||||
use libc::{c_void, memcmp};
|
||||
|
||||
/// Trait for a fixed-size byte array to be used as the output of hash functions.
|
||||
pub trait FixedHash: Sized {
|
||||
@ -214,25 +215,16 @@ macro_rules! impl_hash {
|
||||
|
||||
impl PartialEq for $from {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
for i in 0..$size {
|
||||
if self.0[i] != other.0[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
unsafe { memcmp(self.0.as_ptr() as *const c_void, other.0.as_ptr() as *const c_void, $size) == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $from {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
for i in 0..$size {
|
||||
if self.0[i] > other.0[i] {
|
||||
return Ordering::Greater;
|
||||
} else if self.0[i] < other.0[i] {
|
||||
return Ordering::Less;
|
||||
}
|
||||
}
|
||||
Ordering::Equal
|
||||
let r = unsafe { memcmp(self.0.as_ptr() as *const c_void, other.0.as_ptr() as *const c_void, $size) };
|
||||
if r < 0 { return Ordering::Less }
|
||||
if r > 0 { return Ordering::Greater }
|
||||
return Ordering::Equal;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
extern crate rand;
|
||||
extern crate rustc_serialize;
|
||||
extern crate bigint;
|
||||
extern crate libc;
|
||||
#[macro_use] extern crate heapsize;
|
||||
|
||||
pub mod hash;
|
||||
|
Loading…
Reference in New Issue
Block a user