Add TrustedNodeNotifyingBehavior to CoreNode
MODIFY
WalletWasabi.Tests/IntegrationTests/RegTests.cs
WalletWasabi.Tests/IntegrationTests/RegTests.cs
@@ -449,9 +449,9 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -668,11 +668,11 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -1151,11 +1151,11 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -1317,11 +1317,11 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -1479,11 +1479,11 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -1650,11 +1650,11 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.
@@ -3234,17 +3234,17 @@ namespace WalletWasabi.Tests.IntegrationTests
// Create the services.
// 1. Create connection service.
var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
var nodes2 = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);
- nodes2.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync());
+ nodes2.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());
// 2. Create mempool service.
- Node node = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
- Node node2 = await RegTestFixture.BackendRegTestNode.CreateP2pNodeAsync();
+ Node node2 = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();
node2.Behaviors.Add(bitcoinStore.CreateMempoolBehavior());
// 3. Create wasabi synchronizer service.

MODIFY
WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs
WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs
@@ -56,7 +56,7 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
public async Task P2pWorksAsync()
{
var coreNode = await TestNodeBuilder.CreateAsync();
- using var node = await coreNode.CreateP2pNodeAsync();
+ using var node = await coreNode.CreateNewP2pNodeAsync();
try
{
var blocks = node.GetBlocks(new[] { Network.RegTest.GenesisHash });

MODIFY
WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs
WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs
@@ -2,6 +2,7 @@ using NBitcoin;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -20,7 +21,7 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
public async Task MempoolNotifiesAsync()
{
var coreNode = await TestNodeBuilder.CreateAsync();
- using var node = await coreNode.CreateP2pNodeAsync();
+ using var node = await coreNode.CreateNewP2pNodeAsync();
try
{
var rpc = coreNode.RpcClient;
@@ -65,5 +66,109 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
await coreNode.TryStopAsync();
}
}
+
+ [Fact]
+ public async Task TrustedNotifierNotifiesTxAsync()
+ {
+ var coreNode = await TestNodeBuilder.CreateAsync();
+ try
+ {
+ var rpc = coreNode.RpcClient;
+ await rpc.GenerateAsync(101);
+ var network = rpc.Network;
+
+ var dir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetMethodName());
+
+ var addr = new Key().PubKey.GetSegwitAddress(network);
+ var notifier = coreNode.TrustedNodeNotifyingBehavior;
+
+ var txNum = 10;
+ var txInvEventAwaiter = new EventsAwaiter<uint256>(
+ h => notifier.TransactionInv += h,
+ h => notifier.TransactionInv -= h,
+ txNum);
+
+ var txEventAwaiter = new EventsAwaiter<SmartTransaction>(
+ h => notifier.Transaction += h,
+ h => notifier.Transaction -= h,
+ txNum);
+
+ var txTasks = new List<Task<uint256>>();
+ var batch = rpc.PrepareBatch();
+ for (int i = 0; i < txNum; i++)
+ {
+ txTasks.Add(batch.SendToAddressAsync(addr, Money.Coins(1)));
+ }
+ var batchTask = batch.SendBatchAsync();
+
+ var aht = txInvEventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ var arrivedTxs = await txEventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ var arrivedHashes = await aht;
+
+ await batchTask;
+ var hashes = await Task.WhenAll(txTasks);
+ foreach (var hash in arrivedHashes)
+ {
+ Assert.Contains(hash, hashes);
+ }
+ foreach (var hash in arrivedTxs.Select(x => x.GetHash()))
+ {
+ Assert.Contains(hash, hashes);
+ }
+ }
+ finally
+ {
+ await coreNode.TryStopAsync();
+ }
+ }
+
+ //[Fact]
+ //public async Task TrustedNotifierNotifiesBlockAsync()
+ //{
+ // var coreNode = await TestNodeBuilder.CreateAsync();
+ // try
+ // {
+ // var rpc = coreNode.RpcClient;
+ // await rpc.GenerateAsync(101);
+ // var network = rpc.Network;
+
+ // var dir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetMethodName());
+
+ // var addr = new Key().PubKey.GetSegwitAddress(network);
+ // var notifier = coreNode.TrustedNodeNotifyingBehavior;
+
+ // var blockNum = 10;
+ // var blockInvEventAwaiter = new EventsAwaiter<uint256>(
+ // h => notifier.BlockInv += h,
+ // h => notifier.BlockInv -= h,
+ // blockNum);
+
+ // var blockEventAwaiter = new EventsAwaiter<Block>(
+ // h => notifier.Block += h,
+ // h => notifier.Block -= h,
+ // blockNum);
+
+ // var blockTask = rpc.GenerateToAddressAsync(blockNum, addr);
+
+ // var aht = blockInvEventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ // var arrivedBlocks = await blockEventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ // var arrivedHashes = await aht;
+
+ // var hashes = await blockTask;
+
+ // foreach (var hash in arrivedHashes)
+ // {
+ // Assert.Contains(hash, hashes);
+ // }
+ // foreach (var hash in arrivedBlocks.Select(x => x.GetHash()))
+ // {
+ // Assert.Contains(hash, hashes);
+ // }
+ // }
+ // finally
+ // {
+ // await coreNode.TryStopAsync();
+ // }
+ //}
}
}

MODIFY
WalletWasabi/BitcoinCore/CoreNode.cs
WalletWasabi/BitcoinCore/CoreNode.cs
@@ -18,6 +18,7 @@ using WalletWasabi.BitcoinCore.Endpointing;
using WalletWasabi.BitcoinCore.Processes;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
+using WalletWasabi.Services;
namespace WalletWasabi.BitcoinCore
{
@@ -31,6 +32,8 @@ namespace WalletWasabi.BitcoinCore
public Network Network { get; private set; }
public CoreConfig Config { get; private set; }
+ public TrustedNodeNotifyingBehavior TrustedNodeNotifyingBehavior => P2pNode.Behaviors.Find<TrustedNodeNotifyingBehavior>();
+ public Node P2pNode { get; private set; }
public static async Task<CoreNode> CreateAsync(CoreNodeParams coreNodeParams)
{
@@ -173,6 +176,19 @@ namespace WalletWasabi.BitcoinCore
Logger.LogInfo("Started Bitcoin Core.");
}
+ using var handshakeTimeout = new CancellationTokenSource();
+ handshakeTimeout.CancelAfter(TimeSpan.FromSeconds(21));
+ var nodeConnectionParameters = new NodeConnectionParameters()
+ {
+ UserAgent = $"/WasabiClient:{Constants.ClientVersion.ToString()}/",
+ ConnectCancellation = handshakeTimeout.Token,
+ IsRelay = true
+ };
+
+ nodeConnectionParameters.TemplateBehaviors.Add(new TrustedNodeNotifyingBehavior());
+ coreNode.P2pNode = await Node.ConnectAsync(coreNode.Network, coreNode.P2pEndPoint, nodeConnectionParameters).ConfigureAwait(false);
+ coreNode.P2pNode.VersionHandshake();
+
return coreNode;
}
}
@@ -193,7 +209,7 @@ namespace WalletWasabi.BitcoinCore
return version;
}
- public async Task<Node> CreateP2pNodeAsync()
+ public async Task<Node> CreateNewP2pNodeAsync()
{
return await Node.ConnectAsync(Network, P2pEndPoint).ConfigureAwait(false);
}
@@ -210,6 +226,7 @@ namespace WalletWasabi.BitcoinCore
/// <param name="onlyOwned">Only stop if this node owns the process.</param>
public async Task<bool> TryStopAsync(bool onlyOwned = true)
{
+ DisconnectDisposeNullP2pNode();
Exception exThrown = null;
if (Bridge != null)
{
@@ -236,5 +253,36 @@ namespace WalletWasabi.BitcoinCore
}
return false;
}
+
+ public void DisconnectDisposeNullP2pNode()
+ {
+ if (P2pNode != null)
+ {
+ try
+ {
+ P2pNode?.Disconnect();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogDebug(ex);
+ }
+ finally
+ {
+ try
+ {
+ P2pNode?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogDebug(ex);
+ }
+ finally
+ {
+ P2pNode = null;
+ Logger.LogInfo("P2p Bitcoin node is disconnected.");
+ }
+ }
+ }
+ }
}
}

MODIFY
WalletWasabi/Blockchain/Mempool/MempoolBehavior.cs
WalletWasabi/Blockchain/Mempool/MempoolBehavior.cs
@@ -39,19 +39,14 @@ namespace WalletWasabi.Blockchain.Mempool
if (message.Message.Payload is GetDataPayload getDataPayload)
{
await ProcessGetDataAsync(node, getDataPayload).ConfigureAwait(false);
- return;
}
-
- if (message.Message.Payload is TxPayload txPayload)
+ else if (message.Message.Payload is TxPayload txPayload)
{
ProcessTx(txPayload);
- return;
}
-
- if (message.Message.Payload is InvPayload invPayload)
+ else if (message.Message.Payload is InvPayload invPayload)
{
await ProcessInvAsync(node, invPayload).ConfigureAwait(false);
- return;
}
}
catch (OperationCanceledException ex)
@@ -151,6 +146,7 @@ namespace WalletWasabi.Blockchain.Mempool
private void ProcessTx(TxPayload payload)
{
Transaction transaction = payload.Object;
+ transaction.PrecomputeHash(false, true);
MempoolService.Process(transaction);
}

MODIFY
WalletWasabi/Services/TrustedNodeNotifyingBehavior.cs
WalletWasabi/Services/TrustedNodeNotifyingBehavior.cs
@@ -5,7 +5,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Logging;
+using WalletWasabi.Models;
namespace WalletWasabi.Services
{
@@ -15,7 +17,7 @@ namespace WalletWasabi.Services
public event EventHandler<uint256> BlockInv;
- public event EventHandler<Transaction> Transaction;
+ public event EventHandler<SmartTransaction> Transaction;
public event EventHandler<Block> Block;
@@ -37,37 +39,38 @@ namespace WalletWasabi.Services
{
try
{
- if (message.Message.Payload is InvPayload invPayload)
+ if (message.Message.Payload is TxPayload txPayload)
{
- var payload = new GetDataPayload();
+ Transaction?.Invoke(this, new SmartTransaction(txPayload.Object, Height.Mempool));
+ }
+ else if (message.Message.Payload is BlockPayload blockPayload)
+ {
+ Block?.Invoke(this, blockPayload.Object);
+ }
+ else if (message.Message.Payload is InvPayload invPayload)
+ {
+ var getDataPayload = new GetDataPayload();
foreach (var inv in invPayload.Inventory)
{
if (inv.Type.HasFlag(InventoryType.MSG_TX))
{
TransactionInv?.Invoke(this, inv.Hash);
- payload.Inventory.Add(inv);
+ getDataPayload.Inventory.Add(inv);
}
- else if (inv.Type.HasFlag(InventoryType.MSG_BLOCK))
+
+ if (inv.Type.HasFlag(InventoryType.MSG_BLOCK))
{
BlockInv?.Invoke(this, inv.Hash);
- payload.Inventory.Add(inv);
+ getDataPayload.Inventory.Add(inv);
}
}
- if (payload.Inventory.Any() && node.IsConnected)
+ if (getDataPayload.Inventory.Any() && node.IsConnected)
{
// ask for the whole transaction
- await node.SendMessageAsync(payload);
+ await node.SendMessageAsync(getDataPayload).ConfigureAwait(false);
}
}
- else if (message.Message.Payload is TxPayload txPayload)
- {
- Transaction?.Invoke(this, txPayload.Object);
- }
- else if (message.Message.Payload is BlockPayload blockPayload)
- {
- Block?.Invoke(this, blockPayload.Object);
- }
}
catch (OperationCanceledException ex)
{

Add TrustedNodeNotifyingBehavior to CoreNode