public void AssignBlocksToPeersWithNodesWithDifferentChainsCorrectlyDistributesDownloadTasks() { int ourBlockCount = 5; // Create list of numbers ourBlockCount + 1 to 40 and shuffle it. Random rnd = new Random(); List <int> requiredBlockHeights = new List <int>(); for (int i = ourBlockCount + 1; i <= 40; i++) { requiredBlockHeights.Add(i); } requiredBlockHeights = requiredBlockHeights.OrderBy(a => rnd.Next()).ToList(); // Initialize node's peers. List <PullerDownloadAssignments.PeerInformation> availablePeersInformation = new List <PullerDownloadAssignments.PeerInformation>() { new PullerDownloadAssignments.PeerInformation() { PeerId = "A", QualityScore = 100, ChainHeight = 4, TasksAssignedCount = 0 }, new PullerDownloadAssignments.PeerInformation() { PeerId = "B", QualityScore = 100, ChainHeight = 20, TasksAssignedCount = 0 }, new PullerDownloadAssignments.PeerInformation() { PeerId = "C", QualityScore = 50, ChainHeight = 30, TasksAssignedCount = 0 }, new PullerDownloadAssignments.PeerInformation() { PeerId = "D", QualityScore = 150, ChainHeight = 40, TasksAssignedCount = 0 }, }; // Use the assignment strategy to assign tasks to peers. Dictionary <PullerDownloadAssignments.PeerInformation, List <int> > assignments = PullerDownloadAssignments.AssignBlocksToPeers(requiredBlockHeights, availablePeersInformation); // Check the assignment is valid per our requirements. int tasksAssigned = 0; Assert.Equal(availablePeersInformation.Count, assignments.Count); foreach (KeyValuePair <PullerDownloadAssignments.PeerInformation, List <int> > kvp in assignments) { PullerDownloadAssignments.PeerInformation peer = kvp.Key; List <int> assignedBlockHeights = kvp.Value; tasksAssigned += assignedBlockHeights.Count; switch ((string)peer.PeerId) { case "A": // Peer A should not get any work. Assert.Equal(0, assignedBlockHeights.Count); break; case "B": case "C": case "D": // Peers B and C should only get tasks to download blocks up to its chain height. // Peer D can be assigned anything. Assert.True((assignedBlockHeights.Count == 0) || (assignedBlockHeights.Max() <= peer.ChainHeight)); break; default: // This should never occur. Assert.True(false, "Invalid peer ID."); break; } } Assert.Equal(requiredBlockHeights.Count, tasksAssigned); }
public void AssignBlocksToPeersWithNodesWithLowQualityProtectsLowerHalfRequestsFromBeingAssignedToPoorQualityNodes() { Random rnd = new Random(); int ourBlockCount = rnd.Next(1000) + 1; int bestBlockCount = rnd.Next(1000) + ourBlockCount + 1; // Create list of numbers ourBlockCount + 1 to bestBlockCount and shuffle it. List <int> requiredBlockHeights = new List <int>(); for (int i = ourBlockCount + 1; i <= bestBlockCount; i++) { requiredBlockHeights.Add(i); } requiredBlockHeights = requiredBlockHeights.OrderBy(a => rnd.Next()).ToList(); int medianBlockHeight = requiredBlockHeights.Median(); // Initialize node's peers. We have 5 peers with median quality 100. // Thus peers A and E should not receive any work from the lower half of the requests. List <PullerDownloadAssignments.PeerInformation> availablePeersInformation = new List <PullerDownloadAssignments.PeerInformation>() { new PullerDownloadAssignments.PeerInformation() { PeerId = "A", QualityScore = 20, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }, new PullerDownloadAssignments.PeerInformation() { PeerId = "B", QualityScore = 100, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }, new PullerDownloadAssignments.PeerInformation() { PeerId = "C", QualityScore = 150, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }, new PullerDownloadAssignments.PeerInformation() { PeerId = "D", QualityScore = 120, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }, new PullerDownloadAssignments.PeerInformation() { PeerId = "E", QualityScore = 90, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }, }; // Use the assignment strategy to assign tasks to peers. Dictionary <PullerDownloadAssignments.PeerInformation, List <int> > assignments = PullerDownloadAssignments.AssignBlocksToPeers(requiredBlockHeights, availablePeersInformation); // Check the assignment is valid per our requirements. int tasksAssigned = 0; Assert.Equal(availablePeersInformation.Count, assignments.Count); foreach (KeyValuePair <PullerDownloadAssignments.PeerInformation, List <int> > kvp in assignments) { PullerDownloadAssignments.PeerInformation peer = kvp.Key; List <int> assignedBlockHeights = kvp.Value; tasksAssigned += assignedBlockHeights.Count; switch ((string)peer.PeerId) { case "A": case "E": // Peers A and E should not get work. Assert.True((assignedBlockHeights.Count == 0) || (assignedBlockHeights.Max() >= medianBlockHeight)); break; case "B": case "C": case "D": // PeerS B, C, D can be assigned anything. break; default: // This should never occur. Assert.True(false, "Invalid peer ID."); break; } } Assert.Equal(requiredBlockHeights.Count, tasksAssigned); }
public void AssignBlocksToPeersWithManyNodesWithLowQualityProtectsLowerHalfRequestsFromBeingAssignedToPoorQualityNodes() { Random rnd = new Random(); int ourBlockCount = rnd.Next(1000) + 1; int bestBlockCount = rnd.Next(1000) + ourBlockCount + 1; // Create list of numbers ourBlockCount + 1 to bestBlockCount and shuffle it. List <int> requiredBlockHeights = new List <int>(); for (int i = ourBlockCount + 1; i <= bestBlockCount; i++) { requiredBlockHeights.Add(i); } requiredBlockHeights = requiredBlockHeights.OrderBy(a => rnd.Next()).ToList(); int medianBlockHeight = requiredBlockHeights.Median(); // Randomly generate peers and count median quality. List <PullerDownloadAssignments.PeerInformation> availablePeersInformation = new List <PullerDownloadAssignments.PeerInformation>(); int peerCount = rnd.Next(1000) + 1; List <double> qualities = new List <double>(); for (int i = 0; i < peerCount; i++) { var peerInfo = new PullerDownloadAssignments.PeerInformation() { PeerId = i.ToString(), QualityScore = rnd.NextDouble() * (QualityScore.MaxScore - QualityScore.MinScore) + QualityScore.MinScore, ChainHeight = bestBlockCount, TasksAssignedCount = rnd.Next(1000) }; qualities.Add(peerInfo.QualityScore); availablePeersInformation.Add(peerInfo); } ; double qualityMedian = qualities.Median(); // Use the assignment strategy to assign tasks to peers. Dictionary <PullerDownloadAssignments.PeerInformation, List <int> > assignments = PullerDownloadAssignments.AssignBlocksToPeers(requiredBlockHeights, availablePeersInformation); // Check the assignment is valid per our requirements. int tasksAssigned = 0; Assert.Equal(availablePeersInformation.Count, assignments.Count); foreach (KeyValuePair <PullerDownloadAssignments.PeerInformation, List <int> > kvp in assignments) { PullerDownloadAssignments.PeerInformation peer = kvp.Key; List <int> assignedBlockHeights = kvp.Value; tasksAssigned += assignedBlockHeights.Count; // If the peer is poor, it must not get bottom half work assignment. if (peer.QualityScore < qualityMedian) { Assert.True((assignedBlockHeights.Count == 0) || (assignedBlockHeights.Max() >= medianBlockHeight)); } } Assert.Equal(requiredBlockHeights.Count, tasksAssigned); }
public void AssignBlocksToPeersLargeSampleCorrectlyDistributesDownloadTasks() { Random rnd = new Random(); int iterationCount = 1000; for (int iteration = 0; iteration < iterationCount; iteration++) { // Choose scenario for this iteration. int ourBlockCount = rnd.Next(1000); int bestChainBlockCount = ourBlockCount + rnd.Next(1000); int availablePeerCount = rnd.Next(100) + 1; List <int> requiredBlockHeights = new List <int>(); for (int i = ourBlockCount + 1; i <= bestChainBlockCount; i++) { requiredBlockHeights.Add(i); } requiredBlockHeights = requiredBlockHeights.OrderBy(a => rnd.Next()).ToList(); // Initialize node's peers. int maxPeerChainLength = 0; List <PullerDownloadAssignments.PeerInformation> availablePeersInformation = new List <PullerDownloadAssignments.PeerInformation>(); for (int peerIndex = 0; peerIndex < availablePeerCount; peerIndex++) { PullerDownloadAssignments.PeerInformation peerInfo = new PullerDownloadAssignments.PeerInformation() { ChainHeight = rnd.Next(bestChainBlockCount) + 1, PeerId = peerIndex, QualityScore = rnd.NextDouble() * (QualityScore.MaxScore - QualityScore.MinScore) + QualityScore.MinScore, TasksAssignedCount = rnd.Next(100) }; availablePeersInformation.Add(peerInfo); maxPeerChainLength = Math.Max(maxPeerChainLength, peerInfo.ChainHeight); } // Use the assignment strategy to assign tasks to peers. Dictionary <PullerDownloadAssignments.PeerInformation, List <int> > assignments = PullerDownloadAssignments.AssignBlocksToPeers(requiredBlockHeights, availablePeersInformation); // Check the assignment is valid per our requirements. int tasksAssigned = 0; Assert.Equal(availablePeersInformation.Count, assignments.Count); foreach (KeyValuePair <PullerDownloadAssignments.PeerInformation, List <int> > kvp in assignments) { PullerDownloadAssignments.PeerInformation peer = kvp.Key; List <int> assignedBlockHeights = kvp.Value; tasksAssigned += assignedBlockHeights.Count; // Peers with shorter chain should not get any work // other peers should not get any work exceeding their knowledge. if (peer.ChainHeight <= ourBlockCount) { Assert.Equal(0, assignedBlockHeights.Count); } else if (assignedBlockHeights.Count > 0) { Assert.True(assignedBlockHeights.Max() <= peer.ChainHeight); } } // Check that all tasks that could be assigned were assigned. int taskShouldAssign = requiredBlockHeights.Where(r => r <= maxPeerChainLength).Count(); Assert.Equal(taskShouldAssign, tasksAssigned); } }