126 lines
6.1 KiB
C#
126 lines
6.1 KiB
C#
|
|
using NBitcoin;
|
||
|
|
using NBitcoin.DataEncoders;
|
||
|
|
|
||
|
|
namespace PalladiumWallet.Core.Chain;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Costruisce e registra le <see cref="Network"/> NBitcoin di Palladium a partire dai
|
||
|
|
/// <see cref="ChainProfiles"/> (blueprint §19.3). Tutto il resto del codice ottiene la
|
||
|
|
/// rete da qui, mai da costanti sparse.
|
||
|
|
/// </summary>
|
||
|
|
/// <summary>
|
||
|
|
/// Identità della moneta per NBitcoin: raggruppa le tre reti sotto il codice PLM.
|
||
|
|
/// I getter sono lazy, quindi nessuna ricorsione durante la costruzione.
|
||
|
|
/// </summary>
|
||
|
|
public sealed class PalladiumNetworkSet : INetworkSet
|
||
|
|
{
|
||
|
|
public static readonly PalladiumNetworkSet Instance = new();
|
||
|
|
|
||
|
|
private PalladiumNetworkSet() { }
|
||
|
|
|
||
|
|
public string CryptoCode => "PLM";
|
||
|
|
public Network Mainnet => PalladiumNetworks.Mainnet;
|
||
|
|
public Network Testnet => PalladiumNetworks.Testnet;
|
||
|
|
public Network Regtest => PalladiumNetworks.Regtest;
|
||
|
|
|
||
|
|
public Network GetNetwork(ChainName chainName)
|
||
|
|
{
|
||
|
|
if (chainName == ChainName.Mainnet) return Mainnet;
|
||
|
|
if (chainName == ChainName.Testnet) return Testnet;
|
||
|
|
if (chainName == ChainName.Regtest) return Regtest;
|
||
|
|
throw new ArgumentOutOfRangeException(nameof(chainName));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static class PalladiumNetworks
|
||
|
|
{
|
||
|
|
// Blocco genesi di Bitcoin (la mainnet Palladium lo riusa, blueprint §3).
|
||
|
|
private const string MainGenesisHex =
|
||
|
|
"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c" +
|
||
|
|
Coinbase;
|
||
|
|
|
||
|
|
// Genesi testnet3/regtest di Bitcoin: stessa coinbase, cambiano time/bits/nonce.
|
||
|
|
// TODO: confermare contro chainparams.cpp del nodo (vedi ChainProfiles).
|
||
|
|
private const string TestGenesisHex =
|
||
|
|
"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae18" +
|
||
|
|
Coinbase;
|
||
|
|
|
||
|
|
private const string RegtestGenesisHex =
|
||
|
|
"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f2002000000" +
|
||
|
|
Coinbase;
|
||
|
|
|
||
|
|
private const string Coinbase =
|
||
|
|
"0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
|
||
|
|
|
||
|
|
private static readonly Lazy<Network> _mainnet = new(() => Build(ChainProfiles.Mainnet, MainGenesisHex, 0x504c4d4du));
|
||
|
|
private static readonly Lazy<Network> _testnet = new(() => Build(ChainProfiles.Testnet, TestGenesisHex, 0x504c4d54u));
|
||
|
|
private static readonly Lazy<Network> _regtest = new(() => Build(ChainProfiles.Regtest, RegtestGenesisHex, 0x504c4d52u));
|
||
|
|
|
||
|
|
public static Network Mainnet => _mainnet.Value;
|
||
|
|
public static Network Testnet => _testnet.Value;
|
||
|
|
public static Network Regtest => _regtest.Value;
|
||
|
|
|
||
|
|
public static Network For(NetKind kind) => kind switch
|
||
|
|
{
|
||
|
|
NetKind.Mainnet => Mainnet,
|
||
|
|
NetKind.Testnet => Testnet,
|
||
|
|
NetKind.Regtest => Regtest,
|
||
|
|
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
|
||
|
|
};
|
||
|
|
|
||
|
|
private static Network Build(ChainProfile p, string genesisHex, uint magic)
|
||
|
|
{
|
||
|
|
var legacy = p.ExtKeyHeaders[ScriptKind.Legacy];
|
||
|
|
|
||
|
|
var builder = new NetworkBuilder()
|
||
|
|
.SetName($"plm-{p.NetName}")
|
||
|
|
.SetNetworkSet(PalladiumNetworkSet.Instance)
|
||
|
|
.SetChainName(p.Kind switch
|
||
|
|
{
|
||
|
|
NetKind.Mainnet => ChainName.Mainnet,
|
||
|
|
NetKind.Testnet => ChainName.Testnet,
|
||
|
|
_ => ChainName.Regtest,
|
||
|
|
})
|
||
|
|
// Magic P2P solo segnaposto: il wallet SPV non apre mai connessioni P2P
|
||
|
|
// (parla solo col server di indicizzazione, §3). Serve a NBitcoin per
|
||
|
|
// distinguere le reti registrate.
|
||
|
|
.SetMagic(magic)
|
||
|
|
.SetPort(p.NodeP2pPort)
|
||
|
|
.SetRPCPort(p.NodeP2pPort + 1)
|
||
|
|
.SetGenesis(genesisHex)
|
||
|
|
.SetBase58Bytes(Base58Type.PUBKEY_ADDRESS, [p.AddrP2pkh])
|
||
|
|
.SetBase58Bytes(Base58Type.SCRIPT_ADDRESS, [p.AddrP2sh])
|
||
|
|
.SetBase58Bytes(Base58Type.SECRET_KEY, [p.WifPrefix])
|
||
|
|
// NBitcoin gestisce una sola coppia di header BIP32 per rete: quella
|
||
|
|
// standard xprv/xpub. Le varianti SLIP-132 (y/z/Y/Z) si serializzano
|
||
|
|
// a mano con ChainProfile.ExtKeyHeaders quando servono.
|
||
|
|
.SetBase58Bytes(Base58Type.EXT_SECRET_KEY, legacy.PrivateBytes())
|
||
|
|
.SetBase58Bytes(Base58Type.EXT_PUBLIC_KEY, legacy.PublicBytes())
|
||
|
|
.SetBech32(Bech32Type.WITNESS_PUBKEY_ADDRESS, Encoders.Bech32(p.SegwitHrp))
|
||
|
|
.SetBech32(Bech32Type.WITNESS_SCRIPT_ADDRESS, Encoders.Bech32(p.SegwitHrp))
|
||
|
|
.SetBech32(Bech32Type.TAPROOT_ADDRESS, Encoders.Bech32(p.SegwitHrp))
|
||
|
|
.SetUriScheme(p.UriScheme)
|
||
|
|
.SetConsensus(new Consensus
|
||
|
|
{
|
||
|
|
// Valori usati solo dalle API NBitcoin che li richiedono: la validazione
|
||
|
|
// header reale è custom (Core/Spv) con skip PoW + checkpoint (§7).
|
||
|
|
PowLimit = new Target(new uint256("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff")),
|
||
|
|
PowTargetSpacing = TimeSpan.FromSeconds(p.BlockTimeSeconds),
|
||
|
|
PowTargetTimespan = TimeSpan.FromDays(14),
|
||
|
|
PowAllowMinDifficultyBlocks = p.Kind != NetKind.Mainnet,
|
||
|
|
PowNoRetargeting = p.Kind == NetKind.Regtest,
|
||
|
|
SubsidyHalvingInterval = 210000,
|
||
|
|
MajorityEnforceBlockUpgrade = 750,
|
||
|
|
MajorityRejectBlockOutdated = 950,
|
||
|
|
MajorityWindow = 1000,
|
||
|
|
MinimumChainWork = uint256.Zero,
|
||
|
|
CoinbaseMaturity = 100,
|
||
|
|
SupportSegwit = true,
|
||
|
|
SupportTaproot = true,
|
||
|
|
ConsensusFactory = new ConsensusFactory(),
|
||
|
|
});
|
||
|
|
|
||
|
|
return builder.BuildAndRegister();
|
||
|
|
}
|
||
|
|
}
|