From 597ef8c9edac9c1ac02c533be7cbae937fceed1a Mon Sep 17 00:00:00 2001 From: chalda Date: Wed, 3 Jan 2024 16:08:23 +0100 Subject: [PATCH] [contract] changing bond authority from vote account withdrawer to validator identity (#10) * [contract] changing bond authority from vote account withdrawer to validator identity * removal vote withdrawer keypair generation in test as not needed --- .github/workflows/typescript-lint.yml | 15 ++- package.json | 2 +- packages/validator-bonds-cli/README.md | 5 +- .../test-validator/configureBond.spec.ts | 27 +++--- .../__tests__/test-validator/initBond.spec.ts | 27 +++--- .../__tests__/test-validator/show.spec.ts | 6 +- packages/validator-bonds-cli/package.json | 8 +- .../src/commands/manage/configureBond.ts | 2 +- .../src/commands/manage/initBond.ts | 26 +++--- .../__tests__/bankrun/configureBond.spec.ts | 17 ++-- .../__tests__/bankrun/fundBond.spec.ts | 6 +- .../__tests__/bankrun/initBond.spec.ts | 8 +- .../test-validator/configureBond.spec.ts | 6 +- .../__tests__/test-validator/fundBond.spec.ts | 6 +- .../__tests__/test-validator/initBond.spec.ts | 30 +++--- .../__tests__/test-validator/testValidator.ts | 71 ++++++++++++++- .../__tests__/utils/staking.ts | 32 +++++-- .../__tests__/utils/testTransactions.ts | 22 ++--- .../generated/validator_bonds.ts | 36 ++++---- packages/validator-bonds-sdk/package.json | 6 +- .../src/instructions/claimWithdrawRequest.ts | 2 +- .../src/instructions/initBond.ts | 14 +-- .../orchestrateWithdrawDeposit.ts | 2 +- pnpm-lock.yaml | 64 ++++++------- programs/validator-bonds/src/checks.rs | 91 ++++++++++--------- programs/validator-bonds/src/error.rs | 4 +- programs/validator-bonds/src/events/bond.rs | 2 +- .../src/instructions/bond/configure_bond.rs | 2 +- .../src/instructions/bond/fund_bond.rs | 2 +- .../src/instructions/bond/init_bond.rs | 12 +-- .../withdraw/cancel_withdraw_request.rs | 2 +- .../withdraw/claim_withdraw_request.rs | 12 +-- .../withdraw/init_withdraw_request.rs | 2 +- 33 files changed, 329 insertions(+), 240 deletions(-) diff --git a/.github/workflows/typescript-lint.yml b/.github/workflows/typescript-lint.yml index 435d7e4c..4677621f 100644 --- a/.github/workflows/typescript-lint.yml +++ b/.github/workflows/typescript-lint.yml @@ -9,7 +9,7 @@ on: jobs: lint-and-test: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 50 steps: - name: Checkout project uses: actions/checkout@v4 @@ -28,6 +28,19 @@ jobs: with: components: rustfmt, clippy + - name: Set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + - run: pnpm install - run: pnpm lint - run: pnpm test:cargo diff --git a/package.json b/package.json index 4e21d286..1eb5fb3b 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "4.9.5", - "@marinade.finance/jest-utils": "^2.0.20" + "@marinade.finance/jest-utils": "^2.0.21" }, "pnpm": { "peerDependencyRules": { diff --git a/packages/validator-bonds-cli/README.md b/packages/validator-bonds-cli/README.md index d65f5bbb..792c95e2 100644 --- a/packages/validator-bonds-cli/README.md +++ b/packages/validator-bonds-cli/README.md @@ -19,8 +19,9 @@ The bond account is bound to a validator vote account. ```sh # bond account at mainnet validator-bonds -um init-bond -k \ - --vote-account --vote-account-withdrawer \ - --bond-authority --rent-payer + --vote-account --validator-identity \ + --bond-authority \ + --rent-payer # to configure bond account properties validator-bonds -um configure-bond --help diff --git a/packages/validator-bonds-cli/__tests__/test-validator/configureBond.spec.ts b/packages/validator-bonds-cli/__tests__/test-validator/configureBond.spec.ts index 98732d91..387e5afd 100644 --- a/packages/validator-bonds-cli/__tests__/test-validator/configureBond.spec.ts +++ b/packages/validator-bonds-cli/__tests__/test-validator/configureBond.spec.ts @@ -12,22 +12,22 @@ import { } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/testTransactions' import { AnchorExtendedProvider, + getValidatorInfo, initTest, } from '@marinade.finance/validator-bonds-sdk/__tests__/test-validator/testValidator' -import { createVoteAccount } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/staking' +import { createVoteAccountWithIdentity } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/staking' describe('Configure bond account using CLI', () => { let provider: AnchorExtendedProvider let program: ValidatorBondsProgram - let voteWithdrawerPath: string - let voteWithdrawerKeypair: Keypair - let voteWithdrawerCleanup: () => Promise let bondAuthorityPath: string let bondAuthorityKeypair: Keypair let bondAuthorityCleanup: () => Promise let configAccount: PublicKey let bondAccount: PublicKey let voteAccount: PublicKey + let validatorIdentity: Keypair + let validatorIdentityPath: string beforeAll(async () => { shellMatchers() @@ -35,11 +35,6 @@ describe('Configure bond account using CLI', () => { }) beforeEach(async () => { - ;({ - path: voteWithdrawerPath, - keypair: voteWithdrawerKeypair, - cleanup: voteWithdrawerCleanup, - } = await createTempFileKeypair()) ;({ path: bondAuthorityPath, keypair: bondAuthorityKeypair, @@ -54,11 +49,12 @@ describe('Configure bond account using CLI', () => { expect( provider.connection.getAccountInfo(configAccount) ).resolves.not.toBeNull() - ;({ voteAccount } = await createVoteAccount( + ;({ validatorIdentity, validatorIdentityPath } = await getValidatorInfo( + provider.connection + )) + ;({ voteAccount } = await createVoteAccountWithIdentity( provider, - undefined, - undefined, - voteWithdrawerKeypair + validatorIdentity )) ;({ bondAccount } = await executeInitBondInstruction( program, @@ -66,14 +62,13 @@ describe('Configure bond account using CLI', () => { configAccount, bondAuthorityKeypair, voteAccount, - voteWithdrawerKeypair, + validatorIdentity, 33 )) }) afterEach(async () => { await bondAuthorityCleanup() - await voteWithdrawerCleanup() }) it('configure bond account', async () => { @@ -125,7 +120,7 @@ describe('Configure bond account using CLI', () => { '--vote-account', voteAccount.toBase58(), '--authority', - voteWithdrawerPath, + validatorIdentityPath, '--bond-authority', newBondAuthority.toBase58(), '--revenue-share', diff --git a/packages/validator-bonds-cli/__tests__/test-validator/initBond.spec.ts b/packages/validator-bonds-cli/__tests__/test-validator/initBond.spec.ts index 6223b661..a2d039c2 100644 --- a/packages/validator-bonds-cli/__tests__/test-validator/initBond.spec.ts +++ b/packages/validator-bonds-cli/__tests__/test-validator/initBond.spec.ts @@ -15,9 +15,10 @@ import { import { executeInitConfigInstruction } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/testTransactions' import { AnchorExtendedProvider, + getValidatorInfo, initTest, } from '@marinade.finance/validator-bonds-sdk/__tests__/test-validator/testValidator' -import { createVoteAccount } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/staking' +import { createVoteAccountWithIdentity } from '@marinade.finance/validator-bonds-sdk/__tests__/utils/staking' describe('Init bond account using CLI', () => { let provider: AnchorExtendedProvider @@ -26,11 +27,10 @@ describe('Init bond account using CLI', () => { let rentPayerKeypair: Keypair let rentPayerCleanup: () => Promise const rentPayerFunds = 10 * LAMPORTS_PER_SOL - let voteWithdrawerPath: string - let voteWithdrawerKeypair: Keypair - let voteWithdrawerCleanup: () => Promise let configAccount: PublicKey let voteAccount: PublicKey + let validatorIdentity: Keypair + let validatorIdentityPath: string beforeAll(async () => { shellMatchers() @@ -43,11 +43,6 @@ describe('Init bond account using CLI', () => { keypair: rentPayerKeypair, cleanup: rentPayerCleanup, } = await createTempFileKeypair()) - ;({ - path: voteWithdrawerPath, - keypair: voteWithdrawerKeypair, - cleanup: voteWithdrawerCleanup, - } = await createTempFileKeypair()) ;({ configAccount } = await executeInitConfigInstruction({ program, provider, @@ -57,11 +52,12 @@ describe('Init bond account using CLI', () => { expect( provider.connection.getAccountInfo(configAccount) ).resolves.not.toBeNull() - ;({ voteAccount } = await createVoteAccount( + ;({ validatorIdentity, validatorIdentityPath } = await getValidatorInfo( + provider.connection + )) + ;({ voteAccount } = await createVoteAccountWithIdentity( provider, - undefined, - undefined, - voteWithdrawerKeypair + validatorIdentity )) const tx = new Transaction().add( @@ -79,7 +75,6 @@ describe('Init bond account using CLI', () => { afterEach(async () => { await rentPayerCleanup() - await voteWithdrawerCleanup() }) it('init bond account', async () => { @@ -99,8 +94,8 @@ describe('Init bond account using CLI', () => { configAccount.toBase58(), '--vote-account', voteAccount.toBase58(), - '--vote-account-withdrawer', - voteWithdrawerPath, + '--validator-identity', + validatorIdentityPath, '--bond-authority', bondAuthority.publicKey.toBase58(), '--revenue-share', diff --git a/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts b/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts index 734a4a55..7fd630f3 100644 --- a/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts +++ b/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts @@ -207,9 +207,7 @@ describe('Show command using CLI', () => { expect( provider.connection.getAccountInfo(configAccount) ).resolves.not.toBeNull() - const { voteAccount, authorizedWithdrawer } = await createVoteAccount( - provider - ) + const { voteAccount, validatorIdentity } = await createVoteAccount(provider) const bondAuthority = Keypair.generate() const { bondAccount } = await executeInitBondInstruction( program, @@ -217,7 +215,7 @@ describe('Show command using CLI', () => { configAccount, bondAuthority, voteAccount, - authorizedWithdrawer, + validatorIdentity, 222 ) const [, bump] = bondAddress(configAccount, voteAccount, program.programId) diff --git a/packages/validator-bonds-cli/package.json b/packages/validator-bonds-cli/package.json index f75f1d97..a4436a16 100644 --- a/packages/validator-bonds-cli/package.json +++ b/packages/validator-bonds-cli/package.json @@ -27,9 +27,9 @@ "@marinade.finance/validator-bonds-sdk": "^1.0.1", "@coral-xyz/anchor": "^0.29.0", "@solana/web3.js": "^1.87.6", - "@marinade.finance/cli-common": "^2.0.20", - "@marinade.finance/anchor-common": "^2.0.20", - "@marinade.finance/web3js-common": "^2.0.20", + "@marinade.finance/cli-common": "^2.0.21", + "@marinade.finance/anchor-common": "^2.0.21", + "@marinade.finance/web3js-common": "^2.0.21", "bn.js": "^5.2.1", "jsbi": "^4.3.0", "commander": "^9.5.0", @@ -39,6 +39,6 @@ "yaml": "^2.3.3" }, "devDependencies": { - "@marinade.finance/jest-utils": "^2.0.20" + "@marinade.finance/jest-utils": "^2.0.21" } } diff --git a/packages/validator-bonds-cli/src/commands/manage/configureBond.ts b/packages/validator-bonds-cli/src/commands/manage/configureBond.ts index 9753e627..90affe92 100644 --- a/packages/validator-bonds-cli/src/commands/manage/configureBond.ts +++ b/packages/validator-bonds-cli/src/commands/manage/configureBond.ts @@ -37,7 +37,7 @@ export function installConfigureBond(program: Command) { '--authority ', 'Authority that is permitted to do changes in bonds account. ' + 'It is either the authority defined in bonds account or ' + - 'validator vote account withdrawer authority that the bond account is connected to. ' + + 'vote account validator identity that the bond account is connected to. ' + '(default: wallet keypair)', parsePubkeyOrKeypair ) diff --git a/packages/validator-bonds-cli/src/commands/manage/initBond.ts b/packages/validator-bonds-cli/src/commands/manage/initBond.ts index d79c2ddb..b1e2c16d 100644 --- a/packages/validator-bonds-cli/src/commands/manage/initBond.ts +++ b/packages/validator-bonds-cli/src/commands/manage/initBond.ts @@ -26,9 +26,9 @@ export function installInitBond(program: Command) { parsePubkey ) .option( - '--vote-account-withdrawer ', - 'Validator vote account withdrawer authority. ' + - 'To create the bond the signature of the account is needed (default: wallet keypair)', + '--validator-identity ', + 'Validator identity linked to the vote account. ' + + 'To create the bond the signature of the validator identity is needed (default: wallet keypair)', parsePubkeyOrKeypair ) .option( @@ -52,14 +52,14 @@ export function installInitBond(program: Command) { async ({ config, voteAccount, - voteAccountWithdrawer, + validatorIdentity, bondAuthority, revenueShare, rentPayer, }: { config?: Promise voteAccount: Promise - voteAccountWithdrawer?: Promise + validatorIdentity?: Promise bondAuthority: Promise revenueShare: number rentPayer?: Promise @@ -67,7 +67,7 @@ export function installInitBond(program: Command) { await manageInitBond({ config: await config, voteAccount: await voteAccount, - voteAccountWithdrawer: await voteAccountWithdrawer, + validatorIdentity: await validatorIdentity, bondAuthority: await bondAuthority, revenueShare: revenueShare, rentPayer: await rentPayer, @@ -79,14 +79,14 @@ export function installInitBond(program: Command) { async function manageInitBond({ config = CONFIG_ADDRESS, voteAccount, - voteAccountWithdrawer, + validatorIdentity, bondAuthority, revenueShare, rentPayer, }: { config?: PublicKey voteAccount: PublicKey - voteAccountWithdrawer?: PublicKey | Keypair + validatorIdentity?: PublicKey | Keypair bondAuthority: PublicKey revenueShare: number rentPayer?: PublicKey | Keypair @@ -102,10 +102,10 @@ async function manageInitBond({ signers.push(rentPayer) rentPayer = rentPayer.publicKey } - voteAccountWithdrawer = voteAccountWithdrawer || wallet.publicKey - if (voteAccountWithdrawer instanceof Keypair) { - signers.push(voteAccountWithdrawer) - voteAccountWithdrawer = voteAccountWithdrawer.publicKey + validatorIdentity = validatorIdentity || wallet.publicKey + if (validatorIdentity instanceof Keypair) { + signers.push(validatorIdentity) + validatorIdentity = validatorIdentity.publicKey } bondAuthority = bondAuthority || wallet.publicKey @@ -115,7 +115,7 @@ async function manageInitBond({ configAccount: config, bondAuthority, validatorVoteAccount: voteAccount, - validatorVoteWithdrawer: voteAccountWithdrawer, + validatorIdentity, revenueShareHundredthBps: revenueShare, rentPayer, }) diff --git a/packages/validator-bonds-sdk/__tests__/bankrun/configureBond.spec.ts b/packages/validator-bonds-sdk/__tests__/bankrun/configureBond.spec.ts index b370eed2..327c050d 100644 --- a/packages/validator-bonds-sdk/__tests__/bankrun/configureBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/bankrun/configureBond.spec.ts @@ -22,7 +22,7 @@ describe('Validator Bonds configure bond account', () => { let config: ProgramAccount let bond: ProgramAccount let bondAuthority: Keypair - let withdrawerAuthority: Keypair + let validatorIdentity: Keypair let voterAuthority: Keypair beforeAll(async () => { @@ -38,8 +38,11 @@ describe('Validator Bonds configure bond account', () => { publicKey: configAccount, account: await getConfig(program, configAccount), } - const { voteAccount, authorizedWithdrawer, authorizedVoter } = - await createVoteAccount(provider) + const { + voteAccount, + validatorIdentity: nodePubkey, + authorizedVoter, + } = await createVoteAccount(provider) bondAuthority = Keypair.generate() const { bondAccount } = await executeInitBondInstruction( program, @@ -47,14 +50,14 @@ describe('Validator Bonds configure bond account', () => { config.publicKey, bondAuthority, voteAccount, - authorizedWithdrawer, + nodePubkey, 123 ) bond = { publicKey: bondAccount, account: await getBond(program, bondAccount), } - withdrawerAuthority = authorizedWithdrawer + validatorIdentity = nodePubkey voterAuthority = authorizedVoter }) @@ -91,10 +94,10 @@ describe('Validator Bonds configure bond account', () => { const { instruction } = await configureBondInstruction({ program, bondAccount: bond.publicKey, - authority: withdrawerAuthority, + authority: validatorIdentity, newBondAuthority: newBondAuthority.publicKey, }) - await provider.sendIx([withdrawerAuthority], instruction) + await provider.sendIx([validatorIdentity], instruction) const bondData = await getBond(program, bond.publicKey) expect(bondData.config).toEqual(config.publicKey) diff --git a/packages/validator-bonds-sdk/__tests__/bankrun/fundBond.spec.ts b/packages/validator-bonds-sdk/__tests__/bankrun/fundBond.spec.ts index c4f06a2b..d08e0893 100644 --- a/packages/validator-bonds-sdk/__tests__/bankrun/fundBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/bankrun/fundBond.spec.ts @@ -50,9 +50,7 @@ describe('Validator Bonds fund bond account', () => { publicKey: configAccount, account: await getConfig(program, configAccount), } - const { voteAccount, authorizedWithdrawer } = await createVoteAccount( - provider - ) + const { voteAccount, validatorIdentity } = await createVoteAccount(provider) bondAuthority = Keypair.generate() const { bondAccount } = await executeInitBondInstruction( program, @@ -60,7 +58,7 @@ describe('Validator Bonds fund bond account', () => { config.publicKey, bondAuthority, voteAccount, - authorizedWithdrawer, + validatorIdentity, 123 ) bond = { diff --git a/packages/validator-bonds-sdk/__tests__/bankrun/initBond.spec.ts b/packages/validator-bonds-sdk/__tests__/bankrun/initBond.spec.ts index c8b8b1e8..0c85cdfe 100644 --- a/packages/validator-bonds-sdk/__tests__/bankrun/initBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/bankrun/initBond.spec.ts @@ -41,9 +41,7 @@ describe('Validator Bonds init bond account', () => { it('init bond', async () => { const bondAuthority = Keypair.generate() - const { voteAccount, authorizedWithdrawer } = await createVoteAccount( - provider - ) + const { voteAccount, validatorIdentity } = await createVoteAccount(provider) const rentWallet = await createUserAndFund( provider, Keypair.generate(), @@ -55,10 +53,10 @@ describe('Validator Bonds init bond account', () => { bondAuthority: bondAuthority.publicKey, revenueShareHundredthBps: 30, validatorVoteAccount: voteAccount, - validatorVoteWithdrawer: authorizedWithdrawer.publicKey, + validatorIdentity: validatorIdentity.publicKey, rentPayer: rentWallet.publicKey, }) - await provider.sendIx([rentWallet, authorizedWithdrawer], instruction) + await provider.sendIx([rentWallet, validatorIdentity], instruction) const rentWalletInfo = await provider.connection.getAccountInfo( rentWallet.publicKey diff --git a/packages/validator-bonds-sdk/__tests__/test-validator/configureBond.spec.ts b/packages/validator-bonds-sdk/__tests__/test-validator/configureBond.spec.ts index 85f24786..8072d8df 100644 --- a/packages/validator-bonds-sdk/__tests__/test-validator/configureBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/test-validator/configureBond.spec.ts @@ -6,7 +6,7 @@ import { configureBondInstruction, getBond, } from '../../src' -import { initTest } from './testValidator' +import { getValidatorInfo, initTest } from './testValidator' import { executeInitBondInstruction, executeInitConfigInstruction, @@ -16,10 +16,12 @@ import { ExtendedProvider } from '../utils/provider' describe('Validator Bonds configure bond', () => { let provider: ExtendedProvider let program: ValidatorBondsProgram + let validatorIdentity: Keypair let configAccount: PublicKey beforeAll(async () => { ;({ provider, program } = await initTest()) + ;({ validatorIdentity } = await getValidatorInfo(provider.connection)) }) afterAll(async () => { @@ -41,7 +43,7 @@ describe('Validator Bonds configure bond', () => { configAccount, undefined, undefined, - undefined, + validatorIdentity, 22 ) diff --git a/packages/validator-bonds-sdk/__tests__/test-validator/fundBond.spec.ts b/packages/validator-bonds-sdk/__tests__/test-validator/fundBond.spec.ts index 6333846c..67424664 100644 --- a/packages/validator-bonds-sdk/__tests__/test-validator/fundBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/test-validator/fundBond.spec.ts @@ -36,9 +36,7 @@ describe('Validator Bonds fund bond', () => { program, provider, })) - const { voteAccount, authorizedWithdrawer } = await createVoteAccount( - provider - ) + const { voteAccount, validatorIdentity } = await createVoteAccount(provider) validatorVoteAccount = voteAccount ;({ bondAccount } = await executeInitBondInstruction( program, @@ -46,7 +44,7 @@ describe('Validator Bonds fund bond', () => { configAccount, undefined, validatorVoteAccount, - authorizedWithdrawer + validatorIdentity )) }) diff --git a/packages/validator-bonds-sdk/__tests__/test-validator/initBond.spec.ts b/packages/validator-bonds-sdk/__tests__/test-validator/initBond.spec.ts index 351d66d1..5556f663 100644 --- a/packages/validator-bonds-sdk/__tests__/test-validator/initBond.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/test-validator/initBond.spec.ts @@ -8,22 +8,24 @@ import { getBond, initBondInstruction, } from '../../src' -import { initTest } from './testValidator' +import { getValidatorInfo, initTest } from './testValidator' import { transaction } from '@marinade.finance/anchor-common' import { Wallet, splitAndExecuteTx } from '@marinade.finance/web3js-common' import { signer } from '../utils/helpers' import { executeInitConfigInstruction } from '../utils/testTransactions' import { ExtendedProvider } from '../utils/provider' -import { createVoteAccount } from '../utils/staking' +import { createVoteAccountWithIdentity } from '../utils/staking' import { AnchorProvider } from '@coral-xyz/anchor' describe('Validator Bonds init bond', () => { let provider: ExtendedProvider let program: ValidatorBondsProgram + let validatorIdentity: Keypair let configAccount: PublicKey beforeAll(async () => { ;({ provider, program } = await initTest()) + ;({ validatorIdentity } = await getValidatorInfo(provider.connection)) }) afterAll(async () => { @@ -49,8 +51,8 @@ describe('Validator Bonds init bond', () => { ) }) - const { voteAccount: validatorVoteAccount, authorizedWithdrawer } = - await createVoteAccount(provider) + const { voteAccount: validatorVoteAccount } = + await createVoteAccountWithIdentity(provider, validatorIdentity) const bondAuthority = PublicKey.unique() const { instruction, bondAccount } = await initBondInstruction({ program, @@ -58,9 +60,9 @@ describe('Validator Bonds init bond', () => { bondAuthority, revenueShareHundredthBps: 22, validatorVoteAccount, - validatorVoteWithdrawer: authorizedWithdrawer.publicKey, + validatorIdentity: validatorIdentity.publicKey, }) - await provider.sendIx([authorizedWithdrawer], instruction) + await provider.sendIx([validatorIdentity], instruction) const bondsDataFromList = await findBonds({ program, @@ -91,7 +93,7 @@ describe('Validator Bonds init bond', () => { expect(e.configAddress).toEqual(configAccount) expect(e.revenueShare).toEqual({ hundredthBps: 22 }) expect(e.validatorVoteAccount).toEqual(validatorVoteAccount) - expect(e.validatorVoteWithdrawer).toEqual(authorizedWithdrawer.publicKey) + expect(e.validatorIdentity).toEqual(validatorIdentity.publicKey) }) }) @@ -107,25 +109,25 @@ describe('Validator Bonds init bond', () => { const voteAccounts: [PublicKey, Keypair][] = [] for (let i = 1; i <= numberOfBonds; i++) { - const { voteAccount: validatorVoteAccount, authorizedWithdrawer } = - await createVoteAccount(provider) - voteAccounts.push([validatorVoteAccount, authorizedWithdrawer]) - signers.push(signer(authorizedWithdrawer)) + const { voteAccount: validatorVoteAccount } = + await createVoteAccountWithIdentity(provider, validatorIdentity) + voteAccounts.push([validatorVoteAccount, validatorIdentity]) + signers.push(signer(validatorIdentity)) } for (let i = 1; i <= numberOfBonds; i++) { - const [validatorVoteAccount, validatorVoteWithdrawer] = - voteAccounts[i - 1] + const [validatorVoteAccount, nodeIdentity] = voteAccounts[i - 1] const { instruction } = await initBondInstruction({ program, configAccount, bondAuthority: bondAuthority, revenueShareHundredthBps: 100, validatorVoteAccount, - validatorVoteWithdrawer, + validatorIdentity: nodeIdentity, }) tx.add(instruction) } + expect(tx.instructions.length).toEqual(numberOfBonds) await splitAndExecuteTx({ connection: provider.connection, transaction: tx, diff --git a/packages/validator-bonds-sdk/__tests__/test-validator/testValidator.ts b/packages/validator-bonds-sdk/__tests__/test-validator/testValidator.ts index de4632c4..59ba6554 100644 --- a/packages/validator-bonds-sdk/__tests__/test-validator/testValidator.ts +++ b/packages/validator-bonds-sdk/__tests__/test-validator/testValidator.ts @@ -5,6 +5,7 @@ import { ValidatorBondsProgram, getProgram, getStakeAccount } from '../../src' import { ExtendedProvider } from '../utils/provider' import { Connection, + Keypair, PublicKey, Signer, Transaction, @@ -14,6 +15,8 @@ import { import { transaction } from '@marinade.finance/anchor-common' import { executeTxSimple } from '@marinade.finance/web3js-common' import { sleep } from '../utils/helpers' +import { readFile } from 'fs/promises' +import fs from 'fs' export class AnchorExtendedProvider extends AnchorProvider @@ -51,7 +54,12 @@ export async function initTest(): Promise<{ return { program: getProgram(provider), provider } } -// NOTE: the Anchor.toml configures slots_per_epoch to 32, +export function getValidatorIdentity(): Keypair { + return Keypair.generate() +} + +// NOTE: the Anchor.toml configures slots_per_epoch to 32, otherwise +// waiting for activation will be pretty long and this method probably timeouts export async function waitForStakeAccountActivation({ stakeAccount, connection, @@ -120,3 +128,64 @@ export async function waitForStakeAccountActivation({ } } } + +export async function getValidatorInfo(connection: Connection): Promise<{ + votePubkey: PublicKey + validatorIdentity: Keypair + validatorIdentityPath: string +}> { + // loading the test validator identity key pair, we expect the Anchor paths are defaults + // and that the tests is run with `pnpm test` from the root directory + const testIdentityKeypairPath = + process.cwd() + '/.anchor/test-ledger/validator-keypair.json' + if (!fs.existsSync(testIdentityKeypairPath)) { + throw new Error( + `Expected test validator identity key pair at ${testIdentityKeypairPath} but file not found` + ) + } + const validatorIdentityPath = testIdentityKeypairPath + const validatorIdentity = await parseKeypair(testIdentityKeypairPath) + + // let's verify the leader schedule matches the validator identity + const leaderSchedule = await connection.getLeaderSchedule() + const isScheduledOnlyTestValidator = Object.keys(leaderSchedule).every( + address => address === validatorIdentity.publicKey.toBase58() + ) + if (!isScheduledOnlyTestValidator) { + throw new Error( + 'Error on global setup: expected only test validator being run and scheduled as leader' + ) + } + + const voteAccounts = await connection.getVoteAccounts() + // expecting run on localhost and only one voting vote account is available + // i.e., one validator solana-test-validator is voting and the validator identity is the same + if (voteAccounts.current.length !== 1) { + throw new Error( + 'Expected one vote account of solana-test-validator. Cannot continue in global local test setup.' + + ` Number of vote accounts found: ${voteAccounts.current.length}` + ) + } + const votePubkey = new PublicKey(voteAccounts.current[0].votePubkey) + if ( + voteAccounts.current[0].nodePubkey !== + validatorIdentity.publicKey.toBase58() + ) { + throw new Error( + `Expected validator identity ${validatorIdentity.publicKey.toBase58()} to be the same as the vote account node pubkey ${ + voteAccounts.current[0].nodePubkey + }` + ) + } + + return { + votePubkey, + validatorIdentity, + validatorIdentityPath, + } +} + +async function parseKeypair(path: string): Promise { + const fileContent = await readFile(path, 'utf-8') + return Keypair.fromSecretKey(new Uint8Array(JSON.parse(fileContent))) +} diff --git a/packages/validator-bonds-sdk/__tests__/utils/staking.ts b/packages/validator-bonds-sdk/__tests__/utils/staking.ts index a4e71c33..529a2bff 100644 --- a/packages/validator-bonds-sdk/__tests__/utils/staking.ts +++ b/packages/validator-bonds-sdk/__tests__/utils/staking.ts @@ -173,21 +173,35 @@ export async function getAndCheckStakeAccount( // ----- ENHANCED PROVIDER ----- export type VoteAccountKeys = { voteAccount: PublicKey - nodeIdentity: Keypair + validatorIdentity: Keypair authorizedVoter: Keypair authorizedWithdrawer: Keypair } +export async function createVoteAccountWithIdentity( + provider: ExtendedProvider, + validatorIdentity: Keypair +): Promise { + return await createVoteAccount( + provider, + undefined, + undefined, + undefined, + validatorIdentity + ) +} + export async function createVoteAccount( provider: ExtendedProvider, rentExempt?: number, authorizedVoter?: Keypair, - authorizedWithdrawer?: Keypair + authorizedWithdrawer?: Keypair, + validatorIdentity?: Keypair ): Promise { rentExempt = await getRentExemptVote(provider, rentExempt) const voteAccount = Keypair.generate() - const nodeIdentity = Keypair.generate() + validatorIdentity = validatorIdentity || Keypair.generate() authorizedVoter = authorizedVoter || Keypair.generate() authorizedWithdrawer = authorizedWithdrawer || Keypair.generate() @@ -200,19 +214,23 @@ export async function createVoteAccount( }) const ixInitialize = VoteProgram.initializeAccount({ votePubkey: voteAccount.publicKey, - nodePubkey: nodeIdentity.publicKey, + nodePubkey: validatorIdentity.publicKey, voteInit: { authorizedVoter: authorizedVoter.publicKey, authorizedWithdrawer: authorizedWithdrawer.publicKey, commission: 0, - nodePubkey: nodeIdentity.publicKey, + nodePubkey: validatorIdentity.publicKey, }, }) - await provider.sendIx([voteAccount, nodeIdentity], ixCreate, ixInitialize) + await provider.sendIx( + [voteAccount, validatorIdentity], + ixCreate, + ixInitialize + ) return { voteAccount: voteAccount.publicKey, - nodeIdentity, + validatorIdentity, authorizedVoter, authorizedWithdrawer, } diff --git a/packages/validator-bonds-sdk/__tests__/utils/testTransactions.ts b/packages/validator-bonds-sdk/__tests__/utils/testTransactions.ts index 6a4b6fdd..a15a0d89 100644 --- a/packages/validator-bonds-sdk/__tests__/utils/testTransactions.ts +++ b/packages/validator-bonds-sdk/__tests__/utils/testTransactions.ts @@ -129,20 +129,20 @@ export async function executeInitBondInstruction( config: PublicKey, bondAuthority?: Keypair, voteAccount?: PublicKey, - authorizedWithdrawer?: Keypair, + validatorIdentity?: Keypair, revenueShareHundredthBps: BN | number = Math.floor(Math.random() * 100) + 1 ): Promise<{ bondAccount: PublicKey bondAuthority: Keypair voteAccount: PublicKey - authorizedWithdrawer: Keypair + validatorIdentity: Keypair }> { bondAuthority = bondAuthority || Keypair.generate() if (!voteAccount) { - ;({ voteAccount, authorizedWithdrawer } = await createVoteAccount(provider)) + ;({ voteAccount, validatorIdentity } = await createVoteAccount(provider)) } - if (authorizedWithdrawer === undefined) { - throw new Error('authorizedWithdrawer is undefined') + if (validatorIdentity === undefined) { + throw new Error('nodeIdentity is undefined') } const { instruction, bondAccount } = await initBondInstruction({ program, @@ -150,21 +150,19 @@ export async function executeInitBondInstruction( bondAuthority: bondAuthority.publicKey, revenueShareHundredthBps, validatorVoteAccount: voteAccount, - validatorVoteWithdrawer: authorizedWithdrawer.publicKey, + validatorIdentity: validatorIdentity.publicKey, }) try { - await provider.sendIx([authorizedWithdrawer], instruction) + await provider.sendIx([validatorIdentity], instruction) } catch (e) { console.error( `executeInitBondInstruction: bond account ${pubkey( bondAccount ).toBase58()}, ` + `config: ${pubkey(config).toBase58()}, ` + - `bondAuthority: ${pubkey(bondAuthority.publicKey).toBase58()}, ` + + `bondAuthority: ${pubkey(bondAuthority).toBase58()}, ` + `voteAccount: ${pubkey(voteAccount).toBase58()}, ` + - `authorizedWithdrawer: ${pubkey( - authorizedWithdrawer.publicKey - ).toBase58()}`, + `validatorIdentity: ${pubkey(validatorIdentity).toBase58()}`, e ) throw e @@ -174,6 +172,6 @@ export async function executeInitBondInstruction( bondAccount, bondAuthority, voteAccount, - authorizedWithdrawer, + validatorIdentity, } } diff --git a/packages/validator-bonds-sdk/generated/validator_bonds.ts b/packages/validator-bonds-sdk/generated/validator_bonds.ts index a5edecde..c8b26104 100644 --- a/packages/validator-bonds-sdk/generated/validator_bonds.ts +++ b/packages/validator-bonds-sdk/generated/validator_bonds.ts @@ -122,11 +122,11 @@ export type ValidatorBonds = { "isSigner": false }, { - "name": "authorizedWithdrawer", + "name": "validatorIdentity", "isMut": false, "isSigner": true, "docs": [ - "only validator vote account withdrawer authority may can create the bond" + "only validator vote account validator identity may create the bond" ] }, { @@ -213,7 +213,7 @@ export type ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may change the account" + "validator vote account validator identity or bond authority may change the account" ] }, { @@ -276,7 +276,7 @@ export type ValidatorBonds = { "isMut": false, "isSigner": false, "docs": [ - "new authority owner, it's the bonds program" + "new owner of the stake account, it's the bonds program PDA" ], "pda": { "seeds": [ @@ -378,7 +378,7 @@ export type ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may ask for the withdrawal" + "validator vote account node identity or bond authority may ask for the withdrawal" ] }, { @@ -470,7 +470,7 @@ export type ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may ask for cancelling" + "validator vote account validator identity or bond authority may ask for cancelling" ] }, { @@ -2213,7 +2213,7 @@ export type ValidatorBonds = { "index": false }, { - "name": "validatorVoteWithdrawer", + "name": "validatorIdentity", "type": "publicKey", "index": false }, @@ -3044,8 +3044,8 @@ export type ValidatorBonds = { }, { "code": 6035, - "name": "ValidatorVoteAccountOwnerMismatch", - "msg": "Owner of validator vote account does not match with the provided owner signature" + "name": "VoteAccountValidatorIdentityMismatch", + "msg": "Validator vote account does not match to provided validator identity signature" }, { "code": 6036, @@ -3229,11 +3229,11 @@ export const IDL: ValidatorBonds = { "isSigner": false }, { - "name": "authorizedWithdrawer", + "name": "validatorIdentity", "isMut": false, "isSigner": true, "docs": [ - "only validator vote account withdrawer authority may can create the bond" + "only validator vote account validator identity may create the bond" ] }, { @@ -3320,7 +3320,7 @@ export const IDL: ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may change the account" + "validator vote account validator identity or bond authority may change the account" ] }, { @@ -3383,7 +3383,7 @@ export const IDL: ValidatorBonds = { "isMut": false, "isSigner": false, "docs": [ - "new authority owner, it's the bonds program" + "new owner of the stake account, it's the bonds program PDA" ], "pda": { "seeds": [ @@ -3485,7 +3485,7 @@ export const IDL: ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may ask for the withdrawal" + "validator vote account node identity or bond authority may ask for the withdrawal" ] }, { @@ -3577,7 +3577,7 @@ export const IDL: ValidatorBonds = { "isMut": false, "isSigner": true, "docs": [ - "validator vote account owner or bond authority may ask for cancelling" + "validator vote account validator identity or bond authority may ask for cancelling" ] }, { @@ -5320,7 +5320,7 @@ export const IDL: ValidatorBonds = { "index": false }, { - "name": "validatorVoteWithdrawer", + "name": "validatorIdentity", "type": "publicKey", "index": false }, @@ -6151,8 +6151,8 @@ export const IDL: ValidatorBonds = { }, { "code": 6035, - "name": "ValidatorVoteAccountOwnerMismatch", - "msg": "Owner of validator vote account does not match with the provided owner signature" + "name": "VoteAccountValidatorIdentityMismatch", + "msg": "Validator vote account does not match to provided validator identity signature" }, { "code": 6036, diff --git a/packages/validator-bonds-sdk/package.json b/packages/validator-bonds-sdk/package.json index 13bbc427..8e7a599b 100644 --- a/packages/validator-bonds-sdk/package.json +++ b/packages/validator-bonds-sdk/package.json @@ -28,10 +28,10 @@ ], "devDependencies": { "@coral-xyz/anchor": "^0.29.0", - "@marinade.finance/anchor-common": "^2.0.20", + "@marinade.finance/anchor-common": "^2.0.21", "@marinade.finance/marinade-ts-sdk": "^5.0.6", - "@marinade.finance/ts-common": "^2.0.20", - "@marinade.finance/web3js-common": "^2.0.20", + "@marinade.finance/ts-common": "^2.0.21", + "@marinade.finance/web3js-common": "^2.0.21", "@solana/buffer-layout": "^4.0.1", "@solana/web3.js": "^1.87.6", "anchor-bankrun": "^0.3.0", diff --git a/packages/validator-bonds-sdk/src/instructions/claimWithdrawRequest.ts b/packages/validator-bonds-sdk/src/instructions/claimWithdrawRequest.ts index 5934336c..7ee328a3 100644 --- a/packages/validator-bonds-sdk/src/instructions/claimWithdrawRequest.ts +++ b/packages/validator-bonds-sdk/src/instructions/claimWithdrawRequest.ts @@ -91,7 +91,7 @@ export async function claimWithdrawRequestInstruction({ program, withdrawRequestData.validatorVoteAccount ) - withdrawer = voteAccountData.account.data.authorizedWithdrawer + withdrawer = voteAccountData.account.data.nodePubkey } splitStakeRentPayer = diff --git a/packages/validator-bonds-sdk/src/instructions/initBond.ts b/packages/validator-bonds-sdk/src/instructions/initBond.ts index e04f7aa6..6c5c5210 100644 --- a/packages/validator-bonds-sdk/src/instructions/initBond.ts +++ b/packages/validator-bonds-sdk/src/instructions/initBond.ts @@ -12,7 +12,7 @@ export async function initBondInstruction({ program, configAccount = CONFIG_ADDRESS, validatorVoteAccount, - validatorVoteWithdrawer = walletPubkey(program), + validatorIdentity = walletPubkey(program), bondAuthority = walletPubkey(program), revenueShareHundredthBps, rentPayer = walletPubkey(program), @@ -20,7 +20,7 @@ export async function initBondInstruction({ program: ValidatorBondsProgram configAccount?: PublicKey validatorVoteAccount: PublicKey - validatorVoteWithdrawer?: PublicKey | Keypair | Signer // signer + validatorIdentity?: PublicKey | Keypair | Signer // signer bondAuthority?: PublicKey revenueShareHundredthBps: BN | number rentPayer?: PublicKey | Keypair | Signer // signer @@ -28,10 +28,10 @@ export async function initBondInstruction({ instruction: TransactionInstruction bondAccount: PublicKey }> { - const authorizedWithdrawer = - validatorVoteWithdrawer instanceof PublicKey - ? validatorVoteWithdrawer - : validatorVoteWithdrawer.publicKey + validatorIdentity = + validatorIdentity instanceof PublicKey + ? validatorIdentity + : validatorIdentity.publicKey const renPayerPubkey = rentPayer instanceof PublicKey ? rentPayer : rentPayer.publicKey const [bondAccount] = bondAddress( @@ -53,7 +53,7 @@ export async function initBondInstruction({ config: configAccount, bond: bondAccount, validatorVoteAccount, - authorizedWithdrawer, + validatorIdentity, rentPayer: renPayerPubkey, }) .instruction() diff --git a/packages/validator-bonds-sdk/src/orchestrators/orchestrateWithdrawDeposit.ts b/packages/validator-bonds-sdk/src/orchestrators/orchestrateWithdrawDeposit.ts index 411d8202..e2ebd0eb 100644 --- a/packages/validator-bonds-sdk/src/orchestrators/orchestrateWithdrawDeposit.ts +++ b/packages/validator-bonds-sdk/src/orchestrators/orchestrateWithdrawDeposit.ts @@ -80,7 +80,7 @@ export async function orchestrateWithdrawDeposit({ program, withdrawRequestData.validatorVoteAccount ) - const withdrawer = voteAccountData.account.data.authorizedWithdrawer + const withdrawer = voteAccountData.account.data.nodePubkey const configAccount = bondData.config let amountToWithdraw = withdrawRequestData.requestedAmount.sub( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd0e8d5e..58c09c30 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^29.7.0 version: 29.7.0 '@marinade.finance/jest-utils': - specifier: ^2.0.20 - version: 2.0.20(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2) + specifier: ^2.0.21 + version: 2.0.21(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2) '@types/bn.js': specifier: ^5.1.3 version: 5.1.3 @@ -45,17 +45,17 @@ importers: specifier: ^0.29.0 version: 0.29.0 '@marinade.finance/anchor-common': - specifier: ^2.0.20 - version: 2.0.20(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1) + specifier: ^2.0.21 + version: 2.0.21(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1) '@marinade.finance/cli-common': - specifier: ^2.0.20 - version: 2.0.20(@marinade.finance/web3js-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0)(expand-tilde@2.0.2)(pino@8.16.1)(yaml@2.3.3) + specifier: ^2.0.21 + version: 2.0.21(@marinade.finance/web3js-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0)(expand-tilde@2.0.2)(pino@8.16.1)(yaml@2.3.3) '@marinade.finance/validator-bonds-sdk': specifier: ^1.0.1 version: link:../validator-bonds-sdk '@marinade.finance/web3js-common': - specifier: ^2.0.20 - version: 2.0.20(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) + specifier: ^2.0.21 + version: 2.0.21(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) '@solana/web3.js': specifier: ^1.87.6 version: 1.87.6 @@ -82,8 +82,8 @@ importers: version: 2.3.3 devDependencies: '@marinade.finance/jest-utils': - specifier: ^2.0.20 - version: 2.0.20(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2) + specifier: ^2.0.21 + version: 2.0.21(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2) packages/validator-bonds-sdk: dependencies: @@ -95,17 +95,17 @@ importers: specifier: ^0.29.0 version: 0.29.0 '@marinade.finance/anchor-common': - specifier: ^2.0.20 - version: 2.0.20(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1) + specifier: ^2.0.21 + version: 2.0.21(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1) '@marinade.finance/marinade-ts-sdk': specifier: ^5.0.6 version: 5.0.6(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jsbi@4.3.0) '@marinade.finance/ts-common': - specifier: ^2.0.20 - version: 2.0.20 + specifier: ^2.0.21 + version: 2.0.21 '@marinade.finance/web3js-common': - specifier: ^2.0.20 - version: 2.0.20(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) + specifier: ^2.0.21 + version: 2.0.21(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) '@solana/buffer-layout': specifier: ^4.0.1 version: 4.0.1 @@ -917,24 +917,24 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@marinade.finance/anchor-common@2.0.20(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1): - resolution: {integrity: sha512-o1vw2SevnHE+eRWwyMqwSqFk4RMXPR5ZqAYCAEZBkzcw7QJI0K1BCq3ER1LCMjLPc1gpkO27Dq8vNN6r1cmXbg==} + /@marinade.finance/anchor-common@2.0.21(@coral-xyz/anchor@0.29.0)(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1): + resolution: {integrity: sha512-QwPrjqk6uLnFefW9m4ib2VBvf3DoOXF5/YipRGV0/ZKPTVYk+WdePw8srVcr2NauJ/aDY5hSe8Y02zwlkn/1yA==} peerDependencies: '@coral-xyz/anchor': ^0.28.0 || 0.29 - '@marinade.finance/ts-common': ^2.0.20 + '@marinade.finance/ts-common': ^2.0.21 '@solana/web3.js': ^1.78.5 bn.js: ^5.2.1 dependencies: '@coral-xyz/anchor': 0.29.0 - '@marinade.finance/ts-common': 2.0.20 + '@marinade.finance/ts-common': 2.0.21 '@solana/web3.js': 1.87.6 bn.js: 5.2.1 - /@marinade.finance/cli-common@2.0.20(@marinade.finance/web3js-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0)(expand-tilde@2.0.2)(pino@8.16.1)(yaml@2.3.3): - resolution: {integrity: sha512-UBnoE3thCu0VxIV9QSpIVzg3SyaPi65NQH8WCzsfjuacnV0/0vLUl253/rvpbe7TPYGoZDiykAQRyXUtYBdWjQ==} + /@marinade.finance/cli-common@2.0.21(@marinade.finance/web3js-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0)(expand-tilde@2.0.2)(pino@8.16.1)(yaml@2.3.3): + resolution: {integrity: sha512-PDob845+X/dZJK9AKtANjFPpQ0N3e8vLBpjGLHE51UZY3mWgwSdntSazRLNV4H7E1SC9Ihl/oKxJt4nKx3Lzzw==} engines: {node: '>=16.0.0'} peerDependencies: - '@marinade.finance/web3js-common': ^2.0.20 + '@marinade.finance/web3js-common': ^2.0.21 '@solana/web3.js': ^1.78.5 bn.js: ^5.2.1 borsh: ^0.7.0 @@ -943,7 +943,7 @@ packages: pino: ^8.15.1 yaml: ^2.3.2 dependencies: - '@marinade.finance/web3js-common': 2.0.20(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) + '@marinade.finance/web3js-common': 2.0.21(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0) '@solana/web3.js': 1.87.6 bn.js: 5.2.1 borsh: 0.7.0 @@ -972,8 +972,8 @@ packages: - utf-8-validate dev: true - /@marinade.finance/jest-utils@2.0.20(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2): - resolution: {integrity: sha512-X//GglnqlkWSSng85ak011gHRyzM10Pfg/hKrJUR3SlzUtbUxP4ZbK6NRnAI2hhBPjW/GEY/ete/nndm+qxQgw==} + /@marinade.finance/jest-utils@2.0.21(@jest/globals@29.7.0)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(jest-shell-matchers@1.0.2): + resolution: {integrity: sha512-a0TaESqYlEfU9VkAuvzJr5VosBAkWws3RRQAJjjTssTVXm54oZ1wgpSB6s+peu7ESYvgFom3IoniY/CnKOrr+Q==} peerDependencies: '@jest/globals': ^29.5.0 '@solana/web3.js': ^1.78.4 @@ -1018,20 +1018,20 @@ packages: - utf-8-validate dev: true - /@marinade.finance/ts-common@2.0.20: - resolution: {integrity: sha512-DOr3VNCVxADBlQQ2tLDJEgpF974dsw8lwMvNObxLVyHhckR2aWnT+bIIxuuQxB+XqbRvyyS/gc2kXOKaQzu4Rg==} + /@marinade.finance/ts-common@2.0.21: + resolution: {integrity: sha512-OOoRlmcL5UVHqrM/nhK90c6JYrjJygrhOMqsl1/RcBxm5AbbcQv8Nbt2XHRJ7A844BkGCLuqUQh635N8t1jA4w==} - /@marinade.finance/web3js-common@2.0.20(@marinade.finance/ts-common@2.0.20)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0): - resolution: {integrity: sha512-TBxIUfo0YDOU6MH3iZPQPuRuyKV7WMiFZPdOVE/01rj67TIivlVTiAFM4qOj+hIrNU93zrkGn4Bv2HTx8KbtDg==} + /@marinade.finance/web3js-common@2.0.21(@marinade.finance/ts-common@2.0.21)(@solana/web3.js@1.87.6)(bn.js@5.2.1)(borsh@0.7.0)(bs58@5.0.0): + resolution: {integrity: sha512-iPeozPDUzYpWPtkmXa+UBHg7KgIl8rwj2GHeuvI81AOKgO8Zt8uO1nGwfkSW0NPTWwql2gN/oyjQ5fm6zjPa9w==} engines: {node: '>=16.0.0'} peerDependencies: - '@marinade.finance/ts-common': 2.0.20 + '@marinade.finance/ts-common': 2.0.21 '@solana/web3.js': ^1.78.5 bn.js: ^5.2.1 borsh: ^0.7.0 bs58: ^5.0.0 dependencies: - '@marinade.finance/ts-common': 2.0.20 + '@marinade.finance/ts-common': 2.0.21 '@solana/web3.js': 1.87.6 bn.js: 5.2.1 borsh: 0.7.0 diff --git a/programs/validator-bonds/src/checks.rs b/programs/validator-bonds/src/checks.rs index d6a410fd..74d0feb5 100644 --- a/programs/validator-bonds/src/checks.rs +++ b/programs/validator-bonds/src/checks.rs @@ -9,46 +9,56 @@ use anchor_lang::solana_program::vote::program::id as vote_program_id; use anchor_spl::stake::StakeAccount; use std::ops::Deref; -/// Verification the account is owned by vote program + matching withdrawer authority (owner) -pub fn check_validator_vote_account_withdrawer_authority( +/// Verification the account is owned by vote program + matching validator identity +pub fn check_validator_vote_account_validator_identity( validator_vote_account: &UncheckedAccount, - expected_owner: &Pubkey, + expected_validator_identity: &Pubkey, ) -> Result<()> { + // https://github.com/solana-labs/solana/blob/v1.17.10/sdk/program/src/vote/state/mod.rs#L287 + let node_pubkey = + check_validator_vote_account_pubkey(validator_vote_account, 4, "validator identity")?; + require_keys_eq!( + *expected_validator_identity, + node_pubkey, + ErrorCode::VoteAccountValidatorIdentityMismatch + ); + Ok(()) +} + +fn check_validator_vote_account_pubkey( + validator_vote_account: &UncheckedAccount, + byte_position: usize, + pubkey_name: &str, +) -> Result { require!( validator_vote_account.owner == &vote_program_id(), ErrorCode::InvalidVoteAccountProgramId ); let validator_vote_data = &validator_vote_account.data.borrow()[..]; - // let's find position of the authorized withdrawer within the vote state account data + // let's find position of the pubkey within the vote state account data // https://github.com/solana-labs/solana/pull/30515 // https://github.com/solana-labs/solana/blob/v1.17.10/sdk/program/src/vote/state/mod.rs#L290 - let pos = 36; - if validator_vote_data.len() < pos + 32 { + if validator_vote_data.len() < byte_position + 32 { msg!( - "Cannot get withdrawer authority from vote account {} data", - validator_vote_account.key + "Cannot get {} from vote account {} data", + pubkey_name, + validator_vote_account.key, ); return Err(ErrorCode::FailedToDeserializeVoteAccount.into()); } - let withdrawer_slice: [u8; 32] = - validator_vote_data[pos..pos + 32] - .try_into() - .map_err(|err| { - msg!( - "Cannot get withdrawer authority from vote account {} data: {:?}", - validator_vote_account.key, - err - ); - error!(ErrorCode::FailedToDeserializeVoteAccount) - .with_values(("validator_vote_account", validator_vote_account.key())) - })?; - let authorized_withdrawer = Pubkey::from(withdrawer_slice); - require_keys_eq!( - *expected_owner, - authorized_withdrawer, - ErrorCode::ValidatorVoteAccountOwnerMismatch - ); - Ok(()) + let pubkey_slice: [u8; 32] = validator_vote_data[byte_position..byte_position + 32] + .try_into() + .map_err(|err| { + msg!( + "Cannot get {} from vote account {} data: {:?}", + pubkey_name, + validator_vote_account.key, + err + ); + error!(ErrorCode::FailedToDeserializeVoteAccount) + .with_values(("validator_vote_account", validator_vote_account.key())) + })?; + Ok(Pubkey::from(pubkey_slice)) } /// Bond account change is permitted to bond authority or validator vote account owner @@ -60,7 +70,7 @@ pub fn check_bond_change_permitted( if authority == &bond_account.authority.key() { true } else { - check_validator_vote_account_withdrawer_authority(validator_vote_account, authority) + check_validator_vote_account_validator_identity(validator_vote_account, authority) .map_or(false, |_| true) } } @@ -187,7 +197,7 @@ mod tests { ); let wrong_owner_account = UncheckedAccount::try_from(&account); assert_eq!( - check_validator_vote_account_withdrawer_authority( + check_validator_vote_account_validator_identity( &wrong_owner_account, &vote_init.authorized_voter, ), @@ -207,24 +217,15 @@ mod tests { ); let unchecked_account = UncheckedAccount::try_from(&account); - check_validator_vote_account_withdrawer_authority( - &unchecked_account, - &vote_init.authorized_withdrawer, - ) - .unwrap(); + check_validator_vote_account_validator_identity(&unchecked_account, &vote_init.node_pubkey) + .unwrap(); assert_eq!( - check_validator_vote_account_withdrawer_authority( - &unchecked_account, - &vote_init.authorized_voter, - ), - Err(ErrorCode::ValidatorVoteAccountOwnerMismatch.into()) + check_validator_vote_account_validator_identity(&unchecked_account, &Pubkey::default(),), + Err(ErrorCode::VoteAccountValidatorIdentityMismatch.into()) ); assert_eq!( - check_validator_vote_account_withdrawer_authority( - &unchecked_account, - &Pubkey::default(), - ), - Err(ErrorCode::ValidatorVoteAccountOwnerMismatch.into()) + check_validator_vote_account_validator_identity(&unchecked_account, &Pubkey::default(),), + Err(ErrorCode::VoteAccountValidatorIdentityMismatch.into()) ); } @@ -256,7 +257,7 @@ mod tests { &unchecked_account, )); assert!(check_bond_change_permitted( - &vote_init.authorized_withdrawer, + &vote_init.node_pubkey, &Bond { authority: Pubkey::new_unique(), ..Bond::default() diff --git a/programs/validator-bonds/src/error.rs b/programs/validator-bonds/src/error.rs index e4c785f8..354c3b26 100644 --- a/programs/validator-bonds/src/error.rs +++ b/programs/validator-bonds/src/error.rs @@ -107,8 +107,8 @@ pub enum ErrorCode { #[msg("Provided stake account is not funded under a settlement")] StakeAccountNotFunded, // 6034 0x1792 - #[msg("Owner of validator vote account does not match with the provided owner signature")] - ValidatorVoteAccountOwnerMismatch, // 6035 0x1793 + #[msg("Validator vote account does not match to provided validator identity signature")] + VoteAccountValidatorIdentityMismatch, // 6035 0x1793 #[msg("Bond vote account address does not match with the provided validator vote account")] VoteAccountMismatch, // 6036 0x1794 diff --git a/programs/validator-bonds/src/events/bond.rs b/programs/validator-bonds/src/events/bond.rs index 60018fd1..89952098 100644 --- a/programs/validator-bonds/src/events/bond.rs +++ b/programs/validator-bonds/src/events/bond.rs @@ -6,7 +6,7 @@ use anchor_lang::prelude::*; pub struct InitBondEvent { pub config_address: Pubkey, pub validator_vote_account: Pubkey, - pub validator_vote_withdrawer: Pubkey, + pub validator_identity: Pubkey, pub authority: Pubkey, pub revenue_share: HundredthBasisPoint, pub bond_bump: u8, diff --git a/programs/validator-bonds/src/instructions/bond/configure_bond.rs b/programs/validator-bonds/src/instructions/bond/configure_bond.rs index 9aab580a..d85446b2 100644 --- a/programs/validator-bonds/src/instructions/bond/configure_bond.rs +++ b/programs/validator-bonds/src/instructions/bond/configure_bond.rs @@ -26,7 +26,7 @@ pub struct ConfigureBond<'info> { )] bond: Account<'info, Bond>, - /// validator vote account owner or bond authority may change the account + /// validator vote account validator identity or bond authority may change the account #[account()] authority: Signer<'info>, diff --git a/programs/validator-bonds/src/instructions/bond/fund_bond.rs b/programs/validator-bonds/src/instructions/bond/fund_bond.rs index cc332518..42c23396 100644 --- a/programs/validator-bonds/src/instructions/bond/fund_bond.rs +++ b/programs/validator-bonds/src/instructions/bond/fund_bond.rs @@ -30,7 +30,7 @@ pub struct FundBond<'info> { bond: Account<'info, Bond>, /// CHECK: PDA - /// new authority owner, it's the bonds program + /// new owner of the stake account, it's the bonds program PDA #[account( seeds = [ b"bonds_authority", diff --git a/programs/validator-bonds/src/instructions/bond/init_bond.rs b/programs/validator-bonds/src/instructions/bond/init_bond.rs index 14e198ca..37adfadc 100644 --- a/programs/validator-bonds/src/instructions/bond/init_bond.rs +++ b/programs/validator-bonds/src/instructions/bond/init_bond.rs @@ -1,4 +1,4 @@ -use crate::checks::check_validator_vote_account_withdrawer_authority; +use crate::checks::check_validator_vote_account_validator_identity; use crate::error::ErrorCode; use crate::events::bond::InitBondEvent; use crate::state::bond::Bond; @@ -28,9 +28,9 @@ pub struct InitBond<'info> { )] validator_vote_account: UncheckedAccount<'info>, - /// only validator vote account withdrawer authority may can create the bond + /// only validator vote account validator identity may create the bond #[account()] - authorized_withdrawer: Signer<'info>, + validator_identity: Signer<'info>, #[account( init, @@ -65,9 +65,9 @@ impl<'info> InitBond<'info> { bond_bump: u8, ) -> Result<()> { // verification of the validator vote account - check_validator_vote_account_withdrawer_authority( + check_validator_vote_account_validator_identity( &self.validator_vote_account, - &self.authorized_withdrawer.key(), + &self.validator_identity.key(), )?; self.bond.set_inner(Bond { @@ -81,7 +81,7 @@ impl<'info> InitBond<'info> { emit!(InitBondEvent { config_address: self.bond.config, validator_vote_account: self.bond.validator_vote_account, - validator_vote_withdrawer: self.authorized_withdrawer.key(), + validator_identity: self.validator_identity.key(), authority: self.bond.authority, revenue_share: self.bond.revenue_share, bond_bump: self.bond.bump, diff --git a/programs/validator-bonds/src/instructions/withdraw/cancel_withdraw_request.rs b/programs/validator-bonds/src/instructions/withdraw/cancel_withdraw_request.rs index c26b3db5..13f991a1 100644 --- a/programs/validator-bonds/src/instructions/withdraw/cancel_withdraw_request.rs +++ b/programs/validator-bonds/src/instructions/withdraw/cancel_withdraw_request.rs @@ -25,7 +25,7 @@ pub struct CancelWithdrawRequest<'info> { #[account()] validator_vote_account: UncheckedAccount<'info>, - /// validator vote account owner or bond authority may ask for cancelling + /// validator vote account validator identity or bond authority may ask for cancelling #[account()] pub authority: Signer<'info>, diff --git a/programs/validator-bonds/src/instructions/withdraw/claim_withdraw_request.rs b/programs/validator-bonds/src/instructions/withdraw/claim_withdraw_request.rs index 78fa2ab8..bd33f3be 100644 --- a/programs/validator-bonds/src/instructions/withdraw/claim_withdraw_request.rs +++ b/programs/validator-bonds/src/instructions/withdraw/claim_withdraw_request.rs @@ -1,6 +1,6 @@ use crate::checks::{ check_stake_is_initialized_with_withdrawer_authority, check_stake_valid_delegation, - check_validator_vote_account_withdrawer_authority, + check_validator_vote_account_validator_identity, }; use crate::constants::BONDS_AUTHORITY_SEED; use crate::error::ErrorCode; @@ -18,7 +18,6 @@ use anchor_spl::stake::{authorize, Authorize, Stake, StakeAccount}; /// Withdrawing funds from a bond account, to proceed the withdraw one must create a withdraw request first. /// Withdrawal takes StakeAccount that associated with bonds program and changes owner back to validator vote withdrawer. -// TODO: AquireWithdrawRequest ? #[derive(Accounts)] pub struct ClaimWithdrawRequest<'info> { /// the config root configuration account @@ -70,7 +69,8 @@ pub struct ClaimWithdrawRequest<'info> { #[account(mut)] stake_account: Account<'info, StakeAccount>, - /// CHECK: this has to match with validator vote account withdrawer + // TODO: is correct to pass the stake account to authority of the validator identity? + /// CHECK: this has to match with validator vote account validator identity /// this is the account that will be the new owner (withdrawer authority) of the stake account /// and ultimately it receives the withdrawing funds #[account()] @@ -108,9 +108,9 @@ pub struct ClaimWithdrawRequest<'info> { impl<'info> ClaimWithdrawRequest<'info> { pub fn process(&mut self) -> Result<()> { - // vote account owner matches the authority where the funds will be withdrawn to - // i.e., the address where who will be new owner (withdrawer authority) of the stake account - check_validator_vote_account_withdrawer_authority( + // vote account validator identity matches the authority where the funds will be withdrawn to + // i.e., this address will be the new owner (withdrawer authority) of the stake account + check_validator_vote_account_validator_identity( &self.validator_vote_account, &self.withdrawer.key(), )?; diff --git a/programs/validator-bonds/src/instructions/withdraw/init_withdraw_request.rs b/programs/validator-bonds/src/instructions/withdraw/init_withdraw_request.rs index a527b199..aed22d41 100644 --- a/programs/validator-bonds/src/instructions/withdraw/init_withdraw_request.rs +++ b/programs/validator-bonds/src/instructions/withdraw/init_withdraw_request.rs @@ -36,7 +36,7 @@ pub struct InitWithdrawRequest<'info> { #[account()] validator_vote_account: UncheckedAccount<'info>, - /// validator vote account owner or bond authority may ask for the withdrawal + /// validator vote account node identity or bond authority may ask for the withdrawal #[account()] authority: Signer<'info>,