예제 #1
0
        public async Task DialBackPeer_Test()
        {
            AElfPeerEndpointHelper.TryParse("127.0.0.1:2000", out var endpoint);
            var handshake = await _handshakeProvider.GetHandshakeAsync();

            var grpcPeer = await _peerDialer.DialBackPeerAsync(endpoint, handshake);

            grpcPeer.ShouldNotBeNull();
            grpcPeer.CurrentBlockHash.ShouldBe(handshake.HandshakeData.BestChainHash);
            grpcPeer.CurrentBlockHeight.ShouldBe(handshake.HandshakeData.BestChainHeight);
            grpcPeer.LastKnownLibHeight.ShouldBe(handshake.HandshakeData.LastIrreversibleBlockHeight);
            grpcPeer.Info.Pubkey.ShouldBe(handshake.HandshakeData.Pubkey.ToHex());
            grpcPeer.Info.ProtocolVersion.ShouldBe(handshake.HandshakeData.Version);
        }
예제 #2
0
        public async Task GetHandshakeAsync_Test()
        {
            var handshake = await _handshakeProvider.GetHandshakeAsync();

            handshake.ShouldNotBeNull();
            handshake.HandshakeData.Pubkey.ShouldBe(await _accountService.GetPublicKeyAsync());
        }
예제 #3
0
        public async Task ValidateHandshake_Test()
        {
            var remoteKeyPair = _peerKeyProvider.AuthorizedKey;

            var handshake        = CreateHandshake(remoteKeyPair);
            var validationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.Ok);

            handshake = CreateHandshake(remoteKeyPair);
            var unauthorizedKeyPair = CryptoHelper.GenerateKeyPair();

            handshake.HandshakeData.Pubkey = ByteString.CopyFrom(unauthorizedKeyPair.PublicKey);
            validationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.Unauthorized);

            handshake = CreateHandshake(remoteKeyPair);
            handshake.HandshakeData.ChainId = 1234;
            validationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.InvalidChainId);

            handshake = CreateHandshake(remoteKeyPair);
            handshake.HandshakeData.Version = 0;
            validationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.InvalidVersion);

            handshake = CreateHandshake(remoteKeyPair);
            handshake.HandshakeData.Time =
                TimestampHelper.GetUtcNow().AddMilliseconds(-(NetworkConstants.HandshakeTimeout + 100));
            validationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.HandshakeTimeout);

            handshake = CreateHandshake(remoteKeyPair);
            var maliciousPeer = CryptoHelper.GenerateKeyPair();
            var signature     = CryptoHelper.SignWithPrivateKey(maliciousPeer.PrivateKey, Hash
                                                                .FromMessage(handshake.HandshakeData)
                                                                .ToByteArray());

            handshake.Signature = ByteString.CopyFrom(signature);
            validationResult    = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            validationResult.ShouldBe(HandshakeValidationResult.InvalidSignature);

            var localHandshake = await _handshakeProvider.GetHandshakeAsync();

            validationResult = await _handshakeProvider.ValidateHandshakeAsync(localHandshake);

            validationResult.ShouldBe(HandshakeValidationResult.SelfConnection);
        }
예제 #4
0
        /// <summary>
        /// Given an IP address, will create a handshake to the distant node for
        /// further communications.
        /// </summary>
        /// <returns>The created peer</returns>
        public async Task <GrpcPeer> DialPeerAsync(DnsEndPoint remoteEndpoint)
        {
            var client = await CreateClientAsync(remoteEndpoint);

            if (client == null)
            {
                return(null);
            }

            var handshake = await _handshakeProvider.GetHandshakeAsync();

            var handshakeReply = await CallDoHandshakeAsync(client, remoteEndpoint, handshake);

            // verify handshake
            if (handshakeReply.Error != HandshakeError.HandshakeOk)
            {
                Logger.LogWarning($"Handshake error: {remoteEndpoint} {handshakeReply.Error}.");
                await client.Channel.ShutdownAsync();

                return(null);
            }

            if (await _handshakeProvider.ValidateHandshakeAsync(handshakeReply.Handshake) !=
                HandshakeValidationResult.Ok)
            {
                Logger.LogWarning($"Connect error: {remoteEndpoint} {handshakeReply}.");
                await client.Channel.ShutdownAsync();

                return(null);
            }

            var peer = new GrpcPeer(client, remoteEndpoint, new PeerConnectionInfo
            {
                Pubkey          = handshakeReply.Handshake.HandshakeData.Pubkey.ToHex(),
                ConnectionTime  = TimestampHelper.GetUtcNow(),
                ProtocolVersion = handshakeReply.Handshake.HandshakeData.Version,
                SessionId       = handshakeReply.Handshake.SessionId.ToByteArray(),
                IsInbound       = false
            });

            peer.UpdateLastReceivedHandshake(handshakeReply.Handshake);

            peer.InboundSessionId = handshake.SessionId.ToByteArray();
            peer.UpdateLastSentHandshake(handshake);

            return(peer);
        }
예제 #5
0
        /// <summary>
        /// Updates the current target for the initial sync. For now this method will
        /// not have any effect if the sync is already finished or the target has not
        /// been initialized.
        /// </summary>
        /// <returns></returns>
        public async Task UpdateSyncStateAsync()
        {
            // This method should only be called when the sync target has already been found and the
            // node is syncing.

            if (SyncState != SyncState.Syncing)
            {
                Logger.LogWarning("Trying to update the sync, but it is either finished or not yet been initialized.");
                return;
            }

            var chain = await _blockchainService.GetChainAsync();

            // if the current LIB is higher than the recorded target, update
            // the peers current LIB height. Note that this condition will
            // also be true when the node starts.
            if (chain.LastIrreversibleBlockHeight >= _syncStateProvider.SyncTarget)
            {
                var handshake = await _handshakeProvider.GetHandshakeAsync();

                // Update handshake information of all our peers
                var tasks = _peerPool.GetPeers().Select(async peer =>
                {
                    try
                    {
                        await peer.DoHandshakeAsync(handshake);
                    }
                    catch (NetworkException e)
                    {
                        Logger.LogError(e, "Error while handshaking.");
                    }

                    Logger.LogDebug($"Peer {peer} last known LIB is {peer.LastKnownLibHeight}.");
                }).ToList();

                await Task.WhenAll(tasks);
                await UpdateSyncTargetAsync();
            }
        }
        public async Task DoHandshake_InvalidHandshake_Test()
        {
            AElfPeerEndpointHelper.TryParse(NetworkTestConstants.GoodPeerEndpoint, out var endpoint);

            var handshake = CreateHandshake();

            handshake.HandshakeData.ChainId = 100;
            var result = await _connectionService.DoHandshakeAsync(endpoint, handshake);

            result.Error.ShouldBe(HandshakeError.ChainMismatch);

            handshake = CreateHandshake();
            handshake.HandshakeData.Version = 100;
            result = await _connectionService.DoHandshakeAsync(endpoint, handshake);

            result.Error.ShouldBe(HandshakeError.ProtocolMismatch);

            handshake = CreateHandshake();
            handshake.HandshakeData.Time = handshake.HandshakeData.Time -
                                           TimestampHelper.DurationFromMilliseconds(
                NetworkConstants.HandshakeTimeout + 1000);
            result = await _connectionService.DoHandshakeAsync(endpoint, handshake);

            result.Error.ShouldBe(HandshakeError.SignatureTimeout);

            handshake = CreateHandshake();
            handshake.HandshakeData.Pubkey = ByteString.CopyFrom(CryptoHelper.GenerateKeyPair().PublicKey);
            result = await _connectionService.DoHandshakeAsync(endpoint, handshake);

            result.Error.ShouldBe(HandshakeError.WrongSignature);

            handshake = await _handshakeProvider.GetHandshakeAsync();

            result = await _connectionService.DoHandshakeAsync(endpoint, handshake);

            result.Error.ShouldBe(HandshakeError.ConnectionRefused);
        }
예제 #7
0
        public async Task <HandshakeReply> DoHandshakeAsync(DnsEndPoint endpoint, Handshake handshake)
        {
            // validate the handshake (signature, chain id...)
            var handshakeValidationResult = await _handshakeProvider.ValidateHandshakeAsync(handshake);

            if (handshakeValidationResult != HandshakeValidationResult.Ok)
            {
                var handshakeError = GetHandshakeError(handshakeValidationResult);
                return(new HandshakeReply {
                    Error = handshakeError
                });
            }

            var pubkey = handshake.HandshakeData.Pubkey.ToHex();

            // remove any remaining connection to the peer (before the check
            // that we have room for more connections)
            var currentPeer = _peerPool.FindPeerByPublicKey(pubkey);

            if (currentPeer != null)
            {
                _peerPool.RemovePeer(pubkey);
                await currentPeer.DisconnectAsync(false);
            }

            try
            {
                // mark the (IP; pubkey) pair as currently handshaking
                if (!_peerPool.AddHandshakingPeer(endpoint.Host, pubkey))
                {
                    return new HandshakeReply {
                               Error = HandshakeError.ConnectionRefused
                    }
                }
                ;

                // create the connection to the peer
                var peerEndpoint = new AElfPeerEndpoint(endpoint.Host, handshake.HandshakeData.ListeningPort);
                var grpcPeer     = await _peerDialer.DialBackPeerAsync(peerEndpoint, handshake);

                // add the new peer to the pool
                if (!_peerPool.TryAddPeer(grpcPeer))
                {
                    Logger.LogWarning($"Stopping connection, peer already in the pool {grpcPeer.Info.Pubkey}.");

                    await grpcPeer.DisconnectAsync(false);

                    return(new HandshakeReply {
                        Error = HandshakeError.RepeatedConnection
                    });
                }

                Logger.LogDebug($"Added to pool {grpcPeer.RemoteEndpoint} - {grpcPeer.Info.Pubkey}.");

                // send back our handshake
                var replyHandshake = await _handshakeProvider.GetHandshakeAsync();

                grpcPeer.InboundSessionId = replyHandshake.SessionId.ToByteArray();
                grpcPeer.UpdateLastSentHandshake(replyHandshake);

                return(new HandshakeReply {
                    Handshake = replyHandshake, Error = HandshakeError.HandshakeOk
                });
            }
            finally
            {
                // remove the handshaking mark (IP; pubkey)
                _peerPool.RemoveHandshakingPeer(endpoint.Host, pubkey);
            }
        }
예제 #8
0
        /// <summary>
        /// Connects to a node with the given ip address and adds it to the node's peer pool.
        /// </summary>
        /// <param name="endpoint">the ip address of the distant node</param>
        /// <returns>True if the connection was successful, false otherwise</returns>
        public async Task <bool> ConnectAsync(IPEndPoint endpoint)
        {
            Logger.LogTrace($"Attempting to reach {endpoint}.");

            if (_peerPool.FindPeerByEndpoint(endpoint) != null)
            {
                Logger.LogWarning($"Peer {endpoint} is already in the pool.");
                return(false);
            }

            GrpcPeer peer;

            try
            {
                // create the connection to the distant node
                peer = await _peerDialer.DialPeerAsync(endpoint);
            }
            catch (PeerDialException ex)
            {
                Logger.LogError(ex, $"Dial exception {endpoint}:");
                return(false);
            }

            var peerPubkey = peer.Info.Pubkey;

            if (!_peerPool.TryAddPeer(peer))
            {
                Logger.LogWarning($"Peer {peerPubkey} is already in the pool.");
                await peer.DisconnectAsync(false);

                return(false);
            }

            Handshake peerHandshake;

            try
            {
                peerHandshake = await peer.DoHandshakeAsync(await _handshakeProvider.GetHandshakeAsync());
            }
            catch (NetworkException ex)
            {
                Logger.LogError(ex, $"Handshake failed to {endpoint} - {peerPubkey}.");
                await DisconnectAsync(peer);

                return(false);
            }

            HandshakeError handshakeError = ValidateHandshake(peerHandshake, peerPubkey);

            if (handshakeError != HandshakeError.HandshakeOk)
            {
                Logger.LogWarning($"Invalid handshake [{handshakeError}] from {endpoint} - {peerPubkey}");
                await DisconnectAsync(peer);

                return(false);
            }

            Logger.LogTrace($"Connected to {peer} - LIB height {peer.LastKnownLibHeight}, " +
                            $"best chain [{peer.CurrentBlockHeight}, {peer.CurrentBlockHash}].");

            FireConnectionEvent(peer);

            return(true);
        }