/// <summary> /// Add a contact if possible, based on the algorithm described /// in sections 2.2, 2.4 and 4.2 /// </summary> public void AddContact(Contact contact) { Validate.IsFalse<OurNodeCannotBeAContactException>(ourID == contact.ID, "Cannot add ourselves as a contact!"); contact.Touch(); // Update the LastSeen to now. lock (this) { KBucket kbucket = GetKBucket(contact.ID); if (kbucket.Contains(contact.ID)) { // Replace the existing contact, updating the network info and LastSeen timestamp. kbucket.UpdateContactInfo(contact); } else if (kbucket.IsBucketFull) { if (CanSplit(kbucket)) { // Split the bucket and try again. (KBucket k1, KBucket k2) = kbucket.Split(); int idx = GetKBucketIndex(contact.ID); buckets[idx] = k1; buckets.Insert(idx + 1, k2); buckets[idx].Touch(); buckets[idx + 1].Touch(); AddContact(contact); } else { Contact lastSeenContact = kbucket.Contacts.OrderBy(c => c.LastSeen).First(); RpcError error = lastSeenContact.Protocol.Ping(ourContact); if (error.HasError) { // Null continuation is used because unit tests may not initialize a DHT. dht?.DelayEviction(lastSeenContact, contact); } else { // Still can't add the contact, so put it into the pending list. dht?.AddToPending(contact); } } } else { // Bucket isn't full, so just add the contact. kbucket.AddContact(contact); dht?.ContactAddedToBucket(kbucket, contact); } } }