From f1a050366f62804a5122ff7c975690ee0f9a3192 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Sun, 10 Sep 2017 18:03:35 +0200 Subject: [PATCH] Fix slow balances (#6471) * Update token updates * Update token info fetching * Update logger * Minor fixes to updates and notifications for balances * Use Pubsub * Fix timeout. * Use pubsub for status. * Fix signer subscription. * Process tokens in chunks. * Fix tokens loaded by chunks * Linting * Dispatch tokens asap * Fix chunks processing. * Better filter options * Parallel log fetching. * Fix signer polling. * Fix initial block query. * Token balances updates : the right(er) way * Better tokens info fetching * Fixes in token data fetching * Only fetch what's needed (tokens) * Fix linting issues * Revert "Transaction permissioning (#6441)" This reverts commit eed0e8b03a5ab328d0c4f6fcecd937a1f79f1135. * Revert "Revert "Transaction permissioning (#6441)"" This reverts commit 8f96415e58dde652e5828706eb2639d43416f448. * Update wasm-tests. * Fixing balances fetching * Fix requests tracking in UI * Fix request watching * Update the Logger * PR Grumbles Fixes * PR Grumbles fixes * Linting... --- Cargo.lock | 52 ++-- ethcore/Cargo.toml | 1 + ethcore/src/blockchain/blockchain.rs | 81 ++--- ethcore/src/client/client.rs | 37 ++- ethcore/src/client/test_client.rs | 6 +- ethcore/src/client/traits.rs | 5 +- ethcore/src/lib.rs | 1 + ethcore/src/verification/verification.rs | 7 +- js/src/api/api.js | 7 +- js/src/api/pubsub/eth/eth.js | 2 +- js/src/api/pubsub/parity/parity.js | 32 +- js/src/api/pubsub/pubsub.js | 29 ++ js/src/api/pubsub/pubsubBase.js | 11 +- js/src/api/pubsub/signer/index.js | 16 + js/src/api/pubsub/signer/signer.js | 37 +++ js/src/api/rpc/parity/parity.js | 9 + js/src/api/subscriptions/eth.js | 54 +++- js/src/api/subscriptions/eth.spec.js | 3 +- js/src/api/subscriptions/personal.js | 39 ++- js/src/api/subscriptions/signer.js | 37 ++- js/src/api/transport/jsonRpcBase.js | 25 +- js/src/api/transport/logger.js | 150 +++++++++ js/src/api/transport/ws/ws.js | 17 +- js/src/index.js | 4 - js/src/mobx/hardwareStore.js | 52 ++-- js/src/modals/Transfer/store.js | 3 +- js/src/modals/Transfer/transfer.js | 4 +- js/src/redux/providers/apiReducer.js | 2 +- js/src/redux/providers/balances.js | 288 ++++-------------- js/src/redux/providers/balancesActions.js | 256 ++++++++++------ .../providers/certifications/middleware.js | 2 +- js/src/redux/providers/index.js | 1 + js/src/redux/providers/personal.js | 114 +++++-- js/src/redux/providers/personalActions.js | 24 +- js/src/redux/providers/requestsActions.js | 13 +- js/src/redux/providers/status.js | 247 +++++++-------- js/src/redux/providers/tokens.js | 161 ++++++++++ js/src/redux/providers/tokensActions.js | 212 +++++++++++-- js/src/redux/providers/tokensReducer.js | 13 +- js/src/redux/store.js | 52 +++- js/src/ui/Balance/balance.js | 19 +- js/src/ui/TxList/TxRow/txRow.spec.js | 2 +- js/src/ui/TxList/txList.spec.js | 2 +- js/src/util/tokens.js | 133 -------- js/src/util/tokens/bytecodes.js | 23 ++ js/src/util/tokens/index.js | 283 +++++++++++++++++ js/src/views/Application/Requests/requests.js | 9 +- js/src/views/Home/Dapps/dapp.js | 9 + js/src/views/ParityBar/accountStore.js | 10 +- .../SignRequest/signRequest.spec.js | 31 +- rpc/src/v1/impls/eth_filter.rs | 49 +-- 51 files changed, 1819 insertions(+), 857 deletions(-) create mode 100644 js/src/api/pubsub/signer/index.js create mode 100644 js/src/api/pubsub/signer/signer.js create mode 100644 js/src/api/transport/logger.js create mode 100644 js/src/redux/providers/tokens.js delete mode 100644 js/src/util/tokens.js create mode 100644 js/src/util/tokens/bytecodes.js create mode 100644 js/src/util/tokens/index.js diff --git a/Cargo.lock b/Cargo.lock index 33d49894d..ed88fba72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,6 +295,15 @@ dependencies = [ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "coco" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "common-types" version = "0.1.0" @@ -395,14 +404,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "deque" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "difference" version = "1.0.0" @@ -544,6 +545,7 @@ dependencies = [ "parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "price-info 1.7.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.0", "rlp_derive 0.1.0", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1048,7 +1050,7 @@ name = "gcc" version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2433,18 +2435,27 @@ dependencies = [ [[package]] name = "rayon" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rayon-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.0.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2496,7 +2507,7 @@ dependencies = [ "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2647,6 +2658,11 @@ name = "scoped-tls" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "secur32-sys" version = "0.2.0" @@ -3388,6 +3404,7 @@ dependencies = [ "checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f" "checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32" "checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a" +"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" "checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591" "checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd" @@ -3399,7 +3416,6 @@ dependencies = [ "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" -"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" "checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a" "checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" @@ -3524,8 +3540,9 @@ dependencies = [ "checksum quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6683b0e23d80813b1a535841f0048c1537d3f86d63c999e8373b39a9b0eb74a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5" -"checksum rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c83adcb08e5b922e804fe1918142b422602ef11f2fd670b0b52218cb5984a20" -"checksum rayon-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "767d91bacddf07d442fe39257bf04fd95897d1c47c545d009f6beb03efd038f8" +"checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" +"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" +"checksum rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7febc28567082c345f10cddc3612c6ea020fc3297a1977d472cf9fdb73e6e493" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "841591b1e05609a643e3b4d0045fce04f701daba7151ddcd3ad47b080693d5a9" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" @@ -3544,6 +3561,7 @@ dependencies = [ "checksum rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e114e275f7c9b5d50bb52b28f9aac1921209f02aa6077c8b255e21eefaf8ffa" "checksum schannel 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4e45ac5e9e4698c1c138d2972bedcd90b81fe1efeba805449d2bdd54512de5f9" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" +"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" "checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc" "checksum security-framework 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "42ddf098d78d0b64564b23ee6345d07573e7d10e52ad86875d89ddf5f8378a02" "checksum security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "5bacdada57ea62022500c457c8571c17dfb5e6240b7c8eac5916ffa8c7138a55" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index d96cc2dea..5a2f7327a 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -49,6 +49,7 @@ num = "0.1" num_cpus = "1.2" parking_lot = "0.4" price-info = { path = "../price-info" } +rayon = "0.8" rand = "0.3" rlp = { path = "../util/rlp" } rlp_derive = { path = "../util/rlp_derive" } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index cdd390693..d44d8ff9b 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -44,6 +44,7 @@ use db::{self, Writable, Readable, CacheUpdatePolicy}; use cache_manager::CacheManager; use encoded; use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; +use rayon::prelude::*; use ansi_term::Colour; const LOG_BLOOMS_LEVELS: usize = 3; @@ -152,7 +153,7 @@ pub trait BlockProvider { /// Returns logs matching given filter. fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool, Self: Sized; + where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized; } macro_rules! otry { @@ -363,50 +364,56 @@ impl BlockProvider for BlockChain { } fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool, Self: Sized { + where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized { // sort in reverse order blocks.sort_by(|a, b| b.cmp(a)); - let mut log_index = 0; - let mut logs = blocks.into_iter() - .filter_map(|number| self.block_hash(number).map(|hash| (number, hash))) - .filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts))) - .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes()))) - .flat_map(|(number, hash, mut receipts, mut hashes)| { - if receipts.len() != hashes.len() { - warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len()); - assert!(false); - } - log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len()); + let mut logs = blocks + .chunks(128) + .flat_map(move |blocks_chunk| { + blocks_chunk.into_par_iter() + .filter_map(|number| self.block_hash(*number).map(|hash| (*number, hash))) + .filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts))) + .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes()))) + .flat_map(|(number, hash, mut receipts, mut hashes)| { + if receipts.len() != hashes.len() { + warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len()); + assert!(false); + } + let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len()); - let receipts_len = receipts.len(); - hashes.reverse(); - receipts.reverse(); - receipts.into_iter() - .map(|receipt| receipt.logs) - .zip(hashes) - .enumerate() - .flat_map(move |(index, (mut logs, tx_hash))| { - let current_log_index = log_index; - let no_of_logs = logs.len(); - log_index -= no_of_logs; - - logs.reverse(); - logs.into_iter() + let receipts_len = receipts.len(); + hashes.reverse(); + receipts.reverse(); + receipts.into_iter() + .map(|receipt| receipt.logs) + .zip(hashes) .enumerate() - .map(move |(i, log)| LocalizedLogEntry { - entry: log, - block_hash: hash, - block_number: number, - transaction_hash: tx_hash, - // iterating in reverse order - transaction_index: receipts_len - index - 1, - transaction_log_index: no_of_logs - i - 1, - log_index: current_log_index - i - 1, + .flat_map(move |(index, (mut logs, tx_hash))| { + let current_log_index = log_index; + let no_of_logs = logs.len(); + log_index -= no_of_logs; + + logs.reverse(); + logs.into_iter() + .enumerate() + .map(move |(i, log)| LocalizedLogEntry { + entry: log, + block_hash: hash, + block_number: number, + transaction_hash: tx_hash, + // iterating in reverse order + transaction_index: receipts_len - index - 1, + transaction_log_index: no_of_logs - i - 1, + log_index: current_log_index - i - 1, + }) }) + .filter(|log_entry| matches(&log_entry.entry)) + .take(limit.unwrap_or(::std::usize::MAX)) + .collect::>() }) + .collect::>() }) - .filter(|log_entry| matches(&log_entry.entry)) .take(limit.unwrap_or(::std::usize::MAX)) .collect::>(); logs.reverse(); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 02682ece2..c58f0e945 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -918,7 +918,7 @@ impl Client { _ => {}, } - let block_number = match self.block_number(id.clone()) { + let block_number = match self.block_number(id) { Some(num) => num, None => return None, }; @@ -1155,6 +1155,16 @@ impl Client { (false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()), } } + + fn block_number_ref(&self, id: &BlockId) -> Option { + match *id { + BlockId::Number(number) => Some(number), + BlockId::Hash(ref hash) => self.chain.read().block_number(hash), + BlockId::Earliest => Some(0), + BlockId::Latest => Some(self.chain.read().best_block_number()), + BlockId::Pending => Some(self.chain.read().best_block_number() + 1), + } + } } impl snapshot::DatabaseRestore for Client { @@ -1364,13 +1374,7 @@ impl BlockChainClient for Client { } fn block_number(&self, id: BlockId) -> Option { - match id { - BlockId::Number(number) => Some(number), - BlockId::Hash(ref hash) => self.chain.read().block_number(hash), - BlockId::Earliest => Some(0), - BlockId::Latest => Some(self.chain.read().best_block_number()), - BlockId::Pending => Some(self.chain.read().best_block_number() + 1), - } + self.block_number_ref(&id) } fn block_body(&self, id: BlockId) -> Option { @@ -1651,16 +1655,17 @@ impl BlockChainClient for Client { self.engine.additional_params().into_iter().collect() } - fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option> { - match (self.block_number(from_block), self.block_number(to_block)) { - (Some(from), Some(to)) => Some(self.chain.read().blocks_with_bloom(bloom, from, to)), - _ => None - } - } - fn logs(&self, filter: Filter) -> Vec { + let (from, to) = match (self.block_number_ref(&filter.from_block), self.block_number_ref(&filter.to_block)) { + (Some(from), Some(to)) => (from, to), + _ => return Vec::new(), + }; + + let chain = self.chain.read(); let blocks = filter.bloom_possibilities().iter() - .filter_map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone())) + .map(move |bloom| { + chain.blocks_with_bloom(bloom, from, to) + }) .flat_map(|m| m) // remove duplicate elements .collect::>() diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index f39932f82..b7245b25c 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -24,7 +24,7 @@ use itertools::Itertools; use rustc_hex::FromHex; use hash::keccak; use bigint::prelude::U256; -use bigint::hash::{H256, H2048}; +use bigint::hash::H256; use parking_lot::RwLock; use util::*; use rlp::*; @@ -508,10 +508,6 @@ impl BlockChainClient for TestBlockChainClient { self.receipts.read().get(&id).cloned() } - fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option> { - unimplemented!(); - } - fn logs(&self, filter: Filter) -> Vec { let mut logs = self.logs.read().clone(); let len = logs.len(); diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 5a619a95e..b7616a478 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -35,7 +35,7 @@ use transaction::{LocalizedTransaction, PendingTransaction, SignedTransaction}; use verification::queue::QueueInfo as BlockQueueInfo; use bigint::prelude::U256; -use bigint::hash::{H256, H2048}; +use bigint::hash::H256; use util::{Address, Bytes}; use util::hashdb::DBValue; @@ -181,9 +181,6 @@ pub trait BlockChainClient : Sync + Send { /// Get the best block header. fn best_block_header(&self) -> encoded::Header; - /// Returns numbers of blocks containing given bloom. - fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option>; - /// Returns logs matching given filter. fn logs(&self, filter: Filter) -> Vec; diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 2f2329d64..82b3cfd96 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,6 +102,7 @@ extern crate num; extern crate parking_lot; extern crate price_info; extern crate rand; +extern crate rayon; extern crate rlp; extern crate hash; extern crate heapsize; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 62639e849..4ea0efe51 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -392,14 +392,13 @@ mod tests { self.numbers.get(&index).cloned() } - fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec { - unimplemented!() - } - fn block_receipts(&self, _hash: &H256) -> Option { unimplemented!() } + fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec { + unimplemented!() + } fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec where F: Fn(&LogEntry) -> bool, Self: Sized { diff --git a/js/src/api/api.js b/js/src/api/api.js index 220c3be29..a1a7dbbb5 100644 --- a/js/src/api/api.js +++ b/js/src/api/api.js @@ -71,10 +71,15 @@ export default class Api extends EventEmitter { } } + get isPubSub () { + return !!this._pubsub; + } + get pubsub () { - if (!this._pubsub) { + if (!this.isPubSub) { throw Error('Pubsub is only available with a subscribing-supported transport injected!'); } + return this._pubsub; } diff --git a/js/src/api/pubsub/eth/eth.js b/js/src/api/pubsub/eth/eth.js index 0bbc85bec..044473ec6 100644 --- a/js/src/api/pubsub/eth/eth.js +++ b/js/src/api/pubsub/eth/eth.js @@ -25,7 +25,7 @@ export default class Eth extends PubsubBase { } newHeads (callback) { - return this.addListener('eth', 'newHeads', callback); + return this.addListener('eth', 'newHeads', callback, null); } logs (callback) { diff --git a/js/src/api/pubsub/parity/parity.js b/js/src/api/pubsub/parity/parity.js index 6df4a9204..042d8dc8b 100644 --- a/js/src/api/pubsub/parity/parity.js +++ b/js/src/api/pubsub/parity/parity.js @@ -267,7 +267,7 @@ export default class Parity extends PubsubBase { // parity accounts API (only secure API or configured to be exposed) allAccountsInfo (callback) { - return this._addListener(this._api, 'parity_allAccountsInfo', (error, data) => { + return this.addListener(this._api, 'parity_allAccountsInfo', (error, data) => { error ? callback(error) : callback(null, outAccountInfo(data)); @@ -275,7 +275,7 @@ export default class Parity extends PubsubBase { } getDappAddresses (callback, dappId) { - return this._addListener(this._api, 'parity_getDappAddresses', (error, data) => { + return this.addListener(this._api, 'parity_getDappAddresses', (error, data) => { error ? callback(error) : callback(null, outAddresses(data)); @@ -283,7 +283,7 @@ export default class Parity extends PubsubBase { } getDappDefaultAddress (callback, dappId) { - return this._addListener(this._api, 'parity_getDappDefaultAddress', (error, data) => { + return this.addListener(this._api, 'parity_getDappDefaultAddress', (error, data) => { error ? callback(error) : callback(null, outAddress(data)); @@ -291,7 +291,7 @@ export default class Parity extends PubsubBase { } getNewDappsAddresses (callback) { - return this._addListener(this._api, 'parity_getDappDefaultAddress', (error, addresses) => { + return this.addListener(this._api, 'parity_getDappDefaultAddress', (error, addresses) => { error ? callback(error) : callback(null, addresses ? addresses.map(outAddress) : null); @@ -299,7 +299,7 @@ export default class Parity extends PubsubBase { } getNewDappsDefaultAddress (callback) { - return this._addListener(this._api, 'parity_getNewDappsDefaultAddress', (error, data) => { + return this.addListener(this._api, 'parity_getNewDappsDefaultAddress', (error, data) => { error ? callback(error) : callback(null, outAddress(data)); @@ -307,7 +307,7 @@ export default class Parity extends PubsubBase { } listRecentDapps (callback) { - return this._addListener(this._api, 'parity_listRecentDapps', (error, data) => { + return this.addListener(this._api, 'parity_listRecentDapps', (error, data) => { error ? callback(error) : callback(null, outRecentDapps(data)); @@ -315,7 +315,7 @@ export default class Parity extends PubsubBase { } listGethAccounts (callback) { - return this._addListener(this._api, 'parity_listGethAccounts', (error, data) => { + return this.addListener(this._api, 'parity_listGethAccounts', (error, data) => { error ? callback(error) : callback(null, outAddresses(data)); @@ -323,15 +323,15 @@ export default class Parity extends PubsubBase { } listVaults (callback) { - return this._addListener(this._api, 'parity_listVaults', callback); + return this.addListener(this._api, 'parity_listVaults', callback); } listOpenedVaults (callback) { - return this._addListener(this._api, 'parity_listOpenedVaults', callback); + return this.addListener(this._api, 'parity_listOpenedVaults', callback); } getVaultMeta (callback, vaultName) { - return this._addListener(this._api, 'parity_getVaultMeta', (error, data) => { + return this.addListener(this._api, 'parity_getVaultMeta', (error, data) => { error ? callback(error) : callback(null, outVaultMeta(data)); @@ -339,7 +339,7 @@ export default class Parity extends PubsubBase { } deriveAddressHash (callback, address, password, hash, shouldSave) { - return this._addListener(this._api, 'parity_deriveAddressHash', (error, data) => { + return this.addListener(this._api, 'parity_deriveAddressHash', (error, data) => { error ? callback(error) : callback(null, outAddress(data)); @@ -347,10 +347,18 @@ export default class Parity extends PubsubBase { } deriveAddressIndex (callback, address, password, index, shouldSave) { - return this._addListener(this._api, 'parity_deriveAddressIndex', (error, data) => { + return this.addListener(this._api, 'parity_deriveAddressIndex', (error, data) => { error ? callback(error) : callback(null, outAddress(data)); }, [inAddress(address), password, inDeriveIndex(index), !!shouldSave]); } + + nodeHealth (callback) { + return this.addListener(this._api, 'parity_nodeHealth', (error, data) => { + error + ? callback(error) + : callback(null, data); + }); + } } diff --git a/js/src/api/pubsub/pubsub.js b/js/src/api/pubsub/pubsub.js index edbc201ae..4420967ee 100644 --- a/js/src/api/pubsub/pubsub.js +++ b/js/src/api/pubsub/pubsub.js @@ -16,6 +16,7 @@ import Eth from './eth'; import Parity from './parity'; +import Signer from './signer'; import Net from './net'; import { isFunction } from '../util/types'; @@ -29,6 +30,7 @@ export default class Pubsub { this._eth = new Eth(transport); this._net = new Net(transport); this._parity = new Parity(transport); + this._signer = new Signer(transport); } get net () { @@ -43,8 +45,35 @@ export default class Pubsub { return this._parity; } + get signer () { + return this._signer; + } + unsubscribe (subscriptionIds) { // subscriptions are namespace independent. Thus we can simply removeListener from any. return this._parity.removeListener(subscriptionIds); } + + subscribeAndGetResult (f, callback) { + return new Promise((resolve, reject) => { + let isFirst = true; + let onSubscription = (error, data) => { + const p1 = error ? Promise.reject(error) : Promise.resolve(data); + const p2 = p1.then(callback); + + if (isFirst) { + isFirst = false; + p2 + .then(resolve) + .catch(reject); + } + }; + + try { + f.call(this, onSubscription).catch(reject); + } catch (err) { + reject(err); + } + }); + } } diff --git a/js/src/api/pubsub/pubsubBase.js b/js/src/api/pubsub/pubsubBase.js index fcc7525d5..c50f45775 100644 --- a/js/src/api/pubsub/pubsubBase.js +++ b/js/src/api/pubsub/pubsubBase.js @@ -20,11 +20,12 @@ export default class PubsubBase { this._transport = transport; } - addListener (module, eventName, callback, eventParams) { - return eventParams - ? this._transport.subscribe(module, callback, eventName, eventParams) - : this._transport.subscribe(module, callback, eventName, []); - // this._transport.subscribe(module, callback, eventName); After Patch from tomac is merged to master! => eth_subscribe does not support empty array as params + addListener (module, eventName, callback, eventParams = []) { + if (eventName) { + return this._transport.subscribe(module, callback, eventParams ? [eventName, eventParams] : [eventName]); + } + + return this._transport.subscribe(module, callback, eventParams); } removeListener (subscriptionIds) { diff --git a/js/src/api/pubsub/signer/index.js b/js/src/api/pubsub/signer/index.js new file mode 100644 index 000000000..caa410722 --- /dev/null +++ b/js/src/api/pubsub/signer/index.js @@ -0,0 +1,16 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . +export default from './signer'; diff --git a/js/src/api/pubsub/signer/signer.js b/js/src/api/pubsub/signer/signer.js new file mode 100644 index 000000000..266da6b8a --- /dev/null +++ b/js/src/api/pubsub/signer/signer.js @@ -0,0 +1,37 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . +import PubsubBase from '../pubsubBase'; + +import { outSignerRequest } from '../../format/output'; + +export default class Net extends PubsubBase { + constructor (transport) { + super(transport); + this._api = { + subscribe: 'signer_subscribePending', + unsubscribe: 'signer_unsubscribePending', + subscription: 'signer_pending' + }; + } + + pendingRequests (callback) { + return this.addListener(this._api, null, (error, data) => { + error + ? callback(error) + : callback(null, data.map(outSignerRequest)); + }); + } +} diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 59ccb5884..3a502afab 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -44,6 +44,15 @@ export default class Parity { .execute('parity_addReservedPeer', enode); } + call (requests, blockNumber = 'latest') { + return this._transport + .execute( + 'parity_call', + requests.map((options) => inOptions(options)), + inBlockNumber(blockNumber) + ); + } + chainStatus () { return this._transport .execute('parity_chainStatus') diff --git a/js/src/api/subscriptions/eth.js b/js/src/api/subscriptions/eth.js index 8e56f335f..bbab95672 100644 --- a/js/src/api/subscriptions/eth.js +++ b/js/src/api/subscriptions/eth.js @@ -24,6 +24,13 @@ export default class Eth { this._lastBlock = new BigNumber(-1); this._pollTimerId = null; + + // Try to restart subscription if transport is closed + this._api.transport.on('close', () => { + if (this.isStarted) { + this.start(); + } + }); } get isStarted () { @@ -33,31 +40,56 @@ export default class Eth { start () { this._started = true; - return this._blockNumber(); + if (this._api.isPubSub) { + return Promise.all([ + this._pollBlockNumber(false), + this._api.pubsub + .subscribeAndGetResult( + callback => this._api.pubsub.eth.newHeads(callback), + () => { + return this._api.eth + .blockNumber() + .then(blockNumber => { + this.updateBlock(blockNumber); + return blockNumber; + }); + } + ) + ]); + } + + // fallback to polling + return this._pollBlockNumber(true); } - _blockNumber = () => { - const nextTimeout = (timeout = 1000) => { - this._pollTimerId = setTimeout(() => { - this._blockNumber(); - }, timeout); + _pollBlockNumber = (doTimeout) => { + const nextTimeout = (timeout = 1000, forceTimeout = doTimeout) => { + if (forceTimeout) { + this._pollTimerId = setTimeout(() => { + this._pollBlockNumber(doTimeout); + }, timeout); + } }; if (!this._api.transport.isConnected) { - nextTimeout(500); + nextTimeout(500, true); return; } return this._api.eth .blockNumber() .then((blockNumber) => { - if (!blockNumber.eq(this._lastBlock)) { - this._lastBlock = blockNumber; - this._updateSubscriptions('eth_blockNumber', null, blockNumber); - } + this.updateBlock(blockNumber); nextTimeout(); }) .catch(() => nextTimeout()); } + + updateBlock (blockNumber) { + if (!blockNumber.eq(this._lastBlock)) { + this._lastBlock = blockNumber; + this._updateSubscriptions('eth_blockNumber', null, blockNumber); + } + } } diff --git a/js/src/api/subscriptions/eth.spec.js b/js/src/api/subscriptions/eth.spec.js index 3f5ee81d6..2893a14dc 100644 --- a/js/src/api/subscriptions/eth.spec.js +++ b/js/src/api/subscriptions/eth.spec.js @@ -29,7 +29,8 @@ function stubApi (blockNumber) { return { _calls, transport: { - isConnected: true + isConnected: true, + on: () => {} }, eth: { blockNumber: () => { diff --git a/js/src/api/subscriptions/personal.js b/js/src/api/subscriptions/personal.js index fa7ae823c..8b2b826d0 100644 --- a/js/src/api/subscriptions/personal.js +++ b/js/src/api/subscriptions/personal.js @@ -23,6 +23,13 @@ export default class Personal { this._lastDefaultAccount = '0x0'; this._pollTimerId = null; + + // Try to restart subscription if transport is closed + this._api.transport.on('close', () => { + if (this.isStarted) { + this.start(); + } + }); } get isStarted () { @@ -32,20 +39,42 @@ export default class Personal { start () { this._started = true; + let defaultAccount = null; + + if (this._api.isPubSub) { + defaultAccount = this._api.pubsub + .subscribeAndGetResult( + callback => this._api.pubsub.parity.defaultAccount(callback), + (defaultAccount) => { + this.updateDefaultAccount(defaultAccount); + return defaultAccount; + } + ); + } else { + defaultAccount = this._defaultAccount(); + } + return Promise.all([ - this._defaultAccount(), + defaultAccount, this._listAccounts(), this._accountsInfo(), this._loggingSubscribe() ]); } + updateDefaultAccount (defaultAccount) { + if (this._lastDefaultAccount !== defaultAccount) { + this._lastDefaultAccount = defaultAccount; + this._updateSubscriptions('parity_defaultAccount', null, defaultAccount); + } + } + // FIXME: Because of the different API instances, the "wait for valid changes" approach // doesn't work. Since the defaultAccount is critical to operation, we poll in exactly // same way we do in ../eth (ala eth_blockNumber) and update. This should be moved // to pub-sub as it becomes available _defaultAccount = (timerDisabled = false) => { - const nextTimeout = (timeout = 1000) => { + const nextTimeout = (timeout = 3000) => { if (!timerDisabled) { this._pollTimerId = setTimeout(() => { this._defaultAccount(); @@ -61,11 +90,7 @@ export default class Personal { return this._api.parity .defaultAccount() .then((defaultAccount) => { - if (this._lastDefaultAccount !== defaultAccount) { - this._lastDefaultAccount = defaultAccount; - this._updateSubscriptions('parity_defaultAccount', null, defaultAccount); - } - + this.updateDefaultAccount(defaultAccount); nextTimeout(); }) .catch(() => nextTimeout()); diff --git a/js/src/api/subscriptions/signer.js b/js/src/api/subscriptions/signer.js index 2215ed7f3..a0c202c1b 100644 --- a/js/src/api/subscriptions/signer.js +++ b/js/src/api/subscriptions/signer.js @@ -22,6 +22,13 @@ export default class Signer { this._api = api; this._updateSubscriptions = updateSubscriptions; this._started = false; + + // Try to restart subscription if transport is closed + this._api.transport.on('close', () => { + if (this.isStarted) { + this.start(); + } + }); } get isStarted () { @@ -31,30 +38,50 @@ export default class Signer { start () { this._started = true; + if (this._api.isPubSub) { + const subscription = this._api.pubsub + .subscribeAndGetResult( + callback => this._api.pubsub.signer.pendingRequests(callback), + requests => { + this.updateSubscriptions(requests); + return requests; + } + ); + + return Promise.all([ + this._listRequests(false), + subscription + ]); + } + return Promise.all([ this._listRequests(true), this._loggingSubscribe() ]); } + updateSubscriptions (requests) { + return this._updateSubscriptions('signer_requestsToConfirm', null, requests); + } + _listRequests = (doTimeout) => { - const nextTimeout = (timeout = 1000) => { - if (doTimeout) { + const nextTimeout = (timeout = 1000, forceTimeout = doTimeout) => { + if (forceTimeout) { setTimeout(() => { - this._listRequests(true); + this._listRequests(doTimeout); }, timeout); } }; if (!this._api.transport.isConnected) { - nextTimeout(500); + nextTimeout(500, true); return; } return this._api.signer .requestsToConfirm() .then((requests) => { - this._updateSubscriptions('signer_requestsToConfirm', null, requests); + this.updateSubscriptions(requests); nextTimeout(); }) .catch(() => nextTimeout()); diff --git a/js/src/api/transport/jsonRpcBase.js b/js/src/api/transport/jsonRpcBase.js index 819e1f496..1b96347d3 100644 --- a/js/src/api/transport/jsonRpcBase.js +++ b/js/src/api/transport/jsonRpcBase.js @@ -15,7 +15,11 @@ // along with Parity. If not, see . import EventEmitter from 'eventemitter3'; + import { Logging } from '../subscriptions'; +import logger from './logger'; + +const LOGGER_ENABLED = process.env.NODE_ENV !== 'production'; export default class JsonRpcBase extends EventEmitter { constructor () { @@ -75,6 +79,14 @@ export default class JsonRpcBase extends EventEmitter { } execute (method, ...params) { + let start; + let logId; + + if (LOGGER_ENABLED) { + start = Date.now(); + logId = logger.log({ method, params }); + } + return this._middlewareList.then((middlewareList) => { for (const middleware of middlewareList) { const res = middleware.handle(method, params); @@ -93,7 +105,18 @@ export default class JsonRpcBase extends EventEmitter { } } - return this._execute(method, params); + const result = this._execute(method, params); + + if (!LOGGER_ENABLED) { + return result; + } + + return result + .then((result) => { + logger.set(logId, { result, time: Date.now() - start }); + + return result; + }); }); } diff --git a/js/src/api/transport/logger.js b/js/src/api/transport/logger.js new file mode 100644 index 000000000..930c4a34e --- /dev/null +++ b/js/src/api/transport/logger.js @@ -0,0 +1,150 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import MethodDecodingStore from '~/ui/MethodDecoding/methodDecodingStore'; + +const LOGGER_ENABLED = process.env.NODE_ENV !== 'production'; + +let logger; + +if (LOGGER_ENABLED) { + class Logger { + _logs = {}; + _id = 0; + + log ({ method, params }) { + const logId = this._id++; + + this._logs[logId] = { method, params, date: Date.now() }; + return logId; + } + + set (logId, data) { + this._logs[logId] = { + ...this._logs[logId], + ...data + }; + } + + static sorter (logA, logB) { + return logA.date - logB.date; + } + + get calls () { + const calls = this.methods['eth_call'] || []; + const decoding = MethodDecodingStore.get(window.secureApi); + const contracts = {}; + + const progress = Math.round(calls.length / 20); + + return calls + .reduce((promise, call, index) => { + const { data, to } = call.params[0]; + + contracts[to] = contracts[to] || []; + + return promise + .then(() => decoding.lookup(null, { data, to })) + .then((lookup) => { + if (!lookup.name) { + contracts[to].push(data); + return; + } + + const inputs = lookup.inputs.map((input) => { + if (/bytes/.test(input.type)) { + return '0x' + input.value.map((v) => v.toString(16).padStart(2, 0)).join(''); + } + + return input.value; + }); + + const called = `${lookup.name}(${inputs.join(', ')})`; + + contracts[to].push(called); + + if (index % progress === 0) { + console.warn(`progress: ${Math.round(100 * index / calls.length)}%`); + } + }); + }, Promise.resolve()) + .then(() => { + return Object.keys(contracts) + .map((address) => { + const count = contracts[address].length; + + return { + count, + calls: contracts[address], + to: address + }; + }) + .sort((cA, cB) => cB.count - cA.count); + }); + } + + get logs () { + return Object.values(this._logs).sort(Logger.sorter); + } + + get methods () { + return this.logs.reduce((methods, log) => { + methods[log.method] = methods[log.method] || []; + methods[log.method].push(log); + return methods; + }, {}); + } + + get stats () { + const logs = this.logs; + const methods = this.methods; + + const start = logs[0].date; + const end = logs[logs.length - 1].date; + + // Duration in seconds + const duration = (end - start) / 1000; + const speed = logs.length / duration; + + const sortedMethods = Object.keys(methods) + .map((method) => { + const methodLogs = methods[method].sort(Logger.sorter); + const methodSpeed = methodLogs.length / duration; + + return { + speed: methodSpeed, + count: methodLogs.length, + logs: methodLogs, + method + }; + }) + .sort((mA, mB) => mB.count - mA.count); + + return { + methods: sortedMethods, + speed + }; + } + } + + logger = new Logger(); + + if (window) { + window._logger = logger; + } +} + +export default logger; diff --git a/js/src/api/transport/ws/ws.js b/js/src/api/transport/ws/ws.js index 9c276772d..63abecb83 100644 --- a/js/src/api/transport/ws/ws.js +++ b/js/src/api/transport/ws/ws.js @@ -29,7 +29,7 @@ export default class Ws extends JsonRpcBase { this._url = url; this._token = token; this._messages = {}; - this._subscriptions = { 'eth_subscription': [], 'parity_subscription': [], 'shh_subscription': [] }; + this._subscriptions = {}; this._sessionHash = null; this._connecting = false; @@ -209,6 +209,7 @@ export default class Ws extends JsonRpcBase { // initial pubsub ACK if (id && msg.subscription) { // save subscription to map subId -> messageId + this._subscriptions[msg.subscription] = this._subscriptions[msg.subscription] || {}; this._subscriptions[msg.subscription][res] = id; // resolve promise with messageId because subId's can collide (eth/parity) msg.resolve(id); @@ -223,7 +224,7 @@ export default class Ws extends JsonRpcBase { } // pubsub format - if (method.includes('subscription')) { + if (this._subscriptions[method]) { const messageId = this._messages[this._subscriptions[method][params.subscription]]; if (messageId) { @@ -302,6 +303,16 @@ export default class Ws extends JsonRpcBase { } _methodsFromApi (api) { + if (api.subscription) { + const { subscribe, unsubscribe, subscription } = api; + + return { + method: subscribe, + uMethod: unsubscribe, + subscription + }; + } + const method = `${api}_subscribe`; const uMethod = `${api}_unsubscribe`; const subscription = `${api}_subscription`; @@ -309,7 +320,7 @@ export default class Ws extends JsonRpcBase { return { method, uMethod, subscription }; } - subscribe (api, callback, ...params) { + subscribe (api, callback, params) { return new Promise((resolve, reject) => { const id = this.id; const { method, uMethod, subscription } = this._methodsFromApi(api); diff --git a/js/src/index.js b/js/src/index.js index 7e85dd51f..24cf38286 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -36,7 +36,6 @@ import muiTheme from '~/ui/Theme'; import MainApplication from './main'; import { loadSender, patchApi } from '~/util/tx'; -import { setApi } from '~/redux/providers/apiActions'; import './environment'; @@ -69,9 +68,6 @@ ContractInstances.create(api); const store = initStore(api, hashHistory); -store.dispatch({ type: 'initAll', api }); -store.dispatch(setApi(api)); - window.secureApi = api; ReactDOM.render( diff --git a/js/src/mobx/hardwareStore.js b/js/src/mobx/hardwareStore.js index 46bf3fa58..5a3960c2f 100644 --- a/js/src/mobx/hardwareStore.js +++ b/js/src/mobx/hardwareStore.js @@ -31,6 +31,10 @@ export default class HardwareStore { this._pollId = null; this._pollScan(); + this._subscribeParity(); + this._api.transport.on('close', () => { + this._subscribeParity(); + }); } isConnected (address) { @@ -78,26 +82,30 @@ export default class HardwareStore { }); } - scanParity () { - return this._api.parity - .hardwareAccountsInfo() - .then((hwInfo) => { - Object - .keys(hwInfo) - .forEach((address) => { - const info = hwInfo[address]; + _subscribeParity () { + const onError = error => { + console.warn('HardwareStore::scanParity', error); - info.address = address; - info.via = 'parity'; - }); + return {}; + }; - return hwInfo; - }) - .catch((error) => { - console.warn('HardwareStore::scanParity', error); + return this._api.pubsub + .subscribeAndGetResult( + callback => this._api.pubsub.parity.hardwareAccountsInfo(callback), + hwInfo => { + Object + .keys(hwInfo) + .forEach((address) => { + const info = hwInfo[address]; - return {}; - }); + info.address = address; + info.via = 'parity'; + }); + this.setWallets(hwInfo); + return hwInfo; + }, + onError + ).catch(onError); } scan () { @@ -107,14 +115,10 @@ export default class HardwareStore { // is done, different results will be retrieved via Parity vs. the browser APIs // (latter is Chrome-only, needs the browser app enabled on a Ledger, former is // not intended as a network call, i.e. hw wallet is with the user) - return Promise - .all([ - this.scanParity(), - this.scanLedger() - ]) - .then(([hwAccounts, ledgerAccounts]) => { + return this.scanLedger() + .then((ledgerAccounts) => { transaction(() => { - this.setWallets(Object.assign({}, hwAccounts, ledgerAccounts)); + this.setWallets(Object.assign({}, ledgerAccounts)); this.setScanning(false); }); }); diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index eaccf4f40..71458c85d 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -133,8 +133,8 @@ export default class TransferStore { } @action handleClose = () => { - this.stage = 0; this.onClose(); + this.stage = 0; } @action onUpdateDetails = (type, value) => { @@ -169,7 +169,6 @@ export default class TransferStore { } @action onSend = () => { - this.onNext(); this.sending = true; this diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index ab769ff02..fd2625ee7 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -192,7 +192,7 @@ class Transfer extends Component { renderDialogActions () { const { account } = this.props; - const { extras, sending, stage } = this.store; + const { extras, sending, stage, isValid } = this.store; const cancelBtn = (