diff --git a/launcher/public/img/icon/service-setting-icons/GasLimitIcon.png b/launcher/public/img/icon/service-setting-icons/GasLimitIcon.png new file mode 100644 index 0000000000..b7afccfec3 Binary files /dev/null and b/launcher/public/img/icon/service-setting-icons/GasLimitIcon.png differ diff --git a/launcher/src/backend/TekuGasLimitConfig.js b/launcher/src/backend/TekuGasLimitConfig.js new file mode 100644 index 0000000000..b939d8abb8 --- /dev/null +++ b/launcher/src/backend/TekuGasLimitConfig.js @@ -0,0 +1,40 @@ +export class TekuGasLimitConfig{ + constructor(nodeConnection) { + this.nodeConnection = nodeConnection; + } + + async createGasConfigFile(gasLimit, feeRecipient, configPath){ + const configContent = + `{ + "default_config": { + "fee_recipient": "${feeRecipient}", + "builder": { + "enabled": true, + "gas_limit": "${gasLimit}" + } + } + }`; + await this.nodeConnection.sshService.exec(`echo '${configContent}' > ${configPath}/gas_config.json`); + } + + async removeGasConfigFile(configPath){ + await this.nodeConnection.sshService.exec(`rm -f ${configPath}/gas_config.json`); + } + + async readGasConfigFile(configPath){ + let result = await this.nodeConnection.sshService.exec(`test -f ${configPath}/gas_config.json`) + if(result.rc == 0){ + let gasLimit = await this.nodeConnection.sshService.exec(`cat ${configPath}/gas_config.json`) + if(gasLimit.includes("gas_limit")){ + gasLimit = gasLimit.stdout.match(/^.*gas_limit.*$/gm)[0].split(':')[1]; + return gasLimit; + } + else{ + return ""; + } + } + else{ + return ""; + } + } +} diff --git a/launcher/src/background.js b/launcher/src/background.js index 747f9f2cb5..b24ed453d6 100755 --- a/launcher/src/background.js +++ b/launcher/src/background.js @@ -12,6 +12,7 @@ import { Monitoring } from "./backend/Monitoring.js"; import { StereumUpdater } from "./StereumUpdater.js"; import { ConfigManager } from "./backend/ConfigManager.js"; import { AuthenticationService } from "./backend/AuthenticationService.js"; +import { TekuGasLimitConfig } from "./backend/TekuGasLimitConfig.js"; import { SSHService } from "./backend/SSHService.js"; import path from "path"; import { readFileSync } from "fs"; @@ -27,6 +28,7 @@ const validatorAccountManager = new ValidatorAccountManager(nodeConnection, serv const configManager = new ConfigManager(nodeConnection); configManager.setServiceManager(serviceManager); const authenticationService = new AuthenticationService(nodeConnection); +const tekuGasLimitConfig = new TekuGasLimitConfig(nodeConnection); const sshService = new SSHService(); const { globalShortcut } = require("electron"); const log = require("electron-log"); @@ -727,6 +729,18 @@ ipcMain.handle("create2FAQRCode", async (event, args) => { return await authenticationService.create2FAQRCode(args.type, args.name, args.ip, args.secret); }); +ipcMain.handle("createGasConfigFile", async (event, args) => { + return await tekuGasLimitConfig.createGasConfigFile(args.gasLimit, args.feeRecipient, args.configPath); +}); + +ipcMain.handle("removeGasConfigFile", async (event, args) => { + return await tekuGasLimitConfig.removeGasConfigFile(args); +}); + +ipcMain.handle("readGasConfigFile", async (event, args) => { + return await tekuGasLimitConfig.readGasConfigFile(args); +}); + // Scheme must be registered before the app is ready protocol.registerSchemesAsPrivileged([{ scheme: "app", privileges: { secure: true, standard: true } }]); diff --git a/launcher/src/components/UI/node-page/sections/ExpertWindow.vue b/launcher/src/components/UI/node-page/sections/ExpertWindow.vue index 3c1a7dc408..cee9dfb5d5 100755 --- a/launcher/src/components/UI/node-page/sections/ExpertWindow.vue +++ b/launcher/src/components/UI/node-page/sections/ExpertWindow.vue @@ -381,6 +381,11 @@ export default { async readService() { this.item.yaml = await ControlService.getServiceYAML(this.item.config.serviceID); + let tekuGasLimit = ""; + if(this.item.service === "TekuValidatorService"){ + tekuGasLimit = await ControlService.readGasConfigFile(this.item.config.volumes[0].destinationPath) + } + if (this.item.service === "SSVNetworkService") { this.item.ssvConfig = await ControlService.readSSVNetworkConfig(this.item.config.serviceID); } @@ -404,6 +409,12 @@ export default { if (this.item.yaml.includes(command)) { let match = this.item.yaml.match(new RegExp(`${command}[:=]?([\\S*]*)`)); option.changeValue = match ? match[match.length - 1] : ""; + } else if(this.item.service === "TekuValidatorService" && command == "--gas-limit"){ + if(tekuGasLimit != null){ + tekuGasLimit = tekuGasLimit.replace(/"/g, ""); + } + option.changeValue = tekuGasLimit; + this.somethingIsChanged(option) } else { option.changeValue = ""; } @@ -583,6 +594,26 @@ export default { config: this.item.prometheusConfig, }); } + if (this.item.service === "TekuValidatorService") { + if (this.item.yaml.includes("--gas-limit")) { + await ControlService.createGasConfigFile({ + gasLimit: this.item.yaml.match(/^.*--gas-limit.*$/gm)[0].split('=')[1], + feeRecipient: this.item.yaml.match(/^.*fee-recipient.*$/gm)[0].split('=')[1], + configPath: this.item.config.volumes[0].destinationPath, + }); + if(!this.item.yaml.includes("validators-proposer-config")){ + this.item.yaml = this.item.yaml.replace(/^.*--gas-limit.*$/gm," - --validators-proposer-config=" + this.item.config.volumes[0].servicePath + "/gas_config.json"); + } + else{ + this.item.yaml = this.item.yaml.replace(/\n^.*--gas-limit.*$/gm,""); + } + } + else if (!this.item.yaml.includes("--gas-limit") && this.item.yaml.includes("validators-proposer-config")){ + await ControlService.removeGasConfigFile(this.item.config.volumes[0].destinationPath) + this.item.yaml = this.item.yaml.replace(new RegExp(/\n^.*validators-proposer-config.*$/gm),""); + } + } + await ControlService.writeServiceYAML({ id: this.item.config.serviceID, data: this.item.yaml, diff --git a/launcher/src/store/ControlService.js b/launcher/src/store/ControlService.js index 598418b1cd..c5184ad51b 100755 --- a/launcher/src/store/ControlService.js +++ b/launcher/src/store/ControlService.js @@ -663,6 +663,18 @@ class ControlService extends EventEmitter { async create2FAQRCode(args) { return this.promiseIpc.send("create2FAQRCode", args); } + + async createGasConfigFile(args) { + return this.promiseIpc.send("createGasConfigFile", args); + } + + async removeGasConfigFile(args) { + return this.promiseIpc.send("removeGasConfigFile", args); + } + + async readGasConfigFile(args) { + return this.promiseIpc.send("readGasConfigFile", args); + } } if (!instance) { instance = new ControlService(window.electron); diff --git a/launcher/src/store/services.js b/launcher/src/store/services.js index cca0826e96..34edb4c751 100755 --- a/launcher/src/store/services.js +++ b/launcher/src/store/services.js @@ -124,6 +124,14 @@ export const useServices = defineStore("services", { pattern: ["- --enable-doppelganger-protection"], commands: ["--enable-doppelganger-protection"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --gas-limit=)(.*)(\\n)"], + commands: ["--gas-limit"], + }, ], drag: true, state: "exited", @@ -228,6 +236,14 @@ export const useServices = defineStore("services", { pattern: ["(--enable-doppelganger=)(.*)(\\n)"], commands: ["--enable-doppelganger"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --suggested-gas-limit=)(.*)(\\n)"], + commands: ["--suggested-gas-limit"], + }, ], drag: true, state: "exited", @@ -351,6 +367,14 @@ export const useServices = defineStore("services", { pattern: ["(- --doppelganger-detection=)(.*)(\\n)"], commands: ["--doppelganger-detection"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --suggested-gas-limit=)(.*)(\\n)"], + commands: ["--suggested-gas-limit"], + }, ], drag: true, state: "exited", @@ -475,6 +499,14 @@ export const useServices = defineStore("services", { pattern: ["(- --doppelganger-detection-enabled=)(.*)(\\n)"], commands: ["--doppelganger-detection-enabled"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --gas-limit=)(.*)(\\n)"], + commands: ["--gas-limit"], + }, ], drag: true, state: "exited", @@ -613,7 +645,16 @@ export const useServices = defineStore("services", { sIcon: require("../../public/img/icon/service-icons/execution/Geth-s.png"), headerOption: false, expertOptionsModal: false, - expertOptions: [], + expertOptions: [ + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --miner.gaslimit=)(.*)(\\n)"], + commands: ["--miner.gaslimit"], + }, + ], drag: true, state: "exited", config: { @@ -645,7 +686,24 @@ export const useServices = defineStore("services", { sIcon: require("../../public/img/icon/service-icons/execution/HyperLedger-besu-s.png"), headerOption: false, expertOptionsModal: false, - expertOptions: [], + expertOptions: [ + { + title: "Auto Pruning", + type: "toggle", + changeValue: true, + icon: "/img/icon/service-setting-icons/prunning.png", + pattern: ["(- --pruning-enabled=)(.*)(\\n)"], + commands: ["--pruning-enabled"], + }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --target-gas-limit=)(.*)(\\n)"], + commands: ["--target-gas-limit"], + }, + ], drag: true, state: "exited", config: { @@ -688,6 +746,14 @@ export const useServices = defineStore("services", { pattern: ["(--Pruning.Mode=)(.*)(\\n)"], commands: ["--Pruning.Mode"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --Blocks.TargetBlockGasLimit=)(.*)(\\n)"], + commands: ["--Blocks.TargetBlockGasLimit"], + }, ], drag: true, state: "exited", @@ -894,6 +960,14 @@ export const useServices = defineStore("services", { pattern: ["(- --doppelgangerProtection=)(.*)(\\n)"], commands: ["--doppelgangerProtection"], }, + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --defaultGasLimit=)(.*)(\\n)"], + commands: ["--defaultGasLimit"], + }, ], drag: true, state: "exited", @@ -926,7 +1000,16 @@ export const useServices = defineStore("services", { sIcon: require("../../public/img/icon/service-icons/execution/Erigon-s.png"), headerOption: false, expertOptionsModal: false, - expertOptions: [], + expertOptions: [ + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --miner.gaslimit=)(.*)(\\n)"], + commands: ["--miner.gaslimit"], + }, + ], drag: true, state: "exited", config: { @@ -1101,7 +1184,16 @@ export const useServices = defineStore("services", { sIcon: require("../../public/img/icon/service-icons/execution/Reth-s.png"), headerOption: false, expertOptionsModal: false, - expertOptions: [], + expertOptions: [ + { + title: "Gas Limit", + type: "text", + changeValue: null, + icon: "/img/icon/service-setting-icons/GasLimitIcon.png", + pattern: ["(- --max_cumulative_gas=)(.*)(\\n)"], + commands: ["--max_cumulative_gas"], + }, + ], drag: true, state: "exited", config: {