upgrade_pyth.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock";
  2. import dotenv from "dotenv";
  3. import {
  4. RawSigner,
  5. SUI_CLOCK_OBJECT_ID,
  6. TransactionBlock,
  7. fromB64,
  8. normalizeSuiObjectId,
  9. JsonRpcProvider,
  10. Ed25519Keypair,
  11. testnetConnection,
  12. Connection,
  13. } from "@mysten/sui.js";
  14. import { execSync } from "child_process";
  15. import { resolve } from "path";
  16. import * as fs from "fs";
  17. import { REGISTRY, NETWORK } from "../registry";
  18. dotenv.config({ path: "~/.env" });
  19. // Network dependent settings.
  20. let network = NETWORK.TESTNET; // <= NOTE: Update this when changing network
  21. const walletPrivateKey = process.env.SUI_TESTNET_ALT_KEY_BASE_64 // <= NOTE: Update this when changing network
  22. const guardianPrivateKey = process.env.WH_TESTNET_GUARDIAN_PRIVATE_KEY
  23. const registry = REGISTRY[network];
  24. const provider = new JsonRpcProvider(
  25. new Connection({ fullnode: registry["RPC_URL"] })
  26. );
  27. const PYTH_STATE_ID = registry["PYTH_STATE_ID"]
  28. const PYTH_PACKAGE_ID = registry["PYTH_PACKAGE_ID"]
  29. const WORMHOLE_STATE_ID = registry["WORMHOLE_STATE_ID"]
  30. const WORMHOLE_PACKAGE_ID = registry["WORMHOLE_PACKAGE_ID"]
  31. console.log("WORMHOLE_STATE_ID: ", WORMHOLE_STATE_ID)
  32. console.log("PYTH_STATE_ID: ", WORMHOLE_STATE_ID)
  33. const GOVERNANCE_EMITTER =
  34. //"0000000000000000000000000000000000000000000000000000000000000004";
  35. "63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385";
  36. async function main() {
  37. if (guardianPrivateKey === undefined) {
  38. throw new Error("TESTNET_GUARDIAN_PRIVATE_KEY unset in environment");
  39. }
  40. if (walletPrivateKey === undefined) {
  41. throw new Error("TESTNET_WALLET_PRIVATE_KEY unset in environment");
  42. }
  43. console.log("priv key: ", walletPrivateKey)
  44. const wallet = new RawSigner(
  45. Ed25519Keypair.fromSecretKey(
  46. network == "MAINNET"
  47. ? Buffer.from(walletPrivateKey, "hex")
  48. : Buffer.from(walletPrivateKey, "base64")
  49. ),
  50. provider
  51. );
  52. console.log("wallet address: ", wallet.getAddress());
  53. const pythContractsPath = resolve(`${__dirname}/../../contracts`);
  54. // Build for digest.
  55. const { modules, dependencies, digest } =
  56. buildForBytecodeAndDigest(pythContractsPath);
  57. console.log("dependencies", dependencies);
  58. console.log("digest", digest.toString("hex"));
  59. // We will use the signed VAA when we execute the upgrade.
  60. const guardians = new mock.MockGuardians(0, [guardianPrivateKey]);
  61. const timestamp = 12345678;
  62. const governance = new mock.GovernanceEmitter(GOVERNANCE_EMITTER);
  63. //const module = Buffer.alloc(32)
  64. //module.write("1", 31)
  65. const module = "1"
  66. const action = 0
  67. const chain = 21
  68. // construct payload
  69. const magic = Buffer.alloc(4);
  70. magic.write("PTGM", 0); // magic
  71. console.log("magic buffer: ", magic)
  72. let inner_payload = Buffer.alloc(100)
  73. inner_payload.write(magic.toString(), 0) // magic = "PTGM"
  74. inner_payload.writeUInt8(1, 4); // moduleName = 1
  75. inner_payload.writeUInt8(0, 5); // action = 0
  76. inner_payload.writeUInt16BE(21, 6); // target chain = 21
  77. // create governance message
  78. let msg = governance.publishGovernanceMessage(timestamp, module, inner_payload, action, chain)
  79. // const published = governance.publishWormholeUpgradeContract(
  80. // timestamp,
  81. // 2,
  82. // "0x" + digest.toString("hex") // where is contract address (digest) used, if at all?
  83. // );
  84. // sign governance message
  85. const signedVaa = guardians.addSignatures(msg, [0]);
  86. console.log("Upgrade VAA:", signedVaa.toString("hex"));
  87. // // And execute upgrade with governance VAA.
  88. const upgradeResults = await upgradePyth(
  89. wallet,
  90. PYTH_STATE_ID,
  91. WORMHOLE_STATE_ID,
  92. modules,
  93. dependencies,
  94. signedVaa
  95. );
  96. console.log("tx digest", upgradeResults.digest);
  97. console.log("tx effects", JSON.stringify(upgradeResults.effects!));
  98. console.log("tx events", JSON.stringify(upgradeResults.events!));
  99. // const migrateResults = await migratePyth(
  100. // wallet,
  101. // PYTH_STATE_ID,
  102. // WORMHOLE_STATE_ID,
  103. // signedVaa
  104. // );
  105. // console.log("tx digest", migrateResults.digest);
  106. // console.log("tx effects", JSON.stringify(migrateResults.effects!));
  107. // console.log("tx events", JSON.stringify(migrateResults.events!));
  108. }
  109. main();
  110. function buildForBytecodeAndDigest(packagePath: string) {
  111. const buildOutput: {
  112. modules: string[];
  113. dependencies: string[];
  114. digest: number[];
  115. } = JSON.parse(
  116. execSync(
  117. `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`,
  118. { encoding: "utf-8" }
  119. )
  120. );
  121. return {
  122. modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))),
  123. dependencies: buildOutput.dependencies.map((d: string) =>
  124. normalizeSuiObjectId(d)
  125. ),
  126. digest: Buffer.from(buildOutput.digest),
  127. };
  128. }
  129. async function getPackageId(
  130. provider: JsonRpcProvider,
  131. stateId: string
  132. ): Promise<string> {
  133. const state = await provider
  134. .getObject({
  135. id: stateId,
  136. options: {
  137. showContent: true,
  138. },
  139. })
  140. .then((result) => {
  141. if (result.data?.content?.dataType == "moveObject") {
  142. return result.data.content.fields;
  143. }
  144. throw new Error("not move object");
  145. });
  146. if ("upgrade_cap" in state) {
  147. return state.upgrade_cap.fields.package;
  148. }
  149. throw new Error("upgrade_cap not found");
  150. }
  151. async function upgradePyth(
  152. signer: RawSigner,
  153. pythStateId: string,
  154. wormholeStateId: string,
  155. modules: number[][],
  156. dependencies: string[],
  157. signedVaa: Buffer
  158. ) {
  159. const pythPackage = await getPackageId(
  160. signer.provider,
  161. pythStateId
  162. );
  163. const wormholePackage = await getPackageId(signer.provider, wormholeStateId);
  164. console.log("pythPackage: ", pythPackage)
  165. console.log("wormholePackage: ", wormholePackage)
  166. const tx = new TransactionBlock();
  167. // this works
  168. const [verifiedVaa] = tx.moveCall({
  169. target: `${wormholePackage}::vaa::parse_and_verify`,
  170. arguments: [
  171. tx.object(wormholeStateId),
  172. tx.pure(Array.from(signedVaa)),
  173. tx.object(SUI_CLOCK_OBJECT_ID),
  174. ],
  175. });
  176. // does this work?
  177. const [decreeTicket] = tx.moveCall({
  178. target: `${pythPackage}::contract_upgrade::authorize_governance`,
  179. arguments: [tx.object(pythStateId)],
  180. });
  181. const [decreeReceipt] = tx.moveCall({
  182. target: `${wormholePackage}::governance_message::verify_vaa`,
  183. arguments: [tx.object(wormholeStateId), verifiedVaa, decreeTicket],
  184. typeArguments: [
  185. `${pythPackage}::governance_witness::GovernanceWitness`,
  186. ],
  187. });
  188. // // Authorize upgrade.
  189. // const [upgradeTicket] = tx.moveCall({
  190. // target: `${pythPackage}::contract_upgrade::authorize_upgrade`,
  191. // arguments: [tx.object(pythStateId), decreeReceipt],
  192. // });
  193. // // Build and generate modules and dependencies for upgrade.
  194. // const [upgradeReceipt] = tx.upgrade({
  195. // modules,
  196. // dependencies,
  197. // packageId: pythPackage,
  198. // ticket: upgradeTicket,
  199. // });
  200. // // Commit upgrade.
  201. // tx.moveCall({
  202. // target: `${pythPackage}::contract_upgrade::commit_upgrade`,
  203. // arguments: [tx.object(pythStateId), upgradeReceipt],
  204. // });
  205. tx.setGasBudget(2_000_000_000n);
  206. return signer.signAndExecuteTransactionBlock({
  207. transactionBlock: tx,
  208. options: {
  209. showEffects: true,
  210. showEvents: true,
  211. },
  212. });
  213. }
  214. async function migratePyth(
  215. signer: RawSigner,
  216. pythStateId: string,
  217. wormholeStateId: string,
  218. signedUpgradeVaa: Buffer
  219. ) {
  220. const pythPackage = await getPackageId(
  221. signer.provider,
  222. pythStateId
  223. );
  224. const wormholePackage = await getPackageId(signer.provider, wormholeStateId);
  225. const tx = new TransactionBlock();
  226. const [verifiedVaa] = tx.moveCall({
  227. target: `${wormholePackage}::vaa::parse_and_verify`,
  228. arguments: [
  229. tx.object(wormholeStateId),
  230. tx.pure(Array.from(signedUpgradeVaa)),
  231. tx.object(SUI_CLOCK_OBJECT_ID),
  232. ],
  233. });
  234. const [decreeTicket] = tx.moveCall({
  235. target: `${pythPackage}::contract_upgrade::authorize_governance`,
  236. arguments: [tx.object(pythStateId)],
  237. });
  238. const [decreeReceipt] = tx.moveCall({
  239. target: `${wormholePackage}::governance_message::verify_vaa`,
  240. arguments: [tx.object(wormholeStateId), verifiedVaa, decreeTicket],
  241. typeArguments: [
  242. `${pythPackage}::governance_witness::GovernanceWitness`,
  243. ],
  244. });
  245. tx.moveCall({
  246. target: `${pythPackage}::migrate::migrate`,
  247. arguments: [tx.object(pythStateId), decreeReceipt],
  248. });
  249. return signer.signAndExecuteTransactionBlock({
  250. transactionBlock: tx,
  251. options: {
  252. showEffects: true,
  253. showEvents: true,
  254. },
  255. });
  256. }