From ef545074cb291d0e8f903b44e3d2a1a48d986225 Mon Sep 17 00:00:00 2001 From: Augustus <14297860+augustbleeds@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:52:44 -0400 Subject: [PATCH] BCI-3898: fix multisig set config bug (#497) --- .../starknet-gauntlet-cli/src/index.ts | 1 + .../src/wrapper/index.ts | 32 +++++++++++++++- packages-ts/starknet-gauntlet-ocr2/README.md | 4 +- .../src/commands/ocr2/setConfig.ts | 37 ++++++++++--------- .../starknet-gauntlet-ocr2/src/lib/utils.ts | 6 +-- .../test/commands/ocr2.test.ts | 1 + .../src/utils/configDigest.ts | 25 +++++++++++++ .../starknet-gauntlet/src/utils/index.ts | 1 + 8 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 packages-ts/starknet-gauntlet/src/utils/configDigest.ts diff --git a/packages-ts/starknet-gauntlet-cli/src/index.ts b/packages-ts/starknet-gauntlet-cli/src/index.ts index bfe695cac..2592d5c34 100644 --- a/packages-ts/starknet-gauntlet-cli/src/index.ts +++ b/packages-ts/starknet-gauntlet-cli/src/index.ts @@ -64,6 +64,7 @@ const registerExecuteCommand = ( billingAccessController: process.env.BILLING_ACCESS_CONTROLLER, link: process.env.LINK, secret: flags.secret || process.env.SECRET, + randomSecret: flags.randomSecret || process.env.RANDOM_SECRET, withLedger: !!flags.withLedger || !!process.env.WITH_LEDGER, ledgerPath: (flags.ledgerPath as string) || process.env.LEDGER_PATH, } diff --git a/packages-ts/starknet-gauntlet-multisig/src/wrapper/index.ts b/packages-ts/starknet-gauntlet-multisig/src/wrapper/index.ts index 03e4db2d0..629909e7b 100644 --- a/packages-ts/starknet-gauntlet-multisig/src/wrapper/index.ts +++ b/packages-ts/starknet-gauntlet-multisig/src/wrapper/index.ts @@ -8,6 +8,7 @@ import { Input, IStarknetProvider, IStarknetWallet, + tryToWriteLastConfigDigestToRDD, } from '@chainlink/starknet-gauntlet' import { TransactionResponse } from '@chainlink/starknet-gauntlet/dist/transaction' import { @@ -242,11 +243,40 @@ export const wrapCommand = ( const messages = { [Action.EXECUTE]: `The multisig proposal reached the threshold and can be executed. Run the same command with the flag --multisigProposal=${state.proposal.id}`, [Action.APPROVE]: `The multisig proposal needs ${approvalsLeft} more approvals. Run the same command with the flag --multisigProposal=${state.proposal.id}`, - [Action.NONE]: `The multisig proposal has been executed. No more actions needed`, + [Action.NONE]: `The multisig proposal has been executed. No more actions needed on-chain`, } deps.logger.line() deps.logger.info(`${messages[state.proposal.nextAction]}`) deps.logger.line() + + if (state.proposal.nextAction === Action.NONE) { + // check if the proposal has the config set event + + const txHash = result.responses[0].tx.hash + const txInfo = await this.executionContext.provider.provider.getTransactionReceipt(txHash) + + if (txInfo.isSuccess()) { + const contractEvents = this.command.executionContext.contract.parseEvents(txInfo) + const event = contractEvents[contractEvents.length - 1]['ConfigSet'] + + // this was a set config multisig command + if (event) { + // write lastConfigDigest back to RDD + const hexLastConfigDigest = `${(event.latest_config_digest as bigint).toString(16)}` + // config digest string must be exactly 32 bytes + const paddedHexLastConfigDigest = hexLastConfigDigest.padStart(64, '0') + const configDigest = `0x${paddedHexLastConfigDigest}` + + await tryToWriteLastConfigDigestToRDD( + deps, + this.executionContext.flags.rdd, + this.contractAddress, + configDigest, + ) + } + } + } + return { proposalId } } diff --git a/packages-ts/starknet-gauntlet-ocr2/README.md b/packages-ts/starknet-gauntlet-ocr2/README.md index 5c822de85..01aab2c8d 100644 --- a/packages-ts/starknet-gauntlet-ocr2/README.md +++ b/packages-ts/starknet-gauntlet-ocr2/README.md @@ -66,9 +66,11 @@ This Should set the billing details for this feed on contract address Run the following command substituting with the OCR2 contract address you received in the deploy step: ``` -yarn gauntlet ocr2:set_config --network= --address=
--f= --signers=[] --transmitters=[] --onchainConfig= --offchainConfig= --offchainConfigVersion= +yarn gauntlet ocr2:set_config --network= --address=
--secret= --f= --signers=[] --transmitters=[] --onchainConfig= --offchainConfig= --offchainConfigVersion= ``` +Note: You must include the same random secret to deterministically run the set config multiple times (ex: for multisig proposals among different signers signing the same transaction). This can be achieved by setting a flag or environment variable ``--randomSecret`` or ``RANDOM_SECRET`` respectiviely. + This Should set the config for this feed on contract address. diff --git a/packages-ts/starknet-gauntlet-ocr2/src/commands/ocr2/setConfig.ts b/packages-ts/starknet-gauntlet-ocr2/src/commands/ocr2/setConfig.ts index 5521920b3..8ee960375 100644 --- a/packages-ts/starknet-gauntlet-ocr2/src/commands/ocr2/setConfig.ts +++ b/packages-ts/starknet-gauntlet-ocr2/src/commands/ocr2/setConfig.ts @@ -8,6 +8,7 @@ import { bytesToFelts, BeforeExecute, getRDD, + tryToWriteLastConfigDigestToRDD, } from '@chainlink/starknet-gauntlet' import { time, diff } from '@chainlink/gauntlet-core/dist/utils' import { ocr2ContractLoader } from '../../lib/contracts' @@ -80,7 +81,7 @@ const makeUserInput = async (flags, args, env): Promise => { offchainConfig, offchainConfigVersion: 2, secret: flags.secret || env.secret, - randomSecret: flags.randomSecret || undefined, + randomSecret: flags.randomSecret || env.randomSecret, } } @@ -92,10 +93,18 @@ const makeUserInput = async (flags, args, env): Promise => { offchainConfig: flags.offchainConfig, offchainConfigVersion: parseInt(flags.offchainConfigVersion), secret: flags.secret || env.secret, - randomSecret: flags.randomSecret || undefined, + randomSecret: flags.randomSecret || env.randomSecret, } } +export const validateSecretsNotEmpty = async (input) => { + if (input.secret === undefined) { + throw new Error(`A secret must be provided (--secret flag or SECRET environment variable)`) + } + + return true +} + const makeContractInput = async ( input: SetConfigInput, ctx: ExecutionContext, @@ -122,6 +131,7 @@ const makeContractInput = async ( const { offchainConfig } = await encoding.serializeOffchainConfig( input.offchainConfig, input.secret, + input.randomSecret, ) const onchainConfig = [] // onchain config should be empty array for input (generate onchain) return [oracles, input.f, onchainConfig, 2, bytesToFelts(offchainConfig)] @@ -188,21 +198,13 @@ const afterExecute: AfterExecute = (context, inpu // config digest string must be exactly 32 bytes const paddedHexLastConfigDigest = hexLastConfigDigest.padStart(64, '0') const configDigest = `0x${paddedHexLastConfigDigest}` - deps.logger.info(`lastConfigDigest to save in RDD: ${configDigest}`) - if (context.flags.rdd) { - const rdd = getRDD(context.flags.rdd) - const contractAddr = context.contract.address - // set updated lastConfigDigest - rdd[CONTRACT_TYPES.AGGREGATOR][contractAddr]['config']['lastConfigDigest'] = configDigest - fs.writeFileSync(context.flags.rdd, JSON.stringify(rdd, null, 2)) - deps.logger.success( - `RDD file ${context.flags.rdd} has been updated! You must reformat RDD by running ./bin/degenerate and ./bin/generate in that exact order`, - ) - } else { - deps.logger.warn( - `No RDD file was inputted, you must manually update lastConfigDigest in RDD yourself`, - ) - } + + await tryToWriteLastConfigDigestToRDD( + deps, + context.flags.rdd, + context.contract.address, + configDigest, + ) return { successfulConfiguration: true } } catch (e) { @@ -214,6 +216,7 @@ const afterExecute: AfterExecute = (context, inpu const commandConfig: ExecuteCommandConfig = { ...SetConfig, + validations: [...SetConfig.validations, validateSecretsNotEmpty], makeUserInput: makeUserInput, makeContractInput: makeContractInput, loadContract: ocr2ContractLoader, diff --git a/packages-ts/starknet-gauntlet-ocr2/src/lib/utils.ts b/packages-ts/starknet-gauntlet-ocr2/src/lib/utils.ts index 855ad12c6..d52a440a7 100644 --- a/packages-ts/starknet-gauntlet-ocr2/src/lib/utils.ts +++ b/packages-ts/starknet-gauntlet-ocr2/src/lib/utils.ts @@ -1,8 +1,4 @@ -import { - ExecuteCommandConfig, - makeExecuteCommand, - isValidAddress, -} from '@chainlink/starknet-gauntlet' +import { isValidAddress } from '@chainlink/starknet-gauntlet' export const validateClassHash = async (input) => { if (isValidAddress(input.classHash) || input.classHash === undefined) { diff --git a/packages-ts/starknet-gauntlet-ocr2/test/commands/ocr2.test.ts b/packages-ts/starknet-gauntlet-ocr2/test/commands/ocr2.test.ts index 599d433a6..d10763672 100644 --- a/packages-ts/starknet-gauntlet-ocr2/test/commands/ocr2.test.ts +++ b/packages-ts/starknet-gauntlet-ocr2/test/commands/ocr2.test.ts @@ -73,6 +73,7 @@ const validInput = { }, offchainConfigVersion: 2, secret: 'awe accuse polygon tonic depart acuity onyx inform bound gilbert expire', + randomSecret: 'awe accuse polygon tonic depart acuity onyx inform bound gilbert expire', } const getNumCallsPerAddress = (txReceipt: InvokeTransactionReceiptResponse) => { diff --git a/packages-ts/starknet-gauntlet/src/utils/configDigest.ts b/packages-ts/starknet-gauntlet/src/utils/configDigest.ts new file mode 100644 index 000000000..9d3274b08 --- /dev/null +++ b/packages-ts/starknet-gauntlet/src/utils/configDigest.ts @@ -0,0 +1,25 @@ +import fs from 'fs' +import { CONTRACT_TYPES, getRDD } from '../rdd' +import { Dependencies } from '../dependencies' + +export const tryToWriteLastConfigDigestToRDD = async ( + deps: Pick, + rddPath: string, + contractAddr: string, + configDigest: string, +) => { + deps.logger.info(`lastConfigDigest to save in RDD: ${configDigest}`) + if (rddPath) { + const rdd = getRDD(rddPath) + // set updated lastConfigDigest + rdd[CONTRACT_TYPES.AGGREGATOR][contractAddr]['config']['lastConfigDigest'] = configDigest + fs.writeFileSync(rddPath, JSON.stringify(rdd, null, 2)) + deps.logger.success( + `RDD file ${rddPath} has been updated! You must reformat RDD by running ./bin/degenerate and ./bin/generate in that exact order`, + ) + } else { + deps.logger.warn( + `No RDD file was inputted, you must manually update lastConfigDigest in RDD yourself`, + ) + } +} diff --git a/packages-ts/starknet-gauntlet/src/utils/index.ts b/packages-ts/starknet-gauntlet/src/utils/index.ts index 6f5a55683..4dce8b032 100644 --- a/packages-ts/starknet-gauntlet/src/utils/index.ts +++ b/packages-ts/starknet-gauntlet/src/utils/index.ts @@ -1 +1,2 @@ export * from './address' +export * from './configDigest'