2026-06-11 10:48:21 +02:00
|
|
|
using NBitcoin;
|
|
|
|
|
using PalladiumWallet.Core.Chain;
|
|
|
|
|
using PalladiumWallet.Core.Crypto;
|
|
|
|
|
|
|
|
|
|
namespace PalladiumWallet.Tests.Crypto;
|
|
|
|
|
|
|
|
|
|
public class AddressDerivationTests
|
|
|
|
|
{
|
|
|
|
|
private static byte[] AbandonAboutSeed()
|
|
|
|
|
{
|
|
|
|
|
Assert.True(Bip39.TryParse(
|
|
|
|
|
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
|
|
|
|
out var mnemonic));
|
|
|
|
|
return Bip39.ToSeed(mnemonic!);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void Il_vettore_bip44_produce_lo_stesso_hash_su_bitcoin_e_plm()
|
|
|
|
|
{
|
2026-06-16 14:40:06 +02:00
|
|
|
// Known abandon-about address at m/44'/0'/0'/0/0 (public reference).
|
2026-06-11 10:48:21 +02:00
|
|
|
var account = HdAccount.FromSeed(AbandonAboutSeed(), ScriptKind.Legacy,
|
|
|
|
|
ChainProfiles.Mainnet, new KeyPath("44'/0'/0'"));
|
|
|
|
|
var pubKey = account.GetPublicKey(isChange: false, 0);
|
|
|
|
|
|
|
|
|
|
var bitcoinAddr = pubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main);
|
|
|
|
|
Assert.Equal("1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", bitcoinAddr.ToString());
|
|
|
|
|
|
|
|
|
|
var plmAddr = account.GetReceiveAddress(0);
|
|
|
|
|
Assert.StartsWith("P", plmAddr.ToString());
|
2026-06-16 14:40:06 +02:00
|
|
|
// Same hash160 under the two prefixes: the derivation is identical,
|
|
|
|
|
// only the network dress changes.
|
2026-06-11 10:48:21 +02:00
|
|
|
Assert.Equal(pubKey.Hash, ((BitcoinPubKeyAddress)plmAddr).Hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void Il_vettore_bip49_produce_lo_stesso_script_hash_su_bitcoin_e_plm()
|
|
|
|
|
{
|
|
|
|
|
var account = HdAccount.FromSeed(AbandonAboutSeed(), ScriptKind.WrappedSegwit,
|
|
|
|
|
ChainProfiles.Mainnet, new KeyPath("49'/0'/0'"));
|
|
|
|
|
var pubKey = account.GetPublicKey(isChange: false, 0);
|
|
|
|
|
|
|
|
|
|
var bitcoinAddr = pubKey.GetAddress(ScriptPubKeyType.SegwitP2SH, Network.Main);
|
|
|
|
|
Assert.Equal("37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf", bitcoinAddr.ToString());
|
|
|
|
|
|
|
|
|
|
var plmAddr = account.GetReceiveAddress(0);
|
|
|
|
|
Assert.StartsWith("3", plmAddr.ToString());
|
|
|
|
|
Assert.Equal(((BitcoinScriptAddress)bitcoinAddr).Hash, ((BitcoinScriptAddress)plmAddr).Hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[InlineData(ScriptKind.Legacy, NetKind.Mainnet)]
|
|
|
|
|
[InlineData(ScriptKind.WrappedSegwit, NetKind.Mainnet)]
|
|
|
|
|
[InlineData(ScriptKind.NativeSegwit, NetKind.Mainnet)]
|
|
|
|
|
[InlineData(ScriptKind.Legacy, NetKind.Testnet)]
|
|
|
|
|
[InlineData(ScriptKind.WrappedSegwit, NetKind.Testnet)]
|
|
|
|
|
[InlineData(ScriptKind.NativeSegwit, NetKind.Testnet)]
|
|
|
|
|
[InlineData(ScriptKind.NativeSegwit, NetKind.Regtest)]
|
|
|
|
|
public void Ogni_indirizzo_derivato_fa_roundtrip_sulla_propria_rete(ScriptKind kind, NetKind net)
|
|
|
|
|
{
|
|
|
|
|
var profile = ChainProfiles.For(net);
|
|
|
|
|
var account = HdAccount.FromSeed(AbandonAboutSeed(), kind, profile);
|
|
|
|
|
|
|
|
|
|
var addr = account.GetReceiveAddress(0);
|
|
|
|
|
var parsed = BitcoinAddress.Create(addr.ToString(), PalladiumNetworks.For(net));
|
|
|
|
|
|
|
|
|
|
Assert.Equal(addr.ScriptPubKey, parsed.ScriptPubKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[InlineData(NetKind.Mainnet, "tplm1")]
|
|
|
|
|
[InlineData(NetKind.Mainnet, "rplm1")]
|
|
|
|
|
public void Gli_indirizzi_segwit_mainnet_non_hanno_prefissi_di_altre_reti(NetKind net, string wrongPrefix)
|
|
|
|
|
{
|
|
|
|
|
var account = HdAccount.FromSeed(AbandonAboutSeed(), ScriptKind.NativeSegwit, ChainProfiles.For(net));
|
|
|
|
|
Assert.DoesNotContain(wrongPrefix, account.GetReceiveAddress(0).ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void Il_path_di_account_usa_il_coin_type_del_profilo()
|
|
|
|
|
{
|
|
|
|
|
Assert.Equal("84'/746'/0'",
|
|
|
|
|
DerivationPaths.AccountPath(ScriptKind.NativeSegwit, ChainProfiles.Mainnet).ToString());
|
|
|
|
|
Assert.Equal("84'/1'/0'",
|
|
|
|
|
DerivationPaths.AccountPath(ScriptKind.NativeSegwit, ChainProfiles.Testnet).ToString());
|
|
|
|
|
Assert.Equal("44'/746'/2'",
|
|
|
|
|
DerivationPaths.AccountPath(ScriptKind.Legacy, ChainProfiles.Mainnet, account: 2).ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void Receiving_e_change_derivano_indirizzi_diversi()
|
|
|
|
|
{
|
|
|
|
|
var account = HdAccount.FromSeed(AbandonAboutSeed(), ScriptKind.NativeSegwit, ChainProfiles.Mainnet);
|
|
|
|
|
Assert.NotEqual(account.GetReceiveAddress(0).ToString(), account.GetChangeAddress(0).ToString());
|
|
|
|
|
Assert.NotEqual(account.GetReceiveAddress(0).ToString(), account.GetReceiveAddress(1).ToString());
|
|
|
|
|
}
|
|
|
|
|
}
|