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

tx simulation + config diff + minor refactor #257

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


MULTISIG_ADDRESS=FkDbSMXT3BwjLCdyLnejtXUdn6ka9ozC8fJ7bjvQ41Xv
LINK=9TD23DYGTgUSVGWMjQVZ9cqLrvFRbzJ7MguUncfDLbSG

Expand Down
2 changes: 1 addition & 1 deletion gauntlet/packages/gauntlet-serum-multisig/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"bundle": "yarn build && pkg ."
},
"dependencies": {
"@chainlink/gauntlet-core": "0.0.7",
"@chainlink/gauntlet-core": "0.1.2",
"@chainlink/gauntlet-solana": "*",
"@chainlink/gauntlet-solana-contracts": "*",
"@project-serum/anchor": "^0.22.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ export default class MultisigCreate extends SolanaCommand {
)}. Continue?`,
)

const instruction = await program.account.multisig.createInstruction(multisig, TOTAL_TO_ALLOCATE)
const tx = await program.rpc.createMultisig(owners, new BN(input.threshold), nonce, {
accounts: {
multisig: multisig.publicKey,
rent: SYSVAR_RENT_PUBKEY,
},
signers: [multisig],
instructions: [await program.account.multisig.createInstruction(multisig, TOTAL_TO_ALLOCATE)],
instructions: [instruction],
})
logger.success('New multisig created')
logger.info(`Multisig address: ${multisig.publicKey}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const wrapCommand = (command) => {
const signer = this.wallet.publicKey
const rawTxs = await this.makeRawTransaction(signer)
// If proposal is not provided, we are at creation time, and a new proposal acc should have been created
const proposal = new PublicKey(this.flags.proposal || rawTxs[0].keys[1].pubkey)
const proposal = new PublicKey(this.flags.proposal || this.flags.multisigProposal || rawTxs[0].keys[1].pubkey)

if (this.flags.execute) {
await prompt('CREATION,APPROVAL or EXECUTION TX will be executed. Continue?')
Expand Down Expand Up @@ -114,12 +114,21 @@ export const wrapCommand = (command) => {
- Owners: ${owners}`)

const instructionIndex = new BN(this.flags.instruction || 0).toNumber()

// Build the internal command if necessary
this.command = this.command.buildCommand ? await this.command.buildCommand(this.flags, this.args) : this.command
const rawTxs = await this.command.makeRawTransaction(multisigSigner)
await this.command.simulateTx(multisigSigner, rawTxs, this.wallet.publicKey)
await this.showExecutionInstructions(rawTxs, instructionIndex)
const rawTx = rawTxs[instructionIndex]

// First step should be creating the proposal account. If no proposal flag is provided, proceed to create it
const proposal = this.flags.proposal ? new PublicKey(this.flags.proposal) : await this.createProposalAcount()
const proposalFlag = this.flags.proposal || this.flags.multisigProposal
const proposal = proposalFlag ? new PublicKey(proposalFlag) : await this.createProposalAcount()

if (this.command.beforeExecute) {
await this.command.beforeExecute(signer)
}

const proposalState = await this.fetchState(proposal)
const isCreation = !proposalState
Expand All @@ -137,11 +146,11 @@ export const wrapCommand = (command) => {
}

const isAlreadyExecuted = proposalState.didExecute
if (isAlreadyExecuted) throw new Error('Proposal is already executed')
if (isAlreadyExecuted) throw new Error('Multisig Proposal is already executed')

this.require(
await this.isSameProposal(proposal, rawTx),
'The transaction generated is different from the proposal provided',
'The transaction generated is different from the Multisig Proposal provided',
)

if (!this.isReadyForExecution(proposalState, threshold)) {
Expand All @@ -161,15 +170,15 @@ export const wrapCommand = (command) => {
try {
return await this.program.account.transaction.fetch(proposal)
} catch (e) {
logger.info('Proposal state not found. Should be empty at CREATION time')
logger.info('Multisig Proposal state not found. Should be empty at CREATION time')
return
}
}

isSameProposal = async (proposal: PublicKey, rawTx: TransactionInstruction): Promise<boolean> => {
const state = await this.fetchState(proposal)
if (!state) {
logger.error('Proposal state does not exist. Considering the proposal as different')
logger.error('Multisig Proposal state does not exist. Considering the proposal as different')
return false
}
const isSameData = Buffer.compare(state.data, rawTx.data) === 0
Expand All @@ -179,8 +188,8 @@ export const wrapCommand = (command) => {
}

createProposalAcount = async (): Promise<PublicKey> => {
await prompt('A new proposal account will be created. Continue?')
logger.log('Creating proposal account...')
await prompt('A new Multisig Proposal account will be created. Continue?')
logger.log('Creating Multisig Proposal account...')
const proposal = Keypair.generate()
const txSize = 1300 // Space enough
const proposalInstruction = await SystemProgram.createAccount({
Expand All @@ -191,7 +200,7 @@ export const wrapCommand = (command) => {
programId: this.program.programId,
})
await this.signAndSendRawTx([proposalInstruction], [proposal])
logger.success(`Proposal account created at: ${proposal.publicKey.toString()}`)
logger.success(`Multisig Proposal account created at: ${proposal.publicKey.toString()}`)
return proposal.publicKey
}

Expand All @@ -200,7 +209,7 @@ export const wrapCommand = (command) => {
signer,
context,
): Promise<TransactionInstruction[]> => {
logger.loading(`Generating proposal CREATION data for ${command.id}`)
logger.loading(`Generating Multisig Proposal CREATION data for ${command.id}`)

const tx = this.program.instruction.createTransaction(
context.rawTx.programId,
Expand All @@ -218,7 +227,7 @@ export const wrapCommand = (command) => {
}

approveProposal: ProposalAction = async (proposal: PublicKey, signer): Promise<TransactionInstruction[]> => {
logger.loading(`Generating proposal APPROVAL data for ${command.id}`)
logger.loading(`Generating Multisig Proposal APPROVAL data for ${command.id}`)

const tx = this.program.instruction.approve({
accounts: {
Expand All @@ -235,7 +244,7 @@ export const wrapCommand = (command) => {
signer,
context,
): Promise<TransactionInstruction[]> => {
logger.loading(`Generating proposal EXECUTION data for ${command.id}`)
logger.loading(`Generating Multisig Proposal EXECUTION data for ${command.id}`)

const remainingAccounts = context.proposalState.accounts
.map((t) => (t.pubkey.equals(context.multisigSigner) ? { ...t, isSigner: false } : t))
Expand All @@ -262,16 +271,16 @@ export const wrapCommand = (command) => {
const threshold = multisigState.threshold
const owners = multisigState.owners

logger.debug('Proposal state after action:')
logger.debug('Multisig Proposal state after action:')
logger.debug(JSON.stringify(proposalState, null, 4))
if (proposalState.didExecute == true) {
logger.info(`Proposal has been executed`)
logger.info(`Multisig Proposal has been executed`)
return
}

if (this.isReadyForExecution(proposalState, threshold)) {
logger.info(
`Threshold has been met, an owner needs to run the command once more in order to execute it, with flag --proposal=${proposal}`,
`Threshold has been met, an owner needs to run the command once more in order to execute it, with flag --proposal=${proposal} or --multisigProposal=${proposal}`,
)
return
}
Expand All @@ -281,7 +290,7 @@ export const wrapCommand = (command) => {
`${this.getRemainingSigners(
proposalState,
threshold,
)} more owners should sign this proposal, using the same command with flag --proposal=${proposal}`,
)} more owners should sign this multisig proposal, using the same command providing ${proposal} with flag --proposal=${proposal} or --multisigProposal=${proposal}`,
)
logger.info(`Eligible owners to sign: `)
logger.info(remainingEligibleSigners.toString())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NODE_URL=http://127.0.0.1:8899
PROGRAM_ID_OCR2=E3j24rx12SyVsG6quKuZPbQqZPkhAUCh8Uek4XrKYD2x
PROGRAM_ID_ACCESS_CONTROLLER=2ckhep7Mvy1dExenBqpcdevhRu7CLuuctMcx7G9mWEvo
PROGRAM_ID_STORE=9kRNTZmoZSiTBuXC62dzK9E7gC7huYgcmRRhYv3i4osC
PROGRAM_ID_OCR2=CF13pnKGJ1WJZeEgVAtFdUi4MMndXm9hneiHs8azUaZt
PROGRAM_ID_ACCESS_CONTROLLER=2F5NEkMnCRkmahEAcQfTQcZv1xtGgrWFfjENtTwHLuKg
PROGRAM_ID_STORE=A7Jh2nb1hZHwqEofm4N8SXbKTj82rx7KUfjParQXUyMQ

LINK=9TD23DYGTgUSVGWMjQVZ9cqLrvFRbzJ7MguUncfDLbSG

Expand Down
2 changes: 1 addition & 1 deletion gauntlet/packages/gauntlet-solana-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"bundle": "yarn build && pkg ."
},
"dependencies": {
"@chainlink/gauntlet-core": "0.0.7",
"@chainlink/gauntlet-core": "0.1.2",
"@chainlink/gauntlet-solana": "*",
"@ethersproject/keccak256": "^5.5.0",
"@solana/spl-token": "^0.1.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default class OCR2InitializeFlow extends FlowCommand<TransactionResponse>
FEED: 1,
OCR_2: 2,
PROPOSAL: 3,
PROPOSE_OFFCHAIN: 4,
}

this.requireFlag('rdd', 'This flow only works with information coming from RDD. Please provide the --rdd flag')
Expand Down Expand Up @@ -81,6 +82,7 @@ export default class OCR2InitializeFlow extends FlowCommand<TransactionResponse>
args: [FlowCommand.ID.contract(this.stepIds.OCR_2)],
},
{
id: this.stepIds.PROPOSE_OFFCHAIN,
name: 'Propose Offchain Config',
command: ProposeOffchainConfig,
flags: {
Expand Down Expand Up @@ -109,6 +111,7 @@ export default class OCR2InitializeFlow extends FlowCommand<TransactionResponse>
command: AcceptProposal,
flags: {
proposalId: FlowCommand.ID.data(this.stepIds.PROPOSAL, 'proposal'),
secret: FlowCommand.ID.data(this.stepIds.PROPOSE_OFFCHAIN, 'secret'),
},
args: [FlowCommand.ID.contract(this.stepIds.OCR_2)],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Result } from '@chainlink/gauntlet-core'
import { inspection, BN } from '@chainlink/gauntlet-core/dist/utils'
import { Proto } from '@chainlink/gauntlet-core/dist/crypto'
import { SolanaCommand, TransactionResponse } from '@chainlink/gauntlet-solana'
import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js'
import { CONTRACT_LIST, getContract } from '../../../../lib/contracts'
import { deserializeConfig } from '../../../../lib/encoding'
import WriteOffchainConfig, { OffchainConfig } from '../proposeOffchainConfig'
import { descriptor as OCR2Descriptor } from '../../../../lib/ocr2Proto'
import { toComparableLongNumber, toComparableNumber, toComparablePubKey } from '../../../../lib/inspection'
import RDD from '../../../../lib/rdd'

Expand Down Expand Up @@ -66,16 +65,6 @@ export default class OCR2Inspect extends SolanaCommand {
super(flags, args)
}

deserializeConfig = (buffer: Buffer): any => {
const proto = new Proto.Protobuf({ descriptor: OCR2Descriptor })
const offchain = proto.decode('offchainreporting2_config.OffchainConfigProto', buffer)
const reportingPluginConfig = proto.decode(
'offchainreporting2_config.ReportingPluginConfig',
offchain.reportingPluginConfig,
)
return { ...offchain, reportingPluginConfig }
}

makeFeedInspections = async (bufferedInfo: Keypair, input: Input): Promise<inspection.Inspection[]> => {
const store = getContract(CONTRACT_LIST.STORE, '')
const storeProgram = this.loadProgram(store.idl, store.programId.toString())
Expand Down Expand Up @@ -137,7 +126,7 @@ export default class OCR2Inspect extends SolanaCommand {
new BN(onChainState.offchainConfig.len).toNumber(),
)

const onChainOCRConfig = this.deserializeConfig(bufferedConfig)
const onChainOCRConfig = deserializeConfig(bufferedConfig)
const wrappedComparableLongNumber = (v: any) => {
// Proto encoding will ignore falsy values.
if (!v) return '0'
Expand Down
Loading