docs: translate all code comments to English (language policy)

Translate every Italian /// XML doc comment, <!-- --> XAML comment, and
// inline comment to English across all source files (Core, App, tests).
Add the language policy to CLAUDE.md (conversation Italian; all code
and docs English). Update one test assertion that checked an Italian
exception message that was also translated.
This commit is contained in:
2026-06-16 14:40:06 +02:00
parent 4b82a0852c
commit 3d5a226a5a
59 changed files with 721 additions and 716 deletions
+7 -7
View File
@@ -5,10 +5,10 @@ using System.Text.Json;
namespace PalladiumWallet.Core.Net;
/// <summary>
/// Pinning TLS "trust on first use" (blueprint §9): al primo contatto la
/// fingerprint SHA-256 del certificato del server viene salvata; ai successivi
/// viene confrontata. Se cambia, la connessione va rifiutata e l'utente può
/// sbloccare con un reset esplicito (caso tipico: server self-signed rinnovato).
/// TLS "trust on first use" pinning (blueprint §9): on the first contact the
/// server certificate's SHA-256 fingerprint is saved; on subsequent ones it is
/// compared. If it changes, the connection must be rejected and the user can
/// unlock with an explicit reset (typical case: renewed self-signed server).
/// </summary>
public sealed class CertificatePinStore(string filePath)
{
@@ -18,8 +18,8 @@ public sealed class CertificatePinStore(string filePath)
Convert.ToHexString(SHA256.HashData(certificate.GetRawCertData())).ToLowerInvariant();
/// <summary>
/// Verifica TOFU: true se il certificato è quello già visto (o se è il primo
/// contatto, nel qual caso viene salvato). False = certificato cambiato.
/// TOFU check: true if the certificate is the one already seen (or if it is the
/// first contact, in which case it is saved). False = certificate changed.
/// </summary>
public bool VerifyOrPin(string host, int port, X509Certificate certificate)
{
@@ -36,7 +36,7 @@ public sealed class CertificatePinStore(string filePath)
}
}
/// <summary>Reset del certificato salvato per un server ("reset certificati SSL", §9).</summary>
/// <summary>Reset the saved certificate for a server ("reset SSL certificates", §9).</summary>
public bool Reset(string host, int port)
{
lock (_lock)
+13 -13
View File
@@ -2,24 +2,24 @@ using System.Text.Json;
namespace PalladiumWallet.Core.Net;
/// <summary>Voce di storico di uno scripthash (blockchain.scripthash.get_history).</summary>
/// <summary>History entry for a scripthash (blockchain.scripthash.get_history).</summary>
public readonly record struct HistoryItem(string TxHash, int Height, long FeeSats);
/// <summary>UTXO riportato dal server (blockchain.scripthash.listunspent).</summary>
/// <summary>UTXO reported by the server (blockchain.scripthash.listunspent).</summary>
public readonly record struct UnspentItem(string TxHash, int TxPos, long ValueSats, int Height);
/// <summary>Prova di Merkle (blockchain.transaction.get_merkle).</summary>
/// <summary>Merkle proof (blockchain.transaction.get_merkle).</summary>
public sealed record MerkleProofResponse(int BlockHeight, int Pos, IReadOnlyList<string> Merkle);
/// <summary>Tip della catena notificato da blockchain.headers.subscribe.</summary>
/// <summary>Chain tip notified by blockchain.headers.subscribe.</summary>
public readonly record struct ChainTip(int Height, string HeaderHex);
/// <summary>Peer annunciato da server.peers.subscribe (porte null = non offerte).</summary>
/// <summary>Peer announced by server.peers.subscribe (null ports = not offered).</summary>
public sealed record PeerInfo(string Host, int? TcpPort, int? SslPort, string? Version);
/// <summary>
/// Helper tipizzati sui metodi del protocollo (blueprint §10), sopra il
/// trasporto JSON-RPC generico di <see cref="ElectrumClient"/>.
/// Typed helpers over the protocol methods (blueprint §10), on top of the
/// generic JSON-RPC transport of <see cref="ElectrumClient"/>.
/// </summary>
public static class ElectrumApi
{
@@ -29,7 +29,7 @@ public static class ElectrumApi
return new ChainTip(r.GetProperty("height").GetInt32(), r.GetProperty("hex").GetString()!);
}
/// <summary>Status corrente dello scripthash (null = mai usato).</summary>
/// <summary>Current status of the scripthash (null = never used).</summary>
public static async Task<string?> SubscribeScripthashAsync(this ElectrumClient c, string scripthash,
CancellationToken ct = default)
{
@@ -89,7 +89,7 @@ public static class ElectrumApi
return r.GetString()!;
}
/// <summary>Fee stimata in coin/kB per il target dato; -1 se il server non sa stimare.</summary>
/// <summary>Estimated fee in coin/kB for the given target; -1 if the server cannot estimate.</summary>
public static async Task<decimal> EstimateFeeAsync(this ElectrumClient c, int targetBlocks,
CancellationToken ct = default)
{
@@ -112,7 +112,7 @@ public static class ElectrumApi
public static Task PingAsync(this ElectrumClient c, CancellationToken ct = default) =>
c.RequestAsync("server.ping", ct);
/// <summary>Peer annunciati dal server (scoperta di altri server, §9).</summary>
/// <summary>Peers announced by the server (discovery of other servers, §9).</summary>
public static async Task<IReadOnlyList<PeerInfo>> GetPeersAsync(this ElectrumClient c,
CancellationToken ct = default)
{
@@ -121,9 +121,9 @@ public static class ElectrumApi
}
/// <summary>
/// Parsa la risposta di server.peers.subscribe: lista di
/// [ip, hostname, ["v1.4.2", "pN", "tPORTA", "sPORTA", ...]];
/// "t"/"s" senza numero = porta di default della rete (risolta dal chiamante).
/// Parses the server.peers.subscribe response: a list of
/// [ip, hostname, ["v1.4.2", "pN", "tPORT", "sPORT", ...]];
/// "t"/"s" without a number = network default port (resolved by the caller).
/// </summary>
public static IReadOnlyList<PeerInfo> ParsePeers(JsonElement response)
{
+27 -27
View File
@@ -9,9 +9,9 @@ using System.Threading.Channels;
namespace PalladiumWallet.Core.Net;
/// <summary>
/// Client del protocollo del server di indicizzazione (blueprint §10):
/// JSON-RPC 2.0 newline-delimited su TCP, opzionalmente TLS con pinning TOFU.
/// Le notifiche (subscription) arrivano sull'evento <see cref="NotificationReceived"/>.
/// Client for the indexing server protocol (blueprint §10):
/// newline-delimited JSON-RPC 2.0 over TCP, optionally TLS with TOFU pinning.
/// Notifications (subscriptions) arrive on the <see cref="NotificationReceived"/> event.
/// </summary>
public sealed class ElectrumClient : IAsyncDisposable
{
@@ -25,17 +25,17 @@ public sealed class ElectrumClient : IAsyncDisposable
private readonly Task _readLoop;
private readonly Task _writeLoop;
// Channel single-reader: le task scrivono i payload senza lock;
// il write loop drena tutto in un unico WriteAsync+FlushAsync —
// identico al buffered writer asyncio di Electrum.
// Single-reader channel: tasks write the payloads without a lock;
// the write loop drains everything in a single WriteAsync+FlushAsync —
// identical to Electrum's asyncio buffered writer.
private readonly Channel<byte[]> _outgoing = Channel.CreateUnbounded<byte[]>(
new UnboundedChannelOptions { SingleReader = true, AllowSynchronousContinuations = false });
// Tetto alle richieste in volo (non in coda di scrittura, ma in attesa di
// risposta sul server). Il write-loop batcha già l'invio in un solo segmento;
// questo gate evita di sommergere il server con migliaia di richieste
// simultanee su wallet grandi → niente -101/-102 a raffica né drop della
// connessione. Le scritture restano comunque pipelinate fino a questo grado.
// Cap on in-flight requests (not in the write queue, but awaiting a
// response from the server). The write loop already batches the send into a
// single segment; this gate avoids flooding the server with thousands of
// simultaneous requests on large wallets → no bursts of -101/-102 nor
// connection drops. Writes still stay pipelined up to this degree.
private const int MaxInFlight = 32;
private readonly SemaphoreSlim _inFlight = new(MaxInFlight, MaxInFlight);
@@ -104,8 +104,8 @@ public sealed class ElectrumClient : IAsyncDisposable
public async Task<JsonElement> RequestAsync(string method, CancellationToken ct = default,
params object?[] parameters)
{
// Gate prima di mettere in volo: oltre MaxInFlight richieste in attesa
// si attende che una risposta liberi uno slot, invece di sommergere il server.
// Gate before going in-flight: beyond MaxInFlight pending requests
// we wait for a response to free a slot, instead of flooding the server.
await _inFlight.WaitAsync(ct);
try
{
@@ -137,9 +137,9 @@ public sealed class ElectrumClient : IAsyncDisposable
}
/// <summary>
/// Drain loop: svuota il channel in un unico buffer → un solo WriteAsync+FlushAsync
/// per tutti i messaggi in coda. Quando N richieste sono in coda, vengono
/// trasmesse in un singolo segmento TCP invece di N flush seriali.
/// Drain loop: empties the channel into a single buffer → one WriteAsync+FlushAsync
/// for all queued messages. When N requests are queued, they are
/// transmitted in a single TCP segment instead of N serial flushes.
/// </summary>
private async Task WriteLoopAsync()
{
@@ -166,8 +166,8 @@ public sealed class ElectrumClient : IAsyncDisposable
}
/// <summary>
/// Read loop con PipeReader: buffer pooled, zero allocazioni per-risposta
/// (nessuna stringa intermedia), parsing JSON direttamente da byte span.
/// Read loop with PipeReader: pooled buffers, zero per-response allocations
/// (no intermediate string), JSON parsing directly from a byte span.
/// </summary>
private async Task ReadLoopAsync()
{
@@ -190,8 +190,8 @@ public sealed class ElectrumClient : IAsyncDisposable
}
finally
{
// consumed = tutto ciò che abbiamo consumato (fino all'ultimo \n)
// examined = tutto ciò che abbiamo guardato (fino alla fine del buffer)
// consumed = everything we have consumed (up to the last \n)
// examined = everything we have looked at (up to the end of the buffer)
pipe.AdvanceTo(buffer.Start, buffer.End);
}
@@ -206,7 +206,7 @@ public sealed class ElectrumClient : IAsyncDisposable
{
await pipe.CompleteAsync();
foreach (var (_, tcs) in _pending)
tcs.TrySetException(failure ?? new IOException("Connessione al server chiusa."));
tcs.TrySetException(failure ?? new IOException("Connection to the server closed."));
_pending.Clear();
Disconnected?.Invoke(failure);
}
@@ -229,7 +229,7 @@ public sealed class ElectrumClient : IAsyncDisposable
DispatchSpan(line.FirstSpan);
return;
}
// Multi-segmento (risposta molto lunga): copia su ArrayPool poi parsa.
// Multi-segment (very long response): copy to ArrayPool then parse.
var len = (int)line.Length;
var buf = ArrayPool<byte>.Shared.Rent(len);
try
@@ -242,8 +242,8 @@ public sealed class ElectrumClient : IAsyncDisposable
private void DispatchSpan(ReadOnlySpan<byte> utf8)
{
// JsonDocument.Parse via Utf8JsonReader: nessuna stringa intermedia,
// parsing direttamente dallo span pooled.
// JsonDocument.Parse via Utf8JsonReader: no intermediate string,
// parsing directly from the pooled span.
var reader = new Utf8JsonReader(utf8);
using var doc = JsonDocument.ParseValue(ref reader);
var root = doc.RootElement;
@@ -276,12 +276,12 @@ public sealed class ElectrumClient : IAsyncDisposable
}
}
/// <summary>Errore restituito dal server (campo "error" della risposta JSON-RPC).</summary>
/// <summary>Error returned by the server ("error" field of the JSON-RPC response).</summary>
public sealed class ElectrumServerException(string error) : Exception(error);
/// <summary>
/// Il certificato del server è cambiato rispetto a quello salvato (TOFU, §9).
/// Si sblocca con il reset esplicito dei certificati.
/// The server certificate changed with respect to the saved one (TOFU, §9).
/// It is unlocked with an explicit reset of the certificates.
/// </summary>
public sealed class CertificatePinMismatchException(string host, int port) : Exception(
$"Il certificato TLS di {host}:{port} è cambiato rispetto a quello salvato. " +
+9 -9
View File
@@ -3,7 +3,7 @@ using PalladiumWallet.Core.Chain;
namespace PalladiumWallet.Core.Net;
/// <summary>Server di indicizzazione noto (bootstrap o scoperto dai peer).</summary>
/// <summary>Known indexing server (bootstrap or discovered from peers).</summary>
public sealed record KnownServer(string Host, int TcpPort, int SslPort, string? Version = null)
{
public int PortFor(bool useSsl) => useSsl ? SslPort : TcpPort;
@@ -13,9 +13,9 @@ public sealed record KnownServer(string Host, int TcpPort, int SslPort, string?
}
/// <summary>
/// Registro dei server (blueprint §9): parte dai bootstrap del profilo (§3),
/// si arricchisce con i peer annunciati via server.peers.subscribe e persiste
/// le scoperte su file per le sessioni successive.
/// Server registry (blueprint §9): starts from the profile bootstrap list (§3),
/// is enriched with peers announced via server.peers.subscribe, and persists
/// discovered servers to file for subsequent sessions.
/// </summary>
public sealed class ServerRegistry
{
@@ -42,7 +42,7 @@ public sealed class ServerRegistry
}
catch (JsonException)
{
// File corrotto: si riparte dai soli bootstrap.
// Corrupted file: fall back to bootstrap servers only.
}
}
}
@@ -52,15 +52,15 @@ public sealed class ServerRegistry
get { lock (_lock) return [.. _servers]; }
}
/// <summary>Primo server noto: default quando l'utente non ne indica uno.</summary>
/// <summary>First known server: default when the user does not specify one.</summary>
public KnownServer? Default
{
get { lock (_lock) return _servers.FirstOrDefault(); }
}
/// <summary>
/// Chiede al server connesso i peer che annuncia e integra i nuovi nel
/// registro (porte mancanti → default del profilo). Ritorna quanti sono nuovi.
/// Queries the connected server for its announced peers and adds new ones to the
/// registry (missing ports fall back to the profile defaults). Returns the count of new entries.
/// </summary>
public async Task<int> DiscoverAsync(ElectrumClient client, CancellationToken ct = default)
{
@@ -95,7 +95,7 @@ public sealed class ServerRegistry
private void Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_filePath)!);
// Si salvano solo gli scoperti: i bootstrap vengono dal profilo.
// Only discovered servers are saved; bootstrap servers come from the profile.
var discovered = _servers
.Where(s => !_profile.BootstrapServers.Any(b =>
string.Equals(b.Host, s.Host, StringComparison.OrdinalIgnoreCase)))