Skip to content

Commit

Permalink
REFACTOR: improve error handling for validator-exit (#1914)
Browse files Browse the repository at this point in the history
* REFACTOR: improve error handling for validator-exit

* UPDATE: Single validator beaconStatus Frontend

* UPDATE: multiple function updated

* REFACTOR: check output

---------

Co-authored-by: mabasian <54101509+mabasian@users.noreply.github.com>
  • Loading branch information
gbayasgalan and mabasian committed Jun 19, 2024
1 parent 408440c commit 2d5ac1a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 42 deletions.
88 changes: 63 additions & 25 deletions launcher/src/backend/Monitoring.js
Original file line number Diff line number Diff line change
Expand Up @@ -3210,8 +3210,8 @@ rm -rf diskoutput

async exitValidatorAccount(pubkey, serviceID) {
const beaconStatus = await this.getBeaconStatus();
try {
if (beaconStatus.code === 0) {
if (beaconStatus.code === 0) {
try {
const beaconAPIPort = beaconStatus.data[0].beacon.destinationPort;
const serviceId = beaconStatus.data[0].sid;
if (!Array.isArray(pubkey)) {
Expand All @@ -3228,34 +3228,66 @@ rm -rf diskoutput
}
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);
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 task
this.nodeConnection.taskManager.otherTasksHandler(ref, `Exiting Account`, true, runExitCommand.stdout);
this.nodeConnection.taskManager.otherTasksHandler(ref);
// 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;
// add pubkey into the runExitCommands' result
runExitCommand["pubkey"] = `${pubkey[i]}`;

// 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);
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);

results.push({
pubkey: runExitCommand.pubkey,
code: JSON.parse(stdoutJson).code,
msg: JSON.parse(stdoutJson).message,
});
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) {
this.nodeConnection.taskManager.otherTasksHandler(
ref,
Expand All @@ -3269,13 +3301,19 @@ rm -rf diskoutput
}
}
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;
}
} catch (error) {
console.log("Error occured to get Beacon node status: ", error);
return {
info: "Error occured to get Beacon node status: ",
data: error,
};
} else if (beaconStatus.code !== 0) {
return beaconStatus;
}
}
}
10 changes: 8 additions & 2 deletions launcher/src/backend/ValidatorAccountManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,8 @@ export class ValidatorAccountManager {
const curlTag = await this.nodeConnection.ensureCurlImage();
let command = [
"docker run --rm --network=stereum curlimages/curl:" + curlTag,
`curl ${service.service.includes("Teku") ? "--insecure https" : "http"}://stereum-${service.id}:${validatorPorts[service.service]
`curl ${service.service.includes("Teku") ? "--insecure https" : "http"}://stereum-${service.id}:${
validatorPorts[service.service]
}${apiPath}`,
`-X ${method.toUpperCase()}`,
`-H 'Content-Type: application/json'`,
Expand Down Expand Up @@ -658,7 +659,12 @@ export class ValidatorAccountManager {
}

//Push successful task
this.nodeConnection.taskManager.otherTasksHandler(ref, `Get signed voluntary exit message`, true, data);
this.nodeConnection.taskManager.otherTasksHandler(
ref,
`Get signed voluntary exit message`,
true,
JSON.stringify(data)
);
this.nodeConnection.taskManager.otherTasksHandler(ref);
return data;
} catch (error) {
Expand Down
58 changes: 53 additions & 5 deletions launcher/src/components/UI/staking-page/StakingScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -574,11 +574,34 @@ const withdrawValidatorKey = async () => {
//if single key
const key = stakingStore.selectedSingleKeyToWithdraw;
try {
let res;
if (key && key !== null) {
stakingStore.withdrawAndExitResponse = await ControlService.exitValidatorAccount({
//stakingStore.withdrawAndExitResponse
res = await ControlService.exitValidatorAccount({
pubkey: key.key,
serviceID: stakingStore.selectedServiceToFilter.config?.serviceID,
});
let responseObj = {
pubkey: key.key,
code: null,
msg: [],
flag: "rejected",
};
if (typeof res !== "string") {
responseObj.code = res.code;
if (res.code === 0) {
responseObj.pubkey = res.pubkey;
responseObj.msg = res.msg;
responseObj.flag = "approved";
} else {
responseObj.msg = res.info;
}
} else {
responseObj.msg = res.split("\n");
}
stakingStore.withdrawAndExitResponse = [responseObj];
} else {
//if multiple keys
const multiKeys = stakingStore.keys
Expand All @@ -588,10 +611,35 @@ const withdrawValidatorKey = async () => {
}
})
.filter((key) => key !== undefined);
stakingStore.withdrawAndExitResponse = await ControlService.exitValidatorAccount({
pubkey: multiKeys,
serviceID: stakingStore.selectedServiceToFilter.config?.serviceID,
// stakingStore.withdrawAndExitResponse = await ControlService.exitValidatorAccount({
// pubkey: multiKeys,
// serviceID: stakingStore.selectedServiceToFilter.config?.serviceID,
// });
res = await Promise.all(
multiKeys.map(async (key) => {
return await ControlService.exitValidatorAccount({
pubkey: key,
serviceID: stakingStore.selectedServiceToFilter.config?.serviceID,
});
})
);
stakingStore.withdrawAndExitResponse = res.map((item) => {
let responseObj = {
pubkey: "multi keys",
code: null,
msg: [],
flag: "rejected",
};
if (typeof item !== "string") {
responseObj.pubkey = item.pubkey || "multi keys";
responseObj.code = item.code;
responseObj.msg = item.code === 0 ? item.msg : item.info;
responseObj.flag = item.code === 0 ? "approved" : "rejected";
} else {
responseObj.msg = item.split("\n");
}
return responseObj;
});
}
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
v-else-if="item.pubkey && item.code !== '200'"
class="w-full h-24 flex flex-col justify-center items-start overflow-hidden"
>
<p class="text-sm text-amber-400 text-left font-semibold">
<p v-if="!regectedService" class="text-sm text-amber-400 text-left font-semibold">
{{ useTruncate(item?.pubkey, 20, 20) }}:
<span class="text-md text-red-500 font-semibold text-left">{{ $t("stakingPage.exitFailed") }}</span>
</p>
Expand Down Expand Up @@ -89,6 +89,7 @@ const emit = defineEmits(["confirmWithdraw", "exportMessage"]);
const stakingStore = useStakingStore();
const clickOut = ref("Click outside to cancel");
let regectedService = ref(false);
const responseList = ref([]);
Expand Down Expand Up @@ -133,21 +134,25 @@ const getNumberOfKeys = () => {
let successCount = 0;
let failureCount = 0;
if(Array.isArray(displayResponse.value)) {
if (Array.isArray(displayResponse.value)) {
displayResponse.value.forEach((item) => {
if (item.code === "200") {
if (item.flag === "approved" && item.code === "200") {
regectedService.value = false;
successCount++;
} else {
} else if (item.flag === "approved" && item.code !== "200") {
regectedService.value = false;
failureCount++;
} else if (item.flag === "rejected") {
regectedService.value = true;
failureCount++;
}
});
}
// Combine the counts with the original displayResponse
const combinedResponse = [...useDeepClone(displayResponse.value), { success: successCount, failure: failureCount }];
// Combine the counts with the original displayResponse
const combinedResponse = [...useDeepClone(displayResponse.value), { success: successCount, failure: failureCount }];
// Update the responseList with the combined data
responseList.value = combinedResponse;
// Update the responseList with the combined data
responseList.value = combinedResponse;
}
};
// Set up the watcher after ensuring the store is properly initialized
Expand Down

0 comments on commit 2d5ac1a

Please sign in to comment.