From 4bf79d3de1436d5d35cea3710176bf444c702fa6 Mon Sep 17 00:00:00 2001 From: Juan Gutierrez de Rojas Date: Wed, 23 Apr 2025 17:02:08 +0200 Subject: [PATCH 1/3] Fix: Incorrect Fee Estimation Due to Omitted estimate_mode in Bitcoin Core 28.0 --- NBitcoin/RPC/RPCClient.cs | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/NBitcoin/RPC/RPCClient.cs b/NBitcoin/RPC/RPCClient.cs index 88d090fa2..ce4010967 100644 --- a/NBitcoin/RPC/RPCClient.cs +++ b/NBitcoin/RPC/RPCClient.cs @@ -396,7 +396,7 @@ async static Task CheckSegwitCapabilitiesAsync(RPCClient rpc, Action setRe return; } #endif - var address = new Key().GetAddress(type, rpc.Network); + var address = new Key().GetAddress(type, rpc.Network); if (address == null) { setResult(false); @@ -1084,7 +1084,7 @@ private async Task ToMemoryStreamAsync(Stream stream) return ms; } -#region P2P Networking + #region P2P Networking #if !NOSOCKET public PeerInfo[] GetPeersInfo() { @@ -1242,9 +1242,9 @@ public async Task GetAddedNodeInfoAync(bool detailed, EndPoint no } #endif -#endregion + #endregion -#region Block chain and UTXO + #region Block chain and UTXO public async Task GetBlockchainInfoAsync(CancellationToken cancellationToken = default) { @@ -1899,13 +1899,13 @@ public IEnumerable GetTransactions(int height) return GetTransactions(GetBlockHash(height)); } -#endregion + #endregion -#region Coin generation + #region Coin generation -#endregion + #endregion -#region Raw Transaction + #region Raw Transaction public Transaction DecodeRawTransaction(string rawHex) { @@ -2042,15 +2042,15 @@ public async Task BumpFeeAsync(uint256 txid, CancellationToken can } -#endregion + #endregion -#region Utility functions + #region Utility functions // Estimates the approximate fee per kilobyte needed for a transaction to begin // confirmation within conf_target blocks if possible and return the number of blocks // for which the estimate is valid.Uses virtual transaction size as defined // in BIP 141 (witness data is discounted). -#region Fee Estimation + #region Fee Estimation /// /// (>= Bitcoin Core v0.14) Get the estimated fee per kb for being confirmed in nblock @@ -2112,13 +2112,9 @@ private async Task EstimateSmartFeeImplAsync(int confi { if (Capabilities == null || Capabilities.SupportEstimateSmartFee) { - var parameters = new List() { confirmationTarget }; - if (estimateMode != EstimateSmartFeeMode.Conservative) - { - parameters.Add(estimateMode.ToString().ToUpperInvariant()); - } + var parameters = new object[] { confirmationTarget, estimateMode.ToString().ToUpperInvariant() }; - var request = new RPCRequest(RPCOperations.estimatesmartfee.ToString(), parameters.ToArray()); + var request = new RPCRequest(RPCOperations.estimatesmartfee.ToString(), parameters); request.ThrowIfRPCError = false; var response = await SendCommandAsync(request, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2163,7 +2159,7 @@ private async Task EstimateSmartFeeImplAsync(int confi } } -#endregion + #endregion #nullable enable @@ -2349,7 +2345,7 @@ public bool SetTxFee(FeeRate feeRate, CancellationToken cancellationToken = defa return SendCommand(RPCOperations.settxfee, cancellationToken, new[] { feeRate.FeePerK.ToString() }).Result.ToString() == "true"; } -#endregion + #endregion public async Task GenerateAsync(int nBlocks, CancellationToken cancellationToken = default) { @@ -2400,7 +2396,7 @@ public uint256[] GenerateToAddress(int nBlocks, BitcoinAddress address) return GenerateToAddressAsync(nBlocks, address).GetAwaiter().GetResult(); } -#region Region Hidden Methods + #region Region Hidden Methods /// /// Permanently marks a block as invalid, as if it violated a consensus rule. @@ -2446,7 +2442,7 @@ public async Task AddPeerAddressAsync(IPAddress ip, int port, Cancellation #endif -#endregion + #endregion } #if !NOSOCKET From e872c8127dbe76edc6780b03cb4e5db1e69161e9 Mon Sep 17 00:00:00 2001 From: Juan Gutierrez de Rojas Date: Wed, 23 Apr 2025 17:12:37 +0200 Subject: [PATCH 2/3] Refactor: Adjust indentation and region directives in RPCClient.cs for consistency --- NBitcoin/RPC/RPCClient.cs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/NBitcoin/RPC/RPCClient.cs b/NBitcoin/RPC/RPCClient.cs index ce4010967..f668c0b6a 100644 --- a/NBitcoin/RPC/RPCClient.cs +++ b/NBitcoin/RPC/RPCClient.cs @@ -396,7 +396,7 @@ async static Task CheckSegwitCapabilitiesAsync(RPCClient rpc, Action setRe return; } #endif - var address = new Key().GetAddress(type, rpc.Network); + var address = new Key().GetAddress(type, rpc.Network); if (address == null) { setResult(false); @@ -1084,7 +1084,7 @@ private async Task ToMemoryStreamAsync(Stream stream) return ms; } - #region P2P Networking +#region P2P Networking #if !NOSOCKET public PeerInfo[] GetPeersInfo() { @@ -1242,9 +1242,9 @@ public async Task GetAddedNodeInfoAync(bool detailed, EndPoint no } #endif - #endregion +#endregion - #region Block chain and UTXO +#region Block chain and UTXO public async Task GetBlockchainInfoAsync(CancellationToken cancellationToken = default) { @@ -1899,13 +1899,13 @@ public IEnumerable GetTransactions(int height) return GetTransactions(GetBlockHash(height)); } - #endregion +#endregion - #region Coin generation +#region Coin generation - #endregion +#endregion - #region Raw Transaction +#region Raw Transaction public Transaction DecodeRawTransaction(string rawHex) { @@ -2042,15 +2042,15 @@ public async Task BumpFeeAsync(uint256 txid, CancellationToken can } - #endregion +#endregion - #region Utility functions +#region Utility functions // Estimates the approximate fee per kilobyte needed for a transaction to begin // confirmation within conf_target blocks if possible and return the number of blocks // for which the estimate is valid.Uses virtual transaction size as defined // in BIP 141 (witness data is discounted). - #region Fee Estimation +#region Fee Estimation /// /// (>= Bitcoin Core v0.14) Get the estimated fee per kb for being confirmed in nblock @@ -2159,7 +2159,7 @@ private async Task EstimateSmartFeeImplAsync(int confi } } - #endregion +#endregion #nullable enable @@ -2345,7 +2345,7 @@ public bool SetTxFee(FeeRate feeRate, CancellationToken cancellationToken = defa return SendCommand(RPCOperations.settxfee, cancellationToken, new[] { feeRate.FeePerK.ToString() }).Result.ToString() == "true"; } - #endregion +#endregion public async Task GenerateAsync(int nBlocks, CancellationToken cancellationToken = default) { @@ -2396,7 +2396,7 @@ public uint256[] GenerateToAddress(int nBlocks, BitcoinAddress address) return GenerateToAddressAsync(nBlocks, address).GetAwaiter().GetResult(); } - #region Region Hidden Methods +#region Region Hidden Methods /// /// Permanently marks a block as invalid, as if it violated a consensus rule. @@ -2442,7 +2442,7 @@ public async Task AddPeerAddressAsync(IPAddress ip, int port, Cancellation #endif - #endregion +#endregion } #if !NOSOCKET From d76d4bccabec081a51fbe63a4ff19d24a317e2dc Mon Sep 17 00:00:00 2001 From: Juan Gutierrez de Rojas Date: Thu, 24 Apr 2025 18:26:16 +0200 Subject: [PATCH 3/3] Fix: Allow nullable estimateMode. If null it will use default estimateMode from node --- NBitcoin/RPC/RPCClient.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/NBitcoin/RPC/RPCClient.cs b/NBitcoin/RPC/RPCClient.cs index f668c0b6a..24f35b90f 100644 --- a/NBitcoin/RPC/RPCClient.cs +++ b/NBitcoin/RPC/RPCClient.cs @@ -2060,7 +2060,7 @@ public async Task BumpFeeAsync(uint256 txid, CancellationToken can /// Whether to return a more conservative estimate which also satisfies a longer history. A conservative estimate potentially returns a higher feerate and is more likely to be sufficient for the desired target, but is not as responsive to short term drops in the prevailing fee market. /// The estimated fee rate, block number where estimate was found /// The Fee rate couldn't be estimated because of insufficient data from Bitcoin Core - public EstimateSmartFeeResponse EstimateSmartFee(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative) + public EstimateSmartFeeResponse EstimateSmartFee(int confirmationTarget, EstimateSmartFeeMode? estimateMode = null) { return EstimateSmartFeeAsync(confirmationTarget, estimateMode).GetAwaiter().GetResult(); } @@ -2072,7 +2072,7 @@ public EstimateSmartFeeResponse EstimateSmartFee(int confirmationTarget, Estimat /// Confirmation target in blocks (1 - 1008) /// Whether to return a more conservative estimate which also satisfies a longer history. A conservative estimate potentially returns a higher feerate and is more likely to be sufficient for the desired target, but is not as responsive to short term drops in the prevailing fee market. /// The estimated fee rate, block number where estimate was found or null - public async Task TryEstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, CancellationToken cancellationToken = default) + public async Task TryEstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode? estimateMode = null, CancellationToken cancellationToken = default) { return await EstimateSmartFeeImplAsync(confirmationTarget, estimateMode, cancellationToken).ConfigureAwait(false); } @@ -2084,7 +2084,7 @@ public async Task TryEstimateSmartFeeAsync(int confirm /// Confirmation target in blocks (1 - 1008) /// Whether to return a more conservative estimate which also satisfies a longer history. A conservative estimate potentially returns a higher feerate and is more likely to be sufficient for the desired target, but is not as responsive to short term drops in the prevailing fee market. /// The estimated fee rate, block number where estimate was found or null - public EstimateSmartFeeResponse TryEstimateSmartFee(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative) + public EstimateSmartFeeResponse TryEstimateSmartFee(int confirmationTarget, EstimateSmartFeeMode? estimateMode = null) { return TryEstimateSmartFeeAsync(confirmationTarget, estimateMode).GetAwaiter().GetResult(); } @@ -2097,7 +2097,7 @@ public EstimateSmartFeeResponse TryEstimateSmartFee(int confirmationTarget, Esti /// Whether to return a more conservative estimate which also satisfies a longer history. A conservative estimate potentially returns a higher feerate and is more likely to be sufficient for the desired target, but is not as responsive to short term drops in the prevailing fee market. /// The estimated fee rate, block number where estimate was found /// when fee couldn't be estimated - public async Task EstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, CancellationToken cancellationToken = default) + public async Task EstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode? estimateMode = null, CancellationToken cancellationToken = default) { var feeRate = await EstimateSmartFeeImplAsync(confirmationTarget, estimateMode, cancellationToken); if (feeRate == null) @@ -2108,13 +2108,17 @@ public async Task EstimateSmartFeeAsync(int confirmati /// /// (>= Bitcoin Core v0.14) /// - private async Task EstimateSmartFeeImplAsync(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, CancellationToken cancellationToken = default) + private async Task EstimateSmartFeeImplAsync(int confirmationTarget, EstimateSmartFeeMode? estimateMode = null, CancellationToken cancellationToken = default) { if (Capabilities == null || Capabilities.SupportEstimateSmartFee) { - var parameters = new object[] { confirmationTarget, estimateMode.ToString().ToUpperInvariant() }; + var parameters = new List() { confirmationTarget }; + if (estimateMode != null) + { + parameters.Add(estimateMode.ToString().ToUpperInvariant()); + } - var request = new RPCRequest(RPCOperations.estimatesmartfee.ToString(), parameters); + var request = new RPCRequest(RPCOperations.estimatesmartfee.ToString(), parameters.ToArray()); request.ThrowIfRPCError = false; var response = await SendCommandAsync(request, cancellationToken: cancellationToken).ConfigureAwait(false);