mirror of
https://github.com/tompro/sattle.git
synced 2026-08-27 07:15:59 +00:00
feat: activate only proven wallet owners
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
OWNER_ID,
|
||||
PASSWORD,
|
||||
deferred,
|
||||
installLegacyEncryptedWallet,
|
||||
installLegacyOwnerlessResidue,
|
||||
mocks,
|
||||
} from './wallet.lifecycle.testHarness';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { savedKeyExists, savedKeyOwnerId } from '@/lnurlcash/keys';
|
||||
import { readNwcConnections } from '@/lnurlcash/nwc';
|
||||
import { readPasskeySlots } from '@/lnurlcash/passkeys';
|
||||
import { readTrustedMints } from '@/lnurlcash/trustedMints';
|
||||
import { useNwcStore } from './nwc';
|
||||
import { useWalletStore } from './wallet';
|
||||
|
||||
describe('serialized wallet activation', () => {
|
||||
it('migrates a proven legacy owner before NWC can observe unlocked', async () => {
|
||||
// Given an encrypted legacy wallet with ownerless authorization residue
|
||||
await installLegacyEncryptedWallet();
|
||||
installLegacyOwnerlessResidue();
|
||||
const wallet = useWalletStore();
|
||||
useNwcStore();
|
||||
|
||||
// When password proof unlocks the wallet
|
||||
await wallet.unlock(PASSWORD);
|
||||
|
||||
// Then every legacy namespace belongs to the proven owner before startup
|
||||
expect(savedKeyOwnerId()).toBe(OWNER_ID);
|
||||
expect(readPasskeySlots()).toHaveLength(1);
|
||||
expect(readNwcConnections(OWNER_ID)).toHaveLength(1);
|
||||
expect(readTrustedMints(OWNER_ID)).toHaveLength(1);
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
await vi.waitFor(() => expect(mocks.startService).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
it('serializes a queued create behind an interrupted forget', async () => {
|
||||
// Given an unlocked wallet whose NWC drain is deferred
|
||||
const wallet = useWalletStore();
|
||||
const nwc = useNwcStore();
|
||||
await wallet.create(PASSWORD);
|
||||
const drain = deferred();
|
||||
const stopSpy = vi.spyOn(nwc, 'stop').mockReturnValue(drain.promise);
|
||||
|
||||
// When forget and create are requested without waiting between them
|
||||
const forgetting = wallet.forgetWallet();
|
||||
const creating = wallet.create();
|
||||
await vi.waitFor(() => expect(stopSpy).toHaveBeenCalled());
|
||||
|
||||
// Then the successor cannot install until the old owner drain completes,
|
||||
// and the session keeps its commit capability while the drain runs
|
||||
expect(savedKeyExists()).toBe(true);
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
drain.resolve();
|
||||
await forgetting;
|
||||
const phrase = await creating;
|
||||
expect(phrase.split(' ')).toHaveLength(12);
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
});
|
||||
|
||||
it('drains the active session before file restore reactivation', async () => {
|
||||
// Given an unlocked wallet whose NWC drain is deferred
|
||||
const wallet = useWalletStore();
|
||||
const nwc = useNwcStore();
|
||||
await wallet.create(PASSWORD);
|
||||
const drain = deferred();
|
||||
const stopSpy = vi.spyOn(nwc, 'stop').mockReturnValue(drain.promise);
|
||||
|
||||
// When a valid file restore starts
|
||||
const restoring = wallet.restoreFromBackup({
|
||||
type: 'sattle-backup',
|
||||
version: 1,
|
||||
createdAt: 1,
|
||||
bearers: [],
|
||||
});
|
||||
await vi.waitFor(() => expect(stopSpy).toHaveBeenCalled());
|
||||
|
||||
// Then deactivation waits for the drain before the lifecycle is
|
||||
// invalidated, and reactivation returns to unlocked afterward
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
drain.resolve();
|
||||
await restoring;
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
});
|
||||
|
||||
it('serializes current-wallet Nostr restore through full reactivation', async () => {
|
||||
// Given an unlocked wallet and a relay restore result
|
||||
const wallet = useWalletStore();
|
||||
useNwcStore();
|
||||
await wallet.create(PASSWORD);
|
||||
const ownerId = wallet.pubkey;
|
||||
|
||||
// When the active-wallet Nostr restore runs
|
||||
await wallet.restoreCurrentFromNostr(['wss://relay.example']);
|
||||
|
||||
// Then the same owner is active only after the restore completed
|
||||
expect(mocks.restoreFromNostr).toHaveBeenCalledTimes(1);
|
||||
expect(wallet.state).toBe('unlocked');
|
||||
expect(wallet.pubkey).toBe(ownerId);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user