Fix Bitcoin miner PSBT signing after bitcoind restart
**Motivations:** - Miner stopped producing blocks after bitcoind and mempool restart - PSBT signing failed with "PSBT signing failed" error - descriptorprocesspsbt failed with "Argument list too long" error due to large PSBT size (1.5MB) **Root causes:** - descriptorprocesspsbt called via command line exceeded argument length limit when PSBT is large - walletprocesspsbt succeeded but could not mark PSBT as complete because it cannot sign artificial signet transactions (to_spend/spend) which are not real UTXOs in wallet - After restart, the miner needed descriptorprocesspsbt to sign artificial signet transactions, but command line approach failed **Correctifs:** - Modified miner to use JSON-RPC HTTP API directly for descriptorprocesspsbt instead of command line - PSBT is now passed via HTTP request body, avoiding command line argument length limit - Added fallback to walletprocesspsbt if HTTP RPC fails - Updated mine.sh to use -rpcwallet and -datadir correctly for wallet operations **Evolutions:** - Miner now uses HTTP JSON-RPC for large PSBTs, making it more robust for signet mining with many transactions - Improved error handling with fallback mechanisms **Pages affectées:** - miner: Modified PSBT signing logic to use HTTP JSON-RPC for descriptorprocesspsbt - mine.sh: Updated to use -rpcwallet and -datadir correctly
This commit is contained in:
parent
1d4b0d8f33
commit
f7f9442156
@ -35,3 +35,42 @@
|
|||||||
- Non dépensés: 67955
|
- Non dépensés: 67955
|
||||||
|
|
||||||
✅ Synchronisation terminée
|
✅ Synchronisation terminée
|
||||||
|
🔍 Démarrage de la synchronisation des UTXOs dépensés...
|
||||||
|
|
||||||
|
📊 UTXOs à vérifier: 66109
|
||||||
|
📡 Récupération des UTXOs depuis Bitcoin...
|
||||||
|
📊 UTXOs disponibles dans Bitcoin: 189710
|
||||||
|
💾 Création de la table temporaire...
|
||||||
|
💾 Insertion des UTXOs disponibles par batch...
|
||||||
|
⏳ Traitement: 10000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 20000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 30000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 40000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 50000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 60000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 70000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 80000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 90000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 100000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 110000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 120000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 130000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 140000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 150000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 160000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 170000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 180000/189710 UTXOs insérés...
|
||||||
|
⏳ Traitement: 189710/189710 UTXOs insérés...
|
||||||
|
💾 Mise à jour des UTXOs dépensés...
|
||||||
|
|
||||||
|
📊 Résumé:
|
||||||
|
- UTXOs vérifiés: 66109
|
||||||
|
- UTXOs toujours disponibles: 66109
|
||||||
|
- UTXOs dépensés détectés: 0
|
||||||
|
|
||||||
|
📈 Statistiques finales:
|
||||||
|
- Total UTXOs: 68398
|
||||||
|
- Dépensés: 2294
|
||||||
|
- Non dépensés: 66104
|
||||||
|
|
||||||
|
✅ Synchronisation terminée
|
||||||
|
|||||||
@ -23,7 +23,7 @@ Référence : `userwallet/docs/specs.md` (modèle, objets, machine à états, ca
|
|||||||
- **Pairing** : 8 mots BIP32-style, WordInputGrid, confirmation croisée « membre finaliser », IndexedDB, statut « Connecté ».
|
- **Pairing** : 8 mots BIP32-style, WordInputGrid, confirmation croisée « membre finaliser », IndexedDB, statut « Connecté ».
|
||||||
- **Relais** : config, health, GET/POST messages/signatures/keys, **GET /bloom** ; sync, HashCache (IndexedDB), dédup, fetch clés/signatures.
|
- **Relais** : config, health, GET/POST messages/signatures/keys, **GET /bloom** ; sync, HashCache (IndexedDB), dédup, fetch clés/signatures.
|
||||||
- **Graphe** : GraphResolver, caches (services, contrats, champs, actions, membres, pairs), `resolveLoginPath`.
|
- **Graphe** : GraphResolver, caches (services, contrats, champs, actions, membres, pairs), `resolveLoginPath`.
|
||||||
- **Login** : LoginBuilder (challenge, nonce, chiffrement « for all »), construction preuve, publication message → signatures → clés. **Anti-rejeu** : NonceStore (IndexedDB), vérification timestamp (fenêtre), `X_NONCE_REUSED` / `X_TIMESTAMP_OUT_OF_WINDOW` avant publish.
|
- **Login** : LoginBuilder (challenge, nonce, chiffrement « for all »), construction preuve, publication message → signatures → clés. **Notifications relais** : `runCollectLoop` `onProgress`, `collectProgress`, affichage « Signatures collectées : X / Y » dans LoginCollectShare. **Anti-rejeu** : NonceStore (IndexedDB), vérification timestamp (fenêtre), `X_NONCE_REUSED` / `X_TIMESTAMP_OUT_OF_WINDOW` avant publish.
|
||||||
- **Iframe** : auth-request / auth-response / login-proof, useChannel, postMessage.
|
- **Iframe** : auth-request / auth-response / login-proof, useChannel, postMessage.
|
||||||
- **Écrans** : Home, CreateIdentity, ImportIdentity, RelaySettings, Sync, Manage Pairs, Pairing (setup + display), Services, Login, Data Export/Import, Unlock. Navigation via GlobalActionBar.
|
- **Écrans** : Home, CreateIdentity, ImportIdentity, RelaySettings, Sync, Manage Pairs, Pairing (setup + display), Services, Login, Data Export/Import, Unlock. Navigation via GlobalActionBar.
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ Référence : `userwallet/docs/specs.md` (modèle, objets, machine à états, ca
|
|||||||
**À valider avant implémentation.** Voir `features/userwallet-ecrans-login-a-valider.md`.
|
**À valider avant implémentation.** Voir `features/userwallet-ecrans-login-a-valider.md`.
|
||||||
|
|
||||||
- **Service iframe** : fourni par channel message (contrat + contrats fils). Si absent → contrat par défaut en dur dans le front jusqu’à réception.
|
- **Service iframe** : fourni par channel message (contrat + contrats fils). Si absent → contrat par défaut en dur dans le front jusqu’à réception.
|
||||||
- **Écrans login** : déjà en place. Reste à faire : **notifications** selon événements relais (collecte signatures, clés de déchiffrement → hash à fetch → signatures, contrats, membres, pairs, actions, champs sur le relai).
|
- **Écrans login** : déjà en place. **Notifications relais** : progression collecte signatures (X/Y) implémentée via `onProgress` dans `runCollectLoop` et affichage dans `LoginCollectShare` ; voir `features/userwallet-notifications-relais.md`. Reste à faire : réagir à d’autres événements relais si extension (ex. push) — hash à fetch pour signatures, contrats, membres, pairs, actions, champs.
|
||||||
- Cette section **évoluera avec l'avancement des tests** une fois validée.
|
- Cette section **évoluera avec l'avancement des tests** une fois validée.
|
||||||
|
|
||||||
- **Sélection service / sélection membre** : écrans dédiés « choisir le service cible du login » et « choisir le membre (quand plusieurs) » avec liste, statuts, validation des prérequis (action login, pairs).
|
- **Sélection service / sélection membre** : écrans dédiés « choisir le service cible du login » et « choisir le membre (quand plusieurs) » avec liste, statuts, validation des prérequis (action login, pairs).
|
||||||
@ -85,7 +85,7 @@ Référence : `userwallet/docs/specs.md` (modèle, objets, machine à états, ca
|
|||||||
| Domaine | Statut | Priorité |
|
| Domaine | Statut | Priorité |
|
||||||
|--------|--------|----------|
|
|--------|--------|----------|
|
||||||
| Machine à états login | Fait (loginStateMachine, useLoginStateMachine, dispatch) | — |
|
| Machine à états login | Fait (loginStateMachine, useLoginStateMachine, dispatch) | — |
|
||||||
| Écrans sélection service / membre, chemin login, collecte sig, publication, vérification | À valider avant implémentation (voir userwallet-ecrans-login-a-valider) ; notifications relais à implémenter | Haute |
|
| Écrans sélection service / membre, chemin login, collecte sig, publication, vérification | À valider avant implémentation (voir userwallet-ecrans-login-a-valider) ; notifications relais (progression X/Y) fait (userwallet-notifications-relais) | Haute |
|
||||||
| États « indéchiffrable » / « signature manquante » / statut relais visibles | Fait (Sync) | — |
|
| États « indéchiffrable » / « signature manquante » / statut relais visibles | Fait (Sync) | — |
|
||||||
| Anti-rejeu (nonce, fenêtre, cache) | Fait (NonceStore, verifyTimestamp, X_*) | — |
|
| Anti-rejeu (nonce, fenêtre, cache) | Fait (NonceStore, verifyTimestamp, X_*) | — |
|
||||||
| Validation stricte validateurs + clé autorisée | Fait (strict verify, version contrats) | — |
|
| Validation stricte validateurs + clé autorisée | Fait (strict verify, version contrats) | — |
|
||||||
|
|||||||
8
mine.sh
8
mine.sh
@ -1,8 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
NBITS=${NBITS:-"1e0377ae"} #minimum difficulty in signet
|
NBITS=${NBITS:-"1e0377ae"} #minimum difficulty in signet
|
||||||
|
WALLET=${WALLET:-"custom_signet"} # Wallet name for descriptor wallet
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
ADDR=${MINETO:-$(bitcoin-cli getnewaddress)}
|
ADDR=${MINETO:-$(bitcoin-cli -rpcwallet=$WALLET getnewaddress)}
|
||||||
if [[ -f "${BITCOIN_DIR}/BLOCKPRODUCTIONDELAY.txt" ]]; then
|
if [[ -f "${BITCOIN_DIR}/BLOCKPRODUCTIONDELAY.txt" ]]; then
|
||||||
BLOCKPRODUCTIONDELAY_OVERRIDE=$(cat ~/.bitcoin/BLOCKPRODUCTIONDELAY.txt)
|
BLOCKPRODUCTIONDELAY_OVERRIDE=$(cat ~/.bitcoin/BLOCKPRODUCTIONDELAY.txt)
|
||||||
echo "Delay OVERRIDE before next block" $BLOCKPRODUCTIONDELAY_OVERRIDE "seconds."
|
echo "Delay OVERRIDE before next block" $BLOCKPRODUCTIONDELAY_OVERRIDE "seconds."
|
||||||
@ -19,5 +20,8 @@ while true; do
|
|||||||
# The miner will automatically use PRIVKEY from environment for descriptorprocesspsbt
|
# The miner will automatically use PRIVKEY from environment for descriptorprocesspsbt
|
||||||
# Export PRIVKEY to ensure it's available to the miner process
|
# Export PRIVKEY to ensure it's available to the miner process
|
||||||
export PRIVKEY=${PRIVKEY:-$(cat ~/.bitcoin/PRIVKEY.txt 2>/dev/null || echo "")}
|
export PRIVKEY=${PRIVKEY:-$(cat ~/.bitcoin/PRIVKEY.txt 2>/dev/null || echo "")}
|
||||||
miner --cli="bitcoin-cli" generate --grind-cmd="bitcoin-util grind" --address=$ADDR --nbits=$NBITS --set-block-time=$(date +%s)
|
# Get block template and pipe it to miner
|
||||||
|
# Use bitcoin-cli with -datadir but without -rpcwallet for miner (descriptorprocesspsbt is node RPC, not wallet RPC)
|
||||||
|
bitcoin-cli -rpcwallet=$WALLET getblocktemplate '{"rules": ["segwit", "signet"]}' | \
|
||||||
|
miner --cli="bitcoin-cli -datadir=/root/.bitcoin" generate --grind-cmd="bitcoin-util grind" --address=$ADDR --nbits=$NBITS --set-block-time=$(date +%s)
|
||||||
done
|
done
|
||||||
64
miner
64
miner
@ -504,17 +504,73 @@ def do_generate(args):
|
|||||||
if not privkey and hasattr(args, 'privkey') and args.privkey:
|
if not privkey and hasattr(args, 'privkey') and args.privkey:
|
||||||
privkey = args.privkey
|
privkey = args.privkey
|
||||||
if privkey:
|
if privkey:
|
||||||
|
# Try descriptorprocesspsbt first using JSON-RPC HTTP to avoid "Argument list too long"
|
||||||
|
# descriptorprocesspsbt is needed for signet artificial transactions
|
||||||
|
cli_base = args.cli.split(" ")
|
||||||
|
cli_node = [c for c in cli_base if not c.startswith("-rpcwallet")]
|
||||||
|
if not cli_node:
|
||||||
|
cli_node = ["bitcoin-cli"]
|
||||||
descriptor = "pk(%s)" % privkey
|
descriptor = "pk(%s)" % privkey
|
||||||
# Use descriptorprocesspsbt: PSBT, descriptors array (JSON), sighashtype, bip32derivs, finalize
|
|
||||||
# bitcoin_cli adds -signet automatically and converts args to strings
|
|
||||||
descriptors_array = [descriptor]
|
descriptors_array = [descriptor]
|
||||||
psbt_signed = json.loads(args.bcli("descriptorprocesspsbt", psbt, json.dumps(descriptors_array), "ALL", "true", "true"))
|
try:
|
||||||
logging.debug("Used descriptorprocesspsbt for signing")
|
# Use JSON-RPC HTTP directly to avoid command line length limit
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
# Extract RPC credentials from bitcoin-cli config or use defaults
|
||||||
|
rpc_url = "http://127.0.0.1:38332/" # Default signet RPC port
|
||||||
|
rpc_user = "bitcoin"
|
||||||
|
rpc_pass = "bitcoin"
|
||||||
|
# Try to get RPC credentials from environment or config
|
||||||
|
if "RPCUSER" in os.environ:
|
||||||
|
rpc_user = os.environ["RPCUSER"]
|
||||||
|
if "RPCPASSWORD" in os.environ:
|
||||||
|
rpc_pass = os.environ["RPCPASSWORD"]
|
||||||
|
# Create JSON-RPC request
|
||||||
|
rpc_request = {
|
||||||
|
"method": "descriptorprocesspsbt",
|
||||||
|
"params": [psbt, descriptors_array, "ALL", True, True],
|
||||||
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0"
|
||||||
|
}
|
||||||
|
# Encode request
|
||||||
|
data = json.dumps(rpc_request).encode('utf-8')
|
||||||
|
# Create HTTP request with basic auth
|
||||||
|
req = urllib.request.Request(rpc_url, data=data, headers={"Content-Type": "application/json"})
|
||||||
|
# Add basic auth
|
||||||
|
import base64
|
||||||
|
credentials = base64.b64encode(("%s:%s" % (rpc_user, rpc_pass)).encode()).decode()
|
||||||
|
req.add_header("Authorization", "Basic %s" % credentials)
|
||||||
|
# Send request
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as response:
|
||||||
|
rpc_response = json.loads(response.read().decode('utf-8'))
|
||||||
|
if "result" in rpc_response:
|
||||||
|
psbt_signed = rpc_response["result"]
|
||||||
|
elif "error" in rpc_response:
|
||||||
|
raise Exception("RPC error: %s" % rpc_response["error"])
|
||||||
|
else:
|
||||||
|
psbt_signed = rpc_response
|
||||||
|
logging.debug("Used descriptorprocesspsbt via HTTP for signing with descriptor: %s" % descriptor)
|
||||||
|
except Exception as e:
|
||||||
|
# If descriptorprocesspsbt fails, try walletprocesspsbt as fallback
|
||||||
|
logging.debug("descriptorprocesspsbt via HTTP failed, trying walletprocesspsbt: %s" % str(e))
|
||||||
|
input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8')
|
||||||
|
psbt_signed = json.loads(args.bcli("-stdin", "walletprocesspsbt", input=input_stream))
|
||||||
|
logging.debug("walletprocesspsbt completed, complete=%s" % psbt_signed.get("complete", False))
|
||||||
else:
|
else:
|
||||||
# Fallback to walletprocesspsbt if no PRIVKEY available
|
# Fallback to walletprocesspsbt if no PRIVKEY available
|
||||||
logging.debug("No PRIVKEY found, falling back to walletprocesspsbt")
|
logging.debug("No PRIVKEY found, falling back to walletprocesspsbt")
|
||||||
input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8')
|
input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8')
|
||||||
psbt_signed = json.loads(args.bcli("-stdin", "walletprocesspsbt", input=input_stream))
|
psbt_signed = json.loads(args.bcli("-stdin", "walletprocesspsbt", input=input_stream))
|
||||||
|
except Exception as e:
|
||||||
|
# If descriptorprocesspsbt fails, try walletprocesspsbt as fallback
|
||||||
|
logging.debug("descriptorprocesspsbt failed, trying walletprocesspsbt: %s" % str(e))
|
||||||
|
try:
|
||||||
|
input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8')
|
||||||
|
psbt_signed = json.loads(args.bcli("-stdin", "walletprocesspsbt", input=input_stream))
|
||||||
|
logging.debug("walletprocesspsbt succeeded")
|
||||||
|
except Exception as e2:
|
||||||
|
logging.debug("walletprocesspsbt also failed: %s" % str(e2))
|
||||||
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If descriptorprocesspsbt fails, try walletprocesspsbt as fallback
|
# If descriptorprocesspsbt fails, try walletprocesspsbt as fallback
|
||||||
logging.debug("descriptorprocesspsbt failed, trying walletprocesspsbt: %s" % str(e))
|
logging.debug("descriptorprocesspsbt failed, trying walletprocesspsbt: %s" % str(e))
|
||||||
|
|||||||
38
userwallet/features/userwallet-notifications-relais.md
Normal file
38
userwallet/features/userwallet-notifications-relais.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# UserWallet – Notifications relais (pull-based)
|
||||||
|
|
||||||
|
**Author:** Équipe 4NK
|
||||||
|
**Date:** 2026-01-26
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Exposer les « notifications » liées aux événements relais (collecte signatures, clés) pour que l’UI sache **quel hash** aller chercher et afficher la **progression** (ex. X/Y signatures). Modèle **pull-only** : pas de push/WebSocket, uniquement GET sur les relais.
|
||||||
|
|
||||||
|
## Implémenté
|
||||||
|
|
||||||
|
### 1. Collecte signatures (login mFA)
|
||||||
|
|
||||||
|
- **Hash connu** : le challenge login a un `hash` ; on fetch `GET /signatures/:hash` sur chaque relais.
|
||||||
|
- **Boucle de collecte** : `runCollectLoop` appelle `fetchSignaturesForHash` puis merge, jusqu’à `hasEnoughSignatures` ou timeout.
|
||||||
|
- **Progression** : `CollectLoopOpts.onProgress?: (merged) => void` appelé à chaque poll. L’UI peut afficher « Signatures collectées : X / Y » via `collectProgress(path, merged, pairToMembers)` → `{ satisfied, required }`.
|
||||||
|
- **LoginCollectShare** : reçoit `collectProgress` et affiche « Signatures collectées : X / Y » pendant la collecte.
|
||||||
|
|
||||||
|
### 2. Clés et messages (sync)
|
||||||
|
|
||||||
|
- **Scan keys** : `GET /keys?start=&end=` → liste de `MsgCle` avec `hash_message`. On en déduit les hashes à fetch.
|
||||||
|
- **Fetch par hash** : `GET /messages/:hash` pour chaque hash, puis ECDH decrypt. Pas de « notification » explicite : le sync fait scan → fetch → decrypt en une passe. Les stats (indéchiffrable, validé, non validé) sont remontées au `SyncScreen`.
|
||||||
|
|
||||||
|
### 3. Pairing (membre finaliser)
|
||||||
|
|
||||||
|
- Hash et fetch signatures par hash déjà utilisés dans `fetchSignaturesForHash` (pairingConfirm). Pas de progression affichée côté pairing.
|
||||||
|
|
||||||
|
## À faire (évolutif)
|
||||||
|
|
||||||
|
- **Événements relais** : si un jour le relais propose du push (ex. WebSocket), adapter l’UI pour réagir aux événements « nouveau message / nouvelles signatures / nouvelles clés » et déclencher fetch par hash en conséquence.
|
||||||
|
- **Optimisation** : utiliser éventuellement Bloom ou autre pour réduire les GET quand beaucoup de hashes.
|
||||||
|
|
||||||
|
## Références
|
||||||
|
|
||||||
|
- `features/userwallet-collecte-distante-2-devices.md`
|
||||||
|
- `features/userwallet-dh-systematique-scan-fetch.md`
|
||||||
|
- `userwallet/src/utils/collectSignatures.ts` (`runCollectLoop`, `CollectLoopOpts.onProgress`)
|
||||||
|
- `userwallet/src/utils/loginValidation.ts` (`collectProgress`)
|
||||||
@ -9,14 +9,38 @@ function buildLoginSignUrl(hash: string, nonce: string): string {
|
|||||||
return `${window.location.origin}/login-sign?${params.toString()}`;
|
return `${window.location.origin}/login-sign?${params.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ProgressLine(p: { satisfied: number; required: number }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<p role="status" aria-live="polite">
|
||||||
|
Signatures collectées : {p.satisfied} / {p.required}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MaybeProgress(p: {
|
||||||
|
collectProgress: { satisfied: number; required: number } | null | undefined;
|
||||||
|
}): JSX.Element | null {
|
||||||
|
const c = p.collectProgress;
|
||||||
|
if (c === undefined || c === null || c.required <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <ProgressLine satisfied={c.satisfied} required={c.required} />;
|
||||||
|
}
|
||||||
|
|
||||||
interface LoginCollectShareProps {
|
interface LoginCollectShareProps {
|
||||||
proof: LoginProof;
|
proof: LoginProof;
|
||||||
|
/** Progress from relay fetch (notifications). X/Y signatures. */
|
||||||
|
collectProgress?: { satisfied: number; required: number } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device 1: show link + QR for "Demander signature sur l'autre appareil" during collect.
|
* Device 1: show link + QR for "Demander signature sur l'autre appareil" during collect.
|
||||||
|
* Displays progress (X/Y) when collectProgress provided.
|
||||||
*/
|
*/
|
||||||
export function LoginCollectShare({ proof }: LoginCollectShareProps): JSX.Element {
|
export function LoginCollectShare({
|
||||||
|
proof,
|
||||||
|
collectProgress,
|
||||||
|
}: LoginCollectShareProps): JSX.Element {
|
||||||
const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
|
const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
|
||||||
const url = buildLoginSignUrl(proof.challenge.hash, proof.challenge.nonce);
|
const url = buildLoginSignUrl(proof.challenge.hash, proof.challenge.nonce);
|
||||||
|
|
||||||
@ -31,6 +55,7 @@ export function LoginCollectShare({ proof }: LoginCollectShareProps): JSX.Elemen
|
|||||||
return (
|
return (
|
||||||
<section aria-label={`Demander signature sur l${"'"}autre appareil`}>
|
<section aria-label={`Demander signature sur l${"'"}autre appareil`}>
|
||||||
<h3>{`Demander signature sur l${"'"}autre appareil`}</h3>
|
<h3>{`Demander signature sur l${"'"}autre appareil`}</h3>
|
||||||
|
<MaybeProgress collectProgress={collectProgress} />
|
||||||
<p>Ouvrez ce lien ou scannez le QR sur le 2ᵉ appareil.</p>
|
<p>Ouvrez ce lien ou scannez le QR sur le 2ᵉ appareil.</p>
|
||||||
<p>
|
<p>
|
||||||
<a href={url} target="_blank" rel="noopener noreferrer">
|
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { isPairingSatisfied, getPairsForMember } from '../utils/pairing';
|
|||||||
import {
|
import {
|
||||||
buildAllowedPubkeys,
|
buildAllowedPubkeys,
|
||||||
checkDependenciesSatisfied,
|
checkDependenciesSatisfied,
|
||||||
|
collectProgress,
|
||||||
hasRemoteSignatures,
|
hasRemoteSignatures,
|
||||||
} from '../utils/loginValidation';
|
} from '../utils/loginValidation';
|
||||||
import { publishMessageAndSigs } from '../utils/loginPublish';
|
import { publishMessageAndSigs } from '../utils/loginPublish';
|
||||||
@ -54,6 +55,10 @@ export function LoginScreen(): JSX.Element {
|
|||||||
successCount: number;
|
successCount: number;
|
||||||
relaysCount: number;
|
relaysCount: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const [collectProgressState, setCollectProgressState] = useState<{
|
||||||
|
satisfied: number;
|
||||||
|
required: number;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
const graphResolver = new GraphResolver();
|
const graphResolver = new GraphResolver();
|
||||||
|
|
||||||
@ -221,6 +226,7 @@ export function LoginScreen(): JSX.Element {
|
|||||||
let merged = proof.signatures;
|
let merged = proof.signatures;
|
||||||
if (loginPath !== null) {
|
if (loginPath !== null) {
|
||||||
setIsCollecting(true);
|
setIsCollecting(true);
|
||||||
|
setCollectProgressState(null);
|
||||||
try {
|
try {
|
||||||
const pairToMembers = buildPairToMembers(loginPath.pairs_attendus);
|
const pairToMembers = buildPairToMembers(loginPath.pairs_attendus);
|
||||||
const pubkeyToPair = buildPubkeyToPair(
|
const pubkeyToPair = buildPubkeyToPair(
|
||||||
@ -235,10 +241,21 @@ export function LoginScreen(): JSX.Element {
|
|||||||
loginPath,
|
loginPath,
|
||||||
pairToMembers,
|
pairToMembers,
|
||||||
pubkeyToPair,
|
pubkeyToPair,
|
||||||
{ pollMs: COLLECT_POLL_MS, timeoutMs: COLLECT_TIMEOUT_MS },
|
{
|
||||||
|
pollMs: COLLECT_POLL_MS,
|
||||||
|
timeoutMs: COLLECT_TIMEOUT_MS,
|
||||||
|
onProgress: (m) => {
|
||||||
|
const p = collectProgress(loginPath, m, pairToMembers);
|
||||||
|
setCollectProgressState({
|
||||||
|
satisfied: p.satisfied,
|
||||||
|
required: p.required,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setIsCollecting(false);
|
setIsCollecting(false);
|
||||||
|
setCollectProgressState(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,7 +594,10 @@ export function LoginScreen(): JSX.Element {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{isCollecting && proof !== null && (
|
{isCollecting && proof !== null && (
|
||||||
<LoginCollectShare proof={proof} />
|
<LoginCollectShare
|
||||||
|
proof={proof}
|
||||||
|
collectProgress={collectProgressState}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{awaitingRemoteAccept &&
|
{awaitingRemoteAccept &&
|
||||||
collectedMerged !== null &&
|
collectedMerged !== null &&
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getSignatures } from './relay';
|
import { getSignatures } from './relay';
|
||||||
import { getStoredPairs } from './pairing';
|
import { getStoredPairs } from './pairing';
|
||||||
import { hasEnoughSignatures, collectProgress } from './loginValidation';
|
import { hasEnoughSignatures } from './loginValidation';
|
||||||
import type { LoginPath } from '../types/identity';
|
import type { LoginPath } from '../types/identity';
|
||||||
import type { MsgSignature } from '../types/message';
|
import type { MsgSignature } from '../types/message';
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user