@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.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +1 -1
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/tests/xrpl.test.d.ts +1 -0
- package/dist/xrpl.d.ts +5 -0
- package/package.json +3 -2
- package/src/index.ts +7 -2
- package/src/tests/index.test.ts +41 -0
- package/src/tests/xrpl.test.ts +87 -0
- package/src/xrpl.ts +94 -0
@@ -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
|
+
}
|