Przeglądaj źródła

feat(governance, lazer): add scripts for setting ecdsa signer for lazer solana program (#2515)

Pavel Strakhov 8 miesięcy temu
rodzic
commit
7655b9110d

+ 70 - 0
governance/xc_admin/packages/xc_admin_cli/src/index.ts

@@ -972,4 +972,74 @@ multisigCommand(
     );
   });
 
+multisigCommand(
+  "upgrade-program-and-set-trusted-ecdsa-signer",
+  "Upgrade the Lazer program and set a trusted ECDSA signer",
+)
+  .requiredOption("-b, --buffer <pubkey>", "buffer account for the upgrade")
+  .requiredOption(
+    "-s, --signer <address>",
+    "public address (hex) of the trusted ECDSA signer to add/update",
+  )
+  .requiredOption(
+    "-e, --expiry-time <seconds>",
+    "expiry time in seconds since Unix epoch. Set to 0 to remove the signer.",
+  )
+  .action(async (options: any) => {
+    const vault = await loadVaultFromOptions(options);
+    const targetCluster: PythCluster = options.cluster;
+
+    const buffer: PublicKey = new PublicKey(options.buffer);
+    const trustedSigner = Buffer.from(options.signer, "hex");
+    const expiryTime = new BN(options.expiryTime);
+
+    const programId = SOLANA_LAZER_PROGRAM_ID;
+    const programDataAccount = PublicKey.findProgramAddressSync(
+      [programId.toBuffer()],
+      BPF_UPGRADABLE_LOADER,
+    )[0];
+
+    // This is intruction is not in @solana/web3.js, source : https://docs.rs/solana-program/latest/src/solana_program/bpf_loader_upgradeable.rs.html#200
+    const upgradeInstruction: TransactionInstruction = {
+      programId: BPF_UPGRADABLE_LOADER,
+      // 4-bytes instruction discriminator, got it from https://docs.rs/solana-program/latest/src/solana_program/loader_upgradeable_instruction.rs.html#104
+      data: Buffer.from([3, 0, 0, 0]),
+      keys: [
+        { pubkey: programDataAccount, isSigner: false, isWritable: true },
+        { pubkey: programId, isSigner: false, isWritable: true },
+        { pubkey: buffer, isSigner: false, isWritable: true },
+        { pubkey: vault.wallet.publicKey, isSigner: false, isWritable: true },
+        { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
+        { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
+        {
+          pubkey: await vault.getVaultAuthorityPDA(targetCluster),
+          isSigner: true,
+          isWritable: false,
+        },
+      ],
+    };
+
+    // Create Anchor program instance
+    const lazerProgram = new Program(
+      lazerIdl as Idl,
+      SOLANA_LAZER_PROGRAM_ID,
+      vault.getAnchorProvider(),
+    );
+
+    // Use Anchor to create the instruction
+    const updateSignerInstruction = await lazerProgram.methods
+      .updateEcdsaSigner(trustedSigner, expiryTime)
+      .accounts({
+        topAuthority: await vault.getVaultAuthorityPDA(targetCluster),
+        storage: SOLANA_STORAGE_ID,
+      })
+      .instruction();
+
+    await vault.proposeInstructions(
+      [upgradeInstruction, updateSignerInstruction],
+      targetCluster,
+      DEFAULT_PRIORITY_FEE_CONFIG,
+    );
+  });
+
 program.parse();

+ 30 - 0
governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/idl/lazer.json

@@ -83,6 +83,36 @@
         }
       ]
     },
+    {
+      "name": "updateEcdsaSigner",
+      "accounts": [
+        {
+          "name": "topAuthority",
+          "isMut": false,
+          "isSigner": true
+        },
+        {
+          "name": "storage",
+          "isMut": true,
+          "isSigner": false
+        }
+      ],
+      "args": [
+        {
+          "name": "trustedSigner",
+          "type": {
+            "array": [
+              "u8",
+              20
+            ]
+          }
+        },
+        {
+          "name": "expiresAt",
+          "type": "i64"
+        }
+      ]
+    },
     {
       "name": "verifyMessage",
       "docs": [

+ 52 - 0
lazer/contracts/solana/scripts/add_ecdsa_signer.ts

@@ -0,0 +1,52 @@
+import * as anchor from "@coral-xyz/anchor";
+import { Program } from "@coral-xyz/anchor";
+import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
+import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
+import yargs from "yargs/yargs";
+import { readFileSync } from "fs";
+import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
+
+// Add a trusted signer or change its expiry time.
+//
+// Example:
+// pnpm ts-node scripts/add_ecdsa_signer.ts --url 'https://api.testnet.solana.com' \
+//    --keypair-path .../key.json --trusted-signer b8d50f0bae75bf6e03c104903d7c3afc4a6596da \
+//    --expiry-time-seconds 2057930841
+async function main() {
+  let argv = await yargs(process.argv.slice(2))
+    .options({
+      url: { type: "string", demandOption: true },
+      "keypair-path": { type: "string", demandOption: true },
+      "trusted-signer": { type: "string", demandOption: true },
+      "expiry-time-seconds": { type: "number", demandOption: true },
+    })
+    .parse();
+
+  const keypair = anchor.web3.Keypair.fromSecretKey(
+    new Uint8Array(JSON.parse(readFileSync(argv.keypairPath, "ascii"))),
+  );
+
+  const wallet = new NodeWallet(keypair);
+  const connection = new anchor.web3.Connection(argv.url, {
+    commitment: "confirmed",
+  });
+  const provider = new anchor.AnchorProvider(connection, wallet);
+
+  const program: Program<PythLazerSolanaContract> = new Program(
+    pythLazerSolanaContractIdl as PythLazerSolanaContract,
+    provider,
+  );
+
+  await program.methods
+    .updateEcdsaSigner(
+      [...Buffer.from(argv.trustedSigner, "hex")],
+      new anchor.BN(argv.expiryTimeSeconds),
+    )
+    .accounts({
+      payer: wallet.publicKey,
+    })
+    .rpc();
+  console.log("signer updated");
+}
+
+main();

+ 31 - 3
lazer/contracts/solana/scripts/check_trusted_signer.ts

@@ -46,9 +46,37 @@ async function main() {
   console.log("\nTrusted Signers:");
   console.log("----------------");
 
-  for (const signer of storage.trustedSigners) {
-    if (signer.pubkey.equals(anchor.web3.PublicKey.default)) continue;
-    console.log(`\nPublic Key: ${signer.pubkey.toBase58()}`);
+  const trustedSigners = storage.trustedSigners.slice(
+    0,
+    storage.numTrustedSigners,
+  );
+  for (const signer of trustedSigners) {
+    console.log(
+      `\nPublic Key: ${(signer.pubkey as anchor.web3.PublicKey).toBase58()}`,
+    );
+    console.log(
+      `Expires At: ${new Date(
+        signer.expiresAt.toNumber() * 1000,
+      ).toISOString()}`,
+    );
+    console.log(
+      `Active: ${
+        signer.expiresAt.toNumber() > Date.now() / 1000 ? "Yes" : "No"
+      }`,
+    );
+  }
+
+  console.log("\nTrusted ECDSA Signers:");
+  console.log("----------------");
+
+  const trustedEcdsaSigners = storage.trustedEcdsaSigners.slice(
+    0,
+    storage.numTrustedEcdsaSigners,
+  );
+  for (const signer of trustedEcdsaSigners) {
+    console.log(
+      `\nPublic Address: ${Buffer.from(signer.pubkey as number[]).toString("hex")}`,
+    );
     console.log(
       `Expires At: ${new Date(
         signer.expiresAt.toNumber() * 1000,

+ 43 - 0
lazer/contracts/solana/scripts/verify_ecdsa_message.ts

@@ -0,0 +1,43 @@
+import * as anchor from "@coral-xyz/anchor";
+import { Program } from "@coral-xyz/anchor";
+import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
+import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
+import yargs from "yargs/yargs";
+import { readFileSync } from "fs";
+import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
+
+async function main() {
+  let argv = await yargs(process.argv.slice(2))
+    .options({
+      url: { type: "string", demandOption: true },
+      "keypair-path": { type: "string", demandOption: true },
+      message: { type: "string", demandOption: true },
+    })
+    .parse();
+
+  const keypair = anchor.web3.Keypair.fromSecretKey(
+    new Uint8Array(JSON.parse(readFileSync(argv.keypairPath, "ascii"))),
+  );
+
+  const wallet = new NodeWallet(keypair);
+  const connection = new anchor.web3.Connection(argv.url, {
+    commitment: "confirmed",
+  });
+  const provider = new anchor.AnchorProvider(connection, wallet);
+
+  const program: Program<PythLazerSolanaContract> = new Program(
+    pythLazerSolanaContractIdl as PythLazerSolanaContract,
+    provider,
+  );
+
+  await program.methods
+    .verifyEcdsaMessage(Buffer.from(argv.message, "hex"))
+    .accounts({
+      payer: wallet.publicKey,
+    })
+    .rpc();
+
+  console.log("message is valid");
+}
+
+main();