From 74afffc39efa4d126aab4a14240d352d6f3fa99d Mon Sep 17 00:00:00 2001 From: POPPIN-FUMI Date: Tue, 5 Nov 2024 09:20:53 +0100 Subject: [PATCH] add getSolanaCLI for agave/solana CLI --- .changeset/three-olives-listen.md | 12 ++ .../switch/changeIdentityIncomingV1toV2.ts | 146 ++++++++++++++++++ packages/solv/src/cli/switch/index.ts | 20 +++ .../startTestnetValidatorScript.ts | 2 +- 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 .changeset/three-olives-listen.md create mode 100644 packages/solv/src/cli/switch/changeIdentityIncomingV1toV2.ts diff --git a/.changeset/three-olives-listen.md b/.changeset/three-olives-listen.md new file mode 100644 index 0000000..c5af386 --- /dev/null +++ b/.changeset/three-olives-listen.md @@ -0,0 +1,12 @@ +--- +'@epics-dao/solv': minor +--- + +Now that we have the v2 version of the solv cli, we need to update the solv cli versioin. +So, we are adding a new function `getSolanaCLI` to get the solana cli version for agave/solana. +Also, solv switch incoming for v1 to v2. + +Add - getSolanaCLI to get the solana cli version for agave/solana +Add - solv switch incoming for v1 to v2 + +solana version: 1.x.x will be deprecated soon, so we need to switch to v2. diff --git a/packages/solv/src/cli/switch/changeIdentityIncomingV1toV2.ts b/packages/solv/src/cli/switch/changeIdentityIncomingV1toV2.ts new file mode 100644 index 0000000..5168d4a --- /dev/null +++ b/packages/solv/src/cli/switch/changeIdentityIncomingV1toV2.ts @@ -0,0 +1,146 @@ +import { + AGAVE_VALIDATOR, + IDENTITY_KEY, + IDENTITY_KEY_PATH, + LEDGER_PATH, + MAINNET_VALIDATOR_KEY_PATH, + SOLANA_VALIDATOR, + SOLV_HOME, + TESTNET_VALIDATOR_KEY_PATH, + UNSTAKED_KEY, +} from '@/config/constants' +import { join } from 'path' +import { spawnSync } from 'node:child_process' +import chalk from 'chalk' +import checkValidatorKey from './checkValidatorKey' +import { updateDefaultConfig } from '@/config/updateDefaultConfig' +import { DefaultConfigType } from '@/config/types' +import { Network, NodeType } from '@/config/enums' + +const unstakedKeyPath = join(SOLV_HOME, UNSTAKED_KEY) +const identityKeyPath = join(SOLV_HOME, IDENTITY_KEY) + +export const changeIdentityIncomingV1toV2 = async ( + ip: string, + pubkey: string, + config: DefaultConfigType, +) => { + const isTestnet = config.NETWORK === Network.TESTNET + const isRPC = config.NODE_TYPE === NodeType.RPC + let validatorKeyPath = isTestnet + ? TESTNET_VALIDATOR_KEY_PATH + : MAINNET_VALIDATOR_KEY_PATH + if (isRPC) { + validatorKeyPath = TESTNET_VALIDATOR_KEY_PATH + } + + const isKeyOkay = checkValidatorKey(validatorKeyPath, ip) + if (!isKeyOkay) { + return + } + + // old version of solana client + const solanaClient = SOLANA_VALIDATOR + // new version of solana client + const agaveClient = AGAVE_VALIDATOR + + console.log(chalk.white('🟢 Waiting for restart window...')) + const restartWindowCmd = `ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no solv@${ip} -p 22 'cd ~ && source ~/.profile && ${solanaClient} -l ${LEDGER_PATH} wait-for-restart-window --min-idle-time 2 --skip-new-snapshot-check'` + const result1 = spawnSync(restartWindowCmd, { shell: true, stdio: 'inherit' }) + if (result1.status !== 0) { + console.log( + chalk.yellow( + `⚠️ wait-for-restart-window Failed. Please check your Validator\n$ ssh solv@${ip}\n\nFailed Cmd: ${solanaClient} -l ${LEDGER_PATH} wait-for-restart-window --min-idle-time 2 --skip-new-snapshot-check`, + ), + ) + return + } + + // Set the identity on the unstaked key + console.log(chalk.white('🟢 Setting identity on the new validator...')) + const setIdentityCmd = `ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no solv@${ip} -p 22 'cd ~ && source ~/.profile && ${solanaClient} -l ${LEDGER_PATH} set-identity ${unstakedKeyPath}'` + const result2 = spawnSync(setIdentityCmd, { shell: true, stdio: 'inherit' }) + if (result2.status !== 0) { + console.log( + chalk.yellow( + `⚠️ Set Identity Failed. Please check your Validator\n$ ssh solv@${ip}\n\nFailed Cmd: ${solanaClient} -l ${LEDGER_PATH} set-identity ${unstakedKeyPath}`, + ), + ) + return + } + + // Change the Symlink to the unstaked keypair + console.log( + chalk.white('🟢 Changing the Symlink to the new validator keypair...'), + ) + const result3 = spawnSync( + `ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no solv@${ip} -p 22 'cd ~ && source ~/.profile && ln -sf ${unstakedKeyPath} ${identityKeyPath}'`, + { + shell: true, + stdio: 'inherit', + }, + ) + + if (result3.status !== 0) { + console.log( + chalk.yellow( + `⚠️ Chaning Identity Key Symlink Failed. Please check your Validator\n$ ssh solv@${ip}\n\nFailed Cmd: ln -sf ${unstakedKeyPath} ${identityKeyPath}`, + ), + ) + return + } + + // Download the tower file to the new validator + console.log( + chalk.white('🟢 Uploading the tower file to the new validator...'), + ) + const result4 = spawnSync( + `scp solv@${ip}:${LEDGER_PATH}/tower-1_9-${pubkey}.bin ${LEDGER_PATH}`, + { shell: true, stdio: 'inherit' }, + ) + if (result4.status !== 0) { + console.log( + chalk.yellow( + `⚠️ Upload Tower File Failed. Please check your tower file\n$ ssh solv@${ip}\n\nFailed Cmd: scp solv@${ip}:${LEDGER_PATH}/tower-1_9-${pubkey}.bin ${LEDGER_PATH}`, + ), + ) + return + } + + // Set the identity on the new validator + console.log(chalk.white('🟢 Setting identity on the new validator...')) + const result5 = spawnSync( + `${agaveClient} -l ${LEDGER_PATH} set-identity --require-tower ${validatorKeyPath}`, + { + shell: true, + stdio: 'inherit', + }, + ) + if (result5.status !== 0) { + console.log( + chalk.yellow( + `⚠️ Set Identity Failed. Please check your Validator\n\nFailed Cmd: ${agaveClient} -l ${LEDGER_PATH} set-identity ${validatorKeyPath}\nln -sf ${validatorKeyPath} ${IDENTITY_KEY_PATH}`, + ), + ) + return + } + + const result6 = spawnSync(`ln -sf ${validatorKeyPath} ${IDENTITY_KEY_PATH}`, { + shell: true, + stdio: 'inherit', + }) + + if (result6.status !== 0) { + console.log( + chalk.yellow( + `⚠️ Chaning Identity Key Symlink Failed. Please check your Validator\n\nFailed Cmd: ln -sf ${validatorKeyPath} ${IDENTITY_KEY_PATH}`, + ), + ) + return + } + + console.log(chalk.white('🟢 Identity changed successfully!')) + await updateDefaultConfig({ + IS_DUMMY: false, + }) +} diff --git a/packages/solv/src/cli/switch/index.ts b/packages/solv/src/cli/switch/index.ts index 73c5be9..692057f 100644 --- a/packages/solv/src/cli/switch/index.ts +++ b/packages/solv/src/cli/switch/index.ts @@ -11,6 +11,7 @@ import { checkSSHConnection } from '../scp/checkSSHConnection' import chalk from 'chalk' import { DefaultConfigType } from '@/config/types' import { Network, NodeType } from '@/config/enums' +import { changeIdentityIncomingV1toV2 } from './changeIdentityIncomingV1toV2' type SwitchType = 'Incoming' | 'Outgoing' | '' const SWITCH_TYPES: SwitchType[] = ['Incoming', 'Outgoing'] @@ -18,6 +19,7 @@ const SWITCH_TYPES: SwitchType[] = ['Incoming', 'Outgoing'] type SwitchOptions = { switchType: SwitchType ip: string + v2MigrateIncoming: boolean } export const switchCommand = async ( @@ -28,6 +30,7 @@ export const switchCommand = async ( .command('switch') .option('--ip ', 'IP Address of the New Validator', '') .option('--switchType ', 'Switch Type', '') + .option('--v2-migrate-incoming', 'Switch V1 to V2 Incoming', false) .description('Switch Validator Identity with No Downtime') .action(async (options: SwitchOptions) => { try { @@ -82,6 +85,23 @@ export const switchCommand = async ( return } if (switchType === 'Incoming') { + if (options.v2MigrateIncoming) { + const confirm = await inquirer.prompt<{ confirm: boolean }>([ + { + name: 'confirm', + type: 'confirm', + message: + 'Are you sure you want to migrate V1 to V2 Incoming? This node must be running V2 and the remote node must be running V1.', + }, + ]) + if (!confirm.confirm) { + console.log(chalk.cyan(`Exiting...🌛`)) + process.exit(0) + } + console.log(chalk.white('🟢 Migrating V1 to V2 Incoming...')) + await changeIdentityIncomingV1toV2(ip, pubkey, config) + return + } await changeIdentityIncoming(ip, pubkey, config) } else { await changeIdentityOutgoing(ip, pubkey, config) diff --git a/packages/solv/src/template/startupScripts/startTestnetValidatorScript.ts b/packages/solv/src/template/startupScripts/startTestnetValidatorScript.ts index dab950a..aa78c51 100644 --- a/packages/solv/src/template/startupScripts/startTestnetValidatorScript.ts +++ b/packages/solv/src/template/startupScripts/startTestnetValidatorScript.ts @@ -9,7 +9,7 @@ import { export const startTestnetValidatorScript = () => { const script = `#!/bin/bash -exec solana-validator \\ +exec agave-validator \\ --identity ${IDENTITY_KEY_PATH} \\ --vote-account ${TESTNET_VALIDATOR_VOTE_KEY_PATH} \\ --authorized-voter ${TESTNET_VALIDATOR_KEY_PATH} \\