Merge remote-tracking branch 'origin/master' into check-updates
This commit is contained in:
		
						commit
						0a494962e4
					
				| @ -422,10 +422,10 @@ 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 RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) | ||||
|   script: | ||||
|     - export RUST_BACKTRACE=1 | ||||
|     - if [ "$RUST_FILES_MODIFIED" = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi | ||||
|     - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi | ||||
|   tags: | ||||
|     - rust | ||||
|     - rust-stable | ||||
| @ -435,9 +435,9 @@ js-test: | ||||
|   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) | ||||
|     - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi | ||||
|     - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi | ||||
|   script: | ||||
|     - 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 [ $JS_FILES_MODIFIED -eq 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 | ||||
| @ -480,9 +480,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 "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi | ||||
|     - if [ $JS_FILES_MODIFIED -eq 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 "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi | ||||
|     - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi | ||||
|   tags: | ||||
|     - javascript | ||||
|  | ||||
							
								
								
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1293,7 +1293,7 @@ dependencies = [ | ||||
| [[package]] | ||||
| name = "parity-ui-precompiled" | ||||
| version = "1.4.0" | ||||
| source = "git+https://github.com/ethcore/js-precompiled.git#3d3b2f9e8e8b0fd62c172240bfd001a317cf2979" | ||||
| source = "git+https://github.com/ethcore/js-precompiled.git#f982c84ac216cc4f99d056c912e205bcf9341602" | ||||
| dependencies = [ | ||||
|  "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| @ -170,9 +170,6 @@ | ||||
| 		"enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303", | ||||
| 		"enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303", | ||||
| 		"enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303", | ||||
| 		"enode://84f5d5957b4880a8b0545e32e05472318898ad9fc8ebe1d56c90c12334a98e12351eccfdf3a2bf72432ac38b57e9d348400d17caa083879ade3822390f89773f@10.1.52.78:30303", | ||||
| 		"enode://f90dc9b9bf7b8db97726b7849e175f1eb2707f3d8f281c929336e398dd89b0409fc6aeceb89e846278e9d3ecc3857cebfbe6758ff352ece6fe5d42921ee761db@10.1.173.87:30303", | ||||
| 		"enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@10.3.149.199:30303", | ||||
| 		"enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303", | ||||
| 		"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", | ||||
| 		"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", | ||||
|  | ||||
| @ -1092,20 +1092,6 @@ impl MinerService for Miner { | ||||
| 	fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { | ||||
| 		trace!(target: "miner", "chain_new_blocks"); | ||||
| 
 | ||||
| 		fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> { | ||||
| 			let block = chain | ||||
| 				.block(BlockId::Hash(*hash)) | ||||
| 				// Client should send message after commit to db and inserting to chain.
 | ||||
| 				.expect("Expected in-chain blocks."); | ||||
| 			let block = BlockView::new(&block); | ||||
| 			let txs = block.transactions(); | ||||
| 			// populate sender
 | ||||
| 			for tx in &txs { | ||||
| 				let _sender = tx.sender(); | ||||
| 			} | ||||
| 			txs | ||||
| 		} | ||||
| 
 | ||||
| 		// 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions
 | ||||
| 		//	  should be still available in the queue.
 | ||||
| 		// 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that
 | ||||
| @ -1116,35 +1102,29 @@ impl MinerService for Miner { | ||||
| 
 | ||||
| 		// Then import all transactions...
 | ||||
| 		{ | ||||
| 			let out_of_chain = retracted | ||||
| 				.par_iter() | ||||
| 				.map(|h| fetch_transactions(chain, h)); | ||||
| 			out_of_chain.for_each(|txs| { | ||||
| 				let mut transaction_queue = self.transaction_queue.lock(); | ||||
| 				let _ = self.add_transactions_to_queue( | ||||
| 					chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue | ||||
| 				); | ||||
| 			}); | ||||
| 			retracted.par_iter() | ||||
| 				.map(|hash| { | ||||
| 					let block = chain.block(BlockId::Hash(*hash)) | ||||
| 						.expect("Client is sending message after commit to db and inserting to chain; the block is available; qed"); | ||||
| 					let block = BlockView::new(&block); | ||||
| 					let txs = block.transactions(); | ||||
| 					// populate sender
 | ||||
| 					for tx in &txs { | ||||
| 						let _sender = tx.sender(); | ||||
| 					} | ||||
| 					txs | ||||
| 				}).for_each(|txs| { | ||||
| 					let mut transaction_queue = self.transaction_queue.lock(); | ||||
| 					let _ = self.add_transactions_to_queue( | ||||
| 						chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue | ||||
| 					); | ||||
| 				}); | ||||
| 		} | ||||
| 
 | ||||
| 		// ...and at the end remove old ones
 | ||||
| 		// ...and at the end remove the old ones
 | ||||
| 		{ | ||||
| 			let in_chain = enacted | ||||
| 				.par_iter() | ||||
| 				.map(|h: &H256| fetch_transactions(chain, h)); | ||||
| 
 | ||||
| 			in_chain.for_each(|mut txs| { | ||||
| 				let mut transaction_queue = self.transaction_queue.lock(); | ||||
| 
 | ||||
| 				let to_remove = txs.drain(..) | ||||
| 						.map(|tx| { | ||||
| 							tx.sender().expect("Transaction is in block, so sender has to be defined.") | ||||
| 						}) | ||||
| 						.collect::<HashSet<Address>>(); | ||||
| 				for sender in to_remove { | ||||
| 					transaction_queue.remove_all(sender, chain.latest_nonce(&sender)); | ||||
| 				} | ||||
| 			}); | ||||
| 			let mut transaction_queue = self.transaction_queue.lock(); | ||||
| 			transaction_queue.remove_old(|sender| chain.latest_nonce(sender)); | ||||
| 		} | ||||
| 
 | ||||
| 		if enacted.len() > 0 { | ||||
|  | ||||
| @ -79,8 +79,10 @@ | ||||
| //!		  we check if the transactions should go to `current` (comparing state nonce)
 | ||||
| //!		- When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated.
 | ||||
| //!	3. `remove_all` is used to inform the queue about client (state) nonce changes.
 | ||||
| //!      - It removes all transactions (either from `current` or `future`) with nonce < client nonce
 | ||||
| //!      - It moves matching `future` transactions to `current`
 | ||||
| //!     - It removes all transactions (either from `current` or `future`) with nonce < client nonce
 | ||||
| //!     - It moves matching `future` transactions to `current`
 | ||||
| //! 4. `remove_old` is used as convenient method to update the state nonce for all senders in the queue.
 | ||||
| //!		- Invokes `remove_all` with latest state nonce for all senders.
 | ||||
| 
 | ||||
| use std::ops::Deref; | ||||
| use std::cmp::Ordering; | ||||
| @ -752,6 +754,26 @@ impl TransactionQueue { | ||||
| 	/// Removes all transactions from particular sender up to (excluding) given client (state) nonce.
 | ||||
| 	/// Client (State) Nonce = next valid nonce for this sender.
 | ||||
| 	pub fn remove_all(&mut self, sender: Address, client_nonce: U256) { | ||||
| 		// Check if there is anything in current...
 | ||||
| 		let should_check_in_current = self.current.by_address.row(&sender) | ||||
| 			// If nonce == client_nonce nothing is changed
 | ||||
| 			.and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce < &client_nonce)) | ||||
| 			.map(|_| ()); | ||||
| 		// ... or future
 | ||||
| 		let should_check_in_future = self.future.by_address.row(&sender) | ||||
| 			// if nonce == client_nonce we need to promote to current
 | ||||
| 			.and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce <= &client_nonce)) | ||||
| 			.map(|_| ()); | ||||
| 
 | ||||
| 		if should_check_in_current.or(should_check_in_future).is_none() { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		self.remove_all_internal(sender, client_nonce); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Always updates future and moves transactions from current to future.
 | ||||
| 	fn remove_all_internal(&mut self, sender: Address, client_nonce: U256) { | ||||
| 		// We will either move transaction to future or remove it completely
 | ||||
| 		// so there will be no transactions from this sender in current
 | ||||
| 		self.last_nonces.remove(&sender); | ||||
| @ -765,6 +787,20 @@ impl TransactionQueue { | ||||
| 		assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Checks the current nonce for all transactions' senders in the queue and removes the old transactions.
 | ||||
| 	pub fn remove_old<F>(&mut self, fetch_nonce: F) where | ||||
| 		F: Fn(&Address) -> U256, | ||||
| 	{ | ||||
| 		let senders = self.current.by_address.keys() | ||||
| 			.chain(self.future.by_address.keys()) | ||||
| 			.cloned() | ||||
| 			.collect::<HashSet<_>>(); | ||||
| 
 | ||||
| 		for sender in senders { | ||||
| 			self.remove_all(sender, fetch_nonce(&sender)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Penalize transactions from sender of transaction with given hash.
 | ||||
| 	/// I.e. it should change the priority of the transaction in the queue.
 | ||||
| 	///
 | ||||
| @ -847,7 +883,7 @@ impl TransactionQueue { | ||||
| 		if order.is_some() { | ||||
| 			// This will keep consistency in queue
 | ||||
| 			// Moves all to future and then promotes a batch from current:
 | ||||
| 			self.remove_all(sender, current_nonce); | ||||
| 			self.remove_all_internal(sender, current_nonce); | ||||
| 			assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | ||||
| 			return; | ||||
| 		} | ||||
| @ -2438,7 +2474,7 @@ mod test { | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_reject_transactions_below_bas_gas() { | ||||
| 	fn should_reject_transactions_below_base_gas() { | ||||
| 		// given
 | ||||
| 		let mut txq = TransactionQueue::default(); | ||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||
| @ -2457,4 +2493,26 @@ mod test { | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_clear_all_old_transactions() { | ||||
| 		// given
 | ||||
| 		let mut txq = TransactionQueue::default(); | ||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||
| 		let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into()); | ||||
| 		let nonce1 = tx1.nonce; | ||||
| 
 | ||||
| 		// Insert all transactions
 | ||||
| 		txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); | ||||
| 		txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); | ||||
| 		txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); | ||||
| 		txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); | ||||
| 		assert_eq!(txq.top_transactions().len(), 4); | ||||
| 
 | ||||
| 		// when
 | ||||
| 		txq.remove_old(|_| nonce1 + U256::one()); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(txq.top_transactions().len(), 2); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "parity.js", | ||||
|   "version": "0.2.105", | ||||
|   "version": "0.2.107", | ||||
|   "main": "release/index.js", | ||||
|   "jsnext:main": "src/index.js", | ||||
|   "author": "Parity Team <admin@parity.io>", | ||||
| @ -146,6 +146,7 @@ | ||||
|     "mobx-react-devtools": "4.2.10", | ||||
|     "moment": "2.17.0", | ||||
|     "phoneformat.js": "1.0.3", | ||||
|     "push.js": "0.0.11", | ||||
|     "qs": "6.3.0", | ||||
|     "react": "15.4.1", | ||||
|     "react-ace": "4.1.0", | ||||
|  | ||||
| @ -67,7 +67,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { | ||||
| const api = new SecureApi(`ws://${parityUrl}`, token); | ||||
| ContractInstances.create(api); | ||||
| 
 | ||||
| const store = initStore(api); | ||||
| const store = initStore(api, hashHistory); | ||||
| store.dispatch({ type: 'initAll', api }); | ||||
| store.dispatch(setApi(api)); | ||||
| 
 | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| import thunk from 'redux-thunk'; | ||||
| import { routerMiddleware } from 'react-router-redux'; | ||||
| 
 | ||||
| import ErrorsMiddleware from '~/ui/Errors/middleware'; | ||||
| import SettingsMiddleware from '~/views/Settings/middleware'; | ||||
| @ -22,12 +23,13 @@ import SignerMiddleware from './providers/signerMiddleware'; | ||||
| import statusMiddleware from '~/views/Status/middleware'; | ||||
| import CertificationsMiddleware from './providers/certifications/middleware'; | ||||
| 
 | ||||
| export default function (api) { | ||||
| export default function (api, browserHistory) { | ||||
|   const errors = new ErrorsMiddleware(); | ||||
|   const signer = new SignerMiddleware(api); | ||||
|   const settings = new SettingsMiddleware(); | ||||
|   const status = statusMiddleware(); | ||||
|   const certifications = new CertificationsMiddleware(); | ||||
|   const routeMiddleware = routerMiddleware(browserHistory); | ||||
| 
 | ||||
|   const middleware = [ | ||||
|     settings.toMiddleware(), | ||||
| @ -36,5 +38,5 @@ export default function (api) { | ||||
|     certifications.toMiddleware() | ||||
|   ]; | ||||
| 
 | ||||
|   return middleware.concat(status, thunk); | ||||
|   return middleware.concat(status, routeMiddleware, thunk); | ||||
| } | ||||
|  | ||||
| @ -15,11 +15,14 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { range, uniq, isEqual } from 'lodash'; | ||||
| import BigNumber from 'bignumber.js'; | ||||
| import { push } from 'react-router-redux'; | ||||
| 
 | ||||
| import { hashToImageUrl } from './imagesReducer'; | ||||
| import { setAddressImage } from './imagesActions'; | ||||
| 
 | ||||
| import * as ABIS from '~/contracts/abi'; | ||||
| import { notifyTransaction } from '~/util/notifications'; | ||||
| import imagesEthereum from '../../../assets/images/contracts/ethereum-black-64x64.png'; | ||||
| 
 | ||||
| const ETH = { | ||||
| @ -28,7 +31,64 @@ const ETH = { | ||||
|   image: imagesEthereum | ||||
| }; | ||||
| 
 | ||||
| export function setBalances (balances) { | ||||
| function setBalances (_balances) { | ||||
|   return (dispatch, getState) => { | ||||
|     const state = getState(); | ||||
| 
 | ||||
|     const accounts = state.personal.accounts; | ||||
|     const nextBalances = _balances; | ||||
|     const prevBalances = state.balances.balances; | ||||
|     const balances = { ...prevBalances }; | ||||
| 
 | ||||
|     Object.keys(nextBalances).forEach((address) => { | ||||
|       if (!balances[address]) { | ||||
|         balances[address] = Object.assign({}, nextBalances[address]); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       const balance = Object.assign({}, balances[address]); | ||||
|       const { tokens, txCount = balance.txCount } = nextBalances[address]; | ||||
|       const nextTokens = [].concat(balance.tokens); | ||||
| 
 | ||||
|       tokens.forEach((t) => { | ||||
|         const { token, value } = t; | ||||
|         const { tag } = token; | ||||
| 
 | ||||
|         const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag); | ||||
| 
 | ||||
|         if (tokenIndex === -1) { | ||||
|           nextTokens.push({ | ||||
|             token, | ||||
|             value | ||||
|           }); | ||||
|         } else { | ||||
|           const oldValue = nextTokens[tokenIndex].value; | ||||
| 
 | ||||
|           // If received a token/eth (old value < new value), notify
 | ||||
|           if (oldValue.lt(value) && accounts[address]) { | ||||
|             const account = accounts[address]; | ||||
|             const txValue = value.minus(oldValue); | ||||
| 
 | ||||
|             const redirectToAccount = () => { | ||||
|               const route = `/account/${account.address}`; | ||||
|               dispatch(push(route)); | ||||
|             }; | ||||
| 
 | ||||
|             notifyTransaction(account, token, txValue, redirectToAccount); | ||||
|           } | ||||
| 
 | ||||
|           nextTokens[tokenIndex] = { token, value }; | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       balances[address] = { txCount: txCount || new BigNumber(0), tokens: nextTokens }; | ||||
|     }); | ||||
| 
 | ||||
|     dispatch(_setBalances(balances)); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function _setBalances (balances) { | ||||
|   return { | ||||
|     type: 'setBalances', | ||||
|     balances | ||||
| @ -123,14 +183,14 @@ export function fetchBalances (_addresses) { | ||||
| 
 | ||||
|     const fullFetch = addresses.length === 1; | ||||
| 
 | ||||
|     const fetchedAddresses = uniq(addresses.concat(Object.keys(accounts))); | ||||
|     const addressesToFetch = uniq(addresses.concat(Object.keys(accounts))); | ||||
| 
 | ||||
|     return Promise | ||||
|       .all(fetchedAddresses.map((addr) => fetchAccount(addr, api, fullFetch))) | ||||
|       .all(addressesToFetch.map((addr) => fetchAccount(addr, api, fullFetch))) | ||||
|       .then((accountsBalances) => { | ||||
|         const balances = {}; | ||||
| 
 | ||||
|         fetchedAddresses.forEach((addr, idx) => { | ||||
|         addressesToFetch.forEach((addr, idx) => { | ||||
|           balances[addr] = accountsBalances[idx]; | ||||
|         }); | ||||
| 
 | ||||
| @ -146,10 +206,12 @@ export function fetchBalances (_addresses) { | ||||
| export function updateTokensFilter (_addresses, _tokens) { | ||||
|   return (dispatch, getState) => { | ||||
|     const { api, balances, personal } = getState(); | ||||
|     const { visibleAccounts } = personal; | ||||
|     const { visibleAccounts, accounts } = personal; | ||||
|     const { tokensFilter } = balances; | ||||
| 
 | ||||
|     const addresses = uniq(_addresses || visibleAccounts || []).sort(); | ||||
|     const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts))); | ||||
|     const addresses = uniq(_addresses || addressesToFetch || []).sort(); | ||||
| 
 | ||||
|     const tokens = _tokens || Object.values(balances.tokens) || []; | ||||
|     const tokenAddresses = tokens.map((t) => t.address).sort(); | ||||
| 
 | ||||
| @ -221,8 +283,10 @@ export function updateTokensFilter (_addresses, _tokens) { | ||||
| export function queryTokensFilter (tokensFilter) { | ||||
|   return (dispatch, getState) => { | ||||
|     const { api, personal, balances } = getState(); | ||||
|     const { visibleAccounts } = personal; | ||||
|     const { visibleAccounts, accounts } = personal; | ||||
| 
 | ||||
|     const visibleAddresses = visibleAccounts.map((a) => a.toLowerCase()); | ||||
|     const addressesToFetch = uniq(visibleAddresses.concat(Object.keys(accounts))); | ||||
| 
 | ||||
|     Promise | ||||
|       .all([ | ||||
| @ -237,18 +301,16 @@ export function queryTokensFilter (tokensFilter) { | ||||
|           .concat(logsTo) | ||||
|           .forEach((log) => { | ||||
|             const tokenAddress = log.address; | ||||
| 
 | ||||
|             const fromAddress = '0x' + log.topics[1].slice(-40); | ||||
|             const toAddress = '0x' + log.topics[2].slice(-40); | ||||
| 
 | ||||
|             const fromIdx = visibleAddresses.indexOf(fromAddress); | ||||
|             const toIdx = visibleAddresses.indexOf(toAddress); | ||||
| 
 | ||||
|             if (fromIdx > -1) { | ||||
|               addresses.push(visibleAccounts[fromIdx]); | ||||
|             if (addressesToFetch.includes(fromAddress)) { | ||||
|               addresses.push(fromAddress); | ||||
|             } | ||||
| 
 | ||||
|             if (toIdx > -1) { | ||||
|               addresses.push(visibleAccounts[toIdx]); | ||||
|             if (addressesToFetch.includes(toAddress)) { | ||||
|               addresses.push(toAddress); | ||||
|             } | ||||
| 
 | ||||
|             tokenAddresses.push(tokenAddress); | ||||
| @ -269,9 +331,10 @@ export function queryTokensFilter (tokensFilter) { | ||||
| export function fetchTokensBalances (_addresses = null, _tokens = null) { | ||||
|   return (dispatch, getState) => { | ||||
|     const { api, personal, balances } = getState(); | ||||
|     const { visibleAccounts } = personal; | ||||
|     const { visibleAccounts, accounts } = personal; | ||||
| 
 | ||||
|     const addresses = _addresses || visibleAccounts; | ||||
|     const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts))); | ||||
|     const addresses = _addresses || addressesToFetch; | ||||
|     const tokens = _tokens || Object.values(balances.tokens); | ||||
| 
 | ||||
|     if (addresses.length === 0) { | ||||
|  | ||||
| @ -15,7 +15,6 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { handleActions } from 'redux-actions'; | ||||
| import BigNumber from 'bignumber.js'; | ||||
| 
 | ||||
| const initialState = { | ||||
|   balances: {}, | ||||
| @ -26,39 +25,7 @@ const initialState = { | ||||
| 
 | ||||
| export default handleActions({ | ||||
|   setBalances (state, action) { | ||||
|     const nextBalances = action.balances; | ||||
|     const prevBalances = state.balances; | ||||
|     const balances = { ...prevBalances }; | ||||
| 
 | ||||
|     Object.keys(nextBalances).forEach((address) => { | ||||
|       if (!balances[address]) { | ||||
|         balances[address] = Object.assign({}, nextBalances[address]); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       const balance = Object.assign({}, balances[address]); | ||||
|       const { tokens, txCount = balance.txCount } = nextBalances[address]; | ||||
|       const nextTokens = [].concat(balance.tokens); | ||||
| 
 | ||||
|       tokens.forEach((t) => { | ||||
|         const { token, value } = t; | ||||
|         const { tag } = token; | ||||
| 
 | ||||
|         const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag); | ||||
| 
 | ||||
|         if (tokenIndex === -1) { | ||||
|           nextTokens.push({ | ||||
|             token, | ||||
|             value | ||||
|           }); | ||||
|         } else { | ||||
|           nextTokens[tokenIndex] = { token, value }; | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       balances[address] = Object.assign({}, { txCount: txCount || new BigNumber(0), tokens: nextTokens }); | ||||
|     }); | ||||
| 
 | ||||
|     const { balances } = action; | ||||
|     return Object.assign({}, state, { balances }); | ||||
|   }, | ||||
| 
 | ||||
|  | ||||
| @ -21,11 +21,11 @@ export Status from './status'; | ||||
| 
 | ||||
| export apiReducer from './apiReducer'; | ||||
| export balancesReducer from './balancesReducer'; | ||||
| export blockchainReducer from './blockchainReducer'; | ||||
| export compilerReducer from './compilerReducer'; | ||||
| export imagesReducer from './imagesReducer'; | ||||
| export personalReducer from './personalReducer'; | ||||
| export signerReducer from './signerReducer'; | ||||
| export statusReducer from './statusReducer'; | ||||
| export blockchainReducer from './blockchainReducer'; | ||||
| export compilerReducer from './compilerReducer'; | ||||
| export snackbarReducer from './snackbarReducer'; | ||||
| export statusReducer from './statusReducer'; | ||||
| export walletReducer from './walletReducer'; | ||||
|  | ||||
| @ -54,7 +54,10 @@ export default class Status { | ||||
|         this._api.eth | ||||
|           .getBlockByNumber(blockNumber) | ||||
|           .then((block) => { | ||||
|             this._store.dispatch(statusCollection({ gasLimit: block.gasLimit })); | ||||
|             this._store.dispatch(statusCollection({ | ||||
|               blockTimestamp: block.timestamp, | ||||
|               gasLimit: block.gasLimit | ||||
|             })); | ||||
|           }) | ||||
|           .catch((error) => { | ||||
|             console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error); | ||||
|  | ||||
| @ -19,6 +19,7 @@ import { handleActions } from 'redux-actions'; | ||||
| 
 | ||||
| const initialState = { | ||||
|   blockNumber: new BigNumber(0), | ||||
|   blockTimestamp: new Date(), | ||||
|   devLogs: [], | ||||
|   devLogsLevels: null, | ||||
|   devLogsEnabled: false, | ||||
|  | ||||
| @ -17,7 +17,12 @@ | ||||
| import { combineReducers } from 'redux'; | ||||
| import { routerReducer } from 'react-router-redux'; | ||||
| 
 | ||||
| import { apiReducer, balancesReducer, blockchainReducer, compilerReducer, imagesReducer, personalReducer, signerReducer, statusReducer as nodeStatusReducer, snackbarReducer, walletReducer } from './providers'; | ||||
| import { | ||||
|   apiReducer, balancesReducer, blockchainReducer, | ||||
|   compilerReducer, imagesReducer, personalReducer, | ||||
|   signerReducer, statusReducer as nodeStatusReducer, | ||||
|   snackbarReducer, walletReducer | ||||
| } from './providers'; | ||||
| import certificationsReducer from './providers/certifications/reducer'; | ||||
| 
 | ||||
| import errorReducer from '~/ui/Errors/reducers'; | ||||
|  | ||||
| @ -32,9 +32,9 @@ const storeCreation = window.devToolsExtension | ||||
|   ? window.devToolsExtension()(createStore) | ||||
|   : createStore; | ||||
| 
 | ||||
| export default function (api) { | ||||
| export default function (api, browserHistory) { | ||||
|   const reducers = initReducers(); | ||||
|   const middleware = initMiddleware(api); | ||||
|   const middleware = initMiddleware(api, browserHistory); | ||||
|   const store = applyMiddleware(...middleware)(storeCreation)(reducers); | ||||
| 
 | ||||
|   new BalancesProvider(store, api).start(); | ||||
|  | ||||
| @ -17,11 +17,13 @@ | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar'; | ||||
| 
 | ||||
| import { nodeOrStringProptype } from '~/util/proptypes'; | ||||
| 
 | ||||
| import styles from './actionbar.css'; | ||||
| 
 | ||||
| export default class Actionbar extends Component { | ||||
|   static propTypes = { | ||||
|     title: PropTypes.string, | ||||
|     title: nodeOrStringProptype(), | ||||
|     buttons: PropTypes.array, | ||||
|     children: PropTypes.node, | ||||
|     className: PropTypes.string | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
| .layout { | ||||
|   padding: 0.25em 0.25em 1em 0.25em; | ||||
| 
 | ||||
|   > * { | ||||
|   &>div { | ||||
|     margin-bottom: 0.75em; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -16,21 +16,38 @@ | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| 
 | ||||
| import Actionbar from '../Actionbar'; | ||||
| import { nodeOrStringProptype } from '~/util/proptypes'; | ||||
| 
 | ||||
| import styles from './page.css'; | ||||
| 
 | ||||
| export default class Page extends Component { | ||||
|   static propTypes = { | ||||
|     buttons: PropTypes.array, | ||||
|     className: PropTypes.string, | ||||
|     children: PropTypes.node | ||||
|     children: PropTypes.node, | ||||
|     title: nodeOrStringProptype() | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { className, children } = this.props; | ||||
|     const { buttons, className, children, title } = this.props; | ||||
|     const classes = `${styles.layout} ${className}`; | ||||
|     let actionbar = null; | ||||
| 
 | ||||
|     if (title || buttons) { | ||||
|       actionbar = ( | ||||
|         <Actionbar | ||||
|           buttons={ buttons } | ||||
|           title={ title } /> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={ classes }> | ||||
|         { children } | ||||
|       <div> | ||||
|         { actionbar } | ||||
|         <div className={ classes }> | ||||
|           { children } | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
							
								
								
									
										45
									
								
								js/src/util/notifications.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								js/src/util/notifications.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||
| // This file is part of Parity.
 | ||||
| 
 | ||||
| // Parity is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| 
 | ||||
| // Parity is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||
| // GNU General Public License for more details.
 | ||||
| 
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import Push from 'push.js'; | ||||
| import BigNumber from 'bignumber.js'; | ||||
| import { noop } from 'lodash'; | ||||
| 
 | ||||
| import { fromWei } from '~/api/util/wei'; | ||||
| 
 | ||||
| import ethereumIcon from '~/../assets/images/contracts/ethereum-black-64x64.png'; | ||||
| import unkownIcon from '~/../assets/images/contracts/unknown-64x64.png'; | ||||
| 
 | ||||
| export function notifyTransaction (account, token, _value, onClick) { | ||||
|   const name = account.name || account.address; | ||||
|   const value = token.tag.toLowerCase() === 'eth' | ||||
|     ? fromWei(_value) | ||||
|     : _value.div(new BigNumber(token.format || 1)); | ||||
| 
 | ||||
|   const icon = token.tag.toLowerCase() === 'eth' | ||||
|     ? ethereumIcon | ||||
|     : (token.image || unkownIcon); | ||||
| 
 | ||||
|   Push.create(`${name}`, { | ||||
|     body: `You just received ${value.toFormat()} ${token.tag.toUpperCase()}`, | ||||
|     icon: { | ||||
|       x16: icon, | ||||
|       x32: icon | ||||
|     }, | ||||
|     timeout: 20000, | ||||
|     onClick: onClick || noop | ||||
|   }); | ||||
| } | ||||
| @ -51,9 +51,16 @@ export default class Dapp extends Component { | ||||
|         src = `${dappsUrl}/${app.contentHash}/`; | ||||
|         break; | ||||
|       default: | ||||
|         const dapphost = process.env.NODE_ENV === 'production' && !app.secure | ||||
|           ? `${dappsUrl}/ui` | ||||
|           : ''; | ||||
|         let dapphost = process.env.DAPPS_URL || ( | ||||
|           process.env.NODE_ENV === 'production' && !app.secure | ||||
|             ? `${dappsUrl}/ui` | ||||
|             : '' | ||||
|         ); | ||||
| 
 | ||||
|         if (dapphost === '/') { | ||||
|           dapphost = ''; | ||||
|         } | ||||
| 
 | ||||
|         src = `${dapphost}/${app.url}.html`; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
| @ -23,8 +23,7 @@ export default class Signer extends Component { | ||||
|   render () { | ||||
|     return ( | ||||
|       <div> | ||||
|         <Actionbar | ||||
|           title='Trusted Signer' /> | ||||
|         <Actionbar title='Trusted Signer' /> | ||||
|         <RequestsPage /> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -30,12 +30,6 @@ export default class Store { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @action unsubscribe () { | ||||
|     if (this._timeoutId) { | ||||
|       clearTimeout(this._timeoutId); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @action setBalance = (address, balance) => { | ||||
|     this.setBalances({ [address]: balance }); | ||||
|   } | ||||
| @ -50,6 +44,12 @@ export default class Store { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @action unsubscribe () { | ||||
|     if (this._timeoutId) { | ||||
|       clearTimeout(this._timeoutId); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   fetchBalance (address) { | ||||
|     this._api.eth | ||||
|       .getBalance(address) | ||||
|  | ||||
| @ -22,7 +22,7 @@ import ReorderIcon from 'material-ui/svg-icons/action/reorder'; | ||||
| 
 | ||||
| import { Container } from '~/ui'; | ||||
| 
 | ||||
| import styles from './Debug.css'; | ||||
| import styles from './debug.css'; | ||||
| 
 | ||||
| export default class Debug extends Component { | ||||
|   static propTypes = { | ||||
| @ -14,4 +14,4 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './Debug'; | ||||
| export default from './debug'; | ||||
|  | ||||
| @ -14,4 +14,4 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './MiningSettings'; | ||||
| export default from './miningSettings'; | ||||
|  | ||||
| @ -14,4 +14,4 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './Status'; | ||||
| export default from './status'; | ||||
|  | ||||
| @ -28,10 +28,19 @@ | ||||
|   content: ''; | ||||
| } | ||||
| 
 | ||||
| .blockinfo { | ||||
|   font-size: 24px; | ||||
| .blockInfo { | ||||
|   color: #aaa; | ||||
|   line-height: 24px; | ||||
|   font-size: 1.5em; | ||||
|   line-height: 1.5em; | ||||
| } | ||||
| 
 | ||||
| .blockByline { | ||||
|   color: #aaa; | ||||
|   font-size: 0.75em; | ||||
| } | ||||
| 
 | ||||
| .padBottom { | ||||
|   padding-bottom: 1.25em !important; | ||||
| } | ||||
| 
 | ||||
| .col, | ||||
| @ -14,14 +14,16 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import bytes from 'bytes'; | ||||
| import moment from 'moment'; | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| 
 | ||||
| import { Container, ContainerTitle, Input } from '~/ui'; | ||||
| 
 | ||||
| import styles from './Status.css'; | ||||
| import MiningSettings from '../MiningSettings'; | ||||
| 
 | ||||
| import styles from './status.css'; | ||||
| 
 | ||||
| export default class Status extends Component { | ||||
|   static propTypes = { | ||||
|     nodeStatus: PropTypes.object.isRequired, | ||||
| @ -44,23 +46,26 @@ export default class Status extends Component { | ||||
|         <div className={ styles.container }> | ||||
|           <div className={ styles.row }> | ||||
|             <div className={ styles.col3 }> | ||||
|               <div className={ styles.col12 }> | ||||
|               <div className={ `${styles.col12} ${styles.padBottom}` }> | ||||
|                 <ContainerTitle title='best block' /> | ||||
|                 <h2 { ...this._test('best-block') } className={ styles.blockinfo }> | ||||
|                 <div { ...this._test('best-block') } className={ styles.blockInfo }> | ||||
|                   #{ nodeStatus.blockNumber.toFormat() } | ||||
|                 </h2> | ||||
|                 </div> | ||||
|                 <div className={ styles.blockByline }> | ||||
|                   { moment().calendar(nodeStatus.blockTimestamp) } | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div className={ styles.col12 }> | ||||
|               <div className={ `${styles.col12} ${styles.padBottom}` }> | ||||
|                 <ContainerTitle title='peers' /> | ||||
|                 <h2 { ...this._test('peers') } className={ styles.blockinfo }> | ||||
|                 <div { ...this._test('peers') } className={ styles.blockInfo }> | ||||
|                   { peers } | ||||
|                 </h2> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div className={ styles.col12 }> | ||||
|               <div className={ `${styles.col12} ${styles.padBottom}` }> | ||||
|                 <ContainerTitle title='hash rate' /> | ||||
|                 <h2 { ...this._test('hashrate') } className={ styles.blockinfo }> | ||||
|                 <div { ...this._test('hashrate') } className={ styles.blockInfo }> | ||||
|                   { `${hashrate} H/s` } | ||||
|                 </h2> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className={ styles.col5 }> | ||||
| @ -14,4 +14,4 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './StatusPage'; | ||||
| export default from './statusPage'; | ||||
|  | ||||
| @ -14,5 +14,9 @@ | ||||
| /* You should have received a copy of the GNU General Public License | ||||
| /* along with Parity.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| .container { | ||||
| 
 | ||||
| .body { | ||||
|   &>div { | ||||
|     margin-bottom: 0.25em; | ||||
|   } | ||||
| } | ||||
| @ -23,6 +23,8 @@ 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, | ||||
| @ -39,7 +41,7 @@ class StatusPage extends Component { | ||||
| 
 | ||||
|   render () { | ||||
|     return ( | ||||
|       <div> | ||||
|       <div className={ styles.body }> | ||||
|         <Status { ...this.props } /> | ||||
|         <Debug { ...this.props } /> | ||||
|       </div> | ||||
| @ -16,22 +16,16 @@ | ||||
| 
 | ||||
| import React, { Component } from 'react'; | ||||
| 
 | ||||
| import { Actionbar, Page } from '~/ui'; | ||||
| import { Page } from '~/ui'; | ||||
| 
 | ||||
| import StatusPage from './containers/StatusPage'; | ||||
| 
 | ||||
| import styles from './status.css'; | ||||
| 
 | ||||
| export default class Status extends Component { | ||||
|   render () { | ||||
|     return ( | ||||
|       <div className={ styles.container }> | ||||
|         <Actionbar | ||||
|           title='status' /> | ||||
|         <Page> | ||||
|           <StatusPage /> | ||||
|         </Page> | ||||
|       </div> | ||||
|       <Page title='status'> | ||||
|         <StatusPage /> | ||||
|       </Page> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -20,6 +20,7 @@ const path = require('path'); | ||||
| const WebpackErrorNotificationPlugin = require('webpack-error-notification'); | ||||
| const CopyWebpackPlugin = require('copy-webpack-plugin'); | ||||
| const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||||
| const ExtractTextPlugin = require('extract-text-webpack-plugin'); | ||||
| 
 | ||||
| const Shared = require('./shared'); | ||||
| const DAPPS = require('../src/dapps'); | ||||
| @ -41,7 +42,7 @@ module.exports = { | ||||
|   output: { | ||||
|     publicPath: '/', | ||||
|     path: path.join(__dirname, '../', DEST), | ||||
|     filename: '[name].[hash].js' | ||||
|     filename: '[name].[hash:10].js' | ||||
|   }, | ||||
| 
 | ||||
|   module: { | ||||
| @ -85,13 +86,20 @@ module.exports = { | ||||
|       { | ||||
|         test: /\.css$/, | ||||
|         include: [ /src/ ], | ||||
|         // exclude: [ /src\/dapps/ ],
 | ||||
|         loader: isProd ? ExtractTextPlugin.extract([ | ||||
|           // 'style-loader',
 | ||||
|           'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', | ||||
|           'postcss-loader' | ||||
|         ]) : undefined, | ||||
|         // use: [ 'happypack/loader?id=css' ]
 | ||||
|         use: [ | ||||
|         use: isProd ? undefined : [ | ||||
|           'style-loader', | ||||
|           'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', | ||||
|           'postcss-loader' | ||||
|         ] | ||||
|       }, | ||||
| 
 | ||||
|       { | ||||
|         test: /\.css$/, | ||||
|         exclude: [ /src/ ], | ||||
| @ -99,11 +107,15 @@ module.exports = { | ||||
|       }, | ||||
|       { | ||||
|         test: /\.(png|jpg)$/, | ||||
|         use: [ 'file-loader?name=[name].[hash].[ext]' ] | ||||
|         use: [ 'file-loader?&name=assets/[name].[hash:10].[ext]' ] | ||||
|       }, | ||||
|       { | ||||
|         test: /\.(woff(2)|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, | ||||
|         use: [ 'file-loader' ] | ||||
|         test: /\.(woff(2)|ttf|eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, | ||||
|         use: [ 'file-loader?name=fonts/[name][hash:10].[ext]' ] | ||||
|       }, | ||||
|       { | ||||
|         test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/, | ||||
|         use: [ 'file-loader?name=assets/[name].[hash:10].[ext]' ] | ||||
|       } | ||||
|     ], | ||||
|     noParse: [ | ||||
| @ -153,13 +165,20 @@ module.exports = { | ||||
|     if (!isProd) { | ||||
|       plugins.push( | ||||
|         new webpack.optimize.CommonsChunkPlugin({ | ||||
|           filename: 'commons.[hash].js', | ||||
|           filename: 'commons.[hash:10].js', | ||||
|           name: 'commons', | ||||
|           minChunks: Infinity | ||||
|         }) | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (isProd) { | ||||
|       plugins.push(new ExtractTextPlugin({ | ||||
|         filename: 'styles/[name].[hash:10].css', | ||||
|         allChunks: true | ||||
|       })); | ||||
|     } | ||||
| 
 | ||||
|     return plugins; | ||||
|   }()) | ||||
| }; | ||||
|  | ||||
| @ -89,11 +89,13 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockC | ||||
| 		signer.peek(&id).map(|confirmation| { | ||||
| 			let mut payload = confirmation.payload.clone(); | ||||
| 			// Modify payload
 | ||||
| 			match (&mut payload, modification.gas_price) { | ||||
| 				(&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => { | ||||
| 			if let ConfirmationPayload::SendTransaction(ref mut request) = payload { | ||||
| 				if let Some(gas_price) = modification.gas_price { | ||||
| 					request.gas_price = gas_price.into(); | ||||
| 				}, | ||||
| 				_ => {}, | ||||
| 				} | ||||
| 				if let Some(gas) = modification.gas { | ||||
| 					request.gas = gas.into(); | ||||
| 				} | ||||
| 			} | ||||
| 			// Execute
 | ||||
| 			let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass)); | ||||
|  | ||||
| @ -183,7 +183,7 @@ fn should_confirm_transaction_and_dispatch() { | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
| 		gas_price: U256::from(0x1000), | ||||
| 		gas: U256::from(10_000_000), | ||||
| 		gas: U256::from(0x50505), | ||||
| 		action: Action::Call(recipient), | ||||
| 		value: U256::from(0x1), | ||||
| 		data: vec![] | ||||
| @ -198,7 +198,7 @@ fn should_confirm_transaction_and_dispatch() { | ||||
| 	let request = r#"{
 | ||||
| 		"jsonrpc":"2.0", | ||||
| 		"method":"signer_confirmRequest", | ||||
| 		"params":["0x1", {"gasPrice":"0x1000"}, "test"], | ||||
| 		"params":["0x1", {"gasPrice":"0x1000","gas":"0x50505"}, "test"], | ||||
| 		"id":1 | ||||
| 	}"#;
 | ||||
| 	let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; | ||||
|  | ||||
| @ -142,6 +142,8 @@ pub struct TransactionModification { | ||||
| 	/// Modified gas price
 | ||||
| 	#[serde(rename="gasPrice")] | ||||
| 	pub gas_price: Option<U256>, | ||||
| 	/// Modified gas
 | ||||
| 	pub gas: Option<U256>, | ||||
| } | ||||
| 
 | ||||
| /// Represents two possible return values.
 | ||||
| @ -275,18 +277,26 @@ mod tests { | ||||
| 		let s1 = r#"{
 | ||||
| 			"gasPrice":"0xba43b7400" | ||||
| 		}"#;
 | ||||
| 		let s2 = r#"{}"#; | ||||
| 		let s2 = r#"{"gas": "0x1233"}"#; | ||||
| 		let s3 = r#"{}"#; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res1: TransactionModification = serde_json::from_str(s1).unwrap(); | ||||
| 		let res2: TransactionModification = serde_json::from_str(s2).unwrap(); | ||||
| 		let res3: TransactionModification = serde_json::from_str(s3).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res1, TransactionModification { | ||||
| 			gas_price: Some(U256::from_str("0ba43b7400").unwrap()), | ||||
| 			gas: None, | ||||
| 		}); | ||||
| 		assert_eq!(res2, TransactionModification { | ||||
| 			gas_price: None, | ||||
| 			gas: Some(U256::from_str("1233").unwrap()), | ||||
| 		}); | ||||
| 		assert_eq!(res3, TransactionModification { | ||||
| 			gas_price: None, | ||||
| 			gas: None, | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -1678,7 +1678,7 @@ impl ChainSync { | ||||
| 
 | ||||
| 	pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { | ||||
| 		if packet_id != STATUS_PACKET && !self.peers.contains_key(&peer) { | ||||
| 			debug!(target:"sync", "Unexpected packet from unregistered peer: {}:{}", peer, io.peer_info(peer)); | ||||
| 			debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer)); | ||||
| 			return; | ||||
| 		} | ||||
| 		let rlp = UntrustedRlp::new(data); | ||||
|  | ||||
| @ -131,6 +131,10 @@ impl<'s, 'h> SyncIo for NetSyncIo<'s, 'h> { | ||||
| 	fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { | ||||
| 		self.network.protocol_version(*protocol, peer_id).unwrap_or(0) | ||||
| 	} | ||||
| 
 | ||||
| 	fn peer_info(&self, peer_id: PeerId) -> String { | ||||
| 		self.network.peer_client_version(peer_id) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -22,7 +22,7 @@ use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; | ||||
| use std::ops::*; | ||||
| use std::cmp::min; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::io::{Read, Write}; | ||||
| use std::io::{Read, Write, ErrorKind}; | ||||
| use std::fs; | ||||
| use ethkey::{KeyPair, Secret, Random, Generator}; | ||||
| use mio::*; | ||||
| @ -381,8 +381,6 @@ pub struct Host { | ||||
| impl Host { | ||||
| 	/// Create a new instance
 | ||||
| 	pub fn new(mut config: NetworkConfiguration, stats: Arc<NetworkStats>) -> Result<Host, NetworkError> { | ||||
| 		trace!(target: "host", "Creating new Host object"); | ||||
| 
 | ||||
| 		let mut listen_address = match config.listen_address { | ||||
| 			None => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), DEFAULT_PORT)), | ||||
| 			Some(addr) => addr, | ||||
| @ -405,6 +403,7 @@ impl Host { | ||||
| 		// Setup the server socket
 | ||||
| 		let tcp_listener = try!(TcpListener::bind(&listen_address)); | ||||
| 		listen_address = SocketAddr::new(listen_address.ip(), try!(tcp_listener.local_addr()).port()); | ||||
| 		debug!(target: "network", "Listening at {:?}", listen_address); | ||||
| 		let udp_port = config.udp_port.unwrap_or(listen_address.port()); | ||||
| 		let local_endpoint = NodeEndpoint { address: listen_address, udp_port: udp_port }; | ||||
| 
 | ||||
| @ -707,7 +706,10 @@ impl Host { | ||||
| 				} | ||||
| 			}; | ||||
| 			match TcpStream::connect(&address) { | ||||
| 				Ok(socket) => socket, | ||||
| 				Ok(socket) => { | ||||
| 					trace!(target: "network", "Connecting to {:?}", address); | ||||
| 					socket | ||||
| 				}, | ||||
| 				Err(e) => { | ||||
| 					debug!(target: "network", "Can't connect to address {:?}: {:?}", address, e); | ||||
| 					return; | ||||
| @ -749,7 +751,9 @@ impl Host { | ||||
| 			let socket = match self.tcp_listener.lock().accept() { | ||||
| 				Ok((sock, _addr)) => sock, | ||||
| 				Err(e) => { | ||||
| 					debug!(target: "network", "Error accepting connection: {:?}", e); | ||||
| 					if e.kind() != ErrorKind::WouldBlock { | ||||
| 						debug!(target: "network", "Error accepting connection: {:?}", e); | ||||
| 					} | ||||
| 					break
 | ||||
| 				}, | ||||
| 			}; | ||||
|  | ||||
| @ -435,16 +435,16 @@ impl Session { | ||||
| 
 | ||||
| 				// map to protocol
 | ||||
| 				let protocol = self.info.capabilities[i].protocol; | ||||
| 				let pid = packet_id - self.info.capabilities[i].id_offset; | ||||
| 				let protocol_packet_id = packet_id - self.info.capabilities[i].id_offset; | ||||
| 
 | ||||
| 				match *self.protocol_states.entry(protocol).or_insert_with(|| ProtocolState::Pending(Vec::new())) { | ||||
| 					ProtocolState::Connected => { | ||||
| 						trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, pid, i, self.info.capabilities); | ||||
| 						Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) | ||||
| 						trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, protocol_packet_id, i, self.info.capabilities); | ||||
| 						Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: protocol_packet_id } ) | ||||
| 					} | ||||
| 					ProtocolState::Pending(ref mut pending) => { | ||||
| 						trace!(target: "network", "Packet {} deferred until protocol connection event completion", packet_id); | ||||
| 						pending.push((packet.data, packet_id)); | ||||
| 						pending.push((packet.data, protocol_packet_id)); | ||||
| 
 | ||||
| 						Ok(SessionData::Continue) | ||||
| 					} | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
| 
 | ||||
| use std::hash::Hash; | ||||
| use std::collections::HashMap; | ||||
| use std::collections::hash_map::Keys; | ||||
| 
 | ||||
| /// Structure to hold double-indexed values
 | ||||
| ///
 | ||||
| @ -41,6 +42,11 @@ impl<Row, Col, Val> Table<Row, Col, Val> | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Returns keys iterator for this Table.
 | ||||
| 	pub fn keys(&self) -> Keys<Row, HashMap<Col, Val>> { | ||||
| 		self.map.keys() | ||||
| 	} | ||||
| 
 | ||||
| 	/// Removes all elements from this Table
 | ||||
| 	pub fn clear(&mut self) { | ||||
| 		self.map.clear(); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user