From 5a6e2f89ddc46b0f09560e2b8322207404a16f69 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 14 Nov 2016 17:02:45 +0100 Subject: [PATCH 01/22] Hide external apps by default --- js/src/views/Dapps/AddDapps/AddDapps.js | 5 +- js/src/views/Dapps/Summary/summary.js | 3 +- js/src/views/Dapps/builtin.json | 13 ++- js/src/views/Dapps/dapps.js | 2 +- js/src/views/Dapps/dappsStore.js | 114 ++++++++++++------------ 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/js/src/views/Dapps/AddDapps/AddDapps.js b/js/src/views/Dapps/AddDapps/AddDapps.js index 38bb64792..49d3c5e8d 100644 --- a/js/src/views/Dapps/AddDapps/AddDapps.js +++ b/js/src/views/Dapps/AddDapps/AddDapps.js @@ -52,7 +52,7 @@ export default class AddDapps extends Component { visible scroll> - { store.apps.map(this.renderApp) } + { store.sortedApps.map(this.renderApp) } ); @@ -60,7 +60,8 @@ export default class AddDapps extends Component { renderApp = (app) => { const { store } = this.props; - const isHidden = store.hidden.includes(app.id); + const isHidden = !store.displayApps[app.id].visible; + const onCheck = () => { if (isHidden) { store.showApp(app.id); diff --git a/js/src/views/Dapps/Summary/summary.js b/js/src/views/Dapps/Summary/summary.js index 912f1495e..b0963a560 100644 --- a/js/src/views/Dapps/Summary/summary.js +++ b/js/src/views/Dapps/Summary/summary.js @@ -17,7 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { Link } from 'react-router'; -import { Container, ContainerTitle } from '../../../ui'; +import { Container, ContainerTitle, Tags } from '../../../ui'; import styles from './summary.css'; @@ -49,6 +49,7 @@ export default class Summary extends Component { return ( { image } +
", - "version": "1.0.0" + "version": "1.0.0", + "visible": true }, { "id": "0xd1adaede68d344519025e2ff574650cd99d3830fe6d274c7a7843cdc00e17938", @@ -13,7 +14,8 @@ "name": "Registry", "description": "A global registry of addresses on the network", "author": "Parity Team ", - "version": "1.0.0" + "version": "1.0.0", + "visible": true }, { "id": "0x0a8048117e51e964628d0f2d26342b3cd915248b59bcce2721e1d05f5cfa2208", @@ -21,7 +23,8 @@ "name": "Token Registry", "description": "A registry of transactable tokens on the network", "author": "Parity Team ", - "version": "1.0.0" + "version": "1.0.0", + "visible": true }, { "id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46", @@ -29,7 +32,8 @@ "name": "Method Registry", "description": "A registry of method signatures for lookups on transactions", "author": "Parity Team ", - "version": "1.0.0" + "version": "1.0.0", + "visible": true }, { "id": "0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75", @@ -38,6 +42,7 @@ "description": "A mapping of GitHub URLs to hashes for use in contracts as references", "author": "Parity Team ", "version": "1.0.0", + "visible": false, "secure": true } ] diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index 708c52cb5..5d9877d73 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -54,7 +54,7 @@ export default class Dapps extends Component { />
- { this.store.visible.map(this.renderApp) } + { this.store.visibleApps.map(this.renderApp) }
diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index 599dc18e9..78be6d9fb 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -14,39 +14,42 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import BigNumber from 'bignumber.js'; import { action, computed, observable, transaction } from 'mobx'; +import store from 'store'; import Contracts from '../../contracts'; import { hashToImageUrl } from '../../redux/util'; import builtinApps from './builtin.json'; -const LS_KEY_HIDDEN = 'hiddenApps'; -const LS_KEY_EXTERNAL = 'externalApps'; +const LS_KEY_DISPLAY = 'displayApps'; export default class DappsStore { @observable apps = []; - @observable externalApps = []; - @observable hiddenApps = []; + @observable displayApps = {}; @observable modalOpen = false; constructor (api) { this._api = api; - this._readHiddenApps(); - this._readExternalApps(); + this._readDisplayApps(); - this._fetchBuiltinApps(); - this._fetchLocalApps(); - this._fetchRegistryApps(); + Promise + .all([ + this._fetchBuiltinApps(), + this._fetchLocalApps(), + this._fetchRegistryApps() + ]) + .then(this._writeDisplayApps); } - @computed get visible () { - return this.apps - .filter((app) => { - return this.externalApps.includes(app.id) || !this.hiddenApps.includes(app.id); - }) - .sort((a, b) => a.name.localeCompare(b.name)); + @computed get sortedApps () { + return this.apps.sort((a, b) => a.name.localeCompare(b.name)); + } + + @computed get visibleApps () { + return this.sortedApps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible); } @action openModal = () => { @@ -58,13 +61,33 @@ export default class DappsStore { } @action hideApp = (id) => { - this.hiddenApps = this.hiddenApps.concat(id); - this._writeHiddenApps(); + const app = this.displayApps[id] || {}; + app.visible = false; + + this.displayApps[id] = app; + this._writeDisplayApps(); } @action showApp = (id) => { - this.hiddenApps = this.hiddenApps.filter((_id) => _id !== id); - this._writeHiddenApps(); + const app = this.displayApps[id] || {}; + app.visible = true; + + this.displayApps[id] = app; + this._writeDisplayApps(); + } + + @action setDisplayApps = (apps) => { + this.displayApps = apps; + } + + @action addApp = (app, visible) => { + transaction(() => { + this.apps.push(app); + + if (!this.displayApps[app.id]) { + this.displayApps[app.id] = { visible }; + } + }); } _getHost (api) { @@ -83,9 +106,12 @@ export default class DappsStore { builtinApps.forEach((app, index) => { app.type = 'builtin'; app.image = hashToImageUrl(imageIds[index]); - this.apps.push(app); + this.addApp(app, app.visible); }); }); + }) + .catch((error) => { + console.warn('DappsStore:fetchBuiltinApps', error); }); } @@ -106,7 +132,7 @@ export default class DappsStore { }) .then((apps) => { transaction(() => { - (apps || []).forEach((app) => this.apps.push(app)); + (apps || []).forEach((app) => this.addApp(app, true)); }); }) .catch((error) => { @@ -132,7 +158,9 @@ export default class DappsStore { .then((appsInfo) => { const appIds = appsInfo .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 .all([ @@ -181,7 +209,7 @@ export default class DappsStore { }) .then((apps) => { transaction(() => { - (apps || []).forEach((app) => this.apps.push(app)); + (apps || []).forEach((app) => this.addApp(app, false)); }); }) .catch((error) => { @@ -202,43 +230,11 @@ export default class DappsStore { }); } - _readHiddenApps () { - const stored = localStorage.getItem(LS_KEY_HIDDEN); - - if (stored) { - try { - this.hiddenApps = JSON.parse(stored); - } catch (error) { - console.warn('DappsStore:readHiddenApps', error); - } - } + _readDisplayApps = () => { + this.setDisplayApps(store.get(LS_KEY_DISPLAY) || {}); } - _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); - } + _writeDisplayApps = () => { + store.set(LS_KEY_DISPLAY, this.displayApps); } } From 1e155ae5484df4a360b337bf50024712ca3c93be Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 14 Nov 2016 21:57:28 +0100 Subject: [PATCH 02/22] Render selection inside areas --- js/src/views/Dapps/AddDapps/AddDapps.css | 19 +++++ js/src/views/Dapps/AddDapps/AddDapps.js | 26 ++++++- js/src/views/Dapps/dappsStore.js | 91 ++++++++++++------------ 3 files changed, 87 insertions(+), 49 deletions(-) diff --git a/js/src/views/Dapps/AddDapps/AddDapps.css b/js/src/views/Dapps/AddDapps/AddDapps.css index bff292322..be4438196 100644 --- a/js/src/views/Dapps/AddDapps/AddDapps.css +++ b/js/src/views/Dapps/AddDapps/AddDapps.css @@ -18,3 +18,22 @@ .description { 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; + } +} diff --git a/js/src/views/Dapps/AddDapps/AddDapps.js b/js/src/views/Dapps/AddDapps/AddDapps.js index 49d3c5e8d..4ff4cf42c 100644 --- a/js/src/views/Dapps/AddDapps/AddDapps.js +++ b/js/src/views/Dapps/AddDapps/AddDapps.js @@ -51,13 +51,33 @@ export default class AddDapps extends Component { ] } visible scroll> - - { store.sortedApps.map(this.renderApp) } - +
+
+ { 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 Partity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.') } ); } + renderList (items, header, byline) { + if (!items || !items.length) { + return null; + } + + return ( +
+
+
{ header }
+
{ byline }
+
+ + { items.map(this.renderApp) } + +
+ ); + } + renderApp = (app) => { const { store } = this.props; const isHidden = !store.displayApps[app.id].visible; diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index 78be6d9fb..a6f029b21 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -33,7 +33,7 @@ export default class DappsStore { constructor (api) { this._api = api; - this._readDisplayApps(); + this.readDisplayApps(); Promise .all([ @@ -41,15 +41,23 @@ export default class DappsStore { this._fetchLocalApps(), this._fetchRegistryApps() ]) - .then(this._writeDisplayApps); + .then(this.writeDisplayApps); } - @computed get sortedApps () { - return this.apps.sort((a, b) => a.name.localeCompare(b.name)); + @computed get sortedBuiltin () { + return this.apps.filter((app) => app.type === 'builtin'); + } + + @computed get sortedLocal () { + return this.apps.filter((app) => app.type === 'local'); + } + + @computed get sortedNetwork () { + return this.apps.filter((app) => app.type === 'network'); } @computed get visibleApps () { - return this.sortedApps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible); + return this.apps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible); } @action openModal = () => { @@ -61,32 +69,37 @@ export default class DappsStore { } @action hideApp = (id) => { - const app = this.displayApps[id] || {}; - app.visible = false; - - this.displayApps[id] = app; - this._writeDisplayApps(); + this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: false } }); + this.writeDisplayApps(); } @action showApp = (id) => { - const app = this.displayApps[id] || {}; - app.visible = true; - - this.displayApps[id] = app; - this._writeDisplayApps(); + this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: true } }); + this.writeDisplayApps(); } - @action setDisplayApps = (apps) => { - this.displayApps = apps; + @action readDisplayApps = () => { + this.displayApps = store.get(LS_KEY_DISPLAY) || {}; } - @action addApp = (app, visible) => { + @action writeDisplayApps = () => { + store.set(LS_KEY_DISPLAY, this.displayApps); + } + + @action addApps = (apps) => { transaction(() => { - this.apps.push(app); + this.apps = this.apps + .concat(apps || []) + .sort((a, b) => a.name.localeCompare(b.name)); - if (!this.displayApps[app.id]) { - this.displayApps[app.id] = { visible }; - } + const visibility = {}; + apps.forEach((app) => { + if (!this.displayApps[app.id]) { + visibility[app.id] = { visible: app.visible }; + } + }); + + this.displayApps = Object.assign({}, this.displayApps, visibility); }); } @@ -102,13 +115,13 @@ export default class DappsStore { return Promise .all(builtinApps.map((app) => dappReg.getImage(app.id))) .then((imageIds) => { - transaction(() => { - builtinApps.forEach((app, index) => { + this.addApps( + builtinApps.map((app, index) => { app.type = 'builtin'; app.image = hashToImageUrl(imageIds[index]); - this.addApp(app, app.visible); - }); - }); + return app; + }) + ); }) .catch((error) => { console.warn('DappsStore:fetchBuiltinApps', error); @@ -126,15 +139,12 @@ export default class DappsStore { return apps .map((app) => { app.type = 'local'; + app.visible = true; return app; }) .filter((app) => app.id && !['ui'].includes(app.id)); }) - .then((apps) => { - transaction(() => { - (apps || []).forEach((app) => this.addApp(app, true)); - }); - }) + .then(this.addApps) .catch((error) => { console.warn('DappsStore:fetchLocal', error); }); @@ -175,7 +185,8 @@ export default class DappsStore { image: hashToImageUrl(imageIds[index]), contentHash: this._api.util.bytesToHex(contentIds[index]).substr(2), manifestHash: this._api.util.bytesToHex(manifestIds[index]).substr(2), - type: 'network' + type: 'network', + visible: false }; return app; @@ -207,11 +218,7 @@ export default class DappsStore { }); }); }) - .then((apps) => { - transaction(() => { - (apps || []).forEach((app) => this.addApp(app, false)); - }); - }) + .then(this.addApps) .catch((error) => { console.warn('DappsStore:fetchRegistry', error); }); @@ -229,12 +236,4 @@ export default class DappsStore { return null; }); } - - _readDisplayApps = () => { - this.setDisplayApps(store.get(LS_KEY_DISPLAY) || {}); - } - - _writeDisplayApps = () => { - store.set(LS_KEY_DISPLAY, this.displayApps); - } } From 9bd6378e42fc23411724ecef10d0b08d0bd8d54d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 16 Nov 2016 11:37:25 +0800 Subject: [PATCH 03/22] Typo --- js/src/views/Dapps/AddDapps/AddDapps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/views/Dapps/AddDapps/AddDapps.js b/js/src/views/Dapps/AddDapps/AddDapps.js index 4ff4cf42c..72b24bd2b 100644 --- a/js/src/views/Dapps/AddDapps/AddDapps.js +++ b/js/src/views/Dapps/AddDapps/AddDapps.js @@ -55,7 +55,7 @@ export default class AddDapps extends Component { { 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 Partity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.') } + { 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.') } ); } From 501c7369b0d53cd979150653b323f913d424961d Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 16 Nov 2016 12:27:16 +0100 Subject: [PATCH 04/22] External app overlay --- js/src/views/Dapps/dapps.css | 12 ++++++++++++ js/src/views/Dapps/dapps.js | 25 ++++++++++++++++++++++--- js/src/views/Dapps/dappsStore.js | 2 +- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css index 1a38af3cf..23b9d565c 100644 --- a/js/src/views/Dapps/dapps.css +++ b/js/src/views/Dapps/dapps.css @@ -18,6 +18,7 @@ display: flex; flex-wrap: wrap; margin: -0.125em; + position: relative; } .list+.list { @@ -29,3 +30,14 @@ flex: 0 1 50%; box-sizing: border-box; } + +.overlay { + background: rgba(255, 255, 255, 0.75); + bottom: 0; + left: -0.25em; + position: absolute; + right: -0.25em; + top: 0; + z-index: 100; + padding: 1em; +} diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index 5d9877d73..b34aee7ff 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -37,6 +37,12 @@ export default class Dapps extends Component { store = new DappsStore(this.context.api); render () { + const externalOverlay = ( +
+ dismiss me +
+ ); + return (
@@ -53,14 +59,27 @@ export default class Dapps extends Component { ] } /> -
- { this.store.visibleApps.map(this.renderApp) } -
+ { this.renderList(this.store.sortedLocal) } + { this.renderList(this.store.sortedBuiltin) } + { this.renderList(this.store.sortedNetwork, externalOverlay) }
); } + renderList (items, overlay) { + if (!items || !items.length) { + return null; + } + + return ( +
+ { overlay } + { items.map(this.renderApp) } +
+ ); + } + renderApp = (app) => { return (
Date: Wed, 16 Nov 2016 12:36:15 +0100 Subject: [PATCH 05/22] Update styling --- js/src/ui/Page/page.css | 2 +- js/src/views/Dapps/dapps.css | 6 +++--- js/src/views/Dapps/dapps.js | 6 +++--- js/src/views/Dapps/dappsStore.js | 12 ++++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/js/src/ui/Page/page.css b/js/src/ui/Page/page.css index 81f4e0e68..cdb9fc868 100644 --- a/js/src/ui/Page/page.css +++ b/js/src/ui/Page/page.css @@ -19,5 +19,5 @@ } .layout>div { - padding-bottom: 0.25em; + padding-bottom: 0.75em; } diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css index 23b9d565c..64c6b2cb7 100644 --- a/js/src/views/Dapps/dapps.css +++ b/js/src/views/Dapps/dapps.css @@ -32,12 +32,12 @@ } .overlay { - background: rgba(255, 255, 255, 0.75); - bottom: 0; + background: rgba(50, 50, 50, 0.85); + bottom: 0.5em; left: -0.25em; position: absolute; right: -0.25em; - top: 0; + top: -0.25em; z-index: 100; padding: 1em; } diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index b34aee7ff..c8d67ae9d 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -59,9 +59,9 @@ export default class Dapps extends Component { ] } /> - { this.renderList(this.store.sortedLocal) } - { this.renderList(this.store.sortedBuiltin) } - { this.renderList(this.store.sortedNetwork, externalOverlay) } + { this.renderList(this.store.visibleLocal) } + { this.renderList(this.store.visibleBuiltin) } + { this.renderList(this.store.visibleNetwork, externalOverlay) }
); diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index fa76eca7c..18368fcd4 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -60,6 +60,18 @@ export default class DappsStore { 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 = () => { this.modalOpen = true; } From bbf6d1768d3bfe3648713b7c14b9843f1726f9d3 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 16 Nov 2016 13:08:08 +0100 Subject: [PATCH 06/22] Overal + acceptance in-place --- js/src/views/Dapps/dapps.css | 13 ++++++++++++- js/src/views/Dapps/dapps.js | 27 ++++++++++++++++++++++----- js/src/views/Dapps/dappsStore.js | 12 ++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css index 64c6b2cb7..ea460f0b5 100644 --- a/js/src/views/Dapps/dapps.css +++ b/js/src/views/Dapps/dapps.css @@ -32,7 +32,7 @@ } .overlay { - background: rgba(50, 50, 50, 0.85); + background: rgba(0, 0, 0, 0.85); bottom: 0.5em; left: -0.25em; position: absolute; @@ -40,4 +40,15 @@ 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; + } + } } diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index c8d67ae9d..5d87b808d 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { Checkbox } from 'material-ui'; import { observer } from 'mobx-react'; import { Actionbar, Page } from '../../ui'; @@ -37,11 +38,23 @@ export default class Dapps extends Component { store = new DappsStore(this.context.api); render () { - const externalOverlay = ( -
- dismiss me -
- ); + let externalOverlay = null; + if (this.store.externalOverlayVisible) { + externalOverlay = ( +
+
+
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.
+
+ +
+
+
+ ); + } return (
@@ -89,4 +102,8 @@ export default class Dapps extends Component {
); } + + onClickAcceptExternal = () => { + this.store.closeExternalOverlay(); + } } diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index 18368fcd4..f9fc4863c 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -24,15 +24,18 @@ import { hashToImageUrl } from '../../redux/util'; import builtinApps from './builtin.json'; const LS_KEY_DISPLAY = 'displayApps'; +const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal'; export default class DappsStore { @observable apps = []; @observable displayApps = {}; @observable modalOpen = false; + @observable externalOverlayVisible = true; constructor (api) { this._api = api; + this.loadExternalOverlay(); this.readDisplayApps(); Promise @@ -80,6 +83,15 @@ export default class DappsStore { 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) => { this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: false } }); this.writeDisplayApps(); From 0c26eddd7fb739d92af4a2b9618fb2f081299b34 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 17 Nov 2016 19:37:08 +0100 Subject: [PATCH 07/22] Better GHH event display & tracking --- .../githubhint/Application/application.css | 24 ++- .../githubhint/Application/application.js | 171 ++++++++++++++---- js/src/dapps/githubhint/Events/events.css | 37 ++++ js/src/dapps/githubhint/Events/events.js | 50 +++++ js/src/dapps/githubhint/Events/index.js | 17 ++ 5 files changed, 256 insertions(+), 43 deletions(-) create mode 100644 js/src/dapps/githubhint/Events/events.css create mode 100644 js/src/dapps/githubhint/Events/events.js create mode 100644 js/src/dapps/githubhint/Events/index.js diff --git a/js/src/dapps/githubhint/Application/application.css b/js/src/dapps/githubhint/Application/application.css index be04ecf34..61929f552 100644 --- a/js/src/dapps/githubhint/Application/application.css +++ b/js/src/dapps/githubhint/Application/application.css @@ -15,12 +15,17 @@ /* along with Parity. If not, see . */ -.container { +.body { + text-align: center; background: #333; + color: #fff; +} + +.container { font-family: 'Roboto'; vertical-align: middle; padding: 4em 0; - text-align: center; + margin: 0 0 2em 0; } .form { @@ -98,7 +103,7 @@ color: #333; background: #eee; border: none; - border-radius: 5px; + border-radius: 0.5em; width: 100%; font-size: 1em; text-align: center; @@ -113,20 +118,29 @@ } .hashError, .hashWarning, .hashOk { - padding-top: 0.5em; + margin: 0.5em 0; text-align: center; + padding: 1em 0; + border: 0.25em solid #333; + border-radius: 0.5em; } .hashError { + border-color: #f66; color: #f66; + background: rgba(255, 102, 102, 0.25); } .hashWarning { + border-color: #f80; color: #f80; + background: rgba(255, 236, 0, 0.25); } .hashOk { - opacity: 0.5; + border-color: #6f6; + color: #6f6; + background: rgba(102, 255, 102, 0.25); } .typeButtons { diff --git a/js/src/dapps/githubhint/Application/application.js b/js/src/dapps/githubhint/Application/application.js index 5a7494928..1690bf1c4 100644 --- a/js/src/dapps/githubhint/Application/application.js +++ b/js/src/dapps/githubhint/Application/application.js @@ -19,6 +19,7 @@ import React, { Component } from 'react'; import { api } from '../parity'; import { attachInterface } from '../services'; import Button from '../Button'; +import Events from '../Events'; import IdentityIcon from '../IdentityIcon'; import Loading from '../Loading'; @@ -27,6 +28,8 @@ import styles from './application.css'; const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; +let nextEventId = 0; + export default class Application extends Component { state = { fromAddress: null, @@ -43,7 +46,9 @@ export default class Application extends Component { registerState: '', registerType: 'file', repo: '', - repoError: null + repoError: null, + events: {}, + eventIds: [] } componentDidMount () { @@ -75,7 +80,7 @@ export default class Application extends Component { let hashClass = null; if (contentHashError) { hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning; - } else { + } else if (contentHash) { hashClass = styles.hashOk; } @@ -116,29 +121,34 @@ export default class Application extends Component { } return ( -
-
-
- - -
-
-
- 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. +
+
+
+
+ +
- { valueInputs } -
- { 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. +
+ { valueInputs } +
+ { contentHashError || contentHash } +
+ { registerBusy ? this.renderProgress() : this.renderButtons() }
- { registerBusy ? this.renderProgress() : this.renderButtons() }
+
); } @@ -285,15 +295,29 @@ export default class Application extends Component { } } - trackRequest (promise) { + trackRequest (eventId, promise) { return promise .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); }) .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) => { if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) { @@ -304,27 +328,72 @@ export default class Application extends Component { }); }) .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) => { 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; + contentCommit = contentCommit.substr(0, 2) === '0x' ? contentCommit : `0x${contentCommit}`; - this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' }); - - const values = [contentHash, repo, commit.substr(0, 2) === '0x' ? commit : `0x${commit}`]; + const eventId = nextEventId++; + const values = [contentHash, contentRepo, contentCommit]; 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( - instance + eventId, instance .hint.estimateGas(options, values) .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); 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; - this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' }); - - const values = [contentHash, url]; + const eventId = nextEventId++; + const values = [contentHash, contentUrl]; 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( - instance + eventId, instance .hintURL.estimateGas(options, values) .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); options.gas = gasPassed.toFixed(0); diff --git a/js/src/dapps/githubhint/Events/events.css b/js/src/dapps/githubhint/Events/events.css new file mode 100644 index 000000000..a33c09236 --- /dev/null +++ b/js/src/dapps/githubhint/Events/events.css @@ -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 . +*/ + +.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; + } +} diff --git a/js/src/dapps/githubhint/Events/events.js b/js/src/dapps/githubhint/Events/events.js new file mode 100644 index 000000000..f3b48292b --- /dev/null +++ b/js/src/dapps/githubhint/Events/events.js @@ -0,0 +1,50 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import React, { Component, PropTypes } from 'react'; +import 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 ( + + { this.props.eventIds.map((id) => this.renderEvent(id, this.props.events[id])) } +
+ ); + } + + renderEvent = (eventId, event) => { + return ( + + +
{ moment(event.timestamp).fromNow() }
+
{ event.registerState }
+ + +
{ event.contentUrl || `${event.contentRepo}/${event.contentCommit}` }
+
{ event.contentHash }
+ + + ); + } +} diff --git a/js/src/dapps/githubhint/Events/index.js b/js/src/dapps/githubhint/Events/index.js new file mode 100644 index 000000000..88ad6d407 --- /dev/null +++ b/js/src/dapps/githubhint/Events/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './events'; From 5897e01dbdc542a7a12af249c654ab47c0365d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 18 Nov 2016 11:03:29 +0100 Subject: [PATCH 08/22] Correct format of eth_signTransaction --- rpc/src/v1/helpers/dispatch.rs | 4 ++-- rpc/src/v1/impls/eth.rs | 4 ++++ rpc/src/v1/impls/signing.rs | 5 +++-- rpc/src/v1/impls/signing_unsafe.rs | 5 +++-- rpc/src/v1/tests/mocked/eth.rs | 26 ++++++++++++++++++++++---- rpc/src/v1/tests/mocked/signing.rs | 20 ++++++++++++++++++-- rpc/src/v1/traits/eth.rs | 4 ++++ rpc/src/v1/traits/eth_signing.rs | 8 ++++---- rpc/src/v1/types/confirmations.rs | 6 +++--- rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/transaction.rs | 22 +++++++++++++++++++++- 11 files changed, 85 insertions(+), 21 deletions(-) diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index cbae49cc3..a66bc816d 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -28,6 +28,7 @@ use jsonrpc_core::Error; use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; use v1::types::{ H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, + RichRawTransaction as RpcRichRawTransaction, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse, SignRequest as RpcSignRequest, @@ -47,8 +48,7 @@ pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: }, ConfirmationPayload::SignTransaction(request) => { sign_no_dispatch(client, miner, accounts, request, pass) - .map(|tx| rlp::encode(&tx).to_vec()) - .map(RpcBytes) + .map(RpcRichRawTransaction::from) .map(ConfirmationResponse::SignTransaction) }, ConfirmationPayload::Signature(address, hash) => { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 8207426ba..5f1449e07 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -619,6 +619,10 @@ impl Eth for EthClient where } } + fn submit_transaction(&self, raw: Bytes) -> Result { + self.send_raw_transaction(raw) + } + fn call(&self, request: CallRequest, num: Trailing) -> Result { try!(self.active()); diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index cf20f1045..262e04dfb 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -34,6 +34,7 @@ use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520, Either as RpcEither, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse as RpcConfirmationResponse @@ -201,11 +202,11 @@ impl EthSigning for SigningQueueClient where }); } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); self.handle_dispatch(res, |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)), e => ready.ready(Err(errors::internal("Unexpected result.", e))), } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index da9aeb901..46ffe6ded 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -31,6 +31,7 @@ use v1::types::{ U256 as RpcU256, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, Either as RpcEither, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse as RpcConfirmationResponse, @@ -100,9 +101,9 @@ impl EthSigning for SigningUnsafeClient where ready.ready(result); } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { - Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp), + Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 67e77a6db..861bb5234 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -18,8 +18,10 @@ use std::str::FromStr; use std::collections::HashMap; use std::sync::Arc; use std::time::{Instant, Duration}; +use rustc_serialize::hex::ToHex; +use time::get_time; use rlp; -use jsonrpc_core::IoHandler; + use util::{Uint, U256, Address, H256, FixedHash, Mutex}; use ethcore::account_provider::AccountProvider; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID}; @@ -28,10 +30,10 @@ use ethcore::receipt::LocalizedReceipt; use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; + +use jsonrpc_core::IoHandler; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; -use rustc_serialize::hex::ToHex; -use time::get_time; fn blockchain_client() -> Arc { 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 t = t.with_signature(signature, None); + let signature = t.signature(); 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()); diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index b43c1e180..7431bc45e 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -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 t = t.with_signature(signature, None); + let signature = t.signature(); 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 tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); let async_result = tester.io.handle_request(&request).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // 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_eq!(res, response.to_owned()); })); diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index b524c616f..6308be324 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -102,6 +102,10 @@ build_rpc_trait! { #[rpc(name = "eth_sendRawTransaction")] fn send_raw_transaction(&self, Bytes) -> Result; + /// Alias of `eth_sendRawTransaction`. + #[rpc(name = "eth_submitTransaction")] + fn submit_transaction(&self, Bytes) -> Result; + /// Call contract, returning the output data. #[rpc(name = "eth_call")] fn call(&self, CallRequest, Trailing) -> Result; diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index bd1d1ecdc..09f8c5e03 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -17,7 +17,7 @@ //! Eth rpc interface. 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! { /// Signing methods implementation relying on unlocked accounts. @@ -33,9 +33,9 @@ build_rpc_trait! { fn send_transaction(&self, Ready, TransactionRequest); /// Signs transactions without dispatching it to the network. - /// Returns signed transaction RLP representation. - /// It can be later submitted using `eth_sendRawTransaction`. + /// Returns signed transaction RLP representation and the transaction itself. + /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. #[rpc(async, name = "eth_signTransaction")] - fn sign_transaction(&self, Ready, TransactionRequest); + fn sign_transaction(&self, Ready, TransactionRequest); } } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index e83989326..2b7813df9 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -18,7 +18,7 @@ use std::fmt; 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; /// Confirmation waiting in a queue @@ -76,12 +76,12 @@ impl From<(H160, Bytes)> for DecryptRequest { } /// Confirmation response for particular payload -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum ConfirmationResponse { /// Transaction Hash SendTransaction(H256), /// Transaction RLP - SignTransaction(Bytes), + SignTransaction(RichRawTransaction), /// Signature Signature(H520), /// Decrypted data diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index b1e4ae2c9..924f12884 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -44,7 +44,7 @@ pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; pub use self::log::Log; 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::receipt::Receipt; pub use self::rpc_settings::RpcSettings; diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 0982a5ef9..1f1ef1787 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,7 +19,7 @@ use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, H160, H256, U256, H512}; /// Transaction -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct Transaction { /// Hash pub hash: H256, @@ -62,6 +62,26 @@ pub struct Transaction { 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 for RichRawTransaction { + fn from(t: SignedTransaction) -> Self { + let tx: Transaction = t.into(); + RichRawTransaction { + raw: tx.raw.clone(), + transaction: tx, + } + } +} + impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { let signature = t.signature(); From 5dcb3cbffbac59f48f2d10513030bf203d4031a6 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 12:24:32 +0100 Subject: [PATCH 09/22] Retrieve gasLimit on new blocks --- js/src/redux/providers/status.js | 9 +++++++++ js/src/redux/providers/statusReducer.js | 1 + 2 files changed, 10 insertions(+) diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index 9f47517f5..cdea11bdb 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -51,6 +51,15 @@ export default class Status { } 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) => { console.log('status._subscribeBlockNumber', 'subscriptionId', subscriptionId); diff --git a/js/src/redux/providers/statusReducer.js b/js/src/redux/providers/statusReducer.js index 98bb536ae..c6a5558db 100644 --- a/js/src/redux/providers/statusReducer.js +++ b/js/src/redux/providers/statusReducer.js @@ -28,6 +28,7 @@ const initialState = { enode: '', extraData: '', gasFloorTarget: new BigNumber(0), + gasLimit: new BigNumber(0), hashrate: new BigNumber(0), minGasPrice: new BigNumber(0), netChain: 'morden', From 55a3da8f0a205c4f6d918508854cff018d8bee89 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 18 Nov 2016 13:46:22 +0000 Subject: [PATCH 10/22] [ci skip] js-precompiled 20161118-134410 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46ddc5f07..cab5b0aee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,7 +1249,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#b2513e92603b473799d653583bd86771e0063c08" +source = "git+https://github.com/ethcore/js-precompiled.git#9f65351f2e81cbc9daf458e56dc031796695450a" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index f9ef9c181..eb6ad91b4 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.52", + "version": "0.2.53", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From ffc94343fe7898de2828e398e908af8796474894 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 18 Nov 2016 17:44:40 +0100 Subject: [PATCH 11/22] Fix version for NPM --- js/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/package.json b/js/package.json index eb6ad91b4..d3906ba4f 100644 --- a/js/package.json +++ b/js/package.json @@ -104,7 +104,7 @@ "raw-loader": "^0.5.1", "react-addons-test-utils": "^15.3.0", "react-copy-to-clipboard": "^4.2.3", - "react-hot-loader": "^1.3.0", + "react-hot-loader": "~1.3.0", "rucksack-css": "^0.8.6", "sinon": "^1.17.4", "sinon-as-promised": "^4.0.2", @@ -114,7 +114,7 @@ "webpack": "^1.13.2", "webpack-dev-server": "^1.15.2", "webpack-error-notification": "0.1.6", - "webpack-hot-middleware": "^2.7.1", + "webpack-hot-middleware": "~2.13.2", "websocket": "^1.0.23" }, "dependencies": { @@ -134,7 +134,7 @@ "js-sha3": "^0.5.2", "lodash": "^4.11.1", "marked": "^0.3.6", - "material-ui": "^0.16.1", + "material-ui": "0.16.1", "material-ui-chip-input": "^0.8.0", "mobx": "^2.6.1", "mobx-react": "^3.5.8", @@ -142,16 +142,16 @@ "moment": "^2.14.1", "phoneformat.js": "^1.0.3", "qs": "^6.3.0", - "react": "^15.2.1", + "react": "~15.3.2", "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-dom": "^15.2.1", + "react-dom": "~15.3.2", "react-dropzone": "^3.7.3", "react-redux": "^4.4.5", "react-router": "^2.6.1", "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", "recharts": "^0.15.2", "redux": "^3.5.2", From 15702a875c193359e6617c017dec3f1474a943d5 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 18 Nov 2016 17:45:00 +0100 Subject: [PATCH 12/22] Limit sync reorg (#3509) --- sync/src/block_sync.rs | 31 +++++++++++++++++++++---------- sync/src/chain.rs | 1 + 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/sync/src/block_sync.rs b/sync/src/block_sync.rs index 7c3cbf2d7..ff5411140 100644 --- a/sync/src/block_sync.rs +++ b/sync/src/block_sync.rs @@ -34,6 +34,7 @@ const MAX_RECEPITS_TO_REQUEST: usize = 128; const SUBCHAIN_SIZE: u64 = 256; const MAX_ROUND_PARENTS: usize = 32; const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5; +const MAX_REORG_BLOCKS: u64 = 20; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Downloader state @@ -262,7 +263,8 @@ impl BlockDownloader { State::Blocks => { let count = headers.len(); // 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); } self.blocks.insert_headers(headers); @@ -340,14 +342,21 @@ impl BlockDownloader { self.last_imported_hash = p.clone(); trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash); } else { - match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { - Some(h) => { - 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); + let best = io.chain().chain_info().best_block_number; + if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS { + debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash); + self.reset(); + } else { + match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { + Some(h) => { + 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 { State::Idle => { 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 => { if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index d2939fbac..ffd89ecdd 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1144,6 +1144,7 @@ impl ChainSync { let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown; if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { // 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) { self.request_blocks(io, peer_id, request, BlockSet::NewBlocks); if self.state == SyncState::Idle { From 4a3bdf13df3d27133f0ae324e6ead6d8bc9a8085 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 18 Nov 2016 17:45:19 +0100 Subject: [PATCH 13/22] Check transaction signature when adding to the queue (#3508) --- ethcore/src/miner/miner.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 84e29458d..8a87ea189 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -21,6 +21,7 @@ use util::*; use util::using_queue::{UsingQueue, GetAction}; use account_provider::AccountProvider; use views::{BlockView, HeaderView}; +use header::Header; use state::{State, CleanupMode}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use executive::contract_address; @@ -572,7 +573,16 @@ impl Miner { let schedule = chain.latest_schedule(); let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); + let best_block_header: Header = ::rlp::decode(&chain.best_block_header()); 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 { TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { transaction_queue.add(tx, origin, &fetch_account, &gas_required) From 51012d1faee0d967e001558d417057ad6c2fde45 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 18 Nov 2016 17:50:27 +0100 Subject: [PATCH 14/22] Revert "Limit sync reorganization to 20 blocks" (#3517) --- sync/src/block_sync.rs | 31 ++++++++++--------------------- sync/src/chain.rs | 1 - 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/sync/src/block_sync.rs b/sync/src/block_sync.rs index ff5411140..7c3cbf2d7 100644 --- a/sync/src/block_sync.rs +++ b/sync/src/block_sync.rs @@ -34,7 +34,6 @@ const MAX_RECEPITS_TO_REQUEST: usize = 128; const SUBCHAIN_SIZE: u64 = 256; const MAX_ROUND_PARENTS: usize = 32; const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5; -const MAX_REORG_BLOCKS: u64 = 20; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Downloader state @@ -263,8 +262,7 @@ impl BlockDownloader { State::Blocks => { let count = headers.len(); // At least one of the heades must advance the subchain. Otherwise they are all useless. - if count == 0 || !any_known { - trace!(target: "sync", "No useful headers"); + if !any_known { return Err(BlockDownloaderImportError::Useless); } self.blocks.insert_headers(headers); @@ -342,21 +340,14 @@ impl BlockDownloader { self.last_imported_hash = p.clone(); trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash); } else { - let best = io.chain().chain_info().best_block_number; - if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS { - debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash); - self.reset(); - } else { - match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { - Some(h) => { - 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(); - } + match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { + Some(h) => { + 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); } } } @@ -371,9 +362,7 @@ impl BlockDownloader { match self.state { State::Idle => { self.start_sync_round(io); - if self.state == State::ChainHead { - return self.request_blocks(io, num_active_peers); - } + return self.request_blocks(io, num_active_peers); }, State::ChainHead => { if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index ffd89ecdd..d2939fbac 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1144,7 +1144,6 @@ impl ChainSync { let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown; if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { // 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) { self.request_blocks(io, peer_id, request, BlockSet::NewBlocks); if self.state == SyncState::Idle { From 8fc1506c57b86d496dbaa68f71a6694e6854a9ee Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 18 Nov 2016 17:54:25 +0100 Subject: [PATCH 15/22] ABI can be empty and auto-fill contract name #3511 --- js/src/modals/DeployContract/DetailsStep/detailsStep.js | 6 +++++- js/src/util/validation.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 06b5a85bb..8d0a2457c 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -103,7 +103,7 @@ export default class DetailsStep extends Component { label='contract name' hint='a name for the deployed contract' error={ nameError } - value={ name } + value={ name || '' } onChange={ this.onNameChange } /> Date: Fri, 18 Nov 2016 18:12:38 +0100 Subject: [PATCH 16/22] PR grumble --- js/src/dapps/githubhint/Events/events.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/dapps/githubhint/Events/events.js b/js/src/dapps/githubhint/Events/events.js index f3b48292b..ba74ceaea 100644 --- a/js/src/dapps/githubhint/Events/events.js +++ b/js/src/dapps/githubhint/Events/events.js @@ -28,7 +28,9 @@ export default class Events extends Component { render () { return ( - { this.props.eventIds.map((id) => this.renderEvent(id, this.props.events[id])) } + + { this.props.eventIds.map((id) => this.renderEvent(id, this.props.events[id])) } +
); } From 128cd2a8e4eba6b143b9f8b3375805b273b7d0ab Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 18 Nov 2016 18:14:34 +0100 Subject: [PATCH 17/22] Fixing JS tests --- js/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/package.json b/js/package.json index d3906ba4f..93009883f 100644 --- a/js/package.json +++ b/js/package.json @@ -102,8 +102,9 @@ "postcss-nested": "^1.0.0", "postcss-simple-vars": "^3.0.0", "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-dom": "~15.3.2", "react-hot-loader": "~1.3.0", "rucksack-css": "^0.8.6", "sinon": "^1.17.4", From be1e5ae52fe616d6c6fa929905249c0820961db1 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 18:20:14 +0100 Subject: [PATCH 18/22] Updated overlay size as per comments --- js/src/views/Dapps/dapps.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css index ea460f0b5..0fb1a07b5 100644 --- a/js/src/views/Dapps/dapps.css +++ b/js/src/views/Dapps/dapps.css @@ -34,9 +34,9 @@ .overlay { background: rgba(0, 0, 0, 0.85); bottom: 0.5em; - left: -0.25em; + left: -0.125em; position: absolute; - right: -0.25em; + right: -0.125em; top: -0.25em; z-index: 100; padding: 1em; From 9c62dd3916f8175721d23bf2cf71c70944879abd Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 18 Nov 2016 19:17:35 +0100 Subject: [PATCH 19/22] Limit sync reorg to 20 blocks (#3519) * Limit sync reorg * Fixed tests --- sync/src/block_sync.rs | 31 +++++++++++++++++++++---------- sync/src/chain.rs | 1 + sync/src/tests/chain.rs | 26 +++++++++++++------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/sync/src/block_sync.rs b/sync/src/block_sync.rs index 7c3cbf2d7..ff5411140 100644 --- a/sync/src/block_sync.rs +++ b/sync/src/block_sync.rs @@ -34,6 +34,7 @@ const MAX_RECEPITS_TO_REQUEST: usize = 128; const SUBCHAIN_SIZE: u64 = 256; const MAX_ROUND_PARENTS: usize = 32; const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5; +const MAX_REORG_BLOCKS: u64 = 20; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Downloader state @@ -262,7 +263,8 @@ impl BlockDownloader { State::Blocks => { let count = headers.len(); // 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); } self.blocks.insert_headers(headers); @@ -340,14 +342,21 @@ impl BlockDownloader { self.last_imported_hash = p.clone(); trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash); } else { - match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { - Some(h) => { - 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); + let best = io.chain().chain_info().best_block_number; + if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS { + debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash); + self.reset(); + } else { + match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { + Some(h) => { + 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 { State::Idle => { 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 => { if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index d2939fbac..ffd89ecdd 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1144,6 +1144,7 @@ impl ChainSync { let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown; if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { // 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) { self.request_blocks(io, peer_id, request, BlockSet::NewBlocks); if self.state == SyncState::Idle { diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 17c051162..5fe34428e 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -79,14 +79,14 @@ fn empty_blocks() { fn forked() { ::env_logger::init().ok(); let mut net = TestNet::new(3); - net.peer_mut(0).chain.add_blocks(300, EachBlockWith::Uncle); - net.peer_mut(1).chain.add_blocks(300, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(300, EachBlockWith::Uncle); - net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing); //fork - net.peer_mut(1).chain.add_blocks(200, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(200, EachBlockWith::Uncle); - net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle); //fork between 1 and 2 - net.peer_mut(2).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer_mut(0).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer_mut(1).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer_mut(2).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork + net.peer_mut(1).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer_mut(2).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2 + net.peer_mut(2).chain.add_blocks(1, EachBlockWith::Nothing); // peer 1 has the best chain of 601 blocks let peer1_chain = net.peer(1).chain.numbers.read().clone(); net.sync(); @@ -102,12 +102,12 @@ fn forked_with_misbehaving_peer() { let mut net = TestNet::new(3); // 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.add_blocks(500, EachBlockWith::Nothing); - net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Nothing); - net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Nothing); + net.peer_mut(0).chain.add_blocks(50, EachBlockWith::Nothing); + net.peer_mut(1).chain.add_blocks(10, 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(2).chain.add_blocks(200, EachBlockWith::Uncle); + net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer_mut(2).chain.add_blocks(20, EachBlockWith::Uncle); // peer 1 should sync to peer 2, others should not change let peer0_chain = net.peer(0).chain.numbers.read().clone(); let peer2_chain = net.peer(2).chain.numbers.read().clone(); From d870b71636fd0bf95863a3ce6a3d4f6d4011f4d5 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 18 Nov 2016 18:41:39 +0000 Subject: [PATCH 20/22] updated the european warp bootnode addresses (#3528) --- ethcore/res/ethereum/frontier.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index 9bb9c50ff..2c520c46a 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -182,8 +182,8 @@ "enode://89d5dc2a81e574c19d0465f497c1af96732d1b61a41de89c2a37f35707689ac416529fae1038809852b235c2d30fd325abdc57c122feeefbeaaf802cc7e9580d@45.55.33.62:30303", "enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303", "enode://016b20125f447a3b203a3cae953b2ede8ffe51290c071e7599294be84317635730c397b8ff74404d6be412d539ee5bb5c3c700618723d3b53958c92bd33eaa82@159.203.210.80:30303", - "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@10.6.6.117:30303", - "enode://fe11ef89fc5ac9da358fc160857855f25bbf9e332c79b9ca7089330c02b728b2349988c6062f10982041702110745e203d26975a6b34bcc97144f9fe439034e8@10.1.72.117:30303" + "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", + "enode://8d91c8137890d29110b9463882f17ae4e279cd2c90cf56573187ed1c8546fca5f590a9f05e9f108eb1bd91767ed01ede4daad9e001b61727885eaa246ddb39c2@163.172.171.38:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, From 53fcfa658f8a380a65d9845ad19898533e9f19ef Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 18 Nov 2016 19:49:30 +0100 Subject: [PATCH 21/22] Fix parity.js badly built (#3526) --- js/webpack.libraries.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/js/webpack.libraries.js b/js/webpack.libraries.js index 07e40957e..889a45103 100644 --- a/js/webpack.libraries.js +++ b/js/webpack.libraries.js @@ -26,7 +26,6 @@ const DEST = process.env.BUILD_DEST || '.build'; module.exports = { context: path.join(__dirname, './src'), - target: 'node', entry: { // library 'inject': ['./web3.js'], @@ -39,14 +38,7 @@ module.exports = { library: '[name].js', libraryTarget: 'umd' }, - externals: { - 'node-fetch': 'node-fetch', - 'vertx': 'vertx' - }, module: { - noParse: [ - /babel-polyfill/ - ], loaders: [ { test: /\.js$/, From 60922b61c9313c0872b96fb54f73e1124918465c Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 18 Nov 2016 19:27:32 +0000 Subject: [PATCH 22/22] [ci skip] js-precompiled 20161118-192308 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cab5b0aee..fb441ef96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,7 +1249,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9f65351f2e81cbc9daf458e56dc031796695450a" +source = "git+https://github.com/ethcore/js-precompiled.git#427319583ccde288ba26728c14384392ddbba93d" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 93009883f..9a45fbf18 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.53", + "version": "0.2.54", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ",