public async Task ProcessGetProvidersMessage_HasCloserPeers() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { dht.RoutingTable.Add(other); Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; var request = new DhtMessage { Type = MessageType.GetProviders, Key = cid.Hash.ToArray() }; var response = dht.ProcessGetProviders(request, new DhtMessage()); Assert.AreNotEqual(0, response.CloserPeers.Length); } finally { await dht.StopAsync(); } }
/// <summary> /// Process a get provider request. /// </summary> public DhtMessage ProcessGetProviders(DhtMessage request, DhtMessage response) { // Find providers for the content. var cid = new Cid { Hash = new MultiHash(request.Key) }; response.ProviderPeers = ContentRouter .Get(cid) .Select(pid => { var peer = (pid == Swarm.LocalPeer.Id) ? Swarm.LocalPeer : Swarm.RegisterPeer(new Peer { Id = pid }); return(new DhtPeerMessage { Id = peer.Id.ToArray(), Addresses = peer.Addresses.Select(a => a.WithoutPeerId().ToArray()).ToArray() }); }) .Take(20) .ToArray(); // Also return the closest peers return(ProcessFindNode(request, response)); }
public async Task ProcessFindNodeMessage_Self() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { var request = new DhtMessage { Type = MessageType.FindNode, Key = self.Id.ToArray() }; var response = dht.ProcessFindNode(request, new DhtMessage()); Assert.AreEqual(1, response.CloserPeers.Length); var ok = response.CloserPeers[0].TryToPeer(out Peer found); Assert.IsTrue(ok); Assert.AreEqual(self, found); } finally { await dht.StopAsync(); } }
public async Task ProcessGetProvidersMessage_HasProvider() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { swarm.RegisterPeer(other); Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; dht.ContentRouter.Add(cid, other.Id); var request = new DhtMessage { Type = MessageType.GetProviders, Key = cid.Hash.ToArray() }; var response = dht.ProcessGetProviders(request, new DhtMessage()); Assert.AreEqual(1, response.ProviderPeers.Length); response.ProviderPeers[0].TryToPeer(out Peer found); Assert.AreEqual(other, found); Assert.AreNotEqual(0, found.Addresses.Count()); } finally { await dht.StopAsync(); } }
public async Task ProcessFindNodeMessage_InSwarm() { var swarm = new Swarm { LocalPeer = self }; var other = await swarm.RegisterPeerAsync("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { dht.RoutingTable.Add(other); var request = new DhtMessage { Type = MessageType.FindNode, Key = other.Id.ToArray() }; var response = dht.ProcessFindNode(request, new DhtMessage()); Assert.AreEqual(1, response.CloserPeers.Length); var ok = response.CloserPeers[0].TryToPeer(out Peer found); Assert.IsTrue(ok); Assert.AreEqual(other, found); CollectionAssert.AreEqual( other.Addresses.Select(a => a.WithoutPeerId()).ToArray(), found.Addresses.Select(a => a.WithoutPeerId()).ToArray()); } finally { await dht.StopAsync(); } }
public async Task ProcessFindNodeMessage_NoOtherPeers() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { var request = new DhtMessage { Type = MessageType.FindNode, Key = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h").ToArray() }; var response = dht.ProcessFindNode(request, new DhtMessage()); Assert.AreEqual(0, response.CloserPeers.Length); } finally { await dht.StopAsync(); } }
public async Task ProcessFindNodeMessage_InRoutingTable() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { dht.RoutingTable.Add(other); var request = new DhtMessage { Type = MessageType.FindNode, Key = other.Id.ToArray() }; var response = dht.ProcessFindNode(request, new DhtMessage()); Assert.AreEqual(1, response.CloserPeers.Length); var ok = response.CloserPeers[0].TryToPeer(out Peer found); Assert.IsTrue(ok); Assert.AreEqual(other, found); CollectionAssert.AreEqual(other.Addresses.ToArray(), found.Addresses.Select(a => a.WithoutPeerId()).ToArray()); } finally { await dht.StopAsync(); } }
/// <summary> /// Starts the distributed query. /// </summary> /// <param name="cancel"> /// Is used to stop the task. When cancelled, the <see cref="TaskCanceledException"/> is raised. /// </param> /// <returns> /// A task that represents the asynchronous operation. /// </returns> public async Task RunAsync(CancellationToken cancel) { log.Debug($"Q{Id} run {QueryType} {QueryKey}"); runningQuery = CancellationTokenSource.CreateLinkedTokenSource(cancel); Dht.Stopped += OnDhtStopped; queryMessage = new DhtMessage { Type = QueryType, Key = QueryKey?.ToArray(), }; var tasks = Enumerable .Range(1, ConcurrencyLevel) .Select(i => { var id = i; return(AskAsync(id)); }); try { await Task.WhenAll(tasks).ConfigureAwait(false); } catch (Exception) { // eat it } finally { Dht.Stopped -= OnDhtStopped; } log.Debug($"Q{Id} found {Answers.Count} answers, visited {visited.Count} peers, failed {failedConnects}"); }
/// <summary> /// Process an add provider request. /// </summary> public DhtMessage ProcessAddProvider(Peer remotePeer, DhtMessage request, DhtMessage response) { if (request.ProviderPeers == null) { return(null); } Cid cid; try { cid = new Cid { Hash = new MultiHash(request.Key) }; } catch (Exception) { log.Error($"Bad AddProvider request key {request.Key.ToHexString()}"); return(null); } var providers = request.ProviderPeers .Select(p => p.TryToPeer(out Peer peer) ? peer : (Peer)null) .Where(p => p != null) .Where(p => p == remotePeer) .Where(p => p.Addresses.Count() > 0); foreach (var provider in providers) { Swarm.RegisterPeer(provider); ContentRouter.Add(cid, provider.Id); } ; // There is no response for this request. return(null); }
public async Task ProcessFindNodeMessage_BadNodeId() { var swarm = new Swarm { LocalPeer = self }; swarm.RegisterPeerAddress("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1a"); swarm.RegisterPeerAddress("/ip4/127.0.0.2/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1b"); swarm.RegisterPeerAddress("/ip4/127.0.0.3/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1c"); swarm.RegisterPeerAddress("/ip4/127.0.0.4/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1d"); swarm.RegisterPeerAddress("/ip4/127.0.0.5/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1e"); var dht = new Dht1 { Swarm = swarm, CloserPeerCount = 3 }; await dht.StartAsync(); try { dht.RoutingTable.Add(other); var request = new DhtMessage { Type = MessageType.FindNode, Key = new byte[] { 0xFF, 1, 2, 3 } }; var response = dht.ProcessFindNode(request, new DhtMessage()); Assert.AreEqual(3, response.CloserPeers.Length); } finally { await dht.StopAsync(); } }
/// <summary> /// Process a find node request. /// </summary> public DhtMessage ProcessFindNode(DhtMessage request, DhtMessage response) { // Some random walkers generate a random Key that is not hashed. MultiHash peerId; try { peerId = new MultiHash(request.Key); } catch (Exception) { log.Error($"Bad FindNode request key {request.Key.ToHexString()}"); peerId = MultiHash.ComputeHash(request.Key); } // Do we know the peer?. Peer found = null; if (Swarm.LocalPeer.Id == peerId) { found = Swarm.LocalPeer; } else { found = Swarm.KnownPeers.FirstOrDefault(p => p.Id == peerId); } // Find the closer peers. var closerPeers = new List <Peer>(); if (found != null) { closerPeers.Add(found); } else { closerPeers.AddRange(RoutingTable.NearestPeers(peerId).Take(CloserPeerCount)); } // Build the response. response.CloserPeers = closerPeers .Select(peer => new DhtPeerMessage { Id = peer.Id.ToArray(), Addresses = peer.Addresses.Select(a => a.WithoutPeerId().ToArray()).ToArray() }) .ToArray(); if (log.IsDebugEnabled) { log.Debug($"returning {response.CloserPeers.Length} closer peers"); } return(response); }
/// <inheritdoc /> public async Task ProcessMessageAsync(PeerConnection connection, Stream stream, CancellationToken cancel = default(CancellationToken)) { while (true) { var request = await ProtoBufHelper.ReadMessageAsync <DhtMessage>(stream, cancel); log.Debug($"got message from {connection.RemotePeer}"); var response = new DhtMessage(); // TODO: process the request ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128); await stream.FlushAsync(cancel); } }
/// <summary> /// Advertise that we can provide the CID to the X closest peers /// of the CID. /// </summary> /// <param name="cid"> /// The CID to advertise. /// </param> /// <remarks> /// This starts a background process to send the AddProvider message /// to the 4 closest peers to the <paramref name="cid"/>. /// </remarks> public void Advertise(Cid cid) { _ = Task.Run(async() => { int advertsNeeded = 4; var message = new DhtMessage { Type = MessageType.AddProvider, Key = cid.Hash.ToArray(), ProviderPeers = new DhtPeerMessage[] { new DhtPeerMessage { Id = Swarm.LocalPeer.Id.ToArray(), Addresses = Swarm.LocalPeer.Addresses .Select(a => a.WithoutPeerId().ToArray()) .ToArray() } } }; var peers = RoutingTable .NearestPeers(cid.Hash) .Where(p => p != Swarm.LocalPeer); foreach (var peer in peers) { try { using (var stream = await Swarm.DialAsync(peer, this.ToString())) { ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128); await stream.FlushAsync(); } if (--advertsNeeded == 0) { break; } } catch (Exception) { // eat it. This is fire and forget. } } }); }
/// <inheritdoc /> public async Task ProcessMessageAsync(PeerConnection connection, Stream stream, CancellationToken cancel = default(CancellationToken)) { while (true) { var request = await ProtoBufHelper.ReadMessageAsync <DhtMessage>(stream, cancel).ConfigureAwait(false); log.Debug($"got {request.Type} from {connection.RemotePeer}"); var response = new DhtMessage { Type = request.Type, ClusterLevelRaw = request.ClusterLevelRaw }; switch (request.Type) { case MessageType.Ping: response = ProcessPing(request, response); break; case MessageType.FindNode: response = ProcessFindNode(request, response); break; case MessageType.GetProviders: response = ProcessGetProviders(request, response); break; case MessageType.AddProvider: response = ProcessAddProvider(connection.RemotePeer, request, response); break; default: log.Debug($"unknown {request.Type} from {connection.RemotePeer}"); // TODO: Should we close the stream? continue; } if (response != null) { ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128); await stream.FlushAsync(cancel).ConfigureAwait(false); } } }
public async Task ProcessAddProviderMessage() { var swarm = new Swarm { LocalPeer = self }; var dht = new Dht1 { Swarm = swarm }; await dht.StartAsync(); try { Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; var request = new DhtMessage { Type = MessageType.AddProvider, Key = cid.Hash.ToArray(), ProviderPeers = new DhtPeerMessage[] { new DhtPeerMessage { Id = other.Id.ToArray(), Addresses = other.Addresses.Select(a => a.ToArray()).ToArray() } } }; var response = dht.ProcessAddProvider(other, request, new DhtMessage()); Assert.IsNull(response); var providers = dht.ContentRouter.Get(cid).ToArray(); Assert.AreEqual(1, providers.Length); Assert.AreEqual(other.Id, providers[0]); var provider = swarm.KnownPeers.Single(p => p == other); Assert.AreNotEqual(0, provider.Addresses.Count()); } finally { await dht.StopAsync(); } }
/// <summary> /// Process a ping request. /// </summary> /// <remarks> /// Simply return the <paramref name="request"/>. /// </remarks> DhtMessage ProcessPing(DhtMessage request, DhtMessage response) { return(request); }