public static BitcoinVersionPayload CreateVersionPayload(string userAgent, byte[] nonce, IPEndPoint sender,
                                                                 IPEndPoint receiver)
        {
            var now = DateTimeOffset.UtcNow;
            var bitcoinVersionPayload = new BitcoinVersionPayload(
                new BitcoinVarString(userAgent),
                nonce,
                new BitcoinNetworkAddressPayload(now, ChatClientConfiguration.PeerServices, sender.Address, sender.Port),
                new BitcoinNetworkAddressPayload(now, PeerServices.Network, receiver.Address, receiver.Port));

            return(bitcoinVersionPayload);
        }
        public async Task VersionHandshakeAsync(string userAgent, byte[] nonce, CancellationToken cancellationToken)
        {
            try
            {
                BitcoinVersionPayload outgoingVersionPayload = PayloadFactory.CreateVersionPayload(userAgent, nonce,
                                                                                                   (IPEndPoint)this._tcpClient.Client.LocalEndPoint,
                                                                                                   (IPEndPoint)this._tcpClient.Client.RemoteEndPoint);

                var myVersionMessage = new BitcoinMessage("version", outgoingVersionPayload.Serialize());
                this._logger.LogInformation("Sending own version.");
                await WriteAsync(myVersionMessage.Serialize());

                this._logger.LogInformation("'version' sent.");

                // do it like this so that there is no specific expected order for version and verack, but expect both.
                while (this.PeerVersionPayload == null || this.PeerVerAckReceived == false)
                {
                    var reply = await ReadMessageAsync(cancellationToken);

                    if (reply.Command == "version")
                    {
                        this.PeerVersionPayload = new BitcoinVersionPayload(reply.PayloadBytes);
                        this._logger.LogInformation(
                            $"Version payload decoded from {this.PeerVersionPayload.UserAgent.Text} at {this.PeerVersionPayload.Sender}.");
                        this._logger.LogInformation($"Setting own address to {this.PeerVersionPayload.Receiver}.");


                        this.SelfFromPeer = this.PeerVersionPayload.Receiver.ToPeer();

                        this._logger.LogInformation("Sending version acknowledgement.");
                        await WriteAsync(new BitcoinMessage("verack", new byte[0]).Serialize());

                        this._logger.LogInformation("'verack' sent.");
                    }
                    else if (reply.Command == "verack")
                    {
                        this.PeerVerAckReceived = true;
                    }
                    else
                    {
                        throw new InvalidOperationException(
                                  $"Received unexpected message '{reply.Command}' during handshake.");
                    }
                }
            }
            catch (Exception e)
            {
                DisposeAndThrow(e);
            }
        }