-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #208 from dappnode/pablo/split-validators
Split validators functions into multiple files
- Loading branch information
Showing
8 changed files
with
540 additions
and
524 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { | ||
Web3signerDeleteRequest, | ||
Web3signerDeleteResponse, | ||
} from "@stakingbrain/common"; | ||
import { cron, validatorApi, signerApi, brainDb } from "../index.js"; | ||
import logger from "../modules/logger/index.js"; | ||
|
||
/** | ||
* Delete keystores: | ||
* 1. Write on db | ||
* 2. Delete keystores on web3signer API | ||
* 3. Delete pubkeys on validator API | ||
* 4. Delete feeRecipient on Validator API | ||
* @param deleteRequest | ||
* @returns | ||
*/ | ||
export async function deleteValidators( | ||
deleteRequest: Web3signerDeleteRequest | ||
): Promise<Web3signerDeleteResponse> { | ||
try { | ||
// IMPORTANT: stop the cron. This removes the scheduled cron task from the task queue | ||
// and prevents the cron from running while we are deleting validators | ||
cron.stop(); | ||
|
||
// Delete feeRecipient on Validator API | ||
for (const pubkey of deleteRequest.pubkeys) | ||
await validatorApi | ||
.deleteFeeRecipient(pubkey) | ||
.then(() => logger.debug(`Deleted fee recipient in validator API`)) | ||
.catch((err) => | ||
logger.error(`Error deleting validator feeRecipient`, err) | ||
); | ||
// Delete pubkeys on validator API | ||
await validatorApi | ||
.deleteRemoteKeys(deleteRequest) | ||
.then(() => logger.debug(`Deleted pubkeys in validator API`)) | ||
.catch((err) => logger.error(`Error deleting validator pubkeys`, err)); | ||
|
||
// Delete keystores on web3signer API | ||
const web3signerDeleteResponse = await signerApi.deleteKeystores( | ||
deleteRequest | ||
); | ||
|
||
// Write on db | ||
brainDb.deleteValidators(deleteRequest.pubkeys); | ||
|
||
// IMPORTANT: start the cron | ||
cron.start(); | ||
return web3signerDeleteResponse; | ||
} catch (e) { | ||
cron.restart(); | ||
throw e; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { | ||
BeaconchainPoolVoluntaryExitsPostRequest, | ||
ValidatorExitExecute, | ||
ValidatorExitGet, | ||
} from "@stakingbrain/common"; | ||
import { beaconchainApi, validatorApi, signerApi, brainDb } from "../index.js"; | ||
import logger from "../modules/logger/index.js"; | ||
|
||
/** | ||
* Get exit validators info signed | ||
* @param pubkeys The public keys of the validators to exit | ||
* @returns The exit data signed of each validator | ||
*/ | ||
export async function getExitValidators({ | ||
pubkeys, | ||
}: { | ||
pubkeys: string[]; | ||
}): Promise<BeaconchainPoolVoluntaryExitsPostRequest[]> { | ||
const validatorsExit = await _getExitValidators(pubkeys); | ||
logger.debug(validatorsExit); | ||
return validatorsExit; | ||
} | ||
|
||
/** | ||
* Performs a voluntary exit for a given set of validators as identified via `pubkeys` | ||
* @param pubkeys The public keys of the validators to exit | ||
* @returns The exit status of each validator | ||
*/ | ||
export async function exitValidators({ | ||
pubkeys, | ||
}: { | ||
pubkeys: string[]; | ||
}): Promise<ValidatorExitExecute[]> { | ||
const validatorsToExit = await _getExitValidators(pubkeys); | ||
const exitValidatorsResponses: ValidatorExitExecute[] = []; | ||
for (const validatorToExit of validatorsToExit) { | ||
try { | ||
await beaconchainApi.postVoluntaryExits({ | ||
postVoluntaryExitsRequest: { | ||
message: { | ||
epoch: validatorToExit.message.epoch, | ||
validator_index: validatorToExit.message.validator_index, | ||
}, | ||
signature: validatorToExit.signature, | ||
}, | ||
}); | ||
exitValidatorsResponses.push({ | ||
pubkey: validatorToExit.pubkey, | ||
status: { | ||
exited: true, | ||
message: "Successfully exited validator", | ||
}, | ||
}); | ||
} catch (e) { | ||
exitValidatorsResponses.push({ | ||
pubkey: validatorToExit.pubkey, | ||
status: { | ||
exited: false, | ||
message: `Error exiting validator ${e.message}`, | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
const exitedValidatorsPubkeys = exitValidatorsResponses | ||
.filter((validator) => validator.status.exited === true) | ||
.map((validator) => validator.pubkey); | ||
|
||
// Delete the validator from the validator API | ||
await validatorApi | ||
.deleteRemoteKeys({ pubkeys: exitedValidatorsPubkeys }) | ||
.then(() => logger.debug(`Deleted pubkeys in validator API`)) | ||
.catch((err) => logger.error(`Error deleting validator pubkeys`, err)); | ||
|
||
// Delete the validator from the web3signer API | ||
await signerApi | ||
.deleteKeystores({ pubkeys: exitedValidatorsPubkeys }) | ||
.then(() => logger.debug(`Deleted pubkeys in web3signer API`)); | ||
|
||
// Delete the validator from the brain db | ||
brainDb.deleteValidators(exitedValidatorsPubkeys); | ||
|
||
return exitValidatorsResponses; | ||
} | ||
|
||
/** | ||
* Get exit validators info | ||
* @param pubkeys The public keys of the validators to exit | ||
* @returns The exit validators info signed | ||
*/ | ||
async function _getExitValidators( | ||
pubkeys: string[] | ||
): Promise<ValidatorExitGet[]> { | ||
// Get the current epoch from the beaconchain API to exit the validators | ||
const currentEpoch = await beaconchainApi.getCurrentEpoch(); | ||
|
||
// Get the fork from the beaconchain API to sign the voluntary exit | ||
const fork = await beaconchainApi.getForkFromState({ state_id: "head" }); | ||
|
||
// Get the genesis from the beaconchain API to sign the voluntary exit | ||
const genesis = await beaconchainApi.getGenesis(); | ||
|
||
// Get the validators indexes from the validator API | ||
const validatorPubkeysIndexes = ( | ||
await Promise.all( | ||
pubkeys.map((pubkey) => | ||
beaconchainApi.getValidatorFromState({ state: "head", pubkey }) | ||
) | ||
) | ||
).map((validator) => { | ||
return { | ||
pubkey: validator.data.validator.pubkey, | ||
index: validator.data.index, | ||
}; | ||
}); | ||
|
||
const validatorsExit: ValidatorExitGet[] = []; | ||
for (const validatorIndex of validatorPubkeysIndexes) { | ||
const validatorSignature = await signerApi.signVoluntaryExit({ | ||
signerVoluntaryExitRequest: { | ||
type: "VOLUNTARY_EXIT", | ||
fork_info: { | ||
fork: { | ||
previous_version: fork.data.previous_version, | ||
current_version: fork.data.current_version, | ||
epoch: fork.data.epoch, | ||
}, | ||
genesis_validators_root: genesis.data.genesis_validators_root, | ||
}, | ||
voluntary_exit: { | ||
epoch: currentEpoch.toString(), | ||
validator_index: validatorIndex.index, | ||
}, | ||
}, | ||
pubkey: validatorIndex.pubkey, | ||
}); | ||
validatorsExit.push({ | ||
pubkey: validatorIndex.pubkey, | ||
message: { | ||
epoch: currentEpoch.toString(), | ||
validator_index: validatorIndex.index, | ||
}, | ||
signature: validatorSignature, | ||
}); | ||
} | ||
|
||
return validatorsExit; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { | ||
CustomValidatorGetResponse, | ||
WithdrawalCredentialsFormat, | ||
isValidEcdsaPubkey, | ||
isValidBlsPubkey, | ||
} from "@stakingbrain/common"; | ||
import { brainDb, validatorApi, signerApi, beaconchainApi } from "../index.js"; | ||
import logger from "../modules/logger/index.js"; | ||
|
||
/** | ||
* Get all validators from db | ||
* If running in development mode (NODE_ENV === "development") it will returns booleans for | ||
* validatorImported and validatorFeeRecipientCorrect checks from the validator API | ||
* @returns | ||
*/ | ||
export async function getValidators(): Promise<CustomValidatorGetResponse[]> { | ||
const data = brainDb.data; | ||
if (!data) return []; | ||
|
||
const validatorPubkeys = ( | ||
await validatorApi.getRemoteKeys().catch((e) => { | ||
logger.error(e); | ||
return { data: [] }; | ||
}) | ||
).data.map((validator) => validator.pubkey); | ||
|
||
const signerPubkeys = ( | ||
await signerApi.getKeystores().catch((e) => { | ||
logger.error(e); | ||
return { data: [] }; | ||
}) | ||
).data.map((key) => key.validating_pubkey); | ||
|
||
const validatorsFeeRecipients = await Promise.all( | ||
validatorPubkeys.map((pubkey) => validatorApi.getFeeRecipient(pubkey)) | ||
).catch((e) => { | ||
logger.error(e); | ||
return []; | ||
}); | ||
|
||
const validators: CustomValidatorGetResponse[] = []; | ||
for (const [pubkey, { tag, feeRecipient }] of Object.entries(data)) { | ||
let format: WithdrawalCredentialsFormat, | ||
withdrawalAddress = ""; | ||
try { | ||
withdrawalAddress = ( | ||
await beaconchainApi.getValidatorFromState({ | ||
state: "head", | ||
pubkey, | ||
}) | ||
).data.validator.withdrawal_credentials; | ||
format = isValidEcdsaPubkey(withdrawalAddress) | ||
? "ecdsa" | ||
: isValidBlsPubkey(pubkey) | ||
? "bls" | ||
: "unknown"; | ||
} catch (e) { | ||
logger.error(e); | ||
format = "error"; | ||
} | ||
|
||
validators.push({ | ||
pubkey, | ||
tag, | ||
feeRecipient, | ||
withdrawalCredentials: { | ||
address: withdrawalAddress, | ||
format, | ||
}, | ||
validatorImported: validatorPubkeys.includes(pubkey), | ||
signerImported: signerPubkeys.includes(pubkey), | ||
validatorFeeRecipientCorrect: validatorsFeeRecipients.some( | ||
(feeRecipient) => feeRecipient === feeRecipient | ||
), | ||
}); | ||
} | ||
|
||
return validators; | ||
} |
Oops, something went wrong.