2017-01-25 18:51:41 +01:00
|
|
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
2016-11-30 21:39:06 +01:00
|
|
|
// 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/>.
|
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
import { uniq, range, debounce } from 'lodash';
|
2016-12-08 22:05:16 +01:00
|
|
|
|
2017-02-15 13:37:58 +01:00
|
|
|
import { addCertification, removeCertification } from './actions';
|
|
|
|
|
|
|
|
import { getLogger, LOG_KEYS } from '~/config';
|
2016-12-08 22:05:16 +01:00
|
|
|
import Contract from '~/api/contract';
|
2016-12-05 11:47:13 +01:00
|
|
|
import Contracts from '~/contracts';
|
2017-02-15 13:37:58 +01:00
|
|
|
import CertifierABI from '~/contracts/abi/certifier.json';
|
|
|
|
|
|
|
|
const log = getLogger(LOG_KEYS.CertificationsMiddleware);
|
2016-11-30 21:39:06 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
// TODO: move this to a more general place
|
|
|
|
const updatableFilter = (api, onFilter) => {
|
|
|
|
let filter = null;
|
|
|
|
|
|
|
|
const update = (address, topics) => {
|
|
|
|
if (filter) {
|
|
|
|
filter = filter.then((filterId) => {
|
|
|
|
api.eth.uninstallFilter(filterId);
|
|
|
|
});
|
|
|
|
}
|
2016-12-27 16:23:58 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
filter = (filter || Promise.resolve())
|
|
|
|
.then(() => api.eth.newFilter({
|
|
|
|
fromBlock: 0,
|
|
|
|
toBlock: 'latest',
|
|
|
|
address,
|
|
|
|
topics
|
|
|
|
}))
|
|
|
|
.then((filterId) => {
|
|
|
|
onFilter(filterId);
|
|
|
|
return filterId;
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
console.error('Failed to create certifications filter:', err);
|
|
|
|
});
|
2016-12-27 16:23:58 +01:00
|
|
|
|
|
|
|
return filter;
|
2016-12-19 13:17:15 +01:00
|
|
|
};
|
2016-12-27 16:23:58 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
return update;
|
|
|
|
};
|
|
|
|
|
2016-11-30 21:39:06 +01:00
|
|
|
export default class CertificationsMiddleware {
|
|
|
|
toMiddleware () {
|
2016-12-08 22:05:16 +01:00
|
|
|
const api = Contracts.get()._api;
|
|
|
|
const badgeReg = Contracts.get().badgeReg;
|
2016-12-27 16:23:58 +01:00
|
|
|
|
|
|
|
const contract = new Contract(api, CertifierABI);
|
2016-12-08 22:05:16 +01:00
|
|
|
const Confirmed = contract.events.find((e) => e.name === 'Confirmed');
|
2016-12-09 14:06:02 +01:00
|
|
|
const Revoked = contract.events.find((e) => e.name === 'Revoked');
|
2016-12-08 22:05:16 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
return (store) => {
|
2016-12-27 16:23:58 +01:00
|
|
|
let certifiers = [];
|
|
|
|
let addresses = [];
|
|
|
|
let filterChanged = false;
|
|
|
|
let filter = null;
|
|
|
|
let badgeRegFilter = null;
|
|
|
|
let fetchCertifiersPromise = null;
|
|
|
|
|
|
|
|
const updateFilter = updatableFilter(api, (filterId) => {
|
|
|
|
filterChanged = true;
|
|
|
|
filter = filterId;
|
|
|
|
});
|
|
|
|
|
|
|
|
const badgeRegUpdateFilter = updatableFilter(api, (filterId) => {
|
|
|
|
filterChanged = true;
|
|
|
|
badgeRegFilter = filterId;
|
|
|
|
});
|
|
|
|
|
|
|
|
badgeReg
|
|
|
|
.getContract()
|
|
|
|
.then((badgeRegContract) => {
|
2017-02-15 13:42:55 +01:00
|
|
|
return badgeRegUpdateFilter(badgeRegContract.address, [ [
|
2016-12-27 16:23:58 +01:00
|
|
|
badgeRegContract.instance.Registered.signature,
|
|
|
|
badgeRegContract.instance.Unregistered.signature,
|
|
|
|
badgeRegContract.instance.MetaChanged.signature,
|
|
|
|
badgeRegContract.instance.AddressChanged.signature
|
2017-02-15 13:42:55 +01:00
|
|
|
] ]);
|
2016-12-27 16:23:58 +01:00
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
shortFetchChanges();
|
|
|
|
|
|
|
|
api.subscribe('eth_blockNumber', (err) => {
|
|
|
|
if (err) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchChanges();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function onLogs (logs) {
|
2016-12-19 13:17:15 +01:00
|
|
|
logs = contract.parseEventLogs(logs);
|
|
|
|
logs.forEach((log) => {
|
|
|
|
const certifier = certifiers.find((c) => c.address === log.address);
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
if (!certifier) {
|
|
|
|
throw new Error(`Could not find certifier at ${log.address}.`);
|
|
|
|
}
|
|
|
|
const { id, name, title, icon } = certifier;
|
2016-12-08 22:05:16 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
if (log.event === 'Revoked') {
|
|
|
|
store.dispatch(removeCertification(log.params.who.value, id));
|
|
|
|
} else {
|
|
|
|
store.dispatch(addCertification(log.params.who.value, id, name, title, icon));
|
|
|
|
}
|
|
|
|
});
|
2016-12-27 16:23:58 +01:00
|
|
|
}
|
2016-12-15 13:48:24 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
function onBadgeRegLogs (logs) {
|
2017-02-15 13:42:55 +01:00
|
|
|
return badgeReg.getContract()
|
|
|
|
.then((badgeRegContract) => {
|
|
|
|
logs = badgeRegContract.parseEventLogs(logs);
|
|
|
|
const ids = logs.map((log) => log.params.id.value.toNumber());
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2017-02-15 13:42:55 +01:00
|
|
|
return fetchCertifiers(uniq(ids));
|
|
|
|
});
|
2016-12-27 16:23:58 +01:00
|
|
|
}
|
2016-12-19 13:17:15 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
function _fetchChanges () {
|
|
|
|
const method = filterChanged
|
|
|
|
? 'getFilterLogs'
|
|
|
|
: 'getFilterChanges';
|
|
|
|
|
|
|
|
filterChanged = false;
|
2016-12-08 22:05:16 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
api.eth[method](badgeRegFilter)
|
|
|
|
.then(onBadgeRegLogs)
|
|
|
|
.catch((err) => {
|
|
|
|
console.error('Failed to fetch badge reg events:', err);
|
|
|
|
})
|
|
|
|
.then(() => api.eth[method](filter))
|
2016-12-19 13:17:15 +01:00
|
|
|
.then(onLogs)
|
|
|
|
.catch((err) => {
|
|
|
|
console.error('Failed to fetch new certifier events:', err);
|
2016-12-08 13:07:27 +01:00
|
|
|
});
|
2016-12-27 16:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const shortFetchChanges = debounce(_fetchChanges, 0.5 * 1000, { leading: true });
|
|
|
|
const fetchChanges = debounce(shortFetchChanges, 10 * 1000, { leading: true });
|
2016-12-23 16:43:13 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
function fetchConfirmedEvents () {
|
|
|
|
return updateFilter(certifiers.map((c) => c.address), [
|
|
|
|
[ Confirmed.signature, Revoked.signature ],
|
|
|
|
addresses
|
|
|
|
]).then(() => shortFetchChanges());
|
|
|
|
}
|
|
|
|
|
|
|
|
function fetchCertifiers (ids = []) {
|
|
|
|
if (fetchCertifiersPromise) {
|
|
|
|
return fetchCertifiersPromise;
|
2016-12-23 16:43:13 +01:00
|
|
|
}
|
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
let fetchEvents = false;
|
|
|
|
|
|
|
|
const idsPromise = (certifiers.length === 0)
|
|
|
|
? badgeReg.certifierCount().then((count) => {
|
|
|
|
return range(count);
|
|
|
|
})
|
|
|
|
: Promise.resolve(ids);
|
|
|
|
|
|
|
|
fetchCertifiersPromise = idsPromise
|
|
|
|
.then((ids) => {
|
|
|
|
const promises = ids.map((id) => {
|
|
|
|
return badgeReg.fetchCertifier(id)
|
|
|
|
.then((cert) => {
|
|
|
|
if (!certifiers.some((c) => c.id === cert.id)) {
|
|
|
|
certifiers = certifiers.concat(cert);
|
|
|
|
fetchEvents = true;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
if (/does not exist/.test(err.toString())) {
|
2017-02-15 13:37:58 +01:00
|
|
|
return log.info(err.toString());
|
2016-12-27 16:23:58 +01:00
|
|
|
}
|
|
|
|
|
2017-02-15 13:37:58 +01:00
|
|
|
log.warn(`Could not fetch certifier ${id}:`, err);
|
2016-12-27 16:23:58 +01:00
|
|
|
});
|
|
|
|
});
|
2016-11-30 21:39:06 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
return Promise
|
|
|
|
.all(promises)
|
|
|
|
.then(() => {
|
|
|
|
fetchCertifiersPromise = null;
|
2016-12-15 13:48:24 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
if (fetchEvents) {
|
|
|
|
return fetchConfirmedEvents();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return fetchCertifiersPromise;
|
|
|
|
}
|
2016-12-19 13:17:15 +01:00
|
|
|
|
|
|
|
return (next) => (action) => {
|
|
|
|
switch (action.type) {
|
|
|
|
case 'fetchCertifiers':
|
2016-12-27 16:23:58 +01:00
|
|
|
fetchConfirmedEvents();
|
2016-12-19 13:17:15 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
case 'fetchCertifications':
|
|
|
|
const { address } = action;
|
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
if (!addresses.includes(address)) {
|
|
|
|
addresses = addresses.concat(address);
|
2016-12-19 13:17:15 +01:00
|
|
|
fetchConfirmedEvents();
|
|
|
|
}
|
2016-12-15 13:48:24 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
break;
|
|
|
|
case 'setVisibleAccounts':
|
2016-12-27 16:23:58 +01:00
|
|
|
const _addresses = action.addresses || [];
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-12-27 16:23:58 +01:00
|
|
|
addresses = uniq(addresses.concat(_addresses));
|
2016-12-19 13:17:15 +01:00
|
|
|
fetchConfirmedEvents();
|
2017-01-09 11:15:00 +01:00
|
|
|
next(action);
|
2016-12-15 13:48:24 +01:00
|
|
|
|
2016-12-19 13:17:15 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
next(action);
|
|
|
|
}
|
|
|
|
};
|
2016-11-30 21:39:06 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|