mirror of
https://github.com/Routstr/routstrd.git
synced 2026-10-05 20:38:22 +00:00
The external `cocod` binary was replaced by the in-process Coco wallet long ago, but its HTTP/unix-socket client kept shipping ~280 lines of unreachable code next to the wallet contract. Remove it: - delete `createCocodClient` (spawns `cocod init`, talks over a unix socket, takes a cross-process startup lock) and its helpers `resolveCocodExecutable`, `isCocodInstalled`, `normalizeBalances` and `CocodBalanceOutput`. None of them are reachable from production or from tests; `isCocodInstalled` had no callers at all. - drop the `options.walletClient || createCocodClient(...)` fallback in `createWalletAdapter`. The daemon always passes the in-process client built by `createCocoClient()`, so `walletClient` is now required and the adapter can no longer silently spawn an external binary. - drop the vestigial `cocodPath` config key, which only ever fed that fallback. - delete the "legacy cocod client NPC passthroughs" test block, which covered only the deleted client. The shared wallet contract moves from `cocod-client.ts` to `wallet-client.ts` and loses its `Cocod` prefixes (`WalletClient`, `WalletRuntimeState`, `WalletHttpError`) — none of it describes cocod any more. `WalletRuntimeState` avoids colliding with the existing `WalletState` in `wallet-state.ts`. Legacy cocod *migration* code is deliberately untouched: `stopLegacyCocod`, `assertLegacyCocodNotRunning`, `claimLegacyCocodPidFile`, `migration.ts` and the `legacyCocod*` paths still migrate an existing `~/.cocod` wallet and fence off a running external daemon. Net -456 lines. Existing configs that still contain `cocodPath` load unchanged (unknown keys are ignored) — the key is just no longer written.
107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import { describe, expect, it, mock } from "bun:test";
|
|
import {
|
|
Amount,
|
|
getDecodedToken,
|
|
getEncodedToken,
|
|
} from "@cashu/cashu-ts";
|
|
import {
|
|
createWalletAdapter,
|
|
decodeCashuTokenAmount,
|
|
} from "../../src/daemon/wallet";
|
|
import type { WalletClient } from "../../src/daemon/wallet/wallet-client";
|
|
|
|
// A full modern keyset ID with the same format as Minibits' post-migration
|
|
// active keyset. getEncodedToken stores only its first eight bytes in TokenV4.
|
|
const FULL_KEYSET_ID =
|
|
"01fc0ec0e59cd6fa01b7a88f8cd77fce81fd1e64bca67d752e984992b7a3c3a821";
|
|
const LEGACY_KEYSET_ID = "00107937db0cc865";
|
|
|
|
function makeToken({
|
|
amounts = [5],
|
|
keysetId = FULL_KEYSET_ID,
|
|
unit = "sat",
|
|
}: {
|
|
amounts?: number[];
|
|
keysetId?: string;
|
|
unit?: string;
|
|
} = {}): string {
|
|
return getEncodedToken({
|
|
mint: "https://mint.minibits.cash/Bitcoin",
|
|
unit,
|
|
proofs: amounts.map((amount, index) => ({
|
|
id: keysetId,
|
|
amount: Amount.from(amount),
|
|
secret: `short-keyset-regression-fixture-${index}`,
|
|
C: `02${(index + 1).toString(16).padStart(2, "0").repeat(32)}`,
|
|
})),
|
|
});
|
|
}
|
|
|
|
function makeWalletClient(
|
|
receiveCashu: WalletClient["receiveCashu"],
|
|
): WalletClient {
|
|
// createWalletAdapter is intentionally lazy; this receive-path test only
|
|
// needs the one capability exercised by receiveToken.
|
|
return { receiveCashu } as WalletClient;
|
|
}
|
|
|
|
describe("short keyset TokenV4 compatibility", () => {
|
|
it("reads amount metadata without resolving the shortened proof keyset ID", () => {
|
|
const token = makeToken({ amounts: [1, 4] });
|
|
|
|
// Guard the fixture itself: a full proof decode with no mint keysets must
|
|
// exercise the same short-ID failure that triggered this regression.
|
|
expect(() => getDecodedToken(token, [])).toThrow(/short keyset ID/i);
|
|
expect(decodeCashuTokenAmount(token)).toEqual({
|
|
amount: 5,
|
|
unit: "sat",
|
|
});
|
|
});
|
|
|
|
for (const tokenCase of [
|
|
{
|
|
name: "modern keyset with msat unit",
|
|
input: { amounts: [500, 1000], unit: "msat" },
|
|
expected: { amount: 1500, unit: "msat" as const },
|
|
},
|
|
{
|
|
name: "legacy v0 keyset",
|
|
input: { amounts: [2, 8], keysetId: LEGACY_KEYSET_ID },
|
|
expected: { amount: 10, unit: "sat" as const },
|
|
},
|
|
]) {
|
|
it(`reads ${tokenCase.name} metadata`, () => {
|
|
expect(decodeCashuTokenAmount(makeToken(tokenCase.input))).toEqual(
|
|
tokenCase.expected,
|
|
);
|
|
});
|
|
}
|
|
|
|
it("reports success after cocod receives a short-keyset token", async () => {
|
|
const receiveCashu = mock(async () => "Received 5");
|
|
const walletClient = makeWalletClient(receiveCashu);
|
|
const adapter = await createWalletAdapter({ walletClient });
|
|
|
|
const result = await adapter.receiveToken(makeToken());
|
|
|
|
expect(receiveCashu).toHaveBeenCalledTimes(1);
|
|
expect(result).toEqual({
|
|
success: true,
|
|
amount: 5,
|
|
unit: "sat",
|
|
message: "Received 5",
|
|
});
|
|
});
|
|
|
|
it("does not call the wallet when token metadata is invalid", async () => {
|
|
const receiveCashu = mock(async () => "should not be called");
|
|
const walletClient = makeWalletClient(receiveCashu);
|
|
const adapter = await createWalletAdapter({ walletClient });
|
|
|
|
const result = await adapter.receiveToken("cashuBnot-a-valid-token");
|
|
|
|
expect(receiveCashu).not.toHaveBeenCalled();
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|