Ejemplo n.º 1
0
        public async Task CompleteWithCrashingPeers()
        {
            ImmutableArray <Block <DumbAction> > fixture =
                GenerateBlocks <DumbAction>(15).ToImmutableArray();
            var bc = new BlockCompletion <char, DumbAction>(_ => false, 5);

            bc.Demand(fixture.Select(b => b.Hash));

            BlockCompletion <char, DumbAction> .BlockFetcher blockFetcher =
                (peer, blockHashes, token) => new AsyncEnumerable <Block <DumbAction> >(async yield =>
            {
                // Peer A does crash and Peer B does respond.
                if (peer == 'A')
                {
                    throw new Exception("Peer A can't respond.");
                }

                foreach (Block <DumbAction> b in fixture)
                {
                    if (blockHashes.Contains(b.Hash))
                    {
                        await yield.ReturnAsync(b);
                    }
                }
            });

            Tuple <Block <DumbAction>, char>[] result =
                await AsyncEnumerable.ToArrayAsync(bc.Complete(new[] { 'A', 'B' }, blockFetcher));

            Assert.Equal(
                fixture.Select(b => Tuple.Create(b, 'B')).ToHashSet(),
                result.ToHashSet()
                );
        }
Ejemplo n.º 2
0
        public async Task CompleteWithBlockFetcherGivingWrongBlocks()
        {
            Block <DumbAction> genesis = TestUtils.MineGenesis <DumbAction>(),
                               demand  = TestUtils.MineNext(genesis),
                               wrong   = TestUtils.MineNext(genesis);

            _logger.Debug("Genesis: #{Index} {Hash}", genesis.Index, genesis.Hash);
            _logger.Debug("Demand:  #{Index} {Hash}", demand.Index, demand.Hash);
            _logger.Debug("Wrong:   #{Index} {Hash}", wrong.Index, wrong.Hash);
            var bc = new BlockCompletion <char, DumbAction>(
                ((IEquatable <HashDigest <SHA256> >)genesis.Hash).Equals,
                5
                );

            bc.Demand(demand.Hash);

            long counter = 0;

            BlockCompletion <char, DumbAction> .BlockFetcher wrongBlockFetcher =
                (peer, blockHashes, token) => new AsyncEnumerable <Block <DumbAction> >(async yield =>
            {
                // Provides a wrong block (i.e., not corresponding to the demand) at first call,
                // and then provide a proper block later calls.
                await yield.ReturnAsync(Interlocked.Read(ref counter) < 1 ? wrong : demand);
                Interlocked.Increment(ref counter);
            });

            Tuple <Block <DumbAction>, char>[] result =
                await AsyncEnumerable.ToArrayAsync(bc.Complete(new[] { 'A' }, wrongBlockFetcher));

            Assert.Equal(new[] { Tuple.Create(demand, 'A') }, result);
        }
Ejemplo n.º 3
0
        public async Task CompleteWithNonRespondingPeers()
        {
            ImmutableArray <Block <DumbAction> > fixture =
                GenerateBlocks <DumbAction>(15).ToImmutableArray();
            var bc = new BlockCompletion <char, DumbAction>(_ => false, 5);

            bc.Demand(fixture.Select(b => b.Hash));

            BlockCompletion <char, DumbAction> .BlockFetcher blockFetcher =
                (peer, blockHashes, token) => new AsyncEnumerable <Block <DumbAction> >(async yield =>
            {
                // Peer A does not respond and Peer B does respond.
                if (peer == 'A')
                {
                    while (true)
                    {
                        await Task.Delay(5000, yield.CancellationToken);
                    }
                }

                foreach (Block <DumbAction> b in fixture)
                {
                    if (blockHashes.Contains(b.Hash))
                    {
                        await yield.ReturnAsync(b);
                    }
                }
            });

            Tuple <Block <DumbAction>, char>[] result =
                await AsyncEnumerable.ToArrayAsync(
                    bc.Complete(
                        new[] { 'A', 'B' }, blockFetcher, millisecondsSingleSessionTimeout: 3000
                        )
                    );

            Assert.Equal(
                fixture.Select(b => Tuple.Create(b, 'B')).ToHashSet(),
                result.ToHashSet()
                );
        }
Ejemplo n.º 4
0
        public async Task PeerPool()
        {
            const int tasks = 50;
            ConcurrentDictionary <int, int> peers = new ConcurrentDictionary <int, int>(
                Enumerable.Range(0, 3).Select(peerId => new KeyValuePair <int, int>(peerId, 0))
                );
            var  concurrentWorkersLogs = new ConcurrentBag <int>();
            long done = 0;
            var  pool = new BlockCompletion <int, DumbAction> .PeerPool(peers.Keys);

            var random = new System.Random();

            Task[] spawns = Enumerable.Range(0, tasks).Select(i =>
            {
                int sleep = random.Next(5, 50);
                return(pool.SpawnAsync(async(peerId, cancellationToken) =>
                {
                    try
                    {
                        int counter;
                        do
                        {
                            counter = peers[peerId];
                        }while (!peers.TryUpdate(peerId, counter + 1, counter));

                        concurrentWorkersLogs.Add(
                            peers.Where(kv => kv.Key != peerId).Sum(kv => kv.Value) +
                            counter + 1
                            );

                        await Task.Delay(sleep);

                        do
                        {
                            counter = peers[peerId];
                        }while (!peers.TryUpdate(peerId, counter - 1, counter));
                    }
                    catch (Exception e)
                    {
                        _output.WriteLine(e.ToString());
                    }
                    finally
                    {
                        Interlocked.Increment(ref done);
                        _output.WriteLine($"Task {i} finished.");
                    }
                }));
            }).ToArray();

            _output.WriteLine("Wait spawned tasks to be finished...");
            await Task.WhenAll(spawns);

            _output.WriteLine("All spawned tasks finished; wait PeerPool to be finished...");
            await pool.WaitAll();

            _output.WriteLine("PeerPool finished.");

            Assert.Equal(tasks, Interlocked.Read(ref done));
            Assert.Equal(tasks, concurrentWorkersLogs.Count);
            foreach (int log in concurrentWorkersLogs)
            {
                Assert.InRange(log, 0, 3);
            }
        }
Ejemplo n.º 5
0
        public async Task Complete()
        {
            // 0, 1: Already existed blocks
            // 2, 3, 4,  5,  6: first chunk
            // 7, 8, 9, 10, 11: second chunk
            //   12,    13, 14: last chunk
            ImmutableArray <Block <DumbAction> > fixture =
                GenerateBlocks <DumbAction>(15).ToImmutableArray();

            // Blocks each block has:
            //   A: 0, 4, 8,  12
            //   B: 1, 5, 9,  13
            //   C: 2, 6, 10, 14
            //   D: 3, 7, 11
            char[] peers = { 'A', 'B', 'C', 'D' };
            ImmutableDictionary <char, ImmutableDictionary <HashDigest <SHA256>, Block <DumbAction> > >
            peerBlocks = peers.ToImmutableDictionary(
                p => p,
                p => fixture.Skip(p - 'A').Where((_, i) => i % 4 < 1).ToImmutableDictionary(
                    b => b.Hash,
                    b => b
                    )
                );

            const int initialHeight = 2;
            const int window        = 5;
            var       bc            = new BlockCompletion <char, DumbAction>(
                fixture.Take(initialHeight).Select(b => b.Hash).ToImmutableHashSet().Contains,
                window
                );
            ImmutableArray <HashDigest <SHA256> > initialDemands = fixture
                                                                   .Skip(initialHeight + 10)
                                                                   .Select(b => b.Hash)
                                                                   .ToImmutableArray();

            bc.Demand(initialDemands);
            _logger.Verbose("Initial demands: {0}", initialDemands);
            IAsyncEnumerable <Tuple <Block <DumbAction>, char> > rv = bc.Complete(
                new[] { 'A', 'B', 'C', 'D' },
                (peer, hashes, token) => new AsyncEnumerable <Block <DumbAction> >(async yield =>
            {
                var blocksPeerHas = peerBlocks[peer];
                var sent          = new HashSet <HashDigest <SHA256> >();
                foreach (HashDigest <SHA256> hash in hashes)
                {
                    if (blocksPeerHas.ContainsKey(hash))
                    {
                        Block <DumbAction> block = blocksPeerHas[hash];
                        await yield.ReturnAsync(block);
                        sent.Add(block.Hash);
                    }
                }

                _logger.Verbose("Peer {Peer} sent blocks: {SentBlockHashes}.", peer, sent);
            })
                );

            var downloadedBlocks = new HashSet <Block <DumbAction> >();
            var sourcePeers      = new HashSet <char>();
            await AsyncEnumerable.ForEachAsync(rv, pair =>
            {
                downloadedBlocks.Add(pair.Item1);
                sourcePeers.Add(pair.Item2);
            });

            Assert.Equal(fixture.Skip(2).ToHashSet(), downloadedBlocks);
            Assert.Subset(peers.ToHashSet(), sourcePeers);
        }
Ejemplo n.º 6
0
        public async void EnumerateChunks()
        {
            // 0, 1: Already existed blocks
            // 2, 3, 4,  5,  6: first chunk
            // 7, 8, 9, 10, 11: second chunk
            //   12,    13, 14: last chunk
            ImmutableArray <Block <DumbAction> > fixture =
                GenerateBlocks <DumbAction>(15).ToImmutableArray();
            const int initialHeight = 2;
            const int window        = 5;
            var       bc            = new BlockCompletion <int, DumbAction>(
                fixture.Take(initialHeight).Select(b => b.Hash).ToImmutableHashSet().Contains,
                window
                );
            var logs = new ConcurrentBag <(int, ImmutableArray <HashDigest <SHA256> >)>();
            var ev   = new AsyncAutoResetEvent(false);
            var bg   = Task.Run(async() =>
            {
                await Task.Delay(100);
                int i = 0;
                await AsyncEnumerable.ForEachAsync(bc.EnumerateChunks(), hashes =>
                {
                    ImmutableArray <HashDigest <SHA256> > hashesArray = hashes.ToImmutableArray();
                    logs.Add((i, hashesArray));
                    i++;

                    // To test dynamic demands
                    if (hashesArray.Contains(fixture[7].Hash))
                    {
                        bc.Demand(fixture[14].Hash);
                        bc.Demand(fixture[0].Hash);  // This should be ignored as it's existed.
                        bc.Demand(fixture[3].Hash);  // This should be ignored as it's satisfied.
                    }

                    ev.Set();
                    _logger.Verbose("Got a chunk of hashes: {0}", string.Join(", ", hashesArray));
                });
            });

            // Demand: 2, 3, 4, 5, 6
            bc.Demand(fixture.Skip(initialHeight).Take(5).Select(b => b.Hash));

            // Chunk: 2, 3, 4, 5, 6
            _logger.Verbose("Waiting demand #2-6...");

            // TODO change waiting condition
            await Task.Delay(1000);

            _logger.Verbose("Demand #2-6 processed.");
            var actual = new List <HashDigest <SHA256> >();

            while (logs.TryTake(out var log))
            {
                actual.AddRange(log.Item2);
            }

            Assert.Equal(fixture.Skip(initialHeight).Take(window).Select(b => b.Hash), actual);

            // Complete: 2, 3, 4, 5 (and no 6)
            for (int i = initialHeight; i < initialHeight + window - 1; i++)
            {
                bc.Satisfy(fixture[i]);
            }

            // Demand: 7, 8, 9, 10, 11, 12, 13 (and 14 <- 7 will be added soon)
            bc.Demand(fixture.Skip(initialHeight + window).Select(b => b.Hash));

            // Chunk: 7, 8, 9, 10, 11
            _logger.Verbose("Waiting demand #7-11...");
            // TODO change waiting condition
            await Task.Delay(1000);

            _logger.Verbose("Demand #7-11 processed.");

            actual = new List <HashDigest <SHA256> >();
            while (logs.TryTake(out var log))
            {
                actual.AddRange(log.Item2);
            }

            Assert.Equal(
                fixture.Skip(initialHeight + window).Take(window).Select(b => b.Hash),
                actual
                );

            // Complete: 6, 7, 8, 9, 10, 11
            for (int i = initialHeight + window - 1; i < initialHeight + window * 2; i++)
            {
                bc.Satisfy(fixture[i]);
            }

            // Chunk: 12, 13, 14
            _logger.Verbose("Waiting demand #12-14...");
            // TODO change waiting condition
            await Task.Delay(1000);

            _logger.Verbose("Demand #12-14 processed.");
            actual = new List <HashDigest <SHA256> >();
            while (logs.TryTake(out var log))
            {
                actual.AddRange(log.Item2);
            }

            Assert.Equal(
                fixture.Skip(initialHeight + window * 2).Select(b => b.Hash).ToImmutableHashSet(),
                actual.ToImmutableHashSet()
                );

            // Complete: 12, 13, 14
            for (int i = initialHeight + window * 2; i < fixture.Length; i++)
            {
                bc.Satisfy(fixture[i]);
            }

            await bg;
        }