Skip to content

Commit

Permalink
Set and send client IP address in an addr message if available
Browse files Browse the repository at this point in the history
  • Loading branch information
Coding-Enthusiast committed Dec 21, 2020
1 parent 0786b45 commit 1a35ad7
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 17 deletions.
33 changes: 33 additions & 0 deletions Src/Autarkysoft.Bitcoin/P2PNetwork/ClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
using Autarkysoft.Bitcoin.Encoders;
using Autarkysoft.Bitcoin.ImprovementProposals;
using Autarkysoft.Bitcoin.P2PNetwork.Messages;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
Expand Down Expand Up @@ -174,5 +177,35 @@ public void UpdateNodeAddrs(NetworkAddressWithTime[] nodeAddresses)
Storage.WriteAddrs(nodeAddresses);
}
}


/// <summary>
/// A list of IP addresses that other peers claimed are ours with the number of times each were received.
/// </summary>
public Dictionary<IPAddress, int> localIP = new Dictionary<IPAddress, int>(3);

/// <inheritdoc/>
public IPAddress GetMyIP()
{
if (localIP.Count > 0)
{
KeyValuePair<IPAddress, int> best = localIP.Aggregate((a, b) => a.Value > b.Value ? a : b);
if (best.Value > 3)
{
// at least 4 nodes have approved this IP
return best.Key;
}
}
return IPAddress.Loopback;
}

/// <inheritdoc/>
public void UpdateMyIP(IPAddress addr)
{
if (!IPAddress.IsLoopback(addr) && !localIP.TryAdd(addr, 0))
{
localIP[addr]++;
}
}
}
}
12 changes: 12 additions & 0 deletions Src/Autarkysoft.Bitcoin/P2PNetwork/IClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Autarkysoft.Bitcoin.Blockchain;
using Autarkysoft.Bitcoin.Blockchain.Transactions;
using Autarkysoft.Bitcoin.P2PNetwork.Messages;
using System.Net;
using System.Threading;

namespace Autarkysoft.Bitcoin.P2PNetwork
Expand Down Expand Up @@ -115,5 +116,16 @@ public interface IClientSettings
/// </summary>
/// <param name="nodeAddresses">List of timestamped nodes network addresses</param>
void UpdateNodeAddrs(NetworkAddressWithTime[] nodeAddresses);

/// <summary>
/// Returns best known IP address of this client (<see cref="IPAddress.Loopback"/> if nothing is found).
/// </summary>
/// <returns>Best known IP address</returns>
IPAddress GetMyIP();
/// <summary>
/// Updates this client's IP address.
/// </summary>
/// <param name="addr">IP address to use</param>
void UpdateMyIP(IPAddress addr);
}
}
12 changes: 12 additions & 0 deletions Src/Autarkysoft.Bitcoin/P2PNetwork/ReplyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using Autarkysoft.Bitcoin.Blockchain.Blocks;
using Autarkysoft.Bitcoin.Cryptography;
using Autarkysoft.Bitcoin.Encoders;
using Autarkysoft.Bitcoin.P2PNetwork.Messages;
using Autarkysoft.Bitcoin.P2PNetwork.Messages.MessagePayloads;
using System;
Expand Down Expand Up @@ -78,6 +79,17 @@ private Message[] GetSettingsMessages(Message extraMsg)
{
result.Add(new Message(new FeeFilterPayload(settings.MinTxRelayFee * 1000), settings.Network));
}

if (settings.Relay)
{
var myIp = settings.GetMyIP();
if (!IPAddress.IsLoopback(myIp))
{
uint time = (uint)UnixTimeStamp.GetEpochUtcNow();
var myAddr = new NetworkAddressWithTime(settings.Services, myIp, settings.Port, time);
result.Add(new Message(new AddrPayload(new NetworkAddressWithTime[1] { myAddr }), settings.Network));
}
}
}

return result.Count == 0 ? null : result.ToArray();
Expand Down
14 changes: 14 additions & 0 deletions Src/Tests/Bitcoin/P2PNetwork/MockClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Autarkysoft.Bitcoin.P2PNetwork;
using Autarkysoft.Bitcoin.P2PNetwork.Messages;
using System;
using System.Net;
using System.Threading;
using Xunit;

Expand Down Expand Up @@ -243,6 +244,19 @@ public void UpdateNodeAddrs(NetworkAddressWithTime[] nodeAddresses)
Assert.Equal(expectedStream.ToByteArray(), actualStream.ToByteArray());
}

internal IPAddress myIpToReturn;
public IPAddress GetMyIP()
{
Assert.NotNull(myIpToReturn);
return myIpToReturn;
}

internal IPAddress expUpdateAddr;
public void UpdateMyIP(IPAddress addr)
{
Assert.Equal(expUpdateAddr, addr);
}

#pragma warning restore CS0649 // Field is never assigned to
}
}
108 changes: 91 additions & 17 deletions Src/Tests/Bitcoin/P2PNetwork/ReplyManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ public static IEnumerable<object[]> GetVerackCases()
BlockHeader hdr = BlockHeaderTests.GetSampleBlockHeader();
var getHdrs = new Message(new GetHeadersPayload(mockProtoVer, new BlockHeader[] { hdr }, null), NetworkType.MainNet);


yield return new object[]
{
new MockNodeStatus() { _handShakeToReturn = HandShakeState.None, mediumViolation = true, updateTime = true },
Expand Down Expand Up @@ -560,7 +561,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
},
null
};
Expand All @@ -577,7 +578,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // No feefilter becaue of protocol version
_relay = true, // No feefilter becaue of protocol version, addr
myIpToReturn = IPAddress.Loopback // No addr
},
null
};
Expand All @@ -594,7 +596,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
},
null
};
Expand All @@ -612,7 +614,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // No feefilter becaue of protocol version
_relay = true, // No feefilter becaue of protocol version, addr
myIpToReturn = IPAddress.Loopback // No addr
},
new Message[] { ping }
};
Expand All @@ -630,7 +633,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // No feefilter becaue of protocol version
_relay = true, // No feefilter becaue of protocol version, addr
myIpToReturn = IPAddress.Loopback // No addr
},
new Message[] { ping }
};
Expand All @@ -648,7 +652,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // No feefilter becaue of protocol version
_relay = true, // No feefilter becaue of protocol version, addr
myIpToReturn = IPAddress.Loopback // No addr
},
new Message[] { ping, sendHdr }
};
Expand All @@ -666,7 +671,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // No feefilter becaue of protocol version
_relay = true, // No feefilter becaue of protocol version, addr
myIpToReturn = IPAddress.Loopback // No addr
},
new Message[] { ping, sendHdr }
};
Expand All @@ -684,7 +690,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
},
new Message[] { ping, sendHdr }
};
Expand All @@ -702,7 +708,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = false, // No sync (getheaders)
_relay = true, // feefilter
_relay = true, // feefilter, addr
myIpToReturn = IPAddress.Loopback, // No addr
_fee = feeRateSat
},
new Message[] { ping, sendHdr, feeFilter }
Expand All @@ -724,7 +731,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
},
null
};
Expand All @@ -742,7 +749,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand All @@ -765,7 +772,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand All @@ -789,7 +796,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders)
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand All @@ -813,7 +820,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders) => no sendheaders
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand All @@ -837,7 +844,7 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders) => no sendheaders
_relay = false, // No feefilter
_relay = false, // No feefilter, no addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand All @@ -861,7 +868,8 @@ public static IEnumerable<object[]> GetVerackCases()
{
_netType = NetworkType.MainNet,
_catchup = true, // Start sync (getheaders) => no sendheaders, no feefilter
_relay = true, // feefilter
_relay = true, // feefilter, addr
myIpToReturn = IPAddress.Loopback, // No addr
_protoVer = mockProtoVer,
_bchain = new MockBlockchain()
{
Expand Down Expand Up @@ -908,9 +916,75 @@ public void CheckVerackTest(MockNodeStatus ns, IClientSettings cs, Message[] exp
Assert.Null(ns._handShakeToSet);
}

public static IEnumerable<object[]> GetAddrCases()
{
yield return new object[] { IPAddress.Loopback, null };
yield return new object[] { IPAddress.IPv6Loopback, null };
yield return new object[]
{
IPAddress.Parse("1.2.3.4"),
new NetworkAddressWithTime(NodeServiceFlags.NodeBloom, IPAddress.Parse("1.2.3.4"), 11, 0)
};
}
[Theory]
[MemberData(nameof(GetAddrCases))]
public void GetSettingsMessages_Addr_Test(IPAddress ipToReturn, NetworkAddressWithTime expected)
{
var ns = new MockNodeStatus()
{
_handShakeToReturn = HandShakeState.ReceivedAndReplied,
_handShakeToSet = HandShakeState.Finished,
updateTime = true,
_protVer = Constants.P2PBip31ProtVer, // No ping, no feefilter, no sendheaders
};
var cs = new MockClientSettings()
{
_netType = NetworkType.MainNet,
_catchup = false, // No getheaders
_relay = true, // feefilter, addr
myIpToReturn = ipToReturn,
_services = NodeServiceFlags.NodeBloom,
_port = 11
};
var rep = new ReplyManager(ns, cs);
var msg = new Message(new VerackPayload(), NetworkType.MainNet);

Message[] actual = rep.GetReply(msg);
if (expected is null)
{
Assert.Null(actual);
}
else
{
Assert.Single(actual);
Assert.True(actual[0].TryGetPayloadType(out PayloadType actualPlType));
Assert.Equal(PayloadType.Addr, actualPlType);

var actualPL = new AddrPayload();
Assert.True(actualPL.TryDeserialize(new FastStreamReader(actual[0].PayloadData), out string error), error);
Assert.Single(actualPL.Addresses);
Assert.Equal(expected.NodeIP, actualPL.Addresses[0].NodeIP);
Assert.Equal(expected.NodePort, actualPL.Addresses[0].NodePort);
Assert.Equal(expected.NodeServices, actualPL.Addresses[0].NodeServices);
Assert.NotEqual(0U, actualPL.Addresses[0].Time);
}

// Mock will change the following bool to false if it were called.
Assert.False(ns.updateTime, "UpdateTime() was never called");

// Mock either doesn't have any h.s. to set or if it did set h.s. it was checked and then turned to null
Assert.Null(ns._handShakeToSet);

// Mock will change the following bool to false if it were called.
Assert.False(ns.updateTime, "UpdateTime() was never called");

// Mock either doesn't have any h.s. to set or if it did set h.s. it was checked and then turned to null
Assert.Null(ns._handShakeToSet);
}


public static IEnumerable<object[]> GetVersionCases()
{
int mockProtoVer = 527;
var cs = new MockClientSettings()
{
_protoVer = 123,
Expand Down

0 comments on commit 1a35ad7

Please sign in to comment.