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

Multisig Improvements #142

Merged
merged 4 commits into from
Jan 26, 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ PROGRAM_ID_ACCESS_CONTROLLER=2F5NEkMnCRkmahEAcQfTQcZv1xtGgrWFfjENtTwHLuKg
PROGRAM_ID_STORE=A7Jh2nb1hZHwqEofm4N8SXbKTj82rx7KUfjParQXUyMQ


MULTISIG_ADDRESS=
MULTISIG_SIGNER=
MULTISIG_ADDRESS=FkDbSMXT3BwjLCdyLnejtXUdn6ka9ozC8fJ7bjvQ41Xv
MULTISIG_SIGNER=2pactFWAAayNcTPYp7XuRvMSGWTkLwP8kg2g8FftgYJ7
LINK=9TD23DYGTgUSVGWMjQVZ9cqLrvFRbzJ7MguUncfDLbSG

BILLING_ACCESS_CONTROLLER=2KeBZNtEhe9ws5n7czJxYyAYvTfzD6vMEYWHvACxMGWd
REQUESTER_ACCESS_CONTROLLER=5UF9xKW9rjJJTzwSmvf6EKTkwTnp8RspVCXNtE4xX59d
LOWERING_ACCESS_CONTROLLER=5UF9xKW9rjJJTzwSmvf6EKTkwTnp8RspVCXNtE4xX59d
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Result } from '@chainlink/gauntlet-core'
import { logger } from '@chainlink/gauntlet-core/dist/utils'
import { logger, BN } from '@chainlink/gauntlet-core/dist/utils'
import { SolanaCommand, TransactionResponse } from '@chainlink/gauntlet-solana'
import { PublicKey, SYSVAR_RENT_PUBKEY, Keypair } from '@solana/web3.js'
import BN from 'bn.js'
import { CONTRACT_LIST, getContract } from '@chainlink/gauntlet-solana-contracts'

type Input = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Result } from '@chainlink/gauntlet-core'
import { TransactionResponse, SolanaCommand, RawTransaction } from '@chainlink/gauntlet-solana'
import { logger } from '@chainlink/gauntlet-core/dist/utils'
import { logger, BN, prompt } from '@chainlink/gauntlet-core/dist/utils'
import { PublicKey, SYSVAR_RENT_PUBKEY, Keypair, Transaction } from '@solana/web3.js'
import { CONTRACT_LIST, getContract } from '@chainlink/gauntlet-solana-contracts'
import { ProgramError, parseIdlErrors, Idl, Program } from '@project-serum/anchor'
import { MAX_BUFFER_SIZE } from '../lib/constants'

type ProposalContext = {
rawTx: RawTransaction
Expand All @@ -25,7 +26,7 @@ export const wrapCommand = (command) => {
super(flags, args)
logger.info(`Running ${command.id} command using Serum Multisig`)

this.command = new command(flags, args)
this.command = new command({ ...flags, bufferSize: MAX_BUFFER_SIZE }, args)
this.command.invokeMiddlewares(this.command, this.command.middlewares)
this.require(!!process.env.MULTISIG_ADDRESS, 'Please set MULTISIG_ADDRESS env var')
this.multisigAddress = new PublicKey(process.env.MULTISIG_ADDRESS)
Expand Down Expand Up @@ -56,8 +57,11 @@ export const wrapCommand = (command) => {
- Threshold: ${threshold.toString()}
- Owners: ${owners}`)

// TODO: Should we support many txs?
const rawTx = (await this.command.makeRawTransaction(multisigSigner))[0]
const instructionIndex = new BN(this.flags.instruction || 0).toNumber()
const rawTxs = await this.command.makeRawTransaction(multisigSigner)
await this.showExecutionInstructions(rawTxs, instructionIndex)
const rawTx = rawTxs[instructionIndex]

const isCreation = !this.flags.proposal
if (isCreation) {
const proposal = await this.createProposalAcount()
Expand Down Expand Up @@ -129,7 +133,7 @@ export const wrapCommand = (command) => {
}

createProposal: ProposalAction = async (proposal: PublicKey, context): Promise<string> => {
logger.loading(`Creating proposal`)
logger.loading(`Creating proposal for ${command.id}`)
const tx = await this.program.rpc.createTransaction(
context.rawTx.programId,
context.rawTx.accounts,
Expand All @@ -148,7 +152,7 @@ export const wrapCommand = (command) => {
}

approveProposal: ProposalAction = async (proposal: PublicKey): Promise<string> => {
logger.loading(`Approving proposal`)
logger.loading(`Approving proposal for ${command.id}`)
const tx = await this.program.rpc.approve({
accounts: {
multisig: this.multisigAddress,
Expand All @@ -160,7 +164,7 @@ export const wrapCommand = (command) => {
}

executeProposal: ProposalAction = async (proposal: PublicKey, context): Promise<string> => {
logger.loading(`Executing proposal`)
logger.loading(`Executing proposal for ${command.id}`)
// get the command's starting word to map it to the respective IDL(ocr2, store etc)
const { idl } = getContract(command.id.split(':')[0], '')
try {
Expand Down Expand Up @@ -217,5 +221,16 @@ export const wrapCommand = (command) => {
logger.info(`Eligible owners to sign: `)
logger.info(remainingEligibleSigners.toString())
}

showExecutionInstructions = async (rawTxs: RawTransaction[], instructionIndex: number) => {
logger.info(`Execution Information:
The command ${command.id} with multisig takes up to ${rawTxs.length} (${rawTxs.map(
(_, i) => i,
)}) zero-indexed transactions.
Currently running ${instructionIndex + 1} of ${rawTxs.length}.
`)

await prompt('Continue?')
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { PublicKey } from '@solana/web3.js'
import { CONTRACT_LIST, getContract } from '@chainlink/gauntlet-solana-contracts'
import { Result } from '@chainlink/gauntlet-core'

import BN from 'bn.js'

export default class SetOwners extends SolanaCommand {
static id = 'set:owners'
static category = CONTRACT_LIST.MULTISIG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { SolanaCommand, RawTransaction, TransactionResponse } from '@chainlink/g
import { PublicKey } from '@solana/web3.js'
import { CONTRACT_LIST, getContract } from '@chainlink/gauntlet-solana-contracts'
import { Result } from '@chainlink/gauntlet-core'

import BN from 'bn.js'
import { BN } from '@chainlink/gauntlet-core/dist/utils'

export default class SetThreshold extends SolanaCommand {
static id = 'set:threshold'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MAX_BUFFER_SIZE = 796
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ NODE_URL=http://127.0.0.1:8899
PROGRAM_ID_OCR2=CF13pnKGJ1WJZeEgVAtFdUi4MMndXm9hneiHs8azUaZt
PROGRAM_ID_ACCESS_CONTROLLER=2F5NEkMnCRkmahEAcQfTQcZv1xtGgrWFfjENtTwHLuKg
PROGRAM_ID_STORE=A7Jh2nb1hZHwqEofm4N8SXbKTj82rx7KUfjParQXUyMQ

LINK=9TD23DYGTgUSVGWMjQVZ9cqLrvFRbzJ7MguUncfDLbSG

BILLING_ACCESS_CONTROLLER=2KeBZNtEhe9ws5n7czJxYyAYvTfzD6vMEYWHvACxMGWd
REQUESTER_ACCESS_CONTROLLER=5UF9xKW9rjJJTzwSmvf6EKTkwTnp8RspVCXNtE4xX59d
LOWERING_ACCESS_CONTROLLER=5UF9xKW9rjJJTzwSmvf6EKTkwTnp8RspVCXNtE4xX59d
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ type Input = {
billingAccessController: string
requesterAccessController: string
link: string
billing: {
observationPaymentGjuels: string
transmissionPaymentGjuels: string
}
}

export default class OCR2Inspect extends SolanaCommand {
Expand All @@ -46,6 +50,10 @@ export default class OCR2Inspect extends SolanaCommand {
requesterAccessController,
link,
offchainConfig,
billing: {
observationPaymentGjuels: info.billing.observationPaymentGjuels,
transmissionPaymentGjuels: info.billing.transmissionPaymentGjuels,
},
}
}

Expand Down Expand Up @@ -161,6 +169,16 @@ export default class OCR2Inspect extends SolanaCommand {
toComparableNumber(input.maxAnswer),
'Max Answer',
),
inspection.makeInspection(
toComparableNumber(onChainState.config.billing.transmissionPaymentGjuels),
toComparableNumber(input.billing.transmissionPaymentGjuels),
'Transmission Payment',
),
inspection.makeInspection(
toComparableNumber(onChainState.config.billing.observationPaymentGjuels),
toComparableNumber(input.billing.observationPaymentGjuels),
'Observation Payment',
),
inspection.makeInspection(
toComparablePubKey(onChainState.config.requesterAccessController),
toComparablePubKey(input.requesterAccessController),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class CommitOffchainConfig extends SolanaCommand {
await prompt(`Commit Offchain config?`)
logger.loading('Sending tx...')
const txhash = await this.sendTx(tx, [this.wallet.payer], contract.idl)
logger.success(`Committing offchain config on tx ${tx}`)
logger.success(`Committing offchain config on tx ${txhash}`)

return {
responses: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { logger, prompt, time, BN } from '@chainlink/gauntlet-core/dist/utils'
import { Proto, sharedSecretEncryptions } from '@chainlink/gauntlet-core/dist/crypto'

import { SolanaCommand, TransactionResponse } from '@chainlink/gauntlet-solana'
import { PublicKey } from '@solana/web3.js'
import { AccountMeta, PublicKey } from '@solana/web3.js'
import { MAX_TRANSACTION_BYTES, ORACLES_MAX_LENGTH } from '../../../../lib/constants'
import { CONTRACT_LIST, getContract } from '../../../../lib/contracts'
import { descriptor as OCR2Descriptor } from '../../../../lib/ocr2Proto'
import { getRDD } from '../../../../lib/rdd'
import { divideIntoChunks } from '../../../../lib/utils'
import { divideIntoChunks, makeTx } from '../../../../lib/utils'
import { join } from 'path'

export type Input = {
Expand Down Expand Up @@ -192,46 +192,72 @@ export default class WriteOffchainConfig extends SolanaCommand {
return true
}

execute = async () => {
makeRawTransaction = async (signer: PublicKey) => {
const ocr2 = getContract(CONTRACT_LIST.OCR_2, '')
const address = ocr2.programId.toString()
const program = this.loadProgram(ocr2.idl, address)

const state = new PublicKey(this.flags.state)
const owner = this.wallet.payer

// Throws on invalid input
const input = this.makeInput(this.flags.input)
const maxBufferSize = this.flags.bufferSize || MAX_TRANSACTION_BYTES

this.validateInput(input)

// Check correct format OCR Keys
const offchainConfig = await this.serializeOffchainConfig(input)

logger.info(`Offchain config size: ${offchainConfig.byteLength}`)
this.require(offchainConfig.byteLength < 4096, 'Offchain config must be lower than 4096 bytes')

// There's a byte limit per transaction. Write the config in chunks
const offchainConfigChunks = divideIntoChunks(offchainConfig, MAX_TRANSACTION_BYTES)
const offchainConfigChunks = divideIntoChunks(offchainConfig, maxBufferSize)
if (offchainConfigChunks.length > 1) {
logger.info(
`Config size (${offchainConfig.byteLength} bytes) is bigger than transaction limit. It will be configured using ${offchainConfigChunks.length} transactions`,
`Config size (${offchainConfig.byteLength} bytes) is bigger than transaction limit. It needs to be configured using ${offchainConfigChunks.length} transactions`,
)
}

logger.log('Offchain info:', input)
const startingPoint = new BN(this.flags.chunk || 0).toNumber()
await prompt(`Start writing offchain config from ${startingPoint}/${offchainConfigChunks.length - 1}?`)

const dataInChunks = offchainConfigChunks.map((buffer) =>
program.coder.instruction.encode('write_offchain_config', {
offchainConfig: buffer,
}),
)

const accounts: AccountMeta[] = [
{
pubkey: state,
isSigner: false,
isWritable: true,
},
{
pubkey: signer,
isSigner: true,
isWritable: false,
},
]

return dataInChunks.map((data) => ({
accounts,
data,
programId: program.programId,
}))
}

execute = async () => {
const contract = getContract(CONTRACT_LIST.OCR_2, '')
const state = new PublicKey(this.flags.state)

const rawTx = await this.makeRawTransaction(this.wallet.payer.publicKey)
const startingPoint = new BN(this.flags.instruction || 0).toNumber()

await prompt(`Start writing offchain config from ${startingPoint}/${rawTx.length - 1}?`)

const txs: string[] = []
for (let i = startingPoint; i < offchainConfigChunks.length; i++) {
logger.loading(`Sending ${i}/${offchainConfigChunks.length - 1}...`)
const tx = await program.rpc.writeOffchainConfig(offchainConfigChunks[i], {
accounts: {
state: state,
authority: owner.publicKey,
},
})
txs.push(tx)
for (let i = startingPoint; i < rawTx.length; i++) {
logger.loading(`Sending ${i}/${rawTx.length - 1}...`)
const tx = makeTx([rawTx[i]])
const txhash = await this.sendTx(tx, [this.wallet.payer], contract.idl)
txs.push(txhash)
}
logger.success(`Last tx Write offchain config set on tx ${txs[txs.length - 1]}`)

Expand Down
Loading