Browse Source

simplify verifying messages (#246)

* refactor: renamed file

* feat: added verifySignatureForAddress

* docs: improved tsdoc
Nick Frostbutter 2 tháng trước cách đây
mục cha
commit
8a50e7151d

+ 5 - 0
.changeset/yellow-zoos-juggle.md

@@ -0,0 +1,5 @@
+---
+"gill": minor
+---
+
+added `verifySignatureForAddress` function

+ 65 - 0
packages/gill/src/__tests__/verify-signature.ts

@@ -0,0 +1,65 @@
+import { generateKeyPairSigner, getBase58Decoder, KeyPairSigner, signBytes } from "@solana/kit";
+import { verifySignatureForAddress } from "../core/verify-signature";
+
+describe("verifySignatureForAddress", () => {
+  let testKeyPair: KeyPairSigner;
+  const testMessage: string = "Hello, Solana!";
+  const messageBytes = new TextEncoder().encode(testMessage);
+
+  beforeAll(async () => {
+    testKeyPair = await generateKeyPairSigner();
+  });
+
+  it("should return true for valid signature with SignatureBytes", async () => {
+    const randomBytes = new Uint8Array(32);
+    crypto.getRandomValues(randomBytes);
+    const signedData = await signBytes(testKeyPair.keyPair.privateKey, randomBytes);
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signedData, randomBytes);
+
+    expect(isValid).toBe(true);
+  });
+
+  it("should return true for valid signature with string message", async () => {
+    const signature = await signBytes(testKeyPair.keyPair.privateKey, messageBytes);
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signature, testMessage);
+
+    expect(isValid).toBe(true);
+  });
+
+  it("should return true for valid signature with Uint8Array message", async () => {
+    const signature = await signBytes(testKeyPair.keyPair.privateKey, messageBytes);
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signature, messageBytes);
+
+    expect(isValid).toBe(true);
+  });
+
+  it("should return false for invalid signature", async () => {
+    const signature = await signBytes(testKeyPair.keyPair.privateKey, messageBytes);
+    const differentMessage = "Different message";
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signature, differentMessage);
+
+    expect(isValid).toBe(false);
+  });
+
+  it("should return false for signature from different key pair", async () => {
+    const differentKeyPair = await generateKeyPairSigner();
+    const signature = await signBytes(differentKeyPair.keyPair.privateKey, messageBytes);
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signature, testMessage);
+
+    expect(isValid).toBe(false);
+  });
+
+  it("should handle string signature input", async () => {
+    const signature = await signBytes(testKeyPair.keyPair.privateKey, messageBytes);
+    const signatureString = getBase58Decoder().decode(signature);
+
+    const isValid = await verifySignatureForAddress(testKeyPair.address, signatureString, testMessage);
+
+    expect(isValid).toBe(true);
+  });
+});

+ 14 - 13
packages/gill/src/core/index.ts

@@ -1,19 +1,20 @@
 export { debug, isDebugEnabled } from "./debug";
 
+export * from "./base64-from-transaction";
+export * from "./base64-to-transaction";
 export * from "./const";
-export * from "./utils";
-export * from "./rpc";
-export * from "./explorer";
-export * from "./create-transaction";
-export * from "./send-and-confirm-transaction-with-signers";
-export * from "./prepare-transaction";
+export * from "./create-codama-config";
 export * from "./create-solana-client";
-export * from "./accounts";
-export * from "./keypairs-extractable";
-export * from "./keypairs-base58";
-export * from "./base64-to-transaction";
-export * from "./base64-from-transaction";
-export * from "./simulate-transaction";
+export * from "./create-transaction";
+export * from "./explorer";
 export * from "./get-oldest-signature";
 export * from "./insert-reference-key";
-export * from "./create-codama-config";
+export * from "./keypairs-base58";
+export * from "./keypairs-extractable";
+export * from "./prepare-transaction";
+export * from "./rent";
+export * from "./rpc";
+export * from "./send-and-confirm-transaction-with-signers";
+export * from "./simulate-transaction";
+export * from "./utils";
+export * from "./verify-signature";

+ 0 - 0
packages/gill/src/core/accounts.ts → packages/gill/src/core/rent.ts


+ 44 - 0
packages/gill/src/core/verify-signature.ts

@@ -0,0 +1,44 @@
+import type {} from "@solana/kit";
+import {
+  getBase58Encoder,
+  getPublicKeyFromAddress,
+  verifySignature,
+  type Address,
+  type ReadonlyUint8Array,
+  type Signature,
+  type SignatureBytes,
+} from "@solana/kit";
+
+/**
+ * Verifies a Solana Address had signed the given message.
+ *
+ * @param address - The Solana address expected to have signed the message
+ * @param signature - The signature to verify
+ * @param signedMessage - The original message that was signed
+ * @returns Promise that resolves to `true` if the signature is valid, `false` otherwise
+ *
+ * @example
+ * ```typescript
+ * const isValid = await verifySignatureForAddress(
+ *   "GC5AFcYqshWUnNK23MbWTXPix3FUagZt4fjUAt88FT59" as Address,
+ *   "jrZaHRqiRojydQMxHqqe7FEkfeyw64KfPdF2ww1mm3hpVtGyxBvEU5NmHdZFoawYnYu62ujgqw3gcL2XHYbxd9K",
+ *   "Hello, Solana!!"
+ * );
+ * console.log(isValid); // true or false
+ * ```
+ */
+export async function verifySignatureForAddress(
+  address: Address,
+  signature: string | Signature | SignatureBytes | Uint8Array | ReadonlyUint8Array,
+  message: string | Uint8Array,
+): Promise<boolean> {
+  const publicKey = await getPublicKeyFromAddress(address);
+  if (typeof message === "string") {
+    message = new TextEncoder().encode(message);
+  }
+  // massage the signature into the branded type for `SignatureBytes`
+  if (typeof signature === "string") {
+    signature = getBase58Encoder().encode(signature);
+  }
+  return verifySignature(publicKey, signature as SignatureBytes, message);
+}