Fix hanging block download
MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -5,6 +5,7 @@ using NBitcoin.RPC;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
using WalletWasabi.Models;
@@ -15,6 +16,33 @@ namespace NBitcoin
{
public static class NBitcoinExtensions
{
+ public static async Task<Block> DownloadBlockAsync(this Node node, uint256 hash, CancellationToken cancellationToken = default)
+ {
+ using (var listener = node.CreateListener())
+ {
+ var getdata = new GetDataPayload(new InventoryVector(node.AddSupportedOptions(InventoryType.MSG_BLOCK), hash));
+ await node.SendMessageAsync(getdata);
+
+ // Bitcoin Core processes the messages sequentially and does not send a NOTFOUND message if the remote node is pruned and the data not available
+ // A good way to get any feedback about whether the node knows the block or not is to send a ping request.
+ // If blocks is not known by the remote node, the pong will be sent immediately, else it will be sent after the block download
+ ulong pingNonce = RandomUtils.GetUInt64();
+ await node.SendMessageAsync(new PingPayload() { Nonce = pingNonce });
+ while (true)
+ {
+ var message = listener.ReceiveMessage(cancellationToken);
+ if (message.Message.Payload is NotFoundPayload ||
+ (message.Message.Payload is PongPayload p && p.Nonce == pingNonce))
+ {
+ throw new InvalidOperationException($"Disconnected local node, because it does not have the block data.");
+ }
+ else if (message.Message.Payload is BlockPayload b && b.Object?.GetHash() == hash)
+ {
+ return b.Object;
+ }
+ }
+ }
+ }
public static TxoRef ToTxoRef(this OutPoint me) => new TxoRef(me);
public static IEnumerable<TxoRef> ToTxoRefs(this TxInList me)

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -791,16 +791,12 @@ namespace WalletWasabi.Services
Block blockFromLocalNode = null;
// Should timeout faster. Not sure if it should ever fail though. Maybe let's keep like this later for remote node connection.
- using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(64))) // 1/2 ADSL 512 kbit/s 00:00:32
+ using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(RuntimeParams.Instance.NetworkNodeTimeout))) // 1/2 ADSL 512 kbit/s 00:00:32
{
- blockFromLocalNode = LocalBitcoinCoreNode.GetBlocks(new uint256[] { hash }, cts.Token)?.Single();
+ blockFromLocalNode = await LocalBitcoinCoreNode.DownloadBlockAsync(hash, cts.Token);
}
- if (blockFromLocalNode is null)
- {
- throw new InvalidOperationException($"Disconnected local node, because couldn't parse received block.");
- }
- else if (!blockFromLocalNode.Check())
+ if (!blockFromLocalNode.Check())
{
throw new InvalidOperationException($"Disconnected node, because block invalid block received!");
}
@@ -850,14 +846,7 @@ namespace WalletWasabi.Services
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(RuntimeParams.Instance.NetworkNodeTimeout))) // 1/2 ADSL 512 kbit/s 00:00:32
{
- block = node.GetBlocks(new uint256[] { hash }, cts.Token)?.Single();
- }
-
- if (block is null)
- {
- Logger.LogInfo<WalletService>($"Disconnected node: {node.RemoteSocketAddress}, because couldn't parse received block.");
- node.DisconnectAsync("Couldn't parse block.");
- continue;
+ block = await node.DownloadBlockAsync(hash, cts.Token);
}
if (!block.Check())

Fix hanging block download