109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
|
|
// Copyright (C) 2026 Whiterun LLC,
|
||
|
|
// This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later.
|
||
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html
|
||
|
|
|
||
|
|
import { describe, it, expect } from "vitest";
|
||
|
|
import { encodeHdPublicKey } from "@bitauth/libauth";
|
||
|
|
import type { PathXpub } from "@wizardconnect/core";
|
||
|
|
import { DappConnectionManager } from "./dapp-connection-manager.js";
|
||
|
|
|
||
|
|
// Build a valid xpub string from a deterministic test node.
|
||
|
|
// Uses the secp256k1 generator point (a known valid public key).
|
||
|
|
function makeTestXpub(): string {
|
||
|
|
const node = {
|
||
|
|
chainCode: new Uint8Array(32).fill(0xbb),
|
||
|
|
// secp256k1 generator point (compressed) — always valid
|
||
|
|
publicKey: Uint8Array.from(
|
||
|
|
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
|
||
|
|
.match(/.{2}/g)!
|
||
|
|
.map((h) => parseInt(h, 16)),
|
||
|
|
),
|
||
|
|
childIndex: 0,
|
||
|
|
depth: 3,
|
||
|
|
parentFingerprint: new Uint8Array(4),
|
||
|
|
};
|
||
|
|
const result = encodeHdPublicKey({ node, network: "mainnet" });
|
||
|
|
if (typeof result === "string")
|
||
|
|
throw new Error("Failed to encode test xpub: " + result);
|
||
|
|
return result.hdPublicKey;
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("DappConnectionManager", () => {
|
||
|
|
describe("getSessionPaths", () => {
|
||
|
|
it("returns empty array before wallet_ready", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
expect(mgr.getSessionPaths()).toEqual([]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("restoreSessionPaths", () => {
|
||
|
|
it("restores xpubs so getPubkey works", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const xpub = makeTestXpub();
|
||
|
|
const paths: PathXpub[] = [{ name: "receive", xpub }];
|
||
|
|
|
||
|
|
mgr.restoreSessionPaths(paths);
|
||
|
|
|
||
|
|
expect(mgr.hasPath(0)).toBe(true);
|
||
|
|
const pk = mgr.getPubkey(0, 0n);
|
||
|
|
expect(pk).toBeInstanceOf(Uint8Array);
|
||
|
|
expect(pk!.length).toBe(33);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("makes paths available via getSessionPaths", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const xpub = makeTestXpub();
|
||
|
|
const paths: PathXpub[] = [
|
||
|
|
{ name: "receive", xpub },
|
||
|
|
{ name: "change", xpub },
|
||
|
|
];
|
||
|
|
|
||
|
|
mgr.restoreSessionPaths(paths);
|
||
|
|
|
||
|
|
const stored = mgr.getSessionPaths();
|
||
|
|
expect(stored).toHaveLength(2);
|
||
|
|
expect(stored[0].name).toBe("receive");
|
||
|
|
expect(stored[1].name).toBe("change");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("returns a copy, not a reference", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const xpub = makeTestXpub();
|
||
|
|
mgr.restoreSessionPaths([{ name: "receive", xpub }]);
|
||
|
|
|
||
|
|
const paths = mgr.getSessionPaths();
|
||
|
|
paths.push({ name: "change", xpub });
|
||
|
|
|
||
|
|
expect(mgr.getSessionPaths()).toHaveLength(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("throws on invalid xpub strings", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const paths: PathXpub[] = [{ name: "receive", xpub: "not-a-valid-xpub" }];
|
||
|
|
|
||
|
|
expect(() => mgr.restoreSessionPaths(paths)).toThrow("Invalid xpub");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("can derive multiple child indices after restore", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const xpub = makeTestXpub();
|
||
|
|
mgr.restoreSessionPaths([{ name: "receive", xpub }]);
|
||
|
|
|
||
|
|
for (const idx of [0n, 1n, 5n, 10n]) {
|
||
|
|
const pk = mgr.getPubkey(0, idx);
|
||
|
|
expect(pk, `index ${idx}`).toBeInstanceOf(Uint8Array);
|
||
|
|
expect(pk!.length, `index ${idx}`).toBe(33);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it("does not affect paths not in the restore set", () => {
|
||
|
|
const mgr = new DappConnectionManager();
|
||
|
|
const xpub = makeTestXpub();
|
||
|
|
mgr.restoreSessionPaths([{ name: "receive", xpub }]);
|
||
|
|
|
||
|
|
expect(mgr.hasPath(0)).toBe(true); // receive = 0
|
||
|
|
expect(mgr.hasPath(1)).toBe(false); // change = 1
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|