UserWallet: runCollectLoop options object, root null check, pairing/collect/bip32
**Motivations:**
- Passer runCollectLoop en objet d’options pour lisibilité et évolution
- Éviter non-null assertion sur root dans main.tsx
- Ajustements pairing, collect signatures, bip32
**Root causes:**
- N/A (évolutions + correctifs ciblés)
**Correctifs:**
- main.tsx: vérification root !== null avant createRoot, throw explicite si absent
**Evolutions:**
- LoginScreen: runCollectLoop({ relayEndpoints, hash, ourSigs, path, pairToMembers, pubkeyToPair, opts })
- pairingConfirm.ts, PairingDisplayScreen, PairingSetupBlock: modifications
- collectSignatures.ts, bip32.ts: ajustements
- SyncScreen: modifications mineures
**Pages affectées:**
- userwallet: LoginScreen, main.tsx, PairingDisplayScreen, PairingSetupBlock, SyncScreen, pairingConfirm.ts, bip32.ts, collectSignatures.ts
- data: sync-utxos.log
This commit is contained in:
parent
cb13ab6fbf
commit
9291eab9d5
@ -1,45 +1,3 @@
|
|||||||
⏳ Traitement: 200000/225826 UTXOs insérés...
|
|
||||||
⏳ Traitement: 210000/225826 UTXOs insérés...
|
|
||||||
⏳ Traitement: 220000/225826 UTXOs insérés...
|
|
||||||
💾 Mise à jour des UTXOs dépensés...
|
|
||||||
|
|
||||||
📊 Résumé:
|
|
||||||
- UTXOs vérifiés: 61609
|
|
||||||
- UTXOs toujours disponibles: 61609
|
|
||||||
- UTXOs dépensés détectés: 0
|
|
||||||
|
|
||||||
📈 Statistiques finales:
|
|
||||||
- Total UTXOs: 68398
|
|
||||||
- Dépensés: 6789
|
|
||||||
- Non dépensés: 61609
|
|
||||||
|
|
||||||
✅ Synchronisation terminée
|
|
||||||
🔍 Démarrage de la synchronisation des UTXOs dépensés...
|
|
||||||
|
|
||||||
📊 UTXOs à vérifier: 61609
|
|
||||||
📡 Récupération des UTXOs depuis Bitcoin...
|
|
||||||
📊 UTXOs disponibles dans Bitcoin: 225837
|
|
||||||
💾 Création de la table temporaire...
|
|
||||||
💾 Insertion des UTXOs disponibles par batch...
|
|
||||||
⏳ Traitement: 10000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 20000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 30000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 40000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 50000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 60000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 70000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 80000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 90000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 100000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 110000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 120000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 130000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 140000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 150000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 160000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 170000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 180000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 190000/225837 UTXOs insérés...
|
|
||||||
⏳ Traitement: 200000/225837 UTXOs insérés...
|
⏳ Traitement: 200000/225837 UTXOs insérés...
|
||||||
⏳ Traitement: 210000/225837 UTXOs insérés...
|
⏳ Traitement: 210000/225837 UTXOs insérés...
|
||||||
⏳ Traitement: 220000/225837 UTXOs insérés...
|
⏳ Traitement: 220000/225837 UTXOs insérés...
|
||||||
@ -98,3 +56,45 @@
|
|||||||
- Non dépensés: 61609
|
- Non dépensés: 61609
|
||||||
|
|
||||||
✅ Synchronisation terminée
|
✅ Synchronisation terminée
|
||||||
|
🔍 Démarrage de la synchronisation des UTXOs dépensés...
|
||||||
|
|
||||||
|
📊 UTXOs à vérifier: 61598
|
||||||
|
📡 Récupération des UTXOs depuis Bitcoin...
|
||||||
|
📊 UTXOs disponibles dans Bitcoin: 225867
|
||||||
|
💾 Création de la table temporaire...
|
||||||
|
💾 Insertion des UTXOs disponibles par batch...
|
||||||
|
⏳ Traitement: 10000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 20000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 30000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 40000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 50000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 60000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 70000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 80000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 90000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 100000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 110000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 120000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 130000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 140000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 150000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 160000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 170000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 180000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 190000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 200000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 210000/225867 UTXOs insérés...
|
||||||
|
⏳ Traitement: 220000/225867 UTXOs insérés...
|
||||||
|
💾 Mise à jour des UTXOs dépensés...
|
||||||
|
|
||||||
|
📊 Résumé:
|
||||||
|
- UTXOs vérifiés: 61598
|
||||||
|
- UTXOs toujours disponibles: 61598
|
||||||
|
- UTXOs dépensés détectés: 0
|
||||||
|
|
||||||
|
📈 Statistiques finales:
|
||||||
|
- Total UTXOs: 68398
|
||||||
|
- Dépensés: 6800
|
||||||
|
- Non dépensés: 61598
|
||||||
|
|
||||||
|
✅ Synchronisation terminée
|
||||||
|
|||||||
@ -272,14 +272,14 @@ export function LoginScreen(): JSX.Element {
|
|||||||
);
|
);
|
||||||
const endpoints = relays.map((r) => r.endpoint);
|
const endpoints = relays.map((r) => r.endpoint);
|
||||||
// Boucle de collecte : fetch signatures par hash jusqu'à satisfaction ou timeout
|
// Boucle de collecte : fetch signatures par hash jusqu'à satisfaction ou timeout
|
||||||
merged = await runCollectLoop(
|
merged = await runCollectLoop({
|
||||||
endpoints,
|
relayEndpoints: endpoints,
|
||||||
proof.challenge.hash,
|
hash: proof.challenge.hash,
|
||||||
proof.signatures,
|
ourSigs: proof.signatures,
|
||||||
loginPath,
|
path: loginPath,
|
||||||
pairToMembers,
|
pairToMembers,
|
||||||
pubkeyToPair,
|
pubkeyToPair,
|
||||||
{
|
opts: {
|
||||||
pollMs: COLLECT_POLL_MS,
|
pollMs: COLLECT_POLL_MS,
|
||||||
timeoutMs: COLLECT_TIMEOUT_MS,
|
timeoutMs: COLLECT_TIMEOUT_MS,
|
||||||
onProgress: (m) => {
|
onProgress: (m) => {
|
||||||
@ -290,7 +290,7 @@ export function LoginScreen(): JSX.Element {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsCollecting(false);
|
setIsCollecting(false);
|
||||||
setCollectProgressState(null);
|
setCollectProgressState(null);
|
||||||
@ -884,14 +884,14 @@ export function LoginScreen(): JSX.Element {
|
|||||||
identity?.publicKey ?? '',
|
identity?.publicKey ?? '',
|
||||||
loginPath.pairs_attendus,
|
loginPath.pairs_attendus,
|
||||||
);
|
);
|
||||||
const merged = await runCollectLoop(
|
const merged = await runCollectLoop({
|
||||||
endpoints,
|
relayEndpoints: endpoints,
|
||||||
proof.challenge.hash,
|
hash: proof.challenge.hash,
|
||||||
proof.signatures,
|
ourSigs: proof.signatures,
|
||||||
loginPath,
|
path: loginPath,
|
||||||
pairToMembers,
|
pairToMembers,
|
||||||
pubkeyToPair,
|
pubkeyToPair,
|
||||||
{
|
opts: {
|
||||||
pollMs: COLLECT_POLL_MS,
|
pollMs: COLLECT_POLL_MS,
|
||||||
timeoutMs: COLLECT_TIMEOUT_MS,
|
timeoutMs: COLLECT_TIMEOUT_MS,
|
||||||
onProgress: (m) => {
|
onProgress: (m) => {
|
||||||
@ -902,7 +902,7 @@ export function LoginScreen(): JSX.Element {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
setCollectedMerged(merged);
|
setCollectedMerged(merged);
|
||||||
} finally {
|
} finally {
|
||||||
setIsCollecting(false);
|
setIsCollecting(false);
|
||||||
|
|||||||
@ -89,15 +89,15 @@ export function PairingDisplayScreen(): JSX.Element {
|
|||||||
}
|
}
|
||||||
setIsConfirming(true);
|
setIsConfirming(true);
|
||||||
try {
|
try {
|
||||||
const ok = await runDevice2Confirmation(
|
const ok = await runDevice2Confirmation({
|
||||||
local.uuid,
|
pairLocal: local.uuid,
|
||||||
remote.uuid,
|
pairRemote: remote.uuid,
|
||||||
identity,
|
identity,
|
||||||
relays,
|
relays,
|
||||||
identity.t0_anniversaire,
|
start: identity.t0_anniversaire,
|
||||||
Date.now(),
|
end: Date.now(),
|
||||||
pubkeyHex,
|
remotePublicKey: pubkeyHex,
|
||||||
);
|
});
|
||||||
setJustConnected(ok);
|
setJustConnected(ok);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Pairing confirmation (device 2):', err);
|
console.error('Pairing confirmation (device 2):', err);
|
||||||
|
|||||||
@ -94,13 +94,13 @@ export function PairingSetupBlock(): JSX.Element {
|
|||||||
}
|
}
|
||||||
setIsConfirming(true);
|
setIsConfirming(true);
|
||||||
try {
|
try {
|
||||||
await runDevice1Confirmation(
|
await runDevice1Confirmation({
|
||||||
local.uuid,
|
pairLocal: local.uuid,
|
||||||
remote.uuid,
|
pairRemote: remote.uuid,
|
||||||
identity,
|
identity,
|
||||||
relays,
|
relays,
|
||||||
pubkeyHex,
|
remotePublicKey: pubkeyHex,
|
||||||
);
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Pairing confirmation (device 1):', err);
|
console.error('Pairing confirmation (device 1):', err);
|
||||||
setRemoteError(
|
setRemoteError(
|
||||||
|
|||||||
@ -57,15 +57,15 @@ export function SyncScreen(): JSX.Element {
|
|||||||
remote !== undefined &&
|
remote !== undefined &&
|
||||||
identity.privateKey !== undefined
|
identity.privateKey !== undefined
|
||||||
) {
|
) {
|
||||||
void checkPairingConfirmationFromSync(
|
void checkPairingConfirmationFromSync({
|
||||||
relays,
|
relays,
|
||||||
local.uuid,
|
pairLocal: local.uuid,
|
||||||
remote.uuid,
|
pairRemote: remote.uuid,
|
||||||
identity,
|
identity,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
remote.publicKey,
|
remotePublicKey: remote.publicKey,
|
||||||
).catch((err: unknown) => {
|
}).catch((err: unknown) => {
|
||||||
console.error('Pairing confirmation check during sync:', err);
|
console.error('Pairing confirmation check during sync:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,11 @@ import ReactDOM from 'react-dom/client';
|
|||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
const rootElement = document.getElementById('root');
|
||||||
|
if (rootElement === null) {
|
||||||
|
throw new Error('Root element not found');
|
||||||
|
}
|
||||||
|
ReactDOM.createRoot(rootElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
|
|||||||
@ -171,17 +171,22 @@ function buildMsgCle(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PublishPairingMessageParams {
|
||||||
|
relays: RelayConfig[];
|
||||||
|
message: MembreFinaliserMessage;
|
||||||
|
hash: string;
|
||||||
|
recipientPublicKey: string;
|
||||||
|
senderIdentity: LocalIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish a pairing message (MsgChiffre) to relays.
|
* Publish a pairing message (MsgChiffre) to relays.
|
||||||
* DH obligatoire: encrypt with ECDH and POST MsgCle. recipientPublicKey and senderIdentity required.
|
* DH obligatoire: encrypt with ECDH and POST MsgCle. recipientPublicKey and senderIdentity required.
|
||||||
*/
|
*/
|
||||||
async function publishPairingMessage(
|
async function publishPairingMessage(
|
||||||
relays: RelayConfig[],
|
params: PublishPairingMessageParams,
|
||||||
message: MembreFinaliserMessage,
|
|
||||||
hash: string,
|
|
||||||
recipientPublicKey: string,
|
|
||||||
senderIdentity: LocalIdentity,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const { relays, message, hash, recipientPublicKey, senderIdentity } = params;
|
||||||
if (recipientPublicKey === undefined || recipientPublicKey === '') {
|
if (recipientPublicKey === undefined || recipientPublicKey === '') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Pairing DH obligatoire : clé publique du pair distant requise (exiger publicKey).',
|
'Pairing DH obligatoire : clé publique du pair distant requise (exiger publicKey).',
|
||||||
@ -212,25 +217,31 @@ async function publishPairingMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PublishPairingMessageAndSignatureParams {
|
||||||
|
relays: RelayConfig[];
|
||||||
|
message: MembreFinaliserMessage;
|
||||||
|
hash: string;
|
||||||
|
sig: Signature;
|
||||||
|
recipientPublicKey: string;
|
||||||
|
senderIdentity: LocalIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish message and first signature to relays.
|
* Publish message and first signature to relays.
|
||||||
* DH obligatoire: recipientPublicKey and senderIdentity required.
|
* DH obligatoire: recipientPublicKey and senderIdentity required.
|
||||||
*/
|
*/
|
||||||
export async function publishPairingMessageAndSignature(
|
export async function publishPairingMessageAndSignature(
|
||||||
relays: RelayConfig[],
|
params: PublishPairingMessageAndSignatureParams,
|
||||||
message: MembreFinaliserMessage,
|
|
||||||
hash: string,
|
|
||||||
sig: Signature,
|
|
||||||
recipientPublicKey: string,
|
|
||||||
senderIdentity: LocalIdentity,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await publishPairingMessage(
|
const { relays, message, hash, sig, recipientPublicKey, senderIdentity } =
|
||||||
|
params;
|
||||||
|
await publishPairingMessage({
|
||||||
relays,
|
relays,
|
||||||
message,
|
message,
|
||||||
hash,
|
hash,
|
||||||
recipientPublicKey,
|
recipientPublicKey,
|
||||||
senderIdentity,
|
senderIdentity,
|
||||||
);
|
});
|
||||||
const enabled = relays.filter((r) => r.enabled);
|
const enabled = relays.filter((r) => r.enabled);
|
||||||
const msgSig: MsgSignature = { signature: sig };
|
const msgSig: MsgSignature = { signature: sig };
|
||||||
for (const r of enabled) {
|
for (const r of enabled) {
|
||||||
@ -255,19 +266,32 @@ export async function publishPairingSignature(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FetchPairingMessageParams {
|
||||||
|
relays: RelayConfig[];
|
||||||
|
pairLocal: string;
|
||||||
|
pairRemote: string;
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
senderPublicKey?: string;
|
||||||
|
ourIdentity?: LocalIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch messages in time window, find "membre finaliser" v1 for our pairs.
|
* Fetch messages in time window, find "membre finaliser" v1 for our pairs.
|
||||||
* DH only: ECDH decryption when senderPublicKey and ourIdentity provided. No base64.
|
* DH only: ECDH decryption when senderPublicKey and ourIdentity provided. No base64.
|
||||||
*/
|
*/
|
||||||
export async function fetchPairingMessage(
|
export async function fetchPairingMessage(
|
||||||
relays: RelayConfig[],
|
params: FetchPairingMessageParams,
|
||||||
pairLocal: string,
|
|
||||||
pairRemote: string,
|
|
||||||
start: number,
|
|
||||||
end: number,
|
|
||||||
senderPublicKey?: string,
|
|
||||||
ourIdentity?: LocalIdentity,
|
|
||||||
): Promise<{ message: MembreFinaliserMessage; hash: string } | null> {
|
): Promise<{ message: MembreFinaliserMessage; hash: string } | null> {
|
||||||
|
const {
|
||||||
|
relays,
|
||||||
|
pairLocal,
|
||||||
|
pairRemote,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
senderPublicKey,
|
||||||
|
ourIdentity,
|
||||||
|
} = params;
|
||||||
const enabled = relays.filter((r) => r.enabled);
|
const enabled = relays.filter((r) => r.enabled);
|
||||||
const key = sortedPairKey(pairLocal, pairRemote);
|
const key = sortedPairKey(pairLocal, pairRemote);
|
||||||
const useEcdh =
|
const useEcdh =
|
||||||
@ -401,13 +425,13 @@ async function buildAndPublishPairingVersion2(
|
|||||||
};
|
};
|
||||||
const canonical = JSON.stringify(m2);
|
const canonical = JSON.stringify(m2);
|
||||||
const hash2 = await hashStringAsync(canonical, 'sha256');
|
const hash2 = await hashStringAsync(canonical, 'sha256');
|
||||||
await publishPairingMessage(
|
await publishPairingMessage({
|
||||||
relays,
|
relays,
|
||||||
m2,
|
message: m2,
|
||||||
hash2,
|
hash: hash2,
|
||||||
recipientPublicKey,
|
recipientPublicKey,
|
||||||
senderIdentity,
|
senderIdentity,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,17 +480,22 @@ function delay(ms: number): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RunDevice1ConfirmationParams {
|
||||||
|
pairLocal: string;
|
||||||
|
pairRemote: string;
|
||||||
|
identity: LocalIdentity;
|
||||||
|
relays: RelayConfig[];
|
||||||
|
remotePublicKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run confirmation flow for device 1: create M, sign, publish, poll for remote sig, store.
|
* Run confirmation flow for device 1: create M, sign, publish, poll for remote sig, store.
|
||||||
* remotePublicKey: identity public key of device 2, for ECDH encryption. Required (DH obligatoire).
|
* remotePublicKey: identity public key of device 2, for ECDH encryption. Required (DH obligatoire).
|
||||||
*/
|
*/
|
||||||
export async function runDevice1Confirmation(
|
export async function runDevice1Confirmation(
|
||||||
pairLocal: string,
|
params: RunDevice1ConfirmationParams,
|
||||||
pairRemote: string,
|
|
||||||
identity: LocalIdentity,
|
|
||||||
relays: RelayConfig[],
|
|
||||||
remotePublicKey: string,
|
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
const { pairLocal, pairRemote, identity, relays, remotePublicKey } = params;
|
||||||
if (remotePublicKey === undefined || remotePublicKey === '') {
|
if (remotePublicKey === undefined || remotePublicKey === '') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Pairing DH obligatoire : clé publique du pair distant requise (exiger publicKey).',
|
'Pairing DH obligatoire : clé publique du pair distant requise (exiger publicKey).',
|
||||||
@ -479,14 +508,14 @@ export async function runDevice1Confirmation(
|
|||||||
);
|
);
|
||||||
const n = generateUuid();
|
const n = generateUuid();
|
||||||
const sig = signMembreFinaliser(hash, identity, n);
|
const sig = signMembreFinaliser(hash, identity, n);
|
||||||
await publishPairingMessageAndSignature(
|
await publishPairingMessageAndSignature({
|
||||||
relays,
|
relays,
|
||||||
message,
|
message,
|
||||||
hash,
|
hash,
|
||||||
sig,
|
sig,
|
||||||
remotePublicKey,
|
recipientPublicKey: remotePublicKey,
|
||||||
identity,
|
senderIdentity: identity,
|
||||||
);
|
});
|
||||||
for (let i = 0; i < POLL_ATTEMPTS; i++) {
|
for (let i = 0; i < POLL_ATTEMPTS; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
await delay(POLL_DELAY_MS);
|
await delay(POLL_DELAY_MS);
|
||||||
@ -508,28 +537,41 @@ export async function runDevice1Confirmation(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RunDevice2ConfirmationParams {
|
||||||
|
pairLocal: string;
|
||||||
|
pairRemote: string;
|
||||||
|
identity: LocalIdentity;
|
||||||
|
relays: RelayConfig[];
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
remotePublicKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run confirmation flow for device 2: fetch M, verify sig1, sign, publish sig2, store.
|
* Run confirmation flow for device 2: fetch M, verify sig1, sign, publish sig2, store.
|
||||||
* remotePublicKey: identity public key of device 1 (sender), for ECDH decryption.
|
* remotePublicKey: identity public key of device 1 (sender), for ECDH decryption.
|
||||||
*/
|
*/
|
||||||
export async function runDevice2Confirmation(
|
export async function runDevice2Confirmation(
|
||||||
pairLocal: string,
|
params: RunDevice2ConfirmationParams,
|
||||||
pairRemote: string,
|
|
||||||
identity: LocalIdentity,
|
|
||||||
relays: RelayConfig[],
|
|
||||||
start: number,
|
|
||||||
end: number,
|
|
||||||
remotePublicKey?: string,
|
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const found = await fetchPairingMessage(
|
const {
|
||||||
|
pairLocal,
|
||||||
|
pairRemote,
|
||||||
|
identity,
|
||||||
|
relays,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
remotePublicKey,
|
||||||
|
} = params;
|
||||||
|
const found = await fetchPairingMessage({
|
||||||
relays,
|
relays,
|
||||||
pairLocal,
|
pairLocal,
|
||||||
pairRemote,
|
pairRemote,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
remotePublicKey,
|
senderPublicKey: remotePublicKey,
|
||||||
identity,
|
ourIdentity: identity,
|
||||||
);
|
});
|
||||||
if (found === null) {
|
if (found === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -547,33 +589,46 @@ export async function runDevice2Confirmation(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CheckPairingConfirmationFromSyncParams {
|
||||||
|
relays: RelayConfig[];
|
||||||
|
pairLocal: string;
|
||||||
|
pairRemote: string;
|
||||||
|
identity: LocalIdentity;
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
remotePublicKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for pairing confirmation during Sync (device 1 timed out, user runs Sync later).
|
* Check for pairing confirmation during Sync (device 1 timed out, user runs Sync later).
|
||||||
* Fetches pairing message v1, signatures; if both signers present, stores confirmation.
|
* Fetches pairing message v1, signatures; if both signers present, stores confirmation.
|
||||||
* remotePublicKey: identity public key of the other device (sender), for ECDH decryption.
|
* remotePublicKey: identity public key of the other device (sender), for ECDH decryption.
|
||||||
*/
|
*/
|
||||||
export async function checkPairingConfirmationFromSync(
|
export async function checkPairingConfirmationFromSync(
|
||||||
relays: RelayConfig[],
|
params: CheckPairingConfirmationFromSyncParams,
|
||||||
pairLocal: string,
|
|
||||||
pairRemote: string,
|
|
||||||
identity: LocalIdentity,
|
|
||||||
start: number,
|
|
||||||
end: number,
|
|
||||||
remotePublicKey?: string,
|
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
const {
|
||||||
|
relays,
|
||||||
|
pairLocal,
|
||||||
|
pairRemote,
|
||||||
|
identity,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
remotePublicKey,
|
||||||
|
} = params;
|
||||||
const already = await getPairingConfirmed(pairLocal, pairRemote);
|
const already = await getPairingConfirmed(pairLocal, pairRemote);
|
||||||
if (already) {
|
if (already) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const found = await fetchPairingMessage(
|
const found = await fetchPairingMessage({
|
||||||
relays,
|
relays,
|
||||||
pairLocal,
|
pairLocal,
|
||||||
pairRemote,
|
pairRemote,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
remotePublicKey,
|
senderPublicKey: remotePublicKey,
|
||||||
identity,
|
ourIdentity: identity,
|
||||||
);
|
});
|
||||||
if (found === null) {
|
if (found === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -273,8 +273,18 @@ export function uuidToBip32Words(uuid: string): string[] {
|
|||||||
const uuidBytes = hexToBytes(uuid.replace(/-/g, ''));
|
const uuidBytes = hexToBytes(uuid.replace(/-/g, ''));
|
||||||
const words: string[] = [];
|
const words: string[] = [];
|
||||||
for (let i = 0; i < uuidBytes.length; i += 2) {
|
for (let i = 0; i < uuidBytes.length; i += 2) {
|
||||||
const index = (uuidBytes[i]! << 8) | (uuidBytes[i + 1] ?? 0);
|
const byte1 = uuidBytes[i];
|
||||||
words.push(BIP32_WORDLIST[index % BIP32_WORDLIST.length]!);
|
if (byte1 === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const byte2 = uuidBytes[i + 1] ?? 0;
|
||||||
|
const index = (byte1 << 8) | byte2;
|
||||||
|
const wordIndex = index % BIP32_WORDLIST.length;
|
||||||
|
const word = BIP32_WORDLIST[wordIndex];
|
||||||
|
if (word === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
words.push(word);
|
||||||
}
|
}
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
@ -294,8 +304,12 @@ export function bip32WordsToUuid(words: string[]): string | null {
|
|||||||
}
|
}
|
||||||
const bytes = new Uint8Array(indices.length * 2);
|
const bytes = new Uint8Array(indices.length * 2);
|
||||||
for (let i = 0; i < indices.length; i++) {
|
for (let i = 0; i < indices.length; i++) {
|
||||||
bytes[i * 2] = (indices[i]! >> 8) & 0xff;
|
const index = indices[i];
|
||||||
bytes[i * 2 + 1] = indices[i]! & 0xff;
|
if (index === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bytes[i * 2] = (index >> 8) & 0xff;
|
||||||
|
bytes[i * 2 + 1] = index & 0xff;
|
||||||
}
|
}
|
||||||
const hex = bytesToHex(bytes);
|
const hex = bytesToHex(bytes);
|
||||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
||||||
|
|||||||
@ -151,19 +151,32 @@ export interface CollectLoopOpts {
|
|||||||
onProgress?: (merged: ProofSignature[]) => void;
|
onProgress?: (merged: ProofSignature[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RunCollectLoopParams {
|
||||||
|
relayEndpoints: string[];
|
||||||
|
hash: string;
|
||||||
|
ourSigs: ProofSignature[];
|
||||||
|
path: LoginPath;
|
||||||
|
pairToMembers: Map<string, string[]>;
|
||||||
|
pubkeyToPair: Map<string, string>;
|
||||||
|
opts: CollectLoopOpts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect signatures from relays until we have enough per member, or timeout.
|
* Collect signatures from relays until we have enough per member, or timeout.
|
||||||
* Optional onProgress called each poll for UI (e.g. X/Y signatures).
|
* Optional onProgress called each poll for UI (e.g. X/Y signatures).
|
||||||
*/
|
*/
|
||||||
export async function runCollectLoop(
|
export async function runCollectLoop(
|
||||||
relayEndpoints: string[],
|
params: RunCollectLoopParams,
|
||||||
hash: string,
|
|
||||||
ourSigs: ProofSignature[],
|
|
||||||
path: LoginPath,
|
|
||||||
pairToMembers: Map<string, string[]>,
|
|
||||||
pubkeyToPair: Map<string, string>,
|
|
||||||
opts: CollectLoopOpts,
|
|
||||||
): Promise<ProofSignature[]> {
|
): Promise<ProofSignature[]> {
|
||||||
|
const {
|
||||||
|
relayEndpoints,
|
||||||
|
hash,
|
||||||
|
ourSigs,
|
||||||
|
path,
|
||||||
|
pairToMembers,
|
||||||
|
pubkeyToPair,
|
||||||
|
opts,
|
||||||
|
} = params;
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
let merged = ourSigs;
|
let merged = ourSigs;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user