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); }
/// <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); }
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); }
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); }
/// <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); }