Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improving imported-validator-status #1915

Merged
merged 22 commits into from
Jun 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions launcher/public/output.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;200;300;400;500;600;700;800;900&display=swap");

/*
! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com
! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com
*/

/*
@@ -1970,6 +1970,10 @@ video {
max-height: 491px;
}

.max-h-\[492px\]{
max-height: 492px;
}

.max-h-\[503px\]{
max-height: 503px;
}
@@ -1986,14 +1990,6 @@ video {
max-height: 100%;
}

.max-h-\[493px\]{
max-height: 493px;
}

.max-h-\[492px\]{
max-height: 492px;
}

.min-h-11{
min-height: 2.75rem;
}
@@ -2370,6 +2366,10 @@ video {
max-width: 180px;
}

.max-w-full{
max-width: 100%;
}

.max-w-lg{
max-width: 32rem;
}
211 changes: 118 additions & 93 deletions launcher/src/backend/Monitoring.js
Original file line number Diff line number Diff line change
@@ -3049,6 +3049,7 @@ rm -rf diskoutput
const chunk = validatorPublicKeys.slice(i, i + chunkSize);
const beaconAPICmd = `curl -s -X GET 'http://localhost:${beaconAPIPort}/eth/v1/beacon/states/head/validators?id=${chunk.join()}' -H 'accept: application/json'`;
beaconAPIRunCmd = await this.nodeConnection.sshService.exec(beaconAPICmd);

//check response
validatorNotFound =
beaconAPIRunCmd.rc != 0 ||
@@ -3058,6 +3059,7 @@ rm -rf diskoutput
}
const beaconAPICmdLastEpoch = `curl -s -X GET 'http://localhost:${beaconAPIPort}/eth/v1/beacon/states/head/finality_checkpoints' -H 'accept: application/json'`;
beaconAPIRunCmdLastEpoch = await this.nodeConnection.sshService.exec(beaconAPICmdLastEpoch);

const queryResult = data;
validatorBalances = queryResult.map((key, id) => {
return {
@@ -3066,7 +3068,10 @@ rm -rf diskoutput
balance: key.balance,
status: key.validator.slashed === "true" ? "slashed" : key.status.replace(/_.*/, ""),
pubkey: key.validator.pubkey,
activationepoch: key.validator.activation_epoch,
activationEpoch: key.validator.activation_epoch,
activationElgibilityEpoch: key.validator.activation_eligibility_epoch,
withdrawableEpoch: key.validator.withdrawable_epoch,
exitEpoch: key.validator.exit_epoch,
latestEpoch: parseInt(JSON.parse(beaconAPIRunCmdLastEpoch.stdout).data.current_justified.epoch) + 1,
};
});
@@ -3210,110 +3215,130 @@ rm -rf diskoutput

async exitValidatorAccount(pubkey, serviceID) {
const beaconStatus = await this.getBeaconStatus();
if (beaconStatus.code === 0) {
try {
const beaconAPIPort = beaconStatus.data[0].beacon.destinationPort;
const serviceId = beaconStatus.data[0].sid;
if (!Array.isArray(pubkey)) {
pubkey = [pubkey];

if (beaconStatus.code !== 0) {
return [
{
pubkey: undefined,
code: null,
msg: beaconStatus.info,
},
];
}

try {
const beaconAPIPort = beaconStatus.data[0].beacon.destinationPort;
const serviceId = beaconStatus.data[0].sid;
if (!Array.isArray(pubkey)) {
pubkey = [pubkey];
}
let results = [];

const parseRunExitCommandOutput = (output, pubkey) => {
if (!output.includes("{") || !output.includes("}")) {
return {
pubkey: pubkey,
code: null,
msg: output,
};
}
let results = [];
for (let i = 0; i < pubkey.length; i++) {
const ref = StringUtils.createRandomString(); // Create a random string to identify the task
this.nodeConnection.taskManager.otherTasksHandler(ref, `Exit Account ${pubkey[i].substring(0, 6)}..`);
try {
const result = await this.validatorAccountManager.getExitValidatorMessage(pubkey[i], serviceID);
if (result.data === undefined) {
throw result;
}
const curlTag = await this.nodeConnection.ensureCurlImage();
const exitMsg = result.data;
const exitCommand =
`docker run --rm --network=stereum curlimages/curl:${curlTag} curl ` +
`'http://stereum-${serviceId}:${beaconAPIPort}/eth/v1/beacon/pool/voluntary_exits' ` +
`-H 'accept: */*' ` +
`-H 'Content-Type: application/json' ` +
`-d '${JSON.stringify(exitMsg)}' -i -s`;

const runExitCommand = await this.nodeConnection.sshService.exec(exitCommand);
log.info(runExitCommand);

//Error handling
if (SSHService.checkExecError(runExitCommand) && runExitCommand.stderr)
throw SSHService.extractExecError(runExitCommand);

// Push successful/failed task
if (runExitCommand.stdout.includes('"code":200')) {
this.nodeConnection.taskManager.otherTasksHandler(ref, `Exiting Account`, true, runExitCommand.stdout);
this.nodeConnection.taskManager.otherTasksHandler(ref);
} else {
this.nodeConnection.taskManager.otherTasksHandler(
ref,
`Exiting Account Failed`,
false,
`Exiting Account Failed ${pubkey[i]} Failed:\n` + runExitCommand.stdout
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
return runExitCommand.stdout;
}

// add pubkey into the runExitCommands' result
runExitCommand["pubkey"] = `${pubkey[i]}`;
const jsonStartIndex = output.indexOf("{");
const jsonEndIndex = output.lastIndexOf("}");
const stdoutJson = output.substring(jsonStartIndex, jsonEndIndex + 1);
const parsedJson = JSON.parse(stdoutJson);

if (!runExitCommand.stdout.includes("{") && !runExitCommand.stdout.includes("}")) {
results.push({
pubkey: runExitCommand.pubkey,
code: null,
msg: runExitCommand.stdout,
});
} else {
// Extract the JSON payload from the stdout
const jsonStartIndex = runExitCommand.stdout.indexOf("{");
const jsonEndIndex = runExitCommand.stdout.lastIndexOf("}");
const stdoutJson = runExitCommand.stdout.substring(jsonStartIndex, jsonEndIndex + 1);
let message =
`${parsedJson?.message || ""}${parsedJson?.message && parsedJson?.stacktraces ? "\n" : ""}${
parsedJson?.stacktraces || ""
}`.trim() || output;

let parsedJson = {};
try {
parsedJson = JSON.parse(stdoutJson);
} catch (error) {
console.error("Error parsing JSON, result does not include valid JSON", error);
return runExitCommand.stdout;
}

const code = parsedJson.code ? parsedJson.code : null;

results.push({
pubkey: runExitCommand.pubkey,
code: code,
msg: parsedJson.message ? parsedJson.message : runExitCommand.stdout,
});
}
} catch (error) {
return {
pubkey: pubkey,
code: parsedJson.code || null,
msg: message,
};
};

const handleExitCommand = async (pubkey, serviceId, beaconAPIPort) => {
const ref = StringUtils.createRandomString();
this.nodeConnection.taskManager.otherTasksHandler(ref, `Exit Account ${pubkey.substring(0, 6)}..`);
try {
const result = await this.validatorAccountManager.getExitValidatorMessage(pubkey, serviceID);
if (result.data === undefined || !("data" in result)) {
throw new Error(result);
}

const curlTag = await this.nodeConnection.ensureCurlImage();
const exitMsg = result.data;
const exitCommand =
`docker run --rm --network=stereum curlimages/curl:${curlTag} curl ` +
`'http://stereum-${serviceId}:${beaconAPIPort}/eth/v1/beacon/pool/voluntary_exits' ` +
`-H 'accept: */*' ` +
`-H 'Content-Type: application/json' ` +
`-d '${JSON.stringify(exitMsg)}' -i -s`;

const runExitCommand = await this.nodeConnection.sshService.exec(exitCommand);
log.info(runExitCommand);

if (SSHService.checkExecError(runExitCommand) && runExitCommand.stderr) {
throw new Error(SSHService.extractExecError(runExitCommand));
}

const response = parseRunExitCommandOutput(runExitCommand.stdout, pubkey);

if (response.code === 200) {
this.nodeConnection.taskManager.otherTasksHandler(ref, `Exiting Account`, true, runExitCommand.stdout);
} else {
this.nodeConnection.taskManager.otherTasksHandler(
ref,
`Exiting Account Failed`,
false,
`Exiting Account Failed ${pubkey[i]} Failed:\n` + error
`Exiting Account Failed ${pubkey}:\n${runExitCommand.stdout}`
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
log.error("Exiting signed voluntary account Failed:\n", error);
return error;
}

this.nodeConnection.taskManager.otherTasksHandler(ref);
return response;
} catch (error) {
this.nodeConnection.taskManager.otherTasksHandler(
ref,
`Exiting Account Failed`,
false,
`Exiting Account Failed ${pubkey}:\n${error}`
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
log.error(`Exiting signed voluntary account failed for ${pubkey}:\n`, error);
return {
pubkey: pubkey,
code: null,
msg: error.toString(),
};
}
return results;
} catch (error) {
const ref = StringUtils.createRandomString();
this.nodeConnection.taskManager.otherTasksHandler(
ref,
"Error occurred to get Beacon service ID & port",
false,
`Error occurred to get Beacon service ID & port: ${error}`
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
return error;
};

for (let i = 0; i < pubkey.length; i++) {
const result = await handleExitCommand(pubkey[i], serviceId, beaconAPIPort);
results.push(result);
}
} else if (beaconStatus.code !== 0) {
return beaconStatus;

return results;
} catch (error) {
const ref = StringUtils.createRandomString();
this.nodeConnection.taskManager.otherTasksHandler(
ref,
"Error occurred to get Beacon service ID & port",
false,
`Error occurred to get Beacon service ID & port: ${error}`
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
return [
{
pubkey: undefined,
code: null,
msg: error.toString(),
},
];
}
}
}
2 changes: 1 addition & 1 deletion launcher/src/backend/ValidatorAccountManager.js
Original file line number Diff line number Diff line change
@@ -653,7 +653,7 @@ export class ValidatorAccountManager {
const data = JSON.parse(result.stdout);
if (data.data === undefined) {
if (data.code === undefined || data.message === undefined) {
throw "Undexpected Error: " + result;
throw "Undexpected Error: " + result.stdout;
}
throw data.code + " " + data.message;
}
Loading