/// <summary> /// Sends "version" message to the peer and waits for the response in form of "verack" or "reject" message. /// </summary> /// <param name="cancellationToken">Cancellation that allows aborting the operation at any stage.</param> /// <exception cref="ProtocolException">Thrown when the peer rejected our "version" message.</exception> public async Task RespondToHandShakeAsync(CancellationToken cancellation = default(CancellationToken)) { this.logger.LogTrace("()"); using (var listener = new NetworkPeerListener(this).Where(m => (m.Message.Payload is VerAckPayload) || (m.Message.Payload is RejectPayload))) { this.logger.LogTrace("Responding to handshake with my version."); await this.SendMessageAsync(this.MyVersion); this.logger.LogTrace("Waiting for version acknowledgement or rejection message."); IncomingMessage message = listener.ReceiveMessage(cancellation); if (message.Message.Payload is RejectPayload reject) { this.logger.LogTrace("Version rejected: code {0}, reason '{1}'.", reject.Code, reject.Reason); this.logger.LogTrace("(-)[VERSION_REJECTED]"); throw new ProtocolException("Version rejected " + reject.Code + ": " + reject.Reason); } this.logger.LogTrace("Sending version acknowledgement."); await this.SendMessageAsync(new VerAckPayload()); this.State = NetworkPeerState.HandShaked; } this.logger.LogTrace("(-)"); }
/// <summary> /// Waits for a message of specific type to be received from the peer. /// </summary> /// <typeparam name="TPayload">Type of message to wait for.</typeparam> /// <param name="cancellationToken">Cancellation that allows aborting the operation.</param> /// <returns>Received message.</returns> /// <exception cref="OperationCanceledException">Thrown if the cancellation token was cancelled.</exception> public TPayload ReceiveMessage <TPayload>(CancellationToken cancellationToken = default(CancellationToken)) where TPayload : Payload { this.logger.LogTrace("()"); using (var listener = new NetworkPeerListener(this)) { TPayload res = listener.ReceivePayload <TPayload>(cancellationToken); this.logger.LogTrace("(-):'{0}'", res); return(res); } }
/// <summary> /// Exchanges "version" and "verack" messages with the peer. /// <para>Both parties have to send their "version" messages to the other party /// as well as to acknowledge that they are happy with the other party's "version" information.</para> /// </summary> /// <param name="requirements">Protocol requirement for network peers the node wants to be connected to.</param> /// <param name="cancellationToken">Cancellation that allows aborting the operation at any stage.</param> public async Task VersionHandshakeAsync(NetworkPeerRequirement requirements, CancellationToken cancellationToken) { this.logger.LogTrace("({0}.{1}:{2})", nameof(requirements), nameof(requirements.RequiredServices), requirements?.RequiredServices); requirements = requirements ?? new NetworkPeerRequirement(); using (var listener = new NetworkPeerListener(this).Where(p => (p.Message.Payload is VersionPayload) || (p.Message.Payload is RejectPayload) || (p.Message.Payload is VerAckPayload))) { this.logger.LogTrace("Sending my version."); await this.SendMessageAsync(this.MyVersion).ConfigureAwait(false); this.logger.LogTrace("Waiting for version or rejection message."); Payload payload = listener.ReceivePayload <Payload>(cancellationToken); if (payload is RejectPayload) { this.logger.LogTrace("(-)[HANDSHAKE_REJECTED]"); throw new ProtocolException("Handshake rejected: " + ((RejectPayload)payload).Reason); } var version = (VersionPayload)payload; this.PeerVersion = version; if (!version.AddressReceiver.Address.Equals(this.MyVersion.AddressFrom.Address)) { this.logger.LogDebug("Different external address detected by the node '{0}' instead of '{1}'.", version.AddressReceiver.Address, this.MyVersion.AddressFrom.Address); } if (version.Version < ProtocolVersion.MIN_PEER_PROTO_VERSION) { this.logger.LogDebug("Outdated version {0} received, disconnecting peer.", version.Version); this.Disconnect("Outdated version"); this.logger.LogTrace("(-)[OUTDATED]"); return; } if (!requirements.Check(version)) { this.logger.LogTrace("(-)[UNSUPPORTED_REQUIREMENTS]"); this.Disconnect("The peer does not support the required services requirement"); return; } this.logger.LogTrace("Sending version acknowledgement."); await this.SendMessageAsync(new VerAckPayload()).ConfigureAwait(false); this.logger.LogTrace("Waiting for version acknowledgement."); listener.ReceivePayload <VerAckPayload>(cancellationToken); this.State = NetworkPeerState.HandShaked; if (this.Advertize && this.MyVersion.AddressFrom.Address.IsRoutable(true)) { this.SendMessageVoidAsync(new AddrPayload(new NetworkAddress(this.MyVersion.AddressFrom) { Time = this.dateTimeProvider.GetTimeOffset() })); } } this.logger.LogTrace("(-)"); }