Skip to content

Commit

Permalink
improving imported-validator-status (#1915)
Browse files Browse the repository at this point in the history
* init

* UPDATE: service id changed

* ADD: exit & pending status of validator keys

* FIX: the time status in the vld keys

Since Deposit
Since Withdraw

* ADD: witdrawable epoch

* UPDATE: add the since withdraw time

* temp logs

* FIX: height

* Test commit for dummy result

* REFACTOR: voluntary-exit error handling

* REFACTOR: volutary-exit output

* FIX: withdraw single and multi

* fix: resolve typo in keyRow

* css update

* FIX: debug the exit status

* FIX: eslint bug

* FIX: debug the withdraw status

* FIX: message typo

* FIX: return message

---------

Co-authored-by: mabasian <54101509+mabasian@users.noreply.github.com>
Co-authored-by: Max Behzadi <69126271+MaxTheGeeek@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 26, 2024
1 parent 10ee4e4 commit a2dd85e
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 182 deletions.
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
*/

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

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

.max-h-\[503px\]{
max-height: 503px;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -2370,6 +2366,10 @@ video {
max-width: 180px;
}

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

.max-w-lg{
max-width: 32rem;
}
Expand Down
211 changes: 118 additions & 93 deletions launcher/src/backend/Monitoring.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand All @@ -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 {
Expand All @@ -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,
};
});
Expand Down Expand Up @@ -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
Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit a2dd85e

Please sign in to comment.