check_proposal.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import yargs from "yargs";
  2. import { hideBin } from "yargs/helpers";
  3. import { CosmWasmChain, EvmChain } from "../src/chains";
  4. import { createHash } from "crypto";
  5. import { DefaultStore } from "../src/store";
  6. import {
  7. CosmosUpgradeContract,
  8. EvmSetWormholeAddress,
  9. EvmUpgradeContract,
  10. getProposalInstructions,
  11. MultisigParser,
  12. WormholeMultisigInstruction,
  13. } from "xc_admin_common";
  14. import SquadsMesh from "@sqds/mesh";
  15. import {
  16. getPythClusterApiUrl,
  17. PythCluster,
  18. } from "@pythnetwork/client/lib/cluster";
  19. import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
  20. import { AccountMeta, Keypair, PublicKey } from "@solana/web3.js";
  21. import {
  22. EvmPriceFeedContract,
  23. WormholeEvmContract,
  24. } from "../src/contracts/evm";
  25. import Web3 from "web3";
  26. const parser = yargs(hideBin(process.argv))
  27. .usage("Usage: $0 --cluster <cluster_id> --proposal <proposal_address>")
  28. .options({
  29. cluster: {
  30. type: "string",
  31. demandOption: true,
  32. desc: "Multsig Cluster name to check proposal on can be one of [devnet, testnet, mainnet-beta]",
  33. },
  34. proposal: {
  35. type: "string",
  36. demandOption: true,
  37. desc: "The proposal address to check",
  38. },
  39. });
  40. async function main() {
  41. const argv = await parser.argv;
  42. const cluster = argv.cluster as PythCluster;
  43. const squad = SquadsMesh.endpoint(
  44. getPythClusterApiUrl(cluster),
  45. new NodeWallet(Keypair.generate()) // dummy wallet
  46. );
  47. const transaction = await squad.getTransaction(new PublicKey(argv.proposal));
  48. const instructions = await getProposalInstructions(squad, transaction);
  49. const multisigParser = MultisigParser.fromCluster(cluster);
  50. const parsedInstructions = instructions.map((instruction) => {
  51. return multisigParser.parseInstruction({
  52. programId: instruction.programId,
  53. data: instruction.data as Buffer,
  54. keys: instruction.keys as AccountMeta[],
  55. });
  56. });
  57. for (const instruction of parsedInstructions) {
  58. if (instruction instanceof WormholeMultisigInstruction) {
  59. if (instruction.governanceAction instanceof EvmSetWormholeAddress) {
  60. console.log(
  61. `Verifying EVM set wormhole address on ${instruction.governanceAction.targetChainId}`
  62. );
  63. for (const chain of Object.values(DefaultStore.chains)) {
  64. if (
  65. chain instanceof EvmChain &&
  66. chain.wormholeChainName ===
  67. instruction.governanceAction.targetChainId
  68. ) {
  69. const address = instruction.governanceAction.address;
  70. const contract = new WormholeEvmContract(chain, address);
  71. const currentIndex = await contract.getCurrentGuardianSetIndex();
  72. const guardianSet = await contract.getGuardianSet();
  73. const proxyContract = new EvmPriceFeedContract(chain, address);
  74. const proxyCode = await proxyContract.getCode();
  75. const receiverImplementation =
  76. await proxyContract.getImplementationAddress();
  77. const implementationCode = await new EvmPriceFeedContract(
  78. chain,
  79. receiverImplementation
  80. ).getCode();
  81. const proxyDigest = Web3.utils.keccak256(proxyCode);
  82. const implementationDigest =
  83. Web3.utils.keccak256(implementationCode);
  84. const guardianSetDigest = Web3.utils.keccak256(
  85. JSON.stringify(guardianSet)
  86. );
  87. console.log(
  88. `${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}`
  89. );
  90. }
  91. }
  92. }
  93. if (instruction.governanceAction instanceof EvmUpgradeContract) {
  94. console.log(
  95. `Verifying EVM Upgrade Contract on ${instruction.governanceAction.targetChainId}`
  96. );
  97. for (const chain of Object.values(DefaultStore.chains)) {
  98. if (
  99. chain instanceof EvmChain &&
  100. chain.isMainnet() === (cluster === "mainnet-beta") &&
  101. chain.wormholeChainName ===
  102. instruction.governanceAction.targetChainId
  103. ) {
  104. const address = instruction.governanceAction.address;
  105. const contract = new EvmPriceFeedContract(chain, address);
  106. const code = await contract.getCodeDigestWithoutAddress();
  107. // this should be the same keccak256 of the deployedCode property generated by truffle
  108. console.log(`${chain.getId()} Address:${address} digest:${code}`);
  109. }
  110. }
  111. }
  112. if (instruction.governanceAction instanceof CosmosUpgradeContract) {
  113. console.log(
  114. `Verifying Cosmos Upgrade Contract on ${instruction.governanceAction.targetChainId}`
  115. );
  116. for (const chain of Object.values(DefaultStore.chains)) {
  117. if (
  118. chain instanceof CosmWasmChain &&
  119. chain.wormholeChainName ===
  120. instruction.governanceAction.targetChainId
  121. ) {
  122. const codeId = instruction.governanceAction.codeId;
  123. const code = await chain.getCode(Number(codeId));
  124. // this should be the same checksums.txt in our release file
  125. console.log(
  126. `${chain.getId()} Code Id:${codeId} digest:${createHash("sha256")
  127. .update(code)
  128. .digest("hex")}`
  129. );
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. main();