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
@@ -21,7 +21,7 @@ public class ChainProfileTests
Assert.Equal(50002, p.DefaultSslPort);
Assert.Equal(746, p.Bip44CoinType);
Assert.Equal("palladium", p.UriScheme);
Assert.True(p.SkipPowValidation); // obbligatorio: la catena usa LWMA (§3)
Assert.True(p.SkipPowValidation); // mandatory: the chain uses LWMA (§3)
Assert.Equal(120, p.BlockTimeSeconds);
}
@@ -105,8 +105,8 @@ public class ChainProfileTests
Assert.Equal(1, ChainProfiles.Testnet.Bip44CoinType);
}
// Serializza header (4 byte BE) + payload BIP32 di 74 byte e codifica Base58Check:
// il prefisso testuale risultante dipende solo dall'header.
// Serialize header (4-byte BE) + 74-byte BIP32 payload and encode Base58Check:
// the resulting textual prefix depends only on the header.
private static string EncodeWithHeader(uint header)
{
var data = new byte[78];
@@ -5,7 +5,7 @@ namespace PalladiumWallet.Tests.Chain;
public class PalladiumNetworksTests
{
// Chiave privata fissa per test deterministici (solo test, mai usarla davvero).
// Fixed private key for deterministic tests (test-only, never use it for real).
private static Key TestKey => new(Convert.FromHexString(
"0000000000000000000000000000000000000000000000000000000000000001"));
@@ -17,7 +17,7 @@ public class AddressDerivationTests
[Fact]
public void Il_vettore_bip44_produce_lo_stesso_hash_su_bitcoin_e_plm()
{
// Indirizzo noto di abandon-about su m/44'/0'/0'/0/0 (riferimento pubblico).
// Known abandon-about address at m/44'/0'/0'/0/0 (public reference).
var account = HdAccount.FromSeed(AbandonAboutSeed(), ScriptKind.Legacy,
ChainProfiles.Mainnet, new KeyPath("44'/0'/0'"));
var pubKey = account.GetPublicKey(isChange: false, 0);
@@ -27,8 +27,8 @@ public class AddressDerivationTests
var plmAddr = account.GetReceiveAddress(0);
Assert.StartsWith("P", plmAddr.ToString());
// Stesso hash160 sotto i due prefissi: la derivazione è identica,
// cambia solo la veste di rete.
// Same hash160 under the two prefixes: the derivation is identical,
// only the network dress changes.
Assert.Equal(pubKey.Hash, ((BitcoinPubKeyAddress)plmAddr).Hash);
}
@@ -5,9 +5,9 @@ using PalladiumWallet.Core.Crypto;
namespace PalladiumWallet.Tests.Crypto;
/// <summary>
/// Vettore di test 1 della specifica BIP32 (seed 000102...0e0f). Gli header
/// Legacy della mainnet PLM coincidono con quelli Bitcoin, quindi il confronto
/// con le stringhe della specifica è diretto sulla rete PLM.
/// BIP32 specification test vector 1 (seed 000102...0e0f). The Legacy headers
/// of the PLM mainnet coincide with the Bitcoin ones, so the comparison
/// with the specification strings is direct on the PLM network.
/// </summary>
public class Bip32Tests
{
@@ -40,7 +40,7 @@ public class Bip32Tests
[Fact]
public void I_path_hardened_non_sono_derivabili_da_una_xpub()
{
// Garanzia §17: da sole chiavi pubbliche niente derivazione hardened.
// §17 guarantee: from public keys alone, no hardened derivation.
Assert.ThrowsAny<InvalidOperationException>(() =>
Root.Neuter().Derive(new KeyPath("0'")));
}
@@ -4,7 +4,7 @@ namespace PalladiumWallet.Tests.Crypto;
public class Bip39Tests
{
// Vettori ufficiali Trezor (python-mnemonic/vectors.json), passphrase "TREZOR".
// Official Trezor vectors (python-mnemonic/vectors.json), passphrase "TREZOR".
[Theory]
[InlineData(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
@@ -30,8 +30,8 @@ public class Bip39Tests
[Fact]
public void Il_vettore_giapponese_copre_la_normalizzazione_nfkd()
{
// Vettore ufficiale bip32JP (test_JP_BIP39.json[0]): mnemonica con spazi
// ideografici U+3000 e passphrase con caratteri da normalizzare NFKD.
// Official bip32JP vector (test_JP_BIP39.json[0]): mnemonic with U+3000
// ideographic spaces and a passphrase with characters to normalize NFKD.
const string words =
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら";
const string passphrase = "㍍ガバヴァぱばぐゞちぢ十人十色";
@@ -55,8 +55,8 @@ public class Bip39Tests
[Fact]
public void Checksum_invalido_viene_rifiutato()
{
// 12 × "abandon": parole valide ma checksum errato — il caso che il
// costruttore NBitcoin non controlla da solo.
// 12 × "abandon": valid words but wrong checksum — the case that the
// NBitcoin constructor does not check on its own.
var words = string.Join(' ', Enumerable.Repeat("abandon", 12));
Assert.False(Bip39.TryParse(words, out _));
}
@@ -5,12 +5,12 @@ using PalladiumWallet.Core.Crypto;
namespace PalladiumWallet.Tests.Crypto;
/// <summary>
/// Vettore di test della specifica BIP84 (mnemonica abandon-about, senza
/// passphrase). Gli header SLIP-132 della mainnet PLM coincidono con quelli
/// Bitcoin, quindi zprv/zpub si confrontano direttamente; gli indirizzi si
/// confrontano sul witness program (chain-independent) + prefisso PLM.
/// Il path m/84'/0'/0' è volutamente "personalizzato" (coin type 0, non 746):
/// esercita anche l'import con path custom (§4.2).
/// BIP84 specification test vector (abandon-about mnemonic, without
/// passphrase). The SLIP-132 headers of the PLM mainnet coincide with the
/// Bitcoin ones, so zprv/zpub are compared directly; addresses are
/// compared on the witness program (chain-independent) + PLM prefix.
/// The path m/84'/0'/0' is deliberately "customized" (coin type 0, not 746):
/// it also exercises the import with a custom path (§4.2).
/// </summary>
public class Bip84Slip132Tests
{
@@ -64,12 +64,12 @@ public class Bip84Slip132Tests
[Fact]
public void Una_xkey_con_header_sconosciuto_viene_rifiutata()
{
// xpub Bitcoin valida ma con header Legacy: non è una zpub.
// Valid Bitcoin xpub but with Legacy header: it is not a zpub.
var account = Account();
var asXpub = account.AccountXpub.ToString(Network.Main);
Assert.True(Slip132.TryDecodePublic(asXpub, ChainProfiles.Mainnet, out _, out var kind));
Assert.Equal(ScriptKind.Legacy, kind); // header xpub → riconosciuto come Legacy
Assert.Equal(ScriptKind.Legacy, kind); // xpub header → recognized as Legacy
Assert.False(Slip132.TryDecodePublic("non-base58!!!", ChainProfiles.Mainnet, out _, out _));
Assert.False(Slip132.TryDecodePrivate(asXpub, ChainProfiles.Mainnet, out _, out _)); // pub ≠ priv
}
@@ -88,8 +88,8 @@ public class Bip84Slip132Tests
if (expectedPubKeyHex is not null)
Assert.Equal(expectedPubKeyHex, pubKey.ToHex());
// Il witness program (hash160 della pubkey) è chain-independent: deve
// coincidere con quello dell'indirizzo bc1 del vettore ufficiale.
// The witness program (hash160 of the pubkey) is chain-independent: it must
// coincide with that of the bc1 address from the official vector.
var bitcoinExpected = (BitcoinWitPubKeyAddress)BitcoinAddress.Create(bitcoinAddress, Network.Main);
Assert.Equal(bitcoinExpected.Hash, pubKey.WitHash);
@@ -5,11 +5,11 @@ using PalladiumWallet.Core.Crypto;
namespace PalladiumWallet.Tests.Crypto;
/// <summary>
/// Vettori di test BIP86 (mnemonica abandon-about, senza passphrase).
/// La chiave pubblica tweakizzata (output key, 32 byte x-only) è chain-independent:
/// viene verificata contro i vettori ufficiali Bitcoin, poi si controlla che
/// l'indirizzo PLM abbia il prefisso plm1p (witness v1, bech32m).
/// Il path m/86'/0'/0' usa coin_type=0 (non 746) per aderire ai vettori BIP86.
/// BIP86 test vectors (abandon-about mnemonic, no passphrase).
/// The tweaked public key (output key, 32-byte x-only) is chain-independent:
/// verified against the official Bitcoin vectors, then checked that the PLM
/// address starts with plm1p (witness v1, bech32m).
/// The path m/86'/0'/0' uses coin_type=0 (not 746) to match the BIP86 vectors.
/// </summary>
public class Bip86TaprootTests
{
@@ -46,9 +46,9 @@ public class Bip86TaprootTests
}
/// <summary>
/// L'output key (chiave tweakizzata x-only, 32 byte) è identica al vettore BIP86
/// indipendentemente dalla rete: la rete cambia solo HRP e checksum, non il programma.
/// Indirizzi Bitcoin da https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
/// The output key (tweaked x-only pubkey, 32 bytes) matches the BIP86 vector
/// regardless of network: the network changes only HRP and checksum, not the program.
/// Bitcoin addresses from https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
/// </summary>
[Theory]
[InlineData(false, 0, "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr")]
@@ -60,7 +60,7 @@ public class Bip86TaprootTests
var account = Account();
var plmAddr = account.GetAddress(isChange, index);
// Il witness program (output key tweakizzata) è chain-independent
// The witness program (tweaked output key) is chain-independent.
var btcAddr = (TaprootAddress)BitcoinAddress.Create(bitcoinAddress, Network.Main);
var plmTaproot = (TaprootAddress)plmAddr;
Assert.Equal(btcAddr.PubKey, plmTaproot.PubKey);
@@ -9,7 +9,7 @@ public class PeerParsingTests
[Fact]
public void La_risposta_peers_subscribe_si_parsa_nel_formato_electrumx()
{
// Formato reale: [ip, hostname, ["v...", "pN", "tPORTA", "sPORTA"]].
// Real format: [ip, hostname, ["v...", "pN", "tPORT", "sPORT"]].
const string json = """
[
["173.212.224.67", "173.212.224.67", ["v1.4.2", "p10000", "t50001", "s50002"]],
@@ -20,10 +20,10 @@ public class PeerParsingTests
""";
var peers = ElectrumApi.ParsePeers(JsonDocument.Parse(json).RootElement);
Assert.Equal(3, peers.Count); // l'ultimo non offre porte → scartato
Assert.Equal(3, peers.Count); // last entry has no portsdiscarded
Assert.Equal(new PeerInfo("173.212.224.67", 50001, 50002, "1.4.2"), peers[0]);
// "t" senza numero = porta di default (0 segnala "da risolvere col profilo").
// "t" without a number = default port (0 signals "resolve from profile").
Assert.Equal(new PeerInfo("nodo.esempio.org", 0, null, "1.4"), peers[1]);
Assert.Equal(new PeerInfo("solo-ssl.esempio.org", null, 50002, "1.4"), peers[2]);
}
@@ -55,8 +55,8 @@ public class ServerRegistryTests
var registry = new ServerRegistry(ChainProfiles.Mainnet, path);
var bootstrapCount = registry.All.Count;
// Merge diretto via DiscoverAsync richiede un client: si testa la
// persistenza simulando il file degli scoperti.
// Direct merge via DiscoverAsync requires a client: test persistence
// by simulating the discovered servers file.
var discovered = new[] { new KnownServer("nuovo.esempio.org", 50001, 50002, "1.4.2") };
File.WriteAllText(path, JsonSerializer.Serialize(discovered));
@@ -64,7 +64,7 @@ public class ServerRegistryTests
Assert.Equal(bootstrapCount + 1, reloaded.All.Count);
Assert.Contains(reloaded.All, s => s.Host == "nuovo.esempio.org");
// Un bootstrap duplicato nel file non raddoppia.
// A bootstrap duplicate in the file must not double-count.
File.WriteAllText(path, JsonSerializer.Serialize(new[]
{
new KnownServer("173.212.224.67", 50001, 50002),
+30 -30
View File
@@ -9,18 +9,18 @@ using PalladiumWallet.Core.Wallet;
namespace PalladiumWallet.Tests;
/// <summary>
/// Property-based tests (CsCheck). Ogni test genera centinaia di input casuali e
/// verifica che le proprietà invarianti reggano — crash, eccezioni non attese, o
/// violazioni di roundtrip sono failures.
/// Property-based tests (CsCheck). Each test generates hundreds of random inputs and
/// verifies that invariant properties hold — crashes, unexpected exceptions, or
/// roundtrip violations are failures.
/// </summary>
public class PropertyTests
{
// ── generatori riutilizzabili ─────────────────────────────────────────────
// ── reusable generators ───────────────────────────────────────────────────
private static readonly Gen<string> GenUnit = Gen.OneOf(
Gen.Const("PLM"), Gen.Const("mPLM"), Gen.Const("µPLM"), Gen.Const("sat"));
// uint256 casuale costruito da 4 ulong
// random uint256 built from 4 ulongs
private static readonly Gen<uint256> GenTxid =
Gen.Select(Gen.ULong, Gen.ULong, Gen.ULong, Gen.ULong, (a, b, c, d) =>
{
@@ -32,9 +32,9 @@ public class PropertyTests
return new uint256(bytes);
});
// ── CoinAmount ──────────────────────────────────────────────────────────
// ── CoinAmount ──────────────────────────────────────────────────────────
/// TryParseIn non deve mai lanciare eccezioni su input arbitrario con unità valide.
/// TryParseIn must never throw on arbitrary input with valid units.
[Fact]
public void CoinAmount_TryParseIn_non_lancia_mai_su_input_arbitrario()
{
@@ -46,17 +46,17 @@ public class PropertyTests
}
catch (ArgumentException)
{
// unità sconosciuta: impossibile qui perché usiamo solo unità note
// unknown unit: impossible here because we only use known units
throw;
}
catch (Exception ex)
{
Assert.Fail($"TryParseIn ha lanciato {ex.GetType().Name} per unit={unit}");
Assert.Fail($"TryParseIn threw {ex.GetType().Name} for unit={unit}");
}
});
}
/// FormatIn → TryParseIn: qualsiasi satoshi [0, MaxSupply] deve fare roundtrip esatto.
/// FormatIn → TryParseIn: any satoshi value in [0, MaxSupply] must roundtrip exactly.
[Fact]
public void CoinAmount_roundtrip_FormatIn_TryParseIn_per_ogni_unita()
{
@@ -66,12 +66,12 @@ public class PropertyTests
var formatted = CoinAmount.FormatIn(sats, unit, withLabel: false);
Assert.True(
CoinAmount.TryParseIn(formatted, unit, out var parsed),
$"FormatIn={formatted} unit={unit} non si riparsa");
$"FormatIn={formatted} unit={unit} failed to re-parse");
Assert.Equal(sats, parsed);
});
}
/// TryParseCoins non deve mai lanciare su input arbitrario.
/// TryParseCoins must never throw on arbitrary input.
[Fact]
public void CoinAmount_TryParseCoins_non_lancia_mai_su_input_arbitrario()
{
@@ -83,25 +83,25 @@ public class PropertyTests
}
catch (Exception ex)
{
Assert.Fail($"TryParseCoins ha lanciato {ex.GetType().Name}");
Assert.Fail($"TryParseCoins threw {ex.GetType().Name}");
}
});
}
/// Qualsiasi valore accettato da TryParseCoins deve essere ≥ 0.
/// Any value accepted by TryParseCoins must be ≥ 0.
[Fact]
public void CoinAmount_TryParseCoins_accetta_solo_valori_non_negativi()
{
Gen.String.Sample(text =>
{
if (CoinAmount.TryParseCoins(text, out var sats))
Assert.True(sats >= 0, $"TryParseCoins ha restituito {sats} per '{text}'");
Assert.True(sats >= 0, $"TryParseCoins returned {sats} for '{text}'");
});
}
// ── EncryptedFile ────────────────────────────────────────────────────────
// ── EncryptedFile ────────────────────────────────────────────────────────
/// Encrypt → Decrypt con la stessa password deve restituire il testo originale.
/// Encrypt → Decrypt with the same password must return the original plaintext.
[Fact]
public void EncryptedFile_roundtrip_su_contenuto_e_password_arbitrari()
{
@@ -113,29 +113,29 @@ public class PropertyTests
});
}
/// Decrypt con password sbagliata deve lanciare WrongPasswordException, mai altro.
/// Decrypt with the wrong password must throw WrongPasswordException, never anything else.
[Fact]
public void EncryptedFile_password_sbagliata_lancia_solo_WrongPasswordException()
{
Gen.Select(Gen.String, Gen.String[1, 32], Gen.String[1, 32]).Sample((plaintext, pwd1, pwd2) =>
{
if (pwd1 == pwd2) return; // stessa password: roundtrip valido, salta
if (pwd1 == pwd2) return; // same password: valid roundtrip, skip
var cipher = EncryptedFile.Encrypt(plaintext, pwd1);
try
{
EncryptedFile.Decrypt(cipher, pwd2);
Assert.Fail("Decrypt con password sbagliata non ha lanciato");
Assert.Fail("Decrypt with wrong password did not throw");
}
catch (WrongPasswordException) { /* atteso */ }
catch (WrongPasswordException) { /* expected */ }
catch (Exception ex)
{
Assert.Fail($"Decrypt ha lanciato {ex.GetType().Name} invece di WrongPasswordException");
Assert.Fail($"Decrypt threw {ex.GetType().Name} instead of WrongPasswordException");
}
});
}
/// IsEncrypted non deve mai lanciare su input arbitrario.
/// IsEncrypted must never throw on arbitrary input.
[Fact]
public void EncryptedFile_IsEncrypted_non_lancia_mai()
{
@@ -144,14 +144,14 @@ public class PropertyTests
try { EncryptedFile.IsEncrypted(s); }
catch (Exception ex)
{
Assert.Fail($"IsEncrypted ha lanciato {ex.GetType().Name}");
Assert.Fail($"IsEncrypted threw {ex.GetType().Name}");
}
});
}
// ── MerkleProof ──────────────────────────────────────────────────────────
// ── MerkleProof ──────────────────────────────────────────────────────────
/// Ogni foglia di un albero Merkle generato casualmente deve verificare contro la radice.
/// Every leaf of a randomly generated Merkle tree must verify against the root.
[Fact]
public void MerkleProof_ogni_foglia_verifica_contro_la_sua_radice()
{
@@ -163,18 +163,18 @@ public class PropertyTests
var branch = BuildBranch(txids, pos);
Assert.True(
MerkleProof.Verify(txids[pos], pos, branch, root),
$"Verify fallita per pos={pos} su {txids.Length} foglie");
$"Verify failed for pos={pos} over {txids.Length} leaves");
}
});
}
/// Un txid non presente nelle foglie non deve verificare (e non deve crashare).
/// A txid not present in the leaves must not verify (and must not crash).
[Fact]
public void MerkleProof_txid_estraneo_non_verifica_e_non_crasha()
{
Gen.Select(GenTxid.Array[2, 8], GenTxid).Sample((txids, extra) =>
{
if (txids.Contains(extra)) return; // collisione casuale: salta
if (txids.Contains(extra)) return; // random collision: skip
var root = MerkleProof.ComputeRootFromLeaves(txids);
var branch = BuildBranch(txids, 0);
@@ -182,7 +182,7 @@ public class PropertyTests
});
}
// helper: costruisce il branch per la posizione data
// helper: builds the branch for the given position
private static List<uint256> BuildBranch(IReadOnlyList<uint256> leaves, int position)
{
var branch = new List<uint256>();
+11 -11
View File
@@ -9,7 +9,7 @@ namespace PalladiumWallet.Tests.Spv;
public class ScripthashTests
{
// Vettori calcolati indipendentemente con Python (hashlib), non con NBitcoin.
// Vectors computed independently with Python (hashlib), not with NBitcoin.
[Theory]
[InlineData("76a9140102030405060708090a0b0c0d0e0f101112131488ac",
"5546fc69d399ef99854c132abb060381cc159dbec67c496a6f0e0dbf12e83ae8")]
@@ -39,7 +39,7 @@ public class ScripthashTests
public class MerkleProofTests
{
// Blocco Bitcoin 100000: 4 transazioni (pari), merkle root nota.
// Bitcoin block 100000: 4 transactions (even), known merkle root.
private static readonly uint256[] Block100000Txids =
[
uint256.Parse("8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87"),
@@ -51,7 +51,7 @@ public class MerkleProofTests
private static readonly uint256 Block100000Root =
uint256.Parse("f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766");
// ---- numero pari di transazioni (4 tx) ----
// ---- even number of transactions (4 tx) ----
[Fact]
public void La_radice_calcolata_dalle_foglie_coincide_con_quella_del_blocco()
@@ -88,13 +88,13 @@ public class MerkleProofTests
Assert.True(MerkleProof.Verify(txid, 0, [], root));
}
// ---- numero dispari di transazioni (duplicazione dell'ultimo) ----
// ---- odd number of transactions (last one duplicated) ----
[Fact]
public void Tre_tx_dispari_la_terza_viene_duplicata()
{
// Con 3 tx: livello 1 = [SHA256d(tx0||tx1), SHA256d(tx2||tx2)]
// Verifica che la radice sia deterministica e corretta.
// With 3 tx: level 1 = [SHA256d(tx0||tx1), SHA256d(tx2||tx2)]
// Verify the root is deterministic and correct.
var txids = Block100000Txids.Take(3).ToArray();
var root = MerkleProof.ComputeRootFromLeaves(txids);
var branch2 = BuildBranch(txids, 2);
@@ -104,7 +104,7 @@ public class MerkleProofTests
[Fact]
public void Cinque_tx_dispari_verifica_tutte_le_posizioni()
{
// 5 tx → livello pari (4) → livello pari (2) → radice
// 5 tx → even level (4) → even level (2) → root
var txids = new uint256[5];
for (var i = 0; i < 5; i++)
txids[i] = new uint256(new byte[32].Select((_, j) => (byte)(i * 17 + j)).ToArray());
@@ -118,7 +118,7 @@ public class MerkleProofTests
}
}
// ---- due transazioni (pari minimo) ----
// ---- two transactions (minimum even) ----
[Fact]
public void Due_tx_verifica_entrambe_le_posizioni()
@@ -132,7 +132,7 @@ public class MerkleProofTests
}
}
// ---- proof errate ----
// ---- invalid proofs ----
[Fact]
public void Una_prova_per_la_posizione_sbagliata_fallisce()
@@ -152,7 +152,7 @@ public class MerkleProofTests
public void Branch_alterato_non_verifica()
{
var branch = BuildBranch(Block100000Txids, 0);
branch[0] = uint256.One; // corrompe il primo elemento del branch
branch[0] = uint256.One; // corrupt the first branch element
Assert.False(MerkleProof.Verify(Block100000Txids[0], 0, branch, Block100000Root));
}
@@ -171,7 +171,7 @@ public class MerkleProofTests
Assert.Throws<ArgumentException>(() => MerkleProof.ComputeRootFromLeaves([]));
}
/// <summary>Costruisce il branch per una foglia ricostruendo i livelli dell'albero.</summary>
/// <summary>Builds the Merkle branch for a leaf by reconstructing the tree levels.</summary>
private static List<uint256> BuildBranch(IReadOnlyList<uint256> leaves, int position)
{
var branch = new List<uint256>();
@@ -20,7 +20,7 @@ public class StorageTests
private static string TempPath() =>
Path.Combine(Path.GetTempPath(), $"plm-test-{Guid.NewGuid()}.wallet.json");
// ---- cifratura AES-GCM ----
// ---- AES-GCM encryption ----
[Fact]
public void La_cifratura_fa_roundtrip_con_la_password_giusta()
@@ -257,8 +257,8 @@ public class StorageTests
[Fact]
public void WalletLock_file_lock_preesistente_ma_non_bloccato_viene_acquisito()
{
// Un .lock rimasto da un crash precedente (file esiste ma nessuno lo tiene)
// non deve bloccare l'apertura del wallet.
// A stale .lock left from a previous crash (file exists but nobody holds it)
// must not block wallet opening.
var path = TempPath();
var lockPath = path + ".lock";
try
@@ -5,7 +5,7 @@ namespace PalladiumWallet.Tests.Wallet;
public class CoinAmountTests
{
// ---- importi validi: tutte le unità ----
// ---- valid amounts: all units ----
[Theory]
[InlineData("1", "sat", 1)]
@@ -96,7 +96,7 @@ public class CoinAmountTests
Assert.False(CoinAmount.TryParseIn("99999999999", "PLM", out _));
}
// ---- unità sconosciuta lancia ArgumentException ----
// ---- unknown unit throws ArgumentException ----
[Theory]
[InlineData("banana")]
@@ -6,15 +6,15 @@ using PalladiumWallet.Core.Wallet;
namespace PalladiumWallet.Tests.Wallet;
/// <summary>
/// Test per ImportedKeyAccount e i nuovi percorsi factory in WalletLoader
/// (blueprint §4.4 — importati WIF, xpub, xprv).
/// Tests for ImportedKeyAccount and the new factory paths in WalletLoader
/// (blueprint §4.4 — imported WIF, xpub, xprv).
/// </summary>
public class ImportedKeyAccountTests
{
private static ChainProfile Profile => ChainProfiles.Mainnet;
private static Network Network => PalladiumNetworks.For(Profile.Kind);
// Chiave WIF valida per PLM mainnet (prefix 0x80 = Compressed WIF "K"/"L")
// Valid WIF key for PLM mainnet (prefix 0x80 = Compressed WIF "K"/"L")
private static Key GenerateKey() => new Key();
private static string ToWif(Key key) => key.GetWif(Network).ToString();
@@ -43,7 +43,7 @@ public class ImportedKeyAccountTests
Assert.Equal(addr.ToString(), account.GetAddress(false, 0).ToString());
Assert.Equal(addr.ToString(), account.GetReceiveAddress(0).ToString());
// Change → primo indirizzo
// Change → first address
Assert.Equal(addr.ToString(), account.GetChangeAddress(0).ToString());
}
@@ -147,7 +147,7 @@ public class ImportedKeyAccountTests
[Fact]
public void NewFromXpub_produce_account_watch_only()
{
// Crea un HD account, esporta la zpub, reimporta come xpub watch-only.
// Create an HD account, export the zpub, re-import as watch-only xpub.
var (_, hdFull) = WalletLoader.NewFromMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
null, ScriptKind.NativeSegwit, Profile);
@@ -19,7 +19,7 @@ public class TransactionFactoryTests
return HdAccount.FromMnemonic(mnemonic!, null, ScriptKind.NativeSegwit, Profile);
}
/// <summary>Tx fittizia che accredita <paramref name="sats"/> sull'indirizzo receiving/0.</summary>
/// <summary>Fake transaction that credits <paramref name="sats"/> to the receiving/0 address.</summary>
private static (List<CachedUtxo>, Dictionary<string, Transaction>) Fund(HdAccount account, long sats)
{
var funding = Net.CreateTransaction();
@@ -51,17 +51,17 @@ public class TransactionFactoryTests
feeRateSatPerVByte: 2, changeIndex: 0);
Assert.True(built.Signed);
// Output: destinatario + change.
// Output: recipient + change.
Assert.Equal(2, built.Transaction.Outputs.Count);
Assert.Contains(built.Transaction.Outputs,
o => o.ScriptPubKey == destination.ScriptPubKey && o.Value.Satoshi == 400_000);
// Fee coerente col rate richiesto (±20% per gli arrotondamenti di stima).
// Fee consistent with the requested rate (±20% for estimation rounding).
var vsize = built.Transaction.GetVirtualSize();
var expected = vsize * 2;
Assert.InRange(built.Fee.Satoshi, expected * 0.8, expected * 1.5);
// RBF abilitato (§6.6).
// RBF enabled (§6.6).
Assert.All(built.Transaction.Inputs, i => Assert.True(i.Sequence < Sequence.Final));
}
@@ -96,12 +96,12 @@ public class TransactionFactoryTests
{
var account = Account();
var (utxos, txs) = Fund(account, 1_000_000);
utxos[0].Height = 0; // in mempool: visibile nel saldo pending, non spendibile
utxos[0].Height = 0; // in mempool: visible in pending balance, not spendable
var ex = Assert.Throws<WalletSpendException>(() => new TransactionFactory(account).Build(
utxos, txs, account.GetReceiveAddress(1), amountSats: 100_000,
feeRateSatPerVByte: 2, changeIndex: 0));
Assert.Contains("in attesa di conferma", ex.Message);
Assert.Contains("pending confirmation", ex.Message);
}
[Fact]
@@ -109,7 +109,7 @@ public class TransactionFactoryTests
{
var account = Account();
var (utxos, txs) = Fund(account, 1_000_000);
utxos[0].Frozen = true; // freeze (§6.2)
utxos[0].Frozen = true; // frozen (§6.2)
Assert.Throws<WalletSpendException>(() => new TransactionFactory(account).Build(
utxos, txs, account.GetReceiveAddress(1), amountSats: 100_000,
@@ -130,8 +130,8 @@ public class TransactionFactoryTests
feeRateSatPerVByte: 2, changeIndex: 0);
Assert.False(built.Signed);
// Il flusso air-gapped (§6.5): la PSBT della macchina online si firma
// offline con le chiavi e si finalizza.
// Air-gapped flow (§6.5): the online-machine PSBT is signed
// offline with the keys and then finalised.
var psbt = built.Psbt;
psbt.SignWithKeys(full.GetExtPrivateKey(false, 0));
psbt.Finalize();
@@ -157,7 +157,7 @@ public class TransactionFactoryTests
Assert.False(CoinAmount.TryParseCoins("-1", out _));
}
// 1.5 PLM = 150_000_000 sat, espressi in ciascuna unità (§8).
// 1.5 PLM = 150_000_000 sat, expressed in each unit (§8).
[Theory]
[InlineData("PLM", "1.50000000 PLM", "1.5")]
[InlineData("mPLM", "1500.00000 mPLM", "1500")]
@@ -120,7 +120,7 @@ public class WalletLoaderTests
var (doc, accountSeed) = WalletLoader.NewFromMnemonic(
ValidMnemonic, null, ScriptKind.NativeSegwit, ChainProfiles.Mainnet);
// Crea documento watch-only rimuovendo la mnemonica
// Build a watch-only document by removing the mnemonic.
var docWo = new WalletDocument
{
Network = doc.Network,