From fafb7860150f7493e2fad371a8b672588784895c Mon Sep 17 00:00:00 2001 From: Mohammad Amin Khashkhashi Moghaddam Date: Tue, 25 Jul 2023 16:21:07 +0200 Subject: [PATCH] Add script for verification of evm contract upgrades (#982) --- contract_manager/scripts/check_proposal.ts | 79 +++++++++++++++++++ .../{examples => scripts}/deploy_cosmwasm.ts | 0 contract_manager/src/contracts/evm.ts | 9 ++- 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 contract_manager/scripts/check_proposal.ts rename contract_manager/{examples => scripts}/deploy_cosmwasm.ts (100%) diff --git a/contract_manager/scripts/check_proposal.ts b/contract_manager/scripts/check_proposal.ts new file mode 100644 index 00000000..597765fb --- /dev/null +++ b/contract_manager/scripts/check_proposal.ts @@ -0,0 +1,79 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { EvmChain } from "../src/chains"; +import { DefaultStore } from "../src/store"; +import { + EvmUpgradeContract, + getProposalInstructions, + MultisigParser, + WormholeMultisigInstruction, +} from "xc_admin_common"; +import SquadsMesh from "@sqds/mesh"; +import { + getPythClusterApiUrl, + PythCluster, +} from "@pythnetwork/client/lib/cluster"; +import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; +import { AccountMeta, Keypair, PublicKey } from "@solana/web3.js"; +import { EvmContract } from "../src/contracts/evm"; + +const parser = yargs(hideBin(process.argv)) + .scriptName("check_proposal.ts") + .usage("Usage: $0 --cluster --proposal ") + .options({ + cluster: { + type: "string", + demandOption: true, + desc: "Multsig Cluster name to check proposal on can be one of [devnet, testnet, mainnet-beta]", + }, + proposal: { + type: "string", + demandOption: true, + desc: "The proposal address to check", + }, + }); + +async function main() { + const argv = await parser.argv; + const cluster = argv.cluster as PythCluster; + const squad = SquadsMesh.endpoint( + getPythClusterApiUrl(cluster), + new NodeWallet(Keypair.generate()) // dummy wallet + ); + const transaction = await squad.getTransaction(new PublicKey(argv.proposal)); + const instructions = await getProposalInstructions(squad, transaction); + const multisigParser = MultisigParser.fromCluster(cluster); + const parsedInstructions = instructions.map((instruction) => { + return multisigParser.parseInstruction({ + programId: instruction.programId, + data: instruction.data as Buffer, + keys: instruction.keys as AccountMeta[], + }); + }); + + for (const instruction of parsedInstructions) { + if (instruction instanceof WormholeMultisigInstruction) { + if (instruction.governanceAction instanceof EvmUpgradeContract) { + console.log( + `Verifying EVM Upgrade Contract on ${instruction.governanceAction.targetChainId}` + ); + for (const chain of Object.values(DefaultStore.chains)) { + if ( + chain instanceof EvmChain && + chain.isMainnet() === (cluster === "mainnet-beta") && + chain.wormholeChainName === + instruction.governanceAction.targetChainId + ) { + const address = instruction.governanceAction.address; + const contract = new EvmContract(chain, address); + const code = await contract.getCodeDigestWithoutAddress(); + // this should be the same keccak256 of the deployedCode property generated by truffle + console.log(`Address:${address} digest:${code}`); + } + } + } + } + } +} + +main(); diff --git a/contract_manager/examples/deploy_cosmwasm.ts b/contract_manager/scripts/deploy_cosmwasm.ts similarity index 100% rename from contract_manager/examples/deploy_cosmwasm.ts rename to contract_manager/scripts/deploy_cosmwasm.ts diff --git a/contract_manager/src/contracts/evm.ts b/contract_manager/src/contracts/evm.ts index 340cfa18..316e83de 100644 --- a/contract_manager/src/contracts/evm.ts +++ b/contract_manager/src/contracts/evm.ts @@ -284,8 +284,13 @@ export class EvmContract extends Contract { } /** - * Returns the keccak256 digest of the contract bytecode after replacing any occurences of the contract addr in the bytecode with 0 - * This is used to verify that the contract code is the same on all chains + * Returns the keccak256 digest of the contract bytecode after replacing any occurrences of the contract addr in + * the bytecode with 0.The bytecode stores the deployment address as an immutable variable. + * This behavior is inherited from OpenZeppelin's implementation of UUPSUpgradeable contract. + * You can read more about verification with immutable variables here: + * https://docs.sourcify.dev/docs/immutables/ + * This function can be used to verify that the contract code is the same on all chains and matches + * with the deployedCode property generated by truffle builds */ async getCodeDigestWithoutAddress(): Promise { const code = await this.getCode();