Skip to content

Commit

Permalink
add getSolanaCLI for agave/solana CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
POPPIN-FUMI committed Nov 5, 2024
1 parent 1e3e3f0 commit 74afffc
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 1 deletion.
12 changes: 12 additions & 0 deletions .changeset/three-olives-listen.md
Original file line number Diff line number Diff line change
@@ -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.
146 changes: 146 additions & 0 deletions packages/solv/src/cli/switch/changeIdentityIncomingV1toV2.ts
Original file line number Diff line number Diff line change
@@ -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,
})
}
20 changes: 20 additions & 0 deletions packages/solv/src/cli/switch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ 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']

type SwitchOptions = {
switchType: SwitchType
ip: string
v2MigrateIncoming: boolean
}

export const switchCommand = async (
Expand All @@ -28,6 +30,7 @@ export const switchCommand = async (
.command('switch')
.option('--ip <ip>', 'IP Address of the New Validator', '')
.option('--switchType <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 {
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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} \\
Expand Down

0 comments on commit 74afffc

Please sign in to comment.