From f8305d20c85e08dfd95b0e476eaeac79af18e882 Mon Sep 17 00:00:00 2001 From: Lucas Ontivero Date: Wed, 24 Apr 2024 11:34:36 -0300 Subject: [PATCH] Notify `SoftwareVersion` and `LegalDocumentVersion` (#12692) * Notify versions as part of initial ws exchange * CR suggestions --- .../Middlewares/SatoshiWebSocketHandler.cs | 30 ++++++++--- WalletWasabi.Daemon/Global.cs | 8 ++- .../IntegrationTests/LiveServerTests.cs | 19 ------- .../RegressionTests/BackendTests.cs | 9 ---- .../UnitTests/UpdateStatusTests.cs | 48 ----------------- .../StreamReaderWriterExtensions.cs | 18 ++++++- WalletWasabi/Models/UpdateStatus.cs | 36 +------------ WalletWasabi/Services/Events/Events.cs | 5 +- WalletWasabi/Services/LegalChecker.cs | 36 +++++-------- WalletWasabi/Services/SatoshiSynchronizer.cs | 11 ++++ WalletWasabi/Services/UpdateChecker.cs | 51 ------------------- WalletWasabi/Services/UpdateManager.cs | 37 +++++++++----- WalletWasabi/Services/WasabiSynchronizer.cs | 17 ------- WalletWasabi/Synchronization/FilterMessage.cs | 4 +- .../LegalDocumentVersionMessage.cs | 18 +++++++ .../Synchronization/VersionMessage.cs | 19 +++++++ .../WebClients/Wasabi/WasabiClient.cs | 16 ------ 17 files changed, 135 insertions(+), 247 deletions(-) delete mode 100644 WalletWasabi.Tests/UnitTests/UpdateStatusTests.cs delete mode 100644 WalletWasabi/Services/UpdateChecker.cs create mode 100644 WalletWasabi/Synchronization/LegalDocumentVersionMessage.cs create mode 100644 WalletWasabi/Synchronization/VersionMessage.cs diff --git a/WalletWasabi.Backend/Middlewares/SatoshiWebSocketHandler.cs b/WalletWasabi.Backend/Middlewares/SatoshiWebSocketHandler.cs index dd9aeea985f..a52238974c5 100644 --- a/WalletWasabi.Backend/Middlewares/SatoshiWebSocketHandler.cs +++ b/WalletWasabi.Backend/Middlewares/SatoshiWebSocketHandler.cs @@ -9,6 +9,7 @@ using WalletWasabi.Blockchain.Analysis.FeesEstimation; using WalletWasabi.Blockchain.BlockFilters; using WalletWasabi.Extensions; +using WalletWasabi.Helpers; using WalletWasabi.Logging; using WalletWasabi.Services; using WalletWasabi.Synchronization; @@ -107,7 +108,8 @@ public override async Task ReceiveAsync( var bestKnownBlockHash = reader.ReadUInt256(); socketState.Handshaked = true; - // Send the best block height + await SendSoftwareVersionAsync(socketState.WebSocket, cancellationToken); + await SendLegalDocumentVersionAsync(socketState.WebSocket, cancellationToken); await SendBlockHeightAsync(socketState.WebSocket, cancellationToken); await StartSendingFiltersAsync(socketState.WebSocket, bestKnownBlockHash, cancellationToken); } @@ -151,12 +153,26 @@ private Task SendBlockHeightAsync(WebSocket webSocket, CancellationToken cancell return webSocket.SendAsync(message.ToByteArray(), WebSocketMessageType.Binary, true, cancellationToken); } - /// - /// SendMissingFiltersAsync sends all the filters since bestknownblockhash to the client. - /// - /// The websocket. - /// The latest block id known by the client. - /// The cancellation token. + private Task SendSoftwareVersionAsync(WebSocket webSocket, CancellationToken cancellationToken) + { + var clientVersion = Constants.ClientVersion; + var backendVersion = new Version(int.Parse(Constants.BackendMajorVersion), 0, 0); + var message = new VersionMessage(clientVersion, backendVersion); + return webSocket.SendAsync(message.ToByteArray(), WebSocketMessageType.Binary, true, cancellationToken); + } + + private Task SendLegalDocumentVersionAsync(WebSocket webSocket, CancellationToken cancellationToken) + { + var message = new LegalDocumentVersionMessage(Constants.Ww2LegalDocumentsVersion); + return webSocket.SendAsync(message.ToByteArray(), WebSocketMessageType.Binary, true, cancellationToken); + } + + // + // SendMissingFiltersAsync sends all the filters since bestknownblockhash to the client. + // + // The websocket. + // The latest block id known by the client. + // The cancellation token. private async Task SendMissingFiltersAsync( WebSocket webSocket, uint256 bestKnownBlockHash, diff --git a/WalletWasabi.Daemon/Global.cs b/WalletWasabi.Daemon/Global.cs index 821b7cc3b18..3a19b81b3c3 100644 --- a/WalletWasabi.Daemon/Global.cs +++ b/WalletWasabi.Daemon/Global.cs @@ -83,11 +83,9 @@ public Global(string dataDir, string configFilePath, Config config) var satoshiEndpointUri = new UriBuilder(satoshiUriScheme, coordinatorUri.Host, coordinatorUri.Port, "api/satoshi").Uri; HostedServices.Register(() => new SatoshiSynchronizer(BitcoinStore, satoshiEndpointUri, Config.UseTor ? TorSettings.SocksEndpoint : null, EventBus), "Satoshi Synchronizer"); - HostedServices.Register(() => new UpdateChecker(TimeSpan.FromHours(1), wasabiSynchronizer), "Software Update Checker"); - UpdateChecker updateChecker = HostedServices.Get(); - - LegalChecker = new(DataDir, updateChecker); - UpdateManager = new(DataDir, Config.DownloadNewVersion, HttpClientFactory.NewHttpClient(Mode.DefaultCircuit, maximumRedirects: 10), updateChecker); + var httpClientForUpdates = HttpClientFactory.NewHttpClient(Mode.DefaultCircuit, maximumRedirects: 10); + LegalChecker = new(DataDir, new WasabiClient(httpClientForUpdates), EventBus); + UpdateManager = new(DataDir, Config.DownloadNewVersion, httpClientForUpdates, EventBus); TorStatusChecker = new TorStatusChecker(TimeSpan.FromHours(6), HttpClientFactory.NewHttpClient(Mode.DefaultCircuit), new XmlIssueListParser()); RoundStateUpdaterCircuit = new PersonCircuit(); diff --git a/WalletWasabi.Tests/IntegrationTests/LiveServerTests.cs b/WalletWasabi.Tests/IntegrationTests/LiveServerTests.cs index e87248d29cb..00411d49f9a 100644 --- a/WalletWasabi.Tests/IntegrationTests/LiveServerTests.cs +++ b/WalletWasabi.Tests/IntegrationTests/LiveServerTests.cs @@ -97,25 +97,6 @@ public async Task GetVersionsTestsAsync(Network network) Assert.Equal(new(1, 0), versions.LegalDocumentsVersion); } - [Theory] - [MemberData(nameof(GetNetworks))] - public async Task CheckUpdatesTestsAsync(Network network) - { - using CancellationTokenSource ctsTimeout = new(TimeSpan.FromMinutes(2)); - - WasabiClient client = MakeWasabiClient(network); - UpdateStatus updateStatus = await client.CheckUpdatesAsync(ctsTimeout.Token); - - Assert.True(updateStatus.BackendCompatible); - Assert.True(updateStatus.ClientUpToDate); - Assert.Equal(new Version(1, 0), updateStatus.LegalDocumentsVersion); - Assert.Equal((ushort)4, updateStatus.CurrentBackendMajorVersion); - Assert.Equal(WalletWasabi.Helpers.Constants.ClientVersion.ToString(3), updateStatus.ClientVersion.ToString()); - - var versions = await client.GetVersionsAsync(ctsTimeout.Token); - Assert.Equal(versions.LegalDocumentsVersion, updateStatus.LegalDocumentsVersion); - } - [Theory] [MemberData(nameof(GetNetworks))] public async Task GetLegalDocumentsTestsAsync(Network network) diff --git a/WalletWasabi.Tests/RegressionTests/BackendTests.cs b/WalletWasabi.Tests/RegressionTests/BackendTests.cs index 7793d827dcb..c663cf5c319 100644 --- a/WalletWasabi.Tests/RegressionTests/BackendTests.cs +++ b/WalletWasabi.Tests/RegressionTests/BackendTests.cs @@ -70,15 +70,6 @@ public async Task GetExchangeRatesAsync() Assert.True(rate.Rate > 0); } - [Fact] - public async Task GetClientVersionAsync() - { - WasabiClient client = new(BackendHttpClient); - var uptodate = await client.CheckUpdatesAsync(CancellationToken.None); - Assert.True(uptodate.BackendCompatible); - Assert.True(uptodate.ClientUpToDate); - } - [Fact] public async Task BroadcastReplayTxAsync() { diff --git a/WalletWasabi.Tests/UnitTests/UpdateStatusTests.cs b/WalletWasabi.Tests/UnitTests/UpdateStatusTests.cs deleted file mode 100644 index f1687e1ec5f..00000000000 --- a/WalletWasabi.Tests/UnitTests/UpdateStatusTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -using WalletWasabi.Models; -using Xunit; - -namespace WalletWasabi.Tests.UnitTests; - -public class UpdateStatusTests -{ - [Fact] - public void TestEquality() - { - var backendCompatible = false; - var clientUpToDate = false; - var legalVersion = new Version(1, 1); - var clientVersion = new Version(2, 2); - ushort backendVersion = 1; - - // Create a new instance with the same parameters and make sure they're equal. - var x = new UpdateStatus(backendCompatible, clientUpToDate, legalVersion, backendVersion, clientVersion); - var y = new UpdateStatus(backendCompatible, clientUpToDate, legalVersion, backendVersion, clientVersion); - Assert.Equal(x, y); - Assert.Equal(x.GetHashCode(), y.GetHashCode()); - - // Change one parameter at a time and make sure they aren't equal. - y = new UpdateStatus(true, clientUpToDate, legalVersion, backendVersion, clientVersion); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - y = new UpdateStatus(backendCompatible, true, legalVersion, backendVersion, clientVersion); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - y = new UpdateStatus(backendCompatible, clientUpToDate, new Version(2, 2), backendVersion, clientVersion); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - y = new UpdateStatus(backendCompatible, clientUpToDate, legalVersion, 2, clientVersion); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - y = new UpdateStatus(backendCompatible, clientUpToDate, legalVersion, 2, new Version(3, 3)); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - - // Mess around with the versions a bit and make sure they aren't equal. - y = new UpdateStatus(backendCompatible, clientUpToDate, new Version(1, 1, 1), backendVersion, clientVersion); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - y = new UpdateStatus(backendCompatible, clientUpToDate, legalVersion, backendVersion, new Version(2, 2, 2)); - Assert.NotEqual(x, y); - Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); - } -} diff --git a/WalletWasabi/Extensions/StreamReaderWriterExtensions.cs b/WalletWasabi/Extensions/StreamReaderWriterExtensions.cs index e7a94880071..050cbbacd68 100644 --- a/WalletWasabi/Extensions/StreamReaderWriterExtensions.cs +++ b/WalletWasabi/Extensions/StreamReaderWriterExtensions.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using NBitcoin; -using NBitcoin.RPC; using WalletWasabi.Backend.Models; using WalletWasabi.Blockchain.Analysis.FeesEstimation; using WalletWasabi.Blockchain.Blocks; @@ -83,4 +82,21 @@ public static AllFeeEstimate ReadMiningFeeRates(this BinaryReader reader) return new AllFeeEstimate(estimations); } + + public static void Write(this BinaryWriter writer, Version version) + { + writer.Write(version.Major); + writer.Write(version.Minor); + writer.Write(version.Build); + } + + public static Version ReadVersion(this BinaryReader reader) + { + var major = reader.ReadInt32(); + var minor = reader.ReadInt32(); + var build = reader.ReadInt32(); + return build >= 0 + ? new Version(major, minor, build) + : new Version(major, minor); + } } diff --git a/WalletWasabi/Models/UpdateStatus.cs b/WalletWasabi/Models/UpdateStatus.cs index e7aae781245..4b67353e46d 100644 --- a/WalletWasabi/Models/UpdateStatus.cs +++ b/WalletWasabi/Models/UpdateStatus.cs @@ -1,37 +1,3 @@ namespace WalletWasabi.Models; -public class UpdateStatus : IEquatable -{ - public UpdateStatus(bool backendCompatible, bool clientUpToDate, Version legalDocumentsVersion, ushort currentBackendMajorVersion, Version clientVersion) - { - BackendCompatible = backendCompatible; - ClientUpToDate = clientUpToDate; - LegalDocumentsVersion = legalDocumentsVersion; - CurrentBackendMajorVersion = currentBackendMajorVersion; - ClientVersion = clientVersion; - } - - public bool ClientUpToDate { get; } - public bool BackendCompatible { get; } - public bool IsReadyToInstall { get; set; } - - public Version LegalDocumentsVersion { get; } - public ushort CurrentBackendMajorVersion { get; } - - public Version ClientVersion { get; set; } - - #region EqualityAndComparison - - public static bool operator ==(UpdateStatus? x, UpdateStatus? y) - => (x?.ClientUpToDate, x?.BackendCompatible, x?.LegalDocumentsVersion, x?.CurrentBackendMajorVersion, x?.ClientVersion) == (y?.ClientUpToDate, y?.BackendCompatible, y?.LegalDocumentsVersion, y?.CurrentBackendMajorVersion, y?.ClientVersion); - - public static bool operator !=(UpdateStatus? x, UpdateStatus? y) => !(x == y); - - public override bool Equals(object? obj) => Equals(obj as UpdateStatus); - - public bool Equals(UpdateStatus? other) => this == other; - - public override int GetHashCode() => (ClientUpToDate, BackendCompatible, LegalDocumentsVersion, CurrentBackendMajorVersion, ClientVersion).GetHashCode(); - - #endregion EqualityAndComparison -} +public record UpdateStatus(bool ClientUpToDate, bool BackendCompatible, bool IsReadyToInstall, Version ClientVersion); diff --git a/WalletWasabi/Services/Events/Events.cs b/WalletWasabi/Services/Events/Events.cs index ec458a78560..d775bdd37e5 100644 --- a/WalletWasabi/Services/Events/Events.cs +++ b/WalletWasabi/Services/Events/Events.cs @@ -2,14 +2,15 @@ namespace WalletWasabi.Services.Events; -public record ExchangeRateChanged(decimal UsdBtcRate); - public enum FeeRateSource { Backend, LocalNodeRpc, } +public record ExchangeRateChanged(decimal UsdBtcRate); public record MiningFeeRatesChanged(FeeRateSource Source, AllFeeEstimate AllFeeEstimate); public record ServerTipHeightChanged(uint Height); public record ConnectionStateChanged(bool Connected); +public record SoftwareVersionChanged(Version ClientVersion, Version ServerVersion); +public record LegalDocumentVersionChanged(Version Version); diff --git a/WalletWasabi/Services/LegalChecker.cs b/WalletWasabi/Services/LegalChecker.cs index f1ccb6b0eb0..fd345d7fc5e 100644 --- a/WalletWasabi/Services/LegalChecker.cs +++ b/WalletWasabi/Services/LegalChecker.cs @@ -5,7 +5,8 @@ using System.Threading.Tasks; using WalletWasabi.Legal; using WalletWasabi.Logging; -using WalletWasabi.Models; +using WalletWasabi.Services.Events; +using WalletWasabi.WebClients.Wasabi; namespace WalletWasabi.Services; @@ -16,21 +17,20 @@ public class LegalChecker : IDisposable private bool _disposedValue; - public LegalChecker(string dataDir, UpdateChecker updateChecker) + public LegalChecker(string dataDir, WasabiClient wasabiClient, EventBus eventBus) { LegalFolder = Path.Combine(dataDir, LegalFolderName); ProvisionalLegalFolder = Path.Combine(LegalFolder, ProvisionalLegalFolderName); - UpdateChecker = updateChecker; + WasabiClient = wasabiClient; + LegalDocumentVersionSubscription = eventBus.Subscribe(OnLegalDocumentVersionChanged); } - public event EventHandler? AgreedChanged; - - public event EventHandler? ProvisionalChanged; + public IDisposable LegalDocumentVersionSubscription { get; } /// Lock object to guard and property. private AsyncLock LegalDocumentLock { get; } = new(); - private UpdateChecker UpdateChecker { get; } + private WasabiClient WasabiClient { get; } public string LegalFolder { get; } public string ProvisionalLegalFolder { get; } public LegalDocuments? CurrentLegalDocument { get; private set; } @@ -39,7 +39,6 @@ public LegalChecker(string dataDir, UpdateChecker updateChecker) public async Task InitializeAsync() { - UpdateChecker.UpdateStatusChanged += UpdateChecker_UpdateStatusChangedAsync; CurrentLegalDocument = await LegalDocuments.LoadAgreedAsync(LegalFolder).ConfigureAwait(false); ProvisionalLegalDocument = await LegalDocuments.LoadAgreedAsync(ProvisionalLegalFolder).ConfigureAwait(false); @@ -80,8 +79,9 @@ public bool TryGetNewLegalDocs([NotNullWhen(true)] out LegalDocuments? legalDocu return false; } - private async void UpdateChecker_UpdateStatusChangedAsync(object? _, UpdateStatus updateStatus) + private async void OnLegalDocumentVersionChanged(LegalDocumentVersionChanged evnt) { + var legalDocumentsVersion = evnt.Version; try { LegalDocuments? provisionalLegalDocument = null; @@ -89,24 +89,19 @@ private async void UpdateChecker_UpdateStatusChangedAsync(object? _, UpdateStatu using (await LegalDocumentLock.LockAsync().ConfigureAwait(false)) { // If we don't have it or there is a new one. - if (CurrentLegalDocument is null || CurrentLegalDocument.Version < updateStatus.LegalDocumentsVersion) + if (CurrentLegalDocument is null || CurrentLegalDocument.Version < legalDocumentsVersion) { // UpdateChecker cannot be null as the event called by it. - var content = await UpdateChecker!.WasabiClient.GetLegalDocumentsAsync(CancellationToken.None).ConfigureAwait(false); + var content = await WasabiClient.GetLegalDocumentsAsync(CancellationToken.None).ConfigureAwait(false); // Save it as a provisional legal document. - provisionalLegalDocument = new(updateStatus.LegalDocumentsVersion, content); + provisionalLegalDocument = new(legalDocumentsVersion, content); await provisionalLegalDocument.ToFileAsync(ProvisionalLegalFolder).ConfigureAwait(false); ProvisionalLegalDocument = provisionalLegalDocument; LatestDocumentTaskCompletion.TrySetResult(ProvisionalLegalDocument); } } - - if (provisionalLegalDocument is { }) - { - ProvisionalChanged?.Invoke(this, provisionalLegalDocument); - } } catch (Exception ex) { @@ -129,8 +124,6 @@ public async Task AgreeAsync() CurrentLegalDocument = ProvisionalLegalDocument; ProvisionalLegalDocument = null; } - - AgreedChanged?.Invoke(this, CurrentLegalDocument); } protected virtual void Dispose(bool disposing) @@ -139,10 +132,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - if (UpdateChecker is { } updateChecker) - { - updateChecker.UpdateStatusChanged -= UpdateChecker_UpdateStatusChangedAsync; - } + LegalDocumentVersionSubscription.Dispose(); LatestDocumentTaskCompletion.TrySetCanceled(); } diff --git a/WalletWasabi/Services/SatoshiSynchronizer.cs b/WalletWasabi/Services/SatoshiSynchronizer.cs index 781cf4f4f46..a3f51875ac4 100644 --- a/WalletWasabi/Services/SatoshiSynchronizer.cs +++ b/WalletWasabi/Services/SatoshiSynchronizer.cs @@ -133,6 +133,17 @@ async Task StartReceivingMessagesAsync(WebSocket ws) _eventBus.Publish(new ServerTipHeightChanged(height)); break; + case ResponseMessage.SoftwareVersion: + var clientVersion = reader.ReadVersion(); + var serverVersion = reader.ReadVersion(); + _eventBus.Publish(new SoftwareVersionChanged(clientVersion, serverVersion)); + break; + + case ResponseMessage.LegalDocumentVersion: + var version = reader.ReadVersion(); + _eventBus.Publish(new LegalDocumentVersionChanged(version)); + break; + case ResponseMessage.Filter: var filter = reader.ReadFilterModel(); if (localChain.TipHeight + 1 != filter.Header.Height) diff --git a/WalletWasabi/Services/UpdateChecker.cs b/WalletWasabi/Services/UpdateChecker.cs deleted file mode 100644 index 6673c7498df..00000000000 --- a/WalletWasabi/Services/UpdateChecker.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.ComponentModel; -using System.Threading; -using System.Threading.Tasks; -using WalletWasabi.Bases; -using WalletWasabi.Models; -using WalletWasabi.WebClients.Wasabi; - -namespace WalletWasabi.Services; - -public class UpdateChecker : PeriodicRunner -{ - public UpdateChecker(TimeSpan period, WasabiSynchronizer synchronizer) : base(period) - { - Synchronizer = synchronizer; - UpdateStatus = new UpdateStatus(backendCompatible: true, clientUpToDate: true, new Version(), currentBackendMajorVersion: 0, new Version()); - WasabiClient = Synchronizer.WasabiClient; - Synchronizer.PropertyChanged += Synchronizer_PropertyChanged; - } - - public event EventHandler? UpdateStatusChanged; - - private WasabiSynchronizer Synchronizer { get; } - private UpdateStatus UpdateStatus { get; set; } - public WasabiClient WasabiClient { get; } - - private void Synchronizer_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(WasabiSynchronizer.BackendStatus) && - Synchronizer.BackendStatus == BackendStatus.Connected) - { - // Any time when the synchronizer detects the backend, we immediately check the versions. GUI relies on UpdateStatus changes. - TriggerRound(); - } - } - - protected override async Task ActionAsync(CancellationToken cancel) - { - var newUpdateStatus = await WasabiClient.CheckUpdatesAsync(cancel).ConfigureAwait(false); - if (newUpdateStatus != UpdateStatus) - { - UpdateStatus = newUpdateStatus; - UpdateStatusChanged?.Invoke(this, newUpdateStatus); - } - } - - public override void Dispose() - { - Synchronizer.PropertyChanged -= Synchronizer_PropertyChanged; - base.Dispose(); - } -} diff --git a/WalletWasabi/Services/UpdateManager.cs b/WalletWasabi/Services/UpdateManager.cs index 18189dbc903..ff463d812e0 100644 --- a/WalletWasabi/Services/UpdateManager.cs +++ b/WalletWasabi/Services/UpdateManager.cs @@ -12,6 +12,7 @@ using WalletWasabi.Logging; using WalletWasabi.Microservices; using WalletWasabi.Models; +using WalletWasabi.Services.Events; using WalletWasabi.Tor.Http; namespace WalletWasabi.Services; @@ -21,7 +22,7 @@ public class UpdateManager : IDisposable private const byte MaxTries = 2; private const string ReleaseURL = "https://api.github.com/repos/zkSNACKs/WalletWasabi/releases/latest"; - public UpdateManager(string dataDir, bool downloadNewVersion, IHttpClient httpClient, UpdateChecker updateChecker) + public UpdateManager(string dataDir, bool downloadNewVersion, IHttpClient httpClient, EventBus eventBus) { InstallerDir = Path.Combine(dataDir, "Installer"); HttpClient = httpClient; @@ -31,11 +32,11 @@ public UpdateManager(string dataDir, bool downloadNewVersion, IHttpClient httpCl // The feature is disabled on linux at the moment because we install Wasabi Wallet as a Debian package. DownloadNewVersion = downloadNewVersion && !RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - UpdateChecker = updateChecker; - UpdateChecker.UpdateStatusChanged += UpdateChecker_UpdateStatusChangedAsync; + VersionChangedSubscription = eventBus.Subscribe(OnVersionChanged); } public event EventHandler? UpdateAvailableToGet; + private IDisposable VersionChangedSubscription { get; } private string InstallerPath { get; set; } = ""; @@ -48,17 +49,24 @@ public UpdateManager(string dataDir, bool downloadNewVersion, IHttpClient httpCl /// Install new version on shutdown or not. public bool DoUpdateOnClose { get; set; } - private UpdateChecker UpdateChecker { get; } private CancellationTokenSource CancellationTokenSource { get; } = new(); /// Defensive copy of the token to avoid issues with being disposed. private CancellationToken CancellationToken { get; } - private async void UpdateChecker_UpdateStatusChangedAsync(object? sender, UpdateStatus updateStatus) + private async void OnVersionChanged(SoftwareVersionChanged softwareVersionChanged) { + // If the client version locally is greater than or equal to the backend's reported client version, then good. + var clientUpToDate = Helpers.Constants.ClientVersion >= softwareVersionChanged.ClientVersion; + + // If ClientSupportBackendVersionMin <= backend major <= ClientSupportBackendVersionMax, then our software is compatible. + var backendCompatible = + int.Parse(Helpers.Constants.ClientSupportBackendVersionMax) >= softwareVersionChanged.ServerVersion.Major && + softwareVersionChanged.ServerVersion.Major >= int.Parse(Helpers.Constants.ClientSupportBackendVersionMin); + var tries = 0; - bool updateAvailable = !updateStatus.ClientUpToDate || !updateStatus.BackendCompatible; - Version targetVersion = updateStatus.ClientVersion; + bool updateAvailable = !clientUpToDate || !backendCompatible; + Version targetVersion = softwareVersionChanged.ClientVersion; if (!updateAvailable) { @@ -67,6 +75,9 @@ private async void UpdateChecker_UpdateStatusChangedAsync(object? sender, Update return; } + var isReadyToInstall = false; + var clientVersion = softwareVersionChanged.ClientVersion; + if (DownloadNewVersion) { do @@ -77,8 +88,8 @@ private async void UpdateChecker_UpdateStatusChangedAsync(object? sender, Update (string installerPath, Version newVersion) = await GetInstallerAsync(targetVersion, CancellationToken).ConfigureAwait(false); InstallerPath = installerPath; Logger.LogInfo($"Version {newVersion} downloaded successfully."); - updateStatus.IsReadyToInstall = true; - updateStatus.ClientVersion = newVersion; + isReadyToInstall = true; + clientVersion = newVersion; break; } catch (OperationCanceledException ex) @@ -100,10 +111,11 @@ private async void UpdateChecker_UpdateStatusChangedAsync(object? sender, Update { Logger.LogError($"Getting new update failed with error.", ex); } - } while (tries < MaxTries); + } + while (tries < MaxTries); } - UpdateAvailableToGet?.Invoke(this, updateStatus); + UpdateAvailableToGet?.Invoke(this, new UpdateStatus(clientUpToDate, backendCompatible, isReadyToInstall, clientVersion)); } /// @@ -371,8 +383,7 @@ public void StartInstallingNewVersion() public void Dispose() { - UpdateChecker.UpdateStatusChanged -= UpdateChecker_UpdateStatusChangedAsync; - + VersionChangedSubscription.Dispose(); CancellationTokenSource.Cancel(); CancellationTokenSource.Dispose(); } diff --git a/WalletWasabi/Services/WasabiSynchronizer.cs b/WalletWasabi/Services/WasabiSynchronizer.cs index de6474f451e..364bf754208 100644 --- a/WalletWasabi/Services/WasabiSynchronizer.cs +++ b/WalletWasabi/Services/WasabiSynchronizer.cs @@ -105,7 +105,6 @@ protected override async Task ActionAsync(CancellationToken cancel) { SynchronizeResponse response; - ushort lastUsedApiVersion = WasabiClient.ApiVersion; try { if (SmartHeaderChain.TipHash is null) @@ -127,22 +126,6 @@ protected override async Task ActionAsync(CancellationToken cancel) OnSynchronizeRequestFinished(); throw; } - catch (HttpRequestException ex) when (ex.Message.Contains("Not Found")) - { - TorStatus = TorStatus.Running; - - // Backend API version might be updated meanwhile. Trying to update the versions. - var result = await WasabiClient.CheckUpdatesAsync(cancel).ConfigureAwait(false); - - // If the backend is compatible and the Api version updated then we just used the wrong API. - if (result.BackendCompatible && lastUsedApiVersion != WasabiClient.ApiVersion) - { - // Next request will be fine, do not throw exception. - TriggerRound(); - return; - } - throw; - } catch (Exception) { TorStatus = TorStatus.Running; diff --git a/WalletWasabi/Synchronization/FilterMessage.cs b/WalletWasabi/Synchronization/FilterMessage.cs index b51a7c197ca..1e4f40459e3 100644 --- a/WalletWasabi/Synchronization/FilterMessage.cs +++ b/WalletWasabi/Synchronization/FilterMessage.cs @@ -15,7 +15,9 @@ public enum ResponseMessage HandshakeError, BlockHeight, ExchangeRate, - MiningFeeRates + MiningFeeRates, + SoftwareVersion, + LegalDocumentVersion } public record FilterMessage(FilterModel filterModel) diff --git a/WalletWasabi/Synchronization/LegalDocumentVersionMessage.cs b/WalletWasabi/Synchronization/LegalDocumentVersionMessage.cs new file mode 100644 index 00000000000..2975ff29bd7 --- /dev/null +++ b/WalletWasabi/Synchronization/LegalDocumentVersionMessage.cs @@ -0,0 +1,18 @@ +using System.IO; +using WalletWasabi.Extensions; + +namespace WalletWasabi.Synchronization; + +public record LegalDocumentVersionMessage(Version Version) +{ + public byte[] ToByteArray() + { + using var mem = new MemoryStream(); + using var writer = new BinaryWriter(mem); + + writer.Write((byte) ResponseMessage.LegalDocumentVersion); + writer.Write(Version); + + return mem.ToArray(); + } +} diff --git a/WalletWasabi/Synchronization/VersionMessage.cs b/WalletWasabi/Synchronization/VersionMessage.cs new file mode 100644 index 00000000000..a9426e862cc --- /dev/null +++ b/WalletWasabi/Synchronization/VersionMessage.cs @@ -0,0 +1,19 @@ +using System.IO; +using WalletWasabi.Extensions; + +namespace WalletWasabi.Synchronization; + +public record VersionMessage(Version ClientVersion, Version BackendVersion) +{ + public byte[] ToByteArray() + { + using var mem = new MemoryStream(); + using var writer = new BinaryWriter(mem); + + writer.Write((byte)ResponseMessage.SoftwareVersion); + writer.Write(ClientVersion); + writer.Write(BackendVersion); + + return mem.ToArray(); + } +} diff --git a/WalletWasabi/WebClients/Wasabi/WasabiClient.cs b/WalletWasabi/WebClients/Wasabi/WasabiClient.cs index 7029121f8de..fff4f24a220 100644 --- a/WalletWasabi/WebClients/Wasabi/WasabiClient.cs +++ b/WalletWasabi/WebClients/Wasabi/WasabiClient.cs @@ -218,22 +218,6 @@ public async Task> GetMempoolHashesAsync(int compactness, Cancellat return (Version.Parse(resp.ClientVersion), ushort.Parse(resp.BackendMajorVersion), Version.Parse(resp.Ww2LegalDocumentsVersion)); } - public async Task CheckUpdatesAsync(CancellationToken cancel) - { - var (clientVersion, backendMajorVersion, legalDocumentsVersion) = await GetVersionsAsync(cancel).ConfigureAwait(false); - var clientUpToDate = Helpers.Constants.ClientVersion >= clientVersion; // If the client version locally is greater than or equal to the backend's reported client version, then good. - var backendCompatible = int.Parse(Helpers.Constants.ClientSupportBackendVersionMax) >= backendMajorVersion && backendMajorVersion >= int.Parse(Helpers.Constants.ClientSupportBackendVersionMin); // If ClientSupportBackendVersionMin <= backend major <= ClientSupportBackendVersionMax, then our software is compatible. - var currentBackendMajorVersion = backendMajorVersion; - - if (backendCompatible) - { - // Only refresh if compatible. - ApiVersion = currentBackendMajorVersion; - } - - return new UpdateStatus(backendCompatible, clientUpToDate, legalDocumentsVersion, currentBackendMajorVersion, clientVersion); - } - #endregion software #region wasabi