From ac2c678e6907670884989166d9954356f4f472b4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 4 Jan 2017 20:10:19 +0100 Subject: [PATCH 01/13] reverse caching: redux boilerplate --- js/src/redux/providers/registry/actions.js | 28 +++++++++++++++++++++ js/src/redux/providers/registry/reducer.js | 29 ++++++++++++++++++++++ js/src/redux/reducers.js | 4 ++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 js/src/redux/providers/registry/actions.js create mode 100644 js/src/redux/providers/registry/reducer.js diff --git a/js/src/redux/providers/registry/actions.js b/js/src/redux/providers/registry/actions.js new file mode 100644 index 00000000000..eeeb47f51ae --- /dev/null +++ b/js/src/redux/providers/registry/actions.js @@ -0,0 +1,28 @@ +// Copyright 2015, 2016 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 const setReverse = (address, reverse) => ({ + type: 'setReverse', + address, reverse +}); + +export const startCachingReverses = () => ({ + type: 'startCachingReverses' +}); + +export const stopCachingReverses = () => ({ + type: 'stopCachingReverses' +}); diff --git a/js/src/redux/providers/registry/reducer.js b/js/src/redux/providers/registry/reducer.js new file mode 100644 index 00000000000..42663897a68 --- /dev/null +++ b/js/src/redux/providers/registry/reducer.js @@ -0,0 +1,29 @@ +// Copyright 2015, 2016 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 . + +const initialState = { + reverse: {} // cache for reverse lookup +}; + +export default (state = initialState, action) => { + if (action.type === 'setReverse') { + return { ...state, reverse: { + ...state.reverse, [ action.address ]: action.reverse + } }; + } + + return state; +}; diff --git a/js/src/redux/reducers.js b/js/src/redux/reducers.js index aac1ea779a7..577fca11f32 100644 --- a/js/src/redux/reducers.js +++ b/js/src/redux/reducers.js @@ -24,6 +24,7 @@ import { snackbarReducer, walletReducer } from './providers'; import certificationsReducer from './providers/certifications/reducer'; +import registryReducer from './providers/registry/reducer'; import errorReducer from '~/ui/Errors/reducers'; import settingsReducer from '~/views/Settings/reducers'; @@ -46,6 +47,7 @@ export default function () { personal: personalReducer, signer: signerReducer, snackbar: snackbarReducer, - wallet: walletReducer + wallet: walletReducer, + registry: registryReducer }); } From b38aa8d01fc273065517f9ada60bd81b19eafcd8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Jan 2017 16:56:33 +0100 Subject: [PATCH 02/13] reverse caching: middleware --- js/src/redux/middleware.js | 5 +- js/src/redux/providers/registry/middleware.js | 106 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 js/src/redux/providers/registry/middleware.js diff --git a/js/src/redux/middleware.js b/js/src/redux/middleware.js index b62b4e5f283..5cb635f7f2a 100644 --- a/js/src/redux/middleware.js +++ b/js/src/redux/middleware.js @@ -23,6 +23,7 @@ import SignerMiddleware from './providers/signerMiddleware'; import statusMiddleware from '~/views/Status/middleware'; import CertificationsMiddleware from './providers/certifications/middleware'; import ChainMiddleware from './providers/chainMiddleware'; +import RegistryMiddleware from './providers/registry/middleware'; export default function (api, browserHistory) { const errors = new ErrorsMiddleware(); @@ -32,13 +33,15 @@ export default function (api, browserHistory) { const certifications = new CertificationsMiddleware(); const routeMiddleware = routerMiddleware(browserHistory); const chain = new ChainMiddleware(); + const registry = new RegistryMiddleware(); const middleware = [ settings.toMiddleware(), signer.toMiddleware(), errors.toMiddleware(), certifications.toMiddleware(), - chain.toMiddleware() + chain.toMiddleware(), + registry.toMiddleware() ]; return middleware.concat(status, routeMiddleware, thunk); diff --git a/js/src/redux/providers/registry/middleware.js b/js/src/redux/providers/registry/middleware.js new file mode 100644 index 00000000000..2fda34a5605 --- /dev/null +++ b/js/src/redux/providers/registry/middleware.js @@ -0,0 +1,106 @@ +// Copyright 2015, 2016 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 Contracts from '~/contracts'; +import subscribeToEvent from '~/util/subscribe-to-event'; + +import registryABI from '~/contracts/abi/registry.json'; + +import { setReverse, startCachingReverses } from './actions'; + +export default class RegistryMiddleware { + toMiddleware () { + return (store) => { + const api = Contracts.get()._api; + let contract, confirmedEvents, removedEvents, interval; + + let addressesToCheck = {}; + + const onLog = (log) => { + switch (log.event) { + case 'ReverseConfirmed': + addressesToCheck[log.params.reverse.value] = true; + + break; + case 'ReverseRemoved': + delete addressesToCheck[log.params.reverse.value]; + + break; + } + }; + + const checkReverses = () => { + Object + .keys(addressesToCheck) + .forEach((address) => { + contract + .instance + .reverse + .call({}, [ address ]) + .then((reverse) => store.dispatch(setReverse(address, reverse))); + }); + + addressesToCheck = {}; + }; + + return (next) => (action) => { + switch (action.type) { + case 'initAll': + next(action); + store.dispatch(startCachingReverses()); + + break; + case 'startCachingReverses': + const { registry } = Contracts.get(); + + registry.getInstance() + .then((instance) => api.newContract(registryABI, instance.address)) + .then((_contract) => { + contract = _contract; + + confirmedEvents = subscribeToEvent(_contract, 'ReverseConfirmed'); + confirmedEvents.on('log', onLog); + + removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); + removedEvents.on('log', onLog); + + interval = setInterval(checkReverses, 2000); + }) + .catch((err) => { + console.error('Failed to start caching reverses:', err); + throw err; + }); + + break; + case 'stopCachingReverses': + if (confirmedEvents) { + confirmedEvents.unsubscribe(); + } + if (removedEvents) { + removedEvents.unsubscribe(); + } + if (interval) { + clearInterval(interval); + } + + break; + default: + next(action); + } + }; + }; + } +}; From 6f492a49452ac59bba7de4a21d891b113f833b18 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Jan 2017 17:19:26 +0100 Subject: [PATCH 03/13] address selector: use cached reverses --- js/src/ui/Form/AddressSelect/addressSelect.js | 5 +++- .../Form/AddressSelect/addressSelectStore.js | 27 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index 692ff42858a..7b09b5b0767 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -56,6 +56,7 @@ class AddressSelect extends Component { contacts: PropTypes.object, contracts: PropTypes.object, tokens: PropTypes.object, + reverse: PropTypes.object, // Optional props allowCopy: PropTypes.bool, @@ -584,10 +585,12 @@ class AddressSelect extends Component { function mapStateToProps (state) { const { accountsInfo } = state.personal; const { balances } = state.balances; + const { reverse } = state.registry; return { accountsInfo, - balances + balances, + reverse }; } diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 26f9fe80e16..ae868fb4ba6 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -30,7 +30,29 @@ export default class AddressSelectStore { @observable registryValues = []; initValues = []; - regLookups = []; + regLookups = [ + (address) => { + const name = this.reverse[address]; + + if (!name) { + return null; + } + + return { + address, + name, + description: ( + + ) + }; + } + ]; constructor (api) { this.api = api; @@ -114,7 +136,8 @@ export default class AddressSelectStore { } @action setValues (props) { - const { accounts = {}, contracts = {}, contacts = {} } = props; + const { accounts = {}, contracts = {}, contacts = {}, reverse = {} } = props; + this.reverse = reverse; const accountsN = Object.keys(accounts).length; const contractsN = Object.keys(contracts).length; From 71e80f10d88d8ea0d3bcc0c15c94bd9f321c2776 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Jan 2017 17:20:10 +0100 Subject: [PATCH 04/13] reverse caching: optimize timing Users quickly want all reverses registered in the path, but don't care so much about live data. --- js/src/redux/providers/registry/middleware.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/js/src/redux/providers/registry/middleware.js b/js/src/redux/providers/registry/middleware.js index 2fda34a5605..87d9fbf7025 100644 --- a/js/src/redux/providers/registry/middleware.js +++ b/js/src/redux/providers/registry/middleware.js @@ -25,7 +25,7 @@ export default class RegistryMiddleware { toMiddleware () { return (store) => { const api = Contracts.get()._api; - let contract, confirmedEvents, removedEvents, interval; + let contract, confirmedEvents, removedEvents, timeout, interval; let addressesToCheck = {}; @@ -77,7 +77,8 @@ export default class RegistryMiddleware { removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); removedEvents.on('log', onLog); - interval = setInterval(checkReverses, 2000); + timeout = setTimeout(checkReverses, 5000); + interval = setInterval(checkReverses, 20000); }) .catch((err) => { console.error('Failed to start caching reverses:', err); @@ -95,6 +96,9 @@ export default class RegistryMiddleware { if (interval) { clearInterval(interval); } + if (timeout) { + clearTimeout(timeout); + } break; default: @@ -103,4 +107,4 @@ export default class RegistryMiddleware { }; }; } -}; +} From 2aa76e5acce221aab9e680356d375b155688fa60 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Jan 2017 18:21:15 +0100 Subject: [PATCH 05/13] address selector: simple completion for reversed addresses --- .../ui/Form/AddressSelect/addressSelectStore.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index ae868fb4ba6..39894dac90f 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -32,10 +32,23 @@ export default class AddressSelectStore { initValues = []; regLookups = [ (address) => { - const name = this.reverse[address]; + if (address.length === 0 || address === '0x') { + return null; + } + + let name = this.reverse[address]; if (!name) { - return null; + const addr = Object + .keys(this.reverse) + .find((addr) => addr.slice(0, address.length) === address); + + if (addr) { + address = addr; + name = this.reverse[addr]; + } else { + return null; + } } return { From ba4263e33f8c5b40fd1225f921b694b6e2eb99a7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 6 Jan 2017 18:55:18 +0100 Subject: [PATCH 06/13] fix tests :white_check_mark: --- js/src/modals/ExecuteContract/executeContract.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/modals/ExecuteContract/executeContract.test.js b/js/src/modals/ExecuteContract/executeContract.test.js index ce196408dfd..8d9e4ccca39 100644 --- a/js/src/modals/ExecuteContract/executeContract.test.js +++ b/js/src/modals/ExecuteContract/executeContract.test.js @@ -59,6 +59,9 @@ const STORE = { }, settings: { backgroundSeed: '' + }, + registry: { + reverse: {} } }; } From 38734c92c254673c4618e558a1e1e5b0879db252 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 14:02:52 +0100 Subject: [PATCH 07/13] address selector: lower case for reverse completion --- js/src/ui/Form/AddressSelect/addressSelectStore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 39894dac90f..43a8207acae 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -32,6 +32,7 @@ export default class AddressSelectStore { initValues = []; regLookups = [ (address) => { + address = address.toLowerCase().trim(); if (address.length === 0 || address === '0x') { return null; } @@ -41,7 +42,7 @@ export default class AddressSelectStore { if (!name) { const addr = Object .keys(this.reverse) - .find((addr) => addr.slice(0, address.length) === address); + .find((addr) => addr.toLowerCase().slice(0, address.length) === address); if (addr) { address = addr; From f05ffd2e09fb1dae8d1d7491d067c7eaec0a1a93 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 14:13:11 +0100 Subject: [PATCH 08/13] address selector: complete reverses by name as well --- .../Form/AddressSelect/addressSelectStore.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 43a8207acae..4f6de148a1e 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -31,18 +31,26 @@ export default class AddressSelectStore { initValues = []; regLookups = [ - (address) => { - address = address.toLowerCase().trim(); - if (address.length === 0 || address === '0x') { + (query) => { + query = query.toLowerCase().trim(); + if (query.length === 0 || query === '0x') { return null; } - let name = this.reverse[address]; + let address; + let name = this.reverse[query]; if (!name) { const addr = Object .keys(this.reverse) - .find((addr) => addr.toLowerCase().slice(0, address.length) === address); + .find((addr) => { + if (addr.toLowerCase().slice(0, query.length) === query) { + return true; + } + + const name = this.reverse[addr]; + return name.toLowerCase().slice(0, query.length) === query; + }); if (addr) { address = addr; From 8c2aa4d87c35035d6081ae4b235822d003987e6a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 14:24:04 +0100 Subject: [PATCH 09/13] address selector: unique registry results --- js/src/ui/Form/AddressSelect/addressSelectStore.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 4f6de148a1e..1c93088d774 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -16,7 +16,7 @@ import React from 'react'; import { observable, action } from 'mobx'; -import { flatMap } from 'lodash'; +import { flatMap, uniqBy } from 'lodash'; import { FormattedMessage } from 'react-intl'; import Contracts from '~/contracts'; @@ -239,6 +239,8 @@ export default class AddressSelectStore { .filter((result) => result && !ZERO.test(result.address)); }) .then((results) => { + results = uniqBy(results, (result) => result.address); + this.registryValues = results .map((result) => { const lowercaseAddress = result.address.toLowerCase(); From 61a42cc7e4a93ba7c2097285f82505d5a854341b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 16:59:33 +0100 Subject: [PATCH 10/13] reverse caching: pass API into middleware --- js/src/redux/middleware.js | 4 +- js/src/redux/providers/registry/middleware.js | 163 +++++++++--------- 2 files changed, 81 insertions(+), 86 deletions(-) diff --git a/js/src/redux/middleware.js b/js/src/redux/middleware.js index 5cb635f7f2a..bffeddc98f1 100644 --- a/js/src/redux/middleware.js +++ b/js/src/redux/middleware.js @@ -33,7 +33,7 @@ export default function (api, browserHistory) { const certifications = new CertificationsMiddleware(); const routeMiddleware = routerMiddleware(browserHistory); const chain = new ChainMiddleware(); - const registry = new RegistryMiddleware(); + const registry = new RegistryMiddleware(api); const middleware = [ settings.toMiddleware(), @@ -41,7 +41,7 @@ export default function (api, browserHistory) { errors.toMiddleware(), certifications.toMiddleware(), chain.toMiddleware(), - registry.toMiddleware() + registry ]; return middleware.concat(status, routeMiddleware, thunk); diff --git a/js/src/redux/providers/registry/middleware.js b/js/src/redux/providers/registry/middleware.js index 87d9fbf7025..2c8b45d0e37 100644 --- a/js/src/redux/providers/registry/middleware.js +++ b/js/src/redux/providers/registry/middleware.js @@ -21,90 +21,85 @@ import registryABI from '~/contracts/abi/registry.json'; import { setReverse, startCachingReverses } from './actions'; -export default class RegistryMiddleware { - toMiddleware () { - return (store) => { - const api = Contracts.get()._api; - let contract, confirmedEvents, removedEvents, timeout, interval; - - let addressesToCheck = {}; - - const onLog = (log) => { - switch (log.event) { - case 'ReverseConfirmed': - addressesToCheck[log.params.reverse.value] = true; - - break; - case 'ReverseRemoved': - delete addressesToCheck[log.params.reverse.value]; - - break; - } - }; - - const checkReverses = () => { - Object - .keys(addressesToCheck) - .forEach((address) => { - contract - .instance - .reverse - .call({}, [ address ]) - .then((reverse) => store.dispatch(setReverse(address, reverse))); +export default (api) => (store) => { + let contract, confirmedEvents, removedEvents, timeout, interval; + + let addressesToCheck = {}; + + const onLog = (log) => { + switch (log.event) { + case 'ReverseConfirmed': + addressesToCheck[log.params.reverse.value] = true; + + break; + case 'ReverseRemoved': + delete addressesToCheck[log.params.reverse.value]; + + break; + } + }; + + const checkReverses = () => { + Object + .keys(addressesToCheck) + .forEach((address) => { + contract + .instance + .reverse + .call({}, [ address ]) + .then((reverse) => store.dispatch(setReverse(address, reverse))); + }); + + addressesToCheck = {}; + }; + + return (next) => (action) => { + switch (action.type) { + case 'initAll': + next(action); + store.dispatch(startCachingReverses()); + + break; + case 'startCachingReverses': + const { registry } = Contracts.get(); + + registry.getInstance() + .then((instance) => api.newContract(registryABI, instance.address)) + .then((_contract) => { + contract = _contract; + + confirmedEvents = subscribeToEvent(_contract, 'ReverseConfirmed'); + confirmedEvents.on('log', onLog); + + removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); + removedEvents.on('log', onLog); + + timeout = setTimeout(checkReverses, 5000); + interval = setInterval(checkReverses, 20000); + }) + .catch((err) => { + console.error('Failed to start caching reverses:', err); + throw err; }); - addressesToCheck = {}; - }; - - return (next) => (action) => { - switch (action.type) { - case 'initAll': - next(action); - store.dispatch(startCachingReverses()); - - break; - case 'startCachingReverses': - const { registry } = Contracts.get(); - - registry.getInstance() - .then((instance) => api.newContract(registryABI, instance.address)) - .then((_contract) => { - contract = _contract; - - confirmedEvents = subscribeToEvent(_contract, 'ReverseConfirmed'); - confirmedEvents.on('log', onLog); - - removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); - removedEvents.on('log', onLog); - - timeout = setTimeout(checkReverses, 5000); - interval = setInterval(checkReverses, 20000); - }) - .catch((err) => { - console.error('Failed to start caching reverses:', err); - throw err; - }); - - break; - case 'stopCachingReverses': - if (confirmedEvents) { - confirmedEvents.unsubscribe(); - } - if (removedEvents) { - removedEvents.unsubscribe(); - } - if (interval) { - clearInterval(interval); - } - if (timeout) { - clearTimeout(timeout); - } - - break; - default: - next(action); + break; + case 'stopCachingReverses': + if (confirmedEvents) { + confirmedEvents.unsubscribe(); } - }; - }; - } -} + if (removedEvents) { + removedEvents.unsubscribe(); + } + if (interval) { + clearInterval(interval); + } + if (timeout) { + clearTimeout(timeout); + } + + break; + default: + next(action); + } + }; +}; From cca7627fb805a39a5039f9edbea51114c242fe77 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 17:49:01 +0100 Subject: [PATCH 11/13] reverse caching: PR grumbles --- js/src/redux/providers/registry/reducer.js | 4 ++++ js/src/ui/Form/AddressSelect/addressSelectStore.js | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/js/src/redux/providers/registry/reducer.js b/js/src/redux/providers/registry/reducer.js index 42663897a68..5c267d822bf 100644 --- a/js/src/redux/providers/registry/reducer.js +++ b/js/src/redux/providers/registry/reducer.js @@ -20,6 +20,10 @@ const initialState = { export default (state = initialState, action) => { if (action.type === 'setReverse') { + if (state.reverse[action.address] === action.reverse) { + return state; + } + return { ...state, reverse: { ...state.reverse, [ action.address ]: action.reverse } }; diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 1c93088d774..bdb7c1fb2cf 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -36,6 +36,7 @@ export default class AddressSelectStore { if (query.length === 0 || query === '0x') { return null; } + const startsWithQuery = (s) => new RegExp('^' + query, 'i').test(s); let address; let name = this.reverse[query]; @@ -44,12 +45,8 @@ export default class AddressSelectStore { const addr = Object .keys(this.reverse) .find((addr) => { - if (addr.toLowerCase().slice(0, query.length) === query) { - return true; - } - const name = this.reverse[addr]; - return name.toLowerCase().slice(0, query.length) === query; + return startsWithQuery(addr) || (name && startsWithQuery(name)); }); if (addr) { From 67c298197597827620505e5e254cf6951240e73e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 18:02:59 +0100 Subject: [PATCH 12/13] subscribeToEvent -> subscribeToEvents --- js/src/contracts/verification.js | 4 +- js/src/util/subscribe-to-event.js | 77 ------------------------ js/src/util/subscribe-to-events.js | 97 ++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 79 deletions(-) delete mode 100644 js/src/util/subscribe-to-event.js create mode 100644 js/src/util/subscribe-to-events.js diff --git a/js/src/contracts/verification.js b/js/src/contracts/verification.js index 05b7ea35fab..af2f3b3a768 100644 --- a/js/src/contracts/verification.js +++ b/js/src/contracts/verification.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import subscribeToEvent from '../util/subscribe-to-event'; +import subscribeToEvents from '../util/subscribe-to-events'; export const checkIfVerified = (contract, account) => { return contract.instance.certified.call({}, [account]); @@ -72,7 +72,7 @@ export const awaitPuzzle = (api, contract, account) => { return blockNumber(api) .then((block) => { return new Promise((resolve, reject) => { - const subscription = subscribeToEvent(contract, 'Puzzled', { + const subscription = subscribeToEvents(contract, 'Puzzled', { from: block.toNumber(), filter: (log) => log.params.who.value === account }); diff --git a/js/src/util/subscribe-to-event.js b/js/src/util/subscribe-to-event.js deleted file mode 100644 index 36d1f6d5557..00000000000 --- a/js/src/util/subscribe-to-event.js +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015, 2016 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 EventEmitter from 'eventemitter3'; - -const defaults = { - from: 0, // TODO - to: 'latest', - timeout: null, - filter: () => true -}; - -const subscribeToEvent = (contract, name, opt = {}) => { - opt = Object.assign({}, defaults, opt); - - let subscription = null; - let timeout = null; - - const unsubscribe = () => { - if (subscription) { - contract.unsubscribe(subscription); - subscription = null; - } - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - }; - - const emitter = new EventEmitter(); - emitter.unsubscribe = unsubscribe; - - if (typeof opt.timeout === 'number') { - timeout = setTimeout(() => { - unsubscribe(); - emitter.emit('timeout'); - }, opt.timeout); - } - - const callback = (err, logs) => { - if (err) { - return emitter.emit('error', err); - } - for (let log of logs) { - if (opt.filter(log)) { - emitter.emit('log', log); - } - } - }; - - contract.subscribe(name, { - fromBlock: opt.from, toBlock: opt.to - }, callback) - .then((_subscription) => { - subscription = _subscription; - }) - .catch((err) => { - emitter.emit('error', err); - }); - - return emitter; -}; - -export default subscribeToEvent; diff --git a/js/src/util/subscribe-to-events.js b/js/src/util/subscribe-to-events.js new file mode 100644 index 00000000000..48c99027766 --- /dev/null +++ b/js/src/util/subscribe-to-events.js @@ -0,0 +1,97 @@ +// Copyright 2015, 2016 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 EventEmitter from 'eventemitter3'; + +const defaults = { + from: 0, + to: 'latest', + interval: 5000, + filter: () => true +}; + +const subscribeToEvents = (contract, events, opt = {}) => { + const { api } = contract; + opt = Object.assign({}, defaults, opt); + + let filter = null; + let interval = null; + + const unsubscribe = () => { + if (filter) { + filter + .then((filterId) => { + return api.eth.uninstallFilter(filterId); + }) + .catch((err) => { + emitter.emit('error', err); + }); + filter = null; + } + if (interval) { + clearInterval(interval); + interval = null; + } + }; + + const emitter = new EventEmitter(); + emitter.unsubscribe = unsubscribe; + + const fetcher = (method, filterId) => () => { + api + .eth[method](filterId) + .then((logs) => { + logs = contract.parseEventLogs(logs); + + for (let log of logs) { + if (opt.filter(log)) { + emitter.emit('log', log); + emitter.emit(log.event, log); + } + } + }) + .catch((err) => { + emitter.emit('error', err); + }); + }; + + const signatures = events + .filter((event) => contract.instance[event]) + .map((event) => contract.instance[event].signature); + + filter = api.eth + .newFilter({ + fromBlock: opt.from, toBlock: opt.to, + address: contract.address, + topics: [signatures] + }) + .then((filterId) => { + fetcher('getFilterLogs', filterId)(); // fetch immediately + + const fetchChanges = fetcher('getFilterChanges', filterId); + interval = setInterval(fetchChanges, opt.interval); + + return filterId; + }) + .catch((err) => { + emitter.emit('error', err); + throw err; // reject Promise + }); + + return emitter; +}; + +export default subscribeToEvents; From f12937a21e3bf98c8690c576f0a05c31cc08c0db Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jan 2017 19:03:19 +0100 Subject: [PATCH 13/13] reverse caching: use subscribeToEvents --- js/src/contracts/verification.js | 2 +- js/src/redux/providers/registry/middleware.js | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/js/src/contracts/verification.js b/js/src/contracts/verification.js index af2f3b3a768..3940e0e1814 100644 --- a/js/src/contracts/verification.js +++ b/js/src/contracts/verification.js @@ -72,7 +72,7 @@ export const awaitPuzzle = (api, contract, account) => { return blockNumber(api) .then((block) => { return new Promise((resolve, reject) => { - const subscription = subscribeToEvents(contract, 'Puzzled', { + const subscription = subscribeToEvents(contract, ['Puzzled'], { from: block.toNumber(), filter: (log) => log.params.who.value === account }); diff --git a/js/src/redux/providers/registry/middleware.js b/js/src/redux/providers/registry/middleware.js index 2c8b45d0e37..136a9eb0803 100644 --- a/js/src/redux/providers/registry/middleware.js +++ b/js/src/redux/providers/registry/middleware.js @@ -15,14 +15,14 @@ // along with Parity. If not, see . import Contracts from '~/contracts'; -import subscribeToEvent from '~/util/subscribe-to-event'; +import subscribeToEvents from '~/util/subscribe-to-events'; import registryABI from '~/contracts/abi/registry.json'; import { setReverse, startCachingReverses } from './actions'; export default (api) => (store) => { - let contract, confirmedEvents, removedEvents, timeout, interval; + let contract, subscription, timeout, interval; let addressesToCheck = {}; @@ -68,11 +68,8 @@ export default (api) => (store) => { .then((_contract) => { contract = _contract; - confirmedEvents = subscribeToEvent(_contract, 'ReverseConfirmed'); - confirmedEvents.on('log', onLog); - - removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); - removedEvents.on('log', onLog); + subscription = subscribeToEvents(_contract, ['ReverseConfirmed', 'ReverseRemoved']); + subscription.on('log', onLog); timeout = setTimeout(checkReverses, 5000); interval = setInterval(checkReverses, 20000); @@ -84,11 +81,8 @@ export default (api) => (store) => { break; case 'stopCachingReverses': - if (confirmedEvents) { - confirmedEvents.unsubscribe(); - } - if (removedEvents) { - removedEvents.unsubscribe(); + if (subscription) { + subscription.unsubscribe(); } if (interval) { clearInterval(interval);