check_proposal.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. EvmExecute,
  9. EvmSetWormholeAddress,
  10. EvmUpgradeContract,
  11. getProposalInstructions,
  12. MultisigParser,
  13. WormholeMultisigInstruction,
  14. } from "xc_admin_common";
  15. import SquadsMesh from "@sqds/mesh";
  16. import {
  17. getPythClusterApiUrl,
  18. PythCluster,
  19. } from "@pythnetwork/client/lib/cluster";
  20. import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
  21. import { AccountMeta, Keypair, PublicKey } from "@solana/web3.js";
  22. import {
  23. EvmEntropyContract,
  24. EvmPriceFeedContract,
  25. getCodeDigestWithoutAddress,
  26. EvmWormholeContract,
  27. } from "../src/contracts/evm";
  28. import Web3 from "web3";
  29. const parser = yargs(hideBin(process.argv))
  30. .usage("Usage: $0 --cluster <cluster_id> --proposal <proposal_address>")
  31. .options({
  32. cluster: {
  33. type: "string",
  34. demandOption: true,
  35. desc: "Multsig Cluster name to check proposal on can be one of [devnet, testnet, mainnet-beta]",
  36. },
  37. proposal: {
  38. type: "string",
  39. demandOption: true,
  40. desc: "The proposal address to check",
  41. },
  42. });
  43. async function main() {
  44. const argv = await parser.argv;
  45. const cluster = argv.cluster as PythCluster;
  46. const squad = SquadsMesh.endpoint(
  47. getPythClusterApiUrl(cluster),
  48. new NodeWallet(Keypair.generate()) // dummy wallet
  49. );
  50. const transaction = await squad.getTransaction(new PublicKey(argv.proposal));
  51. const instructions = await getProposalInstructions(squad, transaction);
  52. const multisigParser = MultisigParser.fromCluster(cluster);
  53. const parsedInstructions = instructions.map((instruction) => {
  54. return multisigParser.parseInstruction({
  55. programId: instruction.programId,
  56. data: instruction.data as Buffer,
  57. keys: instruction.keys as AccountMeta[],
  58. });
  59. });
  60. for (const instruction of parsedInstructions) {
  61. if (instruction instanceof WormholeMultisigInstruction) {
  62. if (instruction.governanceAction instanceof EvmSetWormholeAddress) {
  63. console.log(
  64. `Verifying EVM set wormhole address on ${instruction.governanceAction.targetChainId}`
  65. );
  66. for (const chain of Object.values(DefaultStore.chains)) {
  67. if (
  68. chain instanceof EvmChain &&
  69. chain.wormholeChainName ===
  70. instruction.governanceAction.targetChainId
  71. ) {
  72. const address = instruction.governanceAction.address;
  73. const contract = new EvmWormholeContract(chain, address);
  74. const currentIndex = await contract.getCurrentGuardianSetIndex();
  75. const guardianSet = await contract.getGuardianSet();
  76. const proxyContract = new EvmPriceFeedContract(chain, address);
  77. const proxyCode = await proxyContract.getCode();
  78. const receiverImplementation =
  79. await proxyContract.getImplementationAddress();
  80. const implementationCode = await new EvmPriceFeedContract(
  81. chain,
  82. receiverImplementation
  83. ).getCode();
  84. const proxyDigest = Web3.utils.keccak256(proxyCode);
  85. const implementationDigest =
  86. Web3.utils.keccak256(implementationCode);
  87. const guardianSetDigest = Web3.utils.keccak256(
  88. JSON.stringify(guardianSet)
  89. );
  90. console.log(
  91. `${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}`
  92. );
  93. }
  94. }
  95. }
  96. if (instruction.governanceAction instanceof EvmUpgradeContract) {
  97. console.log(
  98. `Verifying EVM Upgrade Contract on ${instruction.governanceAction.targetChainId}`
  99. );
  100. for (const chain of Object.values(DefaultStore.chains)) {
  101. if (
  102. chain instanceof EvmChain &&
  103. chain.isMainnet() === (cluster === "mainnet-beta") &&
  104. chain.wormholeChainName ===
  105. instruction.governanceAction.targetChainId
  106. ) {
  107. const address = instruction.governanceAction.address;
  108. const contract = new EvmPriceFeedContract(chain, address);
  109. const code = await contract.getCodeDigestWithoutAddress();
  110. // this should be the same keccak256 of the deployedCode property generated by truffle
  111. console.log(`${chain.getId()} Address:${address} digest:${code}`);
  112. }
  113. }
  114. }
  115. if (instruction.governanceAction instanceof CosmosUpgradeContract) {
  116. console.log(
  117. `Verifying Cosmos Upgrade Contract on ${instruction.governanceAction.targetChainId}`
  118. );
  119. for (const chain of Object.values(DefaultStore.chains)) {
  120. if (
  121. chain instanceof CosmWasmChain &&
  122. chain.wormholeChainName ===
  123. instruction.governanceAction.targetChainId
  124. ) {
  125. const codeId = instruction.governanceAction.codeId;
  126. const code = await chain.getCode(Number(codeId));
  127. // this should be the same checksums.txt in our release file
  128. console.log(
  129. `${chain.getId()} Code Id:${codeId} digest:${createHash("sha256")
  130. .update(code)
  131. .digest("hex")}`
  132. );
  133. }
  134. }
  135. }
  136. if (instruction.governanceAction instanceof EvmExecute) {
  137. // Note: it only checks for upgrade entropy contracts right now
  138. console.log(
  139. `Verifying EVMExecute Contract on ${instruction.governanceAction.targetChainId}`
  140. );
  141. for (const chain of Object.values(DefaultStore.chains)) {
  142. if (
  143. chain instanceof EvmChain &&
  144. chain.wormholeChainName ===
  145. instruction.governanceAction.targetChainId
  146. ) {
  147. const executorAddress =
  148. instruction.governanceAction.executorAddress;
  149. const callAddress = instruction.governanceAction.callAddress;
  150. const calldata = instruction.governanceAction.calldata;
  151. // currently executor is only being used by the entropy contract
  152. const contract = new EvmEntropyContract(chain, callAddress);
  153. const owner = await contract.getOwner();
  154. if (
  155. executorAddress.toUpperCase() !==
  156. owner.replace("0x", "").toUpperCase()
  157. ) {
  158. console.log(
  159. `Executor Address: ${executorAddress.toUpperCase()} is not equal to Owner Address: ${owner
  160. .replace("0x", "")
  161. .toUpperCase()}`
  162. );
  163. continue;
  164. }
  165. const calldataHex = calldata.toString("hex");
  166. const web3 = new Web3();
  167. const methodSignature = web3.eth.abi
  168. .encodeFunctionSignature("upgradeTo(address)")
  169. .replace("0x", "");
  170. let newImplementationAddress: string | undefined = undefined;
  171. if (calldataHex.startsWith(methodSignature)) {
  172. newImplementationAddress = web3.eth.abi.decodeParameter(
  173. "address",
  174. calldataHex.replace(methodSignature, "")
  175. ) as unknown as string;
  176. }
  177. if (newImplementationAddress === undefined) {
  178. console.log(
  179. `We couldn't parse the instruction for ${chain.getId()}`
  180. );
  181. continue;
  182. }
  183. const newImplementationCode = await getCodeDigestWithoutAddress(
  184. chain.getRpcUrl(),
  185. newImplementationAddress
  186. );
  187. // this should be the same keccak256 of the deployedCode property generated by truffle
  188. console.log(
  189. `${chain.getId()} new implementation address:${newImplementationAddress} digest:${newImplementationCode}`
  190. );
  191. }
  192. }
  193. }
  194. }
  195. }
  196. }
  197. main();