@notabene/verify-proof 1.2.0-next.1 → 1.3.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { verifyPersonalSignXRPL } from "../xrpl";
3
+ import { Wallet } from "xrpl";
4
+ import { sign } from "ripple-keypairs";
5
+ import {
6
+ type SignatureProof,
7
+ ProofStatus,
8
+ ProofTypes,
9
+ } from "@notabene/javascript-sdk";
10
+
11
+ describe("verifyPersonalSignXRPL", async () => {
12
+ // Create test wallets using seed
13
+ const ed25519 = Wallet.fromSeed("snoPBrXtMeMyMHUVTgbuqAfg1SUTb");
14
+ const message = "Ripple signature test";
15
+
16
+ function createXRPLProof(wallet: Wallet): SignatureProof {
17
+ const messageHex = Buffer.from(message).toString("hex");
18
+ const signature = sign(messageHex, wallet.privateKey);
19
+
20
+ return {
21
+ type: ProofTypes.ED25519,
22
+ address: `xrpl:1:${wallet.address}`,
23
+ did: `did:pkh:xrpl:1:${wallet.address}`,
24
+ attestation: message,
25
+ proof: signature,
26
+ status: ProofStatus.PENDING,
27
+ wallet_provider: "XRPL",
28
+ };
29
+ }
30
+
31
+ function existingXRPLProof(): SignatureProof {
32
+ return {
33
+ type: ProofTypes.ED25519,
34
+ proof: 'B080C34BCD2F1392420A5CB46C14772AC54841181CB3387A0793025C480A6A52292A869FB2B86B89EA33E096285F8CC45F5D35D4E29750BA8E127076CADF7906',
35
+ address: `xrpl:1:rGWNp4P9DJpDNhPc7t9GeHoQb8SCKWBfYV`,
36
+ did: `did:pkh:xrpl:1:rGWNp4P9DJpDNhPc7t9GeHoQb8SCKWBfYV`,
37
+ status: ProofStatus.PENDING,
38
+ wallet_provider: "XRPL",
39
+ attestation: "I certify that\n\nxrpl:1 account rGWNp4P9DJpDNhPc7t9GeHoQb8SCKWBfYV\n\nbelonged to did:key:z6Mkq3vnQfZFLVe2UXBUTgonfEhzYr9QF7xvkSSpdt9LXeY6\n\non Thu, 06 Mar 2025 10:34:43 GMT"
40
+ }
41
+ }
42
+
43
+ it("returns verified proof when valid existing XRPL proof", async () => {
44
+ const proof = existingXRPLProof();
45
+ const result = await verifyPersonalSignXRPL(proof, undefined, true);
46
+ expect(result.status).toBe(ProofStatus.VERIFIED);
47
+ });
48
+
49
+ it("returns verified proof when valid secp256k1 signature", async () => {
50
+ const proof = createXRPLProof(ed25519);
51
+ const result = await verifyPersonalSignXRPL(
52
+ proof,
53
+ ed25519.publicKey
54
+ );
55
+ expect(result.status).toBe(ProofStatus.VERIFIED);
56
+ });
57
+
58
+ it("returns failed proof when invalid message", async () => {
59
+ const proof = {
60
+ ...createXRPLProof(ed25519),
61
+ attestation: "evil text",
62
+ };
63
+ const result = await verifyPersonalSignXRPL(
64
+ proof,
65
+ ed25519.publicKey
66
+ );
67
+ expect(result.status).toBe(ProofStatus.FAILED);
68
+ });
69
+
70
+ it("returns failed proof for non-XRPL address", async () => {
71
+ const proof: SignatureProof = {
72
+ type: ProofTypes.ED25519,
73
+ did: `did:pkh:eip155:1:0x123`,
74
+ wallet_provider: "XRPL",
75
+ address: "eip155:1:0x123",
76
+ attestation: "test message",
77
+ proof: "signature",
78
+ status: ProofStatus.PENDING,
79
+ };
80
+
81
+ const result = await verifyPersonalSignXRPL(
82
+ proof,
83
+ ed25519.publicKey
84
+ );
85
+ expect(result.status).toBe(ProofStatus.FAILED);
86
+ });
87
+ });
package/src/xrpl.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
2
+ import { AccountTxTransaction, Client } from "xrpl";
3
+ import { verify } from "ripple-keypairs";
4
+
5
+ export function verifyXRPL(
6
+ message: string,
7
+ publicKey: string,
8
+ proof: string
9
+ ): boolean {
10
+ return verify(Buffer.from(message).toString("hex"), proof, publicKey);
11
+ }
12
+
13
+ export const xrplTestnetWs = [
14
+ "wss://s.altnet.rippletest.net:51233",
15
+ "wss://testnet.xrpl-labs.com/",
16
+ ];
17
+
18
+ export const xrplMainnetWs = [
19
+ "wss://s1.ripple.com",
20
+ "wss://xrplcluster.com/", // full node
21
+ "wss://s2.ripple.com/",
22
+ ];
23
+
24
+ // If the public key is not provided, we need to get it directly
25
+ async function getPublicKey(
26
+ address: string,
27
+ isTest?: boolean
28
+ ): Promise<string | undefined> {
29
+ const servers = isTest ? xrplTestnetWs : xrplMainnetWs;
30
+
31
+ for (const server of servers) {
32
+ try {
33
+ const client = new Client(server);
34
+ await client.connect();
35
+
36
+ const response = await client.request({
37
+ command: "account_tx",
38
+ account: address,
39
+ binary: false,
40
+ limit: 2,
41
+ forward: false,
42
+ });
43
+
44
+ await client.disconnect();
45
+ return getSigningPubkeyFromLatestTx(response.result?.transactions);
46
+ } catch (error) {
47
+ let errorMessage = "Connection to XRPL server failed";
48
+ if (error instanceof Error) {
49
+ errorMessage += `: ${error.message}`;
50
+ }
51
+ console.error(errorMessage);
52
+ // Continue to next server
53
+ }
54
+ }
55
+
56
+ throw new Error("Failed to connect to any XRPL server");
57
+ }
58
+
59
+ function getSigningPubkeyFromLatestTx(
60
+ latestTx: AccountTxTransaction[]
61
+ ): string {
62
+ for (let i = 0; i < latestTx.length; i++) {
63
+ // Check if the Account in the .tx is the address derived from the pubkey
64
+ const signingPubKey = latestTx[i]?.tx_json?.SigningPubKey ?? "0x";
65
+ // TODO: https://github.com/Cypher-Laboratory/xrpl-publickey-getter/blob/main/src/pubKeyGetter.ts#L98
66
+ // Check the public key matches the address properly
67
+ return signingPubKey;
68
+ }
69
+ throw new Error("No valid pubkey found in the latest transactions");
70
+ }
71
+
72
+ export async function verifyPersonalSignXRPL(
73
+ proof: SignatureProof,
74
+ publicKey?: string,
75
+ isTest?: boolean
76
+ ): Promise<SignatureProof> {
77
+ const [ns, , address] = proof.address.split(/:/);
78
+ if (ns !== "xrpl") return { ...proof, status: ProofStatus.FAILED };
79
+
80
+ if (!publicKey) {
81
+ publicKey = await getPublicKey(address, isTest);
82
+ }
83
+
84
+ if (!publicKey) {
85
+ return { ...proof, status: ProofStatus.FAILED };
86
+ }
87
+
88
+ const verified = verifyXRPL(proof.attestation, publicKey, proof.proof);
89
+
90
+ return {
91
+ ...proof,
92
+ status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
93
+ };
94
+ }