Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade program commands #135

Merged
merged 1 commit into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion gauntlet/packages/gauntlet-serum-multisig/networks/.env.local
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
NODE_URL=http://127.0.0.1:8899
NODE_URL=http://127.0.0.1:8899

PROGRAM_ID_MULTISIG=3X4UvrX9gTxSmNmXww53W5dM5fT1Nby246hKV8Mx9LuD
PROGRAM_ID_OCR2=CF13pnKGJ1WJZeEgVAtFdUi4MMndXm9hneiHs8azUaZt
PROGRAM_ID_ACCESS_CONTROLLER=2F5NEkMnCRkmahEAcQfTQcZv1xtGgrWFfjENtTwHLuKg
PROGRAM_ID_STORE=A7Jh2nb1hZHwqEofm4N8SXbKTj82rx7KUfjParQXUyMQ


MULTISIG_ADDRESS=
MULTISIG_SIGNER=
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Result } from '@chainlink/gauntlet-core'
import { logger, prompt } from '@chainlink/gauntlet-core/dist/utils'
import { SolanaCommand, TransactionResponse, RawTransaction } from '@chainlink/gauntlet-solana'
import { AccountMeta, PublicKey, SYSVAR_RENT_PUBKEY, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js'
import { UPGRADEABLE_BPF_LOADER_PROGRAM_ID } from '../../lib/constants'
import { CONTRACT_LIST, getContract } from '../../lib/contracts'
import { SolanaConstructor } from '../../lib/types'
import { encodeInstruction, makeTx } from '../../lib/utils'

export const makeUpgradeProgramCommand = (contractId: CONTRACT_LIST): SolanaConstructor => {
return class UpgradeProgram extends SolanaCommand {
static id = `${contractId}:upgrade_program`
static category = contractId

static examples = [`yarn gauntlet ${contractId}:upgrade_program --network=devnet --buffer=[BUFFER_ACCOUNT]`]

constructor(flags, args) {
super(flags, args)

this.require(!!this.flags.buffer, 'Please provide flags with "buffer"')
}

makeRawTransaction = async (signer: PublicKey) => {
const contract = getContract(contractId, '')

const programId = new PublicKey(contract.programId)
const [programDataKey, _nonce] = await PublicKey.findProgramAddress(
[programId.toBuffer()],
UPGRADEABLE_BPF_LOADER_PROGRAM_ID,
)

const buffer = new PublicKey(this.flags.buffer)
const data = encodeInstruction({ Upgrade: {} })

const accounts: AccountMeta[] = [
{ pubkey: programDataKey, isSigner: false, isWritable: true },
{ pubkey: programId, isSigner: false, isWritable: true },
{ pubkey: buffer, isSigner: false, isWritable: true },
{ pubkey: signer, isSigner: false, isWritable: true },
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
{ pubkey: signer, isSigner: true, isWritable: false },
]

const rawTx: RawTransaction = {
data,
accounts,
programId: UPGRADEABLE_BPF_LOADER_PROGRAM_ID,
}

return [rawTx]
}

execute = async () => {
const rawTx = await this.makeRawTransaction(this.wallet.payer.publicKey)
await prompt(`Continue upgrading the ${contractId} program?`)
logger.loading('Upgrading program...')
const txhash = await this.provider.send(makeTx(rawTx), [this.wallet.payer])
logger.success(`Program upgraded on tx ${txhash}`)
return {
responses: [
{
tx: this.wrapResponse(txhash, this.flags.state),
contract: this.flags.state,
},
],
} as Result<TransactionResponse>
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import ReadState from './read'
import { makeAcceptOwnershipCommand } from '../ownership/acceptOwnership'
import { makeTransferOwnershipCommand } from '../ownership/transferOwnership'
import { CONTRACT_LIST } from '../../../lib/contracts'
import { makeUpgradeProgramCommand } from '../../abstract/upgrade'

export default [
Initialize,
AddAccess,
ReadState,
makeAcceptOwnershipCommand(CONTRACT_LIST.ACCESS_CONTROLLER),
makeTransferOwnershipCommand(CONTRACT_LIST.ACCESS_CONTROLLER),
makeUpgradeProgramCommand(CONTRACT_LIST.ACCESS_CONTROLLER),
]
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Inspection from './inspection'
import { makeAcceptOwnershipCommand } from '../ownership/acceptOwnership'
import { CONTRACT_LIST } from '../../../lib/contracts'
import { makeTransferOwnershipCommand } from '../ownership/transferOwnership'
import { makeUpgradeProgramCommand } from '../../abstract/upgrade'

export default [
Initialize,
Expand All @@ -43,4 +44,5 @@ export default [
SetupRDDFlow,
makeAcceptOwnershipCommand(CONTRACT_LIST.OCR_2),
makeTransferOwnershipCommand(CONTRACT_LIST.OCR_2),
makeUpgradeProgramCommand(CONTRACT_LIST.OCR_2),
]
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SetLoweringAccessController from './setLoweringAccessController'
import { makeAcceptOwnershipCommand } from '../ownership/acceptOwnership'
import { makeTransferOwnershipCommand } from '../ownership/transferOwnership'
import { CONTRACT_LIST } from '../../../lib/contracts'
import { makeUpgradeProgramCommand } from '../../abstract/upgrade'

export default [
Initialize,
Expand All @@ -15,4 +16,5 @@ export default [
SetLoweringAccessController,
makeAcceptOwnershipCommand(CONTRACT_LIST.STORE),
makeTransferOwnershipCommand(CONTRACT_LIST.STORE),
makeUpgradeProgramCommand(CONTRACT_LIST.STORE),
]
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
import { PublicKey } from '@solana/web3.js'

export const MAX_TRANSACTION_BYTES = 996
export const ORACLES_MAX_LENGTH = 19
export const UPGRADEABLE_BPF_LOADER_PROGRAM_ID = new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111')
27 changes: 26 additions & 1 deletion gauntlet/packages/gauntlet-solana-contracts/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Transaction, TransactionInstruction, Keypair } from '@solana/web3.js'
import { Transaction, TransactionInstruction } from '@solana/web3.js'
import { RawTransaction } from '@chainlink/gauntlet-solana'
import * as BufferLayout from '@solana/buffer-layout'

export const divideIntoChunks = (arr: Array<any> | Buffer, chunkSize: number): any[][] => {
const chunks: any[] = []
Expand All @@ -24,3 +25,27 @@ export const makeTx = (rawTx: RawTransaction[]): Transaction => {
new Transaction(),
)
}

// Source: https://github.com/neonlabsorg/multisig/blob/8f1938c82c8db1251fad48a403487af18ecf5eb0/client/loader.ts#L25
export const encodeInstruction = (data: any): Buffer => {
const CHUNK_SIZE = 900

const dataLayout = BufferLayout.union(BufferLayout.u32('tag'), null, 'tag')
dataLayout.addVariant(0, BufferLayout.struct([]), 'InitializeBuffer')
const write = BufferLayout.struct([
BufferLayout.u32('offset'),
BufferLayout.nu64('length'),
BufferLayout.seq(BufferLayout.u8('byte'), BufferLayout.offset(BufferLayout.u32(), -8), 'bytes'),
])
dataLayout.addVariant(1, write, 'Write')
const deployWithMaxLen = BufferLayout.struct([BufferLayout.nu64('max_data_len')])
dataLayout.addVariant(2, deployWithMaxLen, 'DeployWithMaxDataLen')
dataLayout.addVariant(3, BufferLayout.struct([]), 'Upgrade')
dataLayout.addVariant(4, BufferLayout.struct([]), 'SetAuthority')
dataLayout.addVariant(5, BufferLayout.struct([]), 'Close')

// UpgradeableLoaderInstruction tag + offset + chunk length + chunk data
const instructionBuffer = Buffer.alloc(4 + 4 + 8 + CHUNK_SIZE)
const encodedSize = dataLayout.encode(data, instructionBuffer)
return instructionBuffer.slice(0, encodedSize)
}