Selaa lähdekoodia

SVM: Revamped Core and Bridge initialization scripts. (#4530)

* Initialization scripts.

* new script: derive-upgrade-authority

* scripts: changes to package deps  and fixes.

* initialize-core: enh guardian set addresses check
Hernán Di Pietro 1 kuukausi sitten
vanhempi
sitoutus
a3c7176923

+ 13 - 0
solana/scripts/derive-upgrade-authority.ts

@@ -0,0 +1,13 @@
+import { PublicKey } from "@solana/web3.js";
+import { Buffer } from "buffer";
+
+const tokenBridgeProgramId = process.env.TOKEN_BRIDGE_PROGRAM_ID;
+
+if (!tokenBridgeProgramId) {
+    console.error("TOKEN_BRIDGE_PROGRAM_ID environment variable not set.");
+    process.exit(1);
+}
+
+console.log(
+    PublicKey.findProgramAddressSync([Buffer.from("upgrade")], new PublicKey(tokenBridgeProgramId))[0].toString()
+);

+ 68 - 0
solana/scripts/initialize-bridge.ts

@@ -0,0 +1,68 @@
+import { createInitializeInstruction } from "@certusone/wormhole-sdk/lib/cjs/solana/tokenBridge";
+import { AnchorProvider, Wallet, web3 } from "@coral-xyz/anchor";
+import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
+
+function displayHelp() {
+    console.log(`
+  
+      The following environment variables are required:
+      
+      RPC_URL: The RPC URL of the SVM network (Fogo or Solana).
+      TOKEN_BRIDGE_PROGRAM_ID: The program ID of the token bridge program.
+      PRIVATE_KEY: The private key of the account that will be used to initialize the token bridge program.
+                   Can be a keypair-file path or a base58 encoded string.
+      CORE_BRIDGE_PROGRAM_ID: The program ID of the Core bridge program.
+      `)
+}
+
+(async () => {
+  const RPC_URL = process.env.RPC_URL;
+  if (!RPC_URL) {
+    console.error("RPC_URL is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const TOKEN_BRIDGE_PROGRAM_ID = process.env.TOKEN_BRIDGE_PROGRAM_ID;
+  if (!TOKEN_BRIDGE_PROGRAM_ID) {
+    console.error("TOKEN_BRIDGE_PROGRAM_ID is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const CORE_BRIDGE_PROGRAM_ID = process.env.CORE_BRIDGE_PROGRAM_ID;
+  if (!CORE_BRIDGE_PROGRAM_ID) {
+    console.error("CORE_BRIDGE_PROGRAM_ID is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const coreBridgeAddress = new web3.PublicKey(CORE_BRIDGE_PROGRAM_ID);
+  const tokenBridgeAddress = new web3.PublicKey(TOKEN_BRIDGE_PROGRAM_ID);
+
+  const connection = new web3.Connection(RPC_URL, "confirmed");
+
+  const key = process.env.PRIVATE_KEY;
+  
+  if (!key) {
+    console.error("PRIVATE_KEY is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const payer = web3.Keypair.fromSecretKey(
+    key.endsWith(".json") ? new Uint8Array(require(key)) : bs58.decode(key)
+  );
+  const provider = new AnchorProvider(connection, new Wallet(payer));
+
+  const ix = createInitializeInstruction(
+    tokenBridgeAddress,
+    payer.publicKey.toString(),
+    coreBridgeAddress
+  );
+
+  const transaction = new web3.Transaction();
+  transaction.add(ix);
+  const tx = await provider.sendAndConfirm(transaction);
+  console.log(tx);
+})();

+ 111 - 0
solana/scripts/initialize-core.ts

@@ -0,0 +1,111 @@
+import { createInitializeInstruction } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole";
+import { AnchorProvider, Wallet, web3 } from "@coral-xyz/anchor";
+import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
+
+function displayHelp() {
+  console.log(`
+
+    The following environment variables are required:
+    
+    RPC_URL: The RPC URL of the SVM network (Fogo or Solana).
+    CORE_BRIDGE_PROGRAM_ID: The program ID of the core bridge program.
+    PRIVATE_KEY: The private key of the account that will be used to initialize the core bridge program.
+                 Can be a keypair-file path or a base58 encoded string.
+    GUARDIAN_SET: A JSON-array containing one or more guardian addresses, in non-0x prefixed hex format. 
+
+    Optional environment variables:
+    FEE: The fee that will be used to initialize the core bridge program. Defaults to 100000 lamports.
+    EXPIRATION_TIME: The expiration time of the guardian set. Defaults to 86400 seconds (1 day).
+
+    `)
+}
+
+const DEFAULT_FEE = 100000;
+const DEFAULT_EXPIRATION_TIME = 86400;
+
+(async () => {
+  const RPC_URL = process.env.RPC_URL;
+  if (!RPC_URL) {
+    console.error("RPC_URL is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const CORE_BRIDGE_PROGRAM_ID = process.env.CORE_BRIDGE_PROGRAM_ID;
+  if (!CORE_BRIDGE_PROGRAM_ID) {
+    console.error("CORE_BRIDGE_PROGRAM_ID is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const coreBridgeAddress = new web3.PublicKey(CORE_BRIDGE_PROGRAM_ID);
+
+  const connection = new web3.Connection(RPC_URL, "confirmed");
+
+  const key = process.env.PRIVATE_KEY;
+
+  if (!key) {
+    console.error("PRIVATE_KEY is required");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const payer = web3.Keypair.fromSecretKey(
+    key.endsWith(".json") ? new Uint8Array(require(key)) : bs58.decode(key)
+  );
+  const provider = new AnchorProvider(connection, new Wallet(payer));
+
+  const guardianAddress = process.env.GUARDIAN_SET;
+  if (!guardianAddress) {
+    console.error("GUARDIAN_SET is required");
+    displayHelp();
+    process.exit(1);
+  }
+  const fee = process.env.FEE || DEFAULT_FEE;
+  const expirationTime = process.env.EXPIRATION_TIME || DEFAULT_EXPIRATION_TIME;
+
+  if (BigInt(fee) === 0n) {
+    console.error("FEE must be greater than 0");
+    displayHelp();
+    process.exit(1);
+  }
+
+  if (Number(expirationTime) === 0) {
+    console.error("EXPIRATION_TIME must be greater than 0");
+    displayHelp();
+    process.exit(1);
+  }
+
+  // Parse the guardian set
+  const guardianSet = JSON.parse(guardianAddress);
+  if (!Array.isArray(guardianSet)) {
+    console.error("GUARDIAN_SET must be a JSON-array");
+    displayHelp();
+    process.exit(1);
+  }
+  if (guardianSet.length === 0) {
+    console.error("GUARDIAN_SET must contain at least one guardian address");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const guardianSetBuffer = guardianSet.map((guardian) => Buffer.from(guardian, "hex"));
+
+  if (guardianSetBuffer.some((address) => address.length !== 20)) {
+    console.error("GUARDIAN_SET must only contain non-0x prefixed, 20-byte long hex addresses");
+    displayHelp();
+    process.exit(1);
+  }
+
+  const ix = createInitializeInstruction(
+    coreBridgeAddress,
+    payer.publicKey.toString(),
+    Number(expirationTime),
+    BigInt(fee),
+    guardianSetBuffer
+  );
+  const transaction = new web3.Transaction();
+  transaction.add(ix);
+  const tx = await provider.sendAndConfirm(transaction);
+  console.log(tx);
+})();

+ 0 - 45
solana/scripts/initialize_testnet.ts

@@ -1,45 +0,0 @@
-import { createInitializeInstruction } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole";
-import { AnchorProvider, Wallet, web3 } from "@coral-xyz/anchor";
-import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
-
-// Usage:
-// RPC_URL="https://api.devnet.solana.com" CORE_BRIDGE_PROGRAM_ID=3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 SOLANA_KEY="<full_path>.json" npx tsx post_message.ts
-
-(async () => {
-  const RPC_URL = process.env.RPC_URL;
-  if (!RPC_URL) {
-    throw new Error("RPC_URL is required");
-  }
-
-  const CORE_BRIDGE_PROGRAM_ID = process.env.CORE_BRIDGE_PROGRAM_ID;
-  if (!CORE_BRIDGE_PROGRAM_ID) {
-    throw new Error("CORE_BRIDGE_PROGRAM_ID is required");
-  }
-
-  const coreBridgeAddress = new web3.PublicKey(CORE_BRIDGE_PROGRAM_ID);
-
-  const connection = new web3.Connection(RPC_URL, "confirmed");
-
-  const key = process.env.SOLANA_KEY;
-
-  if (!key) {
-    throw new Error("SOLANA_KEY is required");
-  }
-
-  const payer = web3.Keypair.fromSecretKey(
-    key.endsWith(".json") ? new Uint8Array(require(key)) : bs58.decode(key)
-  );
-  const provider = new AnchorProvider(connection, new Wallet(payer));
-
-  const ix = createInitializeInstruction(
-    coreBridgeAddress,
-    payer.publicKey.toString(),
-    86400,
-    BigInt(100),
-    [Buffer.from("13947Bd48b18E53fdAeEe77F3473391aC727C638", "hex")]
-  );
-  const transaction = new web3.Transaction();
-  transaction.add(ix);
-  const tx = await provider.sendAndConfirm(transaction);
-  console.log(tx);
-})();

+ 64 - 4
solana/scripts/package-lock.json

@@ -7,6 +7,7 @@
       "dependencies": {
         "@certusone/wormhole-sdk": "^0.10.18",
         "@coral-xyz/anchor": "^0.31.0",
+        "@solana/web3.js": "^1.98.4",
         "tsx": "^4.19.3"
       }
     },
@@ -2421,17 +2422,17 @@
       }
     },
     "node_modules/@solana/web3.js": {
-      "version": "1.98.0",
-      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.0.tgz",
-      "integrity": "sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA==",
+      "version": "1.98.4",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz",
+      "integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==",
       "license": "MIT",
       "dependencies": {
         "@babel/runtime": "^7.25.0",
         "@noble/curves": "^1.4.2",
         "@noble/hashes": "^1.4.0",
         "@solana/buffer-layout": "^4.0.1",
+        "@solana/codecs-numbers": "^2.1.0",
         "agentkeepalive": "^4.5.0",
-        "bigint-buffer": "^1.1.5",
         "bn.js": "^5.2.1",
         "borsh": "^0.7.0",
         "bs58": "^4.0.1",
@@ -2443,6 +2444,56 @@
         "superstruct": "^2.0.2"
       }
     },
+    "node_modules/@solana/web3.js/node_modules/@solana/codecs-core": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.3.0.tgz",
+      "integrity": "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/errors": "2.3.0"
+      },
+      "engines": {
+        "node": ">=20.18.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.3.3"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/@solana/codecs-numbers": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz",
+      "integrity": "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.3.0",
+        "@solana/errors": "2.3.0"
+      },
+      "engines": {
+        "node": ">=20.18.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.3.3"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/@solana/errors": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.3.0.tgz",
+      "integrity": "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^5.4.1",
+        "commander": "^14.0.0"
+      },
+      "bin": {
+        "errors": "bin/cli.mjs"
+      },
+      "engines": {
+        "node": ">=20.18.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.3.3"
+      }
+    },
     "node_modules/@solana/web3.js/node_modules/@types/ws": {
       "version": "8.18.1",
       "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
@@ -2452,6 +2503,15 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@solana/web3.js/node_modules/commander": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz",
+      "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      }
+    },
     "node_modules/@solana/web3.js/node_modules/eventemitter3": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",

+ 1 - 0
solana/scripts/package.json

@@ -2,6 +2,7 @@
   "dependencies": {
     "@certusone/wormhole-sdk": "^0.10.18",
     "@coral-xyz/anchor": "^0.31.0",
+    "@solana/web3.js": "^1.98.4",
     "tsx": "^4.19.3"
   },
   "overrides": {