/// <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 == SwarmService.LocalPeer.Id ? SwarmService.LocalPeer : SwarmService.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)); }
/// <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}"); }
/// <inheritdoc /> public async Task ProcessMessageAsync(PeerConnection connection, Stream stream, CancellationToken cancel = default) { 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; case MessageType.PutValue: break; case MessageType.GetValue: break; default: _log.Debug($"unknown {request.Type} from {connection.RemotePeer}"); // TODO: Should we close the stream? continue; } if (response == null) { continue; } Serializer.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128); await stream.FlushAsync(cancel).ConfigureAwait(false); } }
/// <summary> /// Advertise that we can provide the CID to the X closest peers /// of the CID. /// </summary> /// <param name="cid"> /// The CID to advertise.ipfs /// </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() => { var advertsNeeded = 4; var message = new DhtMessage { Type = MessageType.AddProvider, Key = cid.Hash.ToArray(), ProviderPeers = new[] { new DhtPeerMessage { Id = SwarmService.LocalPeer.Id.ToArray(), Addresses = SwarmService.LocalPeer.Addresses .Select(a => a.WithoutPeerId().ToArray()) .ToArray() } } }; var peers = RoutingTable .NearestPeers(cid.Hash) .Where(p => p != SwarmService.LocalPeer); foreach (var peer in peers) { try { await using (var stream = await SwarmService.DialAsync(peer, ToString())) { Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128); await stream.FlushAsync(); } if (--advertsNeeded == 0) { break; } } catch (Exception) { // eat it. This is fire and forget. } } }); }
/// <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?. var found = SwarmService.LocalPeer.Id == peerId ? SwarmService.LocalPeer : SwarmService.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.ToString()} closer peers"); } return(response); }
/// <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 var peer) ? peer : (Peer)null) .Where(p => p != null) .Where(p => p == remotePeer) .Where(p => p.Addresses.Any()); //.Where(p => SwarmService.IsAllowed(p)); foreach (var provider in providers) { SwarmService.RegisterPeer(provider); ContentRouter.Add(cid, provider.Id); } // There is no response for this request. return(null); }
/// <summary> /// Process a ping request. /// </summary> /// <remarks> /// Simply return the <paramref name="request"/>. /// </remarks> private static DhtMessage ProcessPing(DhtMessage request, DhtMessage response) { return(request); }