Merge remote-tracking branch 'origin/master' into check-updates
This commit is contained in:
commit
44eda379ad
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1250,7 +1250,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/js-precompiled.git#b2513e92603b473799d653583bd86771e0063c08"
|
source = "git+https://github.com/ethcore/js-precompiled.git#427319583ccde288ba26728c14384392ddbba93d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -182,8 +182,8 @@
|
|||||||
"enode://89d5dc2a81e574c19d0465f497c1af96732d1b61a41de89c2a37f35707689ac416529fae1038809852b235c2d30fd325abdc57c122feeefbeaaf802cc7e9580d@45.55.33.62:30303",
|
"enode://89d5dc2a81e574c19d0465f497c1af96732d1b61a41de89c2a37f35707689ac416529fae1038809852b235c2d30fd325abdc57c122feeefbeaaf802cc7e9580d@45.55.33.62:30303",
|
||||||
"enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303",
|
"enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303",
|
||||||
"enode://016b20125f447a3b203a3cae953b2ede8ffe51290c071e7599294be84317635730c397b8ff74404d6be412d539ee5bb5c3c700618723d3b53958c92bd33eaa82@159.203.210.80:30303",
|
"enode://016b20125f447a3b203a3cae953b2ede8ffe51290c071e7599294be84317635730c397b8ff74404d6be412d539ee5bb5c3c700618723d3b53958c92bd33eaa82@159.203.210.80:30303",
|
||||||
"enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@10.6.6.117:30303",
|
"enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303",
|
||||||
"enode://fe11ef89fc5ac9da358fc160857855f25bbf9e332c79b9ca7089330c02b728b2349988c6062f10982041702110745e203d26975a6b34bcc97144f9fe439034e8@10.1.72.117:30303"
|
"enode://8d91c8137890d29110b9463882f17ae4e279cd2c90cf56573187ed1c8546fca5f590a9f05e9f108eb1bd91767ed01ede4daad9e001b61727885eaa246ddb39c2@163.172.171.38:30303"
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -21,6 +21,7 @@ use util::*;
|
|||||||
use util::using_queue::{UsingQueue, GetAction};
|
use util::using_queue::{UsingQueue, GetAction};
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use views::{BlockView, HeaderView};
|
use views::{BlockView, HeaderView};
|
||||||
|
use header::Header;
|
||||||
use state::{State, CleanupMode};
|
use state::{State, CleanupMode};
|
||||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||||
use executive::contract_address;
|
use executive::contract_address;
|
||||||
@ -572,7 +573,16 @@ impl Miner {
|
|||||||
|
|
||||||
let schedule = chain.latest_schedule();
|
let schedule = chain.latest_schedule();
|
||||||
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
||||||
|
let best_block_header: Header = ::rlp::decode(&chain.best_block_header());
|
||||||
transactions.into_iter()
|
transactions.into_iter()
|
||||||
|
.filter(|tx| match self.engine.verify_transaction_basic(tx, &best_block_header) {
|
||||||
|
Ok(()) => true,
|
||||||
|
Err(e) => {
|
||||||
|
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
.map(|tx| match origin {
|
.map(|tx| match origin {
|
||||||
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
||||||
transaction_queue.add(tx, origin, &fetch_account, &gas_required)
|
transaction_queue.add(tx, origin, &fetch_account, &gas_required)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "0.2.52",
|
"version": "0.2.54",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
@ -102,9 +102,10 @@
|
|||||||
"postcss-nested": "^1.0.0",
|
"postcss-nested": "^1.0.0",
|
||||||
"postcss-simple-vars": "^3.0.0",
|
"postcss-simple-vars": "^3.0.0",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"react-addons-test-utils": "^15.3.0",
|
"react-addons-test-utils": "~15.3.2",
|
||||||
"react-copy-to-clipboard": "^4.2.3",
|
"react-copy-to-clipboard": "^4.2.3",
|
||||||
"react-hot-loader": "^1.3.0",
|
"react-dom": "~15.3.2",
|
||||||
|
"react-hot-loader": "~1.3.0",
|
||||||
"rucksack-css": "^0.8.6",
|
"rucksack-css": "^0.8.6",
|
||||||
"sinon": "^1.17.4",
|
"sinon": "^1.17.4",
|
||||||
"sinon-as-promised": "^4.0.2",
|
"sinon-as-promised": "^4.0.2",
|
||||||
@ -114,7 +115,7 @@
|
|||||||
"webpack": "^1.13.2",
|
"webpack": "^1.13.2",
|
||||||
"webpack-dev-server": "^1.15.2",
|
"webpack-dev-server": "^1.15.2",
|
||||||
"webpack-error-notification": "0.1.6",
|
"webpack-error-notification": "0.1.6",
|
||||||
"webpack-hot-middleware": "^2.7.1",
|
"webpack-hot-middleware": "~2.13.2",
|
||||||
"websocket": "^1.0.23"
|
"websocket": "^1.0.23"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -134,7 +135,7 @@
|
|||||||
"js-sha3": "^0.5.2",
|
"js-sha3": "^0.5.2",
|
||||||
"lodash": "^4.11.1",
|
"lodash": "^4.11.1",
|
||||||
"marked": "^0.3.6",
|
"marked": "^0.3.6",
|
||||||
"material-ui": "^0.16.1",
|
"material-ui": "0.16.1",
|
||||||
"material-ui-chip-input": "^0.8.0",
|
"material-ui-chip-input": "^0.8.0",
|
||||||
"mobx": "^2.6.1",
|
"mobx": "^2.6.1",
|
||||||
"mobx-react": "^3.5.8",
|
"mobx-react": "^3.5.8",
|
||||||
@ -142,16 +143,16 @@
|
|||||||
"moment": "^2.14.1",
|
"moment": "^2.14.1",
|
||||||
"phoneformat.js": "^1.0.3",
|
"phoneformat.js": "^1.0.3",
|
||||||
"qs": "^6.3.0",
|
"qs": "^6.3.0",
|
||||||
"react": "^15.2.1",
|
"react": "~15.3.2",
|
||||||
"react-ace": "^4.0.0",
|
"react-ace": "^4.0.0",
|
||||||
"react-addons-css-transition-group": "^15.2.1",
|
"react-addons-css-transition-group": "~15.3.2",
|
||||||
"react-chartjs-2": "^1.5.0",
|
"react-chartjs-2": "^1.5.0",
|
||||||
"react-dom": "^15.2.1",
|
"react-dom": "~15.3.2",
|
||||||
"react-dropzone": "^3.7.3",
|
"react-dropzone": "^3.7.3",
|
||||||
"react-redux": "^4.4.5",
|
"react-redux": "^4.4.5",
|
||||||
"react-router": "^2.6.1",
|
"react-router": "^2.6.1",
|
||||||
"react-router-redux": "^4.0.5",
|
"react-router-redux": "^4.0.5",
|
||||||
"react-tap-event-plugin": "^1.0.0",
|
"react-tap-event-plugin": "~1.0.0",
|
||||||
"react-tooltip": "^2.0.3",
|
"react-tooltip": "^2.0.3",
|
||||||
"recharts": "^0.15.2",
|
"recharts": "^0.15.2",
|
||||||
"redux": "^3.5.2",
|
"redux": "^3.5.2",
|
||||||
|
@ -15,12 +15,17 @@
|
|||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.container {
|
.body {
|
||||||
|
text-align: center;
|
||||||
background: #333;
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 4em 0;
|
padding: 4em 0;
|
||||||
text-align: center;
|
margin: 0 0 2em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
@ -98,7 +103,7 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
background: #eee;
|
background: #eee;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 5px;
|
border-radius: 0.5em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -113,20 +118,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hashError, .hashWarning, .hashOk {
|
.hashError, .hashWarning, .hashOk {
|
||||||
padding-top: 0.5em;
|
margin: 0.5em 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
padding: 1em 0;
|
||||||
|
border: 0.25em solid #333;
|
||||||
|
border-radius: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hashError {
|
.hashError {
|
||||||
|
border-color: #f66;
|
||||||
color: #f66;
|
color: #f66;
|
||||||
|
background: rgba(255, 102, 102, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hashWarning {
|
.hashWarning {
|
||||||
|
border-color: #f80;
|
||||||
color: #f80;
|
color: #f80;
|
||||||
|
background: rgba(255, 236, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hashOk {
|
.hashOk {
|
||||||
opacity: 0.5;
|
border-color: #6f6;
|
||||||
|
color: #6f6;
|
||||||
|
background: rgba(102, 255, 102, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeButtons {
|
.typeButtons {
|
||||||
|
@ -19,6 +19,7 @@ import React, { Component } from 'react';
|
|||||||
import { api } from '../parity';
|
import { api } from '../parity';
|
||||||
import { attachInterface } from '../services';
|
import { attachInterface } from '../services';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
|
import Events from '../Events';
|
||||||
import IdentityIcon from '../IdentityIcon';
|
import IdentityIcon from '../IdentityIcon';
|
||||||
import Loading from '../Loading';
|
import Loading from '../Loading';
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ import styles from './application.css';
|
|||||||
const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
||||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
|
let nextEventId = 0;
|
||||||
|
|
||||||
export default class Application extends Component {
|
export default class Application extends Component {
|
||||||
state = {
|
state = {
|
||||||
fromAddress: null,
|
fromAddress: null,
|
||||||
@ -43,7 +46,9 @@ export default class Application extends Component {
|
|||||||
registerState: '',
|
registerState: '',
|
||||||
registerType: 'file',
|
registerType: 'file',
|
||||||
repo: '',
|
repo: '',
|
||||||
repoError: null
|
repoError: null,
|
||||||
|
events: {},
|
||||||
|
eventIds: []
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@ -75,7 +80,7 @@ export default class Application extends Component {
|
|||||||
let hashClass = null;
|
let hashClass = null;
|
||||||
if (contentHashError) {
|
if (contentHashError) {
|
||||||
hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning;
|
hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning;
|
||||||
} else {
|
} else if (contentHash) {
|
||||||
hashClass = styles.hashOk;
|
hashClass = styles.hashOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,29 +121,34 @@ export default class Application extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.container }>
|
<div className={ styles.body }>
|
||||||
<div className={ styles.form }>
|
<div className={ styles.container }>
|
||||||
<div className={ styles.typeButtons }>
|
<div className={ styles.form }>
|
||||||
<Button
|
<div className={ styles.typeButtons }>
|
||||||
disabled={ registerBusy }
|
<Button
|
||||||
invert={ registerType !== 'file' }
|
disabled={ registerBusy }
|
||||||
onClick={ this.onClickTypeNormal }>File Link</Button>
|
invert={ registerType !== 'file' }
|
||||||
<Button
|
onClick={ this.onClickTypeNormal }>File Link</Button>
|
||||||
disabled={ registerBusy }
|
<Button
|
||||||
invert={ registerType !== 'content' }
|
disabled={ registerBusy }
|
||||||
onClick={ this.onClickTypeContent }>Content Bundle</Button>
|
invert={ registerType !== 'content' }
|
||||||
</div>
|
onClick={ this.onClickTypeContent }>Content Bundle</Button>
|
||||||
<div className={ styles.box }>
|
|
||||||
<div className={ styles.description }>
|
|
||||||
Provide a valid URL to register. The content information can be used in other contracts that allows for reverse lookups, e.g. image registries, dapp registries, etc.
|
|
||||||
</div>
|
</div>
|
||||||
{ valueInputs }
|
<div className={ styles.box }>
|
||||||
<div className={ hashClass }>
|
<div className={ styles.description }>
|
||||||
{ contentHashError || contentHash }
|
Provide a valid URL to register. The content information can be used in other contracts that allows for reverse lookups, e.g. image registries, dapp registries, etc.
|
||||||
|
</div>
|
||||||
|
{ valueInputs }
|
||||||
|
<div className={ hashClass }>
|
||||||
|
{ contentHashError || contentHash }
|
||||||
|
</div>
|
||||||
|
{ registerBusy ? this.renderProgress() : this.renderButtons() }
|
||||||
</div>
|
</div>
|
||||||
{ registerBusy ? this.renderProgress() : this.renderButtons() }
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Events
|
||||||
|
eventIds={ this.state.eventIds }
|
||||||
|
events={ this.state.events } />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -285,15 +295,29 @@ export default class Application extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trackRequest (promise) {
|
trackRequest (eventId, promise) {
|
||||||
return promise
|
return promise
|
||||||
.then((signerRequestId) => {
|
.then((signerRequestId) => {
|
||||||
this.setState({ signerRequestId, registerState: 'Transaction posted, Waiting for transaction authorization' });
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
signerRequestId,
|
||||||
|
registerState: 'Transaction posted, Waiting for transaction authorization'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
return api.pollMethod('parity_checkRequest', signerRequestId);
|
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||||
})
|
})
|
||||||
.then((txHash) => {
|
.then((txHash) => {
|
||||||
this.setState({ txHash, registerState: 'Transaction authorized, Waiting for network confirmations' });
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
txHash,
|
||||||
|
registerState: 'Transaction authorized, Waiting for network confirmations'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
return api.pollMethod('eth_getTransactionReceipt', txHash, (receipt) => {
|
return api.pollMethod('eth_getTransactionReceipt', txHash, (receipt) => {
|
||||||
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
||||||
@ -304,27 +328,72 @@ export default class Application extends Component {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((txReceipt) => {
|
.then((txReceipt) => {
|
||||||
this.setState({ txReceipt, registerBusy: false, registerState: 'Network confirmed, Received transaction receipt', url: '', commit: '', repo: '', commitError: null, contentHash: '', contentHashOwner: null, contentHashError: null });
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
txReceipt,
|
||||||
|
registerBusy: false,
|
||||||
|
registerState: 'Network confirmed, Received transaction receipt'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('onSend', error);
|
console.error('onSend', error);
|
||||||
this.setState({ registerError: error.message });
|
|
||||||
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
registerState: error.message,
|
||||||
|
registerError: true,
|
||||||
|
registerBusy: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerContent (repo, commit) {
|
registerContent (contentRepo, contentCommit) {
|
||||||
const { contentHash, fromAddress, instance } = this.state;
|
const { contentHash, fromAddress, instance } = this.state;
|
||||||
|
contentCommit = contentCommit.substr(0, 2) === '0x' ? contentCommit : `0x${contentCommit}`;
|
||||||
|
|
||||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
const eventId = nextEventId++;
|
||||||
|
const values = [contentHash, contentRepo, contentCommit];
|
||||||
const values = [contentHash, repo, commit.substr(0, 2) === '0x' ? commit : `0x${commit}`];
|
|
||||||
const options = { from: fromAddress };
|
const options = { from: fromAddress };
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
eventIds: [eventId].concat(this.state.eventIds),
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: {
|
||||||
|
contentHash,
|
||||||
|
contentRepo,
|
||||||
|
contentCommit,
|
||||||
|
fromAddress,
|
||||||
|
registerBusy: true,
|
||||||
|
registerState: 'Estimating gas for the transaction',
|
||||||
|
timestamp: new Date()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
url: '',
|
||||||
|
commit: '',
|
||||||
|
repo: '',
|
||||||
|
commitError: null,
|
||||||
|
contentHash: '',
|
||||||
|
contentHashOwner: null,
|
||||||
|
contentHashError: null
|
||||||
|
});
|
||||||
|
|
||||||
this.trackRequest(
|
this.trackRequest(
|
||||||
instance
|
eventId, instance
|
||||||
.hint.estimateGas(options, values)
|
.hint.estimateGas(options, values)
|
||||||
.then((gas) => {
|
.then((gas) => {
|
||||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
registerState: 'Gas estimated, Posting transaction to the network'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
const gasPassed = gas.mul(1.2);
|
const gasPassed = gas.mul(1.2);
|
||||||
options.gas = gasPassed.toFixed(0);
|
options.gas = gasPassed.toFixed(0);
|
||||||
@ -335,19 +404,45 @@ export default class Application extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerUrl (url) {
|
registerUrl (contentUrl) {
|
||||||
const { contentHash, fromAddress, instance } = this.state;
|
const { contentHash, fromAddress, instance } = this.state;
|
||||||
|
|
||||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
const eventId = nextEventId++;
|
||||||
|
const values = [contentHash, contentUrl];
|
||||||
const values = [contentHash, url];
|
|
||||||
const options = { from: fromAddress };
|
const options = { from: fromAddress };
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
eventIds: [eventId].concat(this.state.eventIds),
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: {
|
||||||
|
contentHash,
|
||||||
|
contentUrl,
|
||||||
|
fromAddress,
|
||||||
|
registerBusy: true,
|
||||||
|
registerState: 'Estimating gas for the transaction',
|
||||||
|
timestamp: new Date()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
url: '',
|
||||||
|
commit: '',
|
||||||
|
repo: '',
|
||||||
|
commitError: null,
|
||||||
|
contentHash: '',
|
||||||
|
contentHashOwner: null,
|
||||||
|
contentHashError: null
|
||||||
|
});
|
||||||
|
|
||||||
this.trackRequest(
|
this.trackRequest(
|
||||||
instance
|
eventId, instance
|
||||||
.hintURL.estimateGas(options, values)
|
.hintURL.estimateGas(options, values)
|
||||||
.then((gas) => {
|
.then((gas) => {
|
||||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
this.setState({
|
||||||
|
events: Object.assign({}, this.state.events, {
|
||||||
|
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||||
|
registerState: 'Gas estimated, Posting transaction to the network'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
const gasPassed = gas.mul(1.2);
|
const gasPassed = gas.mul(1.2);
|
||||||
options.gas = gasPassed.toFixed(0);
|
options.gas = gasPassed.toFixed(0);
|
||||||
|
37
js/src/dapps/githubhint/Events/events.css
Normal file
37
js/src/dapps/githubhint/Events/events.css
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.list {
|
||||||
|
border: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
&[data-busy="true"] {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-error="true"] {
|
||||||
|
color: #f66;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
52
js/src/dapps/githubhint/Events/events.js
Normal file
52
js/src/dapps/githubhint/Events/events.js
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import styles from './events.css';
|
||||||
|
|
||||||
|
export default class Events extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
eventIds: PropTypes.array.isRequired,
|
||||||
|
events: PropTypes.array.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<table className={ styles.list }>
|
||||||
|
<tbody>
|
||||||
|
{ this.props.eventIds.map((id) => this.renderEvent(id, this.props.events[id])) }
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEvent = (eventId, event) => {
|
||||||
|
return (
|
||||||
|
<tr key={ `event_${eventId}` } data-busy={ event.registerBusy } data-error={ event.registerError }>
|
||||||
|
<td>
|
||||||
|
<div>{ moment(event.timestamp).fromNow() }</div>
|
||||||
|
<div>{ event.registerState }</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>{ event.contentUrl || `${event.contentRepo}/${event.contentCommit}` }</div>
|
||||||
|
<div>{ event.contentHash }</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/githubhint/Events/index.js
Normal file
17
js/src/dapps/githubhint/Events/index.js
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './events';
|
@ -103,7 +103,7 @@ export default class DetailsStep extends Component {
|
|||||||
label='contract name'
|
label='contract name'
|
||||||
hint='a name for the deployed contract'
|
hint='a name for the deployed contract'
|
||||||
error={ nameError }
|
error={ nameError }
|
||||||
value={ name }
|
value={ name || '' }
|
||||||
onChange={ this.onNameChange } />
|
onChange={ this.onNameChange } />
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
@ -169,6 +169,10 @@ export default class DetailsStep extends Component {
|
|||||||
const contractName = Object.keys(contracts)[index];
|
const contractName = Object.keys(contracts)[index];
|
||||||
const contract = contracts[contractName];
|
const contract = contracts[contractName];
|
||||||
|
|
||||||
|
if (!this.props.name || this.props.name.trim() === '') {
|
||||||
|
this.onNameChange(null, contractName);
|
||||||
|
}
|
||||||
|
|
||||||
const { abi, bin } = contract;
|
const { abi, bin } = contract;
|
||||||
const code = /^0x/.test(bin) ? bin : `0x${bin}`;
|
const code = /^0x/.test(bin) ? bin : `0x${bin}`;
|
||||||
|
|
||||||
|
@ -48,6 +48,15 @@ export default class Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._store.dispatch(statusBlockNumber(blockNumber));
|
this._store.dispatch(statusBlockNumber(blockNumber));
|
||||||
|
|
||||||
|
this._api.eth
|
||||||
|
.getBlockByNumber(blockNumber)
|
||||||
|
.then((block) => {
|
||||||
|
this._store.dispatch(statusCollection({ gasLimit: block.gasLimit }));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.then((subscriptionId) => {
|
.then((subscriptionId) => {
|
||||||
console.log('status._subscribeBlockNumber', 'subscriptionId', subscriptionId);
|
console.log('status._subscribeBlockNumber', 'subscriptionId', subscriptionId);
|
||||||
|
@ -28,6 +28,7 @@ const initialState = {
|
|||||||
enode: '',
|
enode: '',
|
||||||
extraData: '',
|
extraData: '',
|
||||||
gasFloorTarget: new BigNumber(0),
|
gasFloorTarget: new BigNumber(0),
|
||||||
|
gasLimit: new BigNumber(0),
|
||||||
hashrate: new BigNumber(0),
|
hashrate: new BigNumber(0),
|
||||||
minGasPrice: new BigNumber(0),
|
minGasPrice: new BigNumber(0),
|
||||||
netChain: 'morden',
|
netChain: 'morden',
|
||||||
|
@ -19,5 +19,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.layout>div {
|
.layout>div {
|
||||||
padding-bottom: 0.25em;
|
padding-bottom: 0.75em;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export function validateAbi (abi, api) {
|
|||||||
try {
|
try {
|
||||||
abiParsed = JSON.parse(abi);
|
abiParsed = JSON.parse(abi);
|
||||||
|
|
||||||
if (!api.util.isArray(abiParsed) || !abiParsed.length) {
|
if (!api.util.isArray(abiParsed)) {
|
||||||
abiError = ERRORS.invalidAbi;
|
abiError = ERRORS.invalidAbi;
|
||||||
return { abi, abiError, abiParsed };
|
return { abi, abiError, abiParsed };
|
||||||
}
|
}
|
||||||
|
@ -18,3 +18,22 @@
|
|||||||
.description {
|
.description {
|
||||||
margin-top: .5em !important;
|
margin-top: .5em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
.background {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
margin: 0 -1.5em;
|
||||||
|
padding: 0.5em 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.byline {
|
||||||
|
font-size: 0.75em;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -51,16 +51,37 @@ export default class AddDapps extends Component {
|
|||||||
] }
|
] }
|
||||||
visible
|
visible
|
||||||
scroll>
|
scroll>
|
||||||
<List>
|
<div className={ styles.warning }>
|
||||||
{ store.apps.map(this.renderApp) }
|
</div>
|
||||||
</List>
|
{ this.renderList(store.sortedLocal, 'Applications locally available', 'All applications installed locally on the machine by the user for access by the Parity client.') }
|
||||||
|
{ this.renderList(store.sortedBuiltin, 'Applications bundled with Parity', 'Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.') }
|
||||||
|
{ this.renderList(store.sortedNetwork, 'Applications on the global network', 'These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.') }
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderList (items, header, byline) {
|
||||||
|
if (!items || !items.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.list }>
|
||||||
|
<div className={ styles.background }>
|
||||||
|
<div className={ styles.header }>{ header }</div>
|
||||||
|
<div className={ styles.byline }>{ byline }</div>
|
||||||
|
</div>
|
||||||
|
<List>
|
||||||
|
{ items.map(this.renderApp) }
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderApp = (app) => {
|
renderApp = (app) => {
|
||||||
const { store } = this.props;
|
const { store } = this.props;
|
||||||
const isHidden = store.hidden.includes(app.id);
|
const isHidden = !store.displayApps[app.id].visible;
|
||||||
|
|
||||||
const onCheck = () => {
|
const onCheck = () => {
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
store.showApp(app.id);
|
store.showApp(app.id);
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
import { Container, ContainerTitle } from '../../../ui';
|
import { Container, ContainerTitle, Tags } from '../../../ui';
|
||||||
|
|
||||||
import styles from './summary.css';
|
import styles from './summary.css';
|
||||||
|
|
||||||
@ -49,6 +49,7 @@ export default class Summary extends Component {
|
|||||||
return (
|
return (
|
||||||
<Container className={ styles.container }>
|
<Container className={ styles.container }>
|
||||||
{ image }
|
{ image }
|
||||||
|
<Tags tags={ [app.type] } />
|
||||||
<div className={ styles.description }>
|
<div className={ styles.description }>
|
||||||
<ContainerTitle
|
<ContainerTitle
|
||||||
className={ styles.title }
|
className={ styles.title }
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"name": "Token Deployment",
|
"name": "Token Deployment",
|
||||||
"description": "Deploy new basic tokens that you are able to send around",
|
"description": "Deploy new basic tokens that you are able to send around",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0",
|
||||||
|
"visible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0xd1adaede68d344519025e2ff574650cd99d3830fe6d274c7a7843cdc00e17938",
|
"id": "0xd1adaede68d344519025e2ff574650cd99d3830fe6d274c7a7843cdc00e17938",
|
||||||
@ -13,7 +14,8 @@
|
|||||||
"name": "Registry",
|
"name": "Registry",
|
||||||
"description": "A global registry of addresses on the network",
|
"description": "A global registry of addresses on the network",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0",
|
||||||
|
"visible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0x0a8048117e51e964628d0f2d26342b3cd915248b59bcce2721e1d05f5cfa2208",
|
"id": "0x0a8048117e51e964628d0f2d26342b3cd915248b59bcce2721e1d05f5cfa2208",
|
||||||
@ -21,7 +23,8 @@
|
|||||||
"name": "Token Registry",
|
"name": "Token Registry",
|
||||||
"description": "A registry of transactable tokens on the network",
|
"description": "A registry of transactable tokens on the network",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0",
|
||||||
|
"visible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",
|
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",
|
||||||
@ -29,7 +32,8 @@
|
|||||||
"name": "Method Registry",
|
"name": "Method Registry",
|
||||||
"description": "A registry of method signatures for lookups on transactions",
|
"description": "A registry of method signatures for lookups on transactions",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0",
|
||||||
|
"visible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75",
|
"id": "0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75",
|
||||||
@ -38,6 +42,7 @@
|
|||||||
"description": "A mapping of GitHub URLs to hashes for use in contracts as references",
|
"description": "A mapping of GitHub URLs to hashes for use in contracts as references",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"visible": false,
|
||||||
"secure": true
|
"secure": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin: -0.125em;
|
margin: -0.125em;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list+.list {
|
.list+.list {
|
||||||
@ -29,3 +30,25 @@
|
|||||||
flex: 0 1 50%;
|
flex: 0 1 50%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
background: rgba(0, 0, 0, 0.85);
|
||||||
|
bottom: 0.5em;
|
||||||
|
left: -0.125em;
|
||||||
|
position: absolute;
|
||||||
|
right: -0.125em;
|
||||||
|
top: -0.25em;
|
||||||
|
z-index: 100;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
.body {
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 980px;
|
||||||
|
|
||||||
|
&>div:first-child {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { Checkbox } from 'material-ui';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
import { Actionbar, Page } from '../../ui';
|
import { Actionbar, Page } from '../../ui';
|
||||||
@ -37,6 +38,24 @@ export default class Dapps extends Component {
|
|||||||
store = new DappsStore(this.context.api);
|
store = new DappsStore(this.context.api);
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
let externalOverlay = null;
|
||||||
|
if (this.store.externalOverlayVisible) {
|
||||||
|
externalOverlay = (
|
||||||
|
<div className={ styles.overlay }>
|
||||||
|
<div className={ styles.body }>
|
||||||
|
<div>Applications made available on the network by 3rd-party authors are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each before interacting.</div>
|
||||||
|
<div>
|
||||||
|
<Checkbox
|
||||||
|
className={ styles.accept }
|
||||||
|
label='I understand that these applications are not affiliated with Parity'
|
||||||
|
checked={ false }
|
||||||
|
onCheck={ this.onClickAcceptExternal } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AddDapps store={ this.store } />
|
<AddDapps store={ this.store } />
|
||||||
@ -53,14 +72,27 @@ export default class Dapps extends Component {
|
|||||||
] }
|
] }
|
||||||
/>
|
/>
|
||||||
<Page>
|
<Page>
|
||||||
<div className={ styles.list }>
|
{ this.renderList(this.store.visibleLocal) }
|
||||||
{ this.store.visible.map(this.renderApp) }
|
{ this.renderList(this.store.visibleBuiltin) }
|
||||||
</div>
|
{ this.renderList(this.store.visibleNetwork, externalOverlay) }
|
||||||
</Page>
|
</Page>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderList (items, overlay) {
|
||||||
|
if (!items || !items.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.list }>
|
||||||
|
{ overlay }
|
||||||
|
{ items.map(this.renderApp) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderApp = (app) => {
|
renderApp = (app) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -70,4 +102,8 @@ export default class Dapps extends Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClickAcceptExternal = () => {
|
||||||
|
this.store.closeExternalOverlay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,39 +14,65 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
import { action, computed, observable, transaction } from 'mobx';
|
import { action, computed, observable, transaction } from 'mobx';
|
||||||
|
import store from 'store';
|
||||||
|
|
||||||
import Contracts from '../../contracts';
|
import Contracts from '../../contracts';
|
||||||
import { hashToImageUrl } from '../../redux/util';
|
import { hashToImageUrl } from '../../redux/util';
|
||||||
|
|
||||||
import builtinApps from './builtin.json';
|
import builtinApps from './builtin.json';
|
||||||
|
|
||||||
const LS_KEY_HIDDEN = 'hiddenApps';
|
const LS_KEY_DISPLAY = 'displayApps';
|
||||||
const LS_KEY_EXTERNAL = 'externalApps';
|
const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal';
|
||||||
|
|
||||||
export default class DappsStore {
|
export default class DappsStore {
|
||||||
@observable apps = [];
|
@observable apps = [];
|
||||||
@observable externalApps = [];
|
@observable displayApps = {};
|
||||||
@observable hiddenApps = [];
|
|
||||||
@observable modalOpen = false;
|
@observable modalOpen = false;
|
||||||
|
@observable externalOverlayVisible = true;
|
||||||
|
|
||||||
constructor (api) {
|
constructor (api) {
|
||||||
this._api = api;
|
this._api = api;
|
||||||
|
|
||||||
this._readHiddenApps();
|
this.loadExternalOverlay();
|
||||||
this._readExternalApps();
|
this.readDisplayApps();
|
||||||
|
|
||||||
this._fetchBuiltinApps();
|
Promise
|
||||||
this._fetchLocalApps();
|
.all([
|
||||||
this._fetchRegistryApps();
|
this._fetchBuiltinApps(),
|
||||||
|
this._fetchLocalApps(),
|
||||||
|
this._fetchRegistryApps()
|
||||||
|
])
|
||||||
|
.then(this.writeDisplayApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get visible () {
|
@computed get sortedBuiltin () {
|
||||||
return this.apps
|
return this.apps.filter((app) => app.type === 'builtin');
|
||||||
.filter((app) => {
|
}
|
||||||
return this.externalApps.includes(app.id) || !this.hiddenApps.includes(app.id);
|
|
||||||
})
|
@computed get sortedLocal () {
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
return this.apps.filter((app) => app.type === 'local');
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get sortedNetwork () {
|
||||||
|
return this.apps.filter((app) => app.type === 'network');
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get visibleApps () {
|
||||||
|
return this.apps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get visibleBuiltin () {
|
||||||
|
return this.visibleApps.filter((app) => app.type === 'builtin');
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get visibleLocal () {
|
||||||
|
return this.visibleApps.filter((app) => app.type === 'local');
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get visibleNetwork () {
|
||||||
|
return this.visibleApps.filter((app) => app.type === 'network');
|
||||||
}
|
}
|
||||||
|
|
||||||
@action openModal = () => {
|
@action openModal = () => {
|
||||||
@ -57,14 +83,48 @@ export default class DappsStore {
|
|||||||
this.modalOpen = false;
|
this.modalOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action closeExternalOverlay = () => {
|
||||||
|
this.externalOverlayVisible = false;
|
||||||
|
store.set(LS_KEY_EXTERNAL_ACCEPT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action loadExternalOverlay () {
|
||||||
|
this.externalOverlayVisible = !(store.get(LS_KEY_EXTERNAL_ACCEPT) || false);
|
||||||
|
}
|
||||||
|
|
||||||
@action hideApp = (id) => {
|
@action hideApp = (id) => {
|
||||||
this.hiddenApps = this.hiddenApps.concat(id);
|
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: false } });
|
||||||
this._writeHiddenApps();
|
this.writeDisplayApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action showApp = (id) => {
|
@action showApp = (id) => {
|
||||||
this.hiddenApps = this.hiddenApps.filter((_id) => _id !== id);
|
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: true } });
|
||||||
this._writeHiddenApps();
|
this.writeDisplayApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action readDisplayApps = () => {
|
||||||
|
this.displayApps = store.get(LS_KEY_DISPLAY) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@action writeDisplayApps = () => {
|
||||||
|
store.set(LS_KEY_DISPLAY, this.displayApps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action addApps = (apps) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.apps = this.apps
|
||||||
|
.concat(apps || [])
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
const visibility = {};
|
||||||
|
apps.forEach((app) => {
|
||||||
|
if (!this.displayApps[app.id]) {
|
||||||
|
visibility[app.id] = { visible: app.visible };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.displayApps = Object.assign({}, this.displayApps, visibility);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getHost (api) {
|
_getHost (api) {
|
||||||
@ -79,13 +139,16 @@ export default class DappsStore {
|
|||||||
return Promise
|
return Promise
|
||||||
.all(builtinApps.map((app) => dappReg.getImage(app.id)))
|
.all(builtinApps.map((app) => dappReg.getImage(app.id)))
|
||||||
.then((imageIds) => {
|
.then((imageIds) => {
|
||||||
transaction(() => {
|
this.addApps(
|
||||||
builtinApps.forEach((app, index) => {
|
builtinApps.map((app, index) => {
|
||||||
app.type = 'builtin';
|
app.type = 'builtin';
|
||||||
app.image = hashToImageUrl(imageIds[index]);
|
app.image = hashToImageUrl(imageIds[index]);
|
||||||
this.apps.push(app);
|
return app;
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('DappsStore:fetchBuiltinApps', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,15 +163,12 @@ export default class DappsStore {
|
|||||||
return apps
|
return apps
|
||||||
.map((app) => {
|
.map((app) => {
|
||||||
app.type = 'local';
|
app.type = 'local';
|
||||||
|
app.visible = true;
|
||||||
return app;
|
return app;
|
||||||
})
|
})
|
||||||
.filter((app) => app.id && !['ui'].includes(app.id));
|
.filter((app) => app.id && !['ui'].includes(app.id));
|
||||||
})
|
})
|
||||||
.then((apps) => {
|
.then(this.addApps)
|
||||||
transaction(() => {
|
|
||||||
(apps || []).forEach((app) => this.apps.push(app));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('DappsStore:fetchLocal', error);
|
console.warn('DappsStore:fetchLocal', error);
|
||||||
});
|
});
|
||||||
@ -132,7 +192,9 @@ export default class DappsStore {
|
|||||||
.then((appsInfo) => {
|
.then((appsInfo) => {
|
||||||
const appIds = appsInfo
|
const appIds = appsInfo
|
||||||
.map(([appId, owner]) => this._api.util.bytesToHex(appId))
|
.map(([appId, owner]) => this._api.util.bytesToHex(appId))
|
||||||
.filter((appId) => !builtinApps.find((app) => app.id === appId));
|
.filter((appId) => {
|
||||||
|
return (new BigNumber(appId)).gt(0) && !builtinApps.find((app) => app.id === appId);
|
||||||
|
});
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([
|
.all([
|
||||||
@ -147,7 +209,8 @@ export default class DappsStore {
|
|||||||
image: hashToImageUrl(imageIds[index]),
|
image: hashToImageUrl(imageIds[index]),
|
||||||
contentHash: this._api.util.bytesToHex(contentIds[index]).substr(2),
|
contentHash: this._api.util.bytesToHex(contentIds[index]).substr(2),
|
||||||
manifestHash: this._api.util.bytesToHex(manifestIds[index]).substr(2),
|
manifestHash: this._api.util.bytesToHex(manifestIds[index]).substr(2),
|
||||||
type: 'network'
|
type: 'network',
|
||||||
|
visible: true
|
||||||
};
|
};
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
@ -179,11 +242,7 @@ export default class DappsStore {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((apps) => {
|
.then(this.addApps)
|
||||||
transaction(() => {
|
|
||||||
(apps || []).forEach((app) => this.apps.push(app));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('DappsStore:fetchRegistry', error);
|
console.warn('DappsStore:fetchRegistry', error);
|
||||||
});
|
});
|
||||||
@ -201,44 +260,4 @@ export default class DappsStore {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_readHiddenApps () {
|
|
||||||
const stored = localStorage.getItem(LS_KEY_HIDDEN);
|
|
||||||
|
|
||||||
if (stored) {
|
|
||||||
try {
|
|
||||||
this.hiddenApps = JSON.parse(stored);
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('DappsStore:readHiddenApps', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_readExternalApps () {
|
|
||||||
const stored = localStorage.getItem(LS_KEY_EXTERNAL);
|
|
||||||
|
|
||||||
if (stored) {
|
|
||||||
try {
|
|
||||||
this.externalApps = JSON.parse(stored);
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('DappsStore:readExternalApps', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writeExternalApps () {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(LS_KEY_EXTERNAL, JSON.stringify(this.externalApps));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('DappsStore:writeExternalApps', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writeHiddenApps () {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(LS_KEY_HIDDEN, JSON.stringify(this.hiddenApps));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('DappsStore:writeHiddenApps', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ const DEST = process.env.BUILD_DEST || '.build';
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
context: path.join(__dirname, './src'),
|
context: path.join(__dirname, './src'),
|
||||||
target: 'node',
|
|
||||||
entry: {
|
entry: {
|
||||||
// library
|
// library
|
||||||
'inject': ['./web3.js'],
|
'inject': ['./web3.js'],
|
||||||
@ -39,14 +38,7 @@ module.exports = {
|
|||||||
library: '[name].js',
|
library: '[name].js',
|
||||||
libraryTarget: 'umd'
|
libraryTarget: 'umd'
|
||||||
},
|
},
|
||||||
externals: {
|
|
||||||
'node-fetch': 'node-fetch',
|
|
||||||
'vertx': 'vertx'
|
|
||||||
},
|
|
||||||
module: {
|
module: {
|
||||||
noParse: [
|
|
||||||
/babel-polyfill/
|
|
||||||
],
|
|
||||||
loaders: [
|
loaders: [
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
|
@ -28,6 +28,7 @@ use jsonrpc_core::Error;
|
|||||||
use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
|
use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
|
H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
|
||||||
|
RichRawTransaction as RpcRichRawTransaction,
|
||||||
ConfirmationPayload as RpcConfirmationPayload,
|
ConfirmationPayload as RpcConfirmationPayload,
|
||||||
ConfirmationResponse,
|
ConfirmationResponse,
|
||||||
SignRequest as RpcSignRequest,
|
SignRequest as RpcSignRequest,
|
||||||
@ -47,8 +48,7 @@ pub fn execute<C, M>(client: &C, miner: &M, accounts: &AccountProvider, payload:
|
|||||||
},
|
},
|
||||||
ConfirmationPayload::SignTransaction(request) => {
|
ConfirmationPayload::SignTransaction(request) => {
|
||||||
sign_no_dispatch(client, miner, accounts, request, pass)
|
sign_no_dispatch(client, miner, accounts, request, pass)
|
||||||
.map(|tx| rlp::encode(&tx).to_vec())
|
.map(RpcRichRawTransaction::from)
|
||||||
.map(RpcBytes)
|
|
||||||
.map(ConfirmationResponse::SignTransaction)
|
.map(ConfirmationResponse::SignTransaction)
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Signature(address, hash) => {
|
ConfirmationPayload::Signature(address, hash) => {
|
||||||
|
@ -619,6 +619,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn submit_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
|
||||||
|
self.send_raw_transaction(raw)
|
||||||
|
}
|
||||||
|
|
||||||
fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> {
|
fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ use v1::traits::{EthSigning, ParitySigning};
|
|||||||
use v1::types::{
|
use v1::types::{
|
||||||
H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520,
|
H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520,
|
||||||
Either as RpcEither,
|
Either as RpcEither,
|
||||||
|
RichRawTransaction as RpcRichRawTransaction,
|
||||||
TransactionRequest as RpcTransactionRequest,
|
TransactionRequest as RpcTransactionRequest,
|
||||||
ConfirmationPayload as RpcConfirmationPayload,
|
ConfirmationPayload as RpcConfirmationPayload,
|
||||||
ConfirmationResponse as RpcConfirmationResponse
|
ConfirmationResponse as RpcConfirmationResponse
|
||||||
@ -201,11 +202,11 @@ impl<C: 'static, M: 'static> EthSigning for SigningQueueClient<C, M> where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) {
|
fn sign_transaction(&self, ready: Ready<RpcRichRawTransaction>, request: RpcTransactionRequest) {
|
||||||
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request)));
|
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request)));
|
||||||
self.handle_dispatch(res, |response| {
|
self.handle_dispatch(res, |response| {
|
||||||
match response {
|
match response {
|
||||||
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => ready.ready(Ok(rlp)),
|
Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.ready(Ok(tx)),
|
||||||
Err(e) => ready.ready(Err(e)),
|
Err(e) => ready.ready(Err(e)),
|
||||||
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
|
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ use v1::types::{
|
|||||||
U256 as RpcU256,
|
U256 as RpcU256,
|
||||||
H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
|
H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
|
||||||
Either as RpcEither,
|
Either as RpcEither,
|
||||||
|
RichRawTransaction as RpcRichRawTransaction,
|
||||||
TransactionRequest as RpcTransactionRequest,
|
TransactionRequest as RpcTransactionRequest,
|
||||||
ConfirmationPayload as RpcConfirmationPayload,
|
ConfirmationPayload as RpcConfirmationPayload,
|
||||||
ConfirmationResponse as RpcConfirmationResponse,
|
ConfirmationResponse as RpcConfirmationResponse,
|
||||||
@ -100,9 +101,9 @@ impl<C: 'static, M: 'static> EthSigning for SigningUnsafeClient<C, M> where
|
|||||||
ready.ready(result);
|
ready.ready(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) {
|
fn sign_transaction(&self, ready: Ready<RpcRichRawTransaction>, request: RpcTransactionRequest) {
|
||||||
let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) {
|
let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) {
|
||||||
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp),
|
Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
e => Err(errors::internal("Unexpected result", e)),
|
e => Err(errors::internal("Unexpected result", e)),
|
||||||
};
|
};
|
||||||
|
@ -18,8 +18,10 @@ use std::str::FromStr;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
use rustc_serialize::hex::ToHex;
|
||||||
|
use time::get_time;
|
||||||
use rlp;
|
use rlp;
|
||||||
use jsonrpc_core::IoHandler;
|
|
||||||
use util::{Uint, U256, Address, H256, FixedHash, Mutex};
|
use util::{Uint, U256, Address, H256, FixedHash, Mutex};
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
||||||
@ -28,10 +30,10 @@ use ethcore::receipt::LocalizedReceipt;
|
|||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
use ethcore::miner::{ExternalMiner, MinerService};
|
use ethcore::miner::{ExternalMiner, MinerService};
|
||||||
use ethsync::SyncState;
|
use ethsync::SyncState;
|
||||||
|
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient};
|
use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient};
|
||||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService};
|
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService};
|
||||||
use rustc_serialize::hex::ToHex;
|
|
||||||
use time::get_time;
|
|
||||||
|
|
||||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
let client = TestBlockChainClient::new();
|
let client = TestBlockChainClient::new();
|
||||||
@ -798,9 +800,25 @@ fn rpc_eth_sign_transaction() {
|
|||||||
};
|
};
|
||||||
let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
|
let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
|
||||||
let t = t.with_signature(signature, None);
|
let t = t.with_signature(signature, None);
|
||||||
|
let signature = t.signature();
|
||||||
let rlp = rlp::encode(&t);
|
let rlp = rlp::encode(&t);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
||||||
|
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
||||||
|
r#""tx":{"# +
|
||||||
|
r#""blockHash":null,"blockNumber":null,"creates":null,"# +
|
||||||
|
&format!("\"from\":\"0x{:?}\",", &address) +
|
||||||
|
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
||||||
|
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
||||||
|
r#""input":"0x","nonce":"0x1","# +
|
||||||
|
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
||||||
|
&format!("\"r\":\"0x{}\",", signature.r().to_hex()) +
|
||||||
|
&format!("\"raw\":\"0x{}\",", rlp.to_hex()) +
|
||||||
|
&format!("\"s\":\"0x{}\",", signature.s().to_hex()) +
|
||||||
|
r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# +
|
||||||
|
&format!("\"v\":{},", signature.v()) +
|
||||||
|
r#""value":"0x9184e72a""# +
|
||||||
|
r#"}},"id":1}"#;
|
||||||
|
|
||||||
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
|
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
|
||||||
|
|
||||||
|
@ -268,16 +268,32 @@ fn should_add_sign_transaction_to_the_queue() {
|
|||||||
};
|
};
|
||||||
let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap();
|
let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap();
|
||||||
let t = t.with_signature(signature, None);
|
let t = t.with_signature(signature, None);
|
||||||
|
let signature = t.signature();
|
||||||
let rlp = rlp::encode(&t);
|
let rlp = rlp::encode(&t);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
||||||
|
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
||||||
|
r#""tx":{"# +
|
||||||
|
r#""blockHash":null,"blockNumber":null,"creates":null,"# +
|
||||||
|
&format!("\"from\":\"0x{:?}\",", &address) +
|
||||||
|
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
||||||
|
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
||||||
|
r#""input":"0x","nonce":"0x1","# +
|
||||||
|
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
||||||
|
&format!("\"r\":\"0x{}\",", signature.r().to_hex()) +
|
||||||
|
&format!("\"raw\":\"0x{}\",", rlp.to_hex()) +
|
||||||
|
&format!("\"s\":\"0x{}\",", signature.s().to_hex()) +
|
||||||
|
r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# +
|
||||||
|
&format!("\"v\":{},", signature.v()) +
|
||||||
|
r#""value":"0x9184e72a""# +
|
||||||
|
r#"}},"id":1}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
|
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
|
||||||
let async_result = tester.io.handle_request(&request).unwrap();
|
let async_result = tester.io.handle_request(&request).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
// respond
|
// respond
|
||||||
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(rlp.to_vec().into())));
|
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(t.into())));
|
||||||
assert!(async_result.on_result(move |res| {
|
assert!(async_result.on_result(move |res| {
|
||||||
assert_eq!(res, response.to_owned());
|
assert_eq!(res, response.to_owned());
|
||||||
}));
|
}));
|
||||||
|
@ -102,6 +102,10 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "eth_sendRawTransaction")]
|
#[rpc(name = "eth_sendRawTransaction")]
|
||||||
fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>;
|
fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>;
|
||||||
|
|
||||||
|
/// Alias of `eth_sendRawTransaction`.
|
||||||
|
#[rpc(name = "eth_submitTransaction")]
|
||||||
|
fn submit_transaction(&self, Bytes) -> Result<H256, Error>;
|
||||||
|
|
||||||
/// Call contract, returning the output data.
|
/// Call contract, returning the output data.
|
||||||
#[rpc(name = "eth_call")]
|
#[rpc(name = "eth_call")]
|
||||||
fn call(&self, CallRequest, Trailing<BlockNumber>) -> Result<Bytes, Error>;
|
fn call(&self, CallRequest, Trailing<BlockNumber>) -> Result<Bytes, Error>;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Eth rpc interface.
|
//! Eth rpc interface.
|
||||||
|
|
||||||
use v1::helpers::auto_args::{WrapAsync, Ready};
|
use v1::helpers::auto_args::{WrapAsync, Ready};
|
||||||
use v1::types::{H160, H256, H520, TransactionRequest, Bytes};
|
use v1::types::{H160, H256, H520, TransactionRequest, RichRawTransaction};
|
||||||
|
|
||||||
build_rpc_trait! {
|
build_rpc_trait! {
|
||||||
/// Signing methods implementation relying on unlocked accounts.
|
/// Signing methods implementation relying on unlocked accounts.
|
||||||
@ -33,9 +33,9 @@ build_rpc_trait! {
|
|||||||
fn send_transaction(&self, Ready<H256>, TransactionRequest);
|
fn send_transaction(&self, Ready<H256>, TransactionRequest);
|
||||||
|
|
||||||
/// Signs transactions without dispatching it to the network.
|
/// Signs transactions without dispatching it to the network.
|
||||||
/// Returns signed transaction RLP representation.
|
/// Returns signed transaction RLP representation and the transaction itself.
|
||||||
/// It can be later submitted using `eth_sendRawTransaction`.
|
/// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`.
|
||||||
#[rpc(async, name = "eth_signTransaction")]
|
#[rpc(async, name = "eth_signTransaction")]
|
||||||
fn sign_transaction(&self, Ready<Bytes>, TransactionRequest);
|
fn sign_transaction(&self, Ready<RichRawTransaction>, TransactionRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use v1::types::{U256, TransactionRequest, H160, H256, H520, Bytes};
|
use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes};
|
||||||
use v1::helpers;
|
use v1::helpers;
|
||||||
|
|
||||||
/// Confirmation waiting in a queue
|
/// Confirmation waiting in a queue
|
||||||
@ -76,12 +76,12 @@ impl From<(H160, Bytes)> for DecryptRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Confirmation response for particular payload
|
/// Confirmation response for particular payload
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ConfirmationResponse {
|
pub enum ConfirmationResponse {
|
||||||
/// Transaction Hash
|
/// Transaction Hash
|
||||||
SendTransaction(H256),
|
SendTransaction(H256),
|
||||||
/// Transaction RLP
|
/// Transaction RLP
|
||||||
SignTransaction(Bytes),
|
SignTransaction(RichRawTransaction),
|
||||||
/// Signature
|
/// Signature
|
||||||
Signature(H520),
|
Signature(H520),
|
||||||
/// Decrypted data
|
/// Decrypted data
|
||||||
|
@ -44,7 +44,7 @@ pub use self::hash::{H64, H160, H256, H512, H520, H2048};
|
|||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
pub use self::log::Log;
|
pub use self::log::Log;
|
||||||
pub use self::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo};
|
pub use self::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::{Transaction, RichRawTransaction};
|
||||||
pub use self::transaction_request::TransactionRequest;
|
pub use self::transaction_request::TransactionRequest;
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
pub use self::rpc_settings::RpcSettings;
|
pub use self::rpc_settings::RpcSettings;
|
||||||
|
@ -19,7 +19,7 @@ use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
|||||||
use v1::types::{Bytes, H160, H256, U256, H512};
|
use v1::types::{Bytes, H160, H256, U256, H512};
|
||||||
|
|
||||||
/// Transaction
|
/// Transaction
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
/// Hash
|
/// Hash
|
||||||
pub hash: H256,
|
pub hash: H256,
|
||||||
@ -62,6 +62,26 @@ pub struct Transaction {
|
|||||||
pub s: H256,
|
pub s: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Geth-compatible output for eth_signTransaction method
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||||
|
pub struct RichRawTransaction {
|
||||||
|
/// Raw transaction RLP
|
||||||
|
pub raw: Bytes,
|
||||||
|
/// Transaction details
|
||||||
|
#[serde(rename="tx")]
|
||||||
|
pub transaction: Transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SignedTransaction> for RichRawTransaction {
|
||||||
|
fn from(t: SignedTransaction) -> Self {
|
||||||
|
let tx: Transaction = t.into();
|
||||||
|
RichRawTransaction {
|
||||||
|
raw: tx.raw.clone(),
|
||||||
|
transaction: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LocalizedTransaction> for Transaction {
|
impl From<LocalizedTransaction> for Transaction {
|
||||||
fn from(t: LocalizedTransaction) -> Transaction {
|
fn from(t: LocalizedTransaction) -> Transaction {
|
||||||
let signature = t.signature();
|
let signature = t.signature();
|
||||||
|
@ -34,6 +34,7 @@ const MAX_RECEPITS_TO_REQUEST: usize = 128;
|
|||||||
const SUBCHAIN_SIZE: u64 = 256;
|
const SUBCHAIN_SIZE: u64 = 256;
|
||||||
const MAX_ROUND_PARENTS: usize = 32;
|
const MAX_ROUND_PARENTS: usize = 32;
|
||||||
const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5;
|
const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5;
|
||||||
|
const MAX_REORG_BLOCKS: u64 = 20;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
/// Downloader state
|
/// Downloader state
|
||||||
@ -262,7 +263,8 @@ impl BlockDownloader {
|
|||||||
State::Blocks => {
|
State::Blocks => {
|
||||||
let count = headers.len();
|
let count = headers.len();
|
||||||
// At least one of the heades must advance the subchain. Otherwise they are all useless.
|
// At least one of the heades must advance the subchain. Otherwise they are all useless.
|
||||||
if !any_known {
|
if count == 0 || !any_known {
|
||||||
|
trace!(target: "sync", "No useful headers");
|
||||||
return Err(BlockDownloaderImportError::Useless);
|
return Err(BlockDownloaderImportError::Useless);
|
||||||
}
|
}
|
||||||
self.blocks.insert_headers(headers);
|
self.blocks.insert_headers(headers);
|
||||||
@ -340,14 +342,21 @@ impl BlockDownloader {
|
|||||||
self.last_imported_hash = p.clone();
|
self.last_imported_hash = p.clone();
|
||||||
trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash);
|
trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||||
} else {
|
} else {
|
||||||
match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) {
|
let best = io.chain().chain_info().best_block_number;
|
||||||
Some(h) => {
|
if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS {
|
||||||
self.last_imported_block -= 1;
|
debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||||
self.last_imported_hash = h;
|
self.reset();
|
||||||
trace!(target: "sync", "Searching common header in the blockchain {} ({})", self.last_imported_block, self.last_imported_hash);
|
} else {
|
||||||
}
|
match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) {
|
||||||
None => {
|
Some(h) => {
|
||||||
debug!(target: "sync", "Could not revert to previous block, last: {} ({})", self.last_imported_block, self.last_imported_hash);
|
self.last_imported_block -= 1;
|
||||||
|
self.last_imported_hash = h;
|
||||||
|
trace!(target: "sync", "Searching common header in the blockchain {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug!(target: "sync", "Could not revert to previous block, last: {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,7 +371,9 @@ impl BlockDownloader {
|
|||||||
match self.state {
|
match self.state {
|
||||||
State::Idle => {
|
State::Idle => {
|
||||||
self.start_sync_round(io);
|
self.start_sync_round(io);
|
||||||
return self.request_blocks(io, num_active_peers);
|
if self.state == State::ChainHead {
|
||||||
|
return self.request_blocks(io, num_active_peers);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
State::ChainHead => {
|
State::ChainHead => {
|
||||||
if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD {
|
if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD {
|
||||||
|
@ -1144,6 +1144,7 @@ impl ChainSync {
|
|||||||
let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown;
|
let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown;
|
||||||
if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) {
|
if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) {
|
||||||
// check if got new blocks to download
|
// check if got new blocks to download
|
||||||
|
trace!(target: "sync", "Syncing with {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state);
|
||||||
if let Some(request) = self.new_blocks.request_blocks(io, num_active_peers) {
|
if let Some(request) = self.new_blocks.request_blocks(io, num_active_peers) {
|
||||||
self.request_blocks(io, peer_id, request, BlockSet::NewBlocks);
|
self.request_blocks(io, peer_id, request, BlockSet::NewBlocks);
|
||||||
if self.state == SyncState::Idle {
|
if self.state == SyncState::Idle {
|
||||||
|
@ -79,14 +79,14 @@ fn empty_blocks() {
|
|||||||
fn forked() {
|
fn forked() {
|
||||||
::env_logger::init().ok();
|
::env_logger::init().ok();
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(0).chain.add_blocks(300, EachBlockWith::Uncle);
|
net.peer_mut(0).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.add_blocks(300, EachBlockWith::Uncle);
|
net.peer_mut(1).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(300, EachBlockWith::Uncle);
|
net.peer_mut(2).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||||
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing); //fork
|
net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork
|
||||||
net.peer_mut(1).chain.add_blocks(200, EachBlockWith::Uncle);
|
net.peer_mut(1).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(200, EachBlockWith::Uncle);
|
net.peer_mut(2).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle); //fork between 1 and 2
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2
|
||||||
net.peer_mut(2).chain.add_blocks(10, EachBlockWith::Nothing);
|
net.peer_mut(2).chain.add_blocks(1, EachBlockWith::Nothing);
|
||||||
// peer 1 has the best chain of 601 blocks
|
// peer 1 has the best chain of 601 blocks
|
||||||
let peer1_chain = net.peer(1).chain.numbers.read().clone();
|
let peer1_chain = net.peer(1).chain.numbers.read().clone();
|
||||||
net.sync();
|
net.sync();
|
||||||
@ -102,12 +102,12 @@ fn forked_with_misbehaving_peer() {
|
|||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
// peer 0 is on a totally different chain with higher total difficulty
|
// peer 0 is on a totally different chain with higher total difficulty
|
||||||
net.peer_mut(0).chain = TestBlockChainClient::new_with_extra_data(b"fork".to_vec());
|
net.peer_mut(0).chain = TestBlockChainClient::new_with_extra_data(b"fork".to_vec());
|
||||||
net.peer_mut(0).chain.add_blocks(500, EachBlockWith::Nothing);
|
net.peer_mut(0).chain.add_blocks(50, EachBlockWith::Nothing);
|
||||||
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Nothing);
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||||
net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Nothing);
|
net.peer_mut(2).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||||
|
|
||||||
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Nothing);
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||||
net.peer_mut(2).chain.add_blocks(200, EachBlockWith::Uncle);
|
net.peer_mut(2).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||||
// peer 1 should sync to peer 2, others should not change
|
// peer 1 should sync to peer 2, others should not change
|
||||||
let peer0_chain = net.peer(0).chain.numbers.read().clone();
|
let peer0_chain = net.peer(0).chain.numbers.read().clone();
|
||||||
let peer2_chain = net.peer(2).chain.numbers.read().clone();
|
let peer2_chain = net.peer(2).chain.numbers.read().clone();
|
||||||
|
Loading…
Reference in New Issue
Block a user