From d7012b34f79e8f60607bff584fcabae6aae99d63 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Mon, 4 Mar 2024 20:49:26 +0100 Subject: [PATCH 01/15] adjust nominator status and identity rank --- packages/common/src/chaindata/queries/Era.ts | 18 +- packages/common/src/constants.ts | 3 + packages/common/src/db/queries/Validators.ts | 35 +++- .../src/nominator/NominatorChainInfo.ts | 124 +++++++++++ packages/common/src/nominator/NominatorTx.ts | 18 +- packages/common/src/nominator/nominator.ts | 195 ++++++------------ packages/common/src/scorekeeper/Nominating.ts | 51 +++-- .../common/src/scorekeeper/NumNominations.ts | 3 +- packages/common/src/scorekeeper/Round.ts | 21 +- .../jobs/specificJobs/EraPointsJob.ts | 41 +++- .../jobs/specificJobs/EraStatsJob.ts | 3 + .../jobs/specificJobs/ExecutionJob.ts | 13 +- packages/common/src/utils/Validators.ts | 54 +---- .../gateway/src/controllers/Validators.ts | 13 ++ packages/gateway/src/routes/index.ts | 2 + packages/gateway/src/services/Validator.ts | 14 ++ packages/scorekeeper-status-ui/src/App.tsx | 32 +++ 17 files changed, 398 insertions(+), 242 deletions(-) create mode 100644 packages/common/src/nominator/NominatorChainInfo.ts diff --git a/packages/common/src/chaindata/queries/Era.ts b/packages/common/src/chaindata/queries/Era.ts index c4572db48..b7940e980 100644 --- a/packages/common/src/chaindata/queries/Era.ts +++ b/packages/common/src/chaindata/queries/Era.ts @@ -203,16 +203,18 @@ export const findEraBlockHash = async ( if (!blockHash) { return ["", "Block hash is null"]; } - const testEra = - await chaindata?.api?.query.staking.activeEra.at(blockHash); - if (testEra && testEra.isNone) { - logger.info(`Test era is none`); + const apiAt = await chaindata?.api?.at(blockHash); + if (!apiAt) { + return ["", "API at block hash is null"]; + } + + const testEra = await apiAt.query.staking.activeEra(); + if (testEra && testEra.isEmpty) { + logger.info(`Test era is empty: ${JSON.stringify(testEra)}`); return ["", "Test era is none"]; } - const testIndex = - testEra && testEra?.unwrap && testEra?.unwrap().index?.toNumber() - ? testEra?.unwrap().index.toNumber() - : 0; + const testEraJSON = testEra.toJSON() as { index: string; start: string }; + const testIndex = testEraJSON.index ? Number(testEraJSON.index) : 0; if (era == testIndex) { return [blockHash.toString(), null]; } diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index 060e83827..361da31fe 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -45,6 +45,9 @@ export const CHAINDATA_SLEEP = 300; export const API_PROVIDER_TIMEOUT = 10000; +// The number of eras a nominator should wait until making a next nomination +export const NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD = 1; + /// List of Kusama endpoints we can switch between. export const KusamaEndpoints = [ "wss://kusama-rpc-tn.dwellir.com", diff --git a/packages/common/src/db/queries/Validators.ts b/packages/common/src/db/queries/Validators.ts index c09522bc0..45b4d5090 100644 --- a/packages/common/src/db/queries/Validators.ts +++ b/packages/common/src/db/queries/Validators.ts @@ -5,7 +5,7 @@ import { ValidatorSet, ValidatorSetModel, } from "../models"; -import { allCandidates } from "./Candidate"; +import { allCandidates, getIdentityAddresses } from "./Candidate"; import { NextKeys } from "../../chaindata/queries/ValidatorPref"; export const setValidatorSet = async ( @@ -170,3 +170,36 @@ export const hasBeefyDummy = async (address: string): Promise => { const validator = await getValidator(address); return validator?.keys?.beefy?.slice(0, 10) == "0x62656566"; }; + +// TODO: add tests +// Returns the number of eras a validator stash has been active +export const getValidatorActiveEras = async ( + stash: string, +): Promise => { + let count = 0; + const validatorSets = await getAllValidatorSets(); + for (const era of validatorSets) { + if (era.validators.includes(stash)) { + count++; + } + } + return count; +}; + +// TODO: add tests +// return the number of eras +export const getIdentityValidatorActiveEras = async ( + address: string, +): Promise => { + const identityAddresses = await getIdentityAddresses(address); + let count = 0; + const validatorSets = await getAllValidatorSets(); + for (const era of validatorSets) { + if ( + era.validators.some((validator) => identityAddresses.includes(validator)) + ) { + count++; + } + } + return count; +}; diff --git a/packages/common/src/nominator/NominatorChainInfo.ts b/packages/common/src/nominator/NominatorChainInfo.ts new file mode 100644 index 000000000..ca7053bb6 --- /dev/null +++ b/packages/common/src/nominator/NominatorChainInfo.ts @@ -0,0 +1,124 @@ +import Nominator from "./nominator"; +import { queries } from "../index"; +import { NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD } from "../constants"; + +// Query on-chain info for a nominator +export const getNominatorChainInfo = async (nominator: Nominator) => { + const stash = await nominator.stash(); + const isBonded = await nominator.chaindata.isBonded(stash); + const [bonded, err] = await nominator.chaindata.getDenomBondedAmount(stash); + const currentBlock = await nominator.chaindata.getLatestBlock(); + + const currentEra = (await nominator.chaindata.getCurrentEra()) || 0; + const lastNominationEra = + (await nominator.chaindata.getNominatorLastNominationEra(stash)) || 0; + nominator.lastEraNomination = lastNominationEra; + const currentTargets = + (await nominator.chaindata.getNominatorCurrentTargets(stash)) || []; + const currentNamedTargets = await Promise.all( + currentTargets.map(async (target) => { + const kyc = await queries.isKYC(target); + let name = await queries.getIdentityName(target); + if (!name) { + name = (await nominator.chaindata.getFormattedIdentity(target))?.name; + } + + const scoreResult = await queries.getLatestValidatorScore(target); + const score = scoreResult && scoreResult.total ? scoreResult.total : 0; + + return { + stash: target, + name: name, + kyc: kyc, + score: score, + }; + }), + ); + + const proxyAnnouncements = await queries.getAccountDelayedTx( + nominator.bondedAddress, + ); + + const namedProxyTargets = await Promise.all( + (proxyAnnouncements || []).map(async (announcement) => { + const namedTargets = await Promise.all( + announcement.targets.map(async (target) => { + const kyc = await queries.isKYC(target); + let name = await queries.getIdentityName(target); + + if (!name) { + const formattedIdentity = + await nominator.chaindata.getFormattedIdentity(target); + name = formattedIdentity?.name; + } + + const scoreResult = await queries.getLatestValidatorScore(target); + const score = + scoreResult && scoreResult.total ? scoreResult.total : 0; + + return { + stash: target, + name: name, + kyc: kyc, + score: score, + }; + }), + ); + const executionMsTime = + (nominator.proxyDelay + currentBlock - announcement.number) * 6 * 1000; + return { + ...announcement, + targets: namedTargets, + executionTime: executionMsTime, + }; + }), + ); + + const shouldNominate = + bonded > 50 && + isBonded && + currentEra - lastNominationEra >= + NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD && + proxyAnnouncements.length == 0; + + let status; + + if (namedProxyTargets.length > 0) { + `Pending Proxy Execution at #${namedProxyTargets[0].number}`; + } else if (shouldNominate) { + status = "Awaiting New Nomination"; + } else if (lastNominationEra == 0) { + status = "Not Nominating Anyone"; + } else { + status = `Nominating, last nomination era: ${lastNominationEra} current era: ${currentEra}`; + } + + let state; + if (shouldNominate) { + state = "Ready to Nominate"; + } else if (namedProxyTargets.length > 0) { + state = "Awaiting Proxy Execution"; + } else if (lastNominationEra == 0) { + state = "Not Nominating"; + } else if (namedProxyTargets.length == 0 && lastNominationEra > 0) { + status = "Nominated"; + } + + const stale = + isBonded && + currentEra - lastNominationEra > 8 && + proxyAnnouncements.length == 0 && + bonded > 50; + + return { + state: state, + status: status, + isBonded: isBonded, + bondedAmount: Number(bonded), + lastNominationEra: lastNominationEra, + currentTargets: currentNamedTargets, + proxyAnnouncements: namedProxyTargets, + shouldNominate: shouldNominate, + stale: stale, + }; +}; diff --git a/packages/common/src/nominator/NominatorTx.ts b/packages/common/src/nominator/NominatorTx.ts index e45361f67..188cf24ea 100644 --- a/packages/common/src/nominator/NominatorTx.ts +++ b/packages/common/src/nominator/NominatorTx.ts @@ -20,7 +20,8 @@ export const sendProxyDelayTx = async ( `{Nominator::nominate::proxy} starting tx for ${nominator.address} with proxy delay ${nominator.proxyDelay} blocks`, nominatorLabel, ); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ + state: "Nominating", status: `[noninate] starting proxy delay tx`, updated: Date.now(), stale: false, @@ -34,7 +35,7 @@ export const sendProxyDelayTx = async ( `{Nominator::nominate} there was an error getting the current block`, nominatorLabel, ); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ status: `[noninate] err: no current block`, updated: Date.now(), stale: false, @@ -55,7 +56,8 @@ export const sendProxyDelayTx = async ( callHash, }; await queries.addDelayedTx(delayedTx); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ + state: "Nominating", status: `[noninate] tx: ${JSON.stringify(delayedTx)}`, updated: Date.now(), stale: false, @@ -64,7 +66,8 @@ export const sendProxyDelayTx = async ( const allProxyTxs = await queries.getAllDelayedTxs(); const didSend = await nominator.signAndSendTx(tx); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ + state: "Awaiting Proxy Execution", status: `Announced Proxy Tx: ${didSend}`, nextTargets: targets, updated: Date.now(), @@ -79,7 +82,7 @@ export const sendProxyDelayTx = async ( nominatorLabel, ); logger.error(JSON.stringify(e), nominatorLabel); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ status: `Proxy Delay Error: ${JSON.stringify(e)}`, updated: Date.now(), }); @@ -153,7 +156,8 @@ export const sendProxyTx = async ( ); const currentEra = await chaindata.getCurrentEra(); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ + state: "Awaiting Proxy Execution", status: "Submitted Proxy Tx", currentTargets: namedTargets, updated: Date.now(), @@ -172,7 +176,7 @@ export const sendProxyTx = async ( nominatorLabel, ); logger.error(JSON.stringify(e), nominatorLabel); - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ status: `Proxy Error: ${JSON.stringify(e)}`, updated: Date.now(), }); diff --git a/packages/common/src/nominator/nominator.ts b/packages/common/src/nominator/nominator.ts index e8fe7d9fb..4731eb3ce 100644 --- a/packages/common/src/nominator/nominator.ts +++ b/packages/common/src/nominator/nominator.ts @@ -7,10 +7,18 @@ import { ChainData, Constants, queries, Types } from "../index"; import logger from "../logger"; import EventEmitter from "eventemitter3"; import { sendProxyDelayTx, sendProxyTx } from "./NominatorTx"; +import { getNominatorChainInfo } from "./NominatorChainInfo"; export const nominatorLabel = { label: "Nominator" }; export interface NominatorStatus { + state?: + | "Nominated" + | "Ready to Nominate" + | "Nominating" + | "Awaiting Proxy Execution" + | "Not Nominating" + | "Stale"; status?: string; isBonded?: boolean; bondedAddress?: string; @@ -57,11 +65,6 @@ export default class Nominator extends EventEmitter { // The amount of blocks for a time delay proxy private _proxyDelay: number; - private _canNominate: { canNominate: boolean; reason: string } = { - canNominate: false, - reason: "", - }; - public lastEraNomination: number; public _shouldNominate = false; @@ -99,15 +102,6 @@ export default class Nominator extends EventEmitter { this._proxyDelay = cfg.proxyDelay == 0 ? cfg.proxyDelay : Constants.TIME_DELAY_BLOCKS; - logger.info( - `{nominator::proxyDelay} config proxy delay: ${cfg.proxyDelay}`, - nominatorLabel, - ); - logger.info( - `{nominator::proxy} nominator proxy delay: ${this._proxyDelay}`, - nominatorLabel, - ); - const keyring = new Keyring({ type: "sr25519", }); @@ -120,9 +114,9 @@ export default class Nominator extends EventEmitter { : this.signer.address; logger.info( - `(Nominator::constructor) Nominator signer spawned: ${this.address} | ${ + `(Nominator::constructor) Nominator spawned: ${this.address} | ${ this._isProxy ? "Proxy" : "Controller" - }`, + } ${this._proxyDelay ? `| Delay: ${this._proxyDelay}` : ""} bonded address: ${this._bondedAddress}`, nominatorLabel, ); } @@ -131,9 +125,27 @@ export default class Nominator extends EventEmitter { return this._status; }; - public updateNominatorStatus = (newStatus: NominatorStatus) => { - this._status = { ...this._status, ...newStatus }; - }; + public async updateNominatorStatus(newStatus: NominatorStatus) { + // Always update on-chain data for status + const nominatorInfo = await getNominatorChainInfo(this); + const { + isBonded, + bondedAmount, + lastNominationEra, + proxyAnnouncements, + stale, + } = nominatorInfo; + + this._status = { + ...this._status, + ...newStatus, + isBonded, + bondedAmount, + lastNominationEra, + proxyTxs: proxyAnnouncements, + stale, + }; + } public async shouldNominate(): Promise { const stash = await this.stash(); @@ -152,122 +164,38 @@ export default class Nominator extends EventEmitter { public async init(): Promise { try { - const stash = await this.stash(); - const isBonded = await this.chaindata.isBonded(stash); - const [bonded, err] = await this.chaindata.getDenomBondedAmount(stash); - const currentBlock = await this.chaindata.getLatestBlock(); - - const currentEra = (await this.chaindata.getCurrentEra()) || 0; - const lastNominationEra = - (await this.chaindata.getNominatorLastNominationEra(stash)) || 0; - this.lastEraNomination = lastNominationEra; - const currentTargets = - (await this.chaindata.getNominatorCurrentTargets(stash)) || []; - const currentNamedTargets = await Promise.all( - currentTargets.map(async (target) => { - const kyc = await queries.isKYC(target); - let name = await queries.getIdentityName(target); - if (!name) { - name = (await this.chaindata.getFormattedIdentity(target))?.name; - } - - const scoreResult = await queries.getLatestValidatorScore(target); - const score = - scoreResult && scoreResult.total ? scoreResult.total : 0; - - return { - stash: target, - name: name, - kyc: kyc, - score: score, - }; - }), - ); - - const proxyAnnouncements = await queries.getAccountDelayedTx( - this.bondedAddress, - ); - - const namedProxyTargets = await Promise.all( - (proxyAnnouncements || []).map(async (announcement) => { - const namedTargets = await Promise.all( - announcement.targets.map(async (target) => { - const kyc = await queries.isKYC(target); - let name = await queries.getIdentityName(target); - - if (!name) { - const formattedIdentity = - await this.chaindata.getFormattedIdentity(target); - name = formattedIdentity?.name; - } - - const scoreResult = await queries.getLatestValidatorScore(target); - const score = - scoreResult && scoreResult.total ? scoreResult.total : 0; - - return { - stash: target, - name: name, - kyc: kyc, - score: score, - }; - }), - ); - const executionMsTime = - (this._proxyDelay + currentBlock - announcement.number) * 6 * 1000; - return { - ...announcement, - targets: namedTargets, - executionTime: executionMsTime, - }; - }), - ); - - this._shouldNominate = - bonded > 50 && - isBonded && - currentEra - lastNominationEra >= 1 && - proxyAnnouncements.length == 0; - - const rewardDestination = await this.payee(); - - let nominationStatus; - if (proxyAnnouncements.length > 0) { - nominationStatus = "Announced Proxy Tx"; - } else if (this._shouldNominate) { - nominationStatus = "Initialized"; - } else { - nominationStatus = "Existing Recent Nomination"; - } - - const stale = - isBonded && - currentEra - lastNominationEra > 8 && - proxyAnnouncements.length == 0 && - bonded > 50; + const nominatorInfo = await getNominatorChainInfo(this); + const { + state, + status: nominatorStatus, + isBonded, + bondedAmount, + currentTargets, + lastNominationEra, + proxyAnnouncements, + stale, + } = nominatorInfo; const status: NominatorStatus = { - status: nominationStatus, + state: state, + status: nominatorStatus, bondedAddress: this.bondedAddress, stashAddress: await this.stash(), - bondedAmount: Number(bonded), + bondedAmount: bondedAmount, isBonded: isBonded, isProxy: this._isProxy, proxyDelay: this._proxyDelay, proxyAddress: this.signer.address, - rewardDestination: rewardDestination, + rewardDestination: await this.payee(), lastNominationEra: lastNominationEra, - currentTargets: currentNamedTargets, - proxyTxs: namedProxyTargets, + currentTargets: currentTargets, + proxyTxs: proxyAnnouncements, stale: stale, dryRun: this._dryRun, updated: Date.now(), shouldNominate: this._shouldNominate, }; - this.updateNominatorStatus(status); - this._canNominate = { - canNominate: isBonded, - reason: isBonded ? "Bonded" : "Not bonded", - }; + await this.updateNominatorStatus(status); + return status; } catch (e) { logger.error(`Error getting status for ${this.bondedAddress}: ${e}`); @@ -362,7 +290,8 @@ export default class Nominator extends EventEmitter { try { if (this._dryRun) { logger.info(`DRY RUN ENABLED, SKIPPING TX`, nominatorLabel); - this.updateNominatorStatus({ + await this.updateNominatorStatus({ + state: "Nominating", status: `[signAndSend] DRY RUN TX`, updated: Date.now(), stale: false, @@ -371,7 +300,8 @@ export default class Nominator extends EventEmitter { } else { logger.info(`Sending tx: ${tx.method.toString()}`, nominatorLabel); await tx.signAndSend(this.signer); - this.updateNominatorStatus({ + await this.updateNominatorStatus({ + state: "Nominated", status: `[signAndSend] signed and sent tx`, updated: Date.now(), stale: false, @@ -382,7 +312,7 @@ export default class Nominator extends EventEmitter { } catch (e) { logger.error(`Error sending tx: `, nominatorLabel); logger.error(JSON.stringify(e), nominatorLabel); - this.updateNominatorStatus({ + await this.updateNominatorStatus({ status: `[signAndSend] Error signing and sending tx: ${JSON.stringify(e)}`, updated: Date.now(), stale: false, @@ -404,12 +334,13 @@ export default class Nominator extends EventEmitter { const currentEra = await this.chaindata.getCurrentEra(); const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `[nominate] start`, updated: Date.now(), stale: false, }; - this.updateNominatorStatus(nominatorStatus); + await this.updateNominatorStatus(nominatorStatus); let isBonded; try { const stash = await this.stash(); @@ -421,7 +352,8 @@ export default class Nominator extends EventEmitter { } logger.info(`nominator is bonded: ${isBonded}`, nominatorLabel); - this.updateNominatorStatus({ + await this.updateNominatorStatus({ + state: "Nominating", status: `[nominate] bonded; ${isBonded}`, updated: Date.now(), stale: false, @@ -438,7 +370,8 @@ export default class Nominator extends EventEmitter { ); // Start an announcement for a delayed proxy tx if (this._isProxy && this._proxyDelay > 0) { - this.updateNominatorStatus({ + await this.updateNominatorStatus({ + state: "Nominating", status: `[nominate] proxy ${this._isProxy}; delay ${this._proxyDelay}`, updated: Date.now(), stale: false, @@ -542,13 +475,14 @@ export default class Nominator extends EventEmitter { }), ); const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `Dry Run: Nominated ${targets.length} validators`, updated: Date.now(), stale: false, currentTargets: namedTargets, lastNominationEra: currentEra, }; - this.updateNominatorStatus(nominatorStatus); + await this.updateNominatorStatus(nominatorStatus); // `dryRun` return as blockhash is checked elsewhere to finish the hook of writing db entries return [false, "dryRun"]; } @@ -703,13 +637,14 @@ export default class Nominator extends EventEmitter { }), ); const nominatorStatus: NominatorStatus = { + state: "Nominated", status: `Nominated ${targets.length} validators: ${didSend} ${finalizedBlockHash}`, updated: Date.now(), stale: false, currentTargets: namedTargets, lastNominationEra: currentEra, }; - this.updateNominatorStatus(nominatorStatus); + await this.updateNominatorStatus(nominatorStatus); return [didSend, finalizedBlockHash || null]; // Change to return undefined } catch (e) { logger.error(`Error sending tx: ${JSON.stringify(e)}`, nominatorLabel); diff --git a/packages/common/src/scorekeeper/Nominating.ts b/packages/common/src/scorekeeper/Nominating.ts index eab542e52..de5a1f130 100644 --- a/packages/common/src/scorekeeper/Nominating.ts +++ b/packages/common/src/scorekeeper/Nominating.ts @@ -31,20 +31,6 @@ export const doNominations = async ( return null; } - for (const nom of nominatorGroups) { - const nominatorStatus: NominatorStatus = { - status: `Doing Nominations.....`, - updated: Date.now(), - stale: false, - }; - nom.updateNominatorStatus(nominatorStatus); - } - - const allTargets = candidates.map((c) => { - return { stash: c.stash }; - }); - let counter = 0; - const currentEra = await chaindata.getCurrentEra(); if (!currentEra) { logger.error( @@ -54,22 +40,46 @@ export const doNominations = async ( return null; } + // Update nominator status to start nominations + for (const nom of nominatorGroups) { + const shouldNominate = await nom.shouldNominate(); + if (!shouldNominate) { + const nominatorStatus: NominatorStatus = { + state: "Nominating", + status: `Doing Nominations.....`, + updated: Date.now(), + stale: false, + }; + await nom.updateNominatorStatus(nominatorStatus); + } + } + + // The list of all valid Validators to nominate + const allTargets = candidates.map((c) => { + return { stash: c.stash }; + }); + + // A counter to keep track of the number of nominations + let counter = 0; + for (const nominator of nominatorGroups) { - const nomStash = await nominator.stash(); - const nominatorLastNominated = - await chaindata.getNominatorLastNominationEra(nomStash); - if (nominatorLastNominated + 4 > currentEra) { + const stash = await nominator.stash(); + const shouldNominate = await nominator.shouldNominate(); + if (!shouldNominate) { logger.info( - `Nominator ${nomStash} has already nominated this era: ${nominatorLastNominated}`, + `Nominator ${stash} has already nominated in era: ${nominator.lastEraNomination} (current era: ${currentEra}) - Skipping`, ); continue; } + const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `Nominating...`, updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); + // The number of nominations to do per nominator account // This is either hard coded, or set to "auto", meaning it will find a dynamic amount of validators // to nominate based on the lowest staked validator in the validator set @@ -78,7 +88,6 @@ export const doNominations = async ( if (!api || !denom) return null; const autoNom = await autoNumNominations(api, nominator); const { nominationNum } = autoNom; - const stash = await nominator.stash(); logger.info( `Nominator ${stash} ${nominator.isProxy ? "Proxy" : "Non-Proxy"} with delay ${nominator.proxyDelay} blocks nominate ${nominationNum} validators`, diff --git a/packages/common/src/scorekeeper/NumNominations.ts b/packages/common/src/scorekeeper/NumNominations.ts index 25df39f0c..4833b350b 100644 --- a/packages/common/src/scorekeeper/NumNominations.ts +++ b/packages/common/src/scorekeeper/NumNominations.ts @@ -36,11 +36,12 @@ export const autoNumNominations = async ( nominator: Nominator, ): Promise => { const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `Calculating how many validators to nominate...`, updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); const denom = (await nominator?.chaindata?.getDenom()) || 0; diff --git a/packages/common/src/scorekeeper/Round.ts b/packages/common/src/scorekeeper/Round.ts index 1191caf5a..e70d8e521 100644 --- a/packages/common/src/scorekeeper/Round.ts +++ b/packages/common/src/scorekeeper/Round.ts @@ -60,11 +60,12 @@ export const startRound = async ( for (const nom of filteredNominators) { const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `Round Started`, updated: Date.now(), stale: false, }; - nom.updateNominatorStatus(nominatorStatus); + await nom.updateNominatorStatus(nominatorStatus); } const proxyTxs = await queries.getAllDelayedTxs(); @@ -99,21 +100,23 @@ export const startRound = async ( ); for (const nom of filteredNominators) { const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `[${index}/${allCandidates.length}] ${candidate.name} ${isValid ? "✅ " : "❌"}`, updated: Date.now(), stale: false, }; - nom.updateNominatorStatus(nominatorStatus); + await nom.updateNominatorStatus(nominatorStatus); } } for (const nom of filteredNominators) { const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `Scoring Candidates...`, updated: Date.now(), stale: false, }; - nom.updateNominatorStatus(nominatorStatus); + await nom.updateNominatorStatus(nominatorStatus); } // Score all candidates @@ -159,27 +162,19 @@ export const startRound = async ( await queries.setLastNominatedEraIndex(newEra); for (const nom of filteredNominators) { const nominatorStatus: NominatorStatus = { + state: "Nominated", status: `Nominated!`, updated: Date.now(), stale: false, lastNominationEra: newEra, }; - nom.updateNominatorStatus(nominatorStatus); + await nom.updateNominatorStatus(nominatorStatus); } } else { logger.info( `${numValidatorsNominated} nominated this round, lastNominatedEra not set...`, scorekeeperLabel, ); - for (const nom of filteredNominators) { - const nominatorStatus: NominatorStatus = { - status: `${numValidatorsNominated} nominated, era not set!`, - updated: Date.now(), - stale: false, - lastNominationEra: newEra, - }; - nom.updateNominatorStatus(nominatorStatus); - } } nominating = false; diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts index abafe06bc..e6e0c4dc3 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts @@ -16,18 +16,39 @@ export class EraPointsJob extends Job { export const individualEraPointsJob = async ( chaindata: ChainData, eraIndex: number, -) => { - const erapoints = await queries.getTotalEraPoints(eraIndex); +): Promise => { + try { + const erapoints = await queries.getTotalEraPoints(eraIndex); - // If Era Points for the era exist, and are what the total should be, skip - if (!!erapoints && erapoints.totalEraPoints >= 0 && erapoints.median) { - return; - } else { - const data = await chaindata.getTotalEraPoints(eraIndex); - if (data) { - const { era, total, validators } = data; - await queries.setTotalEraPoints(era, total, validators); + // If Era Points for the era exist, and are what the total should be, skip + if (!!erapoints && erapoints.totalEraPoints >= 0 && erapoints.median) { + return; + } else { + const data = await chaindata.getTotalEraPoints(eraIndex); + if ( + data && + data.era && + data.total && + data.validators && + data.validators.length > 0 + ) { + const { era, total, validators } = data; + await queries.setTotalEraPoints(era, total, validators); + } else { + logger.error( + `Error getting total era points for era: ${JSON.stringify(data)} is null`, + erapointsLabel, + ); + return false; + } } + return true; + } catch (e) { + logger.error( + `Error running individual era points job: ${JSON.stringify(e)}`, + erapointsLabel, + ); + return false; } }; export const eraPointsJob = async ( diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/EraStatsJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/EraStatsJob.ts index 7ab67ff49..d26aac695 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/EraStatsJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/EraStatsJob.ts @@ -17,6 +17,9 @@ export const eraStatsJob = async ( ): Promise => { try { const { chaindata } = metadata; + + await setValidatorRanks(); + const currentSession = await chaindata.getSession(); const currentEra = await chaindata.getCurrentEra(); const validators = await chaindata.currentValidators(); diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts index b8cd0d429..b00b9cee7 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts @@ -93,7 +93,7 @@ export const executionJob = async ( updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); if (bot) { await bot.sendMessage( `@room ${target} has invalid commission: ${commission}`, @@ -117,7 +117,7 @@ export const executionJob = async ( updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); await nominator.cancelTx(announcement); } } @@ -128,7 +128,8 @@ export const executionJob = async ( (validCommission && dataNum + Number(timeDelayBlocks) <= latestBlock); if (shouldExecute) { - nominator.updateNominatorStatus({ + await nominator.updateNominatorStatus({ + state: "Nominating", status: `Starting Delayed Execution for ${callHash} - ${dataNum}`, updated: Date.now(), stale: false, @@ -139,11 +140,12 @@ export const executionJob = async ( ); const nominatorStatus: NominatorStatus = { + state: "Nominating", status: `${isDryRun ? "DRY RUN: " : ""} Executing Valid Proxy Tx: ${data.callHash}`, updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); // time to execute @@ -168,11 +170,12 @@ export const executionJob = async ( // `dryRun` is a special value for the returned block hash that is used to test the execution job without actually sending the transaction if (didSend || finalizedBlockHash == "dryRun") { const nominatorStatus: NominatorStatus = { + state: "Nominated", status: `Executed Proxy Tx: ${finalizedBlockHash == "dryRun" ? "" : didSend} ${finalizedBlockHash}`, updated: Date.now(), stale: false, }; - nominator.updateNominatorStatus(nominatorStatus); + await nominator.updateNominatorStatus(nominatorStatus); nominator.lastEraNomination = era; // Create a Nomination Object diff --git a/packages/common/src/utils/Validators.ts b/packages/common/src/utils/Validators.ts index 7811d16a6..d8e87f0b5 100644 --- a/packages/common/src/utils/Validators.ts +++ b/packages/common/src/utils/Validators.ts @@ -1,51 +1,13 @@ -import { - allCandidates, - getAllValidatorSets, - getIdentityAddresses, - setRank, -} from "../db"; +import { allCandidates, setRank } from "../db"; +import { queries } from "../index"; +// Sets all validators ranks export const setValidatorRanks = async () => { - const rankMap: Map = new Map(); const candidates = await allCandidates(); - const candidateAddresses = candidates.map((candidate) => candidate.stash); - const validatorSets = await getAllValidatorSets(); - if (validatorSets) { - for (const era of validatorSets) { - const validators = era.validators || []; - - for (const validator of validators) { - const candidateExists = candidateAddresses.includes(validator); - if (candidateExists) { - if (rankMap.has(validator)) { - const val = rankMap.get(validator) || 0; - rankMap.set(validator, val + 1); - } else { - rankMap.set(validator, 1); - } - } - } - } - await processRankMap(rankMap); + for (const candidate of candidates) { + const identityRank = await queries.getIdentityValidatorActiveEras( + candidate.stash, + ); + await setRank(candidate.stash, identityRank); } }; - -export const processRankMap = async ( - rankMap: Map, -): Promise => { - return await Promise.all( - Array.from(rankMap.entries()).map(async ([validator, rank]) => { - const rankList: { address: string; rank: number }[] = []; - const identityAddresses: string[] = await getIdentityAddresses(validator); - for (const identityAddress of identityAddresses) { - rankList.push({ address: identityAddress, rank: rank }); - } - const sortedRankList = rankList.sort((a, b) => b.rank - a.rank); - const maxRank: number = Math.max( - ...sortedRankList.map((entry) => entry.rank), - ); - - await setRank(validator, maxRank); - }), - ); -}; diff --git a/packages/gateway/src/controllers/Validators.ts b/packages/gateway/src/controllers/Validators.ts index caeae6a3a..7323a09c9 100644 --- a/packages/gateway/src/controllers/Validators.ts +++ b/packages/gateway/src/controllers/Validators.ts @@ -55,4 +55,17 @@ export default class ValidatorController { } response(context, 200, await ValidatorService.getBeefyDummy()); } + + public static async getValidatorsNumActiveEras(context: any): Promise { + requestEmitter.emit("requestReceived"); + if (await context.cashed()) { + logger.info(`getValidatorsNumActiveEras is cached`, gatewayLabel); + return; + } + response( + context, + 200, + await ValidatorService.getValidatorsNumActiveEras(context.params.address), + ); + } } diff --git a/packages/gateway/src/routes/index.ts b/packages/gateway/src/routes/index.ts index 01189c3ae..724316f63 100644 --- a/packages/gateway/src/routes/index.ts +++ b/packages/gateway/src/routes/index.ts @@ -52,6 +52,7 @@ const API = { CurrentValidatorSet: "/validators/current", Validators: "/validators", Validator: "/validator/:address", + ValidatorsNumActiveEras: "/validators/activeeras/:address", ValidatorsBeefyStats: "/validators/beefy", ValidatorsBeefyDummy: "/validators/beefy/dummy", RewardsValidator: "/rewards/validator/:address", @@ -97,6 +98,7 @@ router.get(API.ScoreMetadata, Score.getLatestScoreMetadata); router.get(API.SessionScoreMetadata, Score.getSessionScoreMetadata); router.get(API.CurrentValidatorSet, Validator.getLatestValidatorSet); +router.get(API.ValidatorsNumActiveEras, Validator.getValidatorsNumActiveEras); router.get( API.LocationsCurrentValidatorSet, diff --git a/packages/gateway/src/services/Validator.ts b/packages/gateway/src/services/Validator.ts index 8e0f08692..ebf8bd029 100644 --- a/packages/gateway/src/services/Validator.ts +++ b/packages/gateway/src/services/Validator.ts @@ -24,3 +24,17 @@ export const getBeefyDummy = async (): Promise => { const validators = await queries.getValidatorsBeefyDummy(); return validators; }; + +export const getValidatorsNumActiveEras = async ( + stash: string, +): Promise => { + const eras = await queries.getValidatorActiveEras(stash); + return eras; +}; + +export const getIdentityValidatorNumActiveEras = async ( + stash: string, +): Promise => { + const eras = await queries.getIdentityValidatorActiveEras(stash); + return eras; +}; diff --git a/packages/scorekeeper-status-ui/src/App.tsx b/packages/scorekeeper-status-ui/src/App.tsx index 543105b29..d10293d79 100644 --- a/packages/scorekeeper-status-ui/src/App.tsx +++ b/packages/scorekeeper-status-ui/src/App.tsx @@ -4,6 +4,7 @@ import { FiAlertTriangle, FiCalendar, FiCheckCircle, + FiCircle, FiClock, FiDollarSign, FiInfo, @@ -265,6 +266,25 @@ const App = () => { } } + const getStateColor = (state) => { + switch (state) { + case "Nominated": + return "green"; + case "Ready to Nominate": + return "blue"; + case "Nominating": + return "orange"; + case "Awaiting Proxy Execution": + return "purple"; + case "Not Nominating": + return "red"; + case "Stale": + return "grey"; + default: + return "black"; + } + }; + return (
{ )} - {nominator.state && ( -
-

- - {nominator.state} -

-
- )} {nominator.status && ( -
-

- - {nominator.status} -

+
+
+

+

{renderNominatorStateIcon(nominator.state)}
+ {nominator.state &&
} + {nominator.status} +

+
)} {nominator.isBonded !== undefined && ( From 3a6d11f935feec71f31cc18c5a84e7b1f0bee187 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Mon, 4 Mar 2024 21:57:54 +0100 Subject: [PATCH 04/15] ui adjustments --- packages/scorekeeper-status-ui/src/EraStatsBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scorekeeper-status-ui/src/EraStatsBar.tsx b/packages/scorekeeper-status-ui/src/EraStatsBar.tsx index 5627050cd..a8c7f60af 100644 --- a/packages/scorekeeper-status-ui/src/EraStatsBar.tsx +++ b/packages/scorekeeper-status-ui/src/EraStatsBar.tsx @@ -57,7 +57,7 @@ const EraStatsBar = ({ currentEndpoint }) => {
- KYC: {eraStats?.kyc} ($ + KYC: {eraStats?.kyc} ( {((eraStats?.kyc / eraStats?.totalNodes) * 100).toFixed(0)}%)
From 88779fdb3b6b33d36bba89cc562f68a870b5667c Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Mon, 4 Mar 2024 22:17:46 +0100 Subject: [PATCH 05/15] ui adjustments --- packages/scorekeeper-status-ui/src/App.css | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/scorekeeper-status-ui/src/App.css b/packages/scorekeeper-status-ui/src/App.css index 2f2fe0a7f..5f87bce34 100644 --- a/packages/scorekeeper-status-ui/src/App.css +++ b/packages/scorekeeper-status-ui/src/App.css @@ -479,6 +479,7 @@ h1 { } .proxyTransactionItem { + color: #0f0; display: flex; flex-direction: column; align-items: center; From 2b9bebbf0c900fdf3176418532c39bd5ab2a9a8c Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Mon, 4 Mar 2024 22:53:02 +0100 Subject: [PATCH 06/15] remove redundant status update --- packages/common/src/scorekeeper/Nominating.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/common/src/scorekeeper/Nominating.ts b/packages/common/src/scorekeeper/Nominating.ts index de5a1f130..74febe5a4 100644 --- a/packages/common/src/scorekeeper/Nominating.ts +++ b/packages/common/src/scorekeeper/Nominating.ts @@ -40,20 +40,6 @@ export const doNominations = async ( return null; } - // Update nominator status to start nominations - for (const nom of nominatorGroups) { - const shouldNominate = await nom.shouldNominate(); - if (!shouldNominate) { - const nominatorStatus: NominatorStatus = { - state: "Nominating", - status: `Doing Nominations.....`, - updated: Date.now(), - stale: false, - }; - await nom.updateNominatorStatus(nominatorStatus); - } - } - // The list of all valid Validators to nominate const allTargets = candidates.map((c) => { return { stash: c.stash }; From c7ca47af55f2da6cfaf1215707c2e4657ce45c58 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Mon, 4 Mar 2024 22:54:24 +0100 Subject: [PATCH 07/15] update todo --- packages/gateway/src/routes/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gateway/src/routes/index.ts b/packages/gateway/src/routes/index.ts index 724316f63..382dd1f99 100644 --- a/packages/gateway/src/routes/index.ts +++ b/packages/gateway/src/routes/index.ts @@ -52,6 +52,7 @@ const API = { CurrentValidatorSet: "/validators/current", Validators: "/validators", Validator: "/validator/:address", + //TODO: add to swagger ValidatorsNumActiveEras: "/validators/activeeras/:address", ValidatorsBeefyStats: "/validators/beefy", ValidatorsBeefyDummy: "/validators/beefy/dummy", From a8aec1dc00b487425f66bc145dd757c40ba25639 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 02:26:29 +0100 Subject: [PATCH 08/15] update ui check --- packages/scorekeeper-status-ui/src/App.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/scorekeeper-status-ui/src/App.tsx b/packages/scorekeeper-status-ui/src/App.tsx index 1a17672e8..296ca65b9 100644 --- a/packages/scorekeeper-status-ui/src/App.tsx +++ b/packages/scorekeeper-status-ui/src/App.tsx @@ -373,7 +373,7 @@ const App = () => { {renderStatusIcon( job.status, job.progress !== undefined - ? job.progress.toFixed(1) + ? job.progress?.toFixed(1) : undefined, )}
@@ -391,7 +391,7 @@ const App = () => {
@@ -405,7 +405,7 @@ const App = () => { border: `1px solid linear-gradient(to right, rgba(255, 0, 0, 0.1) ${job.progress}%, rgba(255, 255, 0, 0.1) ${job.progress}%, rgba(0, 255, 0, 0.1) ${job.progress}%)`, }} > - {job.progress !== undefined ? job.progress.toFixed(1) : 0}% + {job.progress !== undefined ? job.progress?.toFixed(1) : 0}%

{job.iteration && ( @@ -504,7 +504,7 @@ const App = () => {

Bonded Amount:{" "} {new Intl.NumberFormat().format( - nominator.bondedAmount.toFixed(2), + nominator.bondedAmount?.toFixed(2), )}{" "} {currentEndpoint.includes("kusama") ? "KSM" : "DOT"}

@@ -576,8 +576,8 @@ const App = () => { theme="polkadot" /> {target.name - ? `[${target.score.toFixed(0)}] ${target.name}` - : `[${target.score.toFixed(0)}] ${truncateAddress(target.stash)}`}{" "} + ? `[${target.score?.toFixed(0)}] ${target.name}` + : `[${target.score?.toFixed(0)}] ${truncateAddress(target.stash)}`}{" "} {target.kyc && ( Date: Tue, 5 Mar 2024 02:58:18 +0100 Subject: [PATCH 09/15] update shouldNominate --- packages/common/src/nominator/nominator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/common/src/nominator/nominator.ts b/packages/common/src/nominator/nominator.ts index 4731eb3ce..665a0632a 100644 --- a/packages/common/src/nominator/nominator.ts +++ b/packages/common/src/nominator/nominator.ts @@ -152,12 +152,15 @@ export default class Nominator extends EventEmitter { const isBonded = await this.chaindata.isBonded(stash); const [bonded, err] = await this.chaindata.getDenomBondedAmount(stash); const proxyTxs = await queries.getAccountDelayedTx(this.bondedAddress); + const lastNominationEra = + await this.chaindata.getNominatorLastNominationEra(stash); + this.lastEraNomination = lastNominationEra; const currentEra = (await this.chaindata.getCurrentEra()) || 0; this._shouldNominate = isBonded && bonded > 50 && - currentEra - this.lastEraNomination >= 1 && + currentEra - lastNominationEra >= 1 && proxyTxs.length == 0; return this._shouldNominate; } From 4d2bd3481f93bbda5d3d63cb5f923dd8fe873a29 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 12:48:43 +0100 Subject: [PATCH 10/15] add tests, update swagger endpoints, use enums --- package.json | 4 +- packages/common/src/chaindata/queries/Era.ts | 5 +- packages/common/src/db/models.ts | 6 + packages/common/src/db/queries/Candidate.ts | 2 +- packages/common/src/db/queries/Era.ts | 52 +-- .../common/src/db/queries/ValidatorScore.ts | 2 +- packages/common/src/db/queries/Validators.ts | 5 +- .../src/nominator/NominatorChainInfo.ts | 24 +- packages/common/src/nominator/NominatorTx.ts | 20 +- .../src/nominator/__mocks__/nominator.ts | 4 + packages/common/src/nominator/nominator.ts | 84 ++--- packages/common/src/scorekeeper/Nominating.ts | 5 +- .../common/src/scorekeeper/NumNominations.ts | 5 +- packages/common/src/scorekeeper/Round.ts | 33 +- .../jobs/specificJobs/CancelJob.ts | 2 +- .../jobs/specificJobs/ConstraintsJob.ts | 2 +- .../jobs/specificJobs/EraPointsJob.ts | 2 +- .../jobs/specificJobs/ExecutionJob.ts | 14 +- .../jobs/specificJobs/MainScorekeeperJob.ts | 10 +- .../jobs/specificJobs/StaleNomination.ts | 2 +- .../common/src/scorekeeper/scorekeeper.ts | 5 +- packages/common/src/types.ts | 39 ++ packages/common/src/utils/Validators.ts | 2 +- .../test/db/queries/EraStats.unit.test.ts | 15 +- .../scorekeeper/NumNominations.int.test.ts | 15 +- .../common/test/utils/Validators.unit.test.ts | 101 ++++++ packages/core/package.json | 2 +- packages/gateway/src/routes/index.ts | 17 - packages/gateway/src/swagger.yml | 335 ++++++++++++------ packages/scorekeeper-status-ui/src/App.tsx | 5 +- .../scorekeeper-status-ui/src/EraStatsBar.tsx | 8 +- packages/worker/package.json | 2 +- yarn.lock | 335 +++++++++++------- 33 files changed, 758 insertions(+), 406 deletions(-) create mode 100644 packages/common/test/utils/Validators.unit.test.ts diff --git a/package.json b/package.json index ca5008c39..81e5edade 100644 --- a/package.json +++ b/package.json @@ -79,8 +79,8 @@ "typedoc-plugin-markdown": "^3.17.1" }, "dependencies": { - "@polkadot/api": "^10.11.2", - "@polkadot/rpc-provider": "^10.11.2", + "@polkadot/api": "^10.12.1", + "@polkadot/rpc-provider": "^10.12.1", "@types/ws": "^8.5.10", "axios": "^1.6.7", "chalk": "4.1.2", diff --git a/packages/common/src/chaindata/queries/Era.ts b/packages/common/src/chaindata/queries/Era.ts index b7940e980..fd6f4ef1c 100644 --- a/packages/common/src/chaindata/queries/Era.ts +++ b/packages/common/src/chaindata/queries/Era.ts @@ -208,13 +208,12 @@ export const findEraBlockHash = async ( return ["", "API at block hash is null"]; } - const testEra = await apiAt.query.staking.activeEra(); + const testEra = await apiAt.query.staking.currentEra(); if (testEra && testEra.isEmpty) { logger.info(`Test era is empty: ${JSON.stringify(testEra)}`); return ["", "Test era is none"]; } - const testEraJSON = testEra.toJSON() as { index: string; start: string }; - const testIndex = testEraJSON.index ? Number(testEraJSON.index) : 0; + const testIndex = testEra.unwrap().toNumber(); if (era == testIndex) { return [blockHash.toString(), null]; } diff --git a/packages/common/src/db/models.ts b/packages/common/src/db/models.ts index c1c384e68..2b263a2df 100644 --- a/packages/common/src/db/models.ts +++ b/packages/common/src/db/models.ts @@ -463,6 +463,12 @@ export const CandidateSchema = new Schema({ export const CandidateModel = mongoose.model("Candidate", CandidateSchema); +export interface Era { + lastNominatedEraIndex: string; + nextNomination: number; + when: number; +} + export const EraSchema = new Schema({ // The last era a nomination took place lastNominatedEraIndex: { type: String, default: "0" }, diff --git a/packages/common/src/db/queries/Candidate.ts b/packages/common/src/db/queries/Candidate.ts index d780b0bb8..ca3c930d1 100644 --- a/packages/common/src/db/queries/Candidate.ts +++ b/packages/common/src/db/queries/Candidate.ts @@ -334,7 +334,7 @@ export const getIdentityName = async ( .lean() .select({ name: 1 }); - return identity?.name; + return identity?.name || null; } }; diff --git a/packages/common/src/db/queries/Era.ts b/packages/common/src/db/queries/Era.ts index 9c4a61d91..fe455e94b 100644 --- a/packages/common/src/db/queries/Era.ts +++ b/packages/common/src/db/queries/Era.ts @@ -1,33 +1,39 @@ -import { EraModel } from "../models"; +import { Era, EraModel } from "../models"; +import logger from "../../logger"; export const setLastNominatedEraIndex = async ( index: number, ): Promise => { - const data = await EraModel.findOne({}).lean(); - if (!data) { - const eraIndex = new EraModel({ - lastNominatedEraIndex: index.toString(), - when: Date.now(), - }); - await eraIndex.save(); - return true; - } - - await EraModel.findOneAndUpdate( - { lastNominatedEraIndex: /.*/ }, - { - $set: { + try { + const data = await EraModel.findOne({}).lean(); + if (!data) { + const eraIndex = new EraModel({ lastNominatedEraIndex: index.toString(), when: Date.now(), - nextNomination: Date.now() + 86400000, + }); + await eraIndex.save(); + return true; + } + + await EraModel.findOneAndUpdate( + { lastNominatedEraIndex: /.*/ }, + { + $set: { + lastNominatedEraIndex: index.toString(), + when: Date.now(), + nextNomination: Date.now() + 86400000, + }, }, - }, - ).exec(); - return true; + ).exec(); + return true; + } catch (e) { + logger.error( + `Error setting last nominated era index: ${JSON.stringify(e)}`, + ); + return false; + } }; -export const getLastNominatedEraIndex = async (): Promise => { - return EraModel.findOne({ lastNominatedEraIndex: /[0-9]+/ }) - .lean() - .exec(); +export const getLastNominatedEraIndex = async (): Promise => { + return EraModel.findOne({ lastNominatedEraIndex: /[0-9]+/ }).lean(); }; diff --git a/packages/common/src/db/queries/ValidatorScore.ts b/packages/common/src/db/queries/ValidatorScore.ts index b0cdbeeed..383ae04c0 100644 --- a/packages/common/src/db/queries/ValidatorScore.ts +++ b/packages/common/src/db/queries/ValidatorScore.ts @@ -107,7 +107,7 @@ export const getValidatorScore = async ( export const getLatestValidatorScore = async ( address: string, -): Promise => { +): Promise => { return ValidatorScoreModel.findOne({ address: address }, { _id: 0, __v: 0 }) .sort({ session: -1 }) .limit(1) diff --git a/packages/common/src/db/queries/Validators.ts b/packages/common/src/db/queries/Validators.ts index 45b4d5090..856488963 100644 --- a/packages/common/src/db/queries/Validators.ts +++ b/packages/common/src/db/queries/Validators.ts @@ -44,10 +44,7 @@ export const getLatestValidatorSet = async (): Promise => { }; export const getAllValidatorSets = async (): Promise => { - return ValidatorSetModel.find({}) - .sort({ era: -1 }) - .lean() - .exec(); + return ValidatorSetModel.find({}).sort({ era: -1 }).lean(); }; export const validatorSetExistsForEra = async ( diff --git a/packages/common/src/nominator/NominatorChainInfo.ts b/packages/common/src/nominator/NominatorChainInfo.ts index ca7053bb6..26f1e8e14 100644 --- a/packages/common/src/nominator/NominatorChainInfo.ts +++ b/packages/common/src/nominator/NominatorChainInfo.ts @@ -1,13 +1,14 @@ import Nominator from "./nominator"; import { queries } from "../index"; import { NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD } from "../constants"; +import { NominatorState } from "../types"; // Query on-chain info for a nominator export const getNominatorChainInfo = async (nominator: Nominator) => { const stash = await nominator.stash(); const isBonded = await nominator.chaindata.isBonded(stash); const [bonded, err] = await nominator.chaindata.getDenomBondedAmount(stash); - const currentBlock = await nominator.chaindata.getLatestBlock(); + const currentBlock = (await nominator.chaindata.getLatestBlock()) || 0; const currentEra = (await nominator.chaindata.getCurrentEra()) || 0; const lastNominationEra = @@ -20,7 +21,8 @@ export const getNominatorChainInfo = async (nominator: Nominator) => { const kyc = await queries.isKYC(target); let name = await queries.getIdentityName(target); if (!name) { - name = (await nominator.chaindata.getFormattedIdentity(target))?.name; + name = + (await nominator.chaindata.getFormattedIdentity(target))?.name || ""; } const scoreResult = await queries.getLatestValidatorScore(target); @@ -28,8 +30,8 @@ export const getNominatorChainInfo = async (nominator: Nominator) => { return { stash: target, - name: name, - kyc: kyc, + name: name || "", + kyc: kyc || false, score: score, }; }), @@ -49,7 +51,7 @@ export const getNominatorChainInfo = async (nominator: Nominator) => { if (!name) { const formattedIdentity = await nominator.chaindata.getFormattedIdentity(target); - name = formattedIdentity?.name; + name = formattedIdentity?.name || ""; } const scoreResult = await queries.getLatestValidatorScore(target); @@ -58,8 +60,8 @@ export const getNominatorChainInfo = async (nominator: Nominator) => { return { stash: target, - name: name, - kyc: kyc, + name: name || "", + kyc: kyc || false, score: score, }; }), @@ -95,13 +97,13 @@ export const getNominatorChainInfo = async (nominator: Nominator) => { let state; if (shouldNominate) { - state = "Ready to Nominate"; + state = NominatorState.ReadyToNominate; } else if (namedProxyTargets.length > 0) { - state = "Awaiting Proxy Execution"; + state = NominatorState.AwaitingProxyExecution; } else if (lastNominationEra == 0) { - state = "Not Nominating"; + state = NominatorState.NotNominating; } else if (namedProxyTargets.length == 0 && lastNominationEra > 0) { - status = "Nominated"; + state = NominatorState.Nominated; } const stale = diff --git a/packages/common/src/nominator/NominatorTx.ts b/packages/common/src/nominator/NominatorTx.ts index 188cf24ea..7a1273c5a 100644 --- a/packages/common/src/nominator/NominatorTx.ts +++ b/packages/common/src/nominator/NominatorTx.ts @@ -2,9 +2,10 @@ import logger from "../logger"; import { blake2AsHex } from "@polkadot/util-crypto"; import { DelayedTx } from "../db"; import { ChainData, queries } from "../index"; -import Nominator, { nominatorLabel } from "./nominator"; import { ApiPromise } from "@polkadot/api"; import MatrixBot from "../matrix"; +import Nominator, { nominatorLabel } from "./nominator"; +import { NominatorState } from "../types"; // Sends a Proxy Delay Nominate Tx for a given nominator // TODO: unit tests @@ -21,7 +22,7 @@ export const sendProxyDelayTx = async ( nominatorLabel, ); await nominator.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `[noninate] starting proxy delay tx`, updated: Date.now(), stale: false, @@ -57,7 +58,7 @@ export const sendProxyDelayTx = async ( }; await queries.addDelayedTx(delayedTx); await nominator.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `[noninate] tx: ${JSON.stringify(delayedTx)}`, updated: Date.now(), stale: false, @@ -67,7 +68,7 @@ export const sendProxyDelayTx = async ( const didSend = await nominator.signAndSendTx(tx); await nominator.updateNominatorStatus({ - state: "Awaiting Proxy Execution", + state: NominatorState.AwaitingProxyExecution, status: `Announced Proxy Tx: ${didSend}`, nextTargets: targets, updated: Date.now(), @@ -147,17 +148,20 @@ export const sendProxyTx = async ( targets.map(async (val) => { const name = await queries.getIdentityName(val); const kyc = await queries.isKYC(val); + const scoreResult = await queries.getLatestValidatorScore(val); + const score = scoreResult && scoreResult.total ? scoreResult.total : 0; return { address: val, - name: name, - kyc: kyc, + name: name || "", + kyc: kyc || false, + score: score, }; }), ); - const currentEra = await chaindata.getCurrentEra(); + const currentEra = (await chaindata.getCurrentEra()) || 0; await nominator.updateNominatorStatus({ - state: "Awaiting Proxy Execution", + state: NominatorState.AwaitingProxyExecution, status: "Submitted Proxy Tx", currentTargets: namedTargets, updated: Date.now(), diff --git a/packages/common/src/nominator/__mocks__/nominator.ts b/packages/common/src/nominator/__mocks__/nominator.ts index a226b9e99..ed8e876b4 100644 --- a/packages/common/src/nominator/__mocks__/nominator.ts +++ b/packages/common/src/nominator/__mocks__/nominator.ts @@ -100,6 +100,10 @@ class NominatorMock { async sendStakingTx(tx: any, targets: string[]): Promise { return true; } + + async updateNominatorStatus(): Promise { + return true; + } } export default NominatorMock; diff --git a/packages/common/src/nominator/nominator.ts b/packages/common/src/nominator/nominator.ts index 665a0632a..9197440ac 100644 --- a/packages/common/src/nominator/nominator.ts +++ b/packages/common/src/nominator/nominator.ts @@ -8,45 +8,10 @@ import logger from "../logger"; import EventEmitter from "eventemitter3"; import { sendProxyDelayTx, sendProxyTx } from "./NominatorTx"; import { getNominatorChainInfo } from "./NominatorChainInfo"; +import { NominatorState, NominatorStatus } from "../types"; export const nominatorLabel = { label: "Nominator" }; -export interface NominatorStatus { - state?: - | "Nominated" - | "Ready to Nominate" - | "Nominating" - | "Awaiting Proxy Execution" - | "Not Nominating" - | "Stale"; - status?: string; - isBonded?: boolean; - bondedAddress?: string; - bondedAmount?: number; - stashAddress?: string; - proxyAddress?: string; - isProxy?: boolean; - proxyDelay?: number; - isNominating?: boolean; - lastNominationEra?: number; - lastNominationTime?: number; - currentTargets?: - | string[] - | { - stash?: string; - name?: string; - kyc?: boolean; - score?: string | number; - }[]; - nextTargets?: string[]; - proxyTxs?: any[]; - updated: number; - rewardDestination?: string; - stale?: boolean; - dryRun?: boolean; - shouldNominate?: boolean; -} - export default class Nominator extends EventEmitter { public currentlyNominating: Types.Stash[] = []; @@ -65,7 +30,7 @@ export default class Nominator extends EventEmitter { // The amount of blocks for a time delay proxy private _proxyDelay: number; - public lastEraNomination: number; + public lastEraNomination = 0; public _shouldNominate = false; @@ -153,7 +118,7 @@ export default class Nominator extends EventEmitter { const [bonded, err] = await this.chaindata.getDenomBondedAmount(stash); const proxyTxs = await queries.getAccountDelayedTx(this.bondedAddress); const lastNominationEra = - await this.chaindata.getNominatorLastNominationEra(stash); + (await this.chaindata.getNominatorLastNominationEra(stash)) || 0; this.lastEraNomination = lastNominationEra; const currentEra = (await this.chaindata.getCurrentEra()) || 0; @@ -165,7 +130,7 @@ export default class Nominator extends EventEmitter { return this._shouldNominate; } - public async init(): Promise { + public async init(): Promise { try { const nominatorInfo = await getNominatorChainInfo(this); const { @@ -294,7 +259,7 @@ export default class Nominator extends EventEmitter { if (this._dryRun) { logger.info(`DRY RUN ENABLED, SKIPPING TX`, nominatorLabel); await this.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `[signAndSend] DRY RUN TX`, updated: Date.now(), stale: false, @@ -304,7 +269,7 @@ export default class Nominator extends EventEmitter { logger.info(`Sending tx: ${tx.method.toString()}`, nominatorLabel); await tx.signAndSend(this.signer); await this.updateNominatorStatus({ - state: "Nominated", + state: NominatorState.Nominated, status: `[signAndSend] signed and sent tx`, updated: Date.now(), stale: false, @@ -335,9 +300,9 @@ export default class Nominator extends EventEmitter { return false; } - const currentEra = await this.chaindata.getCurrentEra(); + const currentEra = (await this.chaindata.getCurrentEra()) || 0; const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `[nominate] start`, updated: Date.now(), stale: false, @@ -356,7 +321,7 @@ export default class Nominator extends EventEmitter { logger.info(`nominator is bonded: ${isBonded}`, nominatorLabel); await this.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `[nominate] bonded; ${isBonded}`, updated: Date.now(), stale: false, @@ -374,7 +339,7 @@ export default class Nominator extends EventEmitter { // Start an announcement for a delayed proxy tx if (this._isProxy && this._proxyDelay > 0) { await this.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `[nominate] proxy ${this._isProxy}; delay ${this._proxyDelay}`, updated: Date.now(), stale: false, @@ -455,30 +420,30 @@ export default class Nominator extends EventEmitter { // If Dry Run is enabled in the config, nominations will be stubbed but not executed if (this._dryRun) { logger.info(`DRY RUN ENABLED, SKIPPING TX`, nominatorLabel); - const currentEra = await this.chaindata.getCurrentEra(); + const currentEra = (await this.chaindata.getCurrentEra()) || 0; const namedTargets = await Promise.all( targets.map(async (target) => { - const kyc = await queries.isKYC(target); - let name = await queries.getIdentityName(target); + const kyc = (await queries.isKYC(target)) || false; + let name = (await queries.getIdentityName(target)) || ""; // Fetch name using chaindata.getFormattedIdentity only if the name wasn't found initially if (!name) { const formattedIdentity = await this.chaindata.getFormattedIdentity(target); - name = formattedIdentity?.name; + name = formattedIdentity?.name || ""; } return { stash: target, - name, // shorthand for name: name - kyc, // shorthand for kyc: kyc + name: name || "", + kyc: kyc || false, score: 0, }; }), ); const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `Dry Run: Nominated ${targets.length} validators`, updated: Date.now(), stale: false, @@ -620,27 +585,28 @@ export default class Nominator extends EventEmitter { break; } }); - const currentEra = await this.chaindata.getCurrentEra(); + const currentEra = (await this.chaindata.getCurrentEra()) || 0; const namedTargets = await Promise.all( targets.map(async (target) => { const kyc = await queries.isKYC(target); let name = await queries.getIdentityName(target); if (!name) { - name = (await this.chaindata.getFormattedIdentity(target))?.name; + name = + (await this.chaindata.getFormattedIdentity(target))?.name || ""; } const score = await queries.getLatestValidatorScore(target); return { stash: target, - name: name, - kyc: kyc, - score: score && score[0] && score[0].total ? score[0].total : 0, + name: name || "", + kyc: kyc || false, + score: score && score && score?.total ? score?.total : 0, }; }), ); const nominatorStatus: NominatorStatus = { - state: "Nominated", + state: NominatorState.Nominated, status: `Nominated ${targets.length} validators: ${didSend} ${finalizedBlockHash}`, updated: Date.now(), stale: false, @@ -651,7 +617,7 @@ export default class Nominator extends EventEmitter { return [didSend, finalizedBlockHash || null]; // Change to return undefined } catch (e) { logger.error(`Error sending tx: ${JSON.stringify(e)}`, nominatorLabel); - return [false, e]; + return [false, JSON.stringify(e)]; } }; } diff --git a/packages/common/src/scorekeeper/Nominating.ts b/packages/common/src/scorekeeper/Nominating.ts index 74febe5a4..4e793ef87 100644 --- a/packages/common/src/scorekeeper/Nominating.ts +++ b/packages/common/src/scorekeeper/Nominating.ts @@ -10,7 +10,8 @@ import { ChainData, queries, Util } from "../index"; import ApiHandler from "../ApiHandler/ApiHandler"; import MatrixBot from "../matrix"; import { ConfigSchema } from "../config"; -import Nominator, { NominatorStatus } from "../nominator/nominator"; +import Nominator from "../nominator/nominator"; +import { NominatorState, NominatorStatus } from "../types"; // Takes in a list of valid Candidates, and will nominate them based on the nominator groups export const doNominations = async ( @@ -59,7 +60,7 @@ export const doNominations = async ( } const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `Nominating...`, updated: Date.now(), stale: false, diff --git a/packages/common/src/scorekeeper/NumNominations.ts b/packages/common/src/scorekeeper/NumNominations.ts index 4833b350b..53eaa3d2c 100644 --- a/packages/common/src/scorekeeper/NumNominations.ts +++ b/packages/common/src/scorekeeper/NumNominations.ts @@ -5,9 +5,10 @@ */ import { ApiPromise } from "@polkadot/api"; import { scorekeeperLabel } from "./scorekeeper"; -import Nominator, { NominatorStatus } from "../nominator/nominator"; +import Nominator from "../nominator/nominator"; import { Constants } from "../index"; import logger from "../logger"; +import { NominatorState, NominatorStatus } from "../types"; /** * Automatically determines the number of validators a nominator can nominate based on their available balance @@ -36,7 +37,7 @@ export const autoNumNominations = async ( nominator: Nominator, ): Promise => { const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `Calculating how many validators to nominate...`, updated: Date.now(), stale: false, diff --git a/packages/common/src/scorekeeper/Round.ts b/packages/common/src/scorekeeper/Round.ts index e70d8e521..8507c87e5 100644 --- a/packages/common/src/scorekeeper/Round.ts +++ b/packages/common/src/scorekeeper/Round.ts @@ -11,9 +11,10 @@ import { OTV } from "../constraints/constraints"; import { ConfigSchema } from "../config"; import MatrixBot from "../matrix"; import ApiHandler from "../ApiHandler/ApiHandler"; -import Nominator, { NominatorStatus } from "../nominator/nominator"; +import Nominator from "../nominator/nominator"; import { jobStatusEmitter } from "../Events"; import { JobNames } from "./jobs/JobConfigs"; +import { NominatorState, NominatorStatus } from "../types"; /// Handles the beginning of a new round. // - Gets the current era @@ -34,16 +35,6 @@ export const startRound = async ( if (nominating) return []; nominating = true; - const shouldNominatePromises = nominatorGroups.map(async (nom) => { - return { - nominator: nom, - shouldNominate: await nom.shouldNominate(), - }; - }); - const resolvedNominators = await Promise.all(shouldNominatePromises); - const filteredNominators = resolvedNominators - .filter((nom) => nom.shouldNominate) - .map((nom) => nom.nominator); const now = new Date().getTime(); // The nominations sent now won't be active until the next era. @@ -58,9 +49,9 @@ export const startRound = async ( `New round is starting! Era ${newEra} will begin new nominations.`, ); - for (const nom of filteredNominators) { + for (const nom of nominatorGroups) { const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `Round Started`, updated: Date.now(), stale: false, @@ -98,9 +89,9 @@ export const startRound = async ( `[${index}/${allCandidates.length}] checked ${candidate.name} ${isValid ? "Valid" : "Invalid"} [${index}/${allCandidates.length}]`, scorekeeperLabel, ); - for (const nom of filteredNominators) { + for (const nom of nominatorGroups) { const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `[${index}/${allCandidates.length}] ${candidate.name} ${isValid ? "✅ " : "❌"}`, updated: Date.now(), stale: false, @@ -109,9 +100,9 @@ export const startRound = async ( } } - for (const nom of filteredNominators) { + for (const nom of nominatorGroups) { const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.Nominating, status: `Scoring Candidates...`, updated: Date.now(), stale: false, @@ -129,7 +120,7 @@ export const startRound = async ( const scoredCandidate = { name: candidate.name, stash: candidate.stash, - total: score.total, + total: score?.total || 0, }; return scoredCandidate; }), @@ -146,7 +137,7 @@ export const startRound = async ( // TODO unit test that assets this value const numValidatorsNominated = await doNominations( sortedCandidates, - filteredNominators, + nominatorGroups, chaindata, handler, bot, @@ -160,9 +151,9 @@ export const startRound = async ( scorekeeperLabel, ); await queries.setLastNominatedEraIndex(newEra); - for (const nom of filteredNominators) { + for (const nom of nominatorGroups) { const nominatorStatus: NominatorStatus = { - state: "Nominated", + state: NominatorState.Nominated, status: `Nominated!`, updated: Date.now(), stale: false, diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/CancelJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/CancelJob.ts index b901114a2..69b145873 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/CancelJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/CancelJob.ts @@ -20,7 +20,7 @@ export const cancelJob = async ( const latestBlock = await chaindata.getLatestBlock(); if (!latestBlock) { logger.error(`latest block is null`, cronLabel); - return; + return false; } const threshold = latestBlock - 1.2 * config?.proxy?.timeDelayBlocks; diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/ConstraintsJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/ConstraintsJob.ts index ed81ec78b..520d5c5be 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/ConstraintsJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/ConstraintsJob.ts @@ -162,7 +162,7 @@ export const scoreJob = async ( name: JobNames.Score, progress, updated: Date.now(), - iteration: `[${score.toFixed(1)}] ${candidate.name}`, + iteration: `[${score?.toFixed(1)}] ${candidate.name}`, }); logger.info( diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts index e6e0c4dc3..fffa5305a 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/EraPointsJob.ts @@ -22,7 +22,7 @@ export const individualEraPointsJob = async ( // If Era Points for the era exist, and are what the total should be, skip if (!!erapoints && erapoints.totalEraPoints >= 0 && erapoints.median) { - return; + return false; } else { const data = await chaindata.getTotalEraPoints(eraIndex); if ( diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts index b00b9cee7..c6cb1bd3f 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/ExecutionJob.ts @@ -4,7 +4,7 @@ import { Constants, queries, Util } from "../../../index"; import { cronLabel } from "../cron/StartCronJobs"; import { jobStatusEmitter } from "../../../Events"; import { JobNames } from "../JobConfigs"; -import { NominatorStatus } from "../../../nominator/nominator"; +import { NominatorState, NominatorStatus } from "../../../types"; export class ExecutionJob extends Job { constructor(jobConfig: JobConfig, jobRunnerMetadata: JobRunnerMetadata) { @@ -28,19 +28,19 @@ export const executionJob = async ( const latestBlock = await chaindata.getLatestBlock(); if (!latestBlock) { logger.error(`latest block is null`, cronLabel); - return; + return false; } const api = handler.getApi(); if (!api) { logger.error(`api is null`, cronLabel); - return; + return false; } const era = await chaindata.getCurrentEra(); if (!era) { logger.error(`current era is null`, cronLabel); - return; + return false; } const allDelayed = await queries.getAllDelayedTxs(); @@ -129,7 +129,7 @@ export const executionJob = async ( if (shouldExecute) { await nominator.updateNominatorStatus({ - state: "Nominating", + state: NominatorState.Nominating, status: `Starting Delayed Execution for ${callHash} - ${dataNum}`, updated: Date.now(), stale: false, @@ -140,7 +140,7 @@ export const executionJob = async ( ); const nominatorStatus: NominatorStatus = { - state: "Nominating", + state: NominatorState.NotNominating, status: `${isDryRun ? "DRY RUN: " : ""} Executing Valid Proxy Tx: ${data.callHash}`, updated: Date.now(), stale: false, @@ -170,7 +170,7 @@ export const executionJob = async ( // `dryRun` is a special value for the returned block hash that is used to test the execution job without actually sending the transaction if (didSend || finalizedBlockHash == "dryRun") { const nominatorStatus: NominatorStatus = { - state: "Nominated", + state: NominatorState.Nominated, status: `Executed Proxy Tx: ${finalizedBlockHash == "dryRun" ? "" : didSend} ${finalizedBlockHash}`, updated: Date.now(), stale: false, diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/MainScorekeeperJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/MainScorekeeperJob.ts index fbb19c956..67be1a52d 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/MainScorekeeperJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/MainScorekeeperJob.ts @@ -4,6 +4,7 @@ import { queries } from "../../../index"; import { startRound } from "../../Round"; import { jobStatusEmitter } from "../../../Events"; import { JobNames } from "../JobConfigs"; +import { NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD } from "../../../constants"; export class MainScorekeeperJob extends Job { constructor(jobConfig: JobConfig, jobRunnerMetadata: JobRunnerMetadata) { @@ -41,7 +42,8 @@ export const mainScorekeeperJob = async ( return; } - const { lastNominatedEraIndex } = await queries.getLastNominatedEraIndex(); + const lastNominatedEra = await queries.getLastNominatedEraIndex(); + const lastNominatedEraIndex = lastNominatedEra?.lastNominatedEraIndex || 0; const eraBuffer = config.global.networkPrefix == 0 ? 1 : 4; const isNominationRound = Number(lastNominatedEraIndex) <= activeEra - eraBuffer; @@ -55,8 +57,10 @@ export const mainScorekeeperJob = async ( const stash = await nom.stash(); if (!stash || stash === "0x") return false; const lastNominatedEra = - await chaindata.getNominatorLastNominationEra(stash); - return lastNominatedEra <= activeEra - 1; + (await chaindata.getNominatorLastNominationEra(stash)) || 0; + return ( + activeEra - lastNominatedEra >= NOMINATOR_SHOULD_NOMINATE_ERAS_THRESHOLD + ); }), ); diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/StaleNomination.ts b/packages/common/src/scorekeeper/jobs/specificJobs/StaleNomination.ts index 444ecfb57..85f2651a5 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/StaleNomination.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/StaleNomination.ts @@ -38,7 +38,7 @@ export const staleNominationJob = async ( if (!stash || stash === "0x") continue; const lastNominatedEra = - await chaindata.getNominatorLastNominationEra(stash); + (await chaindata.getNominatorLastNominationEra(stash)) || 0; if (lastNominatedEra < Number(currentEra) - threshold) { const message = `Nominator ${stash} has a stale nomination. Last nomination was in era ${nom.getStatus()?.lastNominationEra} (it is now era ${currentEra})`; diff --git a/packages/common/src/scorekeeper/scorekeeper.ts b/packages/common/src/scorekeeper/scorekeeper.ts index c9e23c1a0..05ca7a017 100644 --- a/packages/common/src/scorekeeper/scorekeeper.ts +++ b/packages/common/src/scorekeeper/scorekeeper.ts @@ -9,7 +9,7 @@ import { Util, } from "../index"; -import Nominator, { NominatorStatus } from "../nominator/nominator"; +import Nominator from "../nominator/nominator"; import { registerAPIHandler, registerEventEmitterHandler, @@ -17,6 +17,7 @@ import { import { Job, JobRunnerMetadata, JobStatus } from "./jobs/JobsClass"; import { JobsRunnerFactory } from "./jobs/JobsRunnerFactory"; import { startRound } from "./Round"; +import { NominatorStatus } from "../types"; // import { monitorJob } from "./jobs"; export type NominatorGroup = Config.NominatorConfig[]; @@ -240,7 +241,7 @@ export default class ScoreKeeper { scorekeeperLabel, ); - const currentEra = await this.chaindata.getCurrentEra(); + const currentEra = (await this.chaindata.getCurrentEra()) || 0; this.currentEra = currentEra; // await setAllIdentities(this.chaindata, scorekeeperLabel); diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 7936559dd..6095a0aee 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -111,3 +111,42 @@ export interface TelemetryWsPayload extends Array { 6: any; // location 7: any; // startupTime } + +export enum NominatorState { + Nominated = "Nominated", + ReadyToNominate = "Ready to Nominate", + Nominating = "Nominating", + AwaitingProxyExecution = "Awaiting Proxy Execution", + NotNominating = "Not Nominating", + Stale = "Stale", +} + +export interface NominatorStatus { + state?: NominatorState; + status?: string; + isBonded?: boolean; + bondedAddress?: string; + bondedAmount?: number; + stashAddress?: string; + proxyAddress?: string; + isProxy?: boolean; + proxyDelay?: number; + isNominating?: boolean; + lastNominationEra?: number; + lastNominationTime?: number; + currentTargets?: + | string[] + | { + stash?: string; + name?: string; + kyc?: boolean; + score?: string | number; + }[]; + nextTargets?: string[]; + proxyTxs?: any[]; + updated: number; + rewardDestination?: string; + stale?: boolean; + dryRun?: boolean; + shouldNominate?: boolean; +} diff --git a/packages/common/src/utils/Validators.ts b/packages/common/src/utils/Validators.ts index d8e87f0b5..b296e6b59 100644 --- a/packages/common/src/utils/Validators.ts +++ b/packages/common/src/utils/Validators.ts @@ -1,4 +1,4 @@ -import { allCandidates, setRank } from "../db"; +import { allCandidates, setRank } from "../db/queries"; import { queries } from "../index"; // Sets all validators ranks diff --git a/packages/common/test/db/queries/EraStats.unit.test.ts b/packages/common/test/db/queries/EraStats.unit.test.ts index 72b8d62cd..c0ba80d47 100644 --- a/packages/common/test/db/queries/EraStats.unit.test.ts +++ b/packages/common/test/db/queries/EraStats.unit.test.ts @@ -10,13 +10,15 @@ describe("setEraStats", () => { const totalNodes = 10; const valid = 8; const active = 6; - await setEraStats(era, totalNodes, valid, active); + const kyc = 3; + await setEraStats(era, totalNodes, valid, active, kyc); const eraStats = await EraStatsModel.findOne({ era }).lean(); expect(eraStats).toBeDefined(); expect(eraStats?.totalNodes).toBe(totalNodes); expect(eraStats?.valid).toBe(valid); expect(eraStats?.active).toBe(active); + expect(eraStats?.kyc).toBe(kyc); }); it("should update existing era stats with different values", async () => { @@ -24,6 +26,7 @@ describe("setEraStats", () => { const initialTotalNodes = 5; const initialValid = 4; const initialActive = 3; + const kyc = 2; await new EraStatsModel({ era, totalNodes: initialTotalNodes, @@ -34,13 +37,14 @@ describe("setEraStats", () => { const updatedTotalNodes = 12; const updatedValid = 10; const updatedActive = 8; - await setEraStats(era, updatedTotalNodes, updatedValid, updatedActive); + await setEraStats(era, updatedTotalNodes, updatedValid, updatedActive, kyc); const eraStats = await EraStatsModel.findOne({ era }).lean(); expect(eraStats).toBeDefined(); expect(eraStats?.totalNodes).toBe(updatedTotalNodes); expect(eraStats?.valid).toBe(updatedValid); expect(eraStats?.active).toBe(updatedActive); + expect(eraStats?.kyc).toBe(kyc); }); it("should not update existing era stats if values are the same", async () => { @@ -48,10 +52,11 @@ describe("setEraStats", () => { const totalNodes = 20; const valid = 15; const active = 10; - await new EraStatsModel({ era, totalNodes, valid, active }).save(); + const kyc = 5; + await new EraStatsModel({ era, totalNodes, valid, active, kyc }).save(); // Call setEraStats with the same values - await setEraStats(era, totalNodes, valid, active); + await setEraStats(era, totalNodes, valid, active, kyc); const eraStats = await EraStatsModel.findOne({ era }).lean(); expect(eraStats).toBeDefined(); @@ -59,6 +64,7 @@ describe("setEraStats", () => { expect(eraStats?.totalNodes).toBe(totalNodes); expect(eraStats?.valid).toBe(valid); expect(eraStats?.active).toBe(active); + expect(eraStats?.kyc).toBe(kyc); }); }); @@ -70,6 +76,7 @@ describe("getLatestEraStats", () => { totalNodes: era * 2, valid: era * 1.5, active: era, + kyc: 1, })); await EraStatsModel.create(eraStatsData); diff --git a/packages/common/test/scorekeeper/NumNominations.int.test.ts b/packages/common/test/scorekeeper/NumNominations.int.test.ts index c43c7ff56..1a58ba2e7 100644 --- a/packages/common/test/scorekeeper/NumNominations.int.test.ts +++ b/packages/common/test/scorekeeper/NumNominations.int.test.ts @@ -2,6 +2,8 @@ import { ApiPromise, WsProvider } from "@polkadot/api"; import { autoNumNominations } from "../../src/scorekeeper/NumNominations"; import { KusamaEndpoints } from "../../src/constants"; +import Nominator from "../../src/nominator/nominator"; +import ApiHandler from "../../src/ApiHandler/ApiHandler"; describe("autoNumNominations Integration Test", () => { it("queries the real API and retrieves data", async () => { @@ -10,11 +12,18 @@ describe("autoNumNominations Integration Test", () => { }); await api.isReadyOrError; - const nom = { - stash: () => "EX9uchmfeSqKTM7cMMg8DkH49XV8i4R7a7rqCn8btpZBHDP", + const handler = new ApiHandler(KusamaEndpoints); + + const nominatorConfig = { + isProxy: false, + seed: "0x" + "00".repeat(32), + proxyDelay: 10800, + proxyFor: "EX9uchmfeSqKTM7cMMg8DkH49XV8i4R7a7rqCn8btpZBHDP", }; - const result = await autoNumNominations(api, nom as any); + const nominator = new Nominator(handler, nominatorConfig, 2, null); + + const result = await autoNumNominations(api, nominator); expect(result).toBeDefined(); diff --git a/packages/common/test/utils/Validators.unit.test.ts b/packages/common/test/utils/Validators.unit.test.ts new file mode 100644 index 000000000..9cb61e41b --- /dev/null +++ b/packages/common/test/utils/Validators.unit.test.ts @@ -0,0 +1,101 @@ +// import { setValidatorRanks } from "../../src/utils"; +import { addKusamaCandidates } from "../testUtils/candidate"; +import { Identity } from "../../src/types"; +import { + addCandidate, + getCandidate, + getIdentityValidatorActiveEras, + getValidatorActiveEras, + setCandidateIdentity, + setValidatorSet, +} from "../../src/db/queries"; +import { initTestServerBeforeAll } from "../testUtils/dbUtils"; +import { ValidatorSetModel } from "../../src/db"; +import { setValidatorRanks } from "../../src/utils/Validators"; + +initTestServerBeforeAll(); +describe("setValidatorRanks", () => { + it("should set ranks for all candidates", async () => { + await addKusamaCandidates(); + + await addCandidate( + 2398, + "Blockshard2", + "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + "", + false, + "matrixhandle", + false, + ); + + const identity1: Identity = { + address: "Cp4U5UYg2FaVUpyEtQgfBm9aqge6EEPkJxEFVZFYy7L1AZF", + name: "Blockshard", + display: "Blockshard", + subIdentities: [ + { + name: "Blockshard2", + address: "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + }, + ], + }; + const identity2: Identity = { + address: "D9rwRxuG8xm8TZf5tgkbPxhhTJK5frCJU9wvp59VRjcMkUf", + name: "🎠 Forbole GP01 🇭🇰", + display: "🎠 Forbole GP01 🇭🇰", + }; + const identity3: Identity = { + address: "J4hAvZoHCviZSoPHoSwLida8cEkZR1NXJcGrcfx9saHTk7D", + name: "Anonstake", + display: "Anonstake", + }; + const identity4: Identity = { + address: "EPhtbjecJ9P2SQEGEJ4XmFS4xN7JioBFarSrbqjhj8BuJ2v", + name: "Indigo One", + display: "Indigo One", + }; + const identity5: Identity = { + address: "HhcrzHdB5iBx823XNfBUukjj4TUGzS9oXS8brwLm4ovMuVp", + name: "KIRA Staking", + display: "KIRA Staking", + }; + await setCandidateIdentity(identity1?.address, identity1); + + await setValidatorSet(1, 1, [identity1?.address, identity2?.address]); + await setValidatorSet(5, 2, [identity1?.address, identity2?.address]); + await setValidatorSet(8, 3, [ + identity1?.address, + identity3?.address, + identity5?.address, + "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + ]); + await setValidatorSet(16, 4, [ + identity1?.address, + identity3?.address, + identity4?.address, + "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + identity5?.address, + ]); + await setValidatorSet(100, 5, [identity1?.address, identity4?.address]); + + const validatorSets = await ValidatorSetModel.find({}).exec(); + expect(validatorSets.length).toBe(5); + + const numEras = await getValidatorActiveEras(identity1?.address); + expect(numEras).toBe(5); + + const subNumEras = await getIdentityValidatorActiveEras( + "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + ); + expect(subNumEras).toBe(5); + + await setValidatorRanks(); + const candidate = await getCandidate(identity1?.address); + expect(candidate?.rank).toBe(5); + + const secondNode = await getCandidate( + "HkJjBkX8fPBFJvTtAbUDKWZSsMrNFuMc7TrT8BqVS5YhZXg", + ); + expect(secondNode?.rank).toBe(5); + }, 10000); +}); diff --git a/packages/core/package.json b/packages/core/package.json index 3e1805b6f..ed17917ff 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -40,7 +40,7 @@ "@1kv/worker": "workspace:^", "@koa/router": "^12.0.1", "@octokit/rest": "^20.0.2", - "@polkadot/api": "^10.11.2", + "@polkadot/api": "^10.12.1", "@polkadot/keyring": "^12.6.2", "@types/cron": "^2.4.0", "@types/jest": "^29.5.12", diff --git a/packages/gateway/src/routes/index.ts b/packages/gateway/src/routes/index.ts index 382dd1f99..9f1a1492a 100644 --- a/packages/gateway/src/routes/index.ts +++ b/packages/gateway/src/routes/index.ts @@ -52,7 +52,6 @@ const API = { CurrentValidatorSet: "/validators/current", Validators: "/validators", Validator: "/validator/:address", - //TODO: add to swagger ValidatorsNumActiveEras: "/validators/activeeras/:address", ValidatorsBeefyStats: "/validators/beefy", ValidatorsBeefyDummy: "/validators/beefy/dummy", @@ -132,20 +131,4 @@ router.get(API.BlockIndex, Block.getBlockIndex); router.get(API.StatsTotalReqeusts, Stats.getTotalRequests); router.get(API.StatsEndpointCounts, Stats.getEndpointCounts); -// router.get("/stats/totalRequests", (ctx) => { -// ctx.body = { totalRequests: requestEmitter.listenerCount("requestReceived") }; -// }); -// -// // Endpoint to retrieve the count of requests per endpoint -// router.get("/stats/endpointCounts", (ctx) => { -// const endpointCounts = {}; -// -// // Iterate over all registered endpoints -// requestEmitter.eventNames().forEach((endpoint) => { -// endpointCounts[endpoint] = requestEmitter.listenerCount(endpoint); -// }); -// -// ctx.body = { endpointCounts }; -// }); - export default router; diff --git a/packages/gateway/src/swagger.yml b/packages/gateway/src/swagger.yml index a7d5d882b..e9993719a 100644 --- a/packages/gateway/src/swagger.yml +++ b/packages/gateway/src/swagger.yml @@ -32,6 +32,8 @@ tags: description: Querying score data paths: + + /candidate/{candidateStash}: get: tags: @@ -58,6 +60,33 @@ paths: description: Successful response with a list of candidates + /candidates/rank: + get: + tags: + - Candidates + summary: Retrieve a list of candidates ordered by rank + responses: + 200: + description: Successful response with a list of candidates + + /candidates/valid: + get: + tags: + - Candidates + summary: Retrieve a list of candidates that are valid + responses: + 200: + description: Successful response with a list of candidates + + /candidates/invalid: + get: + tags: + - Candidates + summary: Retrieve a list of candidates that are invalid + responses: + 200: + description: Successful response with a list of candidates + /rewards/validator/{stash}: get: tags: @@ -148,12 +177,7 @@ paths: responses: 200: description: Rewards. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Reward' + /rewards/nominator/{address}/total: get: @@ -171,13 +195,19 @@ paths: responses: '200': description: "Total rewards for the specified nominator." - content: - application/json: - schema: - $ref: '#/components/schemas/RewardTotal' '404': description: "Validator not found." + + /healthcheck: + get: + summary: "health check of the backend" + description: "Health check of the backend" + responses: + '200': + description: "health check of the backend" + + /nominators: get: summary: "Get All Nominators" @@ -207,6 +237,31 @@ paths: '404': description: "Nominator not found." + /nominator/{address}/{last}: + get: + summary: "Get Nominator by Address" + tags: + - Nominator + description: "Retrieve details of a specific nominator by address." + parameters: + - in: path + name: address + required: true + schema: + type: string + description: "The unique address of the nominator." + - in: path + name: last + required: true + schema: + type: string + description: "Number of eras to retrieve." + responses: + '200': + description: "Successful response with nominator details." + '404': + description: "Nominator not found." + /nominations: get: summary: "Get All Nominations" @@ -385,12 +440,12 @@ paths: '404': description: "Data not found." - /delegations/{address}: + + + /erapoints/{address}: get: - summary: "Delegation Data" - tags: - - Candidates - description: "Retrieve details of a specific validator's delegations'" + summary: "Era Points" + description: "Retrieve details of a specific validator's era points'" parameters: - in: path name: address @@ -404,100 +459,178 @@ paths: '404': description: "Data not found." - /opengov/votes/address/{address}: + /totalerapoints: get: - summary: "Open Gov Vote Data" - tags: - - Candidates - description: "Retrieve details of a specific validator's votes" + summary: "Total Era Points" + description: "Retrieves total era points" + responses: + '200': + description: "Successful response." + + /erastats: + get: + summary: "Era Stats" + description: "Retrieves era stats" + responses: + '200': + description: "Successful response." + + /scoremetadata: + get: + summary: "Score Metadata" + description: "Retrieves score metadata" + responses: + '200': + description: "Successful response." + + /scoremetadata/{session}: + get: + summary: "Score Metadata" + description: "Retrieve details of a score metadata for a session'" parameters: - in: path - name: address + name: session required: true schema: type: string - description: "The unique address of the validator." + description: "Session" + responses: + '200': + description: "Successful response." + '404': + description: "Data not found." + + /release: + get: + summary: "Latest Release" + description: "Retrieves latest tagged release" + responses: + '200': + description: "Successful response." + + /location/currentvalidatorset: + get: + summary: "location stats of the current validator set" + description: "location stats of the current validator set" + responses: + '200': + description: "Successful response." + + /locationstats: + get: + summary: "location stats" + description: "location stats" + responses: + '200': + description: "Successful response." + + /locationstats/valid: + get: + summary: "location stats of valid nodes" + description: "location stats of valid nodes" + responses: + '200': + description: "Successful response." + + /locationstats/{session}: + get: + summary: "location stats for a session" + description: "Retrieve details of location stats for a session'" + parameters: + - in: path + name: session + required: true + schema: + type: string + description: "Session" responses: '200': description: "Successful response." '404': description: "Data not found." -# -# -#components: -# schemas: -# Reward: -# type: object -# properties: -# role: -# type: string -# exposurePercentage: -# type: integer -# totalStake: -# type: integer -# commission: -# type: integer -# era: -# type: integer -# validator: -# type: string -# nominator: -# type: string -# rewardAmount: -# type: string -# rewardDestination: -# type: string -# erasMinStake: -# type: number -# format: float -# validatorStakeEfficiency: -# type: number -# format: float -# blockHash: -# type: string -# blockNumber: -# type: integer -# timestamp: -# type: integer -# date: -# type: string -# format: date -# chf: -# type: number -# format: float -# usd: -# type: number -# format: float -# eur: -# type: number -# format: float -# RewardTotal: -# type: object -# properties: -# validator: -# type: string -# example: "ESNMjpEWcenAbCqGEsHVdbbRai79VQYMmV1fNW1kRZogmzx" -# total: -# type: number -# format: float -# example: 0.879469625343 -# rewardCount: -# type: integer -# example: 1 -# RewardStats: -# type: object -# properties: -# total: -# type: number -# format: float -# example: 0.235934990397 -# rewardCount: -# type: integer -# example: 1 -# avgEfficiency: -# type: number -# format: float -# example: 84.46114961271203 -# avgStake: -# type: integer -# example: 7985 \ No newline at end of file + + /validators/current: + get: + summary: "Current Validator Set" + description: "Current Validator Set" + responses: + '200': + description: "Successful response." + + /validators: + get: + summary: "Validator Set Keys" + description: "Validator Set Keys" + responses: + '200': + description: "Successful response." + + /validators/beefy: + get: + summary: "Validator Set Beefy Keys" + description: "Validator Set Beefy Keys" + responses: + '200': + description: "Successful response." + + /validators/beefy/dummmy: + get: + summary: "Validator Set Beefy Dummy Keys" + description: "Validator Set Beefy Dummy Keys" + responses: + '200': + description: "Successful response." + + /validators/{address}: + get: + summary: "Validator Set Keys" + description: "Validator Set Keys" + parameters: + - in: path + name: address + required: true + schema: + type: string + description: "address" + responses: + '200': + description: "Successful response." + + /validators/activeeras/{address}: + get: + summary: "Number of active eras for an address" + description: "Number of active eras for an address" + parameters: + - in: path + name: address + required: true + schema: + type: string + description: "address" + responses: + '200': + description: "Successful response." + + /blockindex: + get: + summary: "The block index of the backend" + description: "block index of the backend" + responses: + '200': + description: "Successful response." + + /stats/totalReqeusts: + get: + summary: "The total number of api requests of the backend" + description: "The total number of api requests of the backend" + responses: + '200': + description: "Successful response." + + /stats/endpointCount: + get: + summary: "The total number of api requests of the backend for an endpoint" + description: "The total number of api requests of the backend for an endpoint" + responses: + '200': + description: "Successful response." \ No newline at end of file diff --git a/packages/scorekeeper-status-ui/src/App.tsx b/packages/scorekeeper-status-ui/src/App.tsx index 296ca65b9..8f2e1c56a 100644 --- a/packages/scorekeeper-status-ui/src/App.tsx +++ b/packages/scorekeeper-status-ui/src/App.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useState } from "react"; +import axios from "axios"; import { FiActivity, FiAlertTriangle, @@ -15,15 +16,13 @@ import { FiUserCheck, FiXCircle, } from "react-icons/fi"; - import { BeatLoader } from "react-spinners"; import { motion } from "framer-motion"; import "./App.css"; -import axios from "axios"; // Ensure the path to your CSS file is correct -import { debounce } from "lodash"; import HealthCheckBar from "./HealthCheckBar"; import { Identicon } from "@polkadot/react-identicon"; import EraStatsBar from "./EraStatsBar"; +import { debounce } from "lodash"; interface Job { name: string; diff --git a/packages/scorekeeper-status-ui/src/EraStatsBar.tsx b/packages/scorekeeper-status-ui/src/EraStatsBar.tsx index a8c7f60af..ff2f37f6e 100644 --- a/packages/scorekeeper-status-ui/src/EraStatsBar.tsx +++ b/packages/scorekeeper-status-ui/src/EraStatsBar.tsx @@ -23,10 +23,7 @@ const EraStatsBar = ({ currentEndpoint }) => { try { const eraStatsEndpoint = new URL("/erastats", currentEndpoint).href; const { data } = await axios.get(eraStatsEndpoint); - // console.log("Era Stats Data:", JSON.stringify(data)); setEraStats(data[0]); - console.log(`era stats`); - console.log(JSON.stringify(eraStats)); } catch (error) { console.error("Error fetching era stats data:", error); } @@ -36,6 +33,11 @@ const EraStatsBar = ({ currentEndpoint }) => { return () => clearInterval(interval); }, [currentEndpoint]); + useEffect(() => { + console.log(`era stats`); + console.log(JSON.stringify(eraStats)); + }, [eraStats]); + return (
diff --git a/packages/worker/package.json b/packages/worker/package.json index 2a48bbe66..d3ee1b416 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@1kv/common": "workspace:^", - "@polkadot/api": "^10.11.2", + "@polkadot/api": "^10.12.1", "bullmq": "^5.1.5", "typescript": "^5.3.3" }, diff --git a/yarn.lock b/yarn.lock index 0fdea3918..8205f2b5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,8 +9,8 @@ __metadata: version: 0.0.0-use.local resolution: "1k-validators-be@workspace:." dependencies: - "@polkadot/api": ^10.11.2 - "@polkadot/rpc-provider": ^10.11.2 + "@polkadot/api": ^10.12.1 + "@polkadot/rpc-provider": ^10.12.1 "@types/coingecko-api": ^1.0.13 "@types/eslint": ^8.44.7 "@types/jest": ^29.5.12 @@ -77,7 +77,7 @@ __metadata: "@ava/typescript": ^4.1.0 "@koa/router": ^12.0.1 "@octokit/rest": ^20.0.2 - "@polkadot/api": ^10.11.2 + "@polkadot/api": ^10.12.1 "@polkadot/keyring": ^12.6.2 "@types/cron": ^2.4.0 "@types/jest": ^29.5.12 @@ -196,7 +196,7 @@ __metadata: dependencies: "@1kv/common": "workspace:^" "@ava/typescript": ^4.1.0 - "@polkadot/api": ^10.11.2 + "@polkadot/api": ^10.12.1 "@typescript-eslint/eslint-plugin": ^5.59.9 "@typescript-eslint/parser": ^5.59.9 ava: ^6.1.1 @@ -1606,7 +1606,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.3": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b @@ -1808,74 +1808,138 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-augment@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/api-augment@npm:10.11.2" +"@polkadot-api/client@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/client@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" dependencies: - "@polkadot/api-base": 10.11.2 - "@polkadot/rpc-augment": 10.11.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-augment": 10.11.2 - "@polkadot/types-codec": 10.11.2 + "@polkadot-api/metadata-builders": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/substrate-bindings": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/substrate-client": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/utils": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + peerDependencies: + rxjs: ">=7.8.0" + checksum: 572a5538013131321722924a8ec4c4a3f18dfe76fc4897d77a65b2aa7e3162c5d11adab30111fd8489ab6952b7bd875f544e863b8cc0e54948233c6d736335e5 + languageName: node + linkType: hard + +"@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + checksum: d30789f6171fce9887139b88e832d121489fbe1694aa6919fb1c4a37e6ac39ba1f8e19caa76c0007fd815820f1c1b1de6e12169c6b638c580eb74eb0137b4b34 + languageName: node + linkType: hard + +"@polkadot-api/json-rpc-provider@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/json-rpc-provider@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + checksum: fb028b1d16e2c39e5130f90e340691afcd1c45f08f39f56e3a46aa5a87f2bdfd2f71ad81f348d7fe78aceae483c50be4eb55e572f5fdf273beab5ac7b253a59c + languageName: node + linkType: hard + +"@polkadot-api/metadata-builders@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/metadata-builders@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + dependencies: + "@polkadot-api/substrate-bindings": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/utils": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + checksum: 64705f32b9f7d43af759db28161e244f1ea45d5a4214a2a7592b1a4b3922378f1bfe50ad659defe93f23a63da306e50df9602a757656200cf1e50387b1a11842 + languageName: node + linkType: hard + +"@polkadot-api/substrate-bindings@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/substrate-bindings@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + dependencies: + "@noble/hashes": ^1.3.1 + "@polkadot-api/utils": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@scure/base": ^1.1.1 + scale-ts: ^1.4.3 + checksum: c17caa73feaee67edff6e106453f4ef54fe2e14fc921e1fa5e1bd4e8e3f4af8e01802ce3959df072f5997d843c9ad4916b0b52103fdf58c7bf006911be326585 + languageName: node + linkType: hard + +"@polkadot-api/substrate-client@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/substrate-client@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + checksum: 655272b98490e084b948e2c798e0f7d4ba7d90d33b9f24b4d01dd2a5ef6e7e31bf523dd6de602354a3911e897c55bdc32dc24f3b504ce22f27a8e40063296bb4 + languageName: node + linkType: hard + +"@polkadot-api/utils@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0": + version: 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + resolution: "@polkadot-api/utils@npm:0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0" + checksum: 48ff709170ee7abad50f784f1123532b6b39725ab709da59fc9ddd859568753456b46c9c42d5aa56cad3d9fa0af27e46ed0650697df840f0ef2c023bd3a512f1 + languageName: node + linkType: hard + +"@polkadot/api-augment@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/api-augment@npm:10.12.1" + dependencies: + "@polkadot/api-base": 10.12.1 + "@polkadot/rpc-augment": 10.12.1 + "@polkadot/types": 10.12.1 + "@polkadot/types-augment": 10.12.1 + "@polkadot/types-codec": 10.12.1 "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 04a59f84abdc242ba0111afc9a41cc84ab562c3fb653db545f9d3ceb081afac2bcee392f709d4742079e1d3d1297fd135eac8727565c237349bdb7aa389ba04b + checksum: 430f4eba309cc7d4d3508d1bc5d949d6312cb96739a93a257f490ff0261d20a4b61a894afcbb13661f4a5e40060f5080a3c710c619b98a0e39e249cdb668072e languageName: node linkType: hard -"@polkadot/api-base@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/api-base@npm:10.11.2" +"@polkadot/api-base@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/api-base@npm:10.12.1" dependencies: - "@polkadot/rpc-core": 10.11.2 - "@polkadot/types": 10.11.2 + "@polkadot/rpc-core": 10.12.1 + "@polkadot/types": 10.12.1 "@polkadot/util": ^12.6.2 rxjs: ^7.8.1 tslib: ^2.6.2 - checksum: 88f3aa2c61ebf0ac75451236767d3e873b1823a4e3bef96433e765899f9f030cdf5d059da0d1ec3e97effc6287974e48b8f60966f019ed8f62e19b35170a9721 + checksum: 9eb9ccdfae93e5bd333bb669dde00e2ed1242142d689f49ae56b501fbfcd5981018eec31e3d7539bc345ebaf856d0c57cc971a2e62f6f1f914ffe68b9eee4544 languageName: node linkType: hard -"@polkadot/api-derive@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/api-derive@npm:10.11.2" +"@polkadot/api-derive@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/api-derive@npm:10.12.1" dependencies: - "@polkadot/api": 10.11.2 - "@polkadot/api-augment": 10.11.2 - "@polkadot/api-base": 10.11.2 - "@polkadot/rpc-core": 10.11.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-codec": 10.11.2 + "@polkadot/api": 10.12.1 + "@polkadot/api-augment": 10.12.1 + "@polkadot/api-base": 10.12.1 + "@polkadot/rpc-core": 10.12.1 + "@polkadot/types": 10.12.1 + "@polkadot/types-codec": 10.12.1 "@polkadot/util": ^12.6.2 "@polkadot/util-crypto": ^12.6.2 rxjs: ^7.8.1 tslib: ^2.6.2 - checksum: e89bcae6ac730fce72bb75f9b491400d561b0c61eb6f390d01e7c7060dd8fe24f893d7278f12e57200e485632f4a19f61af9b3dd5354bdfc07281bc9d2e85cb2 + checksum: fdd725f8b28b4f213e2b61ab6e99cddff26add60cd9a605ebf59b210c9f1a8b79879c169d13e9a0ff712cec9fd64281a235c704c08a3877b33b349a7b13c00c4 languageName: node linkType: hard -"@polkadot/api@npm:10.11.2, @polkadot/api@npm:^10.11.2": - version: 10.11.2 - resolution: "@polkadot/api@npm:10.11.2" +"@polkadot/api@npm:10.12.1, @polkadot/api@npm:^10.12.1": + version: 10.12.1 + resolution: "@polkadot/api@npm:10.12.1" dependencies: - "@polkadot/api-augment": 10.11.2 - "@polkadot/api-base": 10.11.2 - "@polkadot/api-derive": 10.11.2 + "@polkadot/api-augment": 10.12.1 + "@polkadot/api-base": 10.12.1 + "@polkadot/api-derive": 10.12.1 "@polkadot/keyring": ^12.6.2 - "@polkadot/rpc-augment": 10.11.2 - "@polkadot/rpc-core": 10.11.2 - "@polkadot/rpc-provider": 10.11.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-augment": 10.11.2 - "@polkadot/types-codec": 10.11.2 - "@polkadot/types-create": 10.11.2 - "@polkadot/types-known": 10.11.2 + "@polkadot/rpc-augment": 10.12.1 + "@polkadot/rpc-core": 10.12.1 + "@polkadot/rpc-provider": 10.12.1 + "@polkadot/types": 10.12.1 + "@polkadot/types-augment": 10.12.1 + "@polkadot/types-codec": 10.12.1 + "@polkadot/types-create": 10.12.1 + "@polkadot/types-known": 10.12.1 "@polkadot/util": ^12.6.2 "@polkadot/util-crypto": ^12.6.2 eventemitter3: ^5.0.1 rxjs: ^7.8.1 tslib: ^2.6.2 - checksum: 4bb1c3ab28e7a96ea2c7a10f192ed679aff3c49e713164e69f163df7503686cb9fea7a20ab59b2dd6b85ed51e9c84df3efb346902dfd91b2f2e39aec0bdb351f + checksum: 25ef0b7ae5fbc740a9a0e4fd2536475ec473edfe29f94a97b5ea776c7d02b6a95a937564e7a171a459d678784818053a7444dff8302fafc594241f8db29ab440 languageName: node linkType: hard @@ -1929,128 +1993,128 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-augment@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/rpc-augment@npm:10.11.2" +"@polkadot/rpc-augment@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/rpc-augment@npm:10.12.1" dependencies: - "@polkadot/rpc-core": 10.11.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-codec": 10.11.2 + "@polkadot/rpc-core": 10.12.1 + "@polkadot/types": 10.12.1 + "@polkadot/types-codec": 10.12.1 "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 06daf8630fd2e73bb5d6692300e6e9d0b4ed9b4b0814174c5103e73ad59c79036657543ddf34a4da273e16e8dde6482dcc7c422850b3f21fb54a665565881d48 + checksum: 845ea139f6526de67de1afb7e0f4542e29b63af3efdc98934676031eb174b84e49a17a212f06441c400cd234453414f78fff2b8b0c0c5cd54d2364cf2fbb6010 languageName: node linkType: hard -"@polkadot/rpc-core@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/rpc-core@npm:10.11.2" +"@polkadot/rpc-core@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/rpc-core@npm:10.12.1" dependencies: - "@polkadot/rpc-augment": 10.11.2 - "@polkadot/rpc-provider": 10.11.2 - "@polkadot/types": 10.11.2 + "@polkadot/rpc-augment": 10.12.1 + "@polkadot/rpc-provider": 10.12.1 + "@polkadot/types": 10.12.1 "@polkadot/util": ^12.6.2 rxjs: ^7.8.1 tslib: ^2.6.2 - checksum: f1511fbebee55ea993d1cb113c2a163efd7fd4ae03829c91c8400ed1ba6fbd7d6e1aeebcbf9f9997b0c386fdeb7db62eab25ac3ce7ed0a7cea9691ff28488973 + checksum: d9f12597110e237fbb0f8f1c4c86cc8fcc598eddfd4ace0de00928fae446f3ddd8208183b462984738c149048cd51c9556c76d12568b27b77cc6ede4177db700 languageName: node linkType: hard -"@polkadot/rpc-provider@npm:10.11.2, @polkadot/rpc-provider@npm:^10.11.2": - version: 10.11.2 - resolution: "@polkadot/rpc-provider@npm:10.11.2" +"@polkadot/rpc-provider@npm:10.12.1, @polkadot/rpc-provider@npm:^10.12.1": + version: 10.12.1 + resolution: "@polkadot/rpc-provider@npm:10.12.1" dependencies: "@polkadot/keyring": ^12.6.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-support": 10.11.2 + "@polkadot/types": 10.12.1 + "@polkadot/types-support": 10.12.1 "@polkadot/util": ^12.6.2 "@polkadot/util-crypto": ^12.6.2 "@polkadot/x-fetch": ^12.6.2 "@polkadot/x-global": ^12.6.2 "@polkadot/x-ws": ^12.6.2 - "@substrate/connect": 0.7.35 + "@substrate/connect": 0.8.7 eventemitter3: ^5.0.1 mock-socket: ^9.3.1 - nock: ^13.4.0 + nock: ^13.5.0 tslib: ^2.6.2 dependenciesMeta: "@substrate/connect": optional: true - checksum: a266ea4bf1fa4b2f3aaad005a04906bc2ffb6440b96b21789c41aad975311fe9a73f46e57a88c327130d69af06ad39d4e8826d70c5635966e55e2644a77ffa9a + checksum: 29246fa825be3f4f2eb34d63a1ef17ecda1b2e6423cfcb6c119ecbfdf15c18230d085fc111ff4739c07748cc3923a4829ec5deefaebb54b8b6a1252b80d5da39 languageName: node linkType: hard -"@polkadot/types-augment@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types-augment@npm:10.11.2" +"@polkadot/types-augment@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types-augment@npm:10.12.1" dependencies: - "@polkadot/types": 10.11.2 - "@polkadot/types-codec": 10.11.2 + "@polkadot/types": 10.12.1 + "@polkadot/types-codec": 10.12.1 "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 74d6efbac15dd20ce1f4c47adccdc1bf9cabed577c53ae964cf14ab943346c0fb4d0965e93b1276055f42f136d51de4bf10115ef79942bf12f8b309344e57fa4 + checksum: eec5f93331174a587dd804fe4f00b5409b7f775af3d8b8db3289d804d165a1a840311d44e54beaacf0b1ae323ac51905725aad32f9e48ff68a84a3afec03e726 languageName: node linkType: hard -"@polkadot/types-codec@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types-codec@npm:10.11.2" +"@polkadot/types-codec@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types-codec@npm:10.12.1" dependencies: "@polkadot/util": ^12.6.2 "@polkadot/x-bigint": ^12.6.2 tslib: ^2.6.2 - checksum: 154b3c89120b8095c82b55e89e0c0233b9f4e628017dfbbf0a9ca8383e6085bf93a3f91cd21264acdc9e996180e28d734c5f669a70524a3aa1399d0f5b8202d6 + checksum: 800f4172d0504d860e97f6894a8f43bde5ae20abe9f7b0a3a0e85cb8cfbeb80ce77fb381bda49d1defe30f4756939649b607dc4c3660e40c335e1b21651f28e5 languageName: node linkType: hard -"@polkadot/types-create@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types-create@npm:10.11.2" +"@polkadot/types-create@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types-create@npm:10.12.1" dependencies: - "@polkadot/types-codec": 10.11.2 + "@polkadot/types-codec": 10.12.1 "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 4fb124dbd3f42baaf99de68e6b26c4ce3d314ec92ba2c8a88eee72b9c3404bbe84d234e66048edbe26afb155338cdc81bf62a65c9e4dbdfe9c32e694de6f97b2 + checksum: ff051f75c5e692f89de4e7686311a66e7b889a398efefcd373a67a18796c06a80266eed55726a97850b1e4e430ca13004895394a8e368acbe5e030e55c20269f languageName: node linkType: hard -"@polkadot/types-known@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types-known@npm:10.11.2" +"@polkadot/types-known@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types-known@npm:10.12.1" dependencies: "@polkadot/networks": ^12.6.2 - "@polkadot/types": 10.11.2 - "@polkadot/types-codec": 10.11.2 - "@polkadot/types-create": 10.11.2 + "@polkadot/types": 10.12.1 + "@polkadot/types-codec": 10.12.1 + "@polkadot/types-create": 10.12.1 "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 198c92576fa85060bf571a56dcca3041ff749950645109d357ef77a619472096136a2fec2eaf3174742db9d20a181486c29bc23c4224c9968fc60a17b161408c + checksum: dbd4b4ac107908606d24fc5ec1a5be9cc1a972d1345b45908b310dae7fe09fc9f4f49c03edbec4694ed8b2de4c771f98ec228aa155a968b13e82513f33db9eeb languageName: node linkType: hard -"@polkadot/types-support@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types-support@npm:10.11.2" +"@polkadot/types-support@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types-support@npm:10.12.1" dependencies: "@polkadot/util": ^12.6.2 tslib: ^2.6.2 - checksum: 6283a5581876c7fba850add6612cd9285a876f506298581392a7db1f0f92357857da86aac031eef515e8d0ca725ec89259bef676b662616242cc5f5efc2527bc + checksum: a6eb886fb0e1c56f7c22e96a9255d6dac53fd4e30248c96950867a58854b542943b9143a69052b3b73a596ec2b2665b8857ed98267acf0e55fbeebb1670ca125 languageName: node linkType: hard -"@polkadot/types@npm:10.11.2": - version: 10.11.2 - resolution: "@polkadot/types@npm:10.11.2" +"@polkadot/types@npm:10.12.1": + version: 10.12.1 + resolution: "@polkadot/types@npm:10.12.1" dependencies: "@polkadot/keyring": ^12.6.2 - "@polkadot/types-augment": 10.11.2 - "@polkadot/types-codec": 10.11.2 - "@polkadot/types-create": 10.11.2 + "@polkadot/types-augment": 10.12.1 + "@polkadot/types-codec": 10.12.1 + "@polkadot/types-create": 10.12.1 "@polkadot/util": ^12.6.2 "@polkadot/util-crypto": ^12.6.2 rxjs: ^7.8.1 tslib: ^2.6.2 - checksum: 6e4873329bc8754d6ebdf1e4b20b1ced1edbda554a837c6d47dbc6f3abf8001e74884631bba3695cbd08b07e96ae784191a586d286d3bdfc5a985a0785595049 + checksum: 6184f50bef018b1fa11e3caaf851dc6fc7510c3dc56f81cfbde1b401f30ddd8ac73083770af41f3c8b3aa76beb4784d9ddb772460ab77c933628385090ab290c languageName: node linkType: hard @@ -2373,7 +2437,7 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.5": +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.5": version: 1.1.5 resolution: "@scure/base@npm:1.1.5" checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 @@ -2412,20 +2476,46 @@ __metadata: languageName: node linkType: hard -"@substrate/connect-extension-protocol@npm:^1.0.1": - version: 1.0.1 - resolution: "@substrate/connect-extension-protocol@npm:1.0.1" - checksum: 116dee587e81e832e14c25038bd849438c9493c6089aa6c1bf1760780d463880d44d362ed983d57ac3695368ac46f3c9df3dbaed92f36de89626c9735cecd1e4 +"@substrate/connect-extension-protocol@npm:^2.0.0": + version: 2.0.0 + resolution: "@substrate/connect-extension-protocol@npm:2.0.0" + checksum: a7c6ff3fefc0784f28b1d253514c1d2951684fe3d06392dfd70299fa2184fbe040d2bd6e0f113e30a1920920b649d43668aa4565847778ab3334c7e445e880cf + languageName: node + linkType: hard + +"@substrate/connect-known-chains@npm:^1.0.7": + version: 1.0.9 + resolution: "@substrate/connect-known-chains@npm:1.0.9" + checksum: 58a25df8dd8e7836e7ff932c3834e6cee67b820c0ee4832eb08d3ef25e6523c9560edd6ddecc96cb816fcbbb1ed01a3e0df50d1cf58a4bf9cc820180f21269e8 languageName: node linkType: hard -"@substrate/connect@npm:0.7.35": - version: 0.7.35 - resolution: "@substrate/connect@npm:0.7.35" +"@substrate/connect@npm:0.8.7": + version: 0.8.7 + resolution: "@substrate/connect@npm:0.8.7" dependencies: - "@substrate/connect-extension-protocol": ^1.0.1 - smoldot: 2.0.7 - checksum: 4f0cfb2b9521c5300cbcc0245e87b72484738e6cc8103ddf91e3516f627de546d0a152839824ac2716b8ea2f327f3eee0f74a9a07886ed4a091e3b7c1e989441 + "@substrate/connect-extension-protocol": ^2.0.0 + "@substrate/connect-known-chains": ^1.0.7 + "@substrate/light-client-extension-helpers": ^0.0.3 + smoldot: 2.0.21 + checksum: 8390d03f463690b63193363024c4c9edafebebe1722acd95d07a8177630d85039d7115cf1c502d1d8254adbea97e5b3cf49ec36841cebefeb0408201e39e8fc5 + languageName: node + linkType: hard + +"@substrate/light-client-extension-helpers@npm:^0.0.3": + version: 0.0.3 + resolution: "@substrate/light-client-extension-helpers@npm:0.0.3" + dependencies: + "@polkadot-api/client": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/json-rpc-provider": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/json-rpc-provider-proxy": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@polkadot-api/substrate-client": 0.0.1-12c4b0432a814086c3c1a3b8052b31c72c2c9ad3.1.0 + "@substrate/connect-extension-protocol": ^2.0.0 + "@substrate/connect-known-chains": ^1.0.7 + rxjs: ^7.8.1 + peerDependencies: + smoldot: 2.x + checksum: a2e4a1df8f76c10c7ef3193cd4c7633beaea59e840afa71b74a5d90bdd8b335f1ec7b847b3c30e84be6e59bd25a8240549860d3016e3851a14aced45eac339a7 languageName: node linkType: hard @@ -9115,14 +9205,14 @@ __metadata: languageName: node linkType: hard -"nock@npm:^13.4.0": - version: 13.5.3 - resolution: "nock@npm:13.5.3" +"nock@npm:^13.5.0": + version: 13.5.4 + resolution: "nock@npm:13.5.4" dependencies: debug: ^4.1.0 json-stringify-safe: ^5.0.1 propagate: ^2.0.0 - checksum: f119e71d8e56a456a29bfa00a1d959387210fe8ada69443a7102fa9329a9c5fb2e0db149c4a00d304b11681127efd03fc0e6bc142a4ff609b1e153f70aa8712d + checksum: d31f924e34c87ae985edfb7b5a56e8a4dcfc3a072334ceb6d686326581f93090b3e23492663a64ce61b8df4f365b113231d926bc300bcfe9e5eb309c3e4b8628 languageName: node linkType: hard @@ -10464,6 +10554,13 @@ __metadata: languageName: node linkType: hard +"scale-ts@npm:^1.4.3": + version: 1.6.0 + resolution: "scale-ts@npm:1.6.0" + checksum: 2cd6d3e31ea78621fe2e068eedc3beb6a3cfc338c9033f04ec3e355b4b08e134febad655c54a80272a50737136a27436f9d14d6525b126e621a3b77524111056 + languageName: node + linkType: hard + "scheduler@npm:^0.23.0": version: 0.23.0 resolution: "scheduler@npm:0.23.0" @@ -10686,12 +10783,12 @@ __metadata: languageName: node linkType: hard -"smoldot@npm:2.0.7": - version: 2.0.7 - resolution: "smoldot@npm:2.0.7" +"smoldot@npm:2.0.21": + version: 2.0.21 + resolution: "smoldot@npm:2.0.21" dependencies: ws: ^8.8.1 - checksum: fc039bfa0560312ae09c2136dd506f2a1994ead804b7234b49b2ecfac2fd19d306973a0fcfb66019645a8cf9a1702c270bc544a622ae40b63cf14790ea3531e0 + checksum: 464f23dd20e8156ab63dfdccf719da9a9e245b4c1581844c3d76ab64384154f578261810d9f3d1957739577a1bccbf51b6a7467f7862f092c45b47f8e1e7b9a4 languageName: node linkType: hard From aa7a7b30b37cf18d14d06027f33fb154b163088e Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 13:58:43 +0100 Subject: [PATCH 11/15] update client check --- packages/common/src/constants.ts | 4 ++-- packages/common/src/constraints/ScoreCandidates.ts | 4 +++- packages/common/src/constraints/ValidityChecks.ts | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index 361da31fe..f23d496d5 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -103,8 +103,8 @@ export const TIME_DELAY_BLOCKS = 10850; // The number of blocks after a time delay proxy call was announced that we want to cancel the tx. Should be 36 hours export const CANCEL_THRESHOLD = 21700; -// Monitor Cron job for checking if clients have upgraded. This runs ever 15 minutes by default -export const MONITOR_CRON = "0 */15 * * * *"; +// Monitor Cron job for checking if clients have upgraded. This runs ever 3 minutes by default +export const MONITOR_CRON = "0 */3 * * * *"; // Clear Offline Time Cron Job. This runs once every sunday by default // export const CLEAR_OFFLINE_CRON = "0 0 0 * * 0"; diff --git a/packages/common/src/constraints/ScoreCandidates.ts b/packages/common/src/constraints/ScoreCandidates.ts index 7cf9a2f69..e9216f1d0 100644 --- a/packages/common/src/constraints/ScoreCandidates.ts +++ b/packages/common/src/constraints/ScoreCandidates.ts @@ -210,7 +210,9 @@ export const scoreCandidate = async ( const nominatorStakeScore = scaledNominatorStake * constraints.WEIGHT_CONFIG.NOMINATIONS_WEIGHT; - const isAlternativeClient = candidate?.implementation != "Parity Polkadot"; + const isAlternativeClient = candidate?.implementation + ? candidate?.implementation != "Parity Polkadot" + : false; const clientScore = isAlternativeClient ? constraints.WEIGHT_CONFIG.CLIENT_WEIGHT : 0; diff --git a/packages/common/src/constraints/ValidityChecks.ts b/packages/common/src/constraints/ValidityChecks.ts index 95444bb5f..b66f8e112 100644 --- a/packages/common/src/constraints/ValidityChecks.ts +++ b/packages/common/src/constraints/ValidityChecks.ts @@ -89,7 +89,7 @@ export const checkLatestClientVersion = async ( ): Promise => { try { const skipClientUpgrade = config.constraints?.skipClientUpgrade || false; - if (skipClientUpgrade!) { + if (!skipClientUpgrade) { if (candidate?.implementation == "Kagome Node") { await setLatestClientReleaseValidity(candidate.stash, true); return true; @@ -126,7 +126,6 @@ export const checkLatestClientVersion = async ( await setLatestClientReleaseValidity(candidate.stash, true); return true; } - return true; } catch (e) { logger.error( `Error checking latest client version: ${e}`, From d968656c78112b104f116972fd4543b32ee7f5de Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 15:45:27 +0100 Subject: [PATCH 12/15] add client and adjust candidate gateway endpoint --- packages/common/src/db/models.ts | 2 ++ packages/common/src/db/queries/ValidatorScore.ts | 3 +++ packages/gateway/src/services/Candidate.ts | 12 ++++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/common/src/db/models.ts b/packages/common/src/db/models.ts index 2b263a2df..73550cd9d 100644 --- a/packages/common/src/db/models.ts +++ b/packages/common/src/db/models.ts @@ -732,6 +732,7 @@ export interface ValidatorScore { nominatorStake: number; // The randomness factor used to buffer the total randomness: number; + client: number; } export const ValidatorScoreSchema = new Schema({ @@ -769,6 +770,7 @@ export const ValidatorScoreSchema = new Schema({ country: Number, provider: Number, nominatorStake: Number, + client: Number, // The randomness factor used to buffer the total randomness: Number, }); diff --git a/packages/common/src/db/queries/ValidatorScore.ts b/packages/common/src/db/queries/ValidatorScore.ts index 383ae04c0..4a029a982 100644 --- a/packages/common/src/db/queries/ValidatorScore.ts +++ b/packages/common/src/db/queries/ValidatorScore.ts @@ -25,6 +25,7 @@ export const setValidatorScore = async ( nominatorStake, randomness, updated, + client, } = score; const data = await ValidatorScoreModel.findOne({ @@ -54,6 +55,7 @@ export const setValidatorScore = async ( provider, nominatorStake, randomness, + client, }); await score.save(); return true; @@ -82,6 +84,7 @@ export const setValidatorScore = async ( country, provider, nominatorStake, + client, randomness, }, ).exec(); diff --git a/packages/gateway/src/services/Candidate.ts b/packages/gateway/src/services/Candidate.ts index 8ca169701..1da5ba7fc 100644 --- a/packages/gateway/src/services/Candidate.ts +++ b/packages/gateway/src/services/Candidate.ts @@ -3,11 +3,15 @@ import { logger, queries } from "@1kv/common"; const label = { label: "Gateway" }; export const getCandidateData = async (candidate: any): Promise => { - const metadata = await queries.getChainMetadata(); + const [metadata, score, nominations, location] = await Promise.all([ + queries.getChainMetadata(), + queries.getLatestValidatorScore(candidate.stash), + queries.getLatestNominatorStake(candidate.stash), + queries.getCandidateLocation(candidate.name), + ]); + const denom = Math.pow(10, metadata.decimals); - const score = await queries.getLatestValidatorScore(candidate.stash); - const nominations = await queries.getLatestNominatorStake(candidate.stash); - const location = await queries.getCandidateLocation(candidate.name); + return { slotId: candidate.slotId, kyc: candidate.kyc, From f552ccce9374b0ff92e05959d4351abd9841efb0 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 17:40:30 +0100 Subject: [PATCH 13/15] clean old locations, nominator stakes, and scores --- packages/common/src/constants.ts | 2 ++ packages/common/src/db/queries/Candidate.ts | 1 + packages/common/src/db/queries/Location.ts | 17 ++++++++++++++++ .../common/src/db/queries/NominatorStake.ts | 20 +++++++++++++++++++ .../common/src/db/queries/ValidatorScore.ts | 6 ++---- .../jobs/specificJobs/LocationStatsJob.ts | 1 + packages/core/src/index.ts | 2 ++ 7 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index f23d496d5..8603c27d0 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -1,6 +1,8 @@ /// One week in milliseconds. import WS from "ws"; +export const TWO_DAYS_IN_MS = 2 * 24 * 60 * 60 * 1000; + export const FIVE_MINUTES = 5 * 60 * 1000; export const WEEK = 7 * 24 * 60 * 60 * 1000; diff --git a/packages/common/src/db/queries/Candidate.ts b/packages/common/src/db/queries/Candidate.ts index ca3c930d1..358554bf0 100644 --- a/packages/common/src/db/queries/Candidate.ts +++ b/packages/common/src/db/queries/Candidate.ts @@ -516,6 +516,7 @@ export const updateCandidateOnlineTelemetryDetails = async ( telemetryId: { $literal: telemetryNodeDetails.telemetryId }, onlineSince: { $literal: Date.now() }, offlineSince: { $literal: 0 }, + version: telemetryNodeDetails.version, implementation: { $literal: telemetryNodeDetails.nodeImplementation, }, diff --git a/packages/common/src/db/queries/Location.ts b/packages/common/src/db/queries/Location.ts index f3ecd9cc9..fff69cb72 100644 --- a/packages/common/src/db/queries/Location.ts +++ b/packages/common/src/db/queries/Location.ts @@ -9,6 +9,7 @@ import { logger } from "../../index"; import { getLatestSession } from "./Session"; import { HardwareSpec } from "../../types"; import { dbLabel } from "../index"; +import { TWO_DAYS_IN_MS } from "../../constants"; export const getAllLocations = async (): Promise => { return LocationModel.find({}).lean(); @@ -131,6 +132,22 @@ export const cleanBlankLocations = async (): Promise => { }).exec(); }; +// Remove all location data older than two days +export const cleanOldLocations = async (): Promise => { + const twoDaysAgo = Date.now() - TWO_DAYS_IN_MS; + + try { + await LocationModel.deleteMany({ updated: { $lt: twoDaysAgo } }).exec(); + return true; + } catch (error) { + logger.info( + `Error cleaning old locations: ${JSON.stringify(error)}`, + dbLabel, + ); + return false; + } +}; + // Sets a location from heartbeats export const iitExists = async (): Promise => { diff --git a/packages/common/src/db/queries/NominatorStake.ts b/packages/common/src/db/queries/NominatorStake.ts index 75676776e..5ced1250f 100644 --- a/packages/common/src/db/queries/NominatorStake.ts +++ b/packages/common/src/db/queries/NominatorStake.ts @@ -1,4 +1,7 @@ import { NominatorStake, NominatorStakeModel } from "../models"; +import { TWO_DAYS_IN_MS } from "../../constants"; +import { logger } from "../../index"; +import { dbLabel } from "../index"; export const setNominatorStake = async ( validator: string, @@ -84,3 +87,20 @@ export const getNominatorStake = async ( .sort("-era") .limit(limit ? limit : 100); }; + +export const cleanOldNominatorStakes = async (): Promise => { + const twoDaysAgo = Date.now() - TWO_DAYS_IN_MS; + + try { + await NominatorStakeModel.deleteMany({ + updated: { $lt: twoDaysAgo }, + }).exec(); + return true; + } catch (error) { + logger.info( + `Error cleaning old nominator stakes: ${JSON.stringify(error)}`, + dbLabel, + ); + return false; + } +}; diff --git a/packages/common/src/db/queries/ValidatorScore.ts b/packages/common/src/db/queries/ValidatorScore.ts index 4a029a982..9a45e5a6c 100644 --- a/packages/common/src/db/queries/ValidatorScore.ts +++ b/packages/common/src/db/queries/ValidatorScore.ts @@ -1,4 +1,5 @@ import { ValidatorScore, ValidatorScoreModel } from "../models"; +import { TWO_DAYS_IN_MS } from "../../constants"; export const setValidatorScore = async ( address: string, @@ -118,10 +119,7 @@ export const getLatestValidatorScore = async ( }; export const deleteOldValidatorScores = async (): Promise => { - const FIVE_MINUTES = 300000; - const ONE_WEEK = 604800016.56; - const ONE_MONTH = 2629800000; - const timeWindow = Date.now() - ONE_WEEK; + const timeWindow = Date.now() - TWO_DAYS_IN_MS; const scoreToDelete = await ValidatorScoreModel.find({ updated: { $lt: timeWindow }, }).exec(); diff --git a/packages/common/src/scorekeeper/jobs/specificJobs/LocationStatsJob.ts b/packages/common/src/scorekeeper/jobs/specificJobs/LocationStatsJob.ts index 39cfb0761..1b10450a3 100644 --- a/packages/common/src/scorekeeper/jobs/specificJobs/LocationStatsJob.ts +++ b/packages/common/src/scorekeeper/jobs/specificJobs/LocationStatsJob.ts @@ -16,6 +16,7 @@ export const locationStatsJob = async (metadata: JobRunnerMetadata) => { try { const { chaindata } = metadata; await queries.cleanBlankLocations(); + await queries.cleanOldLocations(); jobStatusEmitter.emit("jobProgress", { name: JobNames.LocationStats, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 94d0ba36e..0fe42ffc1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -120,6 +120,8 @@ export const clean = async (scorekeeper) => { try { // Clean locations with None await queries.cleanBlankLocations(); + await queries.cleanOldLocations(); + await queries.cleanOldNominatorStakes(); // Delete all on-chain identities so they get fetched new on startup. await queries.deleteAllIdentities(); From ed90e3db41887cab19dbc459c7bb7c0fc7e2a1d1 Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 17:53:30 +0100 Subject: [PATCH 14/15] version check --- packages/common/src/constraints/ValidityChecks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/common/src/constraints/ValidityChecks.ts b/packages/common/src/constraints/ValidityChecks.ts index b66f8e112..4bd4073b7 100644 --- a/packages/common/src/constraints/ValidityChecks.ts +++ b/packages/common/src/constraints/ValidityChecks.ts @@ -120,6 +120,7 @@ export const checkLatestClientVersion = async ( return true; } } else { + await setLatestClientReleaseValidity(candidate.stash, false); return false; } } else { From 95765529edd8cf9b129e6c1a4b247fc0e55ee2ae Mon Sep 17 00:00:00 2001 From: will pankiewicz Date: Tue, 5 Mar 2024 17:54:07 +0100 Subject: [PATCH 15/15] version check --- packages/common/src/constraints/ValidityChecks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/common/src/constraints/ValidityChecks.ts b/packages/common/src/constraints/ValidityChecks.ts index 4bd4073b7..85a507973 100644 --- a/packages/common/src/constraints/ValidityChecks.ts +++ b/packages/common/src/constraints/ValidityChecks.ts @@ -132,6 +132,7 @@ export const checkLatestClientVersion = async ( `Error checking latest client version: ${e}`, constraintsLabel, ); + await setLatestClientReleaseValidity(candidate.stash, false); return false; } };