CHAPTER 7
We use a wrapper Dht class, which will become the main entry point for our peer, for interacting with other peers. The purposes of this class are:
Code Listing 54: The Dht Class
public class Dht public Contact Contact { get { return ourContact; } } protected BaseRouter router; protected IStorage originatorStorage; protected IProtocol protocol; protected Node node; { originatorStorage = storageFactory(); FinishInitialization(id, protocol, router); } { ourId = id; ourContact = new Contact(protocol, id); node = new Node(ourContact); node.Dht = this; node.BucketList.Dht = this; this.protocol = protocol; this.router = router; this.router.Node = node; this.router.Dht = this; } public void Store(ID key, string val) { TouchBucketWithKey(key); // We're storing to k closer contacts. originatorStorage.Set(key, val); StoreOnCloserContacts(key, val); } public (bool found, List<Contact> contacts, string val) FindValue(ID key) { TouchBucketWithKey(key); string ourVal; List<Contact> contactsQueried = new List<Contact>(); (bool found, List<Contact> contacts, string val) ret = (false, null, null); if (originatorStorage.TryGetValue(key, out ourVal)) { // Sort of odd that we are using the key-value store to find ret = (true, null, ourVal); } { var lookup = router.Lookup(key, router.RpcFindValue); if (lookup.found) { ret = (true, null, lookup.val); // Find the first close contact (other than the one the value var storeTo = lookup.contacts.Where(c => c != lookup.foundBy). if (storeTo != null) { int separatingNodes = GetSeparatingNodesCount(ourContact, storeTo); RpcError error = storeTo.Protocol.Store( HandleError(error, storeTo); } } } return ret; } } |
What exactly should the sender do when a value is not found? The Dht returns the nearest nodes, but given that the lookup failed to find the value, we know these nodes also do not have the value. As far as I’ve been able to determine, neither the spec nor a search of the web indicates what to do.
To get started, let’s just make sure we can set and get values in our local store with an empty bucket list.
Code Listing 55: LocalStoreFindValueTest
[TestMethod] public void LocalStoreFoundValueTest() { VirtualProtocol vp = new VirtualProtocol(); Dht dht = new Dht(ID.RandomID, vp, () => new VirtualStorage(), new Router()); vp.Node = dht.Router.Node; ID key = ID.RandomID; string val = "Test"; dht.Store(key, val); string retval = dht.FindValue(key).val; Assert.IsTrue(retval == val, "Expected to get back what we stored"); } |
This test creates a single contact and stores the value in that contact. We set up the IDs so that the contact’s ID is less (XOR metric) than our peer’s ID, and we use a key of ID.Zero to prevent further complexities when computing the distance. Most of the code here is to set up the conditions to make this test!
Code Listing 56: ValueStoredInCloserNodeTest
[TestMethod] public void ValueStoredInCloserNodeTest() { VirtualProtocol vp1 = new VirtualProtocol(); VirtualProtocol vp2 = new VirtualProtocol(); VirtualStorage store1 = new VirtualStorage(); VirtualStorage store2 = new VirtualStorage(); // Ensures that all nodes are closer, because ID.Max ^ n < ID.Max when n > 0. Dht dht = new Dht(ID.Max, vp1, new Router(), store1, store1, new VirtualStorage()); vp1.Node = dht.Router.Node; ID contactID = ID.Mid; // a closer contact. Contact otherContact = new Contact(vp2, contactID); Node otherNode = new Node(otherContact, store2); vp2.Node = otherNode;
// Add this other contact to our peer list. dht.Router.Node.BucketList.AddContact(otherContact); // We want an integer distance, not an XOR distance. ID key = ID.Zero; // Set the value in the other node, to be discovered by the lookup process. string val = "Test"; otherNode.SimpleStore(key, val); Assert.IsFalse(store1.Contains(key), Assert.IsTrue(store2.Contains(key), // Try and find the value, given our Dht knows about the other contact. string retval = dht.FindValue(key).val; Assert.IsTrue(retval == val, "Expected to get back what we stored"); } |
The method SimpleStore simply stores the value in the node’s storage—this method is available only in DEBUG mode for unit testing.
Code Listing 57: SimpleStore
#if DEBUG // For unit testing public void SimpleStore(ID key, string val) { storage.Set(key, val); } #endif |
We can change the setup of the IDs and verify that the we find the value in a farther node.
Code Listing 58: ValueStoredInFartherNodeTest
[TestMethod] public void ValueStoredInFartherNodeTest() { VirtualProtocol vp1 = new VirtualProtocol(); VirtualProtocol vp2 = new VirtualProtocol(); VirtualStorage store1 = new VirtualStorage(); VirtualStorage store2 = new VirtualStorage(); // Ensures that all nodes are closer, because ID.Max ^ n < ID.Max when n > 0. Dht dht = new Dht(ID.Zero, vp1, new Router(), store1, store1, vp1.Node = dht.Router.Node; ID contactID = ID.Max; // a farther contact. Contact otherContact = new Contact(vp2, contactID); Node otherNode = new Node(otherContact, store2); vp2.Node = otherNode; // Add this other contact to our peer list. dht.Router.Node.BucketList.AddContact(otherContact); // We want an integer distance, not an XOR distance. ID key = ID.One; // Set the value in the other node, to be discovered by the lookup process. string val = "Test"; otherNode.SimpleStore(key, val); Assert.IsFalse(store1.Contains(key), Assert.IsTrue(store2.Contains(key), // Try and find the value, given our Dht knows about the other contact. string retval = dht.FindValue(key).val; Assert.IsTrue(retval == val, "Expected to get back what we stored"); } |
Here we test that when we store a value to our peer, it also gets propagated to another peer that our peer knows about.
Code Listing 59: ValueStoredGetsPropagatedTest
[TestMethod] public void ValueStoredGetsPropagatedTest() { VirtualProtocol vp1 = new VirtualProtocol(); VirtualProtocol vp2 = new VirtualProtocol(); VirtualStorage store1 = new VirtualStorage(); VirtualStorage store2 = new VirtualStorage(); // Ensures that all nodes are closer, because ID.Max ^ n < ID.Max when n > 0. Dht dht = new Dht(ID.Max, vp1, new Router(), store1, store1, new VirtualStorage()); vp1.Node = dht.Router.Node; ID contactID = ID.Mid; // a closer contact. Contact otherContact = new Contact(vp2, contactID); Node otherNode = new Node(otherContact, store2); vp2.Node = otherNode; // Add this other contact to our peer list. dht.Router.Node.BucketList.AddContact(otherContact); // We want an integer distance, not an XOR distance. ID key = ID.Zero; string val = "Test"; Assert.IsFalse(store1.Contains(key), "Obviously we don't have the key-value yet."); Assert.IsFalse(store2.Contains(key), dht.Store(key, val); Assert.IsTrue(store1.Contains(key), Assert.IsTrue(store2.Contains(key), } |
This test verifies that, given three nodes (the first of which is us), where node 2 has the value, a get value also propagates to node 3 because a lookup was performed.
Code Listing 60: GetValuePropagatesToCloserNodeTest
[TestMethod] public void GetValuePropagatesToCloserNodeTest() { VirtualProtocol vp1 = new VirtualProtocol(); VirtualProtocol vp2 = new VirtualProtocol(); VirtualProtocol vp3 = new VirtualProtocol(); VirtualStorage store1 = new VirtualStorage(); VirtualStorage store2 = new VirtualStorage(); VirtualStorage store3 = new VirtualStorage(); VirtualStorage cache3 = new VirtualStorage(); // Ensures that all nodes are closer, because ID.Max ^ n < ID.Max when n > 0. Dht dht = new Dht(ID.Max, vp1, new Router(), store1, store1, new VirtualStorage()); vp1.Node = dht.Router.Node; // Setup node 2: ID contactID2 = ID.Mid; // a closer contact. Contact otherContact2 = new Contact(vp2, contactID2); Node otherNode2 = new Node(otherContact2, store2); vp2.Node = otherNode2; // Add the second contact to our peer list. dht.Router.Node.BucketList.AddContact(otherContact2); // Node 2 has the value. // We want an integer distance, not an XOR distance. ID key = ID.Zero; string val = "Test"; otherNode2.Storage.Set(key, val); // Setup node 3: ID contactID3 = ID.Zero.SetBit(158); // 01000.... -- a farther contact. Contact otherContact3 = new Contact(vp3, contactID3); Node otherNode3 = new Node(otherContact3, store3, cache3); vp3.Node = otherNode3; // Add the third contact to our peer list. dht.Router.Node.BucketList.AddContact(otherContact3); Assert.IsFalse(store1.Contains(key), "Obviously we don't have the key-value yet."); Assert.IsFalse(store3.Contains(key), var ret = dht.FindValue(key); Assert.IsTrue(ret.found, "Expected value to be found."); Assert.IsFalse(store3.Contains(key), "Key should not be in the republish store."); Assert.IsTrue(cache3.Contains(key), "Key should be in the cache store."); Assert.IsTrue(cache3.GetExpirationTimeSec(key.Value) == } |