浏览代码

chore(governance): add starknet chain id and governance actions (#1572)

* chore(governance): add starknet chain id and governance actions, add PayloadByteRepr test

* test(governance): extend GovernancePayload test with starknet types

* fix(target_chains/starknet): use UpgradeContract256Bit to avoid ambiguity when decoding
Pavel Strakhov 1 年之前
父节点
当前提交
5bf3848d11

+ 3 - 10
contract_manager/src/chains.ts

@@ -4,13 +4,12 @@ import {
   SetFee,
   CosmosUpgradeContract,
   EvmUpgradeContract,
-  SuiAuthorizeUpgradeContract,
-  AptosAuthorizeUpgradeContract,
   toChainId,
   SetDataSources,
   SetValidPeriod,
   DataSource,
   EvmSetWormholeAddress,
+  UpgradeContract256Bit,
 } from "xc_admin_common";
 import { AptosClient, AptosAccount, CoinClient, TxnBuilderTypes } from "aptos";
 import Web3 from "web3";
@@ -294,10 +293,7 @@ export class SuiChain extends Chain {
    * @param digest hex string of the 32 byte digest for the new package without the 0x prefix
    */
   generateGovernanceUpgradePayload(digest: string): Buffer {
-    return new SuiAuthorizeUpgradeContract(
-      this.wormholeChainName,
-      digest
-    ).encode();
+    return new UpgradeContract256Bit(this.wormholeChainName, digest).encode();
   }
 
   getProvider(): SuiClient {
@@ -496,10 +492,7 @@ export class AptosChain extends Chain {
    * @param digest hex string of the 32 byte digest for the new package without the 0x prefix
    */
   generateGovernanceUpgradePayload(digest: string): Buffer {
-    return new AptosAuthorizeUpgradeContract(
-      this.wormholeChainName,
-      digest
-    ).encode();
+    return new UpgradeContract256Bit(this.wormholeChainName, digest).encode();
   }
 
   getType(): string {

+ 111 - 16
governance/xc_admin/packages/xc_admin_common/src/__tests__/GovernancePayload.test.ts

@@ -12,15 +12,15 @@ import {
   EvmSetWormholeAddress,
   EvmExecutorAction,
   EvmExecute,
+  StarknetSetWormholeAddress,
 } from "..";
 import * as fc from "fast-check";
 import { ChainName, CHAINS } from "../chains";
 import { Arbitrary, IntArrayConstraints } from "fast-check";
 import {
-  AptosAuthorizeUpgradeContract,
   CosmosUpgradeContract,
   EvmUpgradeContract,
-  SuiAuthorizeUpgradeContract,
+  UpgradeContract256Bit,
 } from "../governance_payload/UpgradeContract";
 import {
   AuthorizeGovernanceDataSourceTransfer,
@@ -159,6 +159,101 @@ test("GovernancePayload ser/de", (done) => {
     )
   );
 
+  const requestGovernanceDataSourceTransfer =
+    new RequestGovernanceDataSourceTransfer("starknet", 1);
+  const requestGovernanceDataSourceTransferBuffer =
+    requestGovernanceDataSourceTransfer.encode();
+  console.log(requestGovernanceDataSourceTransferBuffer.toJSON());
+  expect(
+    requestGovernanceDataSourceTransferBuffer.equals(
+      Buffer.from([80, 84, 71, 77, 1, 5, 234, 147, 0, 0, 0, 1])
+    )
+  ).toBeTruthy();
+
+  const authorizeGovernanceDataSourceTransfer =
+    new AuthorizeGovernanceDataSourceTransfer(
+      "starknet",
+      Buffer.from([1, 2, 3])
+    );
+  const authorizeGovernanceDataSourceTransferBuffer =
+    authorizeGovernanceDataSourceTransfer.encode();
+  console.log(authorizeGovernanceDataSourceTransferBuffer.toJSON());
+  expect(
+    authorizeGovernanceDataSourceTransferBuffer.equals(
+      Buffer.from([80, 84, 71, 77, 1, 1, 234, 147, 1, 2, 3])
+    )
+  ).toBeTruthy();
+
+  const setFee = new SetFee("starknet", 42n, 8n);
+  const setFeeBuffer = setFee.encode();
+  console.log(setFeeBuffer.toJSON());
+  expect(
+    setFeeBuffer.equals(
+      Buffer.from([
+        80, 84, 71, 77, 1, 3, 234, 147, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0,
+        0, 0, 8,
+      ])
+    )
+  ).toBeTruthy();
+
+  const setDataSources = new SetDataSources("starknet", [
+    {
+      emitterChain: 1,
+      emitterAddress:
+        "6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25",
+    },
+    {
+      emitterChain: 26,
+      emitterAddress:
+        "f8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0",
+    },
+  ]);
+  const setDataSourcesBuffer = setDataSources.encode();
+  console.log(setDataSourcesBuffer.toJSON());
+  expect(
+    setDataSourcesBuffer.equals(
+      Buffer.from([
+        80, 84, 71, 77, 1, 2, 234, 147, 2, 0, 1, 107, 177, 69, 9, 166, 18, 240,
+        31, 187, 196, 207, 254, 235, 212, 187, 251, 73, 42, 134, 223, 113, 126,
+        190, 146, 235, 109, 244, 50, 163, 240, 10, 37, 0, 26, 248, 205, 35, 194,
+        171, 145, 35, 119, 48, 119, 11, 190, 160, 141, 97, 0, 92, 221, 160, 152,
+        67, 72, 243, 246, 238, 203, 85, 150, 56, 192, 187, 160,
+      ])
+    )
+  ).toBeTruthy();
+
+  const setWormholeAddress = new StarknetSetWormholeAddress(
+    "starknet",
+    "05033f06d5c47bcce7960ea703b04a0bf64bf33f6f2eb5613496da747522d9c2"
+  );
+  const setWormholeAddressBuffer = setWormholeAddress.encode();
+  console.log(setWormholeAddressBuffer.toJSON());
+  expect(
+    setWormholeAddressBuffer.equals(
+      Buffer.from([
+        80, 84, 71, 77, 1, 6, 234, 147, 5, 3, 63, 6, 213, 196, 123, 204, 231,
+        150, 14, 167, 3, 176, 74, 11, 246, 75, 243, 63, 111, 46, 181, 97, 52,
+        150, 218, 116, 117, 34, 217, 194,
+      ])
+    )
+  ).toBeTruthy();
+
+  const upgradeContract = new UpgradeContract256Bit(
+    "starknet",
+    "043d0ed8155263af0862372df3af9403c502358661f317f62fbdc026d03beaee"
+  );
+  const upgradeContractBuffer = upgradeContract.encode();
+  console.log(upgradeContractBuffer.toJSON());
+  expect(
+    upgradeContractBuffer.equals(
+      Buffer.from([
+        80, 84, 71, 77, 1, 0, 234, 147, 4, 61, 14, 216, 21, 82, 99, 175, 8, 98,
+        55, 45, 243, 175, 148, 3, 197, 2, 53, 134, 97, 243, 23, 246, 47, 189,
+        192, 38, 208, 59, 234, 238,
+      ])
+    )
+  ).toBeTruthy();
+
   done();
 });
 
@@ -218,27 +313,19 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
       const cosmosArb = fc.bigUintN(64).map((codeId) => {
         return new CosmosUpgradeContract(header.targetChainId, codeId);
       });
-      const aptosArb = hexBytesArb({ minLength: 32, maxLength: 32 }).map(
+      const arb256bit = hexBytesArb({ minLength: 32, maxLength: 32 }).map(
         (buffer) => {
-          return new AptosAuthorizeUpgradeContract(
-            header.targetChainId,
-            buffer
-          );
+          return new UpgradeContract256Bit(header.targetChainId, buffer);
         }
       );
 
-      const suiArb = hexBytesArb({ minLength: 32, maxLength: 32 }).map(
-        (buffer) => {
-          return new SuiAuthorizeUpgradeContract(header.targetChainId, buffer);
-        }
-      );
       const evmArb = hexBytesArb({ minLength: 20, maxLength: 20 }).map(
         (address) => {
           return new EvmUpgradeContract(header.targetChainId, address);
         }
       );
 
-      return fc.oneof(cosmosArb, aptosArb, evmArb);
+      return fc.oneof(cosmosArb, arb256bit, evmArb);
     } else if (header.action === "AuthorizeGovernanceDataSourceTransfer") {
       return bufferArb().map((claimVaa) => {
         return new AuthorizeGovernanceDataSourceTransfer(
@@ -268,9 +355,17 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
         );
       });
     } else if (header.action === "SetWormholeAddress") {
-      return hexBytesArb({ minLength: 20, maxLength: 20 }).map((address) => {
-        return new EvmSetWormholeAddress(header.targetChainId, address);
-      });
+      const evmArb = hexBytesArb({ minLength: 20, maxLength: 20 }).map(
+        (address) => {
+          return new EvmSetWormholeAddress(header.targetChainId, address);
+        }
+      );
+      const starknetArb = hexBytesArb({ minLength: 32, maxLength: 32 }).map(
+        (address) => {
+          return new StarknetSetWormholeAddress(header.targetChainId, address);
+        }
+      );
+      return fc.oneof(evmArb, starknetArb);
     } else if (header.action === "Execute") {
       return fc
         .record({

+ 2 - 0
governance/xc_admin/packages/xc_admin_common/src/chains.ts

@@ -67,6 +67,7 @@ export const RECEIVER_CHAINS = {
   parallel: 60048,
   iota: 60049,
   flow_previewnet: 60050,
+  starknet: 60051,
 
   // Testnets as a separate chain ids (to use stable data sources and governance for them)
   injective_testnet: 60013,
@@ -149,6 +150,7 @@ export const RECEIVER_CHAINS = {
   olive_testnet: 50072,
   orange_testnet: 50073,
   polygon_amoy: 50074,
+  starknet_sepolia: 50075,
 };
 
 // If there is any overlapping value the receiver chain will replace the wormhole

+ 29 - 0
governance/xc_admin/packages/xc_admin_common/src/governance_payload/SetWormholeAddress.ts

@@ -31,3 +31,32 @@ export class EvmSetWormholeAddress extends PythGovernanceActionImpl {
     });
   }
 }
+
+export class StarknetSetWormholeAddress extends PythGovernanceActionImpl {
+  static layout: BufferLayout.Structure<Readonly<{ address: string }>> =
+    BufferLayout.struct([BufferLayoutExt.hexBytes(32, "address")]);
+
+  constructor(targetChainId: ChainName, readonly address: string) {
+    super(targetChainId, "SetWormholeAddress");
+  }
+
+  static decode(data: Buffer): StarknetSetWormholeAddress | undefined {
+    const decoded = PythGovernanceActionImpl.decodeWithPayload(
+      data,
+      "SetWormholeAddress",
+      this.layout
+    );
+    if (!decoded) return undefined;
+
+    return new StarknetSetWormholeAddress(
+      decoded[0].targetChainId,
+      decoded[1].address
+    );
+  }
+
+  encode(): Buffer {
+    return super.encodeWithPayload(StarknetSetWormholeAddress.layout, {
+      address: this.address,
+    });
+  }
+}

+ 5 - 36
governance/xc_admin/packages/xc_admin_common/src/governance_payload/UpgradeContract.ts

@@ -34,7 +34,8 @@ export class CosmosUpgradeContract extends PythGovernanceActionImpl {
   }
 }
 
-export class AptosAuthorizeUpgradeContract extends PythGovernanceActionImpl {
+// Used by Aptos, Sui and Starknet
+export class UpgradeContract256Bit extends PythGovernanceActionImpl {
   static layout: BufferLayout.Structure<Readonly<{ hash: string }>> =
     BufferLayout.struct([BufferLayoutExt.hexBytes(32, "hash")]);
 
@@ -42,7 +43,7 @@ export class AptosAuthorizeUpgradeContract extends PythGovernanceActionImpl {
     super(targetChainId, "UpgradeContract");
   }
 
-  static decode(data: Buffer): AptosAuthorizeUpgradeContract | undefined {
+  static decode(data: Buffer): UpgradeContract256Bit | undefined {
     const decoded = PythGovernanceActionImpl.decodeWithPayload(
       data,
       "UpgradeContract",
@@ -50,43 +51,11 @@ export class AptosAuthorizeUpgradeContract extends PythGovernanceActionImpl {
     );
     if (!decoded) return undefined;
 
-    return new AptosAuthorizeUpgradeContract(
-      decoded[0].targetChainId,
-      decoded[1].hash
-    );
-  }
-
-  encode(): Buffer {
-    return super.encodeWithPayload(AptosAuthorizeUpgradeContract.layout, {
-      hash: this.hash,
-    });
-  }
-}
-
-export class SuiAuthorizeUpgradeContract extends PythGovernanceActionImpl {
-  static layout: BufferLayout.Structure<Readonly<{ hash: string }>> =
-    BufferLayout.struct([BufferLayoutExt.hexBytes(32, "hash")]);
-
-  constructor(targetChainId: ChainName, readonly hash: string) {
-    super(targetChainId, "UpgradeContract");
-  }
-
-  static decode(data: Buffer): SuiAuthorizeUpgradeContract | undefined {
-    const decoded = PythGovernanceActionImpl.decodeWithPayload(
-      data,
-      "UpgradeContract",
-      this.layout
-    );
-    if (!decoded) return undefined;
-
-    return new SuiAuthorizeUpgradeContract(
-      decoded[0].targetChainId,
-      decoded[1].hash
-    );
+    return new UpgradeContract256Bit(decoded[0].targetChainId, decoded[1].hash);
   }
 
   encode(): Buffer {
-    return super.encodeWithPayload(SuiAuthorizeUpgradeContract.layout, {
+    return super.encodeWithPayload(UpgradeContract256Bit.layout, {
       hash: this.hash,
     });
   }

+ 22 - 7
governance/xc_admin/packages/xc_admin_common/src/governance_payload/index.ts

@@ -1,8 +1,8 @@
 import { ExecutePostedVaa } from "./ExecutePostedVaa";
 import {
-  AptosAuthorizeUpgradeContract,
   CosmosUpgradeContract,
   EvmUpgradeContract,
+  UpgradeContract256Bit,
 } from "./UpgradeContract";
 import {
   PythGovernanceAction,
@@ -15,7 +15,10 @@ import {
 import { SetDataSources } from "./SetDataSources";
 import { SetValidPeriod } from "./SetValidPeriod";
 import { SetFee } from "./SetFee";
-import { EvmSetWormholeAddress } from "./SetWormholeAddress";
+import {
+  EvmSetWormholeAddress,
+  StarknetSetWormholeAddress,
+} from "./SetWormholeAddress";
 import { EvmExecute } from "./ExecuteAction";
 
 /** Decode a governance payload */
@@ -28,20 +31,21 @@ export function decodeGovernancePayload(
   switch (header.action) {
     case "ExecutePostedVaa":
       return ExecutePostedVaa.decode(data);
-    case "UpgradeContract":
+    case "UpgradeContract": {
       // NOTE: the only way to distinguish the different types of upgrade contract instructions
       // is their payload lengths. We're getting a little lucky here that all of these upgrade instructions
       // have different-length payloads.
       const payloadLength = data.length - PythGovernanceHeader.span;
       if (payloadLength == CosmosUpgradeContract.layout.span) {
         return CosmosUpgradeContract.decode(data);
-      } else if (payloadLength == AptosAuthorizeUpgradeContract.layout.span) {
-        return AptosAuthorizeUpgradeContract.decode(data);
+      } else if (payloadLength == UpgradeContract256Bit.layout.span) {
+        return UpgradeContract256Bit.decode(data);
       } else if (payloadLength == EvmUpgradeContract.layout.span) {
         return EvmUpgradeContract.decode(data);
       } else {
         return undefined;
       }
+    }
     case "AuthorizeGovernanceDataSourceTransfer":
       return AuthorizeGovernanceDataSourceTransfer.decode(data);
     case "SetDataSources":
@@ -52,8 +56,19 @@ export function decodeGovernancePayload(
       return SetValidPeriod.decode(data);
     case "RequestGovernanceDataSourceTransfer":
       return RequestGovernanceDataSourceTransfer.decode(data);
-    case "SetWormholeAddress":
-      return EvmSetWormholeAddress.decode(data);
+    case "SetWormholeAddress": {
+      // NOTE: the only way to distinguish the different types of upgrade contract instructions
+      // is their payload lengths. We're getting a little lucky here that all of these upgrade instructions
+      // have different-length payloads.
+      const payloadLength = data.length - PythGovernanceHeader.span;
+      if (payloadLength == EvmSetWormholeAddress.layout.span) {
+        return EvmSetWormholeAddress.decode(data);
+      } else if (payloadLength == StarknetSetWormholeAddress.layout.span) {
+        return StarknetSetWormholeAddress.decode(data);
+      } else {
+        return undefined;
+      }
+    }
     case "Execute":
       return EvmExecute.decode(data);
     default:

+ 2 - 2
governance/xc_admin/packages/xc_admin_frontend/components/InstructionViews/WormholeInstructionView.tsx

@@ -1,5 +1,4 @@
 import {
-  AptosAuthorizeUpgradeContract,
   AuthorizeGovernanceDataSourceTransfer,
   CosmosUpgradeContract,
   EvmExecute,
@@ -12,6 +11,7 @@ import {
   SetDataSources,
   SetFee,
   SetValidPeriod,
+  UpgradeContract256Bit,
   WormholeMultisigInstruction,
   getProgramName,
 } from 'xc_admin_common'
@@ -293,7 +293,7 @@ export const WormholeInstructionView = ({
         />
       )}
 
-      {governanceAction instanceof AptosAuthorizeUpgradeContract && (
+      {governanceAction instanceof UpgradeContract256Bit && (
         <GovernanceInstructionView
           instruction={governanceAction}
           actionName={governanceAction.action}