From ad971a444cb66e4ee3a97a86f11a6c677ab67951 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:00:56 +0100 Subject: [PATCH 01/30] Add autoRemove functionality for api.subscribe --- js/src/api/subscriptions/manager.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index bc9632592..300ede5f8 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -59,7 +59,7 @@ export default class Manager { return subscription; } - subscribe (subscriptionName, callback) { + subscribe (subscriptionName, callback, autoRemove = false) { return new Promise((resolve, reject) => { const subscription = this._validateType(subscriptionName); @@ -75,6 +75,7 @@ export default class Manager { this.subscriptions[subscriptionId] = { name: subscriptionName, id: subscriptionId, + autoRemove, callback }; @@ -104,13 +105,16 @@ export default class Manager { const { callback } = this.subscriptions[subscriptionId]; try { - callback(error, data); + return callback(error, data); } catch (error) { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } + + return true; } _updateSubscriptions = (subscriptionName, error, data) => { + const cleanup = []; const subscriptions = this.subscriptions .filter(subscription => subscription.name === subscriptionName); @@ -118,8 +122,16 @@ export default class Manager { subscriptions .forEach((subscription) => { - this._sendData(subscription.id, error, data); + const result = this._sendData(subscription.id, error, data); + + if (subscription.autoRemove && !result) { + cleanup.push(subscription.id); + } }); + + cleanup.forEach((subscriptionId) => { + delete this.subscriptions[subscriptionId]; + }); } } From 4ce3142c630c8ca236240aa7642536fb1d2aa777 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:15:27 +0100 Subject: [PATCH 02/30] Add autoRemove functionality to api.contract.subscribe --- js/src/api/contract/contract.js | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index ed922a02c..f34cd8575 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -240,8 +240,8 @@ export default class Contract { } _bindEvent = (event) => { - event.subscribe = (options = {}, callback) => { - return this._subscribe(event, options, callback); + event.subscribe = (options = {}, callback, autoRemove = false) => { + return this._subscribe(event, options, callback, autoRemove); }; event.unsubscribe = (subscriptionId) => { @@ -307,16 +307,16 @@ export default class Contract { return this._api.eth.newFilter(options); } - subscribe (eventName = null, options = {}, callback) { + subscribe (eventName = null, options = {}, callback, autoRemove = false) { try { const event = this._findEvent(eventName); - return this._subscribe(event, options, callback); + return this._subscribe(event, options, callback, autoRemove); } catch (e) { return Promise.reject(e); } } - _subscribe (event = null, _options, callback) { + _subscribe (event = null, _options, callback, autoRemove = false) { const subscriptionId = nextSubscriptionId++; const { skipInitFetch } = _options; delete _options['skipInitFetch']; @@ -326,6 +326,7 @@ export default class Contract { .then((filterId) => { this._subscriptions[subscriptionId] = { options: _options, + autoRemove, callback, filterId }; @@ -338,7 +339,11 @@ export default class Contract { return this._api.eth .getFilterLogs(filterId) .then((logs) => { - callback(null, this.parseEventLogs(logs)); + const result = callback(null, this.parseEventLogs(logs)); + + if (autoRemove && !result) { + this.unsubscribe(subscriptionId); + } this._subscribeToChanges(); return subscriptionId; @@ -438,16 +443,22 @@ export default class Contract { }) ) .then((logsArray) => { - logsArray.forEach((logs, idx) => { + logsArray.forEach((logs, subscriptionId) => { if (!logs || !logs.length) { return; } + let result = false; + try { - subscriptions[idx].callback(null, this.parseEventLogs(logs)); + result = subscriptions[subscriptionId].callback(null, this.parseEventLogs(logs)); } catch (error) { console.error('_sendSubscriptionChanges', error); } + + if (subscriptions[subscriptionId].autoRemove && !result) { + this.unsubscribe(subscriptionId); + } }); }) .catch((error) => { From 1ecda93de97b3974fc07812d9fcc4a530f65ff5e Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:15:39 +0100 Subject: [PATCH 03/30] Align api.subscribe with contract --- js/src/api/subscriptions/manager.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index 300ede5f8..960961287 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -82,7 +82,11 @@ export default class Manager { if (!engine.isStarted) { engine.start(); } else { - this._sendData(subscriptionId, error, data); + const result = this._sendData(subscriptionId, error, data); + + if (autoRemove && !result) { + this.unsubscribe(subscriptionId); + } } resolve(subscriptionId); @@ -102,19 +106,18 @@ export default class Manager { } _sendData (subscriptionId, error, data) { - const { callback } = this.subscriptions[subscriptionId]; + let result = false; try { - return callback(error, data); + result = this.subscriptions[subscriptionId](error, data); } catch (error) { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } - return true; + return result; } _updateSubscriptions = (subscriptionName, error, data) => { - const cleanup = []; const subscriptions = this.subscriptions .filter(subscription => subscription.name === subscriptionName); @@ -125,13 +128,9 @@ export default class Manager { const result = this._sendData(subscription.id, error, data); if (subscription.autoRemove && !result) { - cleanup.push(subscription.id); + this.unsubscribe(subscription.id); } }); - - cleanup.forEach((subscriptionId) => { - delete this.subscriptions[subscriptionId]; - }); } } From 3989e2642b9665b1b90a94b39eb2016a6bbb8550 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:25:50 +0100 Subject: [PATCH 04/30] Typo --- js/src/api/subscriptions/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index 960961287..685aa243c 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -109,7 +109,7 @@ export default class Manager { let result = false; try { - result = this.subscriptions[subscriptionId](error, data); + result = this.subscriptions[subscriptionId].callback(error, data); } catch (error) { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } From 10c356de65bd269833ef12d6927a00d89ca3d9f8 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:52:21 +0100 Subject: [PATCH 05/30] Simplify, consolidate uses between subscription managers --- js/src/api/contract/contract.js | 30 ++++++++++++++++------------- js/src/api/subscriptions/manager.js | 19 +++++++----------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index f34cd8575..233115d68 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -316,6 +316,21 @@ export default class Contract { } } + _sendData (subscriptionId, error, logs) { + const { autoRemove, callback } = this._subscriptions[subscriptionId]; + let result = false; + + try { + result = callback(error, logs); + } catch (error) { + console.warn('_sendData', subscriptionId, error); + } + + if (autoRemove && !result) { + this.unsubscribe(subscriptionId); + } + } + _subscribe (event = null, _options, callback, autoRemove = false) { const subscriptionId = nextSubscriptionId++; const { skipInitFetch } = _options; @@ -339,12 +354,7 @@ export default class Contract { return this._api.eth .getFilterLogs(filterId) .then((logs) => { - const result = callback(null, this.parseEventLogs(logs)); - - if (autoRemove && !result) { - this.unsubscribe(subscriptionId); - } - + this._sendData(subscriptionId, null, this.parseEventLogs(logs)); this._subscribeToChanges(); return subscriptionId; }); @@ -448,17 +458,11 @@ export default class Contract { return; } - let result = false; - try { - result = subscriptions[subscriptionId].callback(null, this.parseEventLogs(logs)); + this.sendData(subscriptionId, null, this.parseEventLogs(logs)); } catch (error) { console.error('_sendSubscriptionChanges', error); } - - if (subscriptions[subscriptionId].autoRemove && !result) { - this.unsubscribe(subscriptionId); - } }); }) .catch((error) => { diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index 685aa243c..a2716684b 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -82,11 +82,7 @@ export default class Manager { if (!engine.isStarted) { engine.start(); } else { - const result = this._sendData(subscriptionId, error, data); - - if (autoRemove && !result) { - this.unsubscribe(subscriptionId); - } + this._sendData(subscriptionId, error, data); } resolve(subscriptionId); @@ -106,15 +102,18 @@ export default class Manager { } _sendData (subscriptionId, error, data) { + const { autoRemove, callback } = this.subscriptions[subscriptionId]; let result = false; try { - result = this.subscriptions[subscriptionId].callback(error, data); + result = callback(error, data); } catch (error) { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } - return result; + if (autoRemove && !result) { + this.unsubscribe(subscriptionId); + } } _updateSubscriptions = (subscriptionName, error, data) => { @@ -125,11 +124,7 @@ export default class Manager { subscriptions .forEach((subscription) => { - const result = this._sendData(subscription.id, error, data); - - if (subscription.autoRemove && !result) { - this.unsubscribe(subscription.id); - } + this._sendData(subscription.id, error, data); }); } } From 13f962ae01c981ab4d4f7972af80b24dd951e246 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:56:12 +0100 Subject: [PATCH 06/30] Attempt to fix ci webpack --- js/src/api/contract/contract.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 233115d68..2ef24c685 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -240,7 +240,7 @@ export default class Contract { } _bindEvent = (event) => { - event.subscribe = (options = {}, callback, autoRemove = false) => { + event.subscribe = (options = {}, callback, autoRemove) => { return this._subscribe(event, options, callback, autoRemove); }; @@ -307,7 +307,7 @@ export default class Contract { return this._api.eth.newFilter(options); } - subscribe (eventName = null, options = {}, callback, autoRemove = false) { + subscribe (eventName = null, options = {}, callback, autoRemove) { try { const event = this._findEvent(eventName); return this._subscribe(event, options, callback, autoRemove); From b76a717720dec7e3688f9054da1e4cec23e324df Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 00:58:53 +0100 Subject: [PATCH 07/30] Fix & with && --- .gitlab-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2460678d4..14f7b41e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -108,9 +108,9 @@ linux-centos: paths: - target/release/parity name: "x86_64-unknown-centos-gnu_parity" -linux-i686: - stage: build - image: ethcore/rust-i686:latest +linux-i686: + stage: build + image: ethcore/rust-i686:latest only: - beta - tags @@ -348,7 +348,7 @@ windows: - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt - set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64 - set RUST_BACKTRACE=1 - - set RUSTFLAGS=%RUSTFLAGS% + - set RUSTFLAGS=%RUSTFLAGS% - rustup default stable-x86_64-pc-windows-msvc - cargo build -j 8 --release #%CARGOFLAGS% - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll @@ -428,7 +428,7 @@ test-rust-stable: script: - export RUST_BACKTRACE=1 - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"&./test.sh $CARGOFLAGS; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test" && ./test.sh $CARGOFLAGS; else echo "skip rust test" && ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust - rust-stable @@ -443,7 +443,7 @@ js-test: script: - export RUST_BACKTRACE=1 - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else echo "skip rust test" && ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust - rust-stable @@ -489,6 +489,6 @@ js-release: - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/install-deps.sh;fi script: - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/build.sh&&./js/scripts/release.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi tags: - javascript From 12592689ddef95f79364ab87a3694c86543569fe Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 9 Dec 2016 01:44:45 +0000 Subject: [PATCH 08/30] [ci skip] js-precompiled 20161209-014235 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff4c35734..6d9374ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#f5365b857b006ed60c02eb9360d60d7ddef65104" +source = "git+https://github.com/ethcore/js-precompiled.git#b3ffa8d1bb55be78f623770c745ac91861ec5c28" 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 4cbac5a75..f75b28bda 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.100", + "version": "0.2.101", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 83f791fa5d1d2387a330b89738a851b30bd7e96a Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 9 Dec 2016 04:43:19 +0000 Subject: [PATCH 09/30] [ci skip] js-precompiled 20161209-044115 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d9374ef6..5795de427 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#b3ffa8d1bb55be78f623770c745ac91861ec5c28" +source = "git+https://github.com/ethcore/js-precompiled.git#1bf7160f6c8f25353d790dbd0935560d3d395727" 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 f75b28bda..4be14d531 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.101", + "version": "0.2.102", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 22a63f6fd391a31ddc263d51bc0be5578b6288dc Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 9 Dec 2016 11:45:16 +0100 Subject: [PATCH 10/30] Added IO Service explicit stop --- util/io/src/service.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/util/io/src/service.rs b/util/io/src/service.rs index 5d773f525..d7bba5b4d 100644 --- a/util/io/src/service.rs +++ b/util/io/src/service.rs @@ -430,6 +430,20 @@ impl IoService where Message: Send + Sync + Clone + 'static { }) } + pub fn stop(&mut self) { + trace!(target: "shutdown", "[IoService] Closing..."); + // Clear handlers so that shared pointers are not stuck on stack + // in Channel::send_sync + self.handlers.write().clear(); + self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e)); + if let Some(thread) = self.thread.take() { + thread.join().unwrap_or_else(|e| { + debug!(target: "shutdown", "Error joining IO service event loop thread: {:?}", e); + }); + } + trace!(target: "shutdown", "[IoService] Closed."); + } + /// Regiter an IO handler with the event loop. pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { try!(self.host_channel.lock().send(IoMessage::AddHandler { @@ -452,17 +466,7 @@ impl IoService where Message: Send + Sync + Clone + 'static { impl Drop for IoService where Message: Send + Sync + Clone { fn drop(&mut self) { - trace!(target: "shutdown", "[IoService] Closing..."); - // Clear handlers so that shared pointers are not stuck on stack - // in Channel::send_sync - self.handlers.write().clear(); - self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e)); - if let Some(thread) = self.thread.take() { - thread.join().unwrap_or_else(|e| { - debug!(target: "shutdown", "Error joining IO service event loop thread: {:?}", e); - }); - } - trace!(target: "shutdown", "[IoService] Closed."); + self.stop() } } From 364dd9dda4b7d3c880323e593795a610612ba2ca Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 9 Dec 2016 13:25:02 +0100 Subject: [PATCH 11/30] Make stop const --- util/io/src/service.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/io/src/service.rs b/util/io/src/service.rs index d7bba5b4d..6086acadd 100644 --- a/util/io/src/service.rs +++ b/util/io/src/service.rs @@ -394,7 +394,7 @@ impl IoChannel where Message: Send + Clone + Sync + 'static { /// 'Message' is a notification message type pub struct IoService where Message: Send + Sync + Clone + 'static { panic_handler: Arc, - thread: Option>, + thread: Mutex>>, host_channel: Mutex>>, handlers: Arc>, HandlerId>>>, } @@ -424,19 +424,19 @@ impl IoService where Message: Send + Sync + Clone + 'static { }); Ok(IoService { panic_handler: panic_handler, - thread: Some(thread), + thread: Mutex::new(Some(thread)), host_channel: Mutex::new(channel), handlers: handlers, }) } - pub fn stop(&mut self) { + pub fn stop(&self) { trace!(target: "shutdown", "[IoService] Closing..."); // Clear handlers so that shared pointers are not stuck on stack // in Channel::send_sync self.handlers.write().clear(); self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e)); - if let Some(thread) = self.thread.take() { + if let Some(thread) = self.thread.lock().take() { thread.join().unwrap_or_else(|e| { debug!(target: "shutdown", "Error joining IO service event loop thread: {:?}", e); }); From befcc9cc1a432b3ec7563418c0dcbe4ec9f83320 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 13:44:10 +0100 Subject: [PATCH 12/30] Cleanups (#3742) * Remove unused file * Combine tx checks into single file * Move UI components into UI * DRY links * Unused rollup config * Cleanup util paths * Revert "Move UI components into UI" This reverts commit 3379e61246cde635c296d31322b71e63395a5cd4. * Re-apply ~/util/tx move * Cleanup unused styles --- js/src/3rdparty/etherscan/links.js | 8 ++- js/src/jsonrpc/rollup.config.js | 12 ----- js/src/modals/AddAddress/addAddress.js | 2 +- js/src/modals/AddContract/addContract.js | 2 +- .../modals/CreateWallet/createWalletStore.js | 5 +- .../DeployContract/DetailsStep/detailsStep.js | 4 +- .../ParametersStep/parametersStep.js | 2 +- .../modals/DeployContract/deployContract.js | 2 +- js/src/modals/EditMeta/editMeta.js | 2 +- .../modals/ExecuteContract/executeContract.js | 4 +- js/src/modals/SMSVerification/store.js | 5 +- js/src/modals/SaveContract/saveContract.js | 2 +- js/src/modals/Shapeshift/shapeshift.js | 2 +- js/src/redux/providers/walletActions.js | 6 +-- js/src/ui/Form/TypedInput/typedInput.js | 3 +- js/src/ui/TxHash/txHash.js | 2 +- js/src/ui/TxList/txList.js | 2 +- js/src/util/check-if-tx-failed.js | 28 ---------- js/src/util/is-testnet.js | 19 ------- ...{wait-for-block-confirmations.js => tx.js} | 17 ++++-- .../Account/Transactions/transactions.css | 3 -- .../Account/Transactions/transactions.js | 2 +- js/src/views/Account/account.css | 2 - js/src/views/Account/account.js | 2 +- js/src/views/Accounts/accounts.css | 2 - js/src/views/Accounts/accounts.js | 2 +- js/src/views/Address/address.css | 52 +++++++++---------- js/src/views/Address/address.js | 4 +- js/src/views/Addresses/addresses.css | 20 ++++--- js/src/views/Addresses/addresses.js | 2 +- js/src/views/Connection/connection.js | 1 - js/src/views/Contract/Events/Event/event.js | 2 +- js/src/views/Contract/contract.css | 26 ++++------ js/src/views/Contract/contract.js | 2 +- js/src/views/Contracts/contracts.css | 18 ------- js/src/views/Contracts/contracts.js | 5 +- js/src/views/Settings/settings.css | 44 +++++++--------- js/src/views/Settings/settings.js | 2 +- .../Account/AccountLink/AccountLink.js | 2 +- .../components/TxHashLink/TxHashLink.js | 2 +- .../Signer/containers/Embedded/embedded.css | 3 -- .../Signer/containers/Embedded/embedded.js | 2 +- .../containers/RequestsPage/RequestsPage.css | 6 --- .../containers/RequestsPage/RequestsPage.js | 5 +- js/src/views/Signer/signer.css | 24 --------- js/src/views/Signer/signer.js | 4 +- .../containers/StatusPage/StatusPage.js | 4 +- .../containers/StatusPage/statusPage.css | 18 ------- 48 files changed, 118 insertions(+), 272 deletions(-) delete mode 100644 js/src/jsonrpc/rollup.config.js delete mode 100644 js/src/util/check-if-tx-failed.js delete mode 100644 js/src/util/is-testnet.js rename js/src/util/{wait-for-block-confirmations.js => tx.js} (73%) delete mode 100644 js/src/views/Contracts/contracts.css delete mode 100644 js/src/views/Signer/signer.css delete mode 100644 js/src/views/Status/containers/StatusPage/statusPage.css diff --git a/js/src/3rdparty/etherscan/links.js b/js/src/3rdparty/etherscan/links.js index 2745873fc..e85572850 100644 --- a/js/src/3rdparty/etherscan/links.js +++ b/js/src/3rdparty/etherscan/links.js @@ -14,10 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +export const url = (isTestnet = false) => { + return `https://${isTestnet ? 'testnet.' : ''}etherscan.io`; +}; + export const txLink = (hash, isTestnet = false) => { - return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/tx/${hash}`; + return `${url(isTestnet)}/tx/${hash}`; }; export const addressLink = (address, isTestnet = false) => { - return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/address/${address}`; + return `${url(isTestnet)}/address/${address}`; }; diff --git a/js/src/jsonrpc/rollup.config.js b/js/src/jsonrpc/rollup.config.js deleted file mode 100644 index cc4f8c931..000000000 --- a/js/src/jsonrpc/rollup.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import babel from 'rollup-plugin-babel'; - -export default { - entry: 'src/index.js', - dest: 'release/index.js', - format: 'cjs', - plugins: [babel({ - babelrc: false, - presets: ['es2015-rollup', 'stage-0'], - runtimeHelpers: true - })] -}; diff --git a/js/src/modals/AddAddress/addAddress.js b/js/src/modals/AddAddress/addAddress.js index 590287e73..e44cb0b3c 100644 --- a/js/src/modals/AddAddress/addAddress.js +++ b/js/src/modals/AddAddress/addAddress.js @@ -19,7 +19,7 @@ import ContentAdd from 'material-ui/svg-icons/content/add'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { Button, Modal, Form, Input, InputAddress } from '~/ui'; -import { ERRORS, validateAddress, validateName } from '../../util/validation'; +import { ERRORS, validateAddress, validateName } from '~/util/validation'; export default class AddAddress extends Component { static contextTypes = { diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js index 71f8a911d..b19398001 100644 --- a/js/src/modals/AddContract/addContract.js +++ b/js/src/modals/AddContract/addContract.js @@ -21,7 +21,7 @@ import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forwa import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui'; -import { ERRORS, validateAbi, validateAddress, validateName } from '../../util/validation'; +import { ERRORS, validateAbi, validateAddress, validateName } from '~/util/validation'; import { eip20, wallet } from '~/contracts/abi'; diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index d8c308a12..1d3aa6ee2 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -16,13 +16,12 @@ import { observable, computed, action, transaction } from 'mobx'; -import { validateUint, validateAddress, validateName } from '~/util/validation'; -import { ERROR_CODES } from '~/api/transport/error'; - import Contract from '~/api/contract'; +import { ERROR_CODES } from '~/api/transport/error'; import { wallet as walletAbi } from '~/contracts/abi'; import { wallet as walletCode } from '~/contracts/code'; +import { validateUint, validateAddress, validateName } from '~/util/validation'; import WalletsUtils from '~/util/wallets'; const STEPS = { diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 54ef8f850..aa0a30e55 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -18,8 +18,8 @@ import React, { Component, PropTypes } from 'react'; import { MenuItem } from 'material-ui'; import { AddressSelect, Form, Input, Select } from '~/ui'; -import { validateAbi } from '../../../util/validation'; -import { parseAbiType } from '../../../util/abi'; +import { validateAbi } from '~/util/validation'; +import { parseAbiType } from '~/util/abi'; export default class DetailsStep extends Component { static contextTypes = { diff --git a/js/src/modals/DeployContract/ParametersStep/parametersStep.js b/js/src/modals/DeployContract/ParametersStep/parametersStep.js index 4c7228b67..4ab5df693 100644 --- a/js/src/modals/DeployContract/ParametersStep/parametersStep.js +++ b/js/src/modals/DeployContract/ParametersStep/parametersStep.js @@ -32,7 +32,7 @@ import React, { Component, PropTypes } from 'react'; import { Form, TypedInput } from '~/ui'; -import { parseAbiType } from '../../../util/abi'; +import { parseAbiType } from '~/util/abi'; import styles from '../deployContract.css'; diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index ae5578e40..5bf4fc389 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -19,7 +19,7 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, TxHash } from '~/ui'; -import { ERRORS, validateAbi, validateCode, validateName } from '../../util/validation'; +import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation'; import DetailsStep from './DetailsStep'; import ParametersStep from './ParametersStep'; diff --git a/js/src/modals/EditMeta/editMeta.js b/js/src/modals/EditMeta/editMeta.js index ad26c19fe..8ab9233f1 100644 --- a/js/src/modals/EditMeta/editMeta.js +++ b/js/src/modals/EditMeta/editMeta.js @@ -19,7 +19,7 @@ import ContentClear from 'material-ui/svg-icons/content/clear'; import ContentSave from 'material-ui/svg-icons/content/save'; import { Button, Form, Input, InputChip, Modal } from '~/ui'; -import { validateName } from '../../util/validation'; +import { validateName } from '~/util/validation'; export default class EditMeta extends Component { static contextTypes = { diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 4a708d17a..2db5e2b04 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -21,8 +21,8 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui'; -import { MAX_GAS_ESTIMATION } from '../../util/constants'; -import { validateAddress, validateUint } from '../../util/validation'; +import { MAX_GAS_ESTIMATION } from '~/util/constants'; +import { validateAddress, validateUint } from '~/util/validation'; import { parseAbiType } from '~/util/abi'; import DetailsStep from './DetailsStep'; diff --git a/js/src/modals/SMSVerification/store.js b/js/src/modals/SMSVerification/store.js index 49b91fa70..7a132aaf7 100644 --- a/js/src/modals/SMSVerification/store.js +++ b/js/src/modals/SMSVerification/store.js @@ -21,9 +21,8 @@ import { sha3 } from '~/api/util/sha3'; import Contracts from '~/contracts'; import { checkIfVerified, checkIfRequested, awaitPuzzle } from '~/contracts/sms-verification'; -import { postToServer } from '../../3rdparty/sms-verification'; -import checkIfTxFailed from '../../util/check-if-tx-failed'; -import waitForConfirmations from '../../util/wait-for-block-confirmations'; +import { postToServer } from '~/3rdparty/sms-verification'; +import { checkIfTxFailed, waitForConfirmations } from '~/util/tx'; export const LOADING = 'fetching-contract'; export const QUERY_DATA = 'query-data'; diff --git a/js/src/modals/SaveContract/saveContract.js b/js/src/modals/SaveContract/saveContract.js index c73fd8a21..5d7863632 100644 --- a/js/src/modals/SaveContract/saveContract.js +++ b/js/src/modals/SaveContract/saveContract.js @@ -20,7 +20,7 @@ import SaveIcon from 'material-ui/svg-icons/content/save'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { Button, Modal, Editor, Form, Input } from '~/ui'; -import { ERRORS, validateName } from '../../util/validation'; +import { ERRORS, validateName } from '~/util/validation'; import styles from './saveContract.css'; diff --git a/js/src/modals/Shapeshift/shapeshift.js b/js/src/modals/Shapeshift/shapeshift.js index 562c8cbf3..9c5d696b0 100644 --- a/js/src/modals/Shapeshift/shapeshift.js +++ b/js/src/modals/Shapeshift/shapeshift.js @@ -19,7 +19,7 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { Button, IdentityIcon, Modal } from '~/ui'; -import initShapeshift from '../../3rdparty/shapeshift'; +import initShapeshift from '~/3rdparty/shapeshift'; import shapeshiftLogo from '../../../assets/images/shapeshift-logo.png'; import AwaitingDepositStep from './AwaitingDepositStep'; diff --git a/js/src/redux/providers/walletActions.js b/js/src/redux/providers/walletActions.js index 8e13ac1a0..4a8a3f82a 100644 --- a/js/src/redux/providers/walletActions.js +++ b/js/src/redux/providers/walletActions.js @@ -17,12 +17,10 @@ import { isEqual, uniq } from 'lodash'; import Contract from '~/api/contract'; -import { wallet as WALLET_ABI } from '~/contracts/abi'; import { bytesToHex, toHex } from '~/api/util/format'; - import { ERROR_CODES } from '~/api/transport/error'; -import { MAX_GAS_ESTIMATION } from '../../util/constants'; - +import { wallet as WALLET_ABI } from '~/contracts/abi'; +import { MAX_GAS_ESTIMATION } from '~/util/constants'; import WalletsUtils from '~/util/wallets'; import { newError } from '~/ui/Errors/actions'; diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js index 9162348e0..a81ec7b79 100644 --- a/js/src/ui/Form/TypedInput/typedInput.js +++ b/js/src/ui/Form/TypedInput/typedInput.js @@ -22,12 +22,11 @@ import IconButton from 'material-ui/IconButton'; import AddIcon from 'material-ui/svg-icons/content/add'; import RemoveIcon from 'material-ui/svg-icons/content/remove'; +import { fromWei, toWei } from '~/api/util/wei'; import Input from '~/ui/Form/Input'; import InputAddressSelect from '~/ui/Form/InputAddressSelect'; import Select from '~/ui/Form/Select'; - import { ABI_TYPES } from '~/util/abi'; -import { fromWei, toWei } from '~/api/util/wei'; import styles from './typedInput.css'; diff --git a/js/src/ui/TxHash/txHash.js b/js/src/ui/TxHash/txHash.js index 81c4e0bf5..715568fd9 100644 --- a/js/src/ui/TxHash/txHash.js +++ b/js/src/ui/TxHash/txHash.js @@ -20,7 +20,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { LinearProgress } from 'material-ui'; -import { txLink } from '../../3rdparty/etherscan/links'; +import { txLink } from '~/3rdparty/etherscan/links'; import ShortenedHash from '../ShortenedHash'; import styles from './txHash.css'; diff --git a/js/src/ui/TxList/txList.js b/js/src/ui/TxList/txList.js index b8c53c1d9..3f5dab264 100644 --- a/js/src/ui/TxList/txList.js +++ b/js/src/ui/TxList/txList.js @@ -20,7 +20,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { observer } from 'mobx-react'; -import { txLink, addressLink } from '../../3rdparty/etherscan/links'; +import { txLink, addressLink } from '~/3rdparty/etherscan/links'; import IdentityIcon from '../IdentityIcon'; import IdentityName from '../IdentityName'; diff --git a/js/src/util/check-if-tx-failed.js b/js/src/util/check-if-tx-failed.js deleted file mode 100644 index 39689bedd..000000000 --- a/js/src/util/check-if-tx-failed.js +++ /dev/null @@ -1,28 +0,0 @@ -// 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 . - -const checkIfTxFailed = (api, tx, gasSent) => { - return api.pollMethod('eth_getTransactionReceipt', tx) - .then((receipt) => { - // TODO: Right now, there's no way to tell wether the EVM code crashed. - // Because you usually send a bit more gas than estimated (to make sure - // it gets mined quickly), we transaction probably failed if all the gas - // has been used up. - return receipt.gasUsed.eq(gasSent); - }); -}; - -export default checkIfTxFailed; diff --git a/js/src/util/is-testnet.js b/js/src/util/is-testnet.js deleted file mode 100644 index c2bf2c450..000000000 --- a/js/src/util/is-testnet.js +++ /dev/null @@ -1,19 +0,0 @@ -// 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 (chain) => { - return chain === 'morden' || chain === 'ropsten' || chain === 'testnet'; -}; diff --git a/js/src/util/wait-for-block-confirmations.js b/js/src/util/tx.js similarity index 73% rename from js/src/util/wait-for-block-confirmations.js rename to js/src/util/tx.js index 79ba2be25..b688d06fe 100644 --- a/js/src/util/wait-for-block-confirmations.js +++ b/js/src/util/tx.js @@ -18,7 +18,18 @@ const isValidReceipt = (receipt) => { return receipt && receipt.blockNumber && receipt.blockNumber.gt(0); }; -const waitForConfirmations = (api, tx, confirmations) => { +export function checkIfTxFailed (api, tx, gasSent) { + return api.pollMethod('eth_getTransactionReceipt', tx) + .then((receipt) => { + // TODO: Right now, there's no way to tell wether the EVM code crashed. + // Because you usually send a bit more gas than estimated (to make sure + // it gets mined quickly), we transaction probably failed if all the gas + // has been used up. + return receipt.gasUsed.eq(gasSent); + }); +} + +export function waitForConfirmations (api, tx, confirmations) { return new Promise((resolve, reject) => { api.pollMethod('eth_getTransactionReceipt', tx, isValidReceipt) .then((receipt) => { @@ -39,6 +50,4 @@ const waitForConfirmations = (api, tx, confirmations) => { .catch(reject); }); }); -}; - -export default waitForConfirmations; +} diff --git a/js/src/views/Account/Transactions/transactions.css b/js/src/views/Account/Transactions/transactions.css index 13d727deb..6b44d298f 100644 --- a/js/src/views/Account/Transactions/transactions.css +++ b/js/src/views/Account/Transactions/transactions.css @@ -15,9 +15,6 @@ /* along with Parity. If not, see . */ -.transactions { -} - .infonone { opacity: 0.25; } diff --git a/js/src/views/Account/Transactions/transactions.js b/js/src/views/Account/Transactions/transactions.js index e71781adf..2e98208b6 100644 --- a/js/src/views/Account/Transactions/transactions.js +++ b/js/src/views/Account/Transactions/transactions.js @@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import etherscan from '../../../3rdparty/etherscan'; +import etherscan from '~/3rdparty/etherscan'; import { Container, TxList } from '~/ui'; import styles from './transactions.css'; diff --git a/js/src/views/Account/account.css b/js/src/views/Account/account.css index b1410f23a..a9a039c83 100644 --- a/js/src/views/Account/account.css +++ b/js/src/views/Account/account.css @@ -14,8 +14,6 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ -.account { -} .btnicon { width: 24px; diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index 98b0a5e97..cbacd5280 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -105,7 +105,7 @@ class Account extends Component { } return ( -
+
{ this.renderDeleteDialog(account) } { this.renderEditDialog(account) } { this.renderFundDialog() } diff --git a/js/src/views/Accounts/accounts.css b/js/src/views/Accounts/accounts.css index 469f33ddc..317905fd5 100644 --- a/js/src/views/Accounts/accounts.css +++ b/js/src/views/Accounts/accounts.css @@ -14,8 +14,6 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ -.accounts { -} .accountTooltip { top: 13.3em; diff --git a/js/src/views/Accounts/accounts.js b/js/src/views/Accounts/accounts.js index a8a29945f..4383b7662 100644 --- a/js/src/views/Accounts/accounts.js +++ b/js/src/views/Accounts/accounts.js @@ -82,7 +82,7 @@ class Accounts extends Component { render () { return ( -
+
{ this.renderNewDialog() } { this.renderNewWalletDialog() } { this.renderActionbar() } diff --git a/js/src/views/Address/address.css b/js/src/views/Address/address.css index d93b88b59..cd44688bb 100644 --- a/js/src/views/Address/address.css +++ b/js/src/views/Address/address.css @@ -14,37 +14,37 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ -.address { -} -.delete .hero { - padding-bottom: 1em; -} +.delete { + .hero { + padding-bottom: 1em; + } -.delete .info { - display: inline-block; -} + .info { + display: inline-block; + } -.delete .icon { - display: inline-block; -} + .icon { + display: inline-block; + } -.delete .nameinfo { - display: inline-block; - text-align: left; -} + .nameinfo { + display: inline-block; + text-align: left; + } -.delete .header { - text-transform: uppercase; - font-size: 1.25em; - padding-bottom: 0.25em; -} + .header { + text-transform: uppercase; + font-size: 1.25em; + padding-bottom: 0.25em; + } -.delete .address { -} + .address { + } -.delete .description { - padding-top: 1em; - font-size: 0.75em; - color: #aaa; + .description { + padding-top: 1em; + font-size: 0.75em; + color: #aaa; + } } diff --git a/js/src/views/Address/address.js b/js/src/views/Address/address.js index 0fae3cb1f..c1427b2be 100644 --- a/js/src/views/Address/address.js +++ b/js/src/views/Address/address.js @@ -28,8 +28,6 @@ import Transactions from '../Account/Transactions'; import Delete from './Delete'; import { setVisibleAccounts } from '~/redux/providers/personalActions'; -import styles from './address.css'; - class Address extends Component { static contextTypes = { api: PropTypes.object.isRequired, @@ -85,7 +83,7 @@ class Address extends Component { } return ( -
+
{ this.renderEditDialog(contact) } { this.renderActionbar(contact) } . */ -.addresses { -} .list { display: flex; @@ -26,21 +24,21 @@ flex: 0 1 50%; width: 50%; position: relative; -} -.address:nth-child(odd)>div { - padding-right: 0.5em !important; -} + &:nth-child(odd)>div { + padding-right: 0.5em !important; + } -.address:nth-child(even)>div { - padding-left: 0.5em !important; + &:nth-child(even)>div { + padding-left: 0.5em !important; + } } .empty { width: 100%; display: block; -} -.empty div { - color: #aaa; + div { + color: #aaa; + } } diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js index 4edfb0e6e..5e0ed4e18 100644 --- a/js/src/views/Addresses/addresses.js +++ b/js/src/views/Addresses/addresses.js @@ -76,7 +76,7 @@ class Addresses extends Component { const { searchValues, sortOrder } = this.state; return ( -
+
{ this.renderActionbar() } { this.renderAddAddress() } diff --git a/js/src/views/Connection/connection.js b/js/src/views/Connection/connection.js index 451624a2b..ad0e0c140 100644 --- a/js/src/views/Connection/connection.js +++ b/js/src/views/Connection/connection.js @@ -19,7 +19,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import ActionCompareArrows from 'material-ui/svg-icons/action/compare-arrows'; import ActionDashboard from 'material-ui/svg-icons/action/dashboard'; -// import CommunicationVpnKey from 'material-ui/svg-icons/communication/vpn-key'; import HardwareDesktopMac from 'material-ui/svg-icons/hardware/desktop-mac'; import NotificationVpnLock from 'material-ui/svg-icons/notification/vpn-lock'; diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js index b42295198..1ed114d07 100644 --- a/js/src/views/Contract/Events/Event/event.js +++ b/js/src/views/Contract/Events/Event/event.js @@ -20,7 +20,7 @@ import React, { Component, PropTypes } from 'react'; import { IdentityIcon, IdentityName, Input, InputAddress } from '~/ui'; import ShortenedHash from '~/ui/ShortenedHash'; -import { txLink } from '../../../../3rdparty/etherscan/links'; +import { txLink } from '~/3rdparty/etherscan/links'; import styles from '../../contract.css'; diff --git a/js/src/views/Contract/contract.css b/js/src/views/Contract/contract.css index eb6cabfa2..f49da0831 100644 --- a/js/src/views/Contract/contract.css +++ b/js/src/views/Contract/contract.css @@ -15,26 +15,26 @@ /* along with Parity. If not, see . */ -.contract { -} - .events { width: 100%; border: none; border-spacing: 0; -} -.events tr { - line-height: 32px; - vertical-align: top; + tr { + line-height: 32px; + vertical-align: top; + } } .event { -} + td { + vertical-align: top; + padding: 1em 0.5em; -.event td { - vertical-align: top; - padding: 1em 0.5em; + div { + white-space: nowrap; + } + } } .txhash { @@ -47,10 +47,6 @@ color: #aaa; } -.event td div { - white-space: nowrap; -} - .mined { } diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js index 2aa37f847..35ad95fe2 100644 --- a/js/src/views/Contract/contract.js +++ b/js/src/views/Contract/contract.js @@ -124,7 +124,7 @@ class Contract extends Component { } return ( -
+
{ this.renderActionbar(account) } { this.renderDeleteDialog(account) } { this.renderEditDialog(account) } diff --git a/js/src/views/Contracts/contracts.css b/js/src/views/Contracts/contracts.css deleted file mode 100644 index eab0858e7..000000000 --- a/js/src/views/Contracts/contracts.css +++ /dev/null @@ -1,18 +0,0 @@ -/* 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 . -*/ -.contracts { -} diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index d97d88b09..b84705b32 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -28,8 +28,6 @@ import { setVisibleAccounts } from '~/redux/providers/personalActions'; import List from '../Accounts/List'; -import styles from './contracts.css'; - class Contracts extends Component { static contextTypes = { api: PropTypes.object.isRequired @@ -80,7 +78,7 @@ class Contracts extends Component { const { searchValues, sortOrder } = this.state; return ( -
+
{ this.renderActionbar() } { this.renderAddContract() } { this.renderAddContract() } @@ -159,7 +157,6 @@ class Contracts extends Component { return ( ); diff --git a/js/src/views/Settings/settings.css b/js/src/views/Settings/settings.css index 91138db68..1eab3aac3 100644 --- a/js/src/views/Settings/settings.css +++ b/js/src/views/Settings/settings.css @@ -15,9 +15,6 @@ /* along with Parity. If not, see . */ -.layout { -} - .menu { display: inline-block; } @@ -35,31 +32,24 @@ padding: 16px 2em !important; line-height: 24px !important; width: auto !important; -} -.tabactive { -} + &>div { + height: 24px !important; -.tab>div, -.tabactive>div { - height: 24px !important; -} + &>div { + display: inline-block !important; + } + } -.tab>div>div, -.tabactive>div>div { - display: inline-block !important; -} + svg { + margin-right: 0.5em; + margin-bottom: 0 !important; + } -.tab svg, -.tabactive svg { - margin-right: 0.5em; - margin-bottom: 0 !important; -} - -.tab .menu, -.tabactive .menu { - vertical-align: top; - display: inline-block; + .menu { + vertical-align: top; + display: inline-block; + } } .imageIcon { @@ -68,6 +58,8 @@ opacity: 0.5; } -.tabactive .imageIcon { - opacity: 1; +.tabactive { + .imageIcon { + opacity: 1; + } } diff --git a/js/src/views/Settings/settings.js b/js/src/views/Settings/settings.js index 54b4fa6dd..a6c455dad 100644 --- a/js/src/views/Settings/settings.js +++ b/js/src/views/Settings/settings.js @@ -45,7 +45,7 @@ export default class Settings extends Component { } return ( -
+
{ this.renderTab(hash, 'views', ) } diff --git a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js index 97ff35ce9..f42675474 100644 --- a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js +++ b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { addressLink } from '../../../../../3rdparty/etherscan/links'; +import { addressLink } from '~/3rdparty/etherscan/links'; import styles from './AccountLink.css'; export default class AccountLink extends Component { diff --git a/js/src/views/Signer/components/TxHashLink/TxHashLink.js b/js/src/views/Signer/components/TxHashLink/TxHashLink.js index 5fbd5695e..bce30eded 100644 --- a/js/src/views/Signer/components/TxHashLink/TxHashLink.js +++ b/js/src/views/Signer/components/TxHashLink/TxHashLink.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { txLink } from '../../../../3rdparty/etherscan/links'; +import { txLink } from '~/3rdparty/etherscan/links'; export default class TxHashLink extends Component { diff --git a/js/src/views/Signer/containers/Embedded/embedded.css b/js/src/views/Signer/containers/Embedded/embedded.css index 94e0f3933..ffc6a209f 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.css +++ b/js/src/views/Signer/containers/Embedded/embedded.css @@ -23,9 +23,6 @@ width: $embedWidth; } -.pending { -} - .none { color: #aaa; } diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index 57639ea11..4629a6522 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -71,7 +71,7 @@ class Embedded extends Component { const items = pending.sort(this._sortRequests).map(this.renderPending); return ( -
+
{ items }
); diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.css b/js/src/views/Signer/containers/RequestsPage/RequestsPage.css index 3701c3097..662c45817 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.css +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.css @@ -15,12 +15,6 @@ /* along with Parity. If not, see . */ -.request { -} - .noRequestsMsg { color: #aaa; } - -.items { -} diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js index ecb2ccd43..f3cd5a267 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js @@ -98,9 +98,7 @@ class RequestsPage extends Component { return ( -
- { items } -
+ { items }
); } @@ -111,7 +109,6 @@ class RequestsPage extends Component { return ( . -*/ -.signer { -} - -.container { -} - -.mainContainer { -} diff --git a/js/src/views/Signer/signer.js b/js/src/views/Signer/signer.js index 536cf1d37..6d68c1dc0 100644 --- a/js/src/views/Signer/signer.js +++ b/js/src/views/Signer/signer.js @@ -19,12 +19,10 @@ import React, { Component } from 'react'; import { Actionbar } from '~/ui'; import RequestsPage from './containers/RequestsPage'; -import styles from './signer.css'; - export default class Signer extends Component { render () { return ( -
+
diff --git a/js/src/views/Status/containers/StatusPage/StatusPage.js b/js/src/views/Status/containers/StatusPage/StatusPage.js index 286e2c20e..c7ab56c5e 100644 --- a/js/src/views/Status/containers/StatusPage/StatusPage.js +++ b/js/src/views/Status/containers/StatusPage/StatusPage.js @@ -23,8 +23,6 @@ import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '~/redux/ import Debug from '../../components/Debug'; import Status from '../../components/Status'; -import styles from './statusPage.css'; - class StatusPage extends Component { static propTypes = { nodeStatus: PropTypes.object.isRequired, @@ -41,7 +39,7 @@ class StatusPage extends Component { render () { return ( -
+
diff --git a/js/src/views/Status/containers/StatusPage/statusPage.css b/js/src/views/Status/containers/StatusPage/statusPage.css deleted file mode 100644 index 850ee0856..000000000 --- a/js/src/views/Status/containers/StatusPage/statusPage.css +++ /dev/null @@ -1,18 +0,0 @@ -/* 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 . -*/ -.body { -} From 2582514b5875be42e74a5de541937ebbc7b40a04 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 13:44:35 +0100 Subject: [PATCH 13/30] GasEditor component (#3750) * Initial split of component (WIP) * GasPriceEditor externalised * Fix lint --- js/src/modals/Transfer/Extras/extras.js | 97 ++------- js/src/modals/Transfer/store.js | 85 ++------ js/src/modals/Transfer/transfer.css | 9 - js/src/modals/Transfer/transfer.js | 23 +-- .../GasPriceSelector/gasPriceSelector.css | 10 + .../GasPriceSelector/gasPriceSelector.js | 194 +++++++++--------- .../GasPriceEditor}/GasPriceSelector/index.js | 0 js/src/ui/GasPriceEditor/gasPriceEditor.css | 49 +++++ js/src/ui/GasPriceEditor/gasPriceEditor.js | 98 +++++++++ js/src/ui/GasPriceEditor/index.js | 17 ++ js/src/ui/GasPriceEditor/store.js | 105 ++++++++++ js/src/ui/index.js | 6 +- js/src/util/validation.js | 24 ++- 13 files changed, 440 insertions(+), 277 deletions(-) rename js/src/{modals/Transfer => ui/GasPriceEditor}/GasPriceSelector/gasPriceSelector.css (85%) rename js/src/{modals/Transfer => ui/GasPriceEditor}/GasPriceSelector/gasPriceSelector.js (77%) rename js/src/{modals/Transfer => ui/GasPriceEditor}/GasPriceSelector/index.js (100%) create mode 100644 js/src/ui/GasPriceEditor/gasPriceEditor.css create mode 100644 js/src/ui/GasPriceEditor/gasPriceEditor.js create mode 100644 js/src/ui/GasPriceEditor/index.js create mode 100644 js/src/ui/GasPriceEditor/store.js diff --git a/js/src/modals/Transfer/Extras/extras.js b/js/src/modals/Transfer/Extras/extras.js index f7ff4612b..6d2bfc821 100644 --- a/js/src/modals/Transfer/Extras/extras.js +++ b/js/src/modals/Transfer/Extras/extras.js @@ -16,96 +16,35 @@ import React, { Component, PropTypes } from 'react'; -import Form, { Input } from '~/ui/Form'; -import GasPriceSelector from '../GasPriceSelector'; - -import styles from '../transfer.css'; +import { GasPriceEditor, Form, Input } from '~/ui'; export default class Extras extends Component { static propTypes = { isEth: PropTypes.bool, data: PropTypes.string, dataError: PropTypes.string, - gas: PropTypes.string, - gasEst: PropTypes.string, - gasError: PropTypes.string, - gasPrice: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.object - ]), - gasPriceDefault: PropTypes.string, - gasPriceError: PropTypes.string, - gasPriceHistogram: PropTypes.object, total: PropTypes.string, totalError: PropTypes.string, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, + gasStore: PropTypes.object.isRequired } render () { - const { gas, gasPrice, gasError, gasEst, gasPriceDefault, gasPriceError, gasPriceHistogram, total, totalError } = this.props; - - const gasLabel = `gas amount (estimated: ${gasEst})`; - const priceLabel = `gas price (current: ${gasPriceDefault})`; + const { gasStore, onChange, total, totalError } = this.props; return (
- { this.renderData() } - -
-
- -
- -
-
- - - -
- -
- -
-
-
- -
-

- You can choose the gas price based on the - distribution of recent included transactions' gas prices. - The lower the gas price is, the cheaper the transaction will - be. The higher the gas price is, the faster it should - get mined by the network. -

-
- + + +
); } @@ -129,14 +68,6 @@ export default class Extras extends Component { ); } - onEditGas = (event) => { - this.props.onChange('gas', event.target.value); - } - - onEditGasPrice = (event, value) => { - this.props.onChange('gasPrice', value); - } - onEditData = (event) => { this.props.onChange('data', event.target.value); } diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index 8f8baf55f..a43057c86 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -23,7 +23,8 @@ import { bytesToHex } from '~/api/util/format'; import Contract from '~/api/contract'; import ERRORS from './errors'; import { ERROR_CODES } from '~/api/transport/error'; -import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants'; +import { DEFAULT_GAS, MAX_GAS_ESTIMATION } from '~/util/constants'; +import GasPriceStore from '~/ui/GasPriceEditor/store'; const TITLES = { transfer: 'transfer details', @@ -48,14 +49,6 @@ export default class TransferStore { @observable data = ''; @observable dataError = null; - @observable gas = DEFAULT_GAS; - @observable gasError = null; - - @observable gasEst = '0'; - @observable gasLimitError = null; - @observable gasPrice = DEFAULT_GASPRICE; - @observable gasPriceError = null; - @observable recipient = ''; @observable recipientError = ERRORS.requireRecipient; @@ -68,11 +61,8 @@ export default class TransferStore { @observable value = '0.0'; @observable valueError = null; - gasPriceHistogram = {}; - account = null; balance = null; - gasLimit = null; onClose = null; senders = null; @@ -81,6 +71,8 @@ export default class TransferStore { isWallet = false; wallet = null; + gasStore = null; + @computed get steps () { const steps = [].concat(this.extras ? STAGES_EXTRA : STAGES_BASIC); @@ -93,7 +85,7 @@ export default class TransferStore { @computed get isValid () { const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError; - const extrasValid = !this.gasError && !this.gasPriceError && !this.totalError; + const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.totalError; const verifyValid = !this.passwordError; switch (this.stage) { @@ -118,11 +110,12 @@ export default class TransferStore { const { account, balance, gasLimit, senders, onClose, newError, sendersBalances } = props; this.account = account; this.balance = balance; - this.gasLimit = gasLimit; this.onClose = onClose; this.isWallet = account && account.wallet; this.newError = newError; + this.gasStore = new GasPriceStore(api, gasLimit); + if (this.isWallet) { this.wallet = props.wallet; this.walletContract = new Contract(this.api, walletAbi); @@ -179,26 +172,6 @@ export default class TransferStore { } } - @action getDefaults = () => { - Promise - .all([ - this.api.parity.gasPriceHistogram(), - this.api.eth.gasPrice() - ]) - .then(([gasPriceHistogram, gasPrice]) => { - transaction(() => { - this.gasPrice = gasPrice.toString(); - this.gasPriceDefault = gasPrice.toFormat(); - this.gasPriceHistogram = gasPriceHistogram; - - this.recalculate(); - }); - }) - .catch((error) => { - console.warn('getDefaults', error); - }); - } - @action onSend = () => { this.onNext(); this.sending = true; @@ -281,25 +254,11 @@ export default class TransferStore { } @action _onUpdateGas = (gas) => { - const gasError = this._validatePositiveNumber(gas); - - transaction(() => { - this.gas = gas; - this.gasError = gasError; - - this.recalculate(); - }); + this.recalculate(); } @action _onUpdateGasPrice = (gasPrice) => { - const gasPriceError = this._validatePositiveNumber(gasPrice); - - transaction(() => { - this.gasPrice = gasPrice; - this.gasPriceError = gasPriceError; - - this.recalculate(); - }); + this.recalculate(); } @action _onUpdateRecipient = (recipient) => { @@ -362,7 +321,7 @@ export default class TransferStore { @action recalculateGas = () => { if (!this.isValid) { - this.gas = 0; + this.gasStore.setGas('0'); return this.recalculate(); } @@ -370,28 +329,20 @@ export default class TransferStore { .estimateGas() .then((gasEst) => { let gas = gasEst; - let gasLimitError = null; if (gas.gt(DEFAULT_GAS)) { gas = gas.mul(1.2); } - if (gas.gte(MAX_GAS_ESTIMATION)) { - gasLimitError = ERRORS.gasException; - } else if (gas.gt(this.gasLimit)) { - gasLimitError = ERRORS.gasBlockLimit; - } - transaction(() => { - this.gas = gas.toFixed(0); - this.gasEst = gasEst.toFormat(); - this.gasLimitError = gasLimitError; + this.gasStore.setEstimated(gasEst.toFixed(0)); + this.gasStore.setGas(gas.toFixed(0)); this.recalculate(); }); }) .catch((error) => { - console.error('etimateGas', error); + console.warn('etimateGas', error); this.recalculate(); }); } @@ -411,9 +362,9 @@ export default class TransferStore { return; } - const { gas, gasPrice, tag, valueAll, isEth, isWallet } = this; + const { tag, valueAll, isEth, isWallet } = this; - const gasTotal = new BigNumber(gasPrice || 0).mul(new BigNumber(gas || 0)); + const gasTotal = new BigNumber(this.gasStore.price || 0).mul(new BigNumber(this.gasStore.gas || 0)); const availableEth = new BigNumber(balance.tokens[0].value); @@ -453,7 +404,7 @@ export default class TransferStore { } transaction(() => { - this.total = this.api.util.fromWei(totalEth).toString(); + this.total = this.api.util.fromWei(totalEth).toFixed(); this.totalError = totalError; this.value = value; this.valueError = valueError; @@ -522,8 +473,8 @@ export default class TransferStore { }; if (!gas) { - options.gas = this.gas; - options.gasPrice = this.gasPrice; + options.gas = this.gasStore.gas; + options.gasPrice = this.gasStore.price; } else { options.gas = MAX_GAS_ESTIMATION; } diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css index 3bd17ba96..28612f299 100644 --- a/js/src/modals/Transfer/transfer.css +++ b/js/src/modals/Transfer/transfer.css @@ -144,15 +144,6 @@ font-size: 1.2rem; } -.chart { - position: absolute; - width: 100%; -} - -.gasPriceDesc { - font-size: 0.9em; -} - .warning { border-radius: 0.5em; background: #f80; diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index e1e2a0951..00e84adaf 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -56,10 +56,6 @@ class Transfer extends Component { store = new TransferStore(this.context.api, this.props); - componentDidMount () { - this.store.getDefaults(); - } - render () { const { stage, extras, steps } = this.store; @@ -186,27 +182,20 @@ class Transfer extends Component { } renderExtrasPage () { - if (!this.store.gasPriceHistogram) { + if (!this.store.gasStore.histogram) { return null; } - const { isEth, data, dataError, gas, gasEst, gasError, gasPrice } = this.store; - const { gasPriceDefault, gasPriceError, gasPriceHistogram, total, totalError } = this.store; + const { isEth, data, dataError, total, totalError } = this.store; return ( ); } @@ -263,15 +252,15 @@ class Transfer extends Component { } renderWarning () { - const { gasLimitError } = this.store; + const { errorEstimated } = this.store.gasStore; - if (!gasLimitError) { + if (!errorEstimated) { return null; } return (
- { gasLimitError } + { errorEstimated }
); } diff --git a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css similarity index 85% rename from js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css rename to js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css index 445174c59..247211c50 100644 --- a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css +++ b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css @@ -15,3 +15,13 @@ /* along with Parity. If not, see . */ +.chart { + position: absolute; + width: 100%; +} + +.columns { + display: flex; + flex-wrap: wrap; + position: relative; +} diff --git a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js similarity index 77% rename from js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js rename to js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js index adf644c4e..893a50188 100644 --- a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js +++ b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js @@ -29,10 +29,7 @@ import { import Slider from 'material-ui/Slider'; import BigNumber from 'bignumber.js'; -import componentStyles from './gasPriceSelector.css'; -import mainStyles from '../transfer.css'; - -const styles = Object.assign({}, mainStyles, componentStyles); +import styles from './gasPriceSelector.css'; const COLORS = { default: 'rgba(255, 99, 132, 0.2)', @@ -194,10 +191,7 @@ class CustomizedShape extends Component { class CustomTooltip extends Component { static propTypes = { - gasPriceHistogram: PropTypes.shape({ - bucketBounds: PropTypes.array.isRequired, - counts: PropTypes.array.isRequired - }).isRequired, + gasPriceHistogram: PropTypes.object.isRequired, type: PropTypes.string, payload: PropTypes.array, label: PropTypes.number, @@ -231,12 +225,16 @@ class CustomTooltip extends Component { } } +const TOOL_STYLE = { + color: 'rgba(255,255,255,0.5)', + backgroundColor: 'rgba(0, 0, 0, 0.75)', + padding: '0 0.5em', + fontSize: '0.75em' +}; + export default class GasPriceSelector extends Component { static propTypes = { - gasPriceHistogram: PropTypes.shape({ - bucketBounds: PropTypes.array.isRequired, - counts: PropTypes.array.isRequired - }).isRequired, + gasPriceHistogram: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired, gasPrice: PropTypes.oneOfType([ @@ -287,21 +285,23 @@ export default class GasPriceSelector extends Component { renderSlider () { const { sliderValue } = this.state; - return (
- -
); + return ( +
+ +
+ ); } renderChart () { @@ -316,85 +316,83 @@ export default class GasPriceSelector extends Component { const countIndex = Math.max(0, Math.min(selectedIndex, gasPriceHistogram.counts.length - 1)); const selectedCount = countModifier(gasPriceHistogram.counts[countIndex]); - return (
-
-
- - +
+
+ - } - line - isAnimationActive={ false } - /> + + } + line + isAnimationActive={ false } + /> - - - - -
+ + + + +
-
- - + - } - /> + + } + /> - } - /> + } + /> - - - - + + + + +
-
); + ); } renderCustomCursor = () => { diff --git a/js/src/modals/Transfer/GasPriceSelector/index.js b/js/src/ui/GasPriceEditor/GasPriceSelector/index.js similarity index 100% rename from js/src/modals/Transfer/GasPriceSelector/index.js rename to js/src/ui/GasPriceEditor/GasPriceSelector/index.js diff --git a/js/src/ui/GasPriceEditor/gasPriceEditor.css b/js/src/ui/GasPriceEditor/gasPriceEditor.css new file mode 100644 index 000000000..cf1fff81c --- /dev/null +++ b/js/src/ui/GasPriceEditor/gasPriceEditor.css @@ -0,0 +1,49 @@ +/* Copyright 2015, 2016 Ethcore (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.columns { + display: flex; + flex-wrap: wrap; + position: relative; +} + +.graphColumn { + flex: 65; +} + +.editColumn { + flex: 35; + padding-left: 1em; + justify-ontent: space-around; + padding-bottom: 12; + display: flex; + flex-wrap: wrap; + position: relative; + flex-direction: column; +} + +.gasPriceDesc { + font-size: 0.75em; + opacity: 0.5; +} + +.row { + display: flex; + flex-wrap: wrap; + position: relative; + flex-direction: column; +} diff --git a/js/src/ui/GasPriceEditor/gasPriceEditor.js b/js/src/ui/GasPriceEditor/gasPriceEditor.js new file mode 100644 index 000000000..c6759ddf1 --- /dev/null +++ b/js/src/ui/GasPriceEditor/gasPriceEditor.js @@ -0,0 +1,98 @@ +// 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 BigNumber from 'bignumber.js'; +import React, { Component, PropTypes } from 'react'; +import { observer } from 'mobx-react'; + +import Input from '../Form/Input'; +import GasPriceSelector from './GasPriceSelector'; +import Store from './store'; + +import styles from './gasPriceEditor.css'; + +@observer +export default class GasPriceEditor extends Component { + static propTypes = { + children: PropTypes.node, + store: PropTypes.object.isRequired, + onChange: PropTypes.func + } + + static Store = Store; + + render () { + const { children, store } = this.props; + const { estimated, priceDefault, price, gas, histogram, errorGas, errorPrice } = store; + + const gasLabel = `gas (estimated: ${new BigNumber(estimated).toFormat()})`; + const priceLabel = `price (current: ${new BigNumber(priceDefault).toFormat()})`; + + return ( +
+
+ +
+ You can choose the gas price based on the + distribution of recent included transaction gas prices. + The lower the gas price is, the cheaper the transaction will + be. The higher the gas price is, the faster it should + get mined by the network. +
+
+ +
+
+ + + +
+ +
+ { children } +
+
+
+ ); + } + + onEditGas = (event, gas) => { + const { store, onChange } = this.props; + + store.setGas(gas); + onChange && onChange('gas', gas); + } + + onEditGasPrice = (event, price) => { + const { store, onChange } = this.props; + + store.setPrice(price); + onChange && onChange('gasPrice', price); + } +} diff --git a/js/src/ui/GasPriceEditor/index.js b/js/src/ui/GasPriceEditor/index.js new file mode 100644 index 000000000..956f6ffd2 --- /dev/null +++ b/js/src/ui/GasPriceEditor/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 './gasPriceEditor'; diff --git a/js/src/ui/GasPriceEditor/store.js b/js/src/ui/GasPriceEditor/store.js new file mode 100644 index 000000000..3f3e50430 --- /dev/null +++ b/js/src/ui/GasPriceEditor/store.js @@ -0,0 +1,105 @@ +// 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 BigNumber from 'bignumber.js'; +import { action, observable, transaction } from 'mobx'; + +import { ERRORS, validatePositiveNumber } from '~/util/validation'; +import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants'; + +export default class GasPriceEditor { + @observable errorEstimated = null; + @observable errorGas = null; + @observable errorPrice = null; + @observable estimated = DEFAULT_GAS; + @observable histogram = null; + @observable price = DEFAULT_GASPRICE; + @observable priceDefault = DEFAULT_GASPRICE; + @observable gas = DEFAULT_GAS; + @observable gasLimit = 0; + + constructor (api, gasLimit, loadDefaults = true) { + this._api = api; + this.gasLimit = gasLimit; + + if (loadDefaults) { + this.loadDefaults(); + } + } + + @action setEstimated = (estimated) => { + transaction(() => { + const bn = new BigNumber(estimated); + + this.estimated = estimated; + + if (bn.gte(MAX_GAS_ESTIMATION)) { + this.errorEstimated = ERRORS.gasException; + } else if (bn.gte(this.gasLimit)) { + this.errorEstimated = ERRORS.gasBlockLimit; + } else { + this.errorEstimated = null; + } + }); + } + + @action setHistogram = (gasHistogram) => { + this.histogram = gasHistogram; + } + + @action setPrice = (price) => { + transaction(() => { + this.errorPrice = validatePositiveNumber(price).numberError; + this.price = price; + }); + } + + @action setGas = (gas) => { + transaction(() => { + const { numberError } = validatePositiveNumber(gas); + const bn = new BigNumber(gas); + + this.gas = gas; + + if (numberError) { + this.errorGas = numberError; + } else if (bn.gte(this.gasLimit)) { + this.errorGas = ERRORS.gasBlockLimit; + } else { + this.errorGas = null; + } + }); + } + + @action loadDefaults () { + Promise + .all([ + this._api.parity.gasPriceHistogram(), + this._api.eth.gasPrice() + ]) + .then(([gasPriceHistogram, gasPrice]) => { + transaction(() => { + this.setPrice(gasPrice.toFixed(0)); + this.setHistogram(gasPriceHistogram); + + this.priceDefault = gasPrice.toFixed(); + }); + }) + .catch((error) => { + console.warn('getDefaults', error); + }); + } +} diff --git a/js/src/ui/index.js b/js/src/ui/index.js index 6c763f0f3..c5a965458 100644 --- a/js/src/ui/index.js +++ b/js/src/ui/index.js @@ -31,6 +31,7 @@ import CopyToClipboard from './CopyToClipboard'; import Editor from './Editor'; import Errors from './Errors'; import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form'; +import GasPriceEditor from './GasPriceEditor'; import IdentityIcon from './IdentityIcon'; import IdentityName from './IdentityName'; import Loading from './Loading'; @@ -67,7 +68,7 @@ export { Errors, Form, FormWrap, - TypedInput, + GasPriceEditor, Input, InputAddress, InputAddressSelect, @@ -91,5 +92,6 @@ export { Tooltip, Tooltips, TxHash, - TxList + TxList, + TypedInput }; diff --git a/js/src/util/validation.js b/js/src/util/validation.js index 2b64141db..3d312a2f4 100644 --- a/js/src/util/validation.js +++ b/js/src/util/validation.js @@ -20,6 +20,7 @@ import util from '~/api/util'; export const ERRORS = { invalidAddress: 'address is an invalid network address', + invalidAmount: 'the supplied amount should be a valid positive number', duplicateAddress: 'the address is already in your address book', invalidChecksum: 'address has failed the checksum formatting', invalidName: 'name should not be blank and longer than 2', @@ -27,7 +28,9 @@ export const ERRORS = { invalidCode: 'code should be the compiled hex string', invalidNumber: 'invalid number format', negativeNumber: 'input number should be positive', - decimalNumber: 'input number should not contain decimals' + decimalNumber: 'input number should not contain decimals', + gasException: 'the transaction will throw an exception with the current values', + gasBlockLimit: 'the transaction execution will exceed the block gas limit' }; export function validateAbi (abi, api) { @@ -133,6 +136,25 @@ export function validateName (name) { }; } +export function validatePositiveNumber (number) { + let numberError = null; + + try { + const v = new BigNumber(number); + + if (v.lt(0)) { + numberError = ERRORS.invalidAmount; + } + } catch (e) { + numberError = ERRORS.invalidAmount; + } + + return { + number, + numberError + }; +} + export function validateUint (value) { let valueError = null; From 1213ada59ce2d48b1aed4b91c67d1dd11f5267aa Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 9 Dec 2016 12:52:50 +0000 Subject: [PATCH 14/30] [ci skip] js-precompiled 20161209-125036 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5795de427..d0664d801 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#1bf7160f6c8f25353d790dbd0935560d3d395727" +source = "git+https://github.com/ethcore/js-precompiled.git#8e8e515f958d2d4a5abec07253a51a052f2b744d" 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 4be14d531..ab501f6a7 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.102", + "version": "0.2.103", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From cc8c2ea58d64aab71c28c695390fda384ad4cd13 Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 7 Dec 2016 23:13:53 +0100 Subject: [PATCH 15/30] Reject existing tx --- ethcore/src/client/client.rs | 4 ++++ ethcore/src/client/test_client.rs | 4 ++++ ethcore/src/client/traits.rs | 3 +++ ethcore/src/miner/miner.rs | 15 ++++++++++++--- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 21c5a2366..dcdadd3d1 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1060,6 +1060,10 @@ impl BlockChainClient for Client { self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address)) } + fn transaction_block(&self, id: TransactionID) -> Option { + self.transaction_address(id).map(|addr| addr.block_hash) + } + fn uncle(&self, id: UncleID) -> Option { let index = id.position; self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index)) diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index dd00db7ec..cce102622 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -432,6 +432,10 @@ impl BlockChainClient for TestBlockChainClient { None // Simple default. } + fn transaction_block(&self, _id: TransactionID) -> Option { + None // Simple default. + } + fn uncle(&self, _id: UncleID) -> Option { None // Simple default. } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 7bf17279c..e23a564d4 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -129,6 +129,9 @@ pub trait BlockChainClient : Sync + Send { /// Get transaction with given hash. fn transaction(&self, id: TransactionID) -> Option; + /// Get the hash of block that contains the transaction, if any. + fn transaction_block(&self, id: TransactionID) -> Option; + /// Get uncle with given id. fn uncle(&self, id: UncleID) -> Option; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 8d1f55567..b171856b9 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -23,7 +23,7 @@ use account_provider::{AccountProvider, Error as AccountError}; use views::{BlockView, HeaderView}; use header::Header; use state::{State, CleanupMode}; -use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; +use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics, TransactionID}; use client::TransactionImportResult; use executive::contract_address; use block::{ClosedBlock, SealedBlock, IsBlock, Block}; @@ -357,6 +357,8 @@ impl Miner { let block_number = open_block.block().fields().header.number(); // TODO Push new uncles too. + let mut tx_count: usize = 0; + let tx_total = transactions.len(); for tx in transactions { let hash = tx.hash(); let start = Instant::now(); @@ -378,7 +380,7 @@ impl Miner { }, _ => {}, } - + trace!(target: "miner", "Adding tx {:?} took {:?}", hash, took); match result { Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) => { debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas); @@ -407,9 +409,12 @@ impl Miner { "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", block_number, hash, e); }, - _ => {} // imported ok + _ => { + tx_count += 1; + } // imported ok } } + trace!(target: "miner", "Pushed {}/{} transactions", tx_count, tx_total); let block = open_block.close(); @@ -580,6 +585,10 @@ impl Miner { let best_block_header: Header = ::rlp::decode(&chain.best_block_header()); transactions.into_iter() .map(|tx| { + if chain.transaction_block(TransactionID::Hash(tx.hash())).is_some() { + debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash()); + return Err(Error::Transaction(TransactionError::AlreadyImported)); + } match self.engine.verify_transaction_basic(&tx, &best_block_header) { Err(e) => { debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e); From d2494d14255cdd431471e84d46fbccc28e10f057 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 15:43:24 +0100 Subject: [PATCH 16/30] GasPrice selection for contract execution --- .../DetailsStep/detailsStep.js | 35 +++- .../ExecuteContract/executeContract.css | 12 ++ .../modals/ExecuteContract/executeContract.js | 154 ++++++++++++------ js/src/modals/Transfer/Extras/extras.js | 11 +- js/src/modals/Transfer/store.js | 2 + js/src/ui/GasPriceEditor/gasPriceEditor.js | 18 +- js/src/ui/GasPriceEditor/store.js | 20 ++- 7 files changed, 184 insertions(+), 68 deletions(-) diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js index b4488729a..3ffb929a9 100644 --- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js +++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js @@ -15,13 +15,19 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import { MenuItem } from 'material-ui'; +import { Checkbox, MenuItem } from 'material-ui'; import { AddressSelect, Form, Input, Select, TypedInput } from '~/ui'; import { parseAbiType } from '~/util/abi'; import styles from '../executeContract.css'; +const CHECK_STYLE = { + position: 'absolute', + top: '38px', + left: '1em' +}; + export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, @@ -31,10 +37,12 @@ export default class DetailsStep extends Component { onAmountChange: PropTypes.func.isRequired, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, + gasEdit: PropTypes.bool, onFromAddressChange: PropTypes.func.isRequired, func: PropTypes.object, funcError: PropTypes.string, onFuncChange: PropTypes.func, + onGasEditClick: PropTypes.func, values: PropTypes.array.isRequired, valuesError: PropTypes.array.isRequired, warning: PropTypes.string, @@ -42,7 +50,7 @@ export default class DetailsStep extends Component { } render () { - const { accounts, amount, amountError, fromAddress, fromAddressError, onFromAddressChange, onAmountChange } = this.props; + const { accounts, amount, amountError, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props; return (
@@ -56,12 +64,23 @@ export default class DetailsStep extends Component { onChange={ onFromAddressChange } /> { this.renderFunctionSelect() } { this.renderParameters() } - +
+
+ +
+
+ +
+
); } diff --git a/js/src/modals/ExecuteContract/executeContract.css b/js/src/modals/ExecuteContract/executeContract.css index a83b373ee..6b7132912 100644 --- a/js/src/modals/ExecuteContract/executeContract.css +++ b/js/src/modals/ExecuteContract/executeContract.css @@ -42,3 +42,15 @@ padding: 0.75em; text-align: center; } + +.columns { + display: flex; + flex-wrap: wrap; + position: relative; + + &>div { + flex: 0 1 50%; + width: 50%; + position: relative; + } +} diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 2db5e2b04..3f7940ca3 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -17,19 +17,36 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { observer } from 'mobx-react'; import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; +import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; +import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; -import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui'; +import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash } from '~/ui'; import { MAX_GAS_ESTIMATION } from '~/util/constants'; import { validateAddress, validateUint } from '~/util/validation'; import { parseAbiType } from '~/util/abi'; import DetailsStep from './DetailsStep'; -import ERRORS from '../Transfer/errors'; import { ERROR_CODES } from '~/api/transport/error'; +const STEP_DETAILS = 0; +const STEP_BUSY_OR_GAS = 1; +const STEP_BUSY = 2; + +const TITLES = { + transfer: 'function details', + sending: 'sending', + complete: 'complete', + gas: 'gas selection', + rejected: 'rejected' +}; +const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete]; +const STAGES_GAS = [TITLES.transfer, TITLES.gas, TITLES.sending, TITLES.complete]; + +@observer class ExecuteContract extends Component { static contextTypes = { api: PropTypes.object.isRequired, @@ -46,21 +63,22 @@ class ExecuteContract extends Component { onFromAddressChange: PropTypes.func.isRequired } + gasStore = new GasPriceEditor.Store(this.context.api, this.props.gasLimit); + state = { amount: '0', amountError: null, + busyState: null, fromAddressError: null, func: null, funcError: null, - gas: null, - gasLimitError: null, + gasEdit: false, + rejected: false, + step: STEP_DETAILS, + sending: false, values: [], valuesError: [], - step: 0, - sending: false, - busyState: null, - txhash: null, - rejected: false + txhash: null } componentDidMount () { @@ -79,15 +97,21 @@ class ExecuteContract extends Component { } render () { - const { sending } = this.state; + const { sending, step, gasEdit, rejected } = this.state; + const steps = gasEdit ? STAGES_GAS : STAGES_BASIC; + + if (rejected) { + steps[steps.length - 1] = TITLES.rejected; + } return ( + current={ step } + steps={ steps } + visible + waiting={ gasEdit ? [STEP_BUSY] : [STEP_BUSY_OR_GAS] }> { this.renderStep() } ); @@ -95,7 +119,7 @@ class ExecuteContract extends Component { renderDialogActions () { const { onClose, fromAddress } = this.props; - const { sending, step, fromAddressError, valuesError } = this.state; + const { gasEdit, sending, step, fromAddressError, valuesError } = this.state; const hasError = fromAddressError || valuesError.find((error) => error); const cancelBtn = ( @@ -105,21 +129,44 @@ class ExecuteContract extends Component { icon={ } onClick={ onClose } /> ); + const postBtn = ( +
- { children } +
diff --git a/js/src/ui/GasPriceEditor/store.js b/js/src/ui/GasPriceEditor/store.js index 3f3e50430..afa5e15b2 100644 --- a/js/src/ui/GasPriceEditor/store.js +++ b/js/src/ui/GasPriceEditor/store.js @@ -15,7 +15,7 @@ // along with Parity. If not, see . import BigNumber from 'bignumber.js'; -import { action, observable, transaction } from 'mobx'; +import { action, computed, observable, transaction } from 'mobx'; import { ERRORS, validatePositiveNumber } from '~/util/validation'; import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants'; @@ -24,12 +24,14 @@ export default class GasPriceEditor { @observable errorEstimated = null; @observable errorGas = null; @observable errorPrice = null; + @observable errorTotal = null; @observable estimated = DEFAULT_GAS; @observable histogram = null; @observable price = DEFAULT_GASPRICE; @observable priceDefault = DEFAULT_GASPRICE; @observable gas = DEFAULT_GAS; @observable gasLimit = 0; + @observable weiValue = '0'; constructor (api, gasLimit, loadDefaults = true) { this._api = api; @@ -40,6 +42,18 @@ export default class GasPriceEditor { } } + @computed get totalValue () { + try { + return new BigNumber(this.gas).mul(this.price).add(this.weiValue); + } catch (error) { + return new BigNumber(0); + } + } + + @action setErrorTotal = (errorTotal) => { + this.errorTotal = errorTotal; + } + @action setEstimated = (estimated) => { transaction(() => { const bn = new BigNumber(estimated); @@ -56,6 +70,10 @@ export default class GasPriceEditor { }); } + @action setEthValue = (weiValue) => { + this.weiValue = weiValue; + } + @action setHistogram = (gasHistogram) => { this.histogram = gasHistogram; } From 63137b15482344ff9df634c086abaabed452eadc Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 9 Dec 2016 15:52:28 +0100 Subject: [PATCH 17/30] Add enhanced Wallet solidity code --- js/src/contracts/snippets/enhanced-wallet.sol | 460 ++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 js/src/contracts/snippets/enhanced-wallet.sol diff --git a/js/src/contracts/snippets/enhanced-wallet.sol b/js/src/contracts/snippets/enhanced-wallet.sol new file mode 100644 index 000000000..374eb595f --- /dev/null +++ b/js/src/contracts/snippets/enhanced-wallet.sol @@ -0,0 +1,460 @@ +//sol Wallet +// Multi-sig, daily-limited account proxy/wallet. +// @authors: +// Gav Wood +// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a +// single, or, crucially, each of a number of, designated owners. +// usage: +// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by +// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the +// interior is executed. +pragma solidity ^0.4.6; + +contract multisig { + // EVENTS + + // this contract can accept a confirmation, in which case + // we record owner and operation (hash) alongside it. + event Confirmation(address owner, bytes32 operation); + event Revoke(address owner, bytes32 operation); + + // some others are in the case of an owner changing. + event OwnerChanged(address oldOwner, address newOwner); + event OwnerAdded(address newOwner); + event OwnerRemoved(address oldOwner); + + // the last one is emitted if the required signatures change + event RequirementChanged(uint newRequirement); + + // Funds has arrived into the wallet (record how much). + event Deposit(address _from, uint value); + // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). + event SingleTransact(address owner, uint value, address to, bytes data); + // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). + event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); + // Confirmation still needed for a transaction. + event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); +} + +contract multisigAbi is multisig { + function isOwner(address _addr) returns (bool); + + function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool); + + function confirm(bytes32 _h) returns(bool); + + // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. + function setDailyLimit(uint _newLimit); + + function addOwner(address _owner); + + function removeOwner(address _owner); + + function changeRequirement(uint _newRequired); + + // Revokes a prior confirmation of the given operation + function revoke(bytes32 _operation); + + function changeOwner(address _from, address _to); + + function execute(address _to, uint _value, bytes _data) returns(bool); +} + +contract WalletLibrary is multisig { + // TYPES + + // struct for the status of a pending operation. + struct PendingState { + uint yetNeeded; + uint ownersDone; + uint index; + } + + // Transaction structure to remember details of transaction lest it need be saved for a later call. + struct Transaction { + address to; + uint value; + bytes data; + } + + /****************************** + ***** MULTI OWNED SECTION **** + ******************************/ + + // MODIFIERS + + // simple single-sig function modifier. + modifier onlyowner { + if (isOwner(msg.sender)) + _; + } + // multi-sig function modifier: the operation must have an intrinsic hash in order + // that later attempts can be realised as the same underlying operation and + // thus count as confirmations. + modifier onlymanyowners(bytes32 _operation) { + if (confirmAndCheck(_operation)) + _; + } + + // METHODS + + // constructor is given number of sigs required to do protected "onlymanyowners" transactions + // as well as the selection of addresses capable of confirming them. + function initMultiowned(address[] _owners, uint _required) { + m_numOwners = _owners.length + 1; + m_owners[1] = uint(msg.sender); + m_ownerIndex[uint(msg.sender)] = 1; + m_required = _required; + + for (uint i = 0; i < _owners.length; ++i) + { + m_owners[2 + i] = uint(_owners[i]); + m_ownerIndex[uint(_owners[i])] = 2 + i; + } + } + + // Revokes a prior confirmation of the given operation + function revoke(bytes32 _operation) { + uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + // make sure they're an owner + if (ownerIndex == 0) return; + uint ownerIndexBit = 2**ownerIndex; + var pending = m_pending[_operation]; + if (pending.ownersDone & ownerIndexBit > 0) { + pending.yetNeeded++; + pending.ownersDone -= ownerIndexBit; + Revoke(msg.sender, _operation); + } + } + + // Replaces an owner `_from` with another `_to`. + function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) { + if (isOwner(_to)) return; + uint ownerIndex = m_ownerIndex[uint(_from)]; + if (ownerIndex == 0) return; + + clearPending(); + m_owners[ownerIndex] = uint(_to); + m_ownerIndex[uint(_from)] = 0; + m_ownerIndex[uint(_to)] = ownerIndex; + OwnerChanged(_from, _to); + } + + function addOwner(address _owner) onlymanyowners(sha3(msg.data)) { + if (isOwner(_owner)) return; + + clearPending(); + if (m_numOwners >= c_maxOwners) + reorganizeOwners(); + if (m_numOwners >= c_maxOwners) + return; + m_numOwners++; + m_owners[m_numOwners] = uint(_owner); + m_ownerIndex[uint(_owner)] = m_numOwners; + OwnerAdded(_owner); + } + + function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) { + uint ownerIndex = m_ownerIndex[uint(_owner)]; + if (ownerIndex == 0) return; + if (m_required > m_numOwners - 1) return; + + m_owners[ownerIndex] = 0; + m_ownerIndex[uint(_owner)] = 0; + clearPending(); + reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot + OwnerRemoved(_owner); + } + + function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) { + if (_newRequired > m_numOwners) return; + m_required = _newRequired; + clearPending(); + RequirementChanged(_newRequired); + } + + function isOwner(address _addr) returns (bool) { + return m_ownerIndex[uint(_addr)] > 0; + } + + + function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { + var pending = m_pending[_operation]; + uint ownerIndex = m_ownerIndex[uint(_owner)]; + + // make sure they're an owner + if (ownerIndex == 0) return false; + + // determine the bit to set for this owner. + uint ownerIndexBit = 2**ownerIndex; + return !(pending.ownersDone & ownerIndexBit == 0); + } + + // INTERNAL METHODS + + function confirmAndCheck(bytes32 _operation) internal returns (bool) { + // determine what index the present sender is: + uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + // make sure they're an owner + if (ownerIndex == 0) return; + + var pending = m_pending[_operation]; + // if we're not yet working on this operation, switch over and reset the confirmation status. + if (pending.yetNeeded == 0) { + // reset count of confirmations needed. + pending.yetNeeded = m_required; + // reset which owners have confirmed (none) - set our bitmap to 0. + pending.ownersDone = 0; + pending.index = m_pendingIndex.length++; + m_pendingIndex[pending.index] = _operation; + } + // determine the bit to set for this owner. + uint ownerIndexBit = 2**ownerIndex; + // make sure we (the message sender) haven't confirmed this operation previously. + if (pending.ownersDone & ownerIndexBit == 0) { + Confirmation(msg.sender, _operation); + // ok - check if count is enough to go ahead. + if (pending.yetNeeded <= 1) { + // enough confirmations: reset and run interior. + delete m_pendingIndex[m_pending[_operation].index]; + delete m_pending[_operation]; + return true; + } + else + { + // not enough: record that this owner in particular confirmed. + pending.yetNeeded--; + pending.ownersDone |= ownerIndexBit; + } + } + } + + function reorganizeOwners() private { + uint free = 1; + while (free < m_numOwners) + { + while (free < m_numOwners && m_owners[free] != 0) free++; + while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--; + if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0) + { + m_owners[free] = m_owners[m_numOwners]; + m_ownerIndex[m_owners[free]] = free; + m_owners[m_numOwners] = 0; + } + } + } + + function clearPending() internal { + uint length = m_pendingIndex.length; + for (uint i = 0; i < length; ++i) + if (m_pendingIndex[i] != 0) + delete m_pending[m_pendingIndex[i]]; + delete m_pendingIndex; + } + + + /****************************** + ****** DAY LIMIT SECTION ***** + ******************************/ + + // MODIFIERS + + // simple modifier for daily limit. + modifier limitedDaily(uint _value) { + if (underLimit(_value)) + _; + } + + // METHODS + + // constructor - stores initial daily limit and records the present day's index. + function initDaylimit(uint _limit) { + m_dailyLimit = _limit; + m_lastDay = today(); + } + // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. + function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) { + m_dailyLimit = _newLimit; + } + // resets the amount already spent today. needs many of the owners to confirm. + function resetSpentToday() onlymanyowners(sha3(msg.data)) { + m_spentToday = 0; + } + + // INTERNAL METHODS + + // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and + // returns true. otherwise just returns false. + function underLimit(uint _value) internal onlyowner returns (bool) { + // reset the spend limit if we're on a different day to last time. + if (today() > m_lastDay) { + m_spentToday = 0; + m_lastDay = today(); + } + // check to see if there's enough left - if so, subtract and return true. + // overflow protection // dailyLimit check + if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) { + m_spentToday += _value; + return true; + } + return false; + } + + // determines today's index. + function today() private constant returns (uint) { return now / 1 days; } + + + /****************************** + ********* WALLET SECTION ***** + ******************************/ + + // METHODS + + // constructor - just pass on the owner array to the multiowned and + // the limit to daylimit + function initWallet(address[] _owners, uint _required, uint _daylimit) { + initMultiowned(_owners, _required); + initDaylimit(_daylimit) ; + } + + // kills the contract sending everything to `_to`. + function kill(address _to) onlymanyowners(sha3(msg.data)) { + suicide(_to); + } + + // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. + // If not, goes into multisig process. We provide a hash on return to allow the sender to provide + // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value + // and _data arguments). They still get the option of using them if they want, anyways. + function execute(address _to, uint _value, bytes _data) onlyowner returns(bool _callValue) { + // first, take the opportunity to check that we're under the daily limit. + if (underLimit(_value)) { + SingleTransact(msg.sender, _value, _to, _data); + // yes - just execute the call. + _callValue =_to.call.value(_value)(_data); + } else { + // determine our operation hash. + bytes32 _r = sha3(msg.data, block.number); + if (!confirm(_r) && m_txs[_r].to == 0) { + m_txs[_r].to = _to; + m_txs[_r].value = _value; + m_txs[_r].data = _data; + ConfirmationNeeded(_r, msg.sender, _value, _to, _data); + } + } + } + + // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order + // to determine the body of the transaction from the hash provided. + function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { + if (m_txs[_h].to != 0) { + m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data); + MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); + delete m_txs[_h]; + return true; + } + } + + // INTERNAL METHODS + + function clearWalletPending() internal { + uint length = m_pendingIndex.length; + for (uint i = 0; i < length; ++i) + delete m_txs[m_pendingIndex[i]]; + clearPending(); + } + + // FIELDS + address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; + + // the number of owners that must confirm the same operation before it is run. + uint m_required; + // pointer used to find a free slot in m_owners + uint m_numOwners; + + uint public m_dailyLimit; + uint public m_spentToday; + uint public m_lastDay; + + // list of owners + uint[256] m_owners; + uint constant c_maxOwners = 250; + + // index on the list of owners to allow reverse lookup + mapping(uint => uint) m_ownerIndex; + // the ongoing operations. + mapping(bytes32 => PendingState) m_pending; + bytes32[] m_pendingIndex; + + // pending transactions we have at present. + mapping (bytes32 => Transaction) m_txs; +} + + +contract Wallet is multisig { + + // WALLET CONSTRUCTOR + // calls the `initWallet` method of the Library in this context + function Wallet(address[] _owners, uint _required, uint _daylimit) { + // Signature of the Wallet Library's init function + bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)")); + address target = _walletLibrary; + + // Compute the size of the call data : arrays has 2 + // 32bytes for offset and length, plus 32bytes per element ; + // plus 2 32bytes for each uint + uint argarraysize = (2 + _owners.length); + uint argsize = (2 + argarraysize) * 32; + + assembly { + // Add the signature first to memory + mstore(0x0, sig) + // Add the call data, which is at the end of the + // code + codecopy(0x4, sub(codesize, argsize), argsize) + // Delegate call to the library + delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0) + } + } + + // METHODS + + // gets called when no other function matches + function() payable { + // just being sent some cash? + if (msg.value > 0) + Deposit(msg.sender, msg.value); + else if (msg.data.length > 0) + _walletLibrary.delegatecall(msg.data); + } + + // Gets an owner by 0-indexed position (using numOwners as the count) + function getOwner(uint ownerIndex) constant returns (address) { + return address(m_owners[ownerIndex + 1]); + } + + // As return statement unavailable in fallback, explicit the method here + + function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { + return _walletLibrary.delegatecall(msg.data); + } + + function isOwner(address _addr) returns (bool) { + return _walletLibrary.delegatecall(msg.data); + } + + // FIELDS + address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; + + // the number of owners that must confirm the same operation before it is run. + uint public m_required; + // pointer used to find a free slot in m_owners + uint public m_numOwners; + + uint public m_dailyLimit; + uint public m_spentToday; + uint public m_lastDay; + + // list of owners + uint[256] m_owners; +} From d992c642ffb2aa5c50e058a51bd59209f8bb7142 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 15:55:50 +0100 Subject: [PATCH 18/30] Adjust to final step on rejected/completed --- js/src/modals/ExecuteContract/executeContract.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 3f7940ca3..7b4e8ccd2 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -307,6 +307,8 @@ class ExecuteContract extends Component { const { api, store } = this.context; const { fromAddress } = this.props; const { amount, func, gasEdit, values } = this.state; + const steps = gasEdit ? STAGES_GAS : STAGES_BASIC; + const finalstep = steps.length - 1; const options = { gas: this.gasStore.gas, gasPrice: this.gasStore.price, @@ -325,7 +327,7 @@ class ExecuteContract extends Component { .pollMethod('parity_checkRequest', requestId) .catch((error) => { if (error.code === ERROR_CODES.REQUEST_REJECTED) { - this.setState({ rejected: true }); + this.setState({ rejected: true, step: finalstep }); return false; } @@ -333,7 +335,7 @@ class ExecuteContract extends Component { }); }) .then((txhash) => { - this.setState({ sending: false, step: 2, txhash, busyState: 'Your transaction has been posted to the network' }); + this.setState({ sending: false, step: finalstep, txhash, busyState: 'Your transaction has been posted to the network' }); }) .catch((error) => { console.error('postTransaction', error); From efee55ae84159f8cf62c54304632e1eb1b21ee9d Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 9 Dec 2016 16:24:11 +0100 Subject: [PATCH 19/30] Updated Wallet Version ! Now only 500k gas for Wallet creation --- js/src/contracts/code/index.js | 2 +- js/src/contracts/code/wallet.js | 10 +++++--- .../CreateWallet/WalletType/walletType.js | 5 +++- .../modals/CreateWallet/createWalletStore.js | 25 +++++++++++++------ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/js/src/contracts/code/index.js b/js/src/contracts/code/index.js index baa144979..ff6d218eb 100644 --- a/js/src/contracts/code/index.js +++ b/js/src/contracts/code/index.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import wallet from './wallet'; +import { wallet } from './wallet'; export { wallet diff --git a/js/src/contracts/code/wallet.js b/js/src/contracts/code/wallet.js index 94aa04b7b..35ed3b106 100644 --- a/js/src/contracts/code/wallet.js +++ b/js/src/contracts/code/wallet.js @@ -16,8 +16,10 @@ /** * @version Solidity v0.4.6 - * @from https://github.com/ethereum/dapp-bin/blob/dd5c485359074d49f571693ae064ce78970f3d6d/wallet/wallet.sol - * @date 22-Nov-2016 @ 15h00 UTC + * @from https://github.com/ethcore/parity/blob/63137b15482344ff9df634c086abaabed452eadc/js/src/contracts/snippets/enhanced-wallet.sol + * @date 09-Dec-2016 @ 16h00 UTC */ -export default '0x606060405234610000576040516113bb3803806113bb83398101604090815281516020830151918301519201915b805b83835b815160019081019055600033600160a060020a03166003825b505550600160a060020a033316600090815261010260205260408120600190555b82518110156100ee57828181518110156100005790602001906020020151600160a060020a0316600282600201610100811015610000570160005b5081905550806002016101026000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b60010161006c565b60008290555b50505061010581905561011264010000000061127861012182021704565b610107555b505b50505061012b565b6201518042045b90565b611282806101396000396000f3606060405236156100da5760e060020a6000350463173825d981146101305780632f54bf6e146101425780634123cb6b1461016657806352375093146101855780635c52c2f5146101a4578063659010e7146101b35780637065cb48146101d2578063746c9171146101e4578063797af62714610203578063b20d30a914610227578063b61d27f614610239578063b75c7dc61461026b578063ba51a6df1461027d578063c2cf73261461028f578063c41a360a146102b6578063cbf0b0c0146102e2578063f00d4b5d146102f4578063f1736d8614610309575b61012e5b600034111561012b5760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b5b565b005b346100005761012e600435610328565b005b3461000057610152600435610415565b604080519115158252519081900360200190f35b3461000057610173610436565b60408051918252519081900360200190f35b346100005761017361043c565b60408051918252519081900360200190f35b346100005761012e610443565b005b346100005761017361047b565b60408051918252519081900360200190f35b346100005761012e600435610482565b005b3461000057610173610571565b60408051918252519081900360200190f35b3461000057610152600435610577565b604080519115158252519081900360200190f35b346100005761012e6004356107e3565b005b34610000576101736004803590602480359160443591820191013561081c565b60408051918252519081900360200190f35b346100005761012e600435610ab3565b005b346100005761012e600435610b5e565b005b3461000057610152600435602435610be0565b604080519115158252519081900360200190f35b34610000576102c6600435610c35565b60408051600160a060020a039092168252519081900360200190f35b346100005761012e600435610c55565b005b346100005761012e600435602435610c93565b005b3461000057610173610d8c565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061035181610d93565b1561040e57600160a060020a03831660009081526101026020526040902054915081151561037e5761040e565b60016001540360005411156103925761040e565b6000600283610100811015610000570160005b5055600160a060020a038316600090815261010260205260408120556103c9610f32565b6103d1611002565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101026020526040812054115b919050565b60015481565b6101075481565b60003660405180838380828437820191505092505050604051809103902061046a81610d93565b15610476576000610106555b5b5b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206104a981610d93565b1561056b576104b782610415565b156104c15761056b565b6104c9610f32565b60015460fa90106104dc576104dc611002565b5b60015460fa90106104ed5761056b565b60018054810190819055600160a060020a03831690600290610100811015610000570160005b5055600154600160a060020a03831660008181526101026020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b60005481565b60008161058381610d93565b156107da5760008381526101086020526040902054600160a060020a0316156107da5760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106385780601f1061060d57610100808354040283529160200191610638565b820191906000526020600020905b81548152906001019060200180831161061b57829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561073d5780601f106107125761010080835404028352916020019161073d565b820191906000526020600020905b81548152906001019060200180831161072057829003601f168201915b5050965050505050505060405180910390a16000838152610108602052604081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f81901061079f57506107d1565b601f0160209004906000526020600020908101906107d191905b808211156107cd57600081556001016107b9565b5090565b5b505050600191505b5b5b5b50919050565b60003660405180838380828437820191505092505050604051809103902061080a81610d93565b1561056b576101058290555b5b5b5050565b600061082733610415565b15610aa85761083584611131565b156108f3577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00433858786866040518086600160a060020a0316815260200185815260200184600160a060020a0316815260200180602001828103825284848281815260200192508082843760405192018290039850909650505050505050a184600160a060020a03168484846040518083838082843782019150509250505060006040518083038185876185025a03f15060009350610aa892505050565b6000364360405180848480828437820191505082815260200193505050506040518091039020905061092481610577565b158015610947575060008181526101086020526040902054600160a060020a0316155b15610aa857600081815261010860209081526040822080546c01000000000000000000000000808a0204600160a060020a0319909116178155600180820188905560029182018054818652948490209094601f928116156101000260001901169290920481019290920481019185919087908390106109d15782800160ff198235161785556109fe565b828001600101855582156109fe579182015b828111156109fe5782358255916020019190600101906109e3565b5b50610a1f9291505b808211156107cd57600081556001016107b9565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf32813386888787604051808760001916815260200186600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284376040519201829003995090975050505050505050a15b5b5b5b949350505050565b600160a060020a033316600090815261010260205260408120549080821515610adb57610b57565b50506000828152610103602052604081206001810154600284900a929083161115610b575780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610b8581610d93565b1561056b57600154821115610b995761056b565b6000829055610ba6610f32565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010360209081526040808320600160a060020a038516845261010290925282205482811515610c185760009350610c2c565b8160020a9050808360010154166000141593505b50505092915050565b6000600282600101610100811015610000570160005b505490505b919050565b600036604051808383808284378201915050925050506040518091039020610c7c81610d93565b1561056b5781600160a060020a0316ff5b5b5b5050565b6000600036604051808383808284378201915050925050506040518091039020610cbc81610d93565b15610b5757610cca83610415565b15610cd457610b57565b600160a060020a038416600090815261010260205260409020549150811515610cfc57610b57565b610d04610f32565b82600160a060020a0316600283610100811015610000570160005b5055600160a060020a0380851660008181526101026020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b6101055481565b600160a060020a033316600090815261010260205260408120548180821515610dbb57610f28565b60008581526101036020526040902080549092501515610e4f576000805483556001808401919091556101048054918201808255828015829011610e2457600083815260209020610e249181019083015b808211156107cd57600081556001016107b9565b5090565b5b50505060028301819055610104805487929081101561000057906000526020600020900160005b50555b8260020a90508082600101541660001415610f285760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610f155760008581526101036020526040902060020154610104805490919081101561000057906000526020600020900160005b506000908190558581526101036020526040812081815560018082018390556002909101919091559350610f2856610f28565b8154600019018255600182018054821790555b5b5b505050919050565b6101045460005b81811015610ff557610108600061010483815481101561000057906000526020600020900160005b50548152602081019190915260400160009081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f819010610fb65750610fe8565b601f016020900490600052602060002090810190610fe891905b808211156107cd57600081556001016107b9565b5090565b5b5050505b600101610f39565b61056b6111a4565b5b5050565b60015b600154811015610476575b600154811080156110325750600281610100811015610000570160005b505415155b1561103f57600101611010565b5b600160015411801561106457506002600154610100811015610000570160005b5054155b156110785760018054600019019055611040565b6001548110801561109c57506002600154610100811015610000570160005b505415155b80156110b85750600281610100811015610000570160005b5054155b15611128576002600154610100811015610000570160005b5054600282610100811015610000570160005b5055806101026000600283610100811015610000570160005b505481526020019081526020016000208190555060006002600154610100811015610000570160005b50555b611005565b5b50565b600061113c33610415565b15610431576101075461114d611278565b111561116657600061010655611161611278565b610107555b610106548281011080159061118357506101055482610106540111155b1561119957506101068054820190556001610431565b5060005b5b5b919050565b6101045460005b818110156112215761010481815481101561000057906000526020600020900160005b50541561121857610103600061010483815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b6001016111ab565b610104805460008083559190915261040e907f4c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe908101905b808211156107cd57600081556001016107b9565b5090565b5b505b5050565b6201518042045b9056'; - +export const wallet = '0x6060604052346100005760405161041b38038061041b83398101604090815281516020830151918301519201915b604080517f696e697457616c6c657428616464726573735b5d2c75696e743235362c75696e81527f7432353629000000000000000000000000000000000000000000000000000000602080830191909152915190819003602501902084516000829052909173__WalletLibrary_________________________91600281019160049182010290819038829003903960006000600483016000866127105a03f45b505050505050505b610337806100e46000396000f36060604052361561006c5760e060020a60003504632f54bf6e81146101245780634123cb6b146101485780635237509314610167578063659010e714610186578063746c9171146101a5578063c2cf7326146101c4578063c41a360a146101eb578063f1736d8614610217575b6101225b60003411156100c15760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a161011e565b600036111561011e5773__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750505b5b5b565b005b3461000057610134600435610236565b604080519115158252519081900360200190f35b3461000057610155610297565b60408051918252519081900360200190f35b346100005761015561029d565b60408051918252519081900360200190f35b34610000576101556102a3565b60408051918252519081900360200190f35b34610000576101556102a9565b60408051918252519081900360200190f35b34610000576101346004356024356102af565b604080519115158252519081900360200190f35b34610000576101fb600435610311565b60408051600160a060020a039092168252519081900360200190f35b3461000057610155610331565b60408051918252519081900360200190f35b600073__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750506040515190505b919050565b60015481565b60045481565b60035481565b60005481565b600073__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750506040515190505b92915050565b6000600582600101610100811015610000570160005b505490505b919050565b6002548156'; +export const walletLibrary = '0x606060405234610000575b611381806100186000396000f3606060405236156100da5760e060020a6000350463173825d981146100df5780632f54bf6e146100f157806352375093146101155780635c52c2f514610134578063659010e7146101435780637065cb4814610162578063797af627146101745780639da5e0eb14610198578063b20d30a9146101aa578063b61d27f6146101bc578063b75c7dc614610227578063ba51a6df14610239578063c2cf73261461024b578063c57c5f6014610272578063cbf0b0c0146102c6578063e46dcfeb146102d8578063f00d4b5d14610331578063f1736d8614610346575b610000565b34610000576100ef600435610365565b005b3461000057610101600435610452565b604080519115158252519081900360200190f35b3461000057610122610473565b60408051918252519081900360200190f35b34610000576100ef610479565b005b34610000576101226104b0565b60408051918252519081900360200190f35b34610000576100ef6004356104b6565b005b34610000576101016004356105a5565b604080519115158252519081900360200190f35b34610000576100ef60043561081e565b005b34610000576100ef600435610832565b005b3461000057604080516020600460443581810135601f810184900484028501840190955284845261010194823594602480359560649492939190920191819084018382808284375094965061086a95505050505050565b604080519115158252519081900360200190f35b34610000576100ef600435610bcc565b005b34610000576100ef600435610c77565b005b3461000057610101600435602435610cf9565b604080519115158252519081900360200190f35b34610000576100ef6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437509496505093359350610d4e92505050565b005b34610000576100ef600435610e13565b005b34610000576100ef60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284375094965050843594602001359350610e5192505050565b005b34610000576100ef600435602435610e6a565b005b3461000057610122610f63565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061038e81610f69565b1561044b57600160a060020a0383166000908152610105602052604090205491508115156103bb5761044b565b60016001540360005411156103cf5761044b565b6000600583610100811015610000570160005b5055600160a060020a03831660009081526101056020526040812055610406611108565b61040e6111dc565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101056020526040812054115b919050565b60045481565b6000366040518083838082843782019150509250505060405180910390206104a081610f69565b156104ab5760006003555b5b5b50565b60035481565b6000366040518083838082843782019150509250505060405180910390206104dd81610f69565b1561059f576104eb82610452565b156104f55761059f565b6104fd611108565b60015460fa9010610510576105106111dc565b5b60015460fa90106105215761059f565b60018054810190819055600160a060020a03831690600590610100811015610000570160005b5055600154600160a060020a03831660008181526101056020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b6000816105b181610f69565b156108155760008381526101086020526040902054600160a060020a0316156108155760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106665780601f1061063b57610100808354040283529160200191610666565b820191906000526020600020905b81548152906001019060200180831161064957829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561076b5780601f106107405761010080835404028352916020019161076b565b820191906000526020600020905b81548152906001019060200180831161074e57829003601f168201915b5050965050505050505060405180910390a1600083815261010860205260408120805473ffffffffffffffffffffffffffffffffffffffff19168155600180820183905560028083018054858255939493909281161561010002600019011604601f8190106107da575061080c565b601f01602090049060005260206000209081019061080c91905b8082111561080857600081556001016107f4565b5090565b5b505050600191505b5b5b5b50919050565b600281905561082b61130b565b6004555b50565b60003660405180838380828437820191505092505050604051809103902061085981610f69565b1561059f5760028290555b5b5b5050565b6000600061087733610452565b15610bc05761088584611315565b156109bc577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd004338587866040518085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109335780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a184600160a060020a03168484604051808280519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561099c5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f1925050509150610bc0565b600036436040518084848082843782019150508281526020019350505050604051809103902090506109ed816105a5565b158015610a10575060008181526101086020526040902054600160a060020a0316155b15610bc057600081815261010860209081526040822080546c01000000000000000000000000808a020473ffffffffffffffffffffffffffffffffffffffff199091161781556001808201889055865160029283018054818752958590209095601f9381161561010002600019011693909304820184900483019390929190880190839010610aaa57805160ff1916838001178555610ad7565b82800160010185558215610ad7579182015b82811115610ad7578251825591602001919060010190610abc565b5b50610af89291505b8082111561080857600081556001016107f4565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328133868887604051808660001916815260200185600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610bae5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a15b5b5b5b5b509392505050565b600160a060020a033316600090815261010560205260408120549080821515610bf457610c70565b50506000828152610106602052604081206001810154600284900a929083161115610c705780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610c9e81610f69565b1561059f57600154821115610cb25761059f565b6000829055610cbf611108565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010660209081526040808320600160a060020a038516845261010590925282205482811515610d315760009350610d45565b8160020a9050808360010154166000141593505b50505092915050565b815160019081019055600033600160a060020a03166006825b505550600160a060020a033316600090815261010560205260408120600190558181555b825181101561044b57828181518110156100005790602001906020020151600160a060020a0316600582600201610100811015610000570160005b5081905550806002016101056000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b600101610d8b565b5b505050565b600036604051808383808284378201915050925050506040518091039020610e3a81610f69565b1561059f5781600160a060020a0316ff5b5b5b5050565b610e5b8383610d4e565b61044b8161081e565b5b505050565b6000600036604051808383808284378201915050925050506040518091039020610e9381610f69565b15610c7057610ea183610452565b15610eab57610c70565b600160a060020a038416600090815261010560205260409020549150811515610ed357610c70565b610edb611108565b82600160a060020a0316600583610100811015610000570160005b5055600160a060020a0380851660008181526101056020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b60025481565b600160a060020a033316600090815261010560205260408120548180821515610f91576110fe565b60008581526101066020526040902080549092501515611025576000805483556001808401919091556101078054918201808255828015829011610ffa57600083815260209020610ffa9181019083015b8082111561080857600081556001016107f4565b5090565b5b50505060028301819055610107805487929081101561000057906000526020600020900160005b50555b8260020a905080826001015416600014156110fe5760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a18154600190116110eb5760008581526101066020526040902060020154610107805490919081101561000057906000526020600020900160005b5060009081905585815261010660205260408120818155600180820183905560029091019190915593506110fe566110fe565b8154600019018255600182018054821790555b5b5b505050919050565b6101075460005b818110156111855761010781815481101561000057906000526020600020900160005b50541561117c57610106600061010783815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b60010161110f565b610107805460008083559190915261044b907f47c4908e245f386bfc1825973249847f4053a761ddb4880ad63c323a7b5a2a25908101905b8082111561080857600081556001016107f4565b5090565b5b505b5050565b60015b6001548110156104ab575b6001548110801561120c5750600581610100811015610000570160005b505415155b15611219576001016111ea565b5b600160015411801561123e57506005600154610100811015610000570160005b5054155b15611252576001805460001901905561121a565b6001548110801561127657506005600154610100811015610000570160005b505415155b80156112925750600581610100811015610000570160005b5054155b15611302576005600154610100811015610000570160005b5054600582610100811015610000570160005b5055806101056000600583610100811015610000570160005b505481526020019081526020016000208190555060006005600154610100811015610000570160005b50555b6111df565b5b50565b6201518042045b90565b600061132033610452565b1561046e5760045461133061130b565b111561134757600060035561134361130b565b6004555b600354828101108015906113615750600254826003540111155b1561137657506003805482019055600161046e565b5060005b5b5b91905056'; +export const walletSourceURL = 'https://github.com/ethcore/parity/blob/63137b15482344ff9df634c086abaabed452eadc/js/src/contracts/snippets/enhanced-wallet.sol'; +export const walletLibraryRegKey = 'walletLibrary'; diff --git a/js/src/modals/CreateWallet/WalletType/walletType.js b/js/src/modals/CreateWallet/WalletType/walletType.js index 868c6ad9b..93dd818f1 100644 --- a/js/src/modals/CreateWallet/WalletType/walletType.js +++ b/js/src/modals/CreateWallet/WalletType/walletType.js @@ -17,6 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { RadioButtons } from '~/ui'; +import { walletSourceURL } from '~/contracts/code/wallet'; // import styles from '../createWallet.css'; @@ -46,7 +47,9 @@ export default class WalletType extends Component { description: ( Create/Deploy a - standard multi-signature + + standard multi-signature + Wallet ) diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index d8c308a12..e6edab56b 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -20,8 +20,10 @@ import { validateUint, validateAddress, validateName } from '~/util/validation'; import { ERROR_CODES } from '~/api/transport/error'; import Contract from '~/api/contract'; +import Contracts from '~/contracts'; import { wallet as walletAbi } from '~/contracts/abi'; import { wallet as walletCode } from '~/contracts/code'; +import { walletLibraryRegKey } from '~/contracts/code/wallet'; import WalletsUtils from '~/util/wallets'; @@ -160,14 +162,23 @@ export default class CreateWalletStore { const { account, owners, required, daylimit } = this.wallet; - const options = { - data: walletCode, - from: account - }; + Contracts + .get() + .registry + .lookupAddress(walletLibraryRegKey) + .then((address) => { + const walletLibraryAddress = address.replace(/^0x/, '').toLowerCase(); + const code = walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress); - this.api - .newContract(walletAbi) - .deploy(options, [ owners, required, daylimit ], this.onDeploymentState) + const options = { + data: code, + from: account + }; + + return this.api + .newContract(walletAbi) + .deploy(options, [ owners, required, daylimit ], this.onDeploymentState); + }) .then((address) => { this.deployed = true; this.wallet.address = address; From caf3a96c1999a4342c87435844f7349cdb5b0480 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 9 Dec 2016 16:29:57 +0100 Subject: [PATCH 20/30] Add fallback Full Fleshed Wallet if no library --- js/src/contracts/code/wallet.js | 4 ++++ js/src/modals/CreateWallet/createWalletStore.js | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/js/src/contracts/code/wallet.js b/js/src/contracts/code/wallet.js index 35ed3b106..92bcf8795 100644 --- a/js/src/contracts/code/wallet.js +++ b/js/src/contracts/code/wallet.js @@ -23,3 +23,7 @@ export const wallet = '0x6060604052346100005760405161041b38038061041b83398101604 export const walletLibrary = '0x606060405234610000575b611381806100186000396000f3606060405236156100da5760e060020a6000350463173825d981146100df5780632f54bf6e146100f157806352375093146101155780635c52c2f514610134578063659010e7146101435780637065cb4814610162578063797af627146101745780639da5e0eb14610198578063b20d30a9146101aa578063b61d27f6146101bc578063b75c7dc614610227578063ba51a6df14610239578063c2cf73261461024b578063c57c5f6014610272578063cbf0b0c0146102c6578063e46dcfeb146102d8578063f00d4b5d14610331578063f1736d8614610346575b610000565b34610000576100ef600435610365565b005b3461000057610101600435610452565b604080519115158252519081900360200190f35b3461000057610122610473565b60408051918252519081900360200190f35b34610000576100ef610479565b005b34610000576101226104b0565b60408051918252519081900360200190f35b34610000576100ef6004356104b6565b005b34610000576101016004356105a5565b604080519115158252519081900360200190f35b34610000576100ef60043561081e565b005b34610000576100ef600435610832565b005b3461000057604080516020600460443581810135601f810184900484028501840190955284845261010194823594602480359560649492939190920191819084018382808284375094965061086a95505050505050565b604080519115158252519081900360200190f35b34610000576100ef600435610bcc565b005b34610000576100ef600435610c77565b005b3461000057610101600435602435610cf9565b604080519115158252519081900360200190f35b34610000576100ef6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437509496505093359350610d4e92505050565b005b34610000576100ef600435610e13565b005b34610000576100ef60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284375094965050843594602001359350610e5192505050565b005b34610000576100ef600435602435610e6a565b005b3461000057610122610f63565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061038e81610f69565b1561044b57600160a060020a0383166000908152610105602052604090205491508115156103bb5761044b565b60016001540360005411156103cf5761044b565b6000600583610100811015610000570160005b5055600160a060020a03831660009081526101056020526040812055610406611108565b61040e6111dc565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101056020526040812054115b919050565b60045481565b6000366040518083838082843782019150509250505060405180910390206104a081610f69565b156104ab5760006003555b5b5b50565b60035481565b6000366040518083838082843782019150509250505060405180910390206104dd81610f69565b1561059f576104eb82610452565b156104f55761059f565b6104fd611108565b60015460fa9010610510576105106111dc565b5b60015460fa90106105215761059f565b60018054810190819055600160a060020a03831690600590610100811015610000570160005b5055600154600160a060020a03831660008181526101056020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b6000816105b181610f69565b156108155760008381526101086020526040902054600160a060020a0316156108155760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106665780601f1061063b57610100808354040283529160200191610666565b820191906000526020600020905b81548152906001019060200180831161064957829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561076b5780601f106107405761010080835404028352916020019161076b565b820191906000526020600020905b81548152906001019060200180831161074e57829003601f168201915b5050965050505050505060405180910390a1600083815261010860205260408120805473ffffffffffffffffffffffffffffffffffffffff19168155600180820183905560028083018054858255939493909281161561010002600019011604601f8190106107da575061080c565b601f01602090049060005260206000209081019061080c91905b8082111561080857600081556001016107f4565b5090565b5b505050600191505b5b5b5b50919050565b600281905561082b61130b565b6004555b50565b60003660405180838380828437820191505092505050604051809103902061085981610f69565b1561059f5760028290555b5b5b5050565b6000600061087733610452565b15610bc05761088584611315565b156109bc577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd004338587866040518085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109335780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a184600160a060020a03168484604051808280519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561099c5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f1925050509150610bc0565b600036436040518084848082843782019150508281526020019350505050604051809103902090506109ed816105a5565b158015610a10575060008181526101086020526040902054600160a060020a0316155b15610bc057600081815261010860209081526040822080546c01000000000000000000000000808a020473ffffffffffffffffffffffffffffffffffffffff199091161781556001808201889055865160029283018054818752958590209095601f9381161561010002600019011693909304820184900483019390929190880190839010610aaa57805160ff1916838001178555610ad7565b82800160010185558215610ad7579182015b82811115610ad7578251825591602001919060010190610abc565b5b50610af89291505b8082111561080857600081556001016107f4565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328133868887604051808660001916815260200185600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610bae5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a15b5b5b5b5b509392505050565b600160a060020a033316600090815261010560205260408120549080821515610bf457610c70565b50506000828152610106602052604081206001810154600284900a929083161115610c705780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610c9e81610f69565b1561059f57600154821115610cb25761059f565b6000829055610cbf611108565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010660209081526040808320600160a060020a038516845261010590925282205482811515610d315760009350610d45565b8160020a9050808360010154166000141593505b50505092915050565b815160019081019055600033600160a060020a03166006825b505550600160a060020a033316600090815261010560205260408120600190558181555b825181101561044b57828181518110156100005790602001906020020151600160a060020a0316600582600201610100811015610000570160005b5081905550806002016101056000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b600101610d8b565b5b505050565b600036604051808383808284378201915050925050506040518091039020610e3a81610f69565b1561059f5781600160a060020a0316ff5b5b5b5050565b610e5b8383610d4e565b61044b8161081e565b5b505050565b6000600036604051808383808284378201915050925050506040518091039020610e9381610f69565b15610c7057610ea183610452565b15610eab57610c70565b600160a060020a038416600090815261010560205260409020549150811515610ed357610c70565b610edb611108565b82600160a060020a0316600583610100811015610000570160005b5055600160a060020a0380851660008181526101056020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b60025481565b600160a060020a033316600090815261010560205260408120548180821515610f91576110fe565b60008581526101066020526040902080549092501515611025576000805483556001808401919091556101078054918201808255828015829011610ffa57600083815260209020610ffa9181019083015b8082111561080857600081556001016107f4565b5090565b5b50505060028301819055610107805487929081101561000057906000526020600020900160005b50555b8260020a905080826001015416600014156110fe5760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a18154600190116110eb5760008581526101066020526040902060020154610107805490919081101561000057906000526020600020900160005b5060009081905585815261010660205260408120818155600180820183905560029091019190915593506110fe566110fe565b8154600019018255600182018054821790555b5b5b505050919050565b6101075460005b818110156111855761010781815481101561000057906000526020600020900160005b50541561117c57610106600061010783815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b60010161110f565b610107805460008083559190915261044b907f47c4908e245f386bfc1825973249847f4053a761ddb4880ad63c323a7b5a2a25908101905b8082111561080857600081556001016107f4565b5090565b5b505b5050565b60015b6001548110156104ab575b6001548110801561120c5750600581610100811015610000570160005b505415155b15611219576001016111ea565b5b600160015411801561123e57506005600154610100811015610000570160005b5054155b15611252576001805460001901905561121a565b6001548110801561127657506005600154610100811015610000570160005b505415155b80156112925750600581610100811015610000570160005b5054155b15611302576005600154610100811015610000570160005b5054600582610100811015610000570160005b5055806101056000600583610100811015610000570160005b505481526020019081526020016000208190555060006005600154610100811015610000570160005b50555b6111df565b5b50565b6201518042045b90565b600061132033610452565b1561046e5760045461133061130b565b111561134757600060035561134361130b565b6004555b600354828101108015906113615750600254826003540111155b1561137657506003805482019055600161046e565b5060005b5b5b91905056'; export const walletSourceURL = 'https://github.com/ethcore/parity/blob/63137b15482344ff9df634c086abaabed452eadc/js/src/contracts/snippets/enhanced-wallet.sol'; export const walletLibraryRegKey = 'walletLibrary'; + +// Used if no Wallet Library found in registry... +// Compiled from `wallet.sol` using Solidity v0.4.6 +export const fullWalletCode = '0x606060405234610000576040516113bb3803806113bb83398101604090815281516020830151918301519201915b805b83835b815160019081019055600033600160a060020a03166003825b505550600160a060020a033316600090815261010260205260408120600190555b82518110156100ee57828181518110156100005790602001906020020151600160a060020a0316600282600201610100811015610000570160005b5081905550806002016101026000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b60010161006c565b60008290555b50505061010581905561011264010000000061127861012182021704565b610107555b505b50505061012b565b6201518042045b90565b611282806101396000396000f3606060405236156100da5760e060020a6000350463173825d981146101305780632f54bf6e146101425780634123cb6b1461016657806352375093146101855780635c52c2f5146101a4578063659010e7146101b35780637065cb48146101d2578063746c9171146101e4578063797af62714610203578063b20d30a914610227578063b61d27f614610239578063b75c7dc61461026b578063ba51a6df1461027d578063c2cf73261461028f578063c41a360a146102b6578063cbf0b0c0146102e2578063f00d4b5d146102f4578063f1736d8614610309575b61012e5b600034111561012b5760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b5b565b005b346100005761012e600435610328565b005b3461000057610152600435610415565b604080519115158252519081900360200190f35b3461000057610173610436565b60408051918252519081900360200190f35b346100005761017361043c565b60408051918252519081900360200190f35b346100005761012e610443565b005b346100005761017361047b565b60408051918252519081900360200190f35b346100005761012e600435610482565b005b3461000057610173610571565b60408051918252519081900360200190f35b3461000057610152600435610577565b604080519115158252519081900360200190f35b346100005761012e6004356107e3565b005b34610000576101736004803590602480359160443591820191013561081c565b60408051918252519081900360200190f35b346100005761012e600435610ab3565b005b346100005761012e600435610b5e565b005b3461000057610152600435602435610be0565b604080519115158252519081900360200190f35b34610000576102c6600435610c35565b60408051600160a060020a039092168252519081900360200190f35b346100005761012e600435610c55565b005b346100005761012e600435602435610c93565b005b3461000057610173610d8c565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061035181610d93565b1561040e57600160a060020a03831660009081526101026020526040902054915081151561037e5761040e565b60016001540360005411156103925761040e565b6000600283610100811015610000570160005b5055600160a060020a038316600090815261010260205260408120556103c9610f32565b6103d1611002565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101026020526040812054115b919050565b60015481565b6101075481565b60003660405180838380828437820191505092505050604051809103902061046a81610d93565b15610476576000610106555b5b5b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206104a981610d93565b1561056b576104b782610415565b156104c15761056b565b6104c9610f32565b60015460fa90106104dc576104dc611002565b5b60015460fa90106104ed5761056b565b60018054810190819055600160a060020a03831690600290610100811015610000570160005b5055600154600160a060020a03831660008181526101026020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b60005481565b60008161058381610d93565b156107da5760008381526101086020526040902054600160a060020a0316156107da5760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106385780601f1061060d57610100808354040283529160200191610638565b820191906000526020600020905b81548152906001019060200180831161061b57829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561073d5780601f106107125761010080835404028352916020019161073d565b820191906000526020600020905b81548152906001019060200180831161072057829003601f168201915b5050965050505050505060405180910390a16000838152610108602052604081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f81901061079f57506107d1565b601f0160209004906000526020600020908101906107d191905b808211156107cd57600081556001016107b9565b5090565b5b505050600191505b5b5b5b50919050565b60003660405180838380828437820191505092505050604051809103902061080a81610d93565b1561056b576101058290555b5b5b5050565b600061082733610415565b15610aa85761083584611131565b156108f3577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00433858786866040518086600160a060020a0316815260200185815260200184600160a060020a0316815260200180602001828103825284848281815260200192508082843760405192018290039850909650505050505050a184600160a060020a03168484846040518083838082843782019150509250505060006040518083038185876185025a03f15060009350610aa892505050565b6000364360405180848480828437820191505082815260200193505050506040518091039020905061092481610577565b158015610947575060008181526101086020526040902054600160a060020a0316155b15610aa857600081815261010860209081526040822080546c01000000000000000000000000808a0204600160a060020a0319909116178155600180820188905560029182018054818652948490209094601f928116156101000260001901169290920481019290920481019185919087908390106109d15782800160ff198235161785556109fe565b828001600101855582156109fe579182015b828111156109fe5782358255916020019190600101906109e3565b5b50610a1f9291505b808211156107cd57600081556001016107b9565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf32813386888787604051808760001916815260200186600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284376040519201829003995090975050505050505050a15b5b5b5b949350505050565b600160a060020a033316600090815261010260205260408120549080821515610adb57610b57565b50506000828152610103602052604081206001810154600284900a929083161115610b575780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610b8581610d93565b1561056b57600154821115610b995761056b565b6000829055610ba6610f32565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010360209081526040808320600160a060020a038516845261010290925282205482811515610c185760009350610c2c565b8160020a9050808360010154166000141593505b50505092915050565b6000600282600101610100811015610000570160005b505490505b919050565b600036604051808383808284378201915050925050506040518091039020610c7c81610d93565b1561056b5781600160a060020a0316ff5b5b5b5050565b6000600036604051808383808284378201915050925050506040518091039020610cbc81610d93565b15610b5757610cca83610415565b15610cd457610b57565b600160a060020a038416600090815261010260205260409020549150811515610cfc57610b57565b610d04610f32565b82600160a060020a0316600283610100811015610000570160005b5055600160a060020a0380851660008181526101026020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b6101055481565b600160a060020a033316600090815261010260205260408120548180821515610dbb57610f28565b60008581526101036020526040902080549092501515610e4f576000805483556001808401919091556101048054918201808255828015829011610e2457600083815260209020610e249181019083015b808211156107cd57600081556001016107b9565b5090565b5b50505060028301819055610104805487929081101561000057906000526020600020900160005b50555b8260020a90508082600101541660001415610f285760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610f155760008581526101036020526040902060020154610104805490919081101561000057906000526020600020900160005b506000908190558581526101036020526040812081815560018082018390556002909101919091559350610f2856610f28565b8154600019018255600182018054821790555b5b5b505050919050565b6101045460005b81811015610ff557610108600061010483815481101561000057906000526020600020900160005b50548152602081019190915260400160009081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f819010610fb65750610fe8565b601f016020900490600052602060002090810190610fe891905b808211156107cd57600081556001016107b9565b5090565b5b5050505b600101610f39565b61056b6111a4565b5b5050565b60015b600154811015610476575b600154811080156110325750600281610100811015610000570160005b505415155b1561103f57600101611010565b5b600160015411801561106457506002600154610100811015610000570160005b5054155b156110785760018054600019019055611040565b6001548110801561109c57506002600154610100811015610000570160005b505415155b80156110b85750600281610100811015610000570160005b5054155b15611128576002600154610100811015610000570160005b5054600282610100811015610000570160005b5055806101026000600283610100811015610000570160005b505481526020019081526020016000208190555060006002600154610100811015610000570160005b50555b611005565b5b50565b600061113c33610415565b15610431576101075461114d611278565b111561116657600061010655611161611278565b610107555b610106548281011080159061118357506101055482610106540111155b1561119957506101068054820190556001610431565b5060005b5b5b919050565b6101045460005b818110156112215761010481815481101561000057906000526020600020900160005b50541561121857610103600061010483815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b6001016111ab565b610104805460008083559190915261040e907f4c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe908101905b808211156107cd57600081556001016107b9565b5090565b5b505b5050565b6201518042045b9056'; diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index e6edab56b..7f516eb25 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -23,7 +23,7 @@ import Contract from '~/api/contract'; import Contracts from '~/contracts'; import { wallet as walletAbi } from '~/contracts/abi'; import { wallet as walletCode } from '~/contracts/code'; -import { walletLibraryRegKey } from '~/contracts/code/wallet'; +import { walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet'; import WalletsUtils from '~/util/wallets'; @@ -167,8 +167,10 @@ export default class CreateWalletStore { .registry .lookupAddress(walletLibraryRegKey) .then((address) => { - const walletLibraryAddress = address.replace(/^0x/, '').toLowerCase(); - const code = walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress); + const walletLibraryAddress = (address || '').replace(/^0x/, '').toLowerCase(); + const code = walletLibraryAddress.length && !/^0+$/.test(walletLibraryAddress) + ? walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress) + : fullWalletCode; const options = { data: code, From ff11634e1d4e2c0dc7945719c40f7d1952d9439e Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 9 Dec 2016 16:55:43 +0100 Subject: [PATCH 21/30] PR Grumble --- js/src/modals/CreateWallet/createWalletStore.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index 5e6e8a81a..3edf8f638 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -20,8 +20,7 @@ import Contract from '~/api/contract'; import Contracts from '~/contracts'; import { ERROR_CODES } from '~/api/transport/error'; import { wallet as walletAbi } from '~/contracts/abi'; -import { wallet as walletCode } from '~/contracts/code'; -import { walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet'; +import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet'; import { validateUint, validateAddress, validateName } from '~/util/validation'; import WalletsUtils from '~/util/wallets'; From ffd8314a115b49c9dc7eeff582bd480fbaec0a8e Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 17:52:25 +0100 Subject: [PATCH 22/30] Be lenient around invalid owners map (#3764) * Be lenient around invalid owners map * Filter invalid owners before render --- js/src/views/Accounts/Summary/summary.js | 6 ++++-- js/src/views/Accounts/accounts.js | 18 +++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js index 764f24edf..aeff8a2e5 100644 --- a/js/src/views/Accounts/Summary/summary.js +++ b/js/src/views/Accounts/Summary/summary.js @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; import { Link } from 'react-router'; import { isEqual } from 'lodash'; @@ -113,15 +114,16 @@ export default class Summary extends Component { renderOwners () { const { owners } = this.props; + const ownersValid = (owners || []).filter((owner) => owner.address && new BigNumber(owner.address).gt(0)); - if (!owners || owners.length === 0) { + if (!ownersValid || ownersValid.length === 0) { return null; } return (
{ - owners.map((owner) => ( + ownersValid.map((owner) => (
({ - owners: walletsInfo[wallet].owners.map((owner) => ({ - address: owner, - name: accountsInfo[owner] && accountsInfo[owner].name || owner - })), - address: wallet - })) + .map((wallet) => { + const owners = walletsInfo[wallet].owners || []; + + return { + owners: owners.map((owner) => ({ + address: owner, + name: accountsInfo[owner] && accountsInfo[owner].name || owner + })), + address: wallet + }; + }) .reduce((walletsOwners, wallet) => { walletsOwners[wallet.address] = wallet.owners; return walletsOwners; From fd23a2972c08455c61fc83e6af80407ee4429a88 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 9 Dec 2016 17:00:59 +0000 Subject: [PATCH 23/30] [ci skip] js-precompiled 20161209-165845 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0664d801..675d94ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#8e8e515f958d2d4a5abec07253a51a052f2b744d" +source = "git+https://github.com/ethcore/js-precompiled.git#b8e8e9a8482a51b9a86bb841674f71aca1e57934" 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 ab501f6a7..77c9686a5 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.103", + "version": "0.2.104", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 1b0945940bb4e2162577696167aeb0b4b9745f18 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 18:14:05 +0100 Subject: [PATCH 24/30] Test for boolean result before unsubscribe --- js/src/api/contract/contract.js | 2 +- js/src/api/subscriptions/manager.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 873dac38c..58dd62e7b 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -325,7 +325,7 @@ export default class Contract { console.warn('_sendData', subscriptionId, error); } - if (autoRemove && !result) { + if (autoRemove && !result && typeof result === 'boolean') { this.unsubscribe(subscriptionId); } } diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index a2716684b..f1afe685a 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -111,7 +111,7 @@ export default class Manager { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } - if (autoRemove && !result) { + if (autoRemove && !result && typeof result === 'boolean') { this.unsubscribe(subscriptionId); } } From 0cbef3050893f3b577cd995cbb7d0dc9304dea2a Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 9 Dec 2016 18:17:31 +0100 Subject: [PATCH 25/30] Unsubscribe on true --- js/src/api/contract/contract.js | 4 ++-- js/src/api/subscriptions/manager.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 58dd62e7b..bfe7cabc4 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -317,7 +317,7 @@ export default class Contract { _sendData (subscriptionId, error, logs) { const { autoRemove, callback } = this._subscriptions[subscriptionId]; - let result = false; + let result = true; try { result = callback(error, logs); @@ -325,7 +325,7 @@ export default class Contract { console.warn('_sendData', subscriptionId, error); } - if (autoRemove && !result && typeof result === 'boolean') { + if (autoRemove && result && typeof result === 'boolean') { this.unsubscribe(subscriptionId); } } diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js index f1afe685a..25e6e6129 100644 --- a/js/src/api/subscriptions/manager.js +++ b/js/src/api/subscriptions/manager.js @@ -103,7 +103,7 @@ export default class Manager { _sendData (subscriptionId, error, data) { const { autoRemove, callback } = this.subscriptions[subscriptionId]; - let result = false; + let result = true; try { result = callback(error, data); @@ -111,7 +111,7 @@ export default class Manager { console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error); } - if (autoRemove && !result && typeof result === 'boolean') { + if (autoRemove && result && typeof result === 'boolean') { this.unsubscribe(subscriptionId); } } From d5d9d78bd974e1b2ba1dc3da798ed9f7f10b50b6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 9 Dec 2016 18:58:37 +0100 Subject: [PATCH 26/30] It's not either-or. --- .gitlab-ci.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 14f7b41e6..55d143a47 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -422,13 +422,14 @@ test-rust-stable: image: ethcore/rust:stable before_script: - git submodule update --init --recursive + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.rs | wc -l) - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) - - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi + - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED" + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - export RUST_BACKTRACE=1 - - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test" && ./test.sh $CARGOFLAGS; else echo "skip rust test" && ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ "$RUST_FILES_MODIFIED" = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust - rust-stable @@ -439,11 +440,11 @@ js-test: - git submodule update --init --recursive - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - export RUST_BACKTRACE=1 - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else echo "skip rust test" && ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust - rust-stable @@ -486,9 +487,9 @@ js-release: before_script: - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/install-deps.sh;fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi + - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi tags: - javascript From 5758bb7cac23ede07217601e014c17013c41934b Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 9 Dec 2016 19:11:00 +0100 Subject: [PATCH 27/30] Rust files are all non-js files --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55d143a47..e78eabb18 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -422,7 +422,7 @@ test-rust-stable: image: ethcore/rust:stable before_script: - git submodule update --init --recursive - - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.rs | wc -l) + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v \.js | wc -l) - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED" - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi From cf0b1bb9b2a1c73e81f178c919e14816daffde37 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 9 Dec 2016 19:21:39 +0100 Subject: [PATCH 28/30] use path prefix --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e78eabb18..d27b58f9a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -422,8 +422,8 @@ test-rust-stable: image: ethcore/rust:stable before_script: - git submodule update --init --recursive - - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v \.js | wc -l) - - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l) + - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED" - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: @@ -438,7 +438,7 @@ js-test: image: ethcore/rust:stable before_script: - git submodule update --init --recursive - - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) + - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: @@ -485,7 +485,7 @@ js-release: - stable image: ethcore/rust:stable before_script: - - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l) + - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: From 647b62b4a2a2b2692e171fca30ccd00bf50c77e9 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 9 Dec 2016 18:23:46 +0000 Subject: [PATCH 29/30] [ci skip] js-precompiled 20161209-182130 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 675d94ec4..58d3e1286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#b8e8e9a8482a51b9a86bb841674f71aca1e57934" +source = "git+https://github.com/ethcore/js-precompiled.git#3d3b2f9e8e8b0fd62c172240bfd001a317cf2979" 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 77c9686a5..404a625b5 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.104", + "version": "0.2.105", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 97f358ced92898484b262c6901c7196a8f3acef2 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 6 Dec 2016 19:23:15 +0100 Subject: [PATCH 30/30] AuthorityRound network simulation test --- Cargo.lock | 2 + ethcore/res/authority_round.json | 3 +- ethcore/src/client/client.rs | 12 +- ethcore/src/client/test_client.rs | 4 +- ethcore/src/engines/authority_round.rs | 27 ++-- ethcore/src/engines/mod.rs | 2 + ethcore/src/spec/spec.rs | 2 +- json/src/spec/authority_round.rs | 7 +- sync/Cargo.toml | 2 + sync/src/lib.rs | 3 + sync/src/tests/chain.rs | 96 +++++++------- sync/src/tests/consensus.rs | 78 +++++++++++ sync/src/tests/helpers.rs | 171 +++++++++++++++++++------ sync/src/tests/mod.rs | 1 + sync/src/tests/snapshot.rs | 2 +- 15 files changed, 311 insertions(+), 101 deletions(-) create mode 100644 sync/src/tests/consensus.rs diff --git a/Cargo.lock b/Cargo.lock index 675d94ec4..5fa56cb26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -660,12 +660,14 @@ dependencies = [ "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.5.0", + "ethcore-devtools 1.5.0", "ethcore-io 1.5.0", "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", "ethcore-ipc-nano 1.5.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", + "ethkey 0.2.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/res/authority_round.json b/ethcore/res/authority_round.json index a0f88b85b..85beb51b4 100644 --- a/ethcore/res/authority_round.json +++ b/ethcore/res/authority_round.json @@ -4,7 +4,8 @@ "AuthorityRound": { "params": { "gasLimitBoundDivisor": "0x0400", - "stepDuration": "1", + "stepDuration": 1, + "startStep": 2, "authorities" : [ "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e", "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1" diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 21c5a2366..3ed6579c5 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -255,6 +255,11 @@ impl Client { self.notify.write().push(Arc::downgrade(&target)); } + /// Returns engine reference. + pub fn engine(&self) -> &Engine { + &*self.engine + } + fn notify(&self, f: F) where F: Fn(&ChainNotify) { for np in self.notify.read().iter() { if let Some(n) = np.upgrade() { @@ -563,6 +568,11 @@ impl Client { results.len() } + /// Get shared miner reference. + pub fn miner(&self) -> Arc { + self.miner.clone() + } + /// Used by PoA to try sealing on period change. pub fn update_sealing(&self) { self.miner.update_sealing(self) @@ -1433,4 +1443,4 @@ mod tests { assert!(client.tree_route(&genesis, &new_hash).is_none()); } -} \ No newline at end of file +} diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index dd00db7ec..8950a9e5c 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -255,7 +255,7 @@ impl TestBlockChainClient { } /// Make a bad block by setting invalid extra data. - pub fn corrupt_block(&mut self, n: BlockNumber) { + pub fn corrupt_block(&self, n: BlockNumber) { let hash = self.block_hash(BlockID::Number(n)).unwrap(); let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap()); header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); @@ -267,7 +267,7 @@ impl TestBlockChainClient { } /// Make a bad block by setting invalid parent hash. - pub fn corrupt_block_parent(&mut self, n: BlockNumber) { + pub fn corrupt_block_parent(&self, n: BlockNumber) { let hash = self.block_hash(BlockID::Number(n)).unwrap(); let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap()); header.set_parent_hash(H256::from(42)); diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index f632c9382..21a6e4761 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -49,6 +49,8 @@ pub struct AuthorityRoundParams { pub authorities: Vec
, /// Number of authorities. pub authority_n: usize, + /// Starting step, + pub start_step: Option, } impl From for AuthorityRoundParams { @@ -58,6 +60,7 @@ impl From for AuthorityRoundParams { step_duration: Duration::from_secs(p.step_duration.into()), authority_n: p.authorities.len(), authorities: p.authorities.into_iter().map(Into::into).collect::>(), + start_step: p.start_step.map(Into::into), } } } @@ -97,7 +100,7 @@ impl AsMillis for Duration { impl AuthorityRound { /// Create a new instance of AuthorityRound engine. pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap) -> Result, Error> { - let initial_step = (unix_now().as_secs() / our_params.step_duration.as_secs()) as usize; + let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / our_params.step_duration.as_secs())) as usize; let engine = Arc::new( AuthorityRound { params: params, @@ -160,14 +163,7 @@ impl IoHandler<()> for TransitionHandler { fn timeout(&self, io: &IoContext<()>, timer: TimerToken) { if timer == ENGINE_TIMEOUT_TOKEN { if let Some(engine) = self.engine.upgrade() { - engine.step.fetch_add(1, AtomicOrdering::SeqCst); - engine.proposed.store(false, AtomicOrdering::SeqCst); - if let Some(ref channel) = *engine.message_channel.lock() { - match channel.send(ClientIoMessage::UpdateSealing) { - Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", engine.step.load(AtomicOrdering::Relaxed)), - Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, engine.step.load(AtomicOrdering::Relaxed)), - } - } + engine.step(); io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis()) .unwrap_or_else(|e| warn!(target: "poa", "Failed to restart consensus step timer: {}.", e)) } @@ -184,6 +180,17 @@ impl Engine for AuthorityRound { fn params(&self) -> &CommonParams { &self.params } fn builtins(&self) -> &BTreeMap { &self.builtins } + fn step(&self) { + self.step.fetch_add(1, AtomicOrdering::SeqCst); + self.proposed.store(false, AtomicOrdering::SeqCst); + if let Some(ref channel) = *self.message_channel.lock() { + match channel.send(ClientIoMessage::UpdateSealing) { + Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", self.step.load(AtomicOrdering::Relaxed)), + Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, self.step.load(AtomicOrdering::Relaxed)), + } + } + } + /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { map![ @@ -235,6 +242,8 @@ impl Engine for AuthorityRound { } else { warn!(target: "poa", "generate_seal: FAIL: Accounts not provided."); } + } else { + trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step); } None } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index d7ff06248..8e407f0b7 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -160,4 +160,6 @@ pub trait Engine : Sync + Send { /// Add an account provider useful for Engines that sign stuff. fn register_account_provider(&self, _account_provider: Arc) {} + /// Trigger next step of the consensus engine. + fn step(&self) {} } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index c9ae087c0..e14ea3949 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -273,7 +273,7 @@ impl Spec { pub fn new_instant() -> Spec { load_bundled!("instant_seal") } /// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work). - /// Accounts with secrets "1".sha3() and "2".sha3() are the authorities. + /// Accounts with secrets "0".sha3() and "1".sha3() are the authorities. pub fn new_test_round() -> Self { load_bundled!("authority_round") } } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 3d73ef1ef..bae17bb24 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -30,6 +30,10 @@ pub struct AuthorityRoundParams { pub step_duration: Uint, /// Valid authorities pub authorities: Vec
, + /// Starting step. Determined automatically if not specified. + /// To be used for testing only. + #[serde(rename="startStep")] + pub start_step: Option, } /// Authority engine deserialization. @@ -50,7 +54,8 @@ mod tests { "params": { "gasLimitBoundDivisor": "0x0400", "stepDuration": "0x02", - "authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] + "authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"], + "startStep" : 24 } }"#; diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 738f5f55c..d7980f0d9 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -26,6 +26,8 @@ heapsize = "0.3" ethcore-ipc = { path = "../ipc/rpc" } semver = "0.2" ethcore-ipc-nano = { path = "../ipc/nano" } +ethcore-devtools = { path = "../devtools" } +ethkey = { path = "../ethkey" } parking_lot = "0.3" [features] diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 2061e4e3a..ced4c3f52 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -37,6 +37,9 @@ extern crate semver; extern crate parking_lot; extern crate rlp; +#[cfg(test)] extern crate ethcore_devtools as devtools; +#[cfg(test)] extern crate ethkey; + #[macro_use] extern crate log; #[macro_use] diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 7705215f5..361d53e29 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -24,8 +24,8 @@ use SyncConfig; fn two_peers() { ::env_logger::init().ok(); let mut net = TestNet::new(3); - net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); net.sync(); assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some()); assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); @@ -35,7 +35,7 @@ fn two_peers() { fn long_chain() { ::env_logger::init().ok(); let mut net = TestNet::new(2); - net.peer_mut(1).chain.add_blocks(50000, EachBlockWith::Nothing); + net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing); net.sync(); assert!(net.peer(0).chain.block(BlockID::Number(50000)).is_some()); assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); @@ -45,8 +45,8 @@ fn long_chain() { fn status_after_sync() { ::env_logger::init().ok(); let mut net = TestNet::new(3); - net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); net.sync(); let status = net.peer(0).sync.read().status(); assert_eq!(status.state, SyncState::Idle); @@ -55,8 +55,8 @@ fn status_after_sync() { #[test] fn takes_few_steps() { let mut net = TestNet::new(3); - net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle); let total_steps = net.sync(); assert!(total_steps < 20); } @@ -67,8 +67,8 @@ fn empty_blocks() { let mut net = TestNet::new(3); for n in 0..200 { let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle }; - net.peer_mut(1).chain.add_blocks(5, with.clone()); - net.peer_mut(2).chain.add_blocks(5, with); + net.peer(1).chain.add_blocks(5, with.clone()); + net.peer(2).chain.add_blocks(5, with); } net.sync(); assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some()); @@ -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(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); + net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork + net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2 + net.peer(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(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(0).chain.add_blocks(50, EachBlockWith::Nothing); + net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Nothing); - net.peer_mut(2).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer(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(); @@ -124,13 +124,13 @@ fn net_hard_fork() { ref_client.add_blocks(50, EachBlockWith::Uncle); { let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap()))); - net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); net.sync(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100); } { let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap()))); - net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing); + net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing); net.sync(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0); } @@ -140,8 +140,8 @@ fn net_hard_fork() { fn restart() { ::env_logger::init().ok(); let mut net = TestNet::new(3); - net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); net.sync(); @@ -166,37 +166,37 @@ fn status_empty() { #[test] fn status_packet() { let mut net = TestNet::new(2); - net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle); - net.peer_mut(1).chain.add_blocks(1, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle); net.start(); net.sync_step_peer(0); - assert_eq!(1, net.peer(0).queue.len()); - assert_eq!(0x00, net.peer(0).queue[0].packet_id); + assert_eq!(1, net.peer(0).queue.read().len()); + assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id); } #[test] fn propagate_hashes() { let mut net = TestNet::new(6); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); net.sync(); - net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); net.sync(); net.trigger_chain_new_blocks(0); //first event just sets the marker net.trigger_chain_new_blocks(0); // 5 peers with NewHahses, 4 with blocks - assert_eq!(9, net.peer(0).queue.len()); + assert_eq!(9, net.peer(0).queue.read().len()); let mut hashes = 0; let mut blocks = 0; - for i in 0..net.peer(0).queue.len() { - if net.peer(0).queue[i].packet_id == 0x1 { + for i in 0..net.peer(0).queue.read().len() { + if net.peer(0).queue.read()[i].packet_id == 0x1 { hashes += 1; } - if net.peer(0).queue[i].packet_id == 0x7 { + if net.peer(0).queue.read()[i].packet_id == 0x7 { blocks += 1; } } @@ -207,24 +207,24 @@ fn propagate_hashes() { #[test] fn propagate_blocks() { let mut net = TestNet::new(20); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); net.sync(); - net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); net.trigger_chain_new_blocks(0); //first event just sets the marker net.trigger_chain_new_blocks(0); - assert!(!net.peer(0).queue.is_empty()); + assert!(!net.peer(0).queue.read().is_empty()); // NEW_BLOCK_PACKET - let blocks = net.peer(0).queue.iter().filter(|p| p.packet_id == 0x7).count(); + let blocks = net.peer(0).queue.read().iter().filter(|p| p.packet_id == 0x7).count(); assert!(blocks > 0); } #[test] fn restart_on_malformed_block() { let mut net = TestNet::new(2); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); - net.peer_mut(1).chain.corrupt_block(6); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(1).chain.corrupt_block(6); net.sync_steps(20); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); @@ -233,8 +233,8 @@ fn restart_on_malformed_block() { #[test] fn restart_on_broken_chain() { let mut net = TestNet::new(2); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); - net.peer_mut(1).chain.corrupt_block_parent(6); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(1).chain.corrupt_block_parent(6); net.sync_steps(20); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); @@ -243,8 +243,8 @@ fn restart_on_broken_chain() { #[test] fn high_td_attach() { let mut net = TestNet::new(2); - net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); - net.peer_mut(1).chain.corrupt_block_parent(6); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer(1).chain.corrupt_block_parent(6); net.sync_steps(20); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); @@ -255,8 +255,8 @@ fn high_td_attach() { fn disconnect_on_unrelated_chain() { ::env_logger::init().ok(); let mut net = TestNet::new(2); - net.peer_mut(0).chain.add_blocks(200, EachBlockWith::Uncle); - net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Nothing); + net.peer(0).chain.add_blocks(200, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(100, EachBlockWith::Nothing); net.sync(); assert_eq!(net.disconnect_events, vec![(0, 0)]); } diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs new file mode 100644 index 000000000..00a036a54 --- /dev/null +++ b/sync/src/tests/consensus.rs @@ -0,0 +1,78 @@ +// 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 . + +use util::*; +use ethcore::client::BlockChainClient; +use ethcore::spec::Spec; +use ethcore::miner::MinerService; +use ethcore::transaction::*; +use ethcore::account_provider::AccountProvider; +use ethkey::KeyPair; +use super::helpers::*; +use SyncConfig; + +#[test] +fn test_authority_round() { + ::env_logger::init().ok(); + + let s1 = KeyPair::from_secret("1".sha3()).unwrap(); + let s2 = KeyPair::from_secret("0".sha3()).unwrap(); + let spec_factory = || { + let spec = Spec::new_test_round(); + let account_provider = AccountProvider::transient_provider(); + account_provider.insert_account(s1.secret().clone(), "").unwrap(); + account_provider.insert_account(s2.secret().clone(), "").unwrap(); + spec.engine.register_account_provider(Arc::new(account_provider)); + spec + }; + let mut net = TestNet::new_with_spec(2, SyncConfig::default(), spec_factory); + let mut net = &mut *net; + // Push transaction to both clients. Only one of them gets lucky to mine a block. + net.peer(0).chain.miner().set_author(s1.address()); + net.peer(0).chain.engine().set_signer(s1.address(), "".to_owned()); + net.peer(1).chain.miner().set_author(s2.address()); + net.peer(1).chain.engine().set_signer(s2.address(), "".to_owned()); + let tx1 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + }.sign(s1.secret(), None); + // exhange statuses + net.sync_steps(5); + net.peer(0).chain.miner().import_own_transaction(&net.peer(0).chain, tx1).unwrap(); + net.sync(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); + + let tx2 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + }.sign(s2.secret(), None); + net.peer(1).chain.miner().import_own_transaction(&net.peer(1).chain, tx2).unwrap(); + net.peer(1).chain.engine().step(); + net.peer(1).chain.miner().update_sealing(&net.peer(1).chain); + net.sync(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); +} + diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index 10c1277a6..d2eed9374 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -17,16 +17,33 @@ use util::*; use network::*; use tests::snapshot::*; -use ethcore::client::{TestBlockChainClient, BlockChainClient}; +use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient, ClientConfig, ChainNotify}; use ethcore::header::BlockNumber; use ethcore::snapshot::SnapshotService; +use ethcore::spec::Spec; +use ethcore::miner::Miner; +use ethcore::db::NUM_COLUMNS; use sync_io::SyncIo; +use io::IoChannel; use api::WARP_SYNC_PROTOCOL_ID; use chain::ChainSync; use ::SyncConfig; +use devtools::{self, GuardedTempResult}; -pub struct TestIo<'p> { - pub chain: &'p mut TestBlockChainClient, +pub trait FlushingBlockChainClient: BlockChainClient { + fn flush(&self) {} +} + +impl FlushingBlockChainClient for EthcoreClient { + fn flush(&self) { + self.flush_queue(); + } +} + +impl FlushingBlockChainClient for TestBlockChainClient {} + +pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { + pub chain: &'p C, pub snapshot_service: &'p TestSnapshotService, pub queue: &'p mut VecDeque, pub sender: Option, @@ -34,8 +51,8 @@ pub struct TestIo<'p> { overlay: RwLock>, } -impl<'p> TestIo<'p> { - pub fn new(chain: &'p mut TestBlockChainClient, ss: &'p TestSnapshotService, queue: &'p mut VecDeque, sender: Option) -> TestIo<'p> { +impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { + pub fn new(chain: &'p C, ss: &'p TestSnapshotService, queue: &'p mut VecDeque, sender: Option) -> TestIo<'p, C> { TestIo { chain: chain, snapshot_service: ss, @@ -47,7 +64,7 @@ impl<'p> TestIo<'p> { } } -impl<'p> SyncIo for TestIo<'p> { +impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { fn disable_peer(&mut self, peer_id: PeerId) { self.disconnect_peer(peer_id); } @@ -99,7 +116,7 @@ impl<'p> SyncIo for TestIo<'p> { } fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { 1 } else { self.eth_protocol_version(peer_id) } + if protocol == &WARP_SYNC_PROTOCOL_ID { 2 } else { self.eth_protocol_version(peer_id) } } fn chain_overlay(&self) -> &RwLock> { @@ -113,31 +130,31 @@ pub struct TestPacket { pub recipient: PeerId, } -pub struct TestPeer { - pub chain: TestBlockChainClient, +pub struct TestPeer where C: FlushingBlockChainClient { + pub chain: C, pub snapshot_service: Arc, pub sync: RwLock, - pub queue: VecDeque, + pub queue: RwLock>, } -pub struct TestNet { - pub peers: Vec, +pub struct TestNet where C: FlushingBlockChainClient { + pub peers: Vec>>, pub started: bool, pub disconnect_events: Vec<(PeerId, PeerId)>, //disconnected (initiated by, to) } -impl TestNet { - pub fn new(n: usize) -> TestNet { +impl TestNet { + pub fn new(n: usize) -> TestNet { Self::new_with_config(n, SyncConfig::default()) } - pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet { + pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet { let mut config = SyncConfig::default(); config.fork_block = fork; Self::new_with_config(n, config) } - pub fn new_with_config(n: usize, config: SyncConfig) -> TestNet { + pub fn new_with_config(n: usize, config: SyncConfig) -> TestNet { let mut net = TestNet { peers: Vec::new(), started: false, @@ -147,31 +164,77 @@ impl TestNet { let chain = TestBlockChainClient::new(); let ss = Arc::new(TestSnapshotService::new()); let sync = ChainSync::new(config.clone(), &chain); - net.peers.push(TestPeer { + net.peers.push(Arc::new(TestPeer { sync: RwLock::new(sync), snapshot_service: ss, chain: chain, - queue: VecDeque::new(), - }); + queue: RwLock::new(VecDeque::new()), + })); } net } +} - pub fn peer(&self, i: usize) -> &TestPeer { +impl TestNet { + pub fn new_with_spec(n: usize, config: SyncConfig, spec_factory: F) -> GuardedTempResult> + where F: Fn() -> Spec + { + let mut net = TestNet { + peers: Vec::new(), + started: false, + disconnect_events: Vec::new(), + }; + let dir = devtools::RandomTempPath::new(); + for _ in 0..n { + let mut client_dir = dir.as_path().clone(); + client_dir.push(devtools::random_filename()); + + let db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + + let spec = spec_factory(); + let client = Arc::try_unwrap(EthcoreClient::new( + ClientConfig::default(), + &spec, + client_dir.as_path(), + Arc::new(Miner::with_spec(&spec)), + IoChannel::disconnected(), + &db_config + ).unwrap()).ok().unwrap(); + + let ss = Arc::new(TestSnapshotService::new()); + let sync = ChainSync::new(config.clone(), &client); + let peer = Arc::new(TestPeer { + sync: RwLock::new(sync), + snapshot_service: ss, + chain: client, + queue: RwLock::new(VecDeque::new()), + }); + peer.chain.add_notify(peer.clone()); + net.peers.push(peer); + } + GuardedTempResult::> { + _temp: dir, + result: Some(net) + } + } +} + +impl TestNet where C: FlushingBlockChainClient { + pub fn peer(&self, i: usize) -> &TestPeer { &self.peers[i] } - pub fn peer_mut(&mut self, i: usize) -> &mut TestPeer { - &mut self.peers[i] + pub fn peer_mut(&mut self, i: usize) -> &mut TestPeer { + Arc::get_mut(&mut self.peers[i]).unwrap() } pub fn start(&mut self) { for peer in 0..self.peers.len() { for client in 0..self.peers.len() { if peer != client { - let mut p = &mut self.peers[peer]; + let p = &self.peers[peer]; p.sync.write().update_targets(&p.chain); - p.sync.write().on_peer_connected(&mut TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(client as PeerId)), client as PeerId); + p.sync.write().on_peer_connected(&mut TestIo::new(&p.chain, &p.snapshot_service, &mut p.queue.write(), Some(client as PeerId)), client as PeerId); } } } @@ -179,18 +242,20 @@ impl TestNet { pub fn sync_step(&mut self) { for peer in 0..self.peers.len() { - if let Some(packet) = self.peers[peer].queue.pop_front() { + let packet = self.peers[peer].queue.write().pop_front(); + if let Some(packet) = packet { let disconnecting = { - let mut p = &mut self.peers[packet.recipient]; + let p = &self.peers[packet.recipient]; + let mut queue = p.queue.write(); trace!("--- {} -> {} ---", peer, packet.recipient); let to_disconnect = { - let mut io = TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(peer as PeerId)); + let mut io = TestIo::new(&p.chain, &p.snapshot_service, &mut queue, Some(peer as PeerId)); ChainSync::dispatch_packet(&p.sync, &mut io, peer as PeerId, packet.packet_id, &packet.data); io.to_disconnect }; for d in &to_disconnect { // notify this that disconnecting peers are disconnecting - let mut io = TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(*d)); + let mut io = TestIo::new(&p.chain, &p.snapshot_service, &mut queue, Some(*d)); p.sync.write().on_peer_aborting(&mut io, *d); self.disconnect_events.push((peer, *d)); } @@ -198,8 +263,9 @@ impl TestNet { }; for d in &disconnecting { // notify other peers that this peer is disconnecting - let mut p = &mut self.peers[*d]; - let mut io = TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(peer as PeerId)); + let p = &self.peers[*d]; + let mut queue = p.queue.write(); + let mut io = TestIo::new(&p.chain, &p.snapshot_service, &mut queue, Some(peer as PeerId)); p.sync.write().on_peer_aborting(&mut io, peer as PeerId); } } @@ -209,13 +275,17 @@ impl TestNet { } pub fn sync_step_peer(&mut self, peer_num: usize) { - let mut peer = self.peer_mut(peer_num); - peer.sync.write().maintain_sync(&mut TestIo::new(&mut peer.chain, &peer.snapshot_service, &mut peer.queue, None)); + let peer = self.peer(peer_num); + peer.chain.flush(); + let mut queue = peer.queue.write(); + peer.sync.write().maintain_peers(&mut TestIo::new(&peer.chain, &peer.snapshot_service, &mut queue, None)); + peer.sync.write().maintain_sync(&mut TestIo::new(&peer.chain, &peer.snapshot_service, &mut queue, None)); + peer.sync.write().propagate_new_transactions(&mut TestIo::new(&peer.chain, &peer.snapshot_service, &mut queue, None)); } pub fn restart_peer(&mut self, i: usize) { - let peer = self.peer_mut(i); - peer.sync.write().restart(&mut TestIo::new(&mut peer.chain, &peer.snapshot_service, &mut peer.queue, None)); + let peer = self.peer(i); + peer.sync.write().restart(&mut TestIo::new(&peer.chain, &peer.snapshot_service, &mut peer.queue.write(), None)); } pub fn sync(&mut self) -> u32 { @@ -239,11 +309,38 @@ impl TestNet { } pub fn done(&self) -> bool { - self.peers.iter().all(|p| p.queue.is_empty()) + self.peers.iter().all(|p| p.queue.read().is_empty()) } pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { - let mut peer = self.peer_mut(peer_id); - peer.sync.write().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &peer.snapshot_service, &mut peer.queue, None), &[], &[], &[], &[], &[]); + let peer = self.peer(peer_id); + let mut queue = peer.queue.write(); + peer.sync.write().chain_new_blocks(&mut TestIo::new(&peer.chain, &peer.snapshot_service, &mut queue, None), &[], &[], &[], &[], &[]); } } + +impl ChainNotify for TestPeer { + fn new_blocks(&self, + imported: Vec, + invalid: Vec, + enacted: Vec, + retracted: Vec, + sealed: Vec, + _duration: u64) + { + let mut queue = self.queue.write(); + let mut io = TestIo::new(&self.chain, &self.snapshot_service, &mut queue, None); + self.sync.write().chain_new_blocks( + &mut io, + &imported, + &invalid, + &enacted, + &retracted, + &sealed); + } + + fn start(&self) {} + + fn stop(&self) {} +} + diff --git a/sync/src/tests/mod.rs b/sync/src/tests/mod.rs index bdb4ae4f9..f805f6c24 100644 --- a/sync/src/tests/mod.rs +++ b/sync/src/tests/mod.rs @@ -17,4 +17,5 @@ pub mod helpers; pub mod snapshot; mod chain; +mod consensus; mod rpc; diff --git a/sync/src/tests/snapshot.rs b/sync/src/tests/snapshot.rs index 5d0b21b47..283d59ee3 100644 --- a/sync/src/tests/snapshot.rs +++ b/sync/src/tests/snapshot.rs @@ -129,7 +129,7 @@ fn snapshot_sync() { let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000)); for i in 0..4 { net.peer_mut(i).snapshot_service = snapshot_service.clone(); - net.peer_mut(i).chain.add_blocks(1, EachBlockWith::Nothing); + net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing); } net.sync_steps(50); assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len());