Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert the revert hanging block, and fix the issues #1585

Merged
merged 2 commits into from Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions WalletWasabi/Extensions/NBitcoinExtensions.cs
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
using WalletWasabi.Models;
Expand All @@ -15,6 +16,38 @@ namespace NBitcoin
{
public static class NBitcoinExtensions
{
public static async Task<Block> DownloadBlockAsync(this Node node, uint256 hash, CancellationToken cancellationToken)
{
if (node.State == NodeState.Connected)
node.VersionHandshake(cancellationToken);
using (var listener = node.CreateListener())
{
var getdata = new GetDataPayload(new InventoryVector(node.AddSupportedOptions(InventoryType.MSG_BLOCK), hash));
await node.SendMessageAsync(getdata);
cancellationToken.ThrowIfCancellationRequested();

// 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 block 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)
{
cancellationToken.ThrowIfCancellationRequested();
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)
Expand Down
23 changes: 7 additions & 16 deletions WalletWasabi/Services/WalletService.cs
Expand Up @@ -806,16 +806,12 @@ public async Task<Block> GetOrDownloadBlockAsync(uint256 hash, CancellationToken

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(64)))
nopara73 marked this conversation as resolved.
Show resolved Hide resolved
{
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!");
}
Expand Down Expand Up @@ -863,14 +859,7 @@ public async Task<Block> GetOrDownloadBlockAsync(uint256 hash, CancellationToken
{
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())
Expand Down Expand Up @@ -1580,7 +1569,9 @@ private void SerializeTransactionCache()
Encoding.UTF8);
}

// Current timeout used when downloading a block from the remote node. It is defined in seconds.
/// <summary>
/// Current timeout used when downloading a block from the remote node. It is defined in seconds.
/// </summary>
private async Task NodeTimeoutsAsync(bool increaseDecrease)
{
if (increaseDecrease)
Expand Down