public static GrpcPeer CreatePeerWithInfo(string ip, PeerConnectionInfo info)
        {
            AElfPeerEndpointHelper.TryParse(ip, out var endpoint);
            var peer = new GrpcPeer(new GrpcClient(CreateMockChannel(), null), endpoint, info);

            peer.InboundSessionId = new byte[] { 0, 1, 2 };
            return(peer);
        }
        public static GrpcPeer CreatePeerWithClient(string ip, string pubkey, PeerService.PeerServiceClient client)
        {
            AElfPeerEndpointHelper.TryParse(ip, out var endpoint);
            var peer = new GrpcPeer(new GrpcClient(CreateMockChannel(), client), endpoint, new PeerConnectionInfo {
                Pubkey = pubkey, SessionId = new byte[] { 0, 1, 2 }
            });

            peer.InboundSessionId = new byte[] { 0, 1, 2 };
            return(peer);
        }
        private void FireConnectionEvent(GrpcPeer peer)
        {
            var nodeInfo = new NodeInfo {
                Endpoint = peer.RemoteEndpoint.ToString(), Pubkey = ByteStringHelper.FromHexString(peer.Info.Pubkey)
            };
            var bestChainHash   = peer.CurrentBlockHash;
            var bestChainHeight = peer.CurrentBlockHeight;

            _ = EventBus.PublishAsync(new PeerConnectedEventData(nodeInfo, bestChainHash, bestChainHeight));
        }
        public GrpcPeerTests()
        {
            _osTestHelper = GetRequiredService <OSTestHelper>();
            _pool         = GetRequiredService <IPeerPool>();

            _grpcPeer             = GrpcTestPeerHelper.CreateNewPeer();
            _grpcPeer.IsConnected = true;
            _nonInterceptedPeer   = MockServiceClient("127.0.0.1:2000");

            _pool.TryAddPeer(_grpcPeer);
        }
示例#5
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);
        }
示例#6
0
        public async Task <GrpcPeer> DialBackPeerAsync(DnsEndPoint remoteEndpoint, Handshake handshake)
        {
            var client = CreateClient(remoteEndpoint);

            await PingNodeAsync(client, remoteEndpoint);

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

            peer.UpdateLastReceivedHandshake(handshake);

            return(peer);
        }
        public static GrpcPeer CreateNewPeer(string ipAddress = "127.0.0.1:2000", bool isValid = true, string publicKey = null)
        {
            var pubkey  = publicKey ?? NetworkTestConstants.FakePubkey;
            var channel = new Channel(ipAddress, ChannelCredentials.Insecure);

            PeerService.PeerServiceClient client;

            if (isValid)
            {
                client = new PeerService.PeerServiceClient(channel.Intercept(metadata =>
                {
                    metadata.Add(GrpcConstants.PubkeyMetadataKey, pubkey);
                    return(metadata);
                }));
            }
            else
            {
                client = new PeerService.PeerServiceClient(channel);
            }

            var connectionInfo = new PeerConnectionInfo
            {
                Pubkey          = pubkey,
                ProtocolVersion = KernelConstants.ProtocolVersion,
                ConnectionTime  = TimestampHelper.GetUtcNow(),
                SessionId       = new byte[] { 0, 1, 2 },
                IsInbound       = true
            };

            AElfPeerEndpointHelper.TryParse(ipAddress, out var endpoint);
            var peer = new GrpcPeer(new GrpcClient(channel, client), endpoint, connectionInfo);

            peer.InboundSessionId = new byte[] { 0, 1, 2 };

            return(peer);
        }
示例#8
0
        private async Task <bool> DialAsync(string ipAddress)
        {
            Logger.LogTrace($"Attempting to reach {ipAddress}.");

            Channel channel = new Channel(ipAddress, ChannelCredentials.Insecure);

            var client = new PeerService.PeerServiceClient(channel.Intercept(metadata =>
            {
                metadata.Add(GrpcConsts.PubkeyMetadataKey, AsyncHelper.RunSync(() => _accountService.GetPublicKeyAsync()).ToHex());
                return(metadata);
            }));

            var hsk = await BuildHandshakeAsync();

            if (channel.State == ChannelState.TransientFailure)
            {
                // if failing give it some time to recover
                await channel.TryWaitForStateChangedAsync(channel.State,
                                                          DateTime.UtcNow.AddSeconds(_networkOptions.PeerDialTimeout));
            }

            ConnectReply connectReply;

            try
            {
                connectReply = await client.ConnectAsync(hsk,
                                                         new CallOptions().WithDeadline(DateTime.UtcNow.AddSeconds(_networkOptions.PeerDialTimeout)));
            }
            catch (RpcException e)
            {
                await channel.ShutdownAsync();

                Logger.LogError(e, $"Could not connect to {ipAddress}.");
                return(false);
            }

            // todo refactor so that connect returns the handshake and we'll check here
            // todo if not correct we kill the channel.
            if (connectReply?.Handshake?.HskData == null || connectReply.Err != AuthError.None)
            {
                Logger.LogWarning($"Incorrect handshake for {ipAddress}, {connectReply?.Err}.");
                await channel.ShutdownAsync();

                return(false);
            }

            var pubKey = connectReply.Handshake.HskData.PublicKey.ToHex();
            var peer   = new GrpcPeer(channel, client, pubKey, ipAddress);

            if (!_authenticatedPeers.TryAdd(pubKey, peer))
            {
                Logger.LogWarning($"Peer {pubKey} is already in list.");
                await channel.ShutdownAsync();

                return(false);
            }

            peer.DisconnectionEvent += PeerOnDisconnectionEvent;

            Logger.LogTrace($"Connected to {peer}.");

            return(true);
        }
示例#9
0
        /// <summary>
        /// First step of the connect/auth process. Used to initiate a connection. The provided payload should be the
        /// clients authentication information. When receiving this call, protocol dictates you send the client your auth
        /// information. The response says whether or not you can connect.
        /// </summary>
        public override async Task <ConnectReply> Connect(Handshake handshake, ServerCallContext context)
        {
            Logger.LogTrace($"{context.Peer} has initiated a connection request.");

            if (handshake?.HskData == null)
            {
                return new ConnectReply {
                           Err = AuthError.InvalidHandshake
                }
            }
            ;

            // verify chain id
            if (handshake.HskData.ChainId != _blockChainService.GetChainId())
            {
                return new ConnectReply {
                           Err = AuthError.ChainMismatch
                }
            }
            ;

            // verify protocol
            if (handshake.HskData.Version != KernelConstants.ProtocolVersion)
            {
                return new ConnectReply {
                           Err = AuthError.ProtocolMismatch
                }
            }
            ;

            // verify signature
            var validData = await _accountService.VerifySignatureAsync(handshake.Sig.ToByteArray(),
                                                                       Hash.FromMessage(handshake.HskData).ToByteArray(), handshake.HskData.PublicKey.ToByteArray());

            if (!validData)
            {
                return new ConnectReply {
                           Err = AuthError.WrongSig
                }
            }
            ;

            var peer = GrpcUrl.Parse(context.Peer);

            if (peer == null)
            {
                return new ConnectReply {
                           Err = AuthError.InvalidPeer
                }
            }
            ;

            var peerAddress = peer.IpAddress + ":" + handshake.HskData.ListeningPort;

            Logger.LogDebug($"Attempting to create channel to {peerAddress}");

            Channel channel = new Channel(peerAddress, ChannelCredentials.Insecure);
            var     client  = new PeerService.PeerServiceClient(channel.Intercept(metadata =>
            {
                metadata.Add(GrpcConsts.PubkeyMetadataKey, AsyncHelper.RunSync(() => _accountService.GetPublicKeyAsync()).ToHex());
                return(metadata);
            }));

            if (channel.State != ChannelState.Ready)
            {
                var c = channel.WaitForStateChangedAsync(channel.State);
            }

            var pubKey   = handshake.HskData.PublicKey.ToHex();
            var grpcPeer = new GrpcPeer(channel, client, pubKey, peerAddress);

            // Verify auth
            bool valid = _peerPool.IsAuthenticatePeer(pubKey);

            if (!valid)
            {
                await channel.ShutdownAsync();

                Logger.LogDebug($"Failed to reach {grpcPeer}");
                return(new ConnectReply {
                    Err = AuthError.WrongAuth
                });
            }

            // send our credentials
            var hsk = await _peerPool.GetHandshakeAsync();

            // If auth ok -> add it to our peers
            _peerPool.AddPeer(grpcPeer);

            return(new ConnectReply {
                Handshake = hsk
            });
        }
        /// <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(DnsEndPoint endpoint)
        {
            Logger.LogDebug($"Attempting to reach {endpoint}.");

            var dialedPeer = await GetDialedPeerWithEndpointAsync(endpoint);

            if (dialedPeer == null)
            {
                return(false);
            }

            var inboundPeer = _peerPool.FindPeerByPublicKey(dialedPeer.Info.Pubkey) as GrpcPeer;

            /* A connection already exists, this can happen when both peers dial each other at the same time. To make
             * sure both sides close the same connection, they both decide based on the times of the handshakes.
             * Scenario steps, chronologically:
             *  1) P1 (hsk_time: t1) --> dials P2 --and-- P1 <-- P2 dials (hsk_time: t2)
             *  2) P2 receives P1s dial with t1 (in the hsk) and add to the pool
             *  3) P1 receives P2s dial with and adds to pool
             *  4) both dials finish and find that the pool already contains the dialed node.
             * To resolve this situation, both peers will choose the connection that was initiated the earliest,
             * so either P1s dial or P2s. */

            GrpcPeer currentPeer = dialedPeer;

            if (inboundPeer != null)
            {
                Logger.LogDebug("Duplicate peer connection detected: " +
                                $"{inboundPeer} ({inboundPeer.LastReceivedHandshakeTime}) " +
                                $"vs {dialedPeer} ({dialedPeer.LastSentHandshakeTime}).");

                if (inboundPeer.LastReceivedHandshakeTime > dialedPeer.LastSentHandshakeTime)
                {
                    // we started the dial first, replace the inbound connection with the dialed
                    if (!_peerPool.TryReplace(inboundPeer.Info.Pubkey, inboundPeer, dialedPeer))
                    {
                        Logger.LogWarning("Replacing the inbound connection failed.");
                    }

                    await inboundPeer.DisconnectAsync(false);

                    Logger.LogDebug($"Replaced the inbound connection with the dialed peer {inboundPeer} .");
                }
                else
                {
                    // keep the inbound connection
                    await dialedPeer.DisconnectAsync(false);

                    currentPeer = inboundPeer;

                    Logger.LogDebug($"Disconnected dialed peer {dialedPeer}.");
                }
            }
            else
            {
                if (!_peerPool.TryAddPeer(dialedPeer))
                {
                    Logger.LogWarning($"Peer add to the failed {dialedPeer.Info.Pubkey}.");
                    await dialedPeer.DisconnectAsync(false);

                    return(false);
                }

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

            try
            {
                await currentPeer.ConfirmHandshakeAsync();
            }
            catch (Exception e)
            {
                Logger.LogDebug(e, $"Confirm handshake error. Peer: {currentPeer.Info.Pubkey}.");
                _peerPool.RemovePeer(currentPeer.Info.Pubkey);
                await currentPeer.DisconnectAsync(false);

                throw;
            }

            currentPeer.IsConnected = true;
            currentPeer.SyncState   = SyncState.Syncing;

            Logger.LogInformation($"Connected to: {currentPeer.RemoteEndpoint} - {currentPeer.Info.Pubkey.Substring(0, 45)}" +
                                  $" - in-token {currentPeer.InboundSessionId?.ToHex()}, out-token {currentPeer.OutboundSessionId?.ToHex()}" +
                                  $" - LIB height {currentPeer.LastKnownLibHeight}" +
                                  $" - best chain [{currentPeer.CurrentBlockHeight}, {currentPeer.CurrentBlockHash}]");

            FireConnectionEvent(currentPeer);

            return(true);
        }