Skip to content

Commit 4a0dd60

Browse files
committed
Implemented LssEjector service plugin
1 parent 3166a2f commit 4a0dd60

File tree

12 files changed

+224
-5
lines changed

12 files changed

+224
-5
lines changed

controls/roles/manage-service/tasks/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
changed_when: false
4343
become: yes
4444
when: (stereum.manage_service.state == "started" or stereum.manage_service.state == "restarted") and item.split(':') | first != '/' and item.split(':') | first != '/sys' and item.split(':') | first != '/proc'
45-
with_items: "{{ stereum_service_configuration.volumes | reject('search', ':/engine.jwt') }}"
45+
with_items: "{{ stereum_service_configuration.volumes | reject('search', ':/engine.jwt') | reject('search', 'all-accounts.keystore.json') | reject('search', 'wallet-password') }}"
4646

4747
- name: Set directory permissions for engine.jwt
4848
file:

controls/roles/update-services/files/updates.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"GethService": [
1010
"latest"
1111
],
12+
"LSSEjector": [
13+
"latest"
14+
],
1215
"PrometheusService": [
1316
"v2.47.2"
1417
],
@@ -29,6 +32,9 @@
2932
"GethService": [
3033
"latest"
3134
],
35+
"LSSEjector": [
36+
"latest"
37+
],
3238
"PrometheusService": [
3339
"v2.47.2"
3440
],

launcher/public/output.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,6 +2565,10 @@ video {
25652565
animation: spin 1s linear infinite;
25662566
}
25672567

2568+
.\!cursor-not-allowed{
2569+
cursor: not-allowed !important;
2570+
}
2571+
25682572
.cursor-default{
25692573
cursor: default;
25702574
}

launcher/src/backend/ServiceManager.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { MetricsExporterService } from "./ethereum-services/MetricsExporterServi
1616
import { ExternalConsensusService } from "./ethereum-services/ExternalConsensusService";
1717
import { ExternalExecutionService } from "./ethereum-services/ExternalExecutionService";
1818
import { CustomService } from "./ethereum-services/CustomService";
19+
import { LssEjectorService } from "./ethereum-services/LssEjectorService";
1920
import { ConfigManager } from "./ConfigManager";
2021
import YAML from "yaml";
2122
// import { file } from "jszip";
@@ -124,6 +125,8 @@ export class ServiceManager {
124125
services.push(ExternalExecutionService.buildByConfiguration(config));
125126
} else if (config.service == "CustomService") {
126127
services.push(CustomService.buildByConfiguration(config));
128+
} else if (config.service == "LssEjectorService") {
129+
services.push(LssEjectorService.buildByConfiguration(config))
127130
}
128131
} else {
129132
log.error("found configuration without service!");
@@ -745,6 +748,13 @@ export class ServiceManager {
745748
args.consensusClients, // TOOD: remove later!
746749
args.otherServices
747750
);
751+
case "LssEjectorService":
752+
return LssEjectorService.buildByUserInput(
753+
args.network,
754+
args.executionClients,
755+
args.consensusClients,
756+
args.otherServices,
757+
)
748758
}
749759
}
750760

launcher/src/backend/ValidatorAccountManager.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ export class ValidatorAccountManager {
171171
let pubkeys = this.batches.map((b) => b.keystores.map((c) => JSON.parse(c).pubkey)).flat();
172172
const message = this.formatImportResult(pubkeys, data);
173173
this.nodeConnection.taskManager.otherTasksHandler(ref);
174+
175+
// Restart LssEjectorService if it is installed and is running
176+
let serviceInfos = await this.serviceManager.readServiceInfos(services)
177+
let lssEjectorClient = serviceInfos.find((service) => service.service === 'LssEjectorService');
178+
if (lssEjectorClient) {
179+
await this.serviceManager.restartService(lssEjectorClient)
180+
}
181+
174182
return message;
175183
} catch (err) {
176184
this.nodeConnection.taskManager.otherTasksHandler(ref, `Import Failed`, false, "Validator Import Failed:\n" + err);
@@ -239,6 +247,19 @@ export class ValidatorAccountManager {
239247
this.nodeConnection.taskManager.otherTasksHandler(ref, `Delete Keys`, true, result.stdout);
240248

241249
this.nodeConnection.taskManager.otherTasksHandler(ref);
250+
251+
// Restart LssEjectorService if it is installed and is running
252+
const serviceInfos = await this.serviceManager.readServiceInfos()
253+
const lssEjectorClient = serviceInfos.find((service) => service.service === 'LssEjectorService');
254+
const validatorsList = await this.listValidators(serviceID)
255+
if (lssEjectorClient && lssEjectorClient.state === 'running') {
256+
if (validatorsList.data.length > 0) {
257+
await this.serviceManager.restartService(lssEjectorClient)
258+
} else {
259+
await this.serviceManager.manageServiceState(lssEjectorClient.config.serviceID, "stopped")
260+
}
261+
}
262+
242263
//Return slashing protection data
243264
if (picked) return data.slashing_protection;
244265
return data;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { NodeService } from "./NodeService";
2+
import { ServiceVolume } from "./ServiceVolume";
3+
4+
export class LssEjectorService extends NodeService {
5+
static buildByUserInput(network, executionClients, consensusClients, otherServices) {
6+
const service = new LssEjectorService();
7+
service.setId();
8+
9+
const executionEndpoint = executionClients
10+
.map((client) => {
11+
return client.buildExecutionClientHttpEndpointUrl();
12+
})
13+
.join();
14+
const consensusEndpoint = consensusClients.map((client) => {
15+
return client.buildConsensusClientHttpEndpointUrl();
16+
})
17+
.join();
18+
19+
const allAccountsKeystoreDir = '/config/all-accounts.keystore.json'
20+
const walletPasswordDir = '/config/wallet-password'
21+
22+
const validatorService = otherServices.find(s => s.service.includes('PrysmValidator'))
23+
24+
const walletsDir = validatorService.volumes.find(
25+
(vol) => vol.servicePath === "/wallets" || vol.destinationPath.includes("/wallets")
26+
).destinationPath
27+
const passwordsDir = validatorService.volumes.find(
28+
(vol) => vol.servicePath === "/passwords" || vol.destinationPath.includes("/passwords")
29+
).destinationPath
30+
31+
const volumes = [
32+
new ServiceVolume(`${walletsDir}/direct/accounts/all-accounts.keystore.json`, allAccountsKeystoreDir, 'ro'),
33+
new ServiceVolume(`${passwordsDir}/wallet-password`, walletPasswordDir, 'ro'),
34+
]
35+
36+
// TODO set default value to a contract address in Stratis network when it is deployed
37+
let stakingContractAddress = ''
38+
if (network === 'auroria') {
39+
stakingContractAddress = '0x0504D06711d02E6275e1724529a801441088f9f4'
40+
}
41+
42+
service.init(
43+
"LssEjectorService", //service
44+
service.id, // id
45+
1, // configVersion
46+
"stratisevm/lss-ejector", // image
47+
"latest", // imageVersion
48+
[
49+
"start",
50+
'--all_accounts_file',
51+
`--consensus_endpoint=${consensusEndpoint}`,
52+
`--execution_endpoint=${executionEndpoint}`,
53+
`--keys_dir=${allAccountsKeystoreDir}`,
54+
`--keystore_password_file=${walletPasswordDir}`,
55+
`--staking_address=${stakingContractAddress}`
56+
], // command
57+
["lss-ejector"], // entrypoint
58+
null, // env
59+
null, // ports
60+
volumes, // volumes
61+
null, // user
62+
network, // network
63+
executionClients,
64+
consensusClients,
65+
null, // mevboost
66+
otherServices,
67+
);
68+
return service;
69+
}
70+
71+
static buildByConfiguration(config) {
72+
const service = new LssEjectorService();
73+
74+
service.initByConfig(config);
75+
76+
return service;
77+
}
78+
}

launcher/src/components/UI/edit-page/EditScreen.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,10 @@ const selectedServiceToRemove = (item) => {
708708
);
709709
if (!itemExists) {
710710
manageStore.confirmChanges.push(confirmDelete);
711+
let lssEjectorService
712+
if (item.service === 'PrysmValidatorService' && (lssEjectorService = serviceStore.installedServices.find(s => s.service === 'LssEjectorService'))) {
713+
selectedServiceToRemove(lssEjectorService)
714+
}
711715
}
712716
};
713717

launcher/src/components/UI/edit-page/components/changes/ChangesBox.vue

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,18 @@ const manageStore = useNodeManage();
5151
5252
const getChanges = computed(() => manageStore.confirmChanges);
5353
54+
const canCancelLssEjectorDelete = (item) => {
55+
if (item.service.service !== 'LssEjectorService' || (item.service.service === 'LssEjectorService' && item.content !== 'DELETE')) {
56+
return true
57+
}
58+
59+
return !manageStore.confirmChanges.find(item => item.content === 'DELETE' && item.service.service === 'PrysmValidatorService')
60+
}
61+
5462
const removeChange = (item) => {
63+
if (!canCancelLssEjectorDelete(item)) {
64+
return
65+
}
5566
emit("remove-change", item);
5667
};
5768
@@ -62,7 +73,11 @@ const contentBgColor = (item) => {
6273
if (content === "INSTALL") {
6374
bg = "bg-green-800 text-gray-100 text-sm font-semibold";
6475
} else if (content === "DELETE") {
65-
bg = "bg-red-800 text-gray-300 text-sm font-semibold";
76+
if (canCancelLssEjectorDelete(item)) {
77+
bg = "bg-red-800 text-gray-300 text-sm font-semibold";
78+
} else {
79+
bg = "bg-gray-800 text-gray-300 text-sm font-semibold !cursor-not-allowed";
80+
}
6681
} else if (content === "NETWORK") {
6782
bg = "bg-teal-700 text-gray-100 text-sm font-semibold min-w-[100px]";
6883
} else if (content === "SWITCH CLIENT") {

launcher/src/components/UI/edit-page/components/modals/AddConnection.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ const toggleConnection = (option) => {
203203
(e) => (e.category === "consensus" || e.service === "CharonService") && e.isConnected
204204
);
205205
props.properties.otherServices = list.value.filter((e) => e.category === "service" && e.isConnected);
206+
if (props.client.service === 'LssEjectorService') {
207+
props.properties.otherServices = list.value.filter((e) => e.category === "validator" && e.isConnected);
208+
}
206209
};
207210
208211
const getConnectionOptions = () => {
@@ -232,6 +235,9 @@ const getConnectionOptions = () => {
232235
if (props.client.service === "FlashbotsMevBoostService") {
233236
return manageStore.newConfiguration.filter((e) => e.category === "consensus");
234237
}
238+
if (props.client.service === "LssEjectorService") {
239+
return manageStore.newConfiguration.filter(e => e.category === "execution" || e.category === "consensus" || e.category === "validator")
240+
}
235241
break;
236242
default:
237243
return [];

launcher/src/components/UI/edit-page/components/modals/AddModal.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
:client="client"
55
:sub-title="getSubTitles"
66
:confirm-text="getConfirmText"
7-
:disabled-button="disabledButton || externalServiceConfirmBtn"
7+
:disabled-button="disabledButton || externalServiceConfirmBtn || lssEjectorEmptyClients"
88
click-outside-text="Click outside to cancel"
99
@close-window="closeWindow"
1010
@confirm-action="confirmInstall"
@@ -21,7 +21,7 @@ import CustomModal from "./CustomModal.vue";
2121
import AddPanel from "./AddPanel";
2222
import AddConnection from "./AddConnection";
2323
import MevboostRelays from "./MevboostRelays.vue";
24-
import { ref, onMounted, computed } from "vue";
24+
import { ref, onMounted, computed, watchEffect } from "vue";
2525
import { useNodeManage } from "@/store/nodeManage";
2626
import { useClickInstall } from "@/store/clickInstallation";
2727
@@ -42,6 +42,7 @@ const isAddPanelActivated = ref(false);
4242
const isRelaysActivated = ref(false);
4343
const isModifyActivated = ref(false);
4444
const disabledButton = ref(false);
45+
const lssEjectorEmptyClients = ref(false)
4546
// eslint-disable-next-line vue/no-setup-props-destructure
4647
const properties = ref({
4748
client: props.client,
@@ -68,6 +69,8 @@ const getConfirmText = computed(() => {
6869
(props.client.category === "validator" && !/Web3Signer/.test(props.client.service))
6970
) {
7071
text = "next";
72+
} else if (props.client.category === "service" && props.client.service === "LssEjectorService") {
73+
text = "next"
7174
} else if (props.client.category === "service" && props.client.service !== "FlashbotsMevBoostService") {
7275
text = "confirm";
7376
} else if (props.client.category === "service" && props.client.service === "FlashbotsMevBoostService") {
@@ -93,6 +96,16 @@ const getSubTitles = computed(() => {
9396
return text;
9497
});
9598
99+
watchEffect(() => {
100+
lssEjectorEmptyClients.value = (
101+
props.client.service === "LssEjectorService" &&
102+
getConfirmText.value === "confirm" &&
103+
(properties.value.executionClients.length === 0 ||
104+
properties.value.consensusClients.length === 0 ||
105+
properties.value.otherServices.length === 0)
106+
)
107+
})
108+
96109
const externalServiceConfirmBtn = computed(() => {
97110
if (props.client.service === "ExternalExecutionService") {
98111
return props.client.config.source === "" || props.client.config.jwtToken === "";
@@ -118,7 +131,8 @@ const confirmInstall = () => {
118131
emit("confirmInstall", properties.value);
119132
} else if (
120133
(props.client.category === "consensus" && getConfirmText.value === "next") ||
121-
(props.client.category === "validator" && getConfirmText.value === "next")
134+
(props.client.category === "validator" && getConfirmText.value === "next") ||
135+
(props.client.category === "service" && props.client.service === "LssEjectorService" && getConfirmText.value === "next")
122136
) {
123137
isAddPanelActivated.value = false;
124138
isModifyActivated.value = true;

0 commit comments

Comments
 (0)