| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import yargs from "yargs";
- import { hideBin } from "yargs/helpers";
- import { CosmWasmChain, EvmChain } from "../src/chains";
- import { createHash } from "crypto";
- import { DefaultStore } from "../src/store";
- import {
- CosmosUpgradeContract,
- EvmExecute,
- EvmSetWormholeAddress,
- EvmUpgradeContract,
- getProposalInstructions,
- MultisigParser,
- WormholeMultisigInstruction,
- } from "@pythnetwork/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 {
- EvmEntropyContract,
- EvmPriceFeedContract,
- getCodeDigestWithoutAddress,
- EvmWormholeContract,
- } from "../src/contracts/evm";
- import Web3 from "web3";
- const parser = yargs(hideBin(process.argv))
- .usage("Usage: $0 --cluster <cluster_id> --proposal <proposal_address>")
- .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 EvmSetWormholeAddress) {
- console.log(
- `Verifying EVM set wormhole address on ${instruction.governanceAction.targetChainId}`,
- );
- for (const chain of Object.values(DefaultStore.chains)) {
- if (
- chain instanceof EvmChain &&
- chain.wormholeChainName ===
- instruction.governanceAction.targetChainId
- ) {
- const address = instruction.governanceAction.address;
- const contract = new EvmWormholeContract(chain, address);
- const currentIndex = await contract.getCurrentGuardianSetIndex();
- const guardianSet = await contract.getGuardianSet();
- const proxyContract = new EvmPriceFeedContract(chain, address);
- const proxyCode = await proxyContract.getCode();
- const receiverImplementation =
- await proxyContract.getImplementationAddress();
- const implementationCode = await new EvmPriceFeedContract(
- chain,
- receiverImplementation,
- ).getCode();
- const proxyDigest = Web3.utils.keccak256(proxyCode);
- const implementationDigest =
- Web3.utils.keccak256(implementationCode);
- const guardianSetDigest = Web3.utils.keccak256(
- JSON.stringify(guardianSet),
- );
- console.log(
- `${chain.getId()} Address:\t\t${address}\nproxy digest:\t\t${proxyDigest}\nimplementation digest:\t${implementationDigest} \nguardian set index:\t${currentIndex} \nguardian set:\t\t${guardianSetDigest}`,
- );
- }
- }
- }
- 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 EvmPriceFeedContract(chain, address);
- const code = await contract.getCodeDigestWithoutAddress();
- // this should be the same keccak256 of the deployedCode property generated by truffle
- console.log(`${chain.getId()} Address:${address} digest:${code}`);
- }
- }
- }
- if (instruction.governanceAction instanceof CosmosUpgradeContract) {
- console.log(
- `Verifying Cosmos Upgrade Contract on ${instruction.governanceAction.targetChainId}`,
- );
- for (const chain of Object.values(DefaultStore.chains)) {
- if (
- chain instanceof CosmWasmChain &&
- chain.wormholeChainName ===
- instruction.governanceAction.targetChainId
- ) {
- const codeId = instruction.governanceAction.codeId;
- const code = await chain.getCode(Number(codeId));
- // this should be the same checksums.txt in our release file
- console.log(
- `${chain.getId()} Code Id:${codeId} digest:${createHash("sha256")
- .update(code)
- .digest("hex")}`,
- );
- }
- }
- }
- if (instruction.governanceAction instanceof EvmExecute) {
- // Note: it only checks for upgrade entropy contracts right now
- console.log(
- `Verifying EVMExecute on ${instruction.governanceAction.targetChainId}`,
- );
- for (const chain of Object.values(DefaultStore.chains)) {
- if (
- chain instanceof EvmChain &&
- chain.wormholeChainName ===
- instruction.governanceAction.targetChainId
- ) {
- const executorAddress =
- instruction.governanceAction.executorAddress;
- const callAddress = instruction.governanceAction.callAddress;
- const calldata = instruction.governanceAction.calldata;
- // TODO: If we add additional EVM contracts using the executor, we need to
- // add some logic here to identify what kind of contract is at the call address.
- const contract = new EvmEntropyContract(chain, callAddress);
- const owner = await contract.getOwner();
- if (
- executorAddress.toUpperCase() !==
- owner.replace("0x", "").toUpperCase()
- ) {
- console.log(
- `Executor Address: ${executorAddress.toUpperCase()} is not equal to Owner Address: ${owner
- .replace("0x", "")
- .toUpperCase()}`,
- );
- continue;
- }
- // TODO: This logic assumes we are calling upgradeTo on the contract at callAddress.
- // In the future, this logic may need to be generalized to support calling other functions.
- const invokedMethod = "upgradeTo(address)";
- const calldataHex = calldata.toString("hex");
- const web3 = new Web3();
- const methodSignature = web3.eth.abi
- .encodeFunctionSignature(invokedMethod)
- .replace("0x", "");
- let newImplementationAddress: string | undefined = undefined;
- if (calldataHex.startsWith(methodSignature)) {
- newImplementationAddress = web3.eth.abi.decodeParameter(
- "address",
- calldataHex.replace(methodSignature, ""),
- ) as unknown as string;
- }
- if (newImplementationAddress === undefined) {
- console.log(
- `We couldn't parse the instruction for ${chain.getId()}`,
- );
- continue;
- }
- const newImplementationCode = await getCodeDigestWithoutAddress(
- chain.getWeb3(),
- newImplementationAddress,
- );
- // this should be the same keccak256 of the deployedCode property generated by truffle
- console.log(
- `${chain.getId()} call ${invokedMethod} with arguments (${newImplementationAddress}) on ${contract.getType()} at address:${callAddress} from executor:${executorAddress}.`,
- );
- console.log(
- `${chain.getId()} new implementation address:${newImplementationAddress} has code digest:${newImplementationCode}`,
- );
- }
- }
- }
- }
- }
- }
- main();
|