feat(wallet): enforce confirmation thresholds before spending UTXOs
Coinbase outputs require COINBASE_MATURITY + 1 = 121 confirmations before they can be spent. The consensus rule (palladiumcore tx_verify.cpp) is nSpendHeight - nHeight >= 120; the +1 adds one block of safety margin, matching the Qt wallet (wallet.cpp: COINBASE_MATURITY+1). Regular UTXOs require MinConfirmations (6 on mainnet, 1 on testnet/regtest — wallet policy, no consensus rule). Changes: - ChainProfile: add CoinbaseMaturity and MinConfirmations fields - ChainProfiles: mainnet CoinbaseMaturity=120 / MinConfirmations=6; testnet/regtest inherit 120, override MinConfirmations=1 - PalladiumNetworks: align NBitcoin Consensus.CoinbaseMaturity to 120 - CachedUtxo: add IsCoinbase flag (ElectrumX listunspent does not expose this; derived from Transaction.IsCoinBase on the raw tx) - WalletSynchronizer: set IsCoinbase during local UTXO reconstruction - TransactionFactory.Build: add tipHeight param; filter by threshold; error reports reason (immature coinbase with X/121 counter, under- confirmed regulars with best-seen count, mempool amounts) - All Build() call sites updated (App, Donate, CLI) - Two new tests: coinbase maturity boundary (119→220 tip) and mainnet min-conf boundary (5→6 confirmations)
This commit is contained in:
@@ -126,6 +126,12 @@ public sealed record ChainProfile
|
||||
/// <summary>Target block time in seconds (LWMA v2: 120s).</summary>
|
||||
public required int BlockTimeSeconds { get; init; }
|
||||
|
||||
/// <summary>Blocks a coinbase output must wait before it can be spent.</summary>
|
||||
public required int CoinbaseMaturity { get; init; }
|
||||
|
||||
/// <summary>Minimum confirmations required for a regular UTXO to be spendable.</summary>
|
||||
public required int MinConfirmations { get; init; }
|
||||
|
||||
/// <summary>BIP32/SLIP-132 headers for each script type.</summary>
|
||||
public required IReadOnlyDictionary<ScriptKind, ExtKeyHeaders> ExtKeyHeaders { get; init; }
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public static class ChainProfiles
|
||||
ExplorerUrl = "https://explorer.palladium-coin.com/",
|
||||
SkipPowValidation = true,
|
||||
BlockTimeSeconds = 120,
|
||||
CoinbaseMaturity = 120,
|
||||
MinConfirmations = 6,
|
||||
ExtKeyHeaders = new Dictionary<ScriptKind, ExtKeyHeaders>
|
||||
{
|
||||
[ScriptKind.Legacy] = new(0x0488ade4, 0x0488b21e), // xprv / xpub
|
||||
@@ -53,6 +55,7 @@ public static class ChainProfiles
|
||||
{
|
||||
Kind = NetKind.Testnet,
|
||||
NetName = "testnet",
|
||||
MinConfirmations = 1,
|
||||
WifPrefix = 0xff,
|
||||
AddrP2pkh = 127,
|
||||
AddrP2sh = 115,
|
||||
|
||||
@@ -114,7 +114,7 @@ public static class PalladiumNetworks
|
||||
MajorityRejectBlockOutdated = 950,
|
||||
MajorityWindow = 1000,
|
||||
MinimumChainWork = uint256.Zero,
|
||||
CoinbaseMaturity = 100,
|
||||
CoinbaseMaturity = 120,
|
||||
SupportSegwit = true,
|
||||
SupportTaproot = true,
|
||||
ConsensusFactory = new ConsensusFactory(),
|
||||
|
||||
Reference in New Issue
Block a user