Exemple #1
0
 /// <summary>
 /// Checks whether the bucket contains given <paramref name="peer"/>.
 /// </summary>
 /// <param name="peer">The <see cref="BoundPeer"/> to check.</param>
 /// <returns><c>true</c> if the bucket contains <paramref name="peer"/>,
 /// <c>false</c> otherwise.</returns>
 public bool Contains(BoundPeer peer)
 {
     return(_peerStates.Contains(peer.Address));
 }
Exemple #2
0
 public PingTimeoutException(BoundPeer target, string message)
     : base(message)
 {
     Target = target;
 }
Exemple #3
0
        internal KBucket BucketOf(BoundPeer peer)
        {
            int index = GetBucketIndexOf(peer.Address);

            return(_buckets[index]);
        }
Exemple #4
0
        public KBucket BucketOf(BoundPeer peer)
        {
            int index = GetBucketIndexOf(peer);

            return(_buckets[index]);
        }
 /// <summary>
 /// Determines whether the <see cref="RoutingTable"/> contains the specified key.
 /// </summary>
 /// <param name="peer">Key to locate in the <see cref="RoutingTable"/>.</param>
 /// <returns><see langword="true"/> if the <see cref="RoutingTable" /> contains
 /// an element with the specified key; otherwise, <see langword="false"/>.</returns>
 public bool Contains(BoundPeer peer)
 {
     return(BucketOf(peer).Contains(peer));
 }
Exemple #6
0
 private void RemovePeer(BoundPeer peer)
 {
     _logger.Debug("Removing peer {Peer} from table.", peer);
     _routing.RemovePeer(peer);
 }
Exemple #7
0
 private async Task RemovePeerAsync(BoundPeer peer, CancellationToken cancellationToken)
 {
     _logger.Debug("Removing peer [{peer}] from table.", peer);
     await _routing.RemovePeerAsync(peer);
 }
Exemple #8
0
        public bool Contains(BoundPeer peer)
        {
            int index = GetBucketIndexOf(peer);

            return(_buckets[index].Contains(peer));
        }
Exemple #9
0
 private void RemovePeer(BoundPeer peer)
 {
     _table.RemovePeer(peer);
 }
        /// <inheritdoc/>
        public Message Decode(
            NetMQMessage encoded,
            bool reply)
        {
            if (encoded.FrameCount == 0)
            {
                throw new ArgumentException("Can't parse empty NetMQMessage.");
            }

            // (reply == true)            [version, type, peer, timestamp, sign, frames...]
            // (reply == false) [identity, version, type, peer, timestamp, sign, frames...]
            NetMQFrame[] remains = reply ? encoded.ToArray() : encoded.Skip(1).ToArray();

            var versionToken = remains[(int)Message.MessageFrame.Version].ConvertToString();

            AppProtocolVersion remoteVersion = AppProtocolVersion.FromToken(versionToken);
            Peer remotePeer;
            var  dictionary =
                (Bencodex.Types.Dictionary)_codec.Decode(
                    remains[(int)Message.MessageFrame.Peer].ToByteArray());

            try
            {
                remotePeer = new BoundPeer(dictionary);
            }
            catch (KeyNotFoundException)
            {
                remotePeer = new Peer(dictionary);
            }

            _messageValidator.ValidateAppProtocolVersion(
                remotePeer,
                reply ? new byte[] { } : encoded[0].ToByteArray(),
                remoteVersion);

            var type =
                (Message.MessageType)remains[(int)Message.MessageFrame.Type].ConvertToInt32();
            var ticks       = remains[(int)Message.MessageFrame.Timestamp].ConvertToInt64();
            var timestamp   = new DateTimeOffset(ticks, TimeSpan.Zero);
            var currentTime = DateTimeOffset.UtcNow;

            _messageValidator.ValidateTimestamp(remotePeer, currentTime, timestamp);

            byte[] signature = remains[(int)Message.MessageFrame.Sign].ToByteArray();

            NetMQFrame[] body = remains.Skip(Message.CommonFrames)
                                .ToArray();

            Message message = CreateMessage(
                type,
                body.Select(frame => frame.ToByteArray()).ToArray());

            message.Version   = remoteVersion;
            message.Remote    = remotePeer;
            message.Timestamp = timestamp;

            var headerWithoutSign = new[]
            {
                remains[(int)Message.MessageFrame.Version],
                remains[(int)Message.MessageFrame.Type],
                remains[(int)Message.MessageFrame.Peer],
                remains[(int)Message.MessageFrame.Timestamp],
            };

            var messageToVerify = headerWithoutSign.Concat(body).ToByteArray();

            if (!remotePeer.PublicKey.Verify(messageToVerify, signature))
            {
                throw new InvalidMessageSignatureException(
                          "The signature of an encoded message is invalid.",
                          remotePeer,
                          remotePeer.PublicKey,
                          messageToVerify,
                          signature);
            }

            if (!reply)
            {
                message.Identity = encoded[0].Buffer.ToArray();
            }

            return(message);
        }
Exemple #11
0
        public KBucket BucketOf(BoundPeer peer)
        {
            int index = GetBucketIndexOf(peer);

            return(BucketOf(index));
        }
Exemple #12
0
        internal KBucket BucketOf(BoundPeer peer)
        {
            int index = GetBucketIndexOf(peer.Address);

            return(BucketOf(index));
        }
Exemple #13
0
 public PingTimeoutException(BoundPeer target)
     : base()
 {
     Target = target;
 }
Exemple #14
0
 public PingTimeoutException(BoundPeer target, string message, Exception innerException)
     : base(message, innerException)
 {
     Target = target;
 }
Exemple #15
0
        private Message CreateMessage(Message.MessageType type)
        {
            var privateKey = new PrivateKey();
            var boundPeer  = new BoundPeer(privateKey.PublicKey, new DnsEndPoint("localhost", 1000));
            IBlockPolicy <DumbAction> policy = new BlockPolicy <DumbAction>();
            BlockChain <DumbAction>   chain  = MakeBlockChain(
                policy,
                new MemoryStore(),
                new TrieStateStore(new MemoryKeyValueStore())
                );
            var codec = new Codec();
            Block <DumbAction> genesis = chain.Genesis;
            var transaction            = chain.MakeTransaction(privateKey, new DumbAction[] { });

            switch (type)
            {
            case Message.MessageType.Ping:
                return(new Ping());

            case Message.MessageType.Pong:
                return(new Pong());

            case Message.MessageType.GetBlockHashes:
                return(new GetBlockHashes(chain.GetBlockLocator(), genesis.Hash));

            case Message.MessageType.TxIds:
                return(new TxIds(new[] { transaction.Id }));

            case Message.MessageType.GetBlocks:
                return(new GetBlocks(new[] { genesis.Hash }, 10));

            case Message.MessageType.GetTxs:
                return(new GetTxs(new[] { transaction.Id }));

            case Message.MessageType.Blocks:
                return(new Libplanet.Net.Messages.Blocks(
                           new[] { codec.Encode(genesis.MarshalBlock()) }));

            case Message.MessageType.Tx:
                return(new Libplanet.Net.Messages.Tx(transaction.Serialize(true)));

            case Message.MessageType.FindNeighbors:
                return(new FindNeighbors(privateKey.ToAddress()));

            case Message.MessageType.Neighbors:
                return(new Neighbors(new[] { boundPeer }));

            case Message.MessageType.BlockHeaderMessage:
                return(new BlockHeaderMessage(genesis.Hash, genesis.Header));

            case Message.MessageType.BlockHashes:
                return(new BlockHashes(0, new[] { genesis.Hash }));

            case Message.MessageType.GetChainStatus:
                return(new GetChainStatus());

            case Message.MessageType.ChainStatus:
                return(new ChainStatus(
                           0,
                           genesis.Hash,
                           chain.Tip.Index,
                           chain.Tip.Hash,
                           chain.Tip.TotalDifficulty));

            case Message.MessageType.DifferentVersion:
                return(new DifferentVersion());

            default:
                throw new Exception($"Cannot create a message of invalid type {type}");
            }
        }
 internal static string ToNetMQAddress(this BoundPeer peer)
 {
     return($"tcp://{peer.EndPoint.Host}:{peer.EndPoint.Port}");
 }
Exemple #17
0
        /// <inheritdoc/>
        public Message Decode(
            NetMQMessage encoded,
            bool reply,
            Action <byte[], Peer, AppProtocolVersion> appProtocolVersionValidator)
        {
            if (encoded.FrameCount == 0)
            {
                throw new ArgumentException("Can't parse empty NetMQMessage.");
            }

            // (reply == true)            [version, type, peer, timestamp, sign, frames...]
            // (reply == false) [identity, version, type, peer, timestamp, sign, frames...]
            NetMQFrame[] remains = reply ? encoded.ToArray() : encoded.Skip(1).ToArray();

            var versionToken = remains[(int)Message.MessageFrame.Version].ConvertToString();

            AppProtocolVersion remoteVersion = AppProtocolVersion.FromToken(versionToken);
            Peer remotePeer;
            var  dictionary =
                (Bencodex.Types.Dictionary)_codec.Decode(
                    remains[(int)Message.MessageFrame.Peer].ToByteArray());

            try
            {
                remotePeer = new BoundPeer(dictionary);
            }
            catch (KeyNotFoundException)
            {
                remotePeer = new Peer(dictionary);
            }

            appProtocolVersionValidator(
                reply ? new byte[] { } : encoded[0].ToByteArray(),
                remotePeer,
                remoteVersion);

            var type =
                (Message.MessageType)remains[(int)Message.MessageFrame.Type].ConvertToInt32();
            var ticks     = remains[(int)Message.MessageFrame.Timestamp].ConvertToInt64();
            var timestamp = new DateTimeOffset(ticks, TimeSpan.Zero);

            var currentTime = DateTimeOffset.UtcNow;

            if (_messageTimestampBuffer is TimeSpan timestampBuffer &&
                (currentTime - timestamp).Duration() > timestampBuffer)
            {
                var msg = $"Received message is invalid, created at " +
                          $"{timestamp.ToString(TimestampFormat, CultureInfo.InvariantCulture)} " +
                          $"but designated lifetime is {timestampBuffer} and " +
                          $"the current datetime offset is " +
                          $"{currentTime.ToString(TimestampFormat, CultureInfo.InvariantCulture)}.";
                throw new InvalidMessageTimestampException(
                          msg, timestamp, _messageTimestampBuffer, currentTime);
            }

            byte[] signature = remains[(int)Message.MessageFrame.Sign].ToByteArray();

            NetMQFrame[] body = remains.Skip(Message.CommonFrames)
                                .ToArray();

            Message message = CreateMessage(
                type,
                body.Select(frame => frame.ToByteArray()).ToArray());

            message.Version   = remoteVersion;
            message.Remote    = remotePeer;
            message.Timestamp = timestamp;

            var headerWithoutSign = new[]
            {
                remains[(int)Message.MessageFrame.Version],
                remains[(int)Message.MessageFrame.Type],
                remains[(int)Message.MessageFrame.Peer],
                remains[(int)Message.MessageFrame.Timestamp],
            };

            var messageForVerify = headerWithoutSign.Concat(body).ToArray();

            if (!remotePeer.PublicKey.Verify(messageForVerify.ToByteArray(), signature))
            {
                throw new InvalidMessageSignatureException(
                          "The message signature is invalid", message);
            }

            if (!reply)
            {
                message.Identity = encoded[0].Buffer.ToArray();
            }

            return(message);
        }
        public static async Task <AppProtocolVersion> QueryAppProtocolVersionTcp(
            this BoundPeer peer,
            TimeSpan?timeout = null,
            CancellationToken cancellationToken = default
            )
        {
            using var client = new TcpClient();
            try
            {
                await client.ConnectAsync(peer.EndPoint.Host, peer.EndPoint.Port);
            }
            catch (SocketException)
            {
                throw new TimeoutException("Cannot find peer.");
            }

            client.ReceiveTimeout = timeout?.Milliseconds ?? 0;
            using var stream      = client.GetStream();
            var key  = new PrivateKey();
            var ping = new Ping
            {
                Identity = Guid.NewGuid().ToByteArray(),
            };
            var messageCodec = new TcpMessageCodec();

            byte[] serialized = messageCodec.Encode(
                ping,
                key,
                peer,
                DateTimeOffset.UtcNow,
                default);
            int length = serialized.Length;
            var buffer = new byte[TcpTransport.MagicCookie.Length + sizeof(int) + length];

            TcpTransport.MagicCookie.CopyTo(buffer, 0);
            BitConverter.GetBytes(length).CopyTo(buffer, TcpTransport.MagicCookie.Length);
            serialized.CopyTo(buffer, TcpTransport.MagicCookie.Length + sizeof(int));
            await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);

            buffer = new byte[1000000];
            int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);

            var magicCookieBuffer = new byte[TcpTransport.MagicCookie.Length];

            Array.Copy(buffer, 0, magicCookieBuffer, 0, TcpTransport.MagicCookie.Length);
            var sizeBuffer = new byte[sizeof(int)];

            Array.Copy(buffer, TcpTransport.MagicCookie.Length, sizeBuffer, 0, sizeof(int));
            length = BitConverter.ToInt32(sizeBuffer, 0);
            var contentBuffer = new byte[length];

            Array.Copy(
                buffer,
                TcpTransport.MagicCookie.Length + sizeof(int),
                contentBuffer,
                0,
                length);

            // length of identity
            Array.Copy(contentBuffer, 4, sizeBuffer, 0, 4);
            length = BitConverter.ToInt32(sizeBuffer, 0);

            // length of apv token
            Array.Copy(contentBuffer, 4 + 4 + length, sizeBuffer, 0, 4);
            int apvLength = BitConverter.ToInt32(sizeBuffer, 0);
            var apvBytes  = new byte[apvLength];

            Array.Copy(contentBuffer, 4 + 4 + length + 4, apvBytes, 0, apvLength);
            var                frame   = new NetMQFrame(apvBytes);
            string             token   = frame.ConvertToString();
            AppProtocolVersion version = AppProtocolVersion.FromToken(token);

            return(version);
        }
Exemple #19
0
        private async Task <BoundPeer> ProcessFoundForSpecificAsync(
            ConcurrentBag <BoundPeer> history,
            IEnumerable <BoundPeer> found,
            Address target,
            int depth,
            TimeSpan?timeout,
            CancellationToken cancellationToken,
            Address searchAddress)
        {
            BoundPeer        peerFound = null;
            List <BoundPeer> peers     = found.Where(
                peer => !peer.Address.Equals(_address)).ToList();

            if (peers.Count == 0)
            {
                _logger.Debug("No any neighbor received.");
                return(peerFound);
            }

            peers = Kademlia.SortByDistance(peers, target);

            List <BoundPeer> closestNeighbors = _routing.Neighbors(target, _bucketSize).ToList();

            Task[] awaitables = peers.Select(peer =>
                                             PingAsync(peer, _requestTimeout, cancellationToken)
                                             ).ToArray();
            try
            {
                await Task.WhenAll(awaitables);
            }
            catch (AggregateException e)
            {
                if (e.InnerExceptions.All(ie => ie is TimeoutException) &&
                    e.InnerExceptions.Count == awaitables.Length)
                {
                    throw new TimeoutException(
                              $"All neighbors found do not respond in {_requestTimeout}."
                              );
                }

                _logger.Error(
                    e,
                    "Some responses from neighbors found unexpectedly terminated: {Exception}",
                    e
                    );
            }

            for (int i = 0; i < closestNeighbors.Count; i++)
            {
                if (string.CompareOrdinal(
                        closestNeighbors[i].Address.ToHex(),
                        searchAddress.ToHex()
                        ) == 0 && _routing.Contains(closestNeighbors[i]))
                {
                    peerFound = closestNeighbors[i];
                    return(peerFound);
                }
            }

            var  findNeighboursTasks = new List <Task <BoundPeer> >();
            Peer closestKnown        = closestNeighbors.Count == 0 ? null : closestNeighbors[0];

            for (int i = 0; i < Kademlia.FindConcurrency && i < peers.Count; i++)
            {
                if (peerFound is null ||
                    string.CompareOrdinal(
                        Kademlia.CalculateDistance(peers[i].Address, target).ToHex(),
                        Kademlia.CalculateDistance(closestKnown.Address, target).ToHex()
                        ) < 1)
                {
                    findNeighboursTasks.Add(FindSpecificPeerAsync(
                                                history,
                                                target,
                                                peers[i],
                                                (depth == -1) ? depth : depth - 1,
                                                searchAddress,
                                                timeout,
                                                cancellationToken));
                }
            }

            var foundSpecificPeer = new List <BoundPeer>();

            try
            {
                foundSpecificPeer.AddRange(await Task.WhenAll(findNeighboursTasks));
            }
            catch (TimeoutException)
            {
                if (findNeighboursTasks.All(findPeerTask => findPeerTask.IsFaulted))
                {
                    throw new TimeoutException(
                              "Timeout exception occurred during " +
                              $"{nameof(ProcessFoundForSpecificAsync)}.");
                }
            }

            for (int i = 0; i < foundSpecificPeer.Count; i++)
            {
                if (string.CompareOrdinal(
                        foundSpecificPeer[i].Address.ToHex(),
                        searchAddress.ToHex()
                        ) == 0 && _routing.Contains(foundSpecificPeer[i]))
                {
                    peerFound = foundSpecificPeer[i];
                    return(peerFound);
                }
            }

            return(peerFound);
        }
Exemple #20
0
        public void BucketTest()
        {
            var bucket = new KBucket(4, new System.Random(), Logger.None);
            var peer1  = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234));
            var peer2 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234));
            var peer3 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234));
            var peer4 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234));
            var peer5 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234));

            // Checks for an empty bucket.
            Assert.True(bucket.IsEmpty);
            Assert.False(bucket.IsFull);
            Assert.Empty(bucket.Peers);
            Assert.Empty(bucket.PeerStates);
            Assert.Null(bucket.GetRandomPeer());
            Assert.Null(bucket.Head);
            Assert.Null(bucket.Tail);

            // Checks for a partially filled bucket.
            bucket.AddPeer(peer1, DateTimeOffset.UtcNow);
            Assert.False(bucket.IsEmpty);
            Assert.False(bucket.IsFull);
            Assert.True(bucket.Contains(peer1));
            Assert.False(bucket.Contains(peer2));
            Assert.NotNull(bucket.GetRandomPeer());
            Assert.Null(bucket.GetRandomPeer(peer1.Address));
            Assert.NotNull(bucket.GetRandomPeer(peer2.Address));
            Assert.Equal(peer1, bucket.Head?.Peer);
            Assert.Equal(peer1, bucket.Tail?.Peer);

            // Sleep statement is used to distinguish updated times.
            Thread.Sleep(100);
            bucket.AddPeer(peer2, DateTimeOffset.UtcNow);
            Assert.Contains(
                bucket.GetRandomPeer(),
                new[] { peer1, peer2 }
                );
            Assert.Contains(
                bucket.GetRandomPeer(peer1.Address),
                new[] { peer2 }
                );

            // Checks for a full bucket.
            Thread.Sleep(100);
            bucket.AddPeer(peer3, DateTimeOffset.UtcNow);
            Thread.Sleep(100);
            bucket.AddPeer(peer4, DateTimeOffset.UtcNow);
            Assert.True(bucket.IsFull);
            Assert.Equal(
                bucket.Peers.ToHashSet(),
                new HashSet <BoundPeer> {
                peer1, peer2, peer3, peer4
            }
                );
            Assert.Contains(
                bucket.GetRandomPeer(),
                new[] { peer1, peer2, peer3, peer4 }
                );
            Thread.Sleep(100);
            bucket.AddPeer(peer5, DateTimeOffset.UtcNow);
            Assert.Equal(
                bucket.Peers.ToHashSet(),
                new HashSet <BoundPeer> {
                peer1, peer2, peer3, peer4
            }
                );
            Assert.False(bucket.Contains(peer5));
            Assert.Equal(peer4, bucket.Head?.Peer);
            Assert.Equal(peer1, bucket.Tail?.Peer);

            // Check order has changed.
            Thread.Sleep(100);
            bucket.AddPeer(peer1, DateTimeOffset.UtcNow);
            Assert.Equal(peer1, bucket.Head?.Peer);
            Assert.Equal(peer2, bucket.Tail?.Peer);

            Assert.False(bucket.RemovePeer(peer5));
            Assert.True(bucket.RemovePeer(peer1));
            Assert.DoesNotContain(peer1, bucket.Peers);
            Assert.Equal(3, bucket.Peers.Count());

            // Clear the bucket.
            bucket.Clear();
            Assert.True(bucket.IsEmpty);
            Assert.Empty(bucket.Peers);
            Assert.Null(bucket.Head);
            Assert.Null(bucket.Tail);
            Assert.Null(bucket.GetRandomPeer());
        }
Exemple #21
0
        public bool RemoveCache(BoundPeer peer)
        {
            KBucket bucket = BucketOf(peer);

            return(bucket.ReplacementCache.TryRemove(peer, out var dateTimeOffset));
        }
        public void BucketTest()
        {
            var bucket = new KBucket(4, new System.Random(), Logger.None);
            var peer1  = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234),
                0);
            var peer2 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234),
                0);
            var peer3 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234),
                0);
            var peer4 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234),
                0);
            var peer5 = new BoundPeer(
                new PrivateKey().PublicKey,
                new DnsEndPoint("0.0.0.0", 1234),
                0);

            Assert.Empty(bucket.Peers);
            Assert.True(bucket.IsEmpty());
            bucket.AddPeer(peer1);
            Assert.True(bucket.Contains(peer1));
            Assert.False(bucket.Contains(peer2));
            Assert.False(bucket.IsEmpty());
            Assert.False(bucket.IsFull());
            // This sleep statement is used to distinguish updated time of followings.
            Thread.Sleep(100);
            bucket.AddPeer(peer2);
            Thread.Sleep(100);
            Assert.Contains(
                bucket.GetRandomPeer(null),
                new[] { peer1, peer2 }
                );
            Assert.Contains(
                bucket.GetRandomPeer(peer1.Address),
                new[] { peer2 }
                );
            bucket.AddPeer(peer3);
            Thread.Sleep(100);
            bucket.AddPeer(peer4);
            Assert.True(bucket.IsFull());
            Assert.Equal(
                bucket.Peers.ToHashSet(),
                new HashSet <BoundPeer> {
                peer1, peer2, peer3, peer4
            }
                );
            Assert.Contains(
                bucket.GetRandomPeer(null),
                new[] { peer1, peer2, peer3, peer4 }
                );
            Thread.Sleep(100);
            bucket.AddPeer(peer5);
            Assert.Equal(
                bucket.Peers.ToHashSet(),
                new HashSet <BoundPeer> {
                peer1, peer2, peer3, peer4
            }
                );
            Assert.False(bucket.Contains(peer5));
            Assert.Equal(peer4, bucket.Head.Key);
            Assert.Equal(peer1, bucket.Tail.Key);
            Thread.Sleep(100);
            bucket.AddPeer(peer1);
            Assert.Equal(peer1, bucket.Head.Key);
            Assert.Equal(peer2, bucket.Tail.Key);

            Assert.False(bucket.RemovePeer(peer5));
            Assert.True(bucket.RemovePeer(peer1));
            Assert.DoesNotContain(peer1, bucket.Peers);
            Assert.Equal(3, bucket.Peers.Count());

            bucket.Clear();
            Assert.True(bucket.IsEmpty());
        }
Exemple #23
0
 /// <summary>
 /// Adds the <paramref name="peer"/> to the table.
 /// </summary>
 /// <param name="peer">The <see cref="BoundPeer"/> to add.</param>
 /// <exception cref="ArgumentException">Thrown when <paramref name="peer"/>'s
 /// <see cref="Address"/> is equal to the <see cref="Address"/> of self.</exception>
 public void AddPeer(BoundPeer peer) => AddPeer(peer, DateTimeOffset.UtcNow);
Exemple #24
0
        /// <inheritdoc/>
        public Message Decode(
            byte[] encoded,
            bool reply)
        {
            if (encoded.Length == 0)
            {
                throw new ArgumentException("Can't parse empty byte array.");
            }

            using var stream = new MemoryStream(encoded);
            var buffer = new byte[sizeof(int)];

            stream.Read(buffer, 0, sizeof(int));
            int frameCount = BitConverter.ToInt32(buffer, 0);
            var frames     = new List <byte[]>();

            for (var i = 0; i < frameCount; i++)
            {
                buffer = new byte[sizeof(int)];
                stream.Read(buffer, 0, sizeof(int));
                int frameSize = BitConverter.ToInt32(buffer, 0);
                buffer = new byte[frameSize];
                stream.Read(buffer, 0, frameSize);
                frames.Add(buffer);
            }

            // (reply == true)            [version, type, peer, timestamp, sign, frames...]
            // (reply == false) [identity, version, type, peer, timestamp, sign, frames...]
            List <byte[]> remains = reply ? frames : frames.Skip(1).ToList();

            var versionToken = Encoding.ASCII.GetString(remains[(int)Message.MessageFrame.Version]);

            AppProtocolVersion remoteVersion = AppProtocolVersion.FromToken(versionToken);
            Peer remotePeer;
            var  dictionary =
                (Bencodex.Types.Dictionary)_codec.Decode(remains[(int)Message.MessageFrame.Peer]);

            try
            {
                remotePeer = new BoundPeer(dictionary);
            }
            catch (KeyNotFoundException)
            {
                remotePeer = new Peer(dictionary);
            }

            _messageValidator.ValidateAppProtocolVersion(
                remotePeer,
                reply ? new byte[] { } : frames[0],
                remoteVersion);

            var type =
                (Message.MessageType)BitConverter.ToInt32(
                    remains[(int)Message.MessageFrame.Type],
                    0);
            long ticks       = BitConverter.ToInt64(remains[(int)Message.MessageFrame.Timestamp], 0);
            var  timestamp   = new DateTimeOffset(ticks, TimeSpan.Zero);
            var  currentTime = DateTimeOffset.UtcNow;

            _messageValidator.ValidateTimestamp(remotePeer, currentTime, timestamp);

            byte[] signature = remains[(int)Message.MessageFrame.Sign];

            byte[][] body = remains.Skip(Message.CommonFrames).ToArray();

            Message message = CreateMessage(type, body);

            message.Version   = remoteVersion;
            message.Remote    = remotePeer;
            message.Timestamp = timestamp;

            var headerWithoutSign = new[]
            {
                remains[(int)Message.MessageFrame.Version],
                remains[(int)Message.MessageFrame.Type],
                remains[(int)Message.MessageFrame.Peer],
                remains[(int)Message.MessageFrame.Timestamp],
            };

            var messageToVerify = headerWithoutSign.Concat(body).Aggregate(
                new byte[] { },
                (arr, bytes) => arr.Concat(bytes).ToArray());

            if (!remotePeer.PublicKey.Verify(messageToVerify, signature))
            {
                throw new InvalidMessageSignatureException(
                          "The signature of an encoded message is invalid.",
                          remotePeer,
                          remotePeer.PublicKey,
                          messageToVerify,
                          signature);
            }

            if (!reply)
            {
                message.Identity = frames[0];
            }

            return(message);
        }
Exemple #25
0
 /// <summary>
 /// Returns <paramref name="k"/> nearest peers to given parameter peer from routing table.
 /// Return value is already sorted with respect to target.
 /// </summary>
 /// <param name="target"><see cref="BoundPeer"/> to look up.</param>
 /// <param name="k">Number of peers to return.</param>
 /// <param name="includeTarget">A boolean value indicates to include a peer with
 /// <see cref="Address"/> of <paramref name="target"/> in return value or not.</param>
 /// <returns>An enumerable of <see cref="BoundPeer"/>.</returns>
 public IReadOnlyList <BoundPeer> Neighbors(BoundPeer target, int k, bool includeTarget)
 => Neighbors(target.Address, k, includeTarget);
Exemple #26
0
 public bool Contains(BoundPeer peer)
 {
     return(_peers.Any(kv => kv.Key.PublicKey.Equals(peer.PublicKey)));
 }