From 3eab672bcd88aa84a9b6110c73e1f23e7379a025 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Mon, 18 Sep 2023 18:48:40 +0100 Subject: [PATCH 01/21] Separate state machine from UI --- src/MasternodeSetupTool/IStateHandler.cs | 84 ++ src/MasternodeSetupTool/MainWindow.xaml.cs | 7 +- .../RegistrationService.cs | 12 +- src/MasternodeSetupTool/StateMachine.cs | 884 ++++++++++++++++++ .../WalletCreationState.cs | 14 + src/MasternodeSetupTool/WalletCredentials.cs | 9 + 6 files changed, 998 insertions(+), 12 deletions(-) create mode 100644 src/MasternodeSetupTool/IStateHandler.cs create mode 100644 src/MasternodeSetupTool/StateMachine.cs create mode 100644 src/MasternodeSetupTool/WalletCreationState.cs create mode 100644 src/MasternodeSetupTool/WalletCredentials.cs diff --git a/src/MasternodeSetupTool/IStateHandler.cs b/src/MasternodeSetupTool/IStateHandler.cs new file mode 100644 index 0000000..4e0b4a6 --- /dev/null +++ b/src/MasternodeSetupTool/IStateHandler.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using MasternodeSetupTool; + +namespace MasternodeSetupTool; + +public interface IStateHandler +{ + public Task OnStart(); + + public Task OnProgramVersionAvailable(string? version); + + public Task OnFederationKeyMissing(); + + public Task OnNodeFailedToStart(NodeType nodeType, string? reason = null); + + public Task OnAskForEULA(); + + public Task OnAskForNewFederationKey(); + + public Task OnShowNewFederationKey(string pubKey, string savePath); + + public Task OnAskToRunIfAlreadyMember(); + + public Task OnAlreadyMember(); + + public Task OnAskForWalletSource(NodeType nodeType); + + public Task OnChooseWallet(List wallets, NodeType nodeType); + + public Task OnChooseAddress(List wallets, NodeType nodeType); + + public Task OnWaitingForCollateral(); + + public Task OnWaitingForRegistrationFee(); + + public Task OnMissingRegistrationFee(string address); + + public Task OnWaitForRegistration(); + + public Task OnRegistrationCanceled(); + + public Task OnRegistrationComplete(); + public Task OnRegistrationFailed(); + + public Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic); + + public Task OnAskForUserMnemonic(NodeType nodeType); + + public Task OnAskForWalletName(NodeType nodeType, bool newWallet); + + public Task OnAskForPassphrase(NodeType nodeType); + + public Task OnAskForWalletPassword(NodeType nodeType); + + public Task OnAskCreatePassword(NodeType nodeType); + + public Task OnAskReenterPassword(NodeType nodeType); + + public Task OnWalletNameExists(); + + public Task OnMnemonicIsInvalid(); + + public Task OnMnemonicExists(); + + public Task OnWalletExistsOrInvalid(NodeType nodeType); + + public Task OnWalletSyncing(NodeType nodeType, int progress); + + public Task OnWalletSynced(NodeType nodeType); + + public Task OnShowWalletName(NodeType nodeType, string walletName); + + public Task OnShowWalletAddress(NodeType nodeType, string address); + + public Task OnRestoreWalletFailed(NodeType nodeType); + public Task OnCreateWalletFailed(NodeType nodeType); + public Task OnResyncFailed(NodeType nodeType); +} + +public enum WalletSource +{ + NewWallet, RestoreWallet, UseExistingWallet +} \ No newline at end of file diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index 4ddd1fb..a760b51 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -38,12 +38,7 @@ public partial class MainWindow : Window, ILogger private bool createdButtons; - private string? collateralWalletMnemonic; - private string? miningWalletMnemonic; - - private string? collateralWalletPassphrase; - private string? miningWalletPassphrase; - + private string? collateralWalletPassword; private string? miningWalletPassword; diff --git a/src/MasternodeSetupTool/RegistrationService.cs b/src/MasternodeSetupTool/RegistrationService.cs index e214179..e9477d1 100644 --- a/src/MasternodeSetupTool/RegistrationService.cs +++ b/src/MasternodeSetupTool/RegistrationService.cs @@ -774,16 +774,16 @@ public string SendTransaction(int apiPort, string hex) return sendActionResult.TransactionId.ToString(); } - public async Task CallJoinFederationRequestAsync(string collateralAddress, string collateralWallet, string collateralPassword, string cirrusWalletName, string cirrusWalletPassword) + public async Task CallJoinFederationRequestAsync(WalletCredentials collateralCredentials, WalletCredentials miningCredentials) { var request = new JoinFederationRequestModel() { - CollateralAddress = collateralAddress, - CollateralWalletName = collateralWallet, - CollateralWalletPassword = collateralPassword, + CollateralAddress = collateralCredentials.ChoosenAddress, + CollateralWalletName = collateralCredentials.Name, + CollateralWalletPassword = collateralCredentials.Password, WalletAccount = "account 0", - WalletName = cirrusWalletName, - WalletPassword = cirrusWalletPassword + WalletName = miningCredentials.Name, + WalletPassword = miningCredentials.Password }; try diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs new file mode 100644 index 0000000..6e52a2e --- /dev/null +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -0,0 +1,884 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; +using MasternodeSetupTool; +using NBitcoin; +using static MasternodeSetupTool.RegistrationService; + +public partial class StateMachine: ILogger +{ + + private readonly RegistrationService registrationService; + private readonly DispatcherTimer timer; + private readonly IStateHandler stateHandler; + + private string currentState = "Begin"; + private string? nextState = null; + + public WalletCredentials? collateralWalletCredentials; + public WalletCredentials? miningWalletCredentials; + + public WalletCreationState? collateralWalletCreationState; + public WalletCreationState? miningWalletCreationState; + + public StateMachine(NetworkType networkType, IStateHandler stateHandler) + { + this.stateHandler = stateHandler; + this.registrationService = new RegistrationService(networkType, this); + + this.timer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + + this.timer.Tick += StateMachine_TickAsync; + this.timer.Start(); + + Task.Run(async () => + { + await this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()); + }); + } + + private async void StateMachine_TickAsync(object? sender, EventArgs e) + { + this.timer.IsEnabled = false; + + // if (this.currentState == "Begin") + // { + // if (!this.createdButtons) + // { + // this.createdButtons = true; + + // Style flatStyle = this.FlatStyle; + + // var button = new Button + // { + // Content = "Run Masternode", + // Tag = "RunMasterNode", + // Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + // Padding = new Thickness(4.0), + // Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + // }; + + // button.Click += new RoutedEventHandler(Button_Click); + // this.stackPanel.Children.Add(button); + + // button = new Button + // { + // Content = "Register Masternode", + // Tag = "SetupMasterNode", + // Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + // Padding = new Thickness(4.0), + // Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + // }; + + // button.Click += new RoutedEventHandler(Button_Click); + + // this.stackPanel.Children.Add(button); + // } + // } + + if (this.nextState == null) + { + this.timer.IsEnabled = true; + + return; + } + + this.currentState = this.nextState; + this.nextState = null; + + if (await RunBranchAsync()) + { + this.timer.IsEnabled = true; + + return; + } + + if (await SetupBranchAsync()) + { + this.timer.IsEnabled = true; + + return; + } + + this.timer.IsEnabled = true; + } + + private async Task RunBranchAsync() + { + // The 'Run' branch + + if (this.currentState == "RunMasterNode_KeyPresent") + { + if (!this.registrationService.CheckFederationKeyExists()) + { + await this.stateHandler.OnFederationKeyMissing(); + + ResetState(); + + return true; + } + + this.nextState = "Run_StartMainChain"; + } + + if (this.currentState == "Run_StartMainChain") + { + if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) + { + await this.stateHandler.OnNodeFailedToStart(NodeType.MainChain); + return false; + } + + this.nextState = "Run_MainChainSynced"; + } + + if (this.currentState == "Run_MainChainSynced") + { + await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureMainChainNodeAddressIndexerIsSyncedAsync().ConfigureAwait(true); + + await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + + this.nextState = "Run_StartSideChain"; + } + + if (this.currentState == "Run_StartSideChain") + { + if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) + { + await this.stateHandler.OnNodeFailedToStart(NodeType.SideChain); + + return false; + } + + this.nextState = "Run_SideChainSynced"; + } + + if (this.currentState == "Run_SideChainSynced") + { + await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureNodeIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + this.nextState = "Run_LaunchBrowser"; + } + + if (this.currentState == "Run_LaunchBrowser") + { + await this.registrationService.StartMasterNodeDashboardAsync().ConfigureAwait(true); + this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); + + ResetState(); + + return true; + } + + return false; + } + + private async Task SetupBranchAsync() + { + if (this.currentState == "SetupMasterNode_Eula") + { + if (!await this.stateHandler.OnAskForEULA()) + { + ResetState(); + return true; + } + + this.nextState = "Setup_KeyPresent"; + } + + if (this.currentState == "Setup_KeyPresent") + { + if (this.registrationService.CheckFederationKeyExists()) + { + if (!await this.stateHandler.OnAskForNewFederationKey()) + { + this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; + return true; + } + + this.registrationService.DeleteFederationKey(); + } + + this.nextState = "Setup_CreateKey"; + } + + if (this.currentState == "Setup_CreateKey") + { + string savePath = this.registrationService.CreateFederationKey(); + + await this.stateHandler.OnShowNewFederationKey(this.registrationService.PubKey, savePath); + + this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_StartMainChain") + { + // All 3 sub-branches of this state require the mainchain and sidechain nodes to be initialized, so do that first. + if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) + { + await this.stateHandler.OnNodeFailedToStart(NodeType.MainChain); + ResetState(); + + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_MainChainSynced"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_MainChainSynced") + { + await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureMainChainNodeAddressIndexerIsSyncedAsync().ConfigureAwait(true); + + await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + + this.nextState = "Setup_CreateRestoreUseExisting_StartSideChain"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_StartSideChain") + { + if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) + { + await this.stateHandler.OnNodeFailedToStart(NodeType.SideChain); + ResetState(); + + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_SideChainSynced"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_SideChainSynced") + { + await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureNodeIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + + this.nextState = "Setup_CreateRestoreUseExisting_CheckIsFederationMember"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_CheckIsFederationMember") + { + if (await this.registrationService.CheckIsFederationMemberAsync().ConfigureAwait(true)) + { + if (await this.stateHandler.OnAskToRunIfAlreadyMember()) + { + this.nextState = "Run_LaunchBrowser"; + return true; + } + else + { + await this.stateHandler.OnAlreadyMember(); + ResetState(); + return true; + } + } + + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Select") + { + //TODO: Probably we need to show picker for collateral and mining wallets independently + switch (await this.stateHandler.OnAskForWalletSource(NodeType.MainChain)) + { + case WalletSource.NewWallet: + this.nextState = "Setup_CreateRestoreUseExisting_Create"; + break; + case WalletSource.RestoreWallet: + this.nextState = "Setup_CreateRestoreUseExisting_Restore"; + break; + case WalletSource.UseExistingWallet: + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting"; + break; + default: + await this.stateHandler.OnRegistrationCanceled(); + ResetState(); + return true; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Create") + { + + if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: true)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_Create_Mining"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Create_Mining") + { + + if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: true)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Create_AskForCollateral") + { + this.collateralWalletCredentials.ChoosenAddress = await HandleAddressSelectionAsync(NodeType.MainChain, this.collateralWalletCredentials.Name); + + if (this.collateralWalletCredentials.ChoosenAddress == null) + { + this.collateralWalletCredentials.ChoosenAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletCredentials.Name).ConfigureAwait(true); + } + + // The 3 sub-branches recombine after this and can share common states. + this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForCollateral") + { + if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletCredentials.Name, RegistrationService.CollateralRequirement).ConfigureAwait(true)) + { + this.nextState = "Setup_CreateRestoreUseExisting_CheckForRegistrationFee"; + } + else + { + await this.stateHandler.OnWaitingForCollateral(); + this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForRegistrationFee") + { + if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) + { + this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; + } + else + { + string? miningAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name).ConfigureAwait(true); + this.miningWalletCredentials.ChoosenAddress = miningAddress; + await this.stateHandler.OnMissingRegistrationFee(miningAddress); + this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForBalance") + { + + if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) + { + this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; + } + else + { + await this.stateHandler.OnWaitingForRegistrationFee(); + this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_PerformRegistration") + { + bool registeredSuccessfully = await this.registrationService.CallJoinFederationRequestAsync(this.collateralWalletCredentials, this.miningWalletCredentials).ConfigureAwait(true); + if (!registeredSuccessfully) + { + await this.stateHandler.OnRegistrationFailed(); + ResetState(); + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_WaitForRegistration"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForRegistration") + { + if (await this.registrationService.MonitorJoinFederationRequestAsync().ConfigureAwait(true)) + { + await this.stateHandler.OnRegistrationComplete(); + this.nextState = "Run_LaunchBrowser"; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Restore") + { + if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: false)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_Restore_Mining"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_Restore_Mining") + { + if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: false)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting") + { + this.collateralWalletCredentials = new WalletCredentials(); + if (!await HandleExistingWalletNameAsync(NodeType.MainChain, this.collateralWalletCredentials)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword") + { + if (!await HandleExistingPasswordAsync(NodeType.MainChain, collateralWalletCredentials)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_Mining"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_Mining") + { + this.miningWalletCredentials = new WalletCredentials(); + if (!await HandleExistingWalletNameAsync(NodeType.SideChain,this.miningWalletCredentials)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword") + { + if (!await HandleExistingPasswordAsync(NodeType.SideChain, miningWalletCredentials)) + { + this.nextState = "Setup_CreateRestoreUseExisting_Select"; + return true; + } + + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced"; + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced") + { + if (await HandleWalletSyncAsync(NodeType.MainChain)) + { + this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced"; + } + else + { + return true; + } + } + + if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced") + { + if (await HandleWalletSyncAsync(NodeType.SideChain)) + { + // Now we can jump back into the same sequence as the other 2 sub-branches. + this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + } + else + { + return true; + } + } + + return false; + } + + private async Task HandleAddressSelectionAsync(NodeType nodeType, string walletName) + { + Network network = nodeType == NodeType.MainChain + ? this.registrationService.MainchainNetwork + : this.registrationService.SidechainNetwork; + + List? addressesWithBalance = await this.registrationService.GetWalletAddressesAsync(walletName, network.DefaultAPIPort); + + if (addressesWithBalance != null) + { + return await this.stateHandler.OnChooseAddress(addressesWithBalance, nodeType); + } + + return null; + } + + private async Task HandleExistingWalletNameAsync(NodeType nodeType, WalletCredentials walletCredentials) + { + Network network = nodeType == NodeType.MainChain + ? this.registrationService.MainchainNetwork + : this.registrationService.SidechainNetwork; + + List? walletsWithBalance = await this.registrationService.GetWalletsWithBalanceAsync(network.DefaultAPIPort); + + if (walletsWithBalance != null) + { + string? walletName = await this.stateHandler.OnChooseWallet(walletsWithBalance, nodeType); + + if (walletName == null) + return false; + + walletCredentials.Name = walletName; + + return true; + } + + return false; + } + + private async Task HandleNewWalletNameAsync(NodeType nodeType, WalletCreationState creationState) + { + do + { + string? walletName = await this.stateHandler.OnAskForWalletName(nodeType, true); + + if (walletName == null) + { + return false; + } + + if (!string.IsNullOrEmpty(walletName)) + { + try + { + Network network = nodeType == NodeType.MainChain + ? this.registrationService.MainchainNetwork + : this.registrationService.SidechainNetwork; + + if (!await this.registrationService.FindWalletByNameAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) + { + creationState.Name = walletName; + break; + } + else + { + await this.stateHandler.OnWalletNameExists(); + } + } + catch + { + } + } + + await this.stateHandler.OnWalletExistsOrInvalid(nodeType); + } while (true); + + return true; + } + + private async Task HandleNewMnemonicAsync(NodeType nodeType, WalletCreationState creationState, bool canChangeMnemonic = false) + { + var mnemonic = string.Join(' ', new Mnemonic("English", WordCount.Twelve).Words); + + if (await this.stateHandler.OnAskForMnemonicConfirmation(nodeType, mnemonic)) + { + return false; + } + + creationState.Mnemonic = mnemonic; + + return true; + } + + private async Task HandleUserMnemonic(NodeType nodeType, WalletCreationState creationState) + { + string? mnemonic; + + do + { + mnemonic = await this.stateHandler.OnAskForUserMnemonic(nodeType); + + if (mnemonic == null) + { + return false; + } + + if (!string.IsNullOrEmpty(mnemonic)) + { + try + { + // Test the mnemonic to ensure validity. + var temp = new Mnemonic(mnemonic, Wordlist.English); + + // If there was no exception, break out of the loop and continue. + break; + } + catch + { + } + } + + await this.stateHandler.OnMnemonicIsInvalid(); + } while (true); + + creationState.Mnemonic = mnemonic; + + return true; + } + + private async Task HandlePassphraseAsync(NodeType nodeType, WalletCreationState creationState) + { + string result = await this.stateHandler.OnAskForPassphrase(nodeType); + + creationState.Passphrase = result; + + return true; + } + + private async Task HandleExistingPasswordAsync(NodeType nodeType, WalletCredentials credentials) + { + while (true) + { + string password = await this.stateHandler.OnAskForWalletPassword(nodeType); + + if (await this.registrationService.CheckWalletPasswordAsync(NodeApiPort(nodeType), credentials.Name, password) == false) + { + if (!await this.stateHandler.OnAskReenterPassword(nodeType)) + { + return false; + } + + continue; + } + + credentials.Password = password; + + return true; + } + } + + private async Task HandleNewPasswordAsync(NodeType nodeType, WalletCreationState creationState) + { + string password = await this.stateHandler.OnAskForWalletPassword(nodeType); + + if (password == null) + { + return false; + } + + creationState.Password = password; + return true; + } + + private async Task HandleWalletCreationAsync(NodeType nodeType, WalletCreationState walletCreationState, bool createNewWallet) + { + Network network = nodeType == NodeType.MainChain + ? this.registrationService.MainchainNetwork + : this.registrationService.SidechainNetwork; + + if (walletCreationState == null ||!walletCreationState.IsValid()) + { + return false; + } + + while (true) + { + try + { + if (!await this.registrationService.RestoreWalletAsync(network.DefaultAPIPort, nodeType, walletCreationState.Name, walletCreationState.Mnemonic, walletCreationState.Passphrase, walletCreationState.Password, createNewWallet).ConfigureAwait(true)) + { + if (createNewWallet) + await this.stateHandler.OnCreateWalletFailed(nodeType); + else + await this.stateHandler.OnRestoreWalletFailed(nodeType); + + return false; + } + break; + } + catch (WalletCollisionException) + { + await this.stateHandler.OnMnemonicExists(); + + if (!await HandleNewMnemonicAsync(nodeType, walletCreationState, canChangeMnemonic: true)) + { + await this.stateHandler.OnRegistrationCanceled(); + return false; + } + } + } + + if (!await this.registrationService.ResyncWalletAsync(network.DefaultAPIPort, walletCreationState.Name).ConfigureAwait(true)) + { + await this.stateHandler.OnResyncFailed(nodeType); + return false; + } + + WalletCredentials newCredentials = new WalletCredentials + { + Name = walletCreationState.Name, + Password = walletCreationState.Password, + }; + + if (nodeType == NodeType.MainChain) + { + this.collateralWalletCredentials = newCredentials; + } + else if (nodeType == NodeType.SideChain) + { + this.miningWalletCredentials = newCredentials; + } + + return true; + } + + private async Task HandleWalletSyncAsync(NodeType nodeType) + { + Network network = nodeType == NodeType.MainChain + ? this.registrationService.MainchainNetwork + : this.registrationService.SidechainNetwork; + + string? walletName = nodeType == NodeType.MainChain + ? this.collateralWalletCredentials.Name + : this.miningWalletCredentials.Name; + + if (walletName == null) + { + throw new ArgumentException("Wallet name can not be null."); + } + + int percentSynced = await this.registrationService.WalletSyncProgressAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true); + this.stateHandler.OnWalletSyncing(nodeType, percentSynced); + if (await this.registrationService.IsWalletSyncedAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) + { + this.stateHandler.OnWalletSynced(nodeType); + return true; + } + else + { + return false; + } + } + + private async Task HandleCreateWalletsAsync(NodeType nodeType, bool createNewMnemonic) + { + WalletCreationState walletCreationState = new WalletCreationState(); + + if (createNewMnemonic) + { + if (!await HandleNewMnemonicAsync(nodeType, walletCreationState)) + { + return false; + } + } + else + { + if (!await HandleUserMnemonic(nodeType, walletCreationState)) + { + return false; + } + } + + if (!await HandleNewWalletNameAsync(nodeType, walletCreationState)) + { + return false; + } + + if (!await HandlePassphraseAsync(nodeType, walletCreationState)) + { + return false; + } + + if (!await HandleNewPasswordAsync(nodeType, walletCreationState)) + { + return false; + } + + if (!await HandleWalletCreationAsync(nodeType, walletCreationState, createNewMnemonic)) + { + return false; + } + + try + { + while (!await HandleWalletSyncAsync(nodeType)) + { + await Task.Delay(TimeSpan.FromSeconds(1)); + } + } + catch + { + return false; + } + + return true; + } + + private void ResetState() + { + this.nextState = null; + this.currentState = "Begin"; + } + + public static string? GetInformationalVersion() => + Assembly + .GetExecutingAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion; + + private string WalletTypeName(NodeType nodeType) + { + return nodeType == NodeType.MainChain ? "collateral" : "mining"; + } + + private WalletCredentials? GetWalletCredentials(NodeType nodeType) + { + if (nodeType == NodeType.MainChain) + { + return collateralWalletCredentials; + } + else + { + return miningWalletCredentials; + } + } + + private int NodeApiPort(NodeType nodeType) + { + Network network = nodeType == NodeType.MainChain ? this.registrationService.MainchainNetwork : this.registrationService.SidechainNetwork; + return network.DefaultAPIPort; + } + + public void Info(string message, string? updateTag = null) + { + throw new NotImplementedException(); + } + + public void Error(string message) + { + throw new NotImplementedException(); + } + + public void Error(Exception exception) + { + throw new NotImplementedException(); + } + + public void Error(string message, Exception exception) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/MasternodeSetupTool/WalletCreationState.cs b/src/MasternodeSetupTool/WalletCreationState.cs new file mode 100644 index 0000000..3e8853d --- /dev/null +++ b/src/MasternodeSetupTool/WalletCreationState.cs @@ -0,0 +1,14 @@ +namespace MasternodeSetupTool; + +public class WalletCreationState +{ + public string Name; + public string Mnemonic; + public string Passphrase; + public string Password; + + public bool IsValid() + { + return !string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Mnemonic) && !string.IsNullOrEmpty(Password); + } +} \ No newline at end of file diff --git a/src/MasternodeSetupTool/WalletCredentials.cs b/src/MasternodeSetupTool/WalletCredentials.cs new file mode 100644 index 0000000..5ff70a4 --- /dev/null +++ b/src/MasternodeSetupTool/WalletCredentials.cs @@ -0,0 +1,9 @@ +namespace MasternodeSetupTool; + +public class WalletCredentials +{ + public string Name; + public string Password; + + public string ChoosenAddress; +} \ No newline at end of file From d0f704b040c2bd6149840f78df51b991673fbc8e Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Tue, 19 Sep 2023 13:38:23 +0100 Subject: [PATCH 02/21] Implement StateHandler --- src/MasternodeSetupTool/IStateHandler.cs | 10 +- src/MasternodeSetupTool/MainWindow.xaml.cs | 1263 +++++--------------- src/MasternodeSetupTool/StateMachine.cs | 62 +- 3 files changed, 356 insertions(+), 979 deletions(-) diff --git a/src/MasternodeSetupTool/IStateHandler.cs b/src/MasternodeSetupTool/IStateHandler.cs index 4e0b4a6..cd0b7ef 100644 --- a/src/MasternodeSetupTool/IStateHandler.cs +++ b/src/MasternodeSetupTool/IStateHandler.cs @@ -4,7 +4,7 @@ namespace MasternodeSetupTool; -public interface IStateHandler +public interface IStateHandler: ILogger { public Task OnStart(); @@ -36,8 +36,6 @@ public interface IStateHandler public Task OnMissingRegistrationFee(string address); - public Task OnWaitForRegistration(); - public Task OnRegistrationCanceled(); public Task OnRegistrationComplete(); @@ -57,11 +55,11 @@ public interface IStateHandler public Task OnAskReenterPassword(NodeType nodeType); - public Task OnWalletNameExists(); + public Task OnWalletNameExists(NodeType nodeType); - public Task OnMnemonicIsInvalid(); + public Task OnMnemonicIsInvalid(NodeType nodeType); - public Task OnMnemonicExists(); + public Task OnMnemonicExists(NodeType nodeType); public Task OnWalletExistsOrInvalid(NodeType nodeType); diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index a760b51..001d2e4 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -8,11 +7,7 @@ using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; -using System.Windows.Threading; -using CSharpFunctionalExtensions; using NBitcoin; -using Stratis.Bitcoin.Features.Wallet.Models; -using static MasternodeSetupTool.RegistrationService; using Color = System.Windows.Media.Color; namespace MasternodeSetupTool @@ -20,7 +15,7 @@ namespace MasternodeSetupTool /// /// Interaction logic for MainWindow.xaml /// - public partial class MainWindow : Window, ILogger + public partial class MainWindow : Window, IStateHandler { private const string MainStackPanelTag = "Main"; private const string StatusBarTextBlockTag = "txtStatusBar"; @@ -29,44 +24,9 @@ public partial class MainWindow : Window, ILogger private readonly StackPanel stackPanel; private readonly TextBlock statusBar; - private readonly RegistrationService registrationService; - - private readonly DispatcherTimer timer; - - private string currentState = "Begin"; - private string? nextState = null; - private bool createdButtons; - - private string? collateralWalletPassword; - private string? miningWalletPassword; - - private string? collateralWalletName; - private string? miningWalletName; - - private string? _collateralAddress; - private string? _miningAddress; - - private string? CollateralAddress - { - get => this._collateralAddress; - set - { - this._collateralAddress = value; - this.CollateralAddressText.Text = $"Collateral wallet address: {value}"; - } - } - - private string? MiningAddress - { - get => this._miningAddress; - set - { - this._miningAddress = value; - this.MiningAddressText.Text = $"Mining wallet address: {value}"; - } - } + private StateMachine stateMachine; private bool PrintStacktraces { @@ -92,14 +52,7 @@ private Style FlatStyle public MainWindow(string[] args) { InitializeComponent(); - - string? appVersion = GetInformationalVersion(); - - if (appVersion != null) - { - this.VersionText.Text = $"Version: {appVersion}"; - } - + this.stackPanel = (StackPanel)this.FindName(MainStackPanelTag); this.statusBar = (TextBlock)this.FindName(StatusBarTextBlockTag); @@ -111,1070 +64,516 @@ public MainWindow(string[] args) if (args.Any(a => a.Contains("-regtest"))) networkType = NetworkType.Regtest; - this.registrationService = new RegistrationService(networkType, this); - - this.timer = new DispatcherTimer - { - Interval = TimeSpan.FromSeconds(1) - }; - - this.timer.Tick += StateMachine_TickAsync; - this.timer.Start(); + this.stateMachine = new StateMachine(networkType, this); } - private async void StateMachine_TickAsync(object? sender, EventArgs e) + private void Button_Click(object sender, RoutedEventArgs e) { - this.timer.IsEnabled = false; - - if (this.currentState == "Begin") - { - if (!this.createdButtons) - { - this.createdButtons = true; - - Style flatStyle = this.FlatStyle; - - var button = new Button - { - Content = "Run Masternode", - Tag = "RunMasterNode", - Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - Padding = new Thickness(4.0), - Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - }; - - button.Click += new RoutedEventHandler(Button_Click); - this.stackPanel.Children.Add(button); - - button = new Button - { - Content = "Register Masternode", - Tag = "SetupMasterNode", - Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - Padding = new Thickness(4.0), - Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - }; - - button.Click += new RoutedEventHandler(Button_Click); - - this.stackPanel.Children.Add(button); - } - } - - if (this.nextState == null) - { - this.timer.IsEnabled = true; - - return; - } - - this.currentState = this.nextState; - this.nextState = null; + Button button = (Button)sender; - if (await RunBranchAsync()) + if (button == null) { - this.timer.IsEnabled = true; - return; } - if (await SetupBranchAsync()) + switch (button.Tag.ToString()) { - this.timer.IsEnabled = true; - - return; + case "RunMasterNode": + { + this.stateMachine.OnRunNode(); + break; + } + case "SetupMasterNode": + { + this.stateMachine.OnSetupNode(); + break; + } } - - this.timer.IsEnabled = true; } - private async Task RunBranchAsync() + private void LogWithBrush(string message, Brush? brush = null, string? updateTag = null) { - // The 'Run' branch - - if (this.currentState == "RunMasterNode_KeyPresent") + this.statusBar.Dispatcher.Invoke(() => { - if (!this.registrationService.CheckFederationKeyExists()) - { - MessageBox.Show("Federation key does not exist", "Key file missing", MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.Yes); - - ResetState(); - - return true; - } - - this.nextState = "Run_StartMainChain"; - } + var inline = new Run(message + "\n"); + inline.Tag = updateTag; - if (this.currentState == "Run_StartMainChain") - { - if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) + if (brush != null) { - Error("Cannot start the Mainchain node, aborting..."); - return false; + inline.Foreground = brush; } - this.nextState = "Run_MainChainSynced"; - } - - if (this.currentState == "Run_MainChainSynced") - { - await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - - await this.registrationService.EnsureMainChainNodeAddressIndexerIsSyncedAsync().ConfigureAwait(true); - - await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - - this.nextState = "Run_StartSideChain"; - } + InlineCollection inlines = this.statusBar.Inlines; + Inline lastInline = inlines.LastInline; - if (this.currentState == "Run_StartSideChain") - { - if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) + if (updateTag != null && lastInline != null && string.Equals(lastInline.Tag, updateTag)) { - Error("Cannot start the Mainchain node, aborting..."); - return false; + inlines.Remove(lastInline); } - this.nextState = "Run_SideChainSynced"; - } + inlines.Add(inline); + this.logScrollArea.ScrollToBottom(); + }); + } - if (this.currentState == "Run_SideChainSynced") + private void Log(string message, string? updateTag = null) + { + this.statusBar.Dispatcher.Invoke(() => { - await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - - await this.registrationService.EnsureNodeIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - - await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - - this.nextState = "Run_LaunchBrowser"; - } + LogWithBrush(message, brush: null, updateTag); + }); + } - if (this.currentState == "Run_LaunchBrowser") + private void Log(string message, Color color, string? updateTag = null) + { + this.statusBar.Dispatcher.Invoke(() => { - await this.registrationService.StartMasterNodeDashboardAsync().ConfigureAwait(true); - this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); + LogWithBrush(message, new SolidColorBrush(color), updateTag); + }); + } - ResetState(); + private void LogError(string message) + { + Log(message, Color.FromRgb(255, 51, 51)); + } - return true; - } + public void Info(string message, string? updateTag = null) + { + Log(message, updateTag: updateTag); + } - return false; + public void Error(string message) + { + LogError(message); } - private async Task SetupBranchAsync() + public void Error(Exception exception) { - if (this.currentState == "SetupMasterNode_Eula") + if (this.PrintStacktraces) { - if (MessageBox.Show("100K collateral is required to operate a Masternode; in addition, a balance of 500.1 CRS is required to fund the registration transaction. Are you happy to proceed?", "End-User License Agreement", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) != MessageBoxResult.Yes) - { - ResetState(); - - return true; - } - - this.nextState = "Setup_KeyPresent"; + LogError($"{exception}"); } + } - if (this.currentState == "Setup_KeyPresent") - { - if (this.registrationService.CheckFederationKeyExists()) - { - if (MessageBox.Show("Federation key exists. Shall we create a new one?", "Key file already present", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.No) - { - this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; - return true; - } + public void Error(string message, Exception exception) + { + Error(message); + Error(exception); + } - this.registrationService.DeleteFederationKey(); - } + private string WalletTypeName(NodeType nodeType) + { + return nodeType == NodeType.MainChain ? "collateral" : "mining"; + } - this.nextState = "Setup_CreateKey"; - } + public static string? GetInformationalVersion() => + Assembly + .GetExecutingAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion; - if (this.currentState == "Setup_CreateKey") + public Task OnStart() + { + if (!this.createdButtons) { - string savePath = this.registrationService.CreateFederationKey(); + this.createdButtons = true; - MessageBox.Show($"Your Masternode public key is: {this.registrationService.PubKey}"); - MessageBox.Show($"Your private key has been saved in the root Cirrus data folder:\r\n{savePath}. Please ensure that you keep a backup of this file."); + Style flatStyle = this.FlatStyle; - this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; - } + var button = new Button + { + Content = "Run Masternode", + Tag = "RunMasterNode", + Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + Padding = new Thickness(4.0), + Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + }; - if (this.currentState == "Setup_CreateRestoreUseExisting_StartMainChain") - { - // All 3 sub-branches of this state require the mainchain and sidechain nodes to be initialized, so do that first. - if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) + button.Click += new RoutedEventHandler(Button_Click); + this.stackPanel.Children.Add(button); + + button = new Button { - Error("Cannot start the Mainchain node, aborting..."); - ResetState(); + Content = "Register Masternode", + Tag = "SetupMasterNode", + Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + Padding = new Thickness(4.0), + Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + }; - return true; - } + button.Click += new RoutedEventHandler(Button_Click); - this.nextState = "Setup_CreateRestoreUseExisting_MainChainSynced"; + this.stackPanel.Children.Add(button); } - if (this.currentState == "Setup_CreateRestoreUseExisting_MainChainSynced") - { - await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + return Task.CompletedTask; + } - await this.registrationService.EnsureMainChainNodeAddressIndexerIsSyncedAsync().ConfigureAwait(true); + public Task OnProgramVersionAvailable(string? version) + { + if (version != null) + { + this.VersionText.Text = $"Version: {version}"; + } - await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); + return Task.CompletedTask; + } - this.nextState = "Setup_CreateRestoreUseExisting_StartSideChain"; - } + public Task OnFederationKeyMissing() + { + MessageBox.Show("Federation key does not exist", "Key file missing", MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.Yes); + return Task.CompletedTask; + } - if (this.currentState == "Setup_CreateRestoreUseExisting_StartSideChain") + public Task OnNodeFailedToStart(NodeType nodeType, string? reason = null) + { + Error($"Cannot start the {nodeType} node, aborting..."); + if (reason != null) { - if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) - { - Error("Cannot start the Sidechain node, aborting..."); - ResetState(); + Error($"Reason: {reason}"); + } + return Task.CompletedTask; + } - return true; - } + public Task OnAskForEULA() + { + return Task.Run( + () => MessageBox.Show("100K collateral is required to operate a Masternode; in addition, a balance of 500.1 CRS is required to fund the registration transaction. Are you happy to proceed?", + "End-User License Agreement", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.Yes); + } - this.nextState = "Setup_CreateRestoreUseExisting_SideChainSynced"; - } + public Task OnAskForNewFederationKey() + { + return Task.Run(() => + { + return MessageBox.Show( + "Federation key exists. Shall we create a new one?", + "Key file already present", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.Yes; + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_SideChainSynced") + public Task OnShowNewFederationKey(string pubKey, string savePath) + { + return Task.Run(() => { - await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + MessageBox.Show($"Your Masternode public key is: {pubKey}"); + MessageBox.Show($"Your private key has been saved in the root Cirrus data folder:\r\n{savePath}. Please ensure that you keep a backup of this file."); + }); + } - await this.registrationService.EnsureNodeIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); + public Task OnAskToRunIfAlreadyMember() + { + return Task.Run(() => + { + return MessageBox.Show("Your node is already a member of a federation. Do you want to run the Masternode Dashboard instead?", + "Member of a federation", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.Yes; + }); - await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - - this.nextState = "Setup_CreateRestoreUseExisting_CheckIsFederationMember"; - } + } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckIsFederationMember") + public Task OnAlreadyMember() + { + return Task.Run(() => { - if (await this.registrationService.CheckIsFederationMemberAsync().ConfigureAwait(true)) - { - if (MessageBox.Show("Your node is already a member of a federation. Do you want to run the Masternode Dashboard instead?", "Member of a federation", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.Yes) - { - this.nextState = "Run_LaunchBrowser"; - return true; - } - else - { - Info("Your node is already a member of a federation. Consider using 'Run Masternode' instead."); - ResetState(); - return true; - } - } - - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - } + Info("Your node is already a member of a federation. Consider using 'Run Masternode' instead."); + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_Select") + public Task OnAskForWalletSource(NodeType nodeType) + { + return Task.Run(() => { var dialog = new CreateRestoreUseExisting(); dialog.ShowDialog(); if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.CreateWallet) { - this.nextState = "Setup_CreateRestoreUseExisting_Create"; + return WalletSource.NewWallet; } if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.RestoreWallet) { - this.nextState = "Setup_CreateRestoreUseExisting_Restore"; + return WalletSource.RestoreWallet; } if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.UseExistingWallet) { - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting"; + return WalletSource.UseExistingWallet; } - if (dialog.Choice == null) - { - LogError("Registration cancelled."); - ResetState(); - return true; - } - } + return null; + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create") + public Task OnChooseWallet(List wallets, NodeType nodeType) + { + return Task.Run(() => { + var selectionDialog = new WalletSelectionDialog(wallets); + selectionDialog.ShowDialog(); - if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: true)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_Create_Mining"; - } + return selectionDialog.SelectedWalletName; + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create_Mining") + public Task OnChooseAddress(List addresses, NodeType nodeType) + { + return Task.Run(() => { + var selectionDialog = new AddressSelectionDialog(addresses); + selectionDialog.ShowDialog(); - if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: true)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; - } + return selectionDialog.SelectedAddress; + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create_AskForCollateral") + public Task OnWaitingForCollateral() + { + return Task.Run(() => { - this.CollateralAddress = await HandleAddressSelectionAsync(NodeType.MainChain, collateralWalletName); - - if (this.CollateralAddress == null) - { - this.CollateralAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletName).ConfigureAwait(true); - - new ShowAddressDialog(NodeType.MainChain, this.CollateralAddress).ShowDialog(); - } - - // The 3 sub-branches recombine after this and can share common states. - this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; - } + Log($"Waiting for collateral wallet to have a balance of at least {RegistrationService.CollateralRequirement} STRAX", updateTag: "OnWaitingForCollateral"); + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForCollateral") + public Task OnWaitingForRegistrationFee() + { + return Task.Run(() => { - if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletName, RegistrationService.CollateralRequirement).ConfigureAwait(true)) - { - this.nextState = "Setup_CreateRestoreUseExisting_CheckForRegistrationFee"; - } - else - { - Log($"Waiting for collateral wallet to have a balance of at least {RegistrationService.CollateralRequirement} STRAX", updateTag: this.currentState); - this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; - } - } + Log("Waiting for registration fee to be sent to the mining wallet...", updateTag: "OnWaitingForRegistrationFee"); + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForRegistrationFee") + public Task OnMissingRegistrationFee(string address) + { + return Task.Run(() => { - if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletName, RegistrationService.FeeRequirement).ConfigureAwait(true)) - { - this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; - } - else - { - string? miningAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletName).ConfigureAwait(true); - this.MiningAddress = miningAddress; - Error($"Insufficient balance to pay registration fee. Please send 500.1 CRS to the mining wallet on address: {miningAddress}"); - this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; - } - } + Error($"Insufficient balance to pay registration fee. Please send 500.1 CRS to the mining wallet on address: {address}"); + }); + } - // if (this.currentState == "Setup_CreateRestoreUseExisting_PerformCrossChain") - // { - // if (MessageBox.Show("Insufficient balance in the mining wallet. Perform a cross-chain transfer of 500.1 STRAX?", "Registration Fee Missing", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.No) - // { - // this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; - // return true; - // } - - // this.cirrusAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletName).ConfigureAwait(true); - - // if (await this.registrationService.PerformCrossChainTransferAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletName, this.collateralWalletPassword, "500.1", this.cirrusAddress, this.collateralAddress).ConfigureAwait(true)) - // { - // this.nextState = "Setup_CreateRestoreUseExisting_WaitForCrossChainTransfer"; - // } - // } - - // if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForCrossChainTransfer") - // { - - // if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletName, RegistrationService.FeeRequirement).ConfigureAwait(true)) - // { - // this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; - // } - // else - // { - // Log("Waiting for registration fee to be sent via cross-chain transfer...", updateTag: this.currentState); - // await Task.Delay(TimeSpan.FromSeconds(30)); - // this.nextState = "Setup_CreateRestoreUseExisting_WaitForCrossChainTransfer"; - // } - // } - - if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForBalance") + public Task OnRegistrationCanceled() + { + return Task.Run(() => { + LogError("Registration cancelled."); + }); + } - if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletName, RegistrationService.FeeRequirement).ConfigureAwait(true)) - { - this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; - } - else - { - Log("Waiting for registration fee to be sent to the mining wallet...", updateTag: this.currentState); - this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; - } - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_PerformRegistration") + public Task OnRegistrationComplete() + { + return Task.Run(() => { - bool registeredSuccessfully = await this.registrationService.CallJoinFederationRequestAsync(this.CollateralAddress, this.collateralWalletName, this.collateralWalletPassword, this.miningWalletName, this.miningWalletPassword).ConfigureAwait(true); - if (!registeredSuccessfully) - { - Error("Failed to register your masternode, aborting..."); - ResetState(); - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_WaitForRegistration"; - } + Log("Registration complete"); + }); + } - if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForRegistration") + public Task OnRegistrationFailed() + { + return Task.Run(() => { - if (await this.registrationService.MonitorJoinFederationRequestAsync().ConfigureAwait(true)) - { - Log("Registration complete"); - this.nextState = "Run_LaunchBrowser"; - } - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_Restore") - { - if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: false)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_Restore_Mining"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_Restore_Mining") - { - if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: false)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting") - { - if (!await HandleExistingWalletNameAsync(NodeType.MainChain)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword") - { - if (!await HandlePasswordAsync(NodeType.MainChain)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_Mining"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_Mining") - { - if (!await HandleExistingWalletNameAsync(NodeType.SideChain)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword") - { - if (!await HandlePasswordAsync(NodeType.SideChain)) - { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; - return true; - } - - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced"; - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced") - { - if (await HandleWalletSyncAsync(NodeType.MainChain)) - { - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced"; - } - else - { - return true; - } - } - - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced") - { - if (await HandleWalletSyncAsync(NodeType.SideChain)) - { - // Now we can jump back into the same sequence as the other 2 sub-branches. - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; - } - else - { - return true; - } - } - - return false; - } - - private async Task HandleAddressSelectionAsync(NodeType nodeType, string walletName) - { - Network network = nodeType == NodeType.MainChain - ? this.registrationService.MainchainNetwork - : this.registrationService.SidechainNetwork; - - List? addressesWithBalance = await this.registrationService.GetWalletAddressesAsync(walletName, network.DefaultAPIPort); - - if (addressesWithBalance != null) - { - var selectionDialog = new AddressSelectionDialog(addressesWithBalance); - selectionDialog.ShowDialog(); - - return selectionDialog.SelectedAddress; - } - - return null; + Error("Failed to register your masternode, aborting..."); + }); } - private async Task HandleExistingWalletNameAsync(NodeType nodeType) + public Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) { - string? walletName; - - Network network = nodeType == NodeType.MainChain - ? this.registrationService.MainchainNetwork - : this.registrationService.SidechainNetwork; - - List? wallesWithBalance = await this.registrationService.GetWalletsWithBalanceAsync(network.DefaultAPIPort); - - if (wallesWithBalance != null) - { - var selectionDialog = new WalletSelectionDialog(wallesWithBalance); - selectionDialog.ShowDialog(); - - if (selectionDialog.SelectedWalletName != null) - { - walletName = selectionDialog.SelectedWalletName; - } - else - { - return false; - } - } - else - { - do - { - var inputBox = new InputBox($"Please enter your {WalletTypeName(nodeType)} ({nodeType}) wallet name:"); - - walletName = inputBox.ShowDialog(); - - if (walletName == null) - { - return false; - } - - if (!string.IsNullOrEmpty(walletName)) - { - try - { - if (await this.registrationService.FindWalletByNameAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) - { - break; - } - } - catch - { - } - } - - MessageBox.Show($"Please ensure that you enter a valid {WalletTypeName(nodeType)} ({nodeType}) wallet name", "Error", MessageBoxButton.OK); - } while (true); - } - - if (nodeType == NodeType.MainChain) - { - this.collateralWalletName = walletName; - } - else + return Task.Run(() => { - this.miningWalletName = walletName; - } - - return true; + var dialog = new ConfirmationDialog($"Enter mnemonic for the {WalletTypeName(nodeType)} wallet", "Mnemonic", mnemonic, false); + dialog.ShowDialog(); + return dialog.DialogResult == true; + }); } - private async Task HandleNewWalletNameAsync(NodeType nodeType) + public Task OnAskForUserMnemonic(NodeType nodeType) { - string? walletName; - do - { - var inputBox = new InputBox($"Please enter new {WalletTypeName(nodeType)} ({nodeType}) wallet name:"); - - walletName = inputBox.ShowDialog(); - - if (walletName == null) - { - return false; - } - - if (!string.IsNullOrEmpty(walletName)) - { - try - { - Network network = nodeType == NodeType.MainChain - ? this.registrationService.MainchainNetwork - : this.registrationService.SidechainNetwork; - - if (!await this.registrationService.FindWalletByNameAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) - { - break; - } - else - { - MessageBox.Show("A wallet with this name already exists", "Error"); - } - } - catch - { - } - } - - MessageBox.Show($"Please ensure that you enter a valid (and non-existing) {WalletTypeName(nodeType)} ({nodeType}) wallet name", "Error", MessageBoxButton.OK); - } while (true); - - if (nodeType == NodeType.MainChain) + return Task.Run(() => { - this.collateralWalletName = walletName; - } - else - { - this.miningWalletName = walletName; - } - - return true; + var inputBox = new InputBox($"Please enter your mnemonic for the {WalletTypeName(nodeType)} ({nodeType}) wallet", "Mnemonic"); + return inputBox.ShowDialog(); + }); } - private bool HandleNewMnemonic(NodeType nodeType, bool canChangeMnemonic = false) + public Task OnAskForWalletName(NodeType nodeType, bool newWallet) { - var mnemonic = string.Join(' ', new Mnemonic("English", WordCount.Twelve).Words); - - var dialog = new ConfirmationDialog($"Enter mnemonic for the {WalletTypeName(nodeType)} wallet", "Mnemonic", mnemonic, canChangeMnemonic); - dialog.ShowDialog(); - - if (dialog.DialogResult != true) + return Task.Run(() => { - return false; - } - - if (nodeType == NodeType.MainChain) - { - this.collateralWalletMnemonic = mnemonic; - } - else - { - this.miningWalletMnemonic = mnemonic; - } - - return true; + var inputBox = new InputBox($"Please enter {nodeType} wallet name:"); + return inputBox.ShowDialog(); + }); } - private bool HandleUserMnemonic(NodeType nodeType) + public Task OnAskForPassphrase(NodeType nodeType) { - string? mnemonic; - - do + return Task.Run(() => { - var inputBox = new InputBox($"Please enter your mnemonic for the {WalletTypeName(nodeType)} ({nodeType}) wallet", "Mnemonic"); + var dialog = new ConfirmationDialog($"Enter passphrase for the {nodeType} wallet", + "Passphrase", + "", + true, + allowEmpty: true); - mnemonic = inputBox.ShowDialog(); + dialog.ShowDialog(); - if (mnemonic == null) + if (dialog.DialogResult != true) { - return false; + return null; } - if (!string.IsNullOrEmpty(mnemonic)) - { - try - { - // Test the mnemonic to ensure validity. - var temp = new Mnemonic(mnemonic, Wordlist.English); - - // If there was no exception, break out of the loop and continue. - break; - } - catch - { - } - } - - MessageBox.Show("Please ensure that you enter a valid mnemonic", "Error", MessageBoxButton.OK); - } while (true); - - if (nodeType == NodeType.MainChain) - { - this.collateralWalletMnemonic = mnemonic; - } - else - { - this.miningWalletMnemonic = mnemonic; - } - - return true; - } - - private bool HandlePassphrase(NodeType nodeType) - { - var dialog = new ConfirmationDialog($"Enter passphrase for the {WalletTypeName(nodeType)} wallet", - "Passphrase", - "", - true, - allowEmpty: true); - - dialog.ShowDialog(); - - if (dialog.DialogResult != true) - { - return false; - } - - string result = dialog.Text2.Text ?? ""; - - if (nodeType == NodeType.MainChain) - { - this.collateralWalletPassphrase = result; - } - else - { - this.miningWalletPassphrase = result; - } - - return true; + return dialog.Text2.Text ?? ""; + }); } - private async Task HandlePasswordAsync(NodeType nodeType) + public Task OnAskForWalletPassword(NodeType nodeType) { - while (true) + return Task.Run(() => { var dialog = new ConfirmationDialog( - $"Enter {WalletTypeName(nodeType)} wallet password ({nodeType})", - "Password", - "", - true); + $"Enter {WalletTypeName(nodeType)} wallet password ({nodeType})", + "Password", + "", + true); dialog.ShowDialog(); if (dialog.DialogResult != true) { - return false; - } - - string password = dialog.Text2.Text ?? string.Empty; - - string walletName; - if (nodeType == NodeType.MainChain) - { - walletName = this.collateralWalletName ?? ""; - } - else - { - walletName = this.miningWalletName ?? ""; - } - - if (await this.registrationService.CheckWalletPasswordAsync(NodeApiPort(nodeType), walletName, password) == false) - { - if (MessageBox.Show("The password you entered is incorrect. Do you want to enter it again?", "Incorrect password", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.No) - { - return false; - } - - continue; + return null; } - if (nodeType == NodeType.MainChain) - { - this.collateralWalletPassword = dialog.Text2.Text; - } - else - { - this.miningWalletPassword = dialog.Text2.Text; - } - - return true; - } + return dialog.Text2.Text ?? string.Empty; + }); } - private async Task HandleWalletCreationAsync(NodeType nodeType, bool createNewWallet) + public Task OnAskCreatePassword(NodeType nodeType) { - Network network = nodeType == NodeType.MainChain - ? this.registrationService.MainchainNetwork - : this.registrationService.SidechainNetwork; - - string? walletName = nodeType == NodeType.MainChain - ? this.collateralWalletName - : this.miningWalletName; - - string? walletMnemonic = nodeType == NodeType.MainChain - ? this.collateralWalletMnemonic - : this.miningWalletMnemonic; - - string? walletPassphrase = nodeType == NodeType.MainChain - ? this.collateralWalletPassphrase - : this.miningWalletPassphrase; + return Task.Run(() => + { + var dialog = new ConfirmationDialog( + $"Enter a new {WalletTypeName(nodeType)} wallet password ({nodeType})", + "Password", + "", + true); - string? walletPassword = nodeType == NodeType.MainChain - ? this.collateralWalletPassword - : this.miningWalletPassword; + dialog.ShowDialog(); - while (true) - { - try - { - if (walletName == null - || walletMnemonic == null - || walletPassphrase == null - || walletPassword == null - || !await this.registrationService.RestoreWalletAsync(network.DefaultAPIPort, nodeType, walletName, walletMnemonic, walletPassphrase, walletPassword, createNewWallet).ConfigureAwait(true)) - { - string action = createNewWallet ? "create" : "restore"; - LogError($"Cannot {action} {WalletTypeName(nodeType)} wallet, aborting..."); - return false; - } - break; - } - catch (WalletCollisionException) + if (dialog.DialogResult != true) { - LogError($"The {WalletTypeName(nodeType)} wallet with this mnemonic already exists."); - LogError("Please provide a new mnemonic."); - - if (!HandleNewMnemonic(nodeType, canChangeMnemonic: true)) - { - LogError("New mnemonic was not provided. Aborting..."); - return false; - } + return null; } - } - if (!await this.registrationService.ResyncWalletAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) - { - LogError($"Cannot resync {WalletTypeName(nodeType)} wallet, aborting..."); - return false; - } - - return true; + return dialog.Text2.Text ?? string.Empty; + }); } - private async Task HandleWalletSyncAsync(NodeType nodeType) + public Task OnAskReenterPassword(NodeType nodeType) { - string logTag = "HandleWalletSyncAsync" + nodeType; - - Network network = nodeType == NodeType.MainChain - ? this.registrationService.MainchainNetwork - : this.registrationService.SidechainNetwork; - - string? walletName = nodeType == NodeType.MainChain - ? this.collateralWalletName - : this.miningWalletName; - - if (walletName == null) - { - throw new ArgumentException("Wallet name can not be null."); - } - - int percentSynced = await this.registrationService.WalletSyncProgressAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true); - Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet is {percentSynced}% synced", updateTag: logTag); - - if (await this.registrationService.IsWalletSyncedAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) + return Task.Run(() => { - Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet synced successfuly.", updateTag: logTag); - return true; - } - else - { - return false; - } + return MessageBox.Show("The password you entered is incorrect. Do you want to enter it again?", + "Incorrect password", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.No; + }); } - private async Task HandleCreateWalletsAsync(NodeType nodeType, bool createNewMnemonic) + public Task OnWalletNameExists(NodeType nodeType) { - if (createNewMnemonic) - { - if (!HandleNewMnemonic(nodeType)) - { - return false; - } - } - else - { - if (!HandleUserMnemonic(nodeType)) - { - return false; - } - } - - if (!await HandleNewWalletNameAsync(nodeType)) - { - return false; - } - - if (!HandlePassphrase(nodeType)) - { - return false; - } - - if (!await HandlePasswordAsync(nodeType)) - { - return false; - } - - if (!await HandleWalletCreationAsync(nodeType, createNewMnemonic)) + return Task.Run(() => { - return false; - } - - try - { - while (!await HandleWalletSyncAsync(nodeType)) - { - await Task.Delay(TimeSpan.FromSeconds(1)); - } - } - catch - { - return false; - } - - return true; + MessageBox.Show("A wallet with this name already exists", "Error"); + }); } - private void Button_Click(object sender, RoutedEventArgs e) + public Task OnMnemonicIsInvalid(NodeType nodeType) { - Button button = (Button)sender; - - if (button == null) - { - return; - } - - switch (button.Tag.ToString()) + return Task.Run(() => { - case "RunMasterNode": - { - this.nextState = "RunMasterNode_KeyPresent"; - - break; - } - case "SetupMasterNode": - { - this.nextState = "SetupMasterNode_Eula"; - - break; - } - } - } - - private void ResetState() - { - this.nextState = null; - this.currentState = "Begin"; + MessageBox.Show("Please ensure that you enter a valid mnemonic", "Error", MessageBoxButton.OK); + }); } - private void LogWithBrush(string message, Brush? brush = null, string? updateTag = null) + public Task OnMnemonicExists(NodeType nodeType) { - this.statusBar.Dispatcher.Invoke(() => + return Task.Run(() => { - var inline = new Run(message + "\n"); - inline.Tag = updateTag; - - if (brush != null) - { - inline.Foreground = brush; - } - - InlineCollection inlines = this.statusBar.Inlines; - Inline lastInline = inlines.LastInline; - - if (updateTag != null && lastInline != null && string.Equals(lastInline.Tag, updateTag)) - { - inlines.Remove(lastInline); - } - - inlines.Add(inline); - this.logScrollArea.ScrollToBottom(); + LogError($"The {WalletTypeName(nodeType)} wallet with this mnemonic already exists."); + LogError("Please provide a new mnemonic."); }); } - private void Log(string message, string? updateTag = null) + public Task OnWalletExistsOrInvalid(NodeType nodeType) { - this.statusBar.Dispatcher.Invoke(() => + return Task.Run(() => { - LogWithBrush(message, brush: null, updateTag); + MessageBox.Show("A wallet with this name already exists", "Error"); }); } - private void Log(string message, Color color, string? updateTag = null) + public Task OnWalletSyncing(NodeType nodeType, int progress) { - this.statusBar.Dispatcher.Invoke(() => + return Task.Run(() => { - LogWithBrush(message, new SolidColorBrush(color), updateTag); + Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet is {progress}% synced", updateTag: $"{nodeType}WalletSyncing"); }); } - private void LogError(string message) + public Task OnWalletSynced(NodeType nodeType) { - Log(message, Color.FromRgb(255, 51, 51)); - } - - public void Info(string message, string? updateTag = null) - { - Log(message, updateTag: updateTag); + return Task.Run(() => + { + Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet synced successfuly.", updateTag: $"{nodeType}WalletSyncing"); + }); } - public void Error(string message) + public Task OnShowWalletName(NodeType nodeType, string walletName) { - LogError(message); + return Task.Run(() => + { + //TODO + }); } - public void Error(Exception exception) + public Task OnShowWalletAddress(NodeType nodeType, string address) { - if (this.PrintStacktraces) + return Task.Run(() => { - LogError($"{exception}"); - } + //TODO + }); } - public void Error(string message, Exception exception) + public Task OnRestoreWalletFailed(NodeType nodeType) { - Error(message); - Error(exception); + return Task.Run(() => + { + LogError($"Can not restore a {WalletTypeName(nodeType)} wallet, aborting..."); + }); } - private string WalletTypeName(NodeType nodeType) + public Task OnCreateWalletFailed(NodeType nodeType) { - return nodeType == NodeType.MainChain ? "collateral" : "mining"; + return Task.Run(() => + { + LogError($"Can not create a {WalletTypeName(nodeType)} wallet, aborting..."); + }); } - private int NodeApiPort(NodeType nodeType) + public Task OnResyncFailed(NodeType nodeType) { - Network network = nodeType == NodeType.MainChain ? this.registrationService.MainchainNetwork : this.registrationService.SidechainNetwork; - return network.DefaultAPIPort; + return Task.Run(() => + { + LogError($"Cannot resync {WalletTypeName(nodeType)} wallet, aborting..."); + }); } - - public static string? GetInformationalVersion() => - Assembly - .GetExecutingAssembly() - ?.GetCustomAttribute() - ?.InformationalVersion; } } diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 6e52a2e..581702b 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -43,44 +43,24 @@ public StateMachine(NetworkType networkType, IStateHandler stateHandler) }); } + public void OnRunNode() + { + this.nextState = "RunMasterNode_KeyPresent"; + } + + public void OnSetupNode() + { + this.nextState = "SetupMasterNode_Eula"; + } + private async void StateMachine_TickAsync(object? sender, EventArgs e) { this.timer.IsEnabled = false; - // if (this.currentState == "Begin") - // { - // if (!this.createdButtons) - // { - // this.createdButtons = true; - - // Style flatStyle = this.FlatStyle; - - // var button = new Button - // { - // Content = "Run Masternode", - // Tag = "RunMasterNode", - // Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - // Padding = new Thickness(4.0), - // Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - // }; - - // button.Click += new RoutedEventHandler(Button_Click); - // this.stackPanel.Children.Add(button); - - // button = new Button - // { - // Content = "Register Masternode", - // Tag = "SetupMasterNode", - // Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - // Padding = new Thickness(4.0), - // Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - // }; - - // button.Click += new RoutedEventHandler(Button_Click); - - // this.stackPanel.Children.Add(button); - // } - // } + if (this.currentState == "Begin") + { + await this.stateHandler.OnStart(); + } if (this.nextState == null) { @@ -575,7 +555,7 @@ private async Task HandleNewWalletNameAsync(NodeType nodeType, WalletCreat } else { - await this.stateHandler.OnWalletNameExists(); + await this.stateHandler.OnWalletNameExists(nodeType); } } catch @@ -631,7 +611,7 @@ private async Task HandleUserMnemonic(NodeType nodeType, WalletCreationSta } } - await this.stateHandler.OnMnemonicIsInvalid(); + await this.stateHandler.OnMnemonicIsInvalid(nodeType); } while (true); creationState.Mnemonic = mnemonic; @@ -711,7 +691,7 @@ private async Task HandleWalletCreationAsync(NodeType nodeType, WalletCrea } catch (WalletCollisionException) { - await this.stateHandler.OnMnemonicExists(); + await this.stateHandler.OnMnemonicExists(nodeType); if (!await HandleNewMnemonicAsync(nodeType, walletCreationState, canChangeMnemonic: true)) { @@ -864,21 +844,21 @@ private int NodeApiPort(NodeType nodeType) public void Info(string message, string? updateTag = null) { - throw new NotImplementedException(); + this.stateHandler.Info(message, updateTag); } public void Error(string message) { - throw new NotImplementedException(); + this.stateHandler.Error(message); } public void Error(Exception exception) { - throw new NotImplementedException(); + this.stateHandler.Error(exception); } public void Error(string message, Exception exception) { - throw new NotImplementedException(); + this.stateHandler.Error(message, exception); } } \ No newline at end of file From b20fa2215e9dc0f17369c0bdd6b05cd95c071709 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 12:14:56 +0100 Subject: [PATCH 03/21] More refactoring --- .../IRegistrationService.cs | 46 ++ src/MasternodeSetupTool/MainWindow.xaml.cs | 428 +++++++----------- .../RegistrationService.cs | 16 +- src/MasternodeSetupTool/StateMachine.cs | 239 +++++----- 4 files changed, 366 insertions(+), 363 deletions(-) create mode 100644 src/MasternodeSetupTool/IRegistrationService.cs diff --git a/src/MasternodeSetupTool/IRegistrationService.cs b/src/MasternodeSetupTool/IRegistrationService.cs new file mode 100644 index 0000000..68b0dd8 --- /dev/null +++ b/src/MasternodeSetupTool/IRegistrationService.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using NBitcoin; +using Stratis.Bitcoin.Features.Wallet.Models; + +namespace MasternodeSetupTool +{ + public interface IRegistrationService + { + Network MainchainNetwork { get; } + NetworkType NetworkType { get; } + string PubKey { get; } + string RootDataDir { get; } + Network SidechainNetwork { get; } + + Task BuildTransactionAsync(int apiPort, string walletName, string walletPassword, string accountName, List recipients, string? opReturnData = null, string? opReturnAmount = null, string feeType = "low", string? changeAddress = null, bool allowUnconfirmed = false); + Task CallJoinFederationRequestAsync(WalletCredentials collateralCredentials, WalletCredentials miningCredentials); + bool CheckFederationKeyExists(); + Task CheckIsFederationMemberAsync(); + Task CheckWalletBalanceAsync(int apiPort, string walletName, int amountToCheck); + Task CheckWalletBalanceWithConfirmationsAsync(int apiPort, string walletName, int amountToCheck, int requiredConfirmations); + Task CheckWalletPasswordAsync(int apiPort, string walletName, string walletPassword); + string CreateFederationKey(); + void DeleteFederationKey(); + Task EnsureBlockstoreIsSyncedAsync(NodeType nodeType, int apiPort); + Task EnsureMainChainNodeAddressIndexerIsSyncedAsync(); + Task EnsureNodeIsInitializedAsync(NodeType nodeType, int apiPort); + Task EnsureNodeIsSyncedAsync(NodeType nodeType, int apiPort); + Task FindWalletByNameAsync(int apiPort, string walletName); + Task GetFirstWalletAddressAsync(int apiPort, string walletName); + Task?> GetWalletAddressesAsync(string walletName, int apiPort); + Task GetWalletBalanceAsync(string walletName, int apiPort); + Task?> GetWalletsWithBalanceAsync(int apiPort); + Task IsWalletSyncedAsync(int apiPort, string walletName); + void LaunchBrowser(string url); + Task MonitorJoinFederationRequestAsync(); + Task PerformCrossChainTransferAsync(int apiPort, string walletName, string walletPassword, string amount, string cirrusAddress, string changeAddress); + Task RestoreWalletAsync(int apiPort, NodeType nodeType, string walletName, string mnemonic, string passphrase, string password, bool createNewWallet); + Task ResyncWalletAsync(int apiPort, string walletName); + string SendTransaction(int apiPort, string hex); + Task ShutdownNodeAsync(NodeType nodeType, int apiPort); + Task StartMasterNodeDashboardAsync(); + Task StartNodeAsync(NodeType nodeType, int apiPort, bool reindex = false); + Task WalletSyncProgressAsync(int apiPort, string walletName); + } +} \ No newline at end of file diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index 001d2e4..68d2550 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -7,6 +7,7 @@ using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; +using System.Windows.Threading; using NBitcoin; using Color = System.Windows.Media.Color; @@ -24,10 +25,12 @@ public partial class MainWindow : Window, IStateHandler private readonly StackPanel stackPanel; private readonly TextBlock statusBar; - private bool createdButtons; + private bool createdButtons = false; private StateMachine stateMachine; + private readonly DispatcherTimer timer; + private bool PrintStacktraces { get @@ -65,6 +68,19 @@ public MainWindow(string[] args) networkType = NetworkType.Regtest; this.stateMachine = new StateMachine(networkType, this); + + this.timer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + + this.timer.Tick += StateMachine_TickAsync; + this.timer.Start(); + } + + private async void StateMachine_TickAsync(object? sender, EventArgs e) + { + await this.stateMachine.TickAsync(); } private void Button_Click(object sender, RoutedEventArgs e) @@ -172,7 +188,7 @@ private string WalletTypeName(NodeType nodeType) ?.GetCustomAttribute() ?.InformationalVersion; - public Task OnStart() + public async Task OnStart() { if (!this.createdButtons) { @@ -180,400 +196,306 @@ public Task OnStart() Style flatStyle = this.FlatStyle; - var button = new Button + try { - Content = "Run Masternode", - Tag = "RunMasterNode", - Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - Padding = new Thickness(4.0), - Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - }; + var button = new Button + { + Content = "Run Masternode", + Tag = "RunMasterNode", + Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + Padding = new Thickness(4.0), + Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + }; + button.Click += new RoutedEventHandler(Button_Click); + this.stackPanel.Children.Add(button); + + button = new Button + { + Content = "Register Masternode", + Tag = "SetupMasterNode", + Margin = new Thickness(16.0, 4.0, 16.0, 4.0), + Padding = new Thickness(4.0), + Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + }; - button.Click += new RoutedEventHandler(Button_Click); - this.stackPanel.Children.Add(button); + button.Click += new RoutedEventHandler(Button_Click); - button = new Button + this.stackPanel.Children.Add(button); + } + catch (Exception ex) { - Content = "Register Masternode", - Tag = "SetupMasterNode", - Margin = new Thickness(16.0, 4.0, 16.0, 4.0), - Padding = new Thickness(4.0), - Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)), - }; - - button.Click += new RoutedEventHandler(Button_Click); - this.stackPanel.Children.Add(button); + } } - - return Task.CompletedTask; } - public Task OnProgramVersionAvailable(string? version) + public async Task OnProgramVersionAvailable(string? version) { if (version != null) { + var thread1 = System.Threading.Thread.CurrentThread; this.VersionText.Text = $"Version: {version}"; } - - return Task.CompletedTask; } - public Task OnFederationKeyMissing() + public async Task OnFederationKeyMissing() { MessageBox.Show("Federation key does not exist", "Key file missing", MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.Yes); - return Task.CompletedTask; } - public Task OnNodeFailedToStart(NodeType nodeType, string? reason = null) + public async Task OnNodeFailedToStart(NodeType nodeType, string? reason = null) { Error($"Cannot start the {nodeType} node, aborting..."); if (reason != null) { Error($"Reason: {reason}"); } - return Task.CompletedTask; } - public Task OnAskForEULA() + public async Task OnAskForEULA() { - return Task.Run( - () => MessageBox.Show("100K collateral is required to operate a Masternode; in addition, a balance of 500.1 CRS is required to fund the registration transaction. Are you happy to proceed?", + return MessageBox.Show("100K collateral is required to operate a Masternode; in addition, a balance of 500.1 CRS is required to fund the registration transaction. Are you happy to proceed?", "End-User License Agreement", MessageBoxButton.YesNo, MessageBoxImage.Warning, - MessageBoxResult.No) == MessageBoxResult.Yes); + MessageBoxResult.No) == MessageBoxResult.Yes; } - - public Task OnAskForNewFederationKey() + + public async Task OnAskForNewFederationKey() { - return Task.Run(() => - { - return MessageBox.Show( + return MessageBox.Show( "Federation key exists. Shall we create a new one?", "Key file already present", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.Yes; - }); } - public Task OnShowNewFederationKey(string pubKey, string savePath) + public async Task OnShowNewFederationKey(string pubKey, string savePath) { - return Task.Run(() => - { - MessageBox.Show($"Your Masternode public key is: {pubKey}"); - MessageBox.Show($"Your private key has been saved in the root Cirrus data folder:\r\n{savePath}. Please ensure that you keep a backup of this file."); - }); + MessageBox.Show($"Your Masternode public key is: {pubKey}"); + MessageBox.Show($"Your private key has been saved in the root Cirrus data folder:\r\n{savePath}. Please ensure that you keep a backup of this file."); } - public Task OnAskToRunIfAlreadyMember() + public async Task OnAskToRunIfAlreadyMember() { - return Task.Run(() => - { - return MessageBox.Show("Your node is already a member of a federation. Do you want to run the Masternode Dashboard instead?", - "Member of a federation", - MessageBoxButton.YesNo, - MessageBoxImage.Warning, - MessageBoxResult.No) == MessageBoxResult.Yes; - }); - + return MessageBox.Show("Your node is already a member of a federation. Do you want to run the Masternode Dashboard instead?", + "Member of a federation", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.Yes; } - public Task OnAlreadyMember() + public async Task OnAlreadyMember() { - return Task.Run(() => - { - Info("Your node is already a member of a federation. Consider using 'Run Masternode' instead."); - }); + Info("Your node is already a member of a federation. Consider using 'Run Masternode' instead."); } - public Task OnAskForWalletSource(NodeType nodeType) + public async Task OnAskForWalletSource(NodeType nodeType) { - return Task.Run(() => - { - var dialog = new CreateRestoreUseExisting(); - dialog.ShowDialog(); + var dialog = new CreateRestoreUseExisting(); + dialog.ShowDialog(); - if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.CreateWallet) - { - return WalletSource.NewWallet; - } + if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.CreateWallet) + { + return WalletSource.NewWallet; + } - if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.RestoreWallet) - { - return WalletSource.RestoreWallet; - } + if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.RestoreWallet) + { + return WalletSource.RestoreWallet; + } - if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.UseExistingWallet) - { - return WalletSource.UseExistingWallet; - } + if (dialog.Choice == CreateRestoreUseExisting.ButtonChoice.UseExistingWallet) + { + return WalletSource.UseExistingWallet; + } - return null; - }); + return null; } - public Task OnChooseWallet(List wallets, NodeType nodeType) + public async Task OnChooseWallet(List wallets, NodeType nodeType) { - return Task.Run(() => - { - var selectionDialog = new WalletSelectionDialog(wallets); - selectionDialog.ShowDialog(); + var selectionDialog = new WalletSelectionDialog(wallets); + selectionDialog.ShowDialog(); - return selectionDialog.SelectedWalletName; - }); + return selectionDialog.SelectedWalletName; } - public Task OnChooseAddress(List addresses, NodeType nodeType) + public async Task OnChooseAddress(List addresses, NodeType nodeType) { - return Task.Run(() => - { - var selectionDialog = new AddressSelectionDialog(addresses); - selectionDialog.ShowDialog(); + var selectionDialog = new AddressSelectionDialog(addresses); + selectionDialog.ShowDialog(); - return selectionDialog.SelectedAddress; - }); + return selectionDialog.SelectedAddress; } - public Task OnWaitingForCollateral() + public async Task OnWaitingForCollateral() { - return Task.Run(() => - { - Log($"Waiting for collateral wallet to have a balance of at least {RegistrationService.CollateralRequirement} STRAX", updateTag: "OnWaitingForCollateral"); - }); + Log($"Waiting for collateral wallet to have a balance of at least {RegistrationService.CollateralRequirement} STRAX", updateTag: "OnWaitingForCollateral"); } - public Task OnWaitingForRegistrationFee() + public async Task OnWaitingForRegistrationFee() { - return Task.Run(() => - { - Log("Waiting for registration fee to be sent to the mining wallet...", updateTag: "OnWaitingForRegistrationFee"); - }); + Log("Waiting for registration fee to be sent to the mining wallet...", updateTag: "OnWaitingForRegistrationFee"); } - public Task OnMissingRegistrationFee(string address) + public async Task OnMissingRegistrationFee(string address) { - return Task.Run(() => - { - Error($"Insufficient balance to pay registration fee. Please send 500.1 CRS to the mining wallet on address: {address}"); - }); + Error($"Insufficient balance to pay registration fee. Please send 500.1 CRS to the mining wallet on address: {address}"); } - public Task OnRegistrationCanceled() + public async Task OnRegistrationCanceled() { - return Task.Run(() => - { - LogError("Registration cancelled."); - }); + LogError("Registration cancelled."); } - public Task OnRegistrationComplete() + public async Task OnRegistrationComplete() { - return Task.Run(() => - { - Log("Registration complete"); - }); + Log("Registration complete"); } - public Task OnRegistrationFailed() + public async Task OnRegistrationFailed() { - return Task.Run(() => - { - Error("Failed to register your masternode, aborting..."); - }); + Error("Failed to register your masternode, aborting..."); } - public Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) + public async Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) { - return Task.Run(() => - { - var dialog = new ConfirmationDialog($"Enter mnemonic for the {WalletTypeName(nodeType)} wallet", "Mnemonic", mnemonic, false); - dialog.ShowDialog(); - return dialog.DialogResult == true; - }); + var dialog = new ConfirmationDialog($"Enter mnemonic for the {WalletTypeName(nodeType)} wallet", "Mnemonic", mnemonic, false); + dialog.ShowDialog(); + return dialog.DialogResult == true; } - public Task OnAskForUserMnemonic(NodeType nodeType) + public async Task OnAskForUserMnemonic(NodeType nodeType) { - return Task.Run(() => - { - var inputBox = new InputBox($"Please enter your mnemonic for the {WalletTypeName(nodeType)} ({nodeType}) wallet", "Mnemonic"); - return inputBox.ShowDialog(); - }); + var inputBox = new InputBox($"Please enter your mnemonic for the {WalletTypeName(nodeType)} ({nodeType}) wallet", "Mnemonic"); + return inputBox.ShowDialog(); } - public Task OnAskForWalletName(NodeType nodeType, bool newWallet) + public async Task OnAskForWalletName(NodeType nodeType, bool newWallet) { - return Task.Run(() => - { - var inputBox = new InputBox($"Please enter {nodeType} wallet name:"); - return inputBox.ShowDialog(); - }); + var inputBox = new InputBox($"Please enter {nodeType} wallet name:"); + return inputBox.ShowDialog(); } - public Task OnAskForPassphrase(NodeType nodeType) + public async Task OnAskForPassphrase(NodeType nodeType) { - return Task.Run(() => - { - var dialog = new ConfirmationDialog($"Enter passphrase for the {nodeType} wallet", - "Passphrase", - "", - true, - allowEmpty: true); + var dialog = new ConfirmationDialog($"Enter passphrase for the {nodeType} wallet", + "Passphrase", + "", + true, + allowEmpty: true); - dialog.ShowDialog(); + dialog.ShowDialog(); - if (dialog.DialogResult != true) - { - return null; - } + if (dialog.DialogResult != true) + { + return null; + } - return dialog.Text2.Text ?? ""; - }); + return dialog.Text2.Text ?? ""; } - public Task OnAskForWalletPassword(NodeType nodeType) + public async Task OnAskForWalletPassword(NodeType nodeType) { - return Task.Run(() => - { - var dialog = new ConfirmationDialog( - $"Enter {WalletTypeName(nodeType)} wallet password ({nodeType})", - "Password", - "", - true); + var dialog = new ConfirmationDialog( + titleText: $"Enter {WalletTypeName(nodeType)} wallet password ({nodeType})", + labelText: "Password", + firstTextContent: "", + firstTextEditable: true); - dialog.ShowDialog(); + dialog.ShowDialog(); - if (dialog.DialogResult != true) - { - return null; - } + if (dialog.DialogResult != true) + { + return null; + } - return dialog.Text2.Text ?? string.Empty; - }); + return dialog.Text2.Text ?? string.Empty; } - public Task OnAskCreatePassword(NodeType nodeType) + public async Task OnAskCreatePassword(NodeType nodeType) { - return Task.Run(() => - { - var dialog = new ConfirmationDialog( - $"Enter a new {WalletTypeName(nodeType)} wallet password ({nodeType})", - "Password", - "", - true); + var dialog = new ConfirmationDialog( + titleText: $"Enter a new {WalletTypeName(nodeType)} wallet password ({nodeType})", + labelText: "Password", + firstTextContent: "", + firstTextEditable: true); - dialog.ShowDialog(); + dialog.ShowDialog(); - if (dialog.DialogResult != true) - { - return null; - } + if (dialog.DialogResult != true) + { + return null; + } - return dialog.Text2.Text ?? string.Empty; - }); + return dialog.Text2.Text ?? string.Empty; } - public Task OnAskReenterPassword(NodeType nodeType) + public async Task OnAskReenterPassword(NodeType nodeType) { - return Task.Run(() => - { - return MessageBox.Show("The password you entered is incorrect. Do you want to enter it again?", - "Incorrect password", - MessageBoxButton.YesNo, - MessageBoxImage.Warning, - MessageBoxResult.No) == MessageBoxResult.No; - }); + return MessageBox.Show("The password you entered is incorrect. Do you want to enter it again?", + "Incorrect password", + MessageBoxButton.YesNo, + MessageBoxImage.Warning, + MessageBoxResult.No) == MessageBoxResult.No; } - public Task OnWalletNameExists(NodeType nodeType) + public async Task OnWalletNameExists(NodeType nodeType) { - return Task.Run(() => - { - MessageBox.Show("A wallet with this name already exists", "Error"); - }); + MessageBox.Show("A wallet with this name already exists", "Error"); } - public Task OnMnemonicIsInvalid(NodeType nodeType) + public async Task OnMnemonicIsInvalid(NodeType nodeType) { - return Task.Run(() => - { - MessageBox.Show("Please ensure that you enter a valid mnemonic", "Error", MessageBoxButton.OK); - }); + MessageBox.Show("Please ensure that you enter a valid mnemonic", "Error", MessageBoxButton.OK); } - public Task OnMnemonicExists(NodeType nodeType) + public async Task OnMnemonicExists(NodeType nodeType) { - return Task.Run(() => - { - LogError($"The {WalletTypeName(nodeType)} wallet with this mnemonic already exists."); - LogError("Please provide a new mnemonic."); - }); + LogError($"The {WalletTypeName(nodeType)} wallet with this mnemonic already exists."); + LogError("Please provide a new mnemonic."); } - public Task OnWalletExistsOrInvalid(NodeType nodeType) + public async Task OnWalletExistsOrInvalid(NodeType nodeType) { - return Task.Run(() => - { - MessageBox.Show("A wallet with this name already exists", "Error"); - }); + MessageBox.Show("A wallet with this name already exists", "Error"); } - public Task OnWalletSyncing(NodeType nodeType, int progress) + public async Task OnWalletSyncing(NodeType nodeType, int progress) { - return Task.Run(() => - { - Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet is {progress}% synced", updateTag: $"{nodeType}WalletSyncing"); - }); + Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet is {progress}% synced", updateTag: $"{nodeType}WalletSyncing"); } - public Task OnWalletSynced(NodeType nodeType) + public async Task OnWalletSynced(NodeType nodeType) { - return Task.Run(() => - { - Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet synced successfuly.", updateTag: $"{nodeType}WalletSyncing"); - }); + Log($"{nodeType} ({WalletTypeName(nodeType)}) wallet synced successfuly.", updateTag: $"{nodeType}WalletSyncing"); } - public Task OnShowWalletName(NodeType nodeType, string walletName) + public async Task OnShowWalletName(NodeType nodeType, string walletName) { - return Task.Run(() => - { - //TODO - }); + //TODO } - public Task OnShowWalletAddress(NodeType nodeType, string address) + public async Task OnShowWalletAddress(NodeType nodeType, string address) { - return Task.Run(() => - { - //TODO - }); + //TODO } - public Task OnRestoreWalletFailed(NodeType nodeType) + public async Task OnRestoreWalletFailed(NodeType nodeType) { - return Task.Run(() => - { - LogError($"Can not restore a {WalletTypeName(nodeType)} wallet, aborting..."); - }); + LogError($"Can not restore a {WalletTypeName(nodeType)} wallet, aborting..."); } - public Task OnCreateWalletFailed(NodeType nodeType) + public async Task OnCreateWalletFailed(NodeType nodeType) { - return Task.Run(() => - { - LogError($"Can not create a {WalletTypeName(nodeType)} wallet, aborting..."); - }); + LogError($"Can not create a {WalletTypeName(nodeType)} wallet, aborting..."); } - public Task OnResyncFailed(NodeType nodeType) + public async Task OnResyncFailed(NodeType nodeType) { - return Task.Run(() => - { - LogError($"Cannot resync {WalletTypeName(nodeType)} wallet, aborting..."); - }); + LogError($"Cannot resync {WalletTypeName(nodeType)} wallet, aborting..."); } } } diff --git a/src/MasternodeSetupTool/RegistrationService.cs b/src/MasternodeSetupTool/RegistrationService.cs index e9477d1..59a77aa 100644 --- a/src/MasternodeSetupTool/RegistrationService.cs +++ b/src/MasternodeSetupTool/RegistrationService.cs @@ -27,7 +27,7 @@ namespace MasternodeSetupTool { - public sealed class RegistrationService + public sealed class RegistrationService : IRegistrationService { public const int CollateralRequirement = 100_000; public const int FeeRequirement = 500; @@ -138,7 +138,7 @@ public async Task StartNodeAsync(NodeType nodeType, int apiPort, bool rein } if (this.networkType == NetworkType.Testnet) - { + { argumentBuilder.Append("-testnet "); } @@ -170,7 +170,7 @@ public async Task StartNodeAsync(NodeType nodeType, int apiPort, bool rein if (process == null || process.HasExited) { - Error($"{nodeType} node process failed to start, exiting..."); + Error($"{nodeType} node process failed to start, exiting..."); return false; } @@ -205,7 +205,7 @@ public async Task ShutdownNodeAsync(NodeType nodeType, int apiPort) .WithHeader("Content-Type", "application/json-patch+json") .PostAsync(); } - catch (Exception e) + catch (Exception e) { Error($"Can not shutdown {nodeType}."); Error(e); @@ -585,7 +585,7 @@ public async Task RestoreWalletAsync(int apiPort, NodeType nodeType, strin return walletBalanceModel.AccountsBalances.FirstOrDefault() ?.Addresses?.Select(item => new AddressItem(item.Address, item.AmountConfirmed)) ?.ToList(); - } + } catch { return null; @@ -605,7 +605,7 @@ public async Task GetWalletBalanceAsync(string walletName, int apiPo return new WalletItem(name: walletName, balance: walletBalanceModel.AccountsBalances[0].SpendableAmount); } - public class WalletCollisionException: Exception { } + public class WalletCollisionException : Exception { } public async Task ResyncWalletAsync(int apiPort, string walletName) { @@ -794,7 +794,7 @@ public async Task CallJoinFederationRequestAsync(WalletCredentials collate .ReceiveJson() .ConfigureAwait(false); - if (response != null && !string.IsNullOrEmpty(response.MinerPublicKey)) + if (response != null && !string.IsNullOrEmpty(response.MinerPublicKey)) { Status($"SUCCESS: The masternode request has now been submitted to the network"); return true; @@ -935,7 +935,7 @@ public async Task StartMasterNodeDashboardAsync() } Status($"Starting the masternode dashboard on {this.networkType}. Start up arguments: {argumentBuilder}"); - + string osSpecificCommand = "CMD.EXE"; string osSpecificArguments = $"/K \"{argumentBuilder}\""; diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 581702b..f84e621 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -8,15 +8,51 @@ using NBitcoin; using static MasternodeSetupTool.RegistrationService; -public partial class StateMachine: ILogger +namespace MasternodeSetupTool; + +public class StateMachine: ILogger { + public enum State + { + Begin, + RunMasterNode_KeyPresent, + Run_StartMainChain, + Run_MainChainSynced, + Run_StartSideChain, + Run_SideChainSynced, + Run_LaunchBrowser, + SetupMasterNode_Eula, + Setup_KeyPresent, + Setup_CreateRestoreUseExisting_StartMainChain, + Setup_CreateKey, + Setup_CreateRestoreUseExisting_MainChainSynced, + Setup_CreateRestoreUseExisting_StartSideChain, + Setup_CreateRestoreUseExisting_SideChainSynced, + Setup_CreateRestoreUseExisting_Create_AskForCollateral, + Setup_CreateRestoreUseExisting_CheckIsFederationMember, + Setup_CreateRestoreUseExisting_Select, + Setup_CreateRestoreUseExisting_Create, + Setup_CreateRestoreUseExisting_Create_Mining, + Setup_CreateRestoreUseExisting_CheckForCollateral, + Setup_CreateRestoreUseExisting_Restore, + Setup_CreateRestoreUseExisting_Restore_Mining, + Setup_CreateRestoreUseExisting_CheckForRegistrationFee, + Setup_CreateRestoreUseExisting_PerformRegistration, + Setup_CreateRestoreUseExisting_WaitForBalance, + Setup_CreateRestoreUseExisting_WaitForRegistration, + Setup_CreateRestoreUseExisting_UseExisting, + Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword, + Setup_CreateRestoreUseExisting_UseExisting_Mining, + Setup_CreateRestoreUseExisting_UseExisting_MiningPassword, + Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced, + Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced + } private readonly RegistrationService registrationService; - private readonly DispatcherTimer timer; private readonly IStateHandler stateHandler; - private string currentState = "Begin"; - private string? nextState = null; + private State currentState = State.Begin; + private State? nextState = null; public WalletCredentials? collateralWalletCredentials; public WalletCredentials? miningWalletCredentials; @@ -24,76 +60,75 @@ public partial class StateMachine: ILogger public WalletCreationState? collateralWalletCreationState; public WalletCreationState? miningWalletCreationState; + + private bool IsEnabled = true; + public StateMachine(NetworkType networkType, IStateHandler stateHandler) { this.stateHandler = stateHandler; this.registrationService = new RegistrationService(networkType, this); - this.timer = new DispatcherTimer - { - Interval = TimeSpan.FromSeconds(1) - }; - - this.timer.Tick += StateMachine_TickAsync; - this.timer.Start(); - Task.Run(async () => - { - await this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()); - }); + this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()).GetAwaiter().GetResult(); } public void OnRunNode() { - this.nextState = "RunMasterNode_KeyPresent"; + this.nextState = State.RunMasterNode_KeyPresent; } public void OnSetupNode() { - this.nextState = "SetupMasterNode_Eula"; + this.nextState = State.SetupMasterNode_Eula; } - private async void StateMachine_TickAsync(object? sender, EventArgs e) + public async Task TickAsync() { - this.timer.IsEnabled = false; + var thread = System.Threading.Thread.CurrentThread; - if (this.currentState == "Begin") + if (!this.IsEnabled) return; + + this.IsEnabled = false; + + if (this.currentState == State.Begin) { await this.stateHandler.OnStart(); } if (this.nextState == null) { - this.timer.IsEnabled = true; + this.IsEnabled = true; return; } - this.currentState = this.nextState; + this.currentState = (State)this.nextState; this.nextState = null; if (await RunBranchAsync()) { - this.timer.IsEnabled = true; + this.IsEnabled = true; return; } if (await SetupBranchAsync()) { - this.timer.IsEnabled = true; + this.IsEnabled = true; return; } - this.timer.IsEnabled = true; + this.IsEnabled = true; } private async Task RunBranchAsync() { // The 'Run' branch - if (this.currentState == "RunMasterNode_KeyPresent") + var thread = System.Threading.Thread.CurrentThread; + + if (this.currentState == State.RunMasterNode_KeyPresent) { if (!this.registrationService.CheckFederationKeyExists()) { @@ -104,10 +139,10 @@ private async Task RunBranchAsync() return true; } - this.nextState = "Run_StartMainChain"; + this.nextState = State.Run_StartMainChain; } - if (this.currentState == "Run_StartMainChain") + if (this.currentState == State.Run_StartMainChain) { if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -115,10 +150,10 @@ private async Task RunBranchAsync() return false; } - this.nextState = "Run_MainChainSynced"; + this.nextState = State.Run_MainChainSynced; } - if (this.currentState == "Run_MainChainSynced") + if (this.currentState == State.Run_MainChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -126,10 +161,10 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = "Run_StartSideChain"; + this.nextState = State.Run_StartSideChain; } - if (this.currentState == "Run_StartSideChain") + if (this.currentState == State.Run_StartSideChain) { if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -138,10 +173,10 @@ private async Task RunBranchAsync() return false; } - this.nextState = "Run_SideChainSynced"; + this.nextState = State.Run_SideChainSynced; } - if (this.currentState == "Run_SideChainSynced") + if (this.currentState == State.Run_SideChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -149,10 +184,10 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = "Run_LaunchBrowser"; + this.nextState = State.Run_LaunchBrowser; } - if (this.currentState == "Run_LaunchBrowser") + if (this.currentState == State.Run_LaunchBrowser) { await this.registrationService.StartMasterNodeDashboardAsync().ConfigureAwait(true); this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); @@ -167,7 +202,7 @@ private async Task RunBranchAsync() private async Task SetupBranchAsync() { - if (this.currentState == "SetupMasterNode_Eula") + if (this.currentState == State.SetupMasterNode_Eula) { if (!await this.stateHandler.OnAskForEULA()) { @@ -175,35 +210,35 @@ private async Task SetupBranchAsync() return true; } - this.nextState = "Setup_KeyPresent"; + this.nextState = State.Setup_KeyPresent; } - if (this.currentState == "Setup_KeyPresent") + if (this.currentState == State.Setup_KeyPresent) { if (this.registrationService.CheckFederationKeyExists()) { if (!await this.stateHandler.OnAskForNewFederationKey()) { - this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; + this.nextState = State.Setup_CreateRestoreUseExisting_StartMainChain; return true; } this.registrationService.DeleteFederationKey(); } - this.nextState = "Setup_CreateKey"; + this.nextState = State.Setup_CreateKey; } - if (this.currentState == "Setup_CreateKey") + if (this.currentState == State.Setup_CreateKey) { string savePath = this.registrationService.CreateFederationKey(); await this.stateHandler.OnShowNewFederationKey(this.registrationService.PubKey, savePath); - this.nextState = "Setup_CreateRestoreUseExisting_StartMainChain"; + this.nextState = State.Setup_CreateRestoreUseExisting_StartMainChain; } - if (this.currentState == "Setup_CreateRestoreUseExisting_StartMainChain") + if (this.currentState == State.Setup_CreateRestoreUseExisting_StartMainChain) { // All 3 sub-branches of this state require the mainchain and sidechain nodes to be initialized, so do that first. if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) @@ -214,10 +249,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = "Setup_CreateRestoreUseExisting_MainChainSynced"; + this.nextState = State.Setup_CreateRestoreUseExisting_MainChainSynced; } - if (this.currentState == "Setup_CreateRestoreUseExisting_MainChainSynced") + if (this.currentState == State.Setup_CreateRestoreUseExisting_MainChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -225,10 +260,10 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = "Setup_CreateRestoreUseExisting_StartSideChain"; + this.nextState = State.Setup_CreateRestoreUseExisting_StartSideChain; } - if (this.currentState == "Setup_CreateRestoreUseExisting_StartSideChain") + if (this.currentState == State.Setup_CreateRestoreUseExisting_StartSideChain) { if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -238,10 +273,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = "Setup_CreateRestoreUseExisting_SideChainSynced"; + this.nextState = State.Setup_CreateRestoreUseExisting_SideChainSynced; } - if (this.currentState == "Setup_CreateRestoreUseExisting_SideChainSynced") + if (this.currentState == State.Setup_CreateRestoreUseExisting_SideChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -249,16 +284,16 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = "Setup_CreateRestoreUseExisting_CheckIsFederationMember"; + this.nextState = State.Setup_CreateRestoreUseExisting_CheckIsFederationMember; } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckIsFederationMember") + if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckIsFederationMember) { if (await this.registrationService.CheckIsFederationMemberAsync().ConfigureAwait(true)) { if (await this.stateHandler.OnAskToRunIfAlreadyMember()) { - this.nextState = "Run_LaunchBrowser"; + this.nextState = State.Run_LaunchBrowser; return true; } else @@ -269,22 +304,22 @@ private async Task SetupBranchAsync() } } - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; } - if (this.currentState == "Setup_CreateRestoreUseExisting_Select") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Select) { //TODO: Probably we need to show picker for collateral and mining wallets independently switch (await this.stateHandler.OnAskForWalletSource(NodeType.MainChain)) { case WalletSource.NewWallet: - this.nextState = "Setup_CreateRestoreUseExisting_Create"; + this.nextState = State.Setup_CreateRestoreUseExisting_Create; break; case WalletSource.RestoreWallet: - this.nextState = "Setup_CreateRestoreUseExisting_Restore"; + this.nextState = State.Setup_CreateRestoreUseExisting_Restore; break; case WalletSource.UseExistingWallet: - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting; break; default: await this.stateHandler.OnRegistrationCanceled(); @@ -293,31 +328,31 @@ private async Task SetupBranchAsync() } } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Create) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: true)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_Create_Mining"; + this.nextState = State.Setup_CreateRestoreUseExisting_Create_Mining; } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create_Mining") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Create_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: true)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; } - if (this.currentState == "Setup_CreateRestoreUseExisting_Create_AskForCollateral") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Create_AskForCollateral) { this.collateralWalletCredentials.ChoosenAddress = await HandleAddressSelectionAsync(NodeType.MainChain, this.collateralWalletCredentials.Name); @@ -327,52 +362,52 @@ private async Task SetupBranchAsync() } // The 3 sub-branches recombine after this and can share common states. - this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; + this.nextState = State.Setup_CreateRestoreUseExisting_CheckForCollateral; } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForCollateral") + if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckForCollateral) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletCredentials.Name, RegistrationService.CollateralRequirement).ConfigureAwait(true)) { - this.nextState = "Setup_CreateRestoreUseExisting_CheckForRegistrationFee"; + this.nextState = State.Setup_CreateRestoreUseExisting_CheckForRegistrationFee; } else { await this.stateHandler.OnWaitingForCollateral(); - this.nextState = "Setup_CreateRestoreUseExisting_CheckForCollateral"; + this.nextState = State.Setup_CreateRestoreUseExisting_CheckForCollateral; } } - if (this.currentState == "Setup_CreateRestoreUseExisting_CheckForRegistrationFee") + if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckForRegistrationFee) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; + this.nextState = State.Setup_CreateRestoreUseExisting_PerformRegistration; } else { string? miningAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name).ConfigureAwait(true); this.miningWalletCredentials.ChoosenAddress = miningAddress; await this.stateHandler.OnMissingRegistrationFee(miningAddress); - this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; + this.nextState = State.Setup_CreateRestoreUseExisting_WaitForBalance; } } - if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForBalance") + if (this.currentState == State.Setup_CreateRestoreUseExisting_WaitForBalance) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = "Setup_CreateRestoreUseExisting_PerformRegistration"; + this.nextState = State.Setup_CreateRestoreUseExisting_PerformRegistration; } else { await this.stateHandler.OnWaitingForRegistrationFee(); - this.nextState = "Setup_CreateRestoreUseExisting_WaitForBalance"; + this.nextState = State.Setup_CreateRestoreUseExisting_WaitForBalance; } } - if (this.currentState == "Setup_CreateRestoreUseExisting_PerformRegistration") + if (this.currentState == State.Setup_CreateRestoreUseExisting_PerformRegistration) { bool registeredSuccessfully = await this.registrationService.CallJoinFederationRequestAsync(this.collateralWalletCredentials, this.miningWalletCredentials).ConfigureAwait(true); if (!registeredSuccessfully) @@ -382,91 +417,91 @@ private async Task SetupBranchAsync() return true; } - this.nextState = "Setup_CreateRestoreUseExisting_WaitForRegistration"; + this.nextState = State.Setup_CreateRestoreUseExisting_WaitForRegistration; } - if (this.currentState == "Setup_CreateRestoreUseExisting_WaitForRegistration") + if (this.currentState == State.Setup_CreateRestoreUseExisting_WaitForRegistration) { if (await this.registrationService.MonitorJoinFederationRequestAsync().ConfigureAwait(true)) { await this.stateHandler.OnRegistrationComplete(); - this.nextState = "Run_LaunchBrowser"; + this.nextState = State.Run_LaunchBrowser; } } - if (this.currentState == "Setup_CreateRestoreUseExisting_Restore") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Restore) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: false)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_Restore_Mining"; + this.nextState = State.Setup_CreateRestoreUseExisting_Restore_Mining; } - if (this.currentState == "Setup_CreateRestoreUseExisting_Restore_Mining") + if (this.currentState == State.Setup_CreateRestoreUseExisting_Restore_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: false)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting) { this.collateralWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.MainChain, this.collateralWalletCredentials)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword; } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword) { if (!await HandleExistingPasswordAsync(NodeType.MainChain, collateralWalletCredentials)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_Mining"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_Mining; } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_Mining") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_Mining) { this.miningWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.SideChain,this.miningWalletCredentials)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_MiningPassword; } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_MiningPassword") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_MiningPassword) { if (!await HandleExistingPasswordAsync(NodeType.SideChain, miningWalletCredentials)) { - this.nextState = "Setup_CreateRestoreUseExisting_Select"; + this.nextState = State.Setup_CreateRestoreUseExisting_Select; return true; } - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced; } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced) { if (await HandleWalletSyncAsync(NodeType.MainChain)) { - this.nextState = "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced"; + this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced; } else { @@ -474,12 +509,12 @@ private async Task SetupBranchAsync() } } - if (this.currentState == "Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced") + if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced) { if (await HandleWalletSyncAsync(NodeType.SideChain)) { // Now we can jump back into the same sequence as the other 2 sub-branches. - this.nextState = "Setup_CreateRestoreUseExisting_Create_AskForCollateral"; + this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; } else { @@ -810,7 +845,7 @@ private async Task HandleCreateWalletsAsync(NodeType nodeType, bool create private void ResetState() { this.nextState = null; - this.currentState = "Begin"; + this.currentState = State.Begin; } public static string? GetInformationalVersion() => From 45d63da6bd5d449ccc774f255bbdce08f8c50dd1 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 12:18:50 +0100 Subject: [PATCH 04/21] Fix state names --- src/MasternodeSetupTool/StateMachine.cs | 164 ++++++++++++------------ 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index f84e621..59c2ad5 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -23,29 +23,29 @@ public enum State Run_LaunchBrowser, SetupMasterNode_Eula, Setup_KeyPresent, - Setup_CreateRestoreUseExisting_StartMainChain, + Setup_StartMainChain, Setup_CreateKey, - Setup_CreateRestoreUseExisting_MainChainSynced, - Setup_CreateRestoreUseExisting_StartSideChain, - Setup_CreateRestoreUseExisting_SideChainSynced, - Setup_CreateRestoreUseExisting_Create_AskForCollateral, - Setup_CreateRestoreUseExisting_CheckIsFederationMember, - Setup_CreateRestoreUseExisting_Select, - Setup_CreateRestoreUseExisting_Create, - Setup_CreateRestoreUseExisting_Create_Mining, - Setup_CreateRestoreUseExisting_CheckForCollateral, - Setup_CreateRestoreUseExisting_Restore, - Setup_CreateRestoreUseExisting_Restore_Mining, - Setup_CreateRestoreUseExisting_CheckForRegistrationFee, - Setup_CreateRestoreUseExisting_PerformRegistration, - Setup_CreateRestoreUseExisting_WaitForBalance, - Setup_CreateRestoreUseExisting_WaitForRegistration, - Setup_CreateRestoreUseExisting_UseExisting, - Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword, - Setup_CreateRestoreUseExisting_UseExisting_Mining, - Setup_CreateRestoreUseExisting_UseExisting_MiningPassword, - Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced, - Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced + Setup_MainChainSynced, + Setup_StartSideChain, + Setup_SideChainSynced, + Setup_Create_AskForCollateral, + Setup_CheckIsFederationMember, + Setup_Select, + Setup_Create, + Setup_Create_Mining, + Setup_CheckForCollateral, + Setup_Restore, + Setup_Restore_Mining, + Setup_CheckForRegistrationFee, + Setup_PerformRegistration, + Setup_WaitForBalance, + Setup_WaitForRegistration, + Setup_UseExisting, + Setup_UseExisting_CollateralPassword, + Setup_UseExisting_Mining, + Setup_UseExisting_MiningPassword, + Setup_UseExisting_CheckMainWalletSynced, + Setup_UseExisting_CheckSideWalletSynced } private readonly RegistrationService registrationService; @@ -219,7 +219,7 @@ private async Task SetupBranchAsync() { if (!await this.stateHandler.OnAskForNewFederationKey()) { - this.nextState = State.Setup_CreateRestoreUseExisting_StartMainChain; + this.nextState = State.Setup_StartMainChain; return true; } @@ -235,10 +235,10 @@ private async Task SetupBranchAsync() await this.stateHandler.OnShowNewFederationKey(this.registrationService.PubKey, savePath); - this.nextState = State.Setup_CreateRestoreUseExisting_StartMainChain; + this.nextState = State.Setup_StartMainChain; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_StartMainChain) + if (this.currentState == State.Setup_StartMainChain) { // All 3 sub-branches of this state require the mainchain and sidechain nodes to be initialized, so do that first. if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) @@ -249,10 +249,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_MainChainSynced; + this.nextState = State.Setup_MainChainSynced; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_MainChainSynced) + if (this.currentState == State.Setup_MainChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -260,10 +260,10 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Setup_CreateRestoreUseExisting_StartSideChain; + this.nextState = State.Setup_StartSideChain; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_StartSideChain) + if (this.currentState == State.Setup_StartSideChain) { if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -273,10 +273,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_SideChainSynced; + this.nextState = State.Setup_SideChainSynced; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_SideChainSynced) + if (this.currentState == State.Setup_SideChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -284,10 +284,10 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Setup_CreateRestoreUseExisting_CheckIsFederationMember; + this.nextState = State.Setup_CheckIsFederationMember; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckIsFederationMember) + if (this.currentState == State.Setup_CheckIsFederationMember) { if (await this.registrationService.CheckIsFederationMemberAsync().ConfigureAwait(true)) { @@ -304,22 +304,22 @@ private async Task SetupBranchAsync() } } - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Select) + if (this.currentState == State.Setup_Select) { //TODO: Probably we need to show picker for collateral and mining wallets independently switch (await this.stateHandler.OnAskForWalletSource(NodeType.MainChain)) { case WalletSource.NewWallet: - this.nextState = State.Setup_CreateRestoreUseExisting_Create; + this.nextState = State.Setup_Create; break; case WalletSource.RestoreWallet: - this.nextState = State.Setup_CreateRestoreUseExisting_Restore; + this.nextState = State.Setup_Restore; break; case WalletSource.UseExistingWallet: - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting; + this.nextState = State.Setup_UseExisting; break; default: await this.stateHandler.OnRegistrationCanceled(); @@ -328,31 +328,31 @@ private async Task SetupBranchAsync() } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Create) + if (this.currentState == State.Setup_Create) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: true)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_Create_Mining; + this.nextState = State.Setup_Create_Mining; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Create_Mining) + if (this.currentState == State.Setup_Create_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: true)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; + this.nextState = State.Setup_Create_AskForCollateral; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Create_AskForCollateral) + if (this.currentState == State.Setup_Create_AskForCollateral) { this.collateralWalletCredentials.ChoosenAddress = await HandleAddressSelectionAsync(NodeType.MainChain, this.collateralWalletCredentials.Name); @@ -362,52 +362,52 @@ private async Task SetupBranchAsync() } // The 3 sub-branches recombine after this and can share common states. - this.nextState = State.Setup_CreateRestoreUseExisting_CheckForCollateral; + this.nextState = State.Setup_CheckForCollateral; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckForCollateral) + if (this.currentState == State.Setup_CheckForCollateral) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletCredentials.Name, RegistrationService.CollateralRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_CreateRestoreUseExisting_CheckForRegistrationFee; + this.nextState = State.Setup_CheckForRegistrationFee; } else { await this.stateHandler.OnWaitingForCollateral(); - this.nextState = State.Setup_CreateRestoreUseExisting_CheckForCollateral; + this.nextState = State.Setup_CheckForCollateral; } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_CheckForRegistrationFee) + if (this.currentState == State.Setup_CheckForRegistrationFee) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_CreateRestoreUseExisting_PerformRegistration; + this.nextState = State.Setup_PerformRegistration; } else { string? miningAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name).ConfigureAwait(true); this.miningWalletCredentials.ChoosenAddress = miningAddress; await this.stateHandler.OnMissingRegistrationFee(miningAddress); - this.nextState = State.Setup_CreateRestoreUseExisting_WaitForBalance; + this.nextState = State.Setup_WaitForBalance; } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_WaitForBalance) + if (this.currentState == State.Setup_WaitForBalance) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_CreateRestoreUseExisting_PerformRegistration; + this.nextState = State.Setup_PerformRegistration; } else { await this.stateHandler.OnWaitingForRegistrationFee(); - this.nextState = State.Setup_CreateRestoreUseExisting_WaitForBalance; + this.nextState = State.Setup_WaitForBalance; } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_PerformRegistration) + if (this.currentState == State.Setup_PerformRegistration) { bool registeredSuccessfully = await this.registrationService.CallJoinFederationRequestAsync(this.collateralWalletCredentials, this.miningWalletCredentials).ConfigureAwait(true); if (!registeredSuccessfully) @@ -417,10 +417,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_WaitForRegistration; + this.nextState = State.Setup_WaitForRegistration; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_WaitForRegistration) + if (this.currentState == State.Setup_WaitForRegistration) { if (await this.registrationService.MonitorJoinFederationRequestAsync().ConfigureAwait(true)) { @@ -429,79 +429,79 @@ private async Task SetupBranchAsync() } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Restore) + if (this.currentState == State.Setup_Restore) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: false)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_Restore_Mining; + this.nextState = State.Setup_Restore_Mining; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_Restore_Mining) + if (this.currentState == State.Setup_Restore_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: false)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; + this.nextState = State.Setup_Create_AskForCollateral; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting) + if (this.currentState == State.Setup_UseExisting) { this.collateralWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.MainChain, this.collateralWalletCredentials)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword; + this.nextState = State.Setup_UseExisting_CollateralPassword; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CollateralPassword) + if (this.currentState == State.Setup_UseExisting_CollateralPassword) { if (!await HandleExistingPasswordAsync(NodeType.MainChain, collateralWalletCredentials)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_Mining; + this.nextState = State.Setup_UseExisting_Mining; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_Mining) + if (this.currentState == State.Setup_UseExisting_Mining) { this.miningWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.SideChain,this.miningWalletCredentials)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_MiningPassword; + this.nextState = State.Setup_UseExisting_MiningPassword; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_MiningPassword) + if (this.currentState == State.Setup_UseExisting_MiningPassword) { if (!await HandleExistingPasswordAsync(NodeType.SideChain, miningWalletCredentials)) { - this.nextState = State.Setup_CreateRestoreUseExisting_Select; + this.nextState = State.Setup_Select; return true; } - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced; + this.nextState = State.Setup_UseExisting_CheckMainWalletSynced; } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CheckMainWalletSynced) + if (this.currentState == State.Setup_UseExisting_CheckMainWalletSynced) { if (await HandleWalletSyncAsync(NodeType.MainChain)) { - this.nextState = State.Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced; + this.nextState = State.Setup_UseExisting_CheckSideWalletSynced; } else { @@ -509,12 +509,12 @@ private async Task SetupBranchAsync() } } - if (this.currentState == State.Setup_CreateRestoreUseExisting_UseExisting_CheckSideWalletSynced) + if (this.currentState == State.Setup_UseExisting_CheckSideWalletSynced) { if (await HandleWalletSyncAsync(NodeType.SideChain)) { // Now we can jump back into the same sequence as the other 2 sub-branches. - this.nextState = State.Setup_CreateRestoreUseExisting_Create_AskForCollateral; + this.nextState = State.Setup_Create_AskForCollateral; } else { @@ -776,10 +776,10 @@ private async Task HandleWalletSyncAsync(NodeType nodeType) } int percentSynced = await this.registrationService.WalletSyncProgressAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true); - this.stateHandler.OnWalletSyncing(nodeType, percentSynced); + await this.stateHandler.OnWalletSyncing(nodeType, percentSynced); if (await this.registrationService.IsWalletSyncedAsync(network.DefaultAPIPort, walletName).ConfigureAwait(true)) { - this.stateHandler.OnWalletSynced(nodeType); + await this.stateHandler.OnWalletSynced(nodeType); return true; } else From 872fc56b102a6f703250d7ba97cb454c044ced02 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 14:14:01 +0100 Subject: [PATCH 05/21] Extract StateHolder --- src/MasternodeSetupTool/DefaultStateHolder.cs | 50 +++++ src/MasternodeSetupTool/IStateHolder.cs | 11 ++ src/MasternodeSetupTool/StateMachine.cs | 174 +++++++++--------- 3 files changed, 145 insertions(+), 90 deletions(-) create mode 100644 src/MasternodeSetupTool/DefaultStateHolder.cs create mode 100644 src/MasternodeSetupTool/IStateHolder.cs diff --git a/src/MasternodeSetupTool/DefaultStateHolder.cs b/src/MasternodeSetupTool/DefaultStateHolder.cs new file mode 100644 index 0000000..3f69d26 --- /dev/null +++ b/src/MasternodeSetupTool/DefaultStateHolder.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MasternodeSetupTool +{ + public class DefaultStateHolder : IStateHolder + { + public StateMachine.State currentState_; + public StateMachine.State? nextState_; + + public StateMachine.State CurrentState + { + get + { + return this.currentState_; + } + + private set + { + this.currentState_ = value; + } + } + + public StateMachine.State? NextState + { + get + { + return this.nextState_; + } + + set + { + this.nextState_ = value; + } + } + + public void SwitchToNextState() + { + StateMachine.State? nextState = this.NextState; + if (nextState != null) + { + this.CurrentState = (StateMachine.State)nextState; + this.NextState = null; + } + } + } +} diff --git a/src/MasternodeSetupTool/IStateHolder.cs b/src/MasternodeSetupTool/IStateHolder.cs new file mode 100644 index 0000000..d1105ce --- /dev/null +++ b/src/MasternodeSetupTool/IStateHolder.cs @@ -0,0 +1,11 @@ +using static MasternodeSetupTool.StateMachine; + +namespace MasternodeSetupTool; + +public interface IStateHolder +{ + State CurrentState { get; } + State? NextState { get; set; } + + void SwitchToNextState(); +} \ No newline at end of file diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 59c2ad5..cf61a4a 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; -using System.Windows; -using System.Windows.Threading; -using MasternodeSetupTool; using NBitcoin; using static MasternodeSetupTool.RegistrationService; @@ -51,8 +48,7 @@ public enum State private readonly RegistrationService registrationService; private readonly IStateHandler stateHandler; - private State currentState = State.Begin; - private State? nextState = null; + private IStateHolder stateHolder; public WalletCredentials? collateralWalletCredentials; public WalletCredentials? miningWalletCredentials; @@ -67,19 +63,19 @@ public StateMachine(NetworkType networkType, IStateHandler stateHandler) { this.stateHandler = stateHandler; this.registrationService = new RegistrationService(networkType, this); - + this.stateHolder = new DefaultStateHolder(); this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()).GetAwaiter().GetResult(); } public void OnRunNode() { - this.nextState = State.RunMasterNode_KeyPresent; + this.stateHolder.NextState = State.RunMasterNode_KeyPresent; } public void OnSetupNode() { - this.nextState = State.SetupMasterNode_Eula; + this.stateHolder.NextState = State.SetupMasterNode_Eula; } public async Task TickAsync() @@ -90,20 +86,19 @@ public async Task TickAsync() this.IsEnabled = false; - if (this.currentState == State.Begin) + if (this.stateHolder.CurrentState == State.Begin) { await this.stateHandler.OnStart(); } - if (this.nextState == null) + if (this.stateHolder.NextState == null) { this.IsEnabled = true; return; } - this.currentState = (State)this.nextState; - this.nextState = null; + this.stateHolder.SwitchToNextState(); if (await RunBranchAsync()) { @@ -128,7 +123,7 @@ private async Task RunBranchAsync() var thread = System.Threading.Thread.CurrentThread; - if (this.currentState == State.RunMasterNode_KeyPresent) + if (this.stateHolder.CurrentState == State.RunMasterNode_KeyPresent) { if (!this.registrationService.CheckFederationKeyExists()) { @@ -139,10 +134,10 @@ private async Task RunBranchAsync() return true; } - this.nextState = State.Run_StartMainChain; + this.stateHolder.NextState = State.Run_StartMainChain; } - if (this.currentState == State.Run_StartMainChain) + if (this.stateHolder.CurrentState == State.Run_StartMainChain) { if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -150,10 +145,10 @@ private async Task RunBranchAsync() return false; } - this.nextState = State.Run_MainChainSynced; + this.stateHolder.NextState = State.Run_MainChainSynced; } - if (this.currentState == State.Run_MainChainSynced) + if (this.stateHolder.CurrentState == State.Run_MainChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -161,10 +156,10 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Run_StartSideChain; + this.stateHolder.NextState = State.Run_StartSideChain; } - if (this.currentState == State.Run_StartSideChain) + if (this.stateHolder.CurrentState == State.Run_StartSideChain) { if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -173,10 +168,10 @@ private async Task RunBranchAsync() return false; } - this.nextState = State.Run_SideChainSynced; + this.stateHolder.NextState = State.Run_SideChainSynced; } - if (this.currentState == State.Run_SideChainSynced) + if (this.stateHolder.CurrentState == State.Run_SideChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -184,10 +179,10 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Run_LaunchBrowser; + this.stateHolder.NextState = State.Run_LaunchBrowser; } - if (this.currentState == State.Run_LaunchBrowser) + if (this.stateHolder.CurrentState == State.Run_LaunchBrowser) { await this.registrationService.StartMasterNodeDashboardAsync().ConfigureAwait(true); this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); @@ -202,7 +197,7 @@ private async Task RunBranchAsync() private async Task SetupBranchAsync() { - if (this.currentState == State.SetupMasterNode_Eula) + if (this.stateHolder.CurrentState == State.SetupMasterNode_Eula) { if (!await this.stateHandler.OnAskForEULA()) { @@ -210,35 +205,35 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_KeyPresent; + this.stateHolder.NextState = State.Setup_KeyPresent; } - if (this.currentState == State.Setup_KeyPresent) + if (this.stateHolder.CurrentState == State.Setup_KeyPresent) { if (this.registrationService.CheckFederationKeyExists()) { if (!await this.stateHandler.OnAskForNewFederationKey()) { - this.nextState = State.Setup_StartMainChain; + this.stateHolder.NextState = State.Setup_StartMainChain; return true; } this.registrationService.DeleteFederationKey(); } - this.nextState = State.Setup_CreateKey; + this.stateHolder.NextState = State.Setup_CreateKey; } - if (this.currentState == State.Setup_CreateKey) + if (this.stateHolder.CurrentState == State.Setup_CreateKey) { string savePath = this.registrationService.CreateFederationKey(); await this.stateHandler.OnShowNewFederationKey(this.registrationService.PubKey, savePath); - this.nextState = State.Setup_StartMainChain; + this.stateHolder.NextState = State.Setup_StartMainChain; } - if (this.currentState == State.Setup_StartMainChain) + if (this.stateHolder.CurrentState == State.Setup_StartMainChain) { // All 3 sub-branches of this state require the mainchain and sidechain nodes to be initialized, so do that first. if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) @@ -249,10 +244,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_MainChainSynced; + this.stateHolder.NextState = State.Setup_MainChainSynced; } - if (this.currentState == State.Setup_MainChainSynced) + if (this.stateHolder.CurrentState == State.Setup_MainChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -260,10 +255,10 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Setup_StartSideChain; + this.stateHolder.NextState = State.Setup_StartSideChain; } - if (this.currentState == State.Setup_StartSideChain) + if (this.stateHolder.CurrentState == State.Setup_StartSideChain) { if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { @@ -273,10 +268,10 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_SideChainSynced; + this.stateHolder.NextState = State.Setup_SideChainSynced; } - if (this.currentState == State.Setup_SideChainSynced) + if (this.stateHolder.CurrentState == State.Setup_SideChainSynced) { await this.registrationService.EnsureNodeIsInitializedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); @@ -284,16 +279,16 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); - this.nextState = State.Setup_CheckIsFederationMember; + this.stateHolder.NextState = State.Setup_CheckIsFederationMember; } - if (this.currentState == State.Setup_CheckIsFederationMember) + if (this.stateHolder.CurrentState == State.Setup_CheckIsFederationMember) { if (await this.registrationService.CheckIsFederationMemberAsync().ConfigureAwait(true)) { if (await this.stateHandler.OnAskToRunIfAlreadyMember()) { - this.nextState = State.Run_LaunchBrowser; + this.stateHolder.NextState = State.Run_LaunchBrowser; return true; } else @@ -304,22 +299,22 @@ private async Task SetupBranchAsync() } } - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; } - if (this.currentState == State.Setup_Select) + if (this.stateHolder.CurrentState == State.Setup_Select) { //TODO: Probably we need to show picker for collateral and mining wallets independently switch (await this.stateHandler.OnAskForWalletSource(NodeType.MainChain)) { case WalletSource.NewWallet: - this.nextState = State.Setup_Create; + this.stateHolder.NextState = State.Setup_Create; break; case WalletSource.RestoreWallet: - this.nextState = State.Setup_Restore; + this.stateHolder.NextState = State.Setup_Restore; break; case WalletSource.UseExistingWallet: - this.nextState = State.Setup_UseExisting; + this.stateHolder.NextState = State.Setup_UseExisting; break; default: await this.stateHandler.OnRegistrationCanceled(); @@ -328,31 +323,31 @@ private async Task SetupBranchAsync() } } - if (this.currentState == State.Setup_Create) + if (this.stateHolder.CurrentState == State.Setup_Create) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: true)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_Create_Mining; + this.stateHolder.NextState = State.Setup_Create_Mining; } - if (this.currentState == State.Setup_Create_Mining) + if (this.stateHolder.CurrentState == State.Setup_Create_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: true)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_Create_AskForCollateral; + this.stateHolder.NextState = State.Setup_Create_AskForCollateral; } - if (this.currentState == State.Setup_Create_AskForCollateral) + if (this.stateHolder.CurrentState == State.Setup_Create_AskForCollateral) { this.collateralWalletCredentials.ChoosenAddress = await HandleAddressSelectionAsync(NodeType.MainChain, this.collateralWalletCredentials.Name); @@ -362,52 +357,52 @@ private async Task SetupBranchAsync() } // The 3 sub-branches recombine after this and can share common states. - this.nextState = State.Setup_CheckForCollateral; + this.stateHolder.NextState = State.Setup_CheckForCollateral; } - if (this.currentState == State.Setup_CheckForCollateral) + if (this.stateHolder.CurrentState == State.Setup_CheckForCollateral) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.MainchainNetwork.DefaultAPIPort, this.collateralWalletCredentials.Name, RegistrationService.CollateralRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_CheckForRegistrationFee; + this.stateHolder.NextState = State.Setup_CheckForRegistrationFee; } else { await this.stateHandler.OnWaitingForCollateral(); - this.nextState = State.Setup_CheckForCollateral; + this.stateHolder.NextState = State.Setup_CheckForCollateral; } } - if (this.currentState == State.Setup_CheckForRegistrationFee) + if (this.stateHolder.CurrentState == State.Setup_CheckForRegistrationFee) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_PerformRegistration; + this.stateHolder.NextState = State.Setup_PerformRegistration; } else { string? miningAddress = await this.registrationService.GetFirstWalletAddressAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name).ConfigureAwait(true); this.miningWalletCredentials.ChoosenAddress = miningAddress; await this.stateHandler.OnMissingRegistrationFee(miningAddress); - this.nextState = State.Setup_WaitForBalance; + this.stateHolder.NextState = State.Setup_WaitForBalance; } } - if (this.currentState == State.Setup_WaitForBalance) + if (this.stateHolder.CurrentState == State.Setup_WaitForBalance) { if (await this.registrationService.CheckWalletBalanceAsync(this.registrationService.SidechainNetwork.DefaultAPIPort, this.miningWalletCredentials.Name, RegistrationService.FeeRequirement).ConfigureAwait(true)) { - this.nextState = State.Setup_PerformRegistration; + this.stateHolder.NextState = State.Setup_PerformRegistration; } else { await this.stateHandler.OnWaitingForRegistrationFee(); - this.nextState = State.Setup_WaitForBalance; + this.stateHolder.NextState = State.Setup_WaitForBalance; } } - if (this.currentState == State.Setup_PerformRegistration) + if (this.stateHolder.CurrentState == State.Setup_PerformRegistration) { bool registeredSuccessfully = await this.registrationService.CallJoinFederationRequestAsync(this.collateralWalletCredentials, this.miningWalletCredentials).ConfigureAwait(true); if (!registeredSuccessfully) @@ -417,91 +412,91 @@ private async Task SetupBranchAsync() return true; } - this.nextState = State.Setup_WaitForRegistration; + this.stateHolder.NextState = State.Setup_WaitForRegistration; } - if (this.currentState == State.Setup_WaitForRegistration) + if (this.stateHolder.CurrentState == State.Setup_WaitForRegistration) { if (await this.registrationService.MonitorJoinFederationRequestAsync().ConfigureAwait(true)) { await this.stateHandler.OnRegistrationComplete(); - this.nextState = State.Run_LaunchBrowser; + this.stateHolder.NextState = State.Run_LaunchBrowser; } } - if (this.currentState == State.Setup_Restore) + if (this.stateHolder.CurrentState == State.Setup_Restore) { if (!await HandleCreateWalletsAsync(NodeType.MainChain, createNewMnemonic: false)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_Restore_Mining; + this.stateHolder.NextState = State.Setup_Restore_Mining; } - if (this.currentState == State.Setup_Restore_Mining) + if (this.stateHolder.CurrentState == State.Setup_Restore_Mining) { if (!await HandleCreateWalletsAsync(NodeType.SideChain, createNewMnemonic: false)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_Create_AskForCollateral; + this.stateHolder.NextState = State.Setup_Create_AskForCollateral; } - if (this.currentState == State.Setup_UseExisting) + if (this.stateHolder.CurrentState == State.Setup_UseExisting) { this.collateralWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.MainChain, this.collateralWalletCredentials)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_UseExisting_CollateralPassword; + this.stateHolder.NextState = State.Setup_UseExisting_CollateralPassword; } - if (this.currentState == State.Setup_UseExisting_CollateralPassword) + if (this.stateHolder.CurrentState == State.Setup_UseExisting_CollateralPassword) { if (!await HandleExistingPasswordAsync(NodeType.MainChain, collateralWalletCredentials)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_UseExisting_Mining; + this.stateHolder.NextState = State.Setup_UseExisting_Mining; } - if (this.currentState == State.Setup_UseExisting_Mining) + if (this.stateHolder.CurrentState == State.Setup_UseExisting_Mining) { this.miningWalletCredentials = new WalletCredentials(); if (!await HandleExistingWalletNameAsync(NodeType.SideChain,this.miningWalletCredentials)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_UseExisting_MiningPassword; + this.stateHolder.NextState = State.Setup_UseExisting_MiningPassword; } - if (this.currentState == State.Setup_UseExisting_MiningPassword) + if (this.stateHolder.CurrentState == State.Setup_UseExisting_MiningPassword) { if (!await HandleExistingPasswordAsync(NodeType.SideChain, miningWalletCredentials)) { - this.nextState = State.Setup_Select; + this.stateHolder.NextState = State.Setup_Select; return true; } - this.nextState = State.Setup_UseExisting_CheckMainWalletSynced; + this.stateHolder.NextState = State.Setup_UseExisting_CheckMainWalletSynced; } - if (this.currentState == State.Setup_UseExisting_CheckMainWalletSynced) + if (this.stateHolder.CurrentState == State.Setup_UseExisting_CheckMainWalletSynced) { if (await HandleWalletSyncAsync(NodeType.MainChain)) { - this.nextState = State.Setup_UseExisting_CheckSideWalletSynced; + this.stateHolder.NextState = State.Setup_UseExisting_CheckSideWalletSynced; } else { @@ -509,12 +504,12 @@ private async Task SetupBranchAsync() } } - if (this.currentState == State.Setup_UseExisting_CheckSideWalletSynced) + if (this.stateHolder.CurrentState == State.Setup_UseExisting_CheckSideWalletSynced) { if (await HandleWalletSyncAsync(NodeType.SideChain)) { // Now we can jump back into the same sequence as the other 2 sub-branches. - this.nextState = State.Setup_Create_AskForCollateral; + this.stateHolder.NextState = State.Setup_Create_AskForCollateral; } else { @@ -844,8 +839,7 @@ private async Task HandleCreateWalletsAsync(NodeType nodeType, bool create private void ResetState() { - this.nextState = null; - this.currentState = State.Begin; + this.stateHolder.NextState = State.Begin; } public static string? GetInformationalVersion() => From 88d15f1e00de78e583a1d049ac05ff9615831f45 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 14:15:33 +0100 Subject: [PATCH 06/21] Add base for unit tests --- .../MasternodeSetupTool.Tests.csproj | 26 +++++++++++++++++++ src/MasternodeSetupTool.Tests/UnitTest1.cs | 26 +++++++++++++++++++ src/MasternodeSetupTool.Tests/Usings.cs | 1 + 3 files changed, 53 insertions(+) create mode 100644 src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj create mode 100644 src/MasternodeSetupTool.Tests/UnitTest1.cs create mode 100644 src/MasternodeSetupTool.Tests/Usings.cs diff --git a/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj b/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj new file mode 100644 index 0000000..b87259a --- /dev/null +++ b/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj @@ -0,0 +1,26 @@ + + + + net6.0-windows + enable + enable + + false + true + + + + + + + + + + + + + + + + + diff --git a/src/MasternodeSetupTool.Tests/UnitTest1.cs b/src/MasternodeSetupTool.Tests/UnitTest1.cs new file mode 100644 index 0000000..b77e2bd --- /dev/null +++ b/src/MasternodeSetupTool.Tests/UnitTest1.cs @@ -0,0 +1,26 @@ +using Moq; +using NBitcoin; + +namespace MasternodeSetupTool.Tests +{ + public class Tests + { + private NetworkType networkType; + private StateMachine stateMachine; + private Mock stateHandler; + + [SetUp] + public void Setup() + { + networkType = NetworkType.Testnet; + stateHandler = new Mock(); + stateMachine = new StateMachine(networkType, stateHandler.Object); + } + + [Test] + public async Task SetupNode() + { + + } + } +} \ No newline at end of file diff --git a/src/MasternodeSetupTool.Tests/Usings.cs b/src/MasternodeSetupTool.Tests/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/src/MasternodeSetupTool.Tests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file From aa0085f588da359f4dce37b0419612c74a35773f Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 14:15:55 +0100 Subject: [PATCH 07/21] Update project --- src/MasternodeSetupTool.sln | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/MasternodeSetupTool.sln b/src/MasternodeSetupTool.sln index cbed098..a6bcf7f 100644 --- a/src/MasternodeSetupTool.sln +++ b/src/MasternodeSetupTool.sln @@ -33,6 +33,8 @@ Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MasternodeSetupToolInstalle EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.FederatedSidechains.AdminDashboard", "..\StratisMasternodeDashboard\src\StratisMasternodeDashboard\Stratis.FederatedSidechains.AdminDashboard.csproj", "{31ECC5D0-F4E8-4C5B-A1C9-3A503BAAD0A8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasternodeSetupTool.Tests", "MasternodeSetupTool.Tests\MasternodeSetupTool.Tests.csproj", "{9BC703A3-4C17-481A-B9E7-BF7DA0275D16}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -55,6 +57,10 @@ Global {31ECC5D0-F4E8-4C5B-A1C9-3A503BAAD0A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {31ECC5D0-F4E8-4C5B-A1C9-3A503BAAD0A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {31ECC5D0-F4E8-4C5B-A1C9-3A503BAAD0A8}.Release|Any CPU.Build.0 = Release|Any CPU + {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 42562d1cbc02e3f81622237aa582a55441163a54 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 17:31:27 +0100 Subject: [PATCH 08/21] Add automated integration tests --- .../AutomatedFlowTest.csproj | 14 + .../AutomatedStateHandler.cs | 283 ++++++++++++++++++ src/AutomatedFlowTest/Configuration.cs | 29 ++ src/AutomatedFlowTest/Program.cs | 65 ++++ src/AutomatedFlowTest/data.json | 14 + src/MasternodeSetupTool.sln | 8 +- .../RegistrationService.cs | 3 +- src/MasternodeSetupTool/StateMachine.cs | 8 +- 8 files changed, 415 insertions(+), 9 deletions(-) create mode 100644 src/AutomatedFlowTest/AutomatedFlowTest.csproj create mode 100644 src/AutomatedFlowTest/AutomatedStateHandler.cs create mode 100644 src/AutomatedFlowTest/Configuration.cs create mode 100644 src/AutomatedFlowTest/Program.cs create mode 100644 src/AutomatedFlowTest/data.json diff --git a/src/AutomatedFlowTest/AutomatedFlowTest.csproj b/src/AutomatedFlowTest/AutomatedFlowTest.csproj new file mode 100644 index 0000000..c93c66f --- /dev/null +++ b/src/AutomatedFlowTest/AutomatedFlowTest.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0-windows + enable + enable + + + + + + + diff --git a/src/AutomatedFlowTest/AutomatedStateHandler.cs b/src/AutomatedFlowTest/AutomatedStateHandler.cs new file mode 100644 index 0000000..bea9c5b --- /dev/null +++ b/src/AutomatedFlowTest/AutomatedStateHandler.cs @@ -0,0 +1,283 @@ +// See https://aka.ms/new-console-template for more information +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.IL; +using MasternodeSetupTool; +using NBitcoin; +using Stratis.Bitcoin.Features.Wallet.Models; +using Stratis.SmartContracts; +using NodeType = MasternodeSetupTool.NodeType; + +class AutomatedStateHandler : IStateHandler +{ + private Configuration configuration; + + public AutomatedStateHandler(Configuration configuration) + { + this.configuration = configuration; + } + + public void Error(string message) + { + Console.WriteLine($"Error: {message}"); + } + + public void Error(Exception exception) + { + Console.WriteLine($"Error: {exception}"); + } + + public void Error(string message, Exception exception) + { + Console.WriteLine($"Error: {message}"); + Console.WriteLine($"Error: {exception}"); + } + + public void Info(string message, string? updateTag = null) + { + Console.WriteLine($"Info: {message}"); + } + + public async Task OnAlreadyMember() + { + Console.WriteLine("This node is already a member"); + } + + public async Task OnAskCreatePassword(NodeType nodeType) + { + Console.WriteLine($"Asked to create a password for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletPassword; + } + else + { + return this.configuration.miningWalletPassword; + } + } + + public async Task OnAskForEULA() + { + Console.WriteLine($"Asked for EULA"); + return true; + } + + public async Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) + { + Console.WriteLine($"Asked for mnemonic confirmation"); + Console.WriteLine($"Mnemonic: {mnemonic}"); + return true; + } + + public async Task OnAskForNewFederationKey() + { + Console.WriteLine($"Asked for new federation key, deny"); + return false; + } + + public async Task OnAskForPassphrase(NodeType nodeType) + { + Console.WriteLine($"Asked to create a passphrase for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletPassphrase; + } + else + { + return this.configuration.miningWalletPassphrase; + } + } + + public async Task OnAskForUserMnemonic(NodeType nodeType) + { + Console.WriteLine($"Asked a mnemonic for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletMnemonic; + } + else + { + return this.configuration.miningWalletMnemonic; + } + } + + public async Task OnAskForWalletName(NodeType nodeType, bool newWallet) + { + Console.WriteLine($"Asked a wallet name for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletName; + } + else + { + return this.configuration.miningWalletName; + } + } + + public async Task OnAskForWalletPassword(NodeType nodeType) + { + Console.WriteLine($"Asked a password for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletPassword; + } + else + { + return this.configuration.miningWalletPassword; + } + } + + public async Task OnAskForWalletSource(NodeType nodeType) + { + Console.WriteLine($"Asked a wallet source for {nodeType} wallet"); + + if (nodeType == NodeType.MainChain) + { + return this.configuration.collateralWalletSource; + } + else + { + return this.configuration.miningWalletSource; + } + } + + public async Task OnAskReenterPassword(NodeType nodeType) + { + Console.WriteLine($"Asked to reenter password, deny"); + return false; + } + + public async Task OnAskToRunIfAlreadyMember() + { + Console.WriteLine($"Already a member, stopping"); + return false; + } + + public async Task OnChooseAddress(List addresses, NodeType nodeType) + { + Console.WriteLine($"Choosing address {addresses.FirstOrDefault()?.Address}"); + return addresses.FirstOrDefault()?.Address; + } + + public async Task OnChooseWallet(List wallets, NodeType nodeType) + { + Console.WriteLine($"Choosing wallet {wallets.FirstOrDefault()?.Name}"); + return wallets.FirstOrDefault()?.Name; + } + + public async Task OnCreateWalletFailed(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet creation failed"); + } + + public async Task OnFederationKeyMissing() + { + Console.WriteLine($"Missing federation key"); + } + + public async Task OnMissingRegistrationFee(string address) + { + Console.WriteLine($"Missing registration fee on address: {address}"); + } + + public async Task OnMnemonicExists(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet mnemonic already exists"); + } + + public async Task OnMnemonicIsInvalid(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet mnemonic is invalid"); + } + + public async Task OnNodeFailedToStart(NodeType nodeType, string? reason = null) + { + Console.WriteLine($"{nodeType} node failed to start"); + Console.WriteLine($"Reason: {reason}"); + } + + public async Task OnProgramVersionAvailable(string? version) + { + Console.WriteLine($"App version: {version ?? "null"}"); + } + + public async Task OnRegistrationCanceled() + { + Console.WriteLine($"Registration canceled"); + } + + public async Task OnRegistrationComplete() + { + Console.WriteLine($"Registration complete"); + } + + public async Task OnRegistrationFailed() + { + Console.WriteLine($"Registration failed"); + } + + public async Task OnRestoreWalletFailed(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet restore failed"); + } + + public async Task OnResyncFailed(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet resync failed"); + } + + public async Task OnShowNewFederationKey(string pubKey, string savePath) + { + Console.WriteLine($"New pubKey is: {pubKey}"); + Console.WriteLine($"New savePath is: {savePath}"); + } + + public async Task OnShowWalletAddress(NodeType nodeType, string address) + { + Console.WriteLine($"{nodeType} wallet address is {address}"); + } + + public async Task OnShowWalletName(NodeType nodeType, string walletName) + { + Console.WriteLine($"{nodeType} wallet name is {walletName}"); + } + + public async Task OnStart() + { + Console.WriteLine($"Started"); + } + + public async Task OnWaitingForCollateral() + { + Console.WriteLine($"Waiting for collateral"); + } + + public async Task OnWaitingForRegistrationFee() + { + Console.WriteLine($"Waiting for registration fee"); + } + + public async Task OnWalletExistsOrInvalid(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet exists or invalid"); + } + + public async Task OnWalletNameExists(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet name already exists"); + } + + public async Task OnWalletSynced(NodeType nodeType) + { + Console.WriteLine($"{nodeType} wallet synced"); + } + + public async Task OnWalletSyncing(NodeType nodeType, int progress) + { + Console.WriteLine($"{nodeType} wallet is syncing, {progress}%"); + } +} diff --git a/src/AutomatedFlowTest/Configuration.cs b/src/AutomatedFlowTest/Configuration.cs new file mode 100644 index 0000000..5bd2efe --- /dev/null +++ b/src/AutomatedFlowTest/Configuration.cs @@ -0,0 +1,29 @@ +using MasternodeSetupTool; +using NBitcoin; + +public class Configuration +{ + public FlowType flowType = FlowType.SetupNode; + + public NetworkType networkType = NetworkType.Testnet; + + public WalletSource collateralWalletSource = WalletSource.UseExistingWallet; + public WalletSource miningWalletSource = WalletSource.UseExistingWallet; + + public string collateralWalletName = "TestWallet"; + public string miningWalletName = "TestWallet"; + + public string collateralWalletPassword = "12345"; + public string miningWalletPassword = "12345"; + + public string collateralWalletPassphrase = ""; + public string miningWalletPassphrase = ""; + + public string collateralWalletMnemonic = ""; + public string miningWalletMnemonic = ""; +} + +public enum FlowType +{ + RunNode, SetupNode +} diff --git a/src/AutomatedFlowTest/Program.cs b/src/AutomatedFlowTest/Program.cs new file mode 100644 index 0000000..a93d53a --- /dev/null +++ b/src/AutomatedFlowTest/Program.cs @@ -0,0 +1,65 @@ +using MasternodeSetupTool; +using Newtonsoft.Json; + +class Program +{ + static void Main(string[] args) + { + Configuration configuration = TryGetConfigurationFromParams(args) ?? new Configuration(); + + var stateHandler = new AutomatedStateHandler(configuration); + + var stateMachine = new StateMachine(configuration.networkType, stateHandler); + + Task.Run(async () => + { + switch (configuration.flowType) + { + case FlowType.RunNode: + stateMachine.OnRunNode(); + break; + case FlowType.SetupNode: + stateMachine.OnSetupNode(); + break; + } + + while (true) + { + await stateMachine.TickAsync(); + await Task.Delay(1000); + } + }).Wait(); + } + + private static Configuration? TryGetConfigurationFromParams(string[] args) + { + if (args.Length != 0) + { + string? configArg = args.FirstOrDefault(a => a.Contains("-config")); + if (configArg != null) + { + string[] parts = configArg.Split('='); + if (parts.Length == 2) + { + string? filePath = parts[1]; + if (!string.IsNullOrEmpty(filePath)) + { + try + { + string jsonConfig = File.ReadAllText(filePath); + Console.WriteLine($"Using configuration from {filePath}"); + Console.WriteLine(jsonConfig); + return JsonConvert.DeserializeObject(jsonConfig); + } catch + { + + } + } + } + } + } + + return null; + } + +} \ No newline at end of file diff --git a/src/AutomatedFlowTest/data.json b/src/AutomatedFlowTest/data.json new file mode 100644 index 0000000..1c362ed --- /dev/null +++ b/src/AutomatedFlowTest/data.json @@ -0,0 +1,14 @@ +{ + "flowType": "SetupNode", + "networkType": "Testnet", + "collateralWalletSource": "UseExistingWallet", + "miningWalletSource": "UseExistingWallet", + "collateralWalletName": "TestWallet", + "miningWalletName": "TestWallet", + "collateralWalletPassword": "12345", + "miningWalletPassword": "12345", + "collateralWalletPassphrase": "", + "miningWalletPassphrase": "", + "collateralWalletMnemonic": "", + "miningWalletMnemonic": "" +} diff --git a/src/MasternodeSetupTool.sln b/src/MasternodeSetupTool.sln index a6bcf7f..a174768 100644 --- a/src/MasternodeSetupTool.sln +++ b/src/MasternodeSetupTool.sln @@ -33,7 +33,9 @@ Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MasternodeSetupToolInstalle EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.FederatedSidechains.AdminDashboard", "..\StratisMasternodeDashboard\src\StratisMasternodeDashboard\Stratis.FederatedSidechains.AdminDashboard.csproj", "{31ECC5D0-F4E8-4C5B-A1C9-3A503BAAD0A8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasternodeSetupTool.Tests", "MasternodeSetupTool.Tests\MasternodeSetupTool.Tests.csproj", "{9BC703A3-4C17-481A-B9E7-BF7DA0275D16}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MasternodeSetupTool.Tests", "MasternodeSetupTool.Tests\MasternodeSetupTool.Tests.csproj", "{9BC703A3-4C17-481A-B9E7-BF7DA0275D16}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomatedFlowTest", "AutomatedFlowTest\AutomatedFlowTest.csproj", "{7EFD772D-AC69-477C-A731-7EABF10B8DE6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -61,6 +63,10 @@ Global {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Debug|Any CPU.Build.0 = Debug|Any CPU {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BC703A3-4C17-481A-B9E7-BF7DA0275D16}.Release|Any CPU.Build.0 = Release|Any CPU + {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/MasternodeSetupTool/RegistrationService.cs b/src/MasternodeSetupTool/RegistrationService.cs index 59a77aa..45b59cf 100644 --- a/src/MasternodeSetupTool/RegistrationService.cs +++ b/src/MasternodeSetupTool/RegistrationService.cs @@ -158,10 +158,9 @@ public async Task StartNodeAsync(NodeType nodeType, int apiPort, bool rein { FileName = Path.Combine(fullPath, "Stratis.CirrusMinerD.exe"), Arguments = argumentBuilder.ToString(), - UseShellExecute = false, + UseShellExecute = true, WindowStyle = ProcessWindowStyle.Minimized, WorkingDirectory = fullPath, - RedirectStandardError = true, }; var process = Process.Start(startInfo); diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index cf61a4a..4e67bb8 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -59,11 +59,11 @@ public enum State private bool IsEnabled = true; - public StateMachine(NetworkType networkType, IStateHandler stateHandler) + public StateMachine(NetworkType networkType, IStateHandler stateHandler, IStateHolder stateHolder = null) { this.stateHandler = stateHandler; this.registrationService = new RegistrationService(networkType, this); - this.stateHolder = new DefaultStateHolder(); + this.stateHolder = stateHolder ?? new DefaultStateHolder(); this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()).GetAwaiter().GetResult(); } @@ -80,8 +80,6 @@ public void OnSetupNode() public async Task TickAsync() { - var thread = System.Threading.Thread.CurrentThread; - if (!this.IsEnabled) return; this.IsEnabled = false; @@ -121,8 +119,6 @@ private async Task RunBranchAsync() { // The 'Run' branch - var thread = System.Threading.Thread.CurrentThread; - if (this.stateHolder.CurrentState == State.RunMasterNode_KeyPresent) { if (!this.registrationService.CheckFederationKeyExists()) From 3ebe305dd8b4077816ea8d101502404fe7801a83 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Wed, 20 Sep 2023 17:59:20 +0100 Subject: [PATCH 09/21] Define end state --- .../AutomatedStateHandler.cs | 9 ++------ src/AutomatedFlowTest/Program.cs | 10 +++++++-- src/MasternodeSetupTool/DefaultStateHolder.cs | 21 ++++++++++++------- src/MasternodeSetupTool/StateMachine.cs | 21 ++++++++++--------- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/AutomatedFlowTest/AutomatedStateHandler.cs b/src/AutomatedFlowTest/AutomatedStateHandler.cs index bea9c5b..97c1ac7 100644 --- a/src/AutomatedFlowTest/AutomatedStateHandler.cs +++ b/src/AutomatedFlowTest/AutomatedStateHandler.cs @@ -1,10 +1,5 @@ -// See https://aka.ms/new-console-template for more information -using ICSharpCode.Decompiler.CSharp.Syntax; -using ICSharpCode.Decompiler.IL; -using MasternodeSetupTool; -using NBitcoin; -using Stratis.Bitcoin.Features.Wallet.Models; -using Stratis.SmartContracts; +using MasternodeSetupTool; + using NodeType = MasternodeSetupTool.NodeType; class AutomatedStateHandler : IStateHandler diff --git a/src/AutomatedFlowTest/Program.cs b/src/AutomatedFlowTest/Program.cs index a93d53a..8c63b1b 100644 --- a/src/AutomatedFlowTest/Program.cs +++ b/src/AutomatedFlowTest/Program.cs @@ -7,9 +7,10 @@ static void Main(string[] args) { Configuration configuration = TryGetConfigurationFromParams(args) ?? new Configuration(); - var stateHandler = new AutomatedStateHandler(configuration); + IStateHandler stateHandler = new AutomatedStateHandler(configuration); + IStateHolder stateHolder = new DefaultStateHolder(repeatOnEndState: false); - var stateMachine = new StateMachine(configuration.networkType, stateHandler); + var stateMachine = new StateMachine(configuration.networkType, stateHandler, stateHolder); Task.Run(async () => { @@ -25,6 +26,11 @@ static void Main(string[] args) while (true) { + if (stateHolder.CurrentState == StateMachine.State.End) + { + return; + } + await stateMachine.TickAsync(); await Task.Delay(1000); } diff --git a/src/MasternodeSetupTool/DefaultStateHolder.cs b/src/MasternodeSetupTool/DefaultStateHolder.cs index 3f69d26..243c271 100644 --- a/src/MasternodeSetupTool/DefaultStateHolder.cs +++ b/src/MasternodeSetupTool/DefaultStateHolder.cs @@ -1,16 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MasternodeSetupTool +namespace MasternodeSetupTool { public class DefaultStateHolder : IStateHolder { - public StateMachine.State currentState_; + public StateMachine.State currentState_ = StateMachine.State.Begin; public StateMachine.State? nextState_; + private bool repeatOnEndState; + + public DefaultStateHolder(bool repeatOnEndState = true) + { + this.repeatOnEndState = repeatOnEndState; + } + public StateMachine.State CurrentState { get @@ -42,6 +43,10 @@ public void SwitchToNextState() StateMachine.State? nextState = this.NextState; if (nextState != null) { + if (nextState == StateMachine.State.End && this.repeatOnEndState) + { + nextState = StateMachine.State.Begin; + } this.CurrentState = (StateMachine.State)nextState; this.NextState = null; } diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 4e67bb8..150533d 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -12,6 +12,7 @@ public class StateMachine: ILogger public enum State { Begin, + End, RunMasterNode_KeyPresent, Run_StartMainChain, Run_MainChainSynced, @@ -125,7 +126,7 @@ private async Task RunBranchAsync() { await this.stateHandler.OnFederationKeyMissing(); - ResetState(); + GoToEndState(); return true; } @@ -183,7 +184,7 @@ private async Task RunBranchAsync() await this.registrationService.StartMasterNodeDashboardAsync().ConfigureAwait(true); this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); - ResetState(); + GoToEndState(); return true; } @@ -197,7 +198,7 @@ private async Task SetupBranchAsync() { if (!await this.stateHandler.OnAskForEULA()) { - ResetState(); + GoToEndState(); return true; } @@ -235,7 +236,7 @@ private async Task SetupBranchAsync() if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.MainChain); - ResetState(); + GoToEndState(); return true; } @@ -259,7 +260,7 @@ private async Task SetupBranchAsync() if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.SideChain); - ResetState(); + GoToEndState(); return true; } @@ -290,7 +291,7 @@ private async Task SetupBranchAsync() else { await this.stateHandler.OnAlreadyMember(); - ResetState(); + GoToEndState(); return true; } } @@ -314,7 +315,7 @@ private async Task SetupBranchAsync() break; default: await this.stateHandler.OnRegistrationCanceled(); - ResetState(); + GoToEndState(); return true; } } @@ -404,7 +405,7 @@ private async Task SetupBranchAsync() if (!registeredSuccessfully) { await this.stateHandler.OnRegistrationFailed(); - ResetState(); + GoToEndState(); return true; } @@ -833,9 +834,9 @@ private async Task HandleCreateWalletsAsync(NodeType nodeType, bool create return true; } - private void ResetState() + private void GoToEndState() { - this.stateHolder.NextState = State.Begin; + this.stateHolder.NextState = State.End; } public static string? GetInformationalVersion() => From 98322e513b17a9897731a19e485d9b2333269003 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:58:26 +0300 Subject: [PATCH 10/21] Make every state switch triggered by tick --- src/MasternodeSetupTool/StateMachine.cs | 59 +++++++++++++++---------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 150533d..93b0b5e 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NBitcoin; -using static MasternodeSetupTool.RegistrationService; +using static MasternodeSetupTool.RegistrationService; namespace MasternodeSetupTool; @@ -127,22 +122,23 @@ private async Task RunBranchAsync() await this.stateHandler.OnFederationKeyMissing(); GoToEndState(); - return true; } this.stateHolder.NextState = State.Run_StartMainChain; + return true; } - + if (this.stateHolder.CurrentState == State.Run_StartMainChain) { if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.MainChain); - return false; + return true; } this.stateHolder.NextState = State.Run_MainChainSynced; + return true; } if (this.stateHolder.CurrentState == State.Run_MainChainSynced) @@ -154,6 +150,7 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); this.stateHolder.NextState = State.Run_StartSideChain; + return true; } if (this.stateHolder.CurrentState == State.Run_StartSideChain) @@ -161,11 +158,12 @@ private async Task RunBranchAsync() if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.SideChain); - - return false; + GoToEndState(); + return true; } this.stateHolder.NextState = State.Run_SideChainSynced; + return true; } if (this.stateHolder.CurrentState == State.Run_SideChainSynced) @@ -177,6 +175,7 @@ private async Task RunBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); this.stateHolder.NextState = State.Run_LaunchBrowser; + return true; } if (this.stateHolder.CurrentState == State.Run_LaunchBrowser) @@ -185,7 +184,6 @@ private async Task RunBranchAsync() this.registrationService.LaunchBrowser($"http://localhost:{RegistrationService.DashboardPort}"); GoToEndState(); - return true; } @@ -219,6 +217,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_CreateKey; + return true; } if (this.stateHolder.CurrentState == State.Setup_CreateKey) @@ -228,6 +227,7 @@ private async Task SetupBranchAsync() await this.stateHandler.OnShowNewFederationKey(this.registrationService.PubKey, savePath); this.stateHolder.NextState = State.Setup_StartMainChain; + return true; } if (this.stateHolder.CurrentState == State.Setup_StartMainChain) @@ -236,12 +236,13 @@ private async Task SetupBranchAsync() if (!await this.registrationService.StartNodeAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.MainChain); - GoToEndState(); + GoToEndState(); return true; } this.stateHolder.NextState = State.Setup_MainChainSynced; + return true; } if (this.stateHolder.CurrentState == State.Setup_MainChainSynced) @@ -253,6 +254,7 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.MainChain, this.registrationService.MainchainNetwork.DefaultAPIPort).ConfigureAwait(true); this.stateHolder.NextState = State.Setup_StartSideChain; + return true; } if (this.stateHolder.CurrentState == State.Setup_StartSideChain) @@ -260,12 +262,13 @@ private async Task SetupBranchAsync() if (!await this.registrationService.StartNodeAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true)) { await this.stateHandler.OnNodeFailedToStart(NodeType.SideChain); - GoToEndState(); + GoToEndState(); return true; } this.stateHolder.NextState = State.Setup_SideChainSynced; + return true; } if (this.stateHolder.CurrentState == State.Setup_SideChainSynced) @@ -277,6 +280,7 @@ private async Task SetupBranchAsync() await this.registrationService.EnsureBlockstoreIsSyncedAsync(NodeType.SideChain, this.registrationService.SidechainNetwork.DefaultAPIPort).ConfigureAwait(true); this.stateHolder.NextState = State.Setup_CheckIsFederationMember; + return true; } if (this.stateHolder.CurrentState == State.Setup_CheckIsFederationMember) @@ -297,6 +301,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_Select; + return true; } if (this.stateHolder.CurrentState == State.Setup_Select) @@ -316,8 +321,8 @@ private async Task SetupBranchAsync() default: await this.stateHandler.OnRegistrationCanceled(); GoToEndState(); - return true; } + return true; } if (this.stateHolder.CurrentState == State.Setup_Create) @@ -330,6 +335,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_Create_Mining; + return true; } if (this.stateHolder.CurrentState == State.Setup_Create_Mining) @@ -342,6 +348,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_Create_AskForCollateral; + return true; } if (this.stateHolder.CurrentState == State.Setup_Create_AskForCollateral) @@ -355,6 +362,7 @@ private async Task SetupBranchAsync() // The 3 sub-branches recombine after this and can share common states. this.stateHolder.NextState = State.Setup_CheckForCollateral; + return true; } if (this.stateHolder.CurrentState == State.Setup_CheckForCollateral) @@ -368,6 +376,7 @@ private async Task SetupBranchAsync() await this.stateHandler.OnWaitingForCollateral(); this.stateHolder.NextState = State.Setup_CheckForCollateral; } + return true; } if (this.stateHolder.CurrentState == State.Setup_CheckForRegistrationFee) @@ -383,6 +392,7 @@ private async Task SetupBranchAsync() await this.stateHandler.OnMissingRegistrationFee(miningAddress); this.stateHolder.NextState = State.Setup_WaitForBalance; } + return true; } if (this.stateHolder.CurrentState == State.Setup_WaitForBalance) @@ -397,6 +407,7 @@ private async Task SetupBranchAsync() await this.stateHandler.OnWaitingForRegistrationFee(); this.stateHolder.NextState = State.Setup_WaitForBalance; } + return true; } if (this.stateHolder.CurrentState == State.Setup_PerformRegistration) @@ -410,6 +421,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_WaitForRegistration; + return true; } if (this.stateHolder.CurrentState == State.Setup_WaitForRegistration) @@ -419,6 +431,7 @@ private async Task SetupBranchAsync() await this.stateHandler.OnRegistrationComplete(); this.stateHolder.NextState = State.Run_LaunchBrowser; } + return true; } if (this.stateHolder.CurrentState == State.Setup_Restore) @@ -430,6 +443,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_Restore_Mining; + return true; } if (this.stateHolder.CurrentState == State.Setup_Restore_Mining) @@ -441,6 +455,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_Create_AskForCollateral; + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting) @@ -453,6 +468,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_UseExisting_CollateralPassword; + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting_CollateralPassword) @@ -464,6 +480,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_UseExisting_Mining; + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting_Mining) @@ -476,6 +493,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_UseExisting_MiningPassword; + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting_MiningPassword) @@ -487,6 +505,7 @@ private async Task SetupBranchAsync() } this.stateHolder.NextState = State.Setup_UseExisting_CheckMainWalletSynced; + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting_CheckMainWalletSynced) @@ -495,10 +514,7 @@ private async Task SetupBranchAsync() { this.stateHolder.NextState = State.Setup_UseExisting_CheckSideWalletSynced; } - else - { - return true; - } + return true; } if (this.stateHolder.CurrentState == State.Setup_UseExisting_CheckSideWalletSynced) @@ -508,10 +524,7 @@ private async Task SetupBranchAsync() // Now we can jump back into the same sequence as the other 2 sub-branches. this.stateHolder.NextState = State.Setup_Create_AskForCollateral; } - else - { - return true; - } + return true; } return false; From 6be9b782090bfbf6797768a05b925f981247a87f Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 12:04:49 +0100 Subject: [PATCH 11/21] Fix errors --- src/MasternodeSetupTool/StateMachine.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/MasternodeSetupTool/StateMachine.cs index 93b0b5e..06979ee 100644 --- a/src/MasternodeSetupTool/StateMachine.cs +++ b/src/MasternodeSetupTool/StateMachine.cs @@ -1,4 +1,9 @@ -using static MasternodeSetupTool.RegistrationService; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using NBitcoin; +using static MasternodeSetupTool.RegistrationService; namespace MasternodeSetupTool; @@ -321,6 +326,7 @@ private async Task SetupBranchAsync() default: await this.stateHandler.OnRegistrationCanceled(); GoToEndState(); + break; } return true; } From d607a51737c433f5863c30a48d67cb688f3a4f06 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 12:24:07 +0100 Subject: [PATCH 12/21] Moved core logic to separate project --- .../AutomatedFlowTest.csproj | 4 +- .../AddressItem.cs | 0 src/Core/Core.csproj | 24 ++++++++ src/Core/CoreLib.cs | 27 +++++++++ src/Core/DefaultStateHolder.cs | 55 +++++++++++++++++++ src/{MasternodeSetupTool => Core}/ILogger.cs | 0 .../IRegistrationService.cs | 0 .../IStateHandler.cs | 0 .../IStateHolder.cs | 0 .../RegistrationService.cs | 12 +--- .../StateMachine.cs | 0 .../WalletCreationState.cs | 0 .../WalletCredentials.cs | 0 .../WalletItem.cs | 0 .../MasternodeSetupTool.Tests.csproj | 2 +- src/MasternodeSetupTool/App.xaml.cs | 22 +------- .../MasternodeSetupTool.csproj | 16 +----- 17 files changed, 113 insertions(+), 49 deletions(-) rename src/{MasternodeSetupTool => Core}/AddressItem.cs (100%) create mode 100644 src/Core/Core.csproj create mode 100644 src/Core/CoreLib.cs create mode 100644 src/Core/DefaultStateHolder.cs rename src/{MasternodeSetupTool => Core}/ILogger.cs (100%) rename src/{MasternodeSetupTool => Core}/IRegistrationService.cs (100%) rename src/{MasternodeSetupTool => Core}/IStateHandler.cs (100%) rename src/{MasternodeSetupTool => Core}/IStateHolder.cs (100%) rename src/{MasternodeSetupTool => Core}/RegistrationService.cs (99%) rename src/{MasternodeSetupTool => Core}/StateMachine.cs (100%) rename src/{MasternodeSetupTool => Core}/WalletCreationState.cs (100%) rename src/{MasternodeSetupTool => Core}/WalletCredentials.cs (100%) rename src/{MasternodeSetupTool => Core}/WalletItem.cs (100%) diff --git a/src/AutomatedFlowTest/AutomatedFlowTest.csproj b/src/AutomatedFlowTest/AutomatedFlowTest.csproj index c93c66f..6f505e5 100644 --- a/src/AutomatedFlowTest/AutomatedFlowTest.csproj +++ b/src/AutomatedFlowTest/AutomatedFlowTest.csproj @@ -1,4 +1,4 @@ - + Exe @@ -8,7 +8,7 @@ - + diff --git a/src/MasternodeSetupTool/AddressItem.cs b/src/Core/AddressItem.cs similarity index 100% rename from src/MasternodeSetupTool/AddressItem.cs rename to src/Core/AddressItem.cs diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj new file mode 100644 index 0000000..cd782d7 --- /dev/null +++ b/src/Core/Core.csproj @@ -0,0 +1,24 @@ + + + + net6.0-windows + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/src/Core/CoreLib.cs b/src/Core/CoreLib.cs new file mode 100644 index 0000000..380ff30 --- /dev/null +++ b/src/Core/CoreLib.cs @@ -0,0 +1,27 @@ +using Flurl.Http; +using Flurl.Http.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Stratis.Bitcoin.Utilities.JsonConverters; + +namespace MasternodeSetupTool +{ + public static class CoreLib + { + public static void Initialize() + { + FlurlHttp.Configure(settings => { + var jsonSettings = new JsonSerializerSettings + { + Converters = new List() + { + new DateTimeToUnixTimeConverter(), + new IsoDateTimeConverter(), + } + }; + + settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings); + }); + } + } +} \ No newline at end of file diff --git a/src/Core/DefaultStateHolder.cs b/src/Core/DefaultStateHolder.cs new file mode 100644 index 0000000..243c271 --- /dev/null +++ b/src/Core/DefaultStateHolder.cs @@ -0,0 +1,55 @@ +namespace MasternodeSetupTool +{ + public class DefaultStateHolder : IStateHolder + { + public StateMachine.State currentState_ = StateMachine.State.Begin; + public StateMachine.State? nextState_; + + private bool repeatOnEndState; + + public DefaultStateHolder(bool repeatOnEndState = true) + { + this.repeatOnEndState = repeatOnEndState; + } + + public StateMachine.State CurrentState + { + get + { + return this.currentState_; + } + + private set + { + this.currentState_ = value; + } + } + + public StateMachine.State? NextState + { + get + { + return this.nextState_; + } + + set + { + this.nextState_ = value; + } + } + + public void SwitchToNextState() + { + StateMachine.State? nextState = this.NextState; + if (nextState != null) + { + if (nextState == StateMachine.State.End && this.repeatOnEndState) + { + nextState = StateMachine.State.Begin; + } + this.CurrentState = (StateMachine.State)nextState; + this.NextState = null; + } + } + } +} diff --git a/src/MasternodeSetupTool/ILogger.cs b/src/Core/ILogger.cs similarity index 100% rename from src/MasternodeSetupTool/ILogger.cs rename to src/Core/ILogger.cs diff --git a/src/MasternodeSetupTool/IRegistrationService.cs b/src/Core/IRegistrationService.cs similarity index 100% rename from src/MasternodeSetupTool/IRegistrationService.cs rename to src/Core/IRegistrationService.cs diff --git a/src/MasternodeSetupTool/IStateHandler.cs b/src/Core/IStateHandler.cs similarity index 100% rename from src/MasternodeSetupTool/IStateHandler.cs rename to src/Core/IStateHandler.cs diff --git a/src/MasternodeSetupTool/IStateHolder.cs b/src/Core/IStateHolder.cs similarity index 100% rename from src/MasternodeSetupTool/IStateHolder.cs rename to src/Core/IStateHolder.cs diff --git a/src/MasternodeSetupTool/RegistrationService.cs b/src/Core/RegistrationService.cs similarity index 99% rename from src/MasternodeSetupTool/RegistrationService.cs rename to src/Core/RegistrationService.cs index 45b59cf..7b6fed9 100644 --- a/src/MasternodeSetupTool/RegistrationService.cs +++ b/src/Core/RegistrationService.cs @@ -1,17 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; +using System.Diagnostics; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; using Flurl; using Flurl.Http; -using ICSharpCode.Decompiler.CSharp.Syntax; -using LiteDB; using NBitcoin; using NBitcoin.DataEncoders; using Newtonsoft.Json; diff --git a/src/MasternodeSetupTool/StateMachine.cs b/src/Core/StateMachine.cs similarity index 100% rename from src/MasternodeSetupTool/StateMachine.cs rename to src/Core/StateMachine.cs diff --git a/src/MasternodeSetupTool/WalletCreationState.cs b/src/Core/WalletCreationState.cs similarity index 100% rename from src/MasternodeSetupTool/WalletCreationState.cs rename to src/Core/WalletCreationState.cs diff --git a/src/MasternodeSetupTool/WalletCredentials.cs b/src/Core/WalletCredentials.cs similarity index 100% rename from src/MasternodeSetupTool/WalletCredentials.cs rename to src/Core/WalletCredentials.cs diff --git a/src/MasternodeSetupTool/WalletItem.cs b/src/Core/WalletItem.cs similarity index 100% rename from src/MasternodeSetupTool/WalletItem.cs rename to src/Core/WalletItem.cs diff --git a/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj b/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj index b87259a..ef24dc3 100644 --- a/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj +++ b/src/MasternodeSetupTool.Tests/MasternodeSetupTool.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/MasternodeSetupTool/App.xaml.cs b/src/MasternodeSetupTool/App.xaml.cs index c42b434..98834b5 100644 --- a/src/MasternodeSetupTool/App.xaml.cs +++ b/src/MasternodeSetupTool/App.xaml.cs @@ -1,10 +1,4 @@ -using System.Collections.Generic; -using System.Windows; -using Flurl.Http; -using Flurl.Http.Configuration; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Stratis.Bitcoin.Utilities.JsonConverters; +using System.Windows; namespace MasternodeSetupTool { @@ -15,19 +9,7 @@ public partial class App : Application { private void Application_Startup(object sender, StartupEventArgs e) { - FlurlHttp.Configure(settings => { - var jsonSettings = new JsonSerializerSettings - { - Converters = new List() - { - new DateTimeToUnixTimeConverter(), - new IsoDateTimeConverter(), - } - }; - - settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings); - }); - + CoreLib.Initialize(); var wnd = new MainWindow(e.Args); wnd.Show(); diff --git a/src/MasternodeSetupTool/MasternodeSetupTool.csproj b/src/MasternodeSetupTool/MasternodeSetupTool.csproj index 3fd0152..8c0bb46 100644 --- a/src/MasternodeSetupTool/MasternodeSetupTool.csproj +++ b/src/MasternodeSetupTool/MasternodeSetupTool.csproj @@ -14,21 +14,7 @@ - - - - - - - - - - - - - - - + From 4eb268e40b13a355fa9d4106f32b752c2a0dfa91 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:40:51 +0100 Subject: [PATCH 13/21] Reduce tick period --- src/MasternodeSetupTool/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index 68d2550..aa74768 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -71,7 +71,7 @@ public MainWindow(string[] args) this.timer = new DispatcherTimer { - Interval = TimeSpan.FromSeconds(1) + Interval = TimeSpan.FromMilliseconds(200) }; this.timer.Tick += StateMachine_TickAsync; From 1aef25dc62ce1ccea98fc54ee237f76aabfc33e0 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:41:24 +0100 Subject: [PATCH 14/21] Push Core module addition --- src/MasternodeSetupTool.sln | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/MasternodeSetupTool.sln b/src/MasternodeSetupTool.sln index a174768..ab8100b 100644 --- a/src/MasternodeSetupTool.sln +++ b/src/MasternodeSetupTool.sln @@ -35,7 +35,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.FederatedSidechains EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MasternodeSetupTool.Tests", "MasternodeSetupTool.Tests\MasternodeSetupTool.Tests.csproj", "{9BC703A3-4C17-481A-B9E7-BF7DA0275D16}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomatedFlowTest", "AutomatedFlowTest\AutomatedFlowTest.csproj", "{7EFD772D-AC69-477C-A731-7EABF10B8DE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomatedFlowTest", "AutomatedFlowTest\AutomatedFlowTest.csproj", "{7EFD772D-AC69-477C-A731-7EABF10B8DE6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{486CE220-C929-4125-AB22-9ADBCC4CB293}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -67,6 +69,10 @@ Global {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EFD772D-AC69-477C-A731-7EABF10B8DE6}.Release|Any CPU.Build.0 = Release|Any CPU + {486CE220-C929-4125-AB22-9ADBCC4CB293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {486CE220-C929-4125-AB22-9ADBCC4CB293}.Debug|Any CPU.Build.0 = Debug|Any CPU + {486CE220-C929-4125-AB22-9ADBCC4CB293}.Release|Any CPU.ActiveCfg = Release|Any CPU + {486CE220-C929-4125-AB22-9ADBCC4CB293}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 314838f1018baf9a28f038be9a384ea0055afca9 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:42:51 +0100 Subject: [PATCH 15/21] Set wallet name and address label --- src/MasternodeSetupTool/MainWindow.xaml.cs | 74 +++++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index aa74768..fcb6145 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -8,7 +8,10 @@ using System.Windows.Documents; using System.Windows.Media; using System.Windows.Threading; +using ICSharpCode.Decompiler.CSharp.Syntax; using NBitcoin; +using Stratis.Bitcoin.Features.Wallet.Models; +using Stratis.SmartContracts; using Color = System.Windows.Media.Color; namespace MasternodeSetupTool @@ -25,6 +28,12 @@ public partial class MainWindow : Window, IStateHandler private readonly StackPanel stackPanel; private readonly TextBlock statusBar; + private string collateralWalletName; + private string miningWalletName; + + private string collateralAddress; + private string miningAddress; + private bool createdButtons = false; private StateMachine stateMachine; @@ -190,6 +199,15 @@ private string WalletTypeName(NodeType nodeType) public async Task OnStart() { + this.collateralWalletName = null; + this.miningWalletName = null; + + this.collateralAddress = null; + this.miningAddress = null; + + this.UpdateWalletInfoLabel(NodeType.MainChain); + this.UpdateWalletInfoLabel(NodeType.SideChain); + if (!this.createdButtons) { this.createdButtons = true; @@ -233,7 +251,6 @@ public async Task OnProgramVersionAvailable(string? version) { if (version != null) { - var thread1 = System.Threading.Thread.CurrentThread; this.VersionText.Text = $"Version: {version}"; } } @@ -475,12 +492,30 @@ public async Task OnWalletSynced(NodeType nodeType) public async Task OnShowWalletName(NodeType nodeType, string walletName) { - //TODO + if (nodeType == NodeType.MainChain) + { + this.collateralWalletName = walletName; + } + else + { + this.miningWalletName = walletName; + } + + this.UpdateWalletInfoLabel(nodeType); } public async Task OnShowWalletAddress(NodeType nodeType, string address) { - //TODO + if (nodeType == NodeType.MainChain) + { + this.collateralAddress = address; + } + else + { + this.miningAddress = address; + } + + this.UpdateWalletInfoLabel(nodeType); } public async Task OnRestoreWalletFailed(NodeType nodeType) @@ -497,5 +532,38 @@ public async Task OnResyncFailed(NodeType nodeType) { LogError($"Cannot resync {WalletTypeName(nodeType)} wallet, aborting..."); } + + private void UpdateWalletInfoLabel(NodeType nodeType) + { + string? name; + string? address; + + TextBlock target; + + if (nodeType == NodeType.MainChain) + { + name = this.collateralWalletName; + address = this.collateralAddress; + target = this.CollateralAddressText; + } + else + { + name = this.miningWalletName; + address = this.miningAddress; + target = this.MiningAddressText; + } + + string label = name; + if (label == null) + { + label = ""; + } else + { + if (address != null) + label += $": {address}"; + } + + target.Text = label; + } } } From 651854a467a022e7d09ec5ccc2434bdbe34ac9ec Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:43:32 +0100 Subject: [PATCH 16/21] Remove unused imports --- src/MasternodeSetupTool/MainWindow.xaml.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/MasternodeSetupTool/MainWindow.xaml.cs b/src/MasternodeSetupTool/MainWindow.xaml.cs index fcb6145..bc9e4be 100644 --- a/src/MasternodeSetupTool/MainWindow.xaml.cs +++ b/src/MasternodeSetupTool/MainWindow.xaml.cs @@ -8,10 +8,7 @@ using System.Windows.Documents; using System.Windows.Media; using System.Windows.Threading; -using ICSharpCode.Decompiler.CSharp.Syntax; using NBitcoin; -using Stratis.Bitcoin.Features.Wallet.Models; -using Stratis.SmartContracts; using Color = System.Windows.Media.Color; namespace MasternodeSetupTool From 2e699d000c90fa8a6e7578ad2b87204741d0a6fe Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 13:50:21 +0100 Subject: [PATCH 17/21] Make registrationService injectable in StateMachine --- src/Core/StateMachine.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Core/StateMachine.cs b/src/Core/StateMachine.cs index 06979ee..29f6e7f 100644 --- a/src/Core/StateMachine.cs +++ b/src/Core/StateMachine.cs @@ -46,7 +46,7 @@ public enum State Setup_UseExisting_CheckSideWalletSynced } - private readonly RegistrationService registrationService; + private readonly IRegistrationService registrationService; private readonly IStateHandler stateHandler; private IStateHolder stateHolder; @@ -60,13 +60,15 @@ public enum State private bool IsEnabled = true; - public StateMachine(NetworkType networkType, IStateHandler stateHandler, IStateHolder stateHolder = null) + public StateMachine( + NetworkType networkType, + IStateHandler stateHandler, + IRegistrationService? registrationService = null, + IStateHolder? stateHolder = null) { this.stateHandler = stateHandler; - this.registrationService = new RegistrationService(networkType, this); + this.registrationService = registrationService ?? new RegistrationService(networkType, this); this.stateHolder = stateHolder ?? new DefaultStateHolder(); - - this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()).GetAwaiter().GetResult(); } public void OnRunNode() @@ -88,6 +90,7 @@ public async Task TickAsync() if (this.stateHolder.CurrentState == State.Begin) { await this.stateHandler.OnStart(); + await this.stateHandler.OnProgramVersionAvailable(GetInformationalVersion()); } if (this.stateHolder.NextState == null) From 8b42ff2d7ba05f460cf64189733a282e369ba539 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 14:24:28 +0100 Subject: [PATCH 18/21] Implement file logging for integration tests --- .../AutomatedStateHandler.cs | 99 ++++++++++--------- src/AutomatedFlowTest/CompositeLogger.cs | 20 ++++ src/AutomatedFlowTest/Configuration.cs | 3 + src/AutomatedFlowTest/ConsoleLogger.cs | 10 ++ src/AutomatedFlowTest/FileLogger.cs | 34 +++++++ src/AutomatedFlowTest/Program.cs | 29 +++++- src/AutomatedFlowTest/data.json | 4 +- 7 files changed, 149 insertions(+), 50 deletions(-) create mode 100644 src/AutomatedFlowTest/CompositeLogger.cs create mode 100644 src/AutomatedFlowTest/ConsoleLogger.cs create mode 100644 src/AutomatedFlowTest/FileLogger.cs diff --git a/src/AutomatedFlowTest/AutomatedStateHandler.cs b/src/AutomatedFlowTest/AutomatedStateHandler.cs index 97c1ac7..4574cde 100644 --- a/src/AutomatedFlowTest/AutomatedStateHandler.cs +++ b/src/AutomatedFlowTest/AutomatedStateHandler.cs @@ -2,44 +2,46 @@ using NodeType = MasternodeSetupTool.NodeType; -class AutomatedStateHandler : IStateHandler +public class AutomatedStateHandler : IStateHandler { private Configuration configuration; + private ILogger logger; - public AutomatedStateHandler(Configuration configuration) + public AutomatedStateHandler(Configuration configuration, ILogger logger) { this.configuration = configuration; + this.logger = logger; } public void Error(string message) { - Console.WriteLine($"Error: {message}"); + this.logger.Log($"Error: {message}"); } public void Error(Exception exception) { - Console.WriteLine($"Error: {exception}"); + this.logger.Log($"Error: {exception}"); } public void Error(string message, Exception exception) { - Console.WriteLine($"Error: {message}"); - Console.WriteLine($"Error: {exception}"); + this.logger.Log($"Error: {message}"); + this.logger.Log($"Error: {exception}"); } public void Info(string message, string? updateTag = null) { - Console.WriteLine($"Info: {message}"); + this.logger.Log($"Info: {message}"); } public async Task OnAlreadyMember() { - Console.WriteLine("This node is already a member"); + this.logger.Log("This node is already a member"); } public async Task OnAskCreatePassword(NodeType nodeType) { - Console.WriteLine($"Asked to create a password for {nodeType} wallet"); + this.logger.Log($"Asked to create a password for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -53,26 +55,26 @@ public async Task OnAlreadyMember() public async Task OnAskForEULA() { - Console.WriteLine($"Asked for EULA"); + this.logger.Log($"Asked for EULA"); return true; } public async Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) { - Console.WriteLine($"Asked for mnemonic confirmation"); - Console.WriteLine($"Mnemonic: {mnemonic}"); + this.logger.Log($"Asked for mnemonic confirmation"); + this.logger.Log($"Mnemonic: {mnemonic}"); return true; } public async Task OnAskForNewFederationKey() { - Console.WriteLine($"Asked for new federation key, deny"); + this.logger.Log($"Asked for new federation key, deny"); return false; } public async Task OnAskForPassphrase(NodeType nodeType) { - Console.WriteLine($"Asked to create a passphrase for {nodeType} wallet"); + this.logger.Log($"Asked to create a passphrase for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -86,7 +88,7 @@ public async Task OnAskForNewFederationKey() public async Task OnAskForUserMnemonic(NodeType nodeType) { - Console.WriteLine($"Asked a mnemonic for {nodeType} wallet"); + this.logger.Log($"Asked a mnemonic for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -100,7 +102,7 @@ public async Task OnAskForNewFederationKey() public async Task OnAskForWalletName(NodeType nodeType, bool newWallet) { - Console.WriteLine($"Asked a wallet name for {nodeType} wallet"); + this.logger.Log($"Asked a wallet name for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -114,7 +116,7 @@ public async Task OnAskForNewFederationKey() public async Task OnAskForWalletPassword(NodeType nodeType) { - Console.WriteLine($"Asked a password for {nodeType} wallet"); + this.logger.Log($"Asked a password for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -128,7 +130,7 @@ public async Task OnAskForNewFederationKey() public async Task OnAskForWalletSource(NodeType nodeType) { - Console.WriteLine($"Asked a wallet source for {nodeType} wallet"); + this.logger.Log($"Asked a wallet source for {nodeType} wallet"); if (nodeType == NodeType.MainChain) { @@ -142,137 +144,142 @@ public async Task OnAskForNewFederationKey() public async Task OnAskReenterPassword(NodeType nodeType) { - Console.WriteLine($"Asked to reenter password, deny"); + this.logger.Log($"Asked to reenter password, deny"); return false; } public async Task OnAskToRunIfAlreadyMember() { - Console.WriteLine($"Already a member, stopping"); + this.logger.Log($"Already a member, stopping"); return false; } public async Task OnChooseAddress(List addresses, NodeType nodeType) { - Console.WriteLine($"Choosing address {addresses.FirstOrDefault()?.Address}"); + this.logger.Log($"Choosing address {addresses.FirstOrDefault()?.Address}"); return addresses.FirstOrDefault()?.Address; } public async Task OnChooseWallet(List wallets, NodeType nodeType) { - Console.WriteLine($"Choosing wallet {wallets.FirstOrDefault()?.Name}"); + this.logger.Log($"Choosing wallet {wallets.FirstOrDefault()?.Name}"); return wallets.FirstOrDefault()?.Name; } public async Task OnCreateWalletFailed(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet creation failed"); + this.logger.Log($"{nodeType} wallet creation failed"); } public async Task OnFederationKeyMissing() { - Console.WriteLine($"Missing federation key"); + this.logger.Log($"Missing federation key"); } public async Task OnMissingRegistrationFee(string address) { - Console.WriteLine($"Missing registration fee on address: {address}"); + this.logger.Log($"Missing registration fee on address: {address}"); } public async Task OnMnemonicExists(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet mnemonic already exists"); + this.logger.Log($"{nodeType} wallet mnemonic already exists"); } public async Task OnMnemonicIsInvalid(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet mnemonic is invalid"); + this.logger.Log($"{nodeType} wallet mnemonic is invalid"); } public async Task OnNodeFailedToStart(NodeType nodeType, string? reason = null) { - Console.WriteLine($"{nodeType} node failed to start"); - Console.WriteLine($"Reason: {reason}"); + this.logger.Log($"{nodeType} node failed to start"); + this.logger.Log($"Reason: {reason}"); } public async Task OnProgramVersionAvailable(string? version) { - Console.WriteLine($"App version: {version ?? "null"}"); + this.logger.Log($"App version: {version ?? "null"}"); } public async Task OnRegistrationCanceled() { - Console.WriteLine($"Registration canceled"); + this.logger.Log($"Registration canceled"); } public async Task OnRegistrationComplete() { - Console.WriteLine($"Registration complete"); + this.logger.Log($"Registration complete"); } public async Task OnRegistrationFailed() { - Console.WriteLine($"Registration failed"); + this.logger.Log($"Registration failed"); } public async Task OnRestoreWalletFailed(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet restore failed"); + this.logger.Log($"{nodeType} wallet restore failed"); } public async Task OnResyncFailed(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet resync failed"); + this.logger.Log($"{nodeType} wallet resync failed"); } public async Task OnShowNewFederationKey(string pubKey, string savePath) { - Console.WriteLine($"New pubKey is: {pubKey}"); - Console.WriteLine($"New savePath is: {savePath}"); + this.logger.Log($"New pubKey is: {pubKey}"); + this.logger.Log($"New savePath is: {savePath}"); } public async Task OnShowWalletAddress(NodeType nodeType, string address) { - Console.WriteLine($"{nodeType} wallet address is {address}"); + this.logger.Log($"{nodeType} wallet address is {address}"); } public async Task OnShowWalletName(NodeType nodeType, string walletName) { - Console.WriteLine($"{nodeType} wallet name is {walletName}"); + this.logger.Log($"{nodeType} wallet name is {walletName}"); } public async Task OnStart() { - Console.WriteLine($"Started"); + this.logger.Log($"Started"); } public async Task OnWaitingForCollateral() { - Console.WriteLine($"Waiting for collateral"); + this.logger.Log($"Waiting for collateral"); } public async Task OnWaitingForRegistrationFee() { - Console.WriteLine($"Waiting for registration fee"); + this.logger.Log($"Waiting for registration fee"); } public async Task OnWalletExistsOrInvalid(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet exists or invalid"); + this.logger.Log($"{nodeType} wallet exists or invalid"); } public async Task OnWalletNameExists(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet name already exists"); + this.logger.Log($"{nodeType} wallet name already exists"); } public async Task OnWalletSynced(NodeType nodeType) { - Console.WriteLine($"{nodeType} wallet synced"); + this.logger.Log($"{nodeType} wallet synced"); } public async Task OnWalletSyncing(NodeType nodeType, int progress) { - Console.WriteLine($"{nodeType} wallet is syncing, {progress}%"); + this.logger.Log($"{nodeType} wallet is syncing, {progress}%"); + } + + public interface ILogger + { + void Log(string message); } } diff --git a/src/AutomatedFlowTest/CompositeLogger.cs b/src/AutomatedFlowTest/CompositeLogger.cs new file mode 100644 index 0000000..4c9cb56 --- /dev/null +++ b/src/AutomatedFlowTest/CompositeLogger.cs @@ -0,0 +1,20 @@ +namespace AutomatedFlowTest +{ + public class CompositeLogger: AutomatedStateHandler.ILogger + { + private IEnumerable Loggers; + + public CompositeLogger(IEnumerable loggers) + { + this.Loggers = loggers; + } + + public void Log(string message) + { + foreach (AutomatedStateHandler.ILogger logger in this.Loggers) + { + logger.Log(message); + } + } + } +} diff --git a/src/AutomatedFlowTest/Configuration.cs b/src/AutomatedFlowTest/Configuration.cs index 5bd2efe..3fe55bd 100644 --- a/src/AutomatedFlowTest/Configuration.cs +++ b/src/AutomatedFlowTest/Configuration.cs @@ -21,6 +21,9 @@ public class Configuration public string collateralWalletMnemonic = ""; public string miningWalletMnemonic = ""; + + public bool writeConsoleLog = true; + public string? logFilePath = null; } public enum FlowType diff --git a/src/AutomatedFlowTest/ConsoleLogger.cs b/src/AutomatedFlowTest/ConsoleLogger.cs new file mode 100644 index 0000000..354b2e0 --- /dev/null +++ b/src/AutomatedFlowTest/ConsoleLogger.cs @@ -0,0 +1,10 @@ +namespace AutomatedFlowTest +{ + public class ConsoleLogger : AutomatedStateHandler.ILogger + { + public void Log(string message) + { + Console.WriteLine(message); + } + } +} diff --git a/src/AutomatedFlowTest/FileLogger.cs b/src/AutomatedFlowTest/FileLogger.cs new file mode 100644 index 0000000..45902ef --- /dev/null +++ b/src/AutomatedFlowTest/FileLogger.cs @@ -0,0 +1,34 @@ +namespace AutomatedFlowTest +{ + public class FileLogger : AutomatedStateHandler.ILogger + { + private readonly string FilePath; + + public FileLogger(string filePath) + { + this.FilePath = filePath; + + // Test if we can access the file or directory + try + { + Directory.CreateDirectory(Path.GetDirectoryName(this.FilePath)); // Ensure directory exists + File.AppendAllText(this.FilePath, ""); // Try accessing the file + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to initialize the logger with file path: {this.FilePath}.", ex); + } + } + + public void Log(string message) + { + try + { + File.AppendAllText(this.FilePath, message + "\n"); + } + catch + { + } + } + } +} diff --git a/src/AutomatedFlowTest/Program.cs b/src/AutomatedFlowTest/Program.cs index 8c63b1b..df7492a 100644 --- a/src/AutomatedFlowTest/Program.cs +++ b/src/AutomatedFlowTest/Program.cs @@ -1,4 +1,5 @@ -using MasternodeSetupTool; +using AutomatedFlowTest; +using MasternodeSetupTool; using Newtonsoft.Json; class Program @@ -7,10 +8,13 @@ static void Main(string[] args) { Configuration configuration = TryGetConfigurationFromParams(args) ?? new Configuration(); - IStateHandler stateHandler = new AutomatedStateHandler(configuration); + IStateHandler stateHandler = new AutomatedStateHandler(configuration, BuildLogger(configuration)); IStateHolder stateHolder = new DefaultStateHolder(repeatOnEndState: false); - var stateMachine = new StateMachine(configuration.networkType, stateHandler, stateHolder); + var stateMachine = new StateMachine( + networkType: configuration.networkType, + stateHandler: stateHandler, + stateHolder: stateHolder); Task.Run(async () => { @@ -68,4 +72,23 @@ static void Main(string[] args) return null; } + private static AutomatedStateHandler.ILogger BuildLogger(Configuration configuration) + { + List loggers = new List(); + + if (configuration != null) + { + if (configuration.writeConsoleLog) + { + loggers.Add(new ConsoleLogger()); + } + + if (!string.IsNullOrEmpty(configuration.logFilePath)) + { + loggers.Add(new FileLogger(configuration.logFilePath)); + } + } + + return new CompositeLogger(loggers); + } } \ No newline at end of file diff --git a/src/AutomatedFlowTest/data.json b/src/AutomatedFlowTest/data.json index 1c362ed..92df29a 100644 --- a/src/AutomatedFlowTest/data.json +++ b/src/AutomatedFlowTest/data.json @@ -10,5 +10,7 @@ "collateralWalletPassphrase": "", "miningWalletPassphrase": "", "collateralWalletMnemonic": "", - "miningWalletMnemonic": "" + "miningWalletMnemonic": "", + "writeConsoleLog": true, + "logFilePath": "C:\\Users\\Administrator\\Source\\Repos\\stratisproject\\MasternodeSetupTool\\src\\AutomatedFlowTest\\log.txt" } From 32be99f93fc896934ea65cee7ab96a7a7f800516 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 15:14:07 +0100 Subject: [PATCH 19/21] Make flow fully configurable --- .../AutomatedStateHandler.cs | 33 ++++++++++++------- src/AutomatedFlowTest/Configuration.cs | 20 +++++++---- .../{data.json => example.json} | 11 +++++-- 3 files changed, 44 insertions(+), 20 deletions(-) rename src/AutomatedFlowTest/{data.json => example.json} (65%) diff --git a/src/AutomatedFlowTest/AutomatedStateHandler.cs b/src/AutomatedFlowTest/AutomatedStateHandler.cs index 4574cde..cbdc6f1 100644 --- a/src/AutomatedFlowTest/AutomatedStateHandler.cs +++ b/src/AutomatedFlowTest/AutomatedStateHandler.cs @@ -56,20 +56,20 @@ public async Task OnAlreadyMember() public async Task OnAskForEULA() { this.logger.Log($"Asked for EULA"); - return true; + return this.configuration.confirmEULA; } public async Task OnAskForMnemonicConfirmation(NodeType nodeType, string mnemonic) { this.logger.Log($"Asked for mnemonic confirmation"); this.logger.Log($"Mnemonic: {mnemonic}"); - return true; + return this.configuration.confirmMnemonic; } public async Task OnAskForNewFederationKey() { - this.logger.Log($"Asked for new federation key, deny"); - return false; + this.logger.Log($"Asked for new federation key"); + return this.configuration.confirmNewFederationKey; } public async Task OnAskForPassphrase(NodeType nodeType) @@ -144,26 +144,37 @@ public async Task OnAskForNewFederationKey() public async Task OnAskReenterPassword(NodeType nodeType) { - this.logger.Log($"Asked to reenter password, deny"); - return false; + this.logger.Log($"Asked to reenter password"); + + return this.configuration.confirmReenterPassword; } public async Task OnAskToRunIfAlreadyMember() { this.logger.Log($"Already a member, stopping"); - return false; + return this.configuration.confirmRunIfAlreadyMember; } public async Task OnChooseAddress(List addresses, NodeType nodeType) { - this.logger.Log($"Choosing address {addresses.FirstOrDefault()?.Address}"); - return addresses.FirstOrDefault()?.Address; + string? address = (nodeType == NodeType.MainChain + ? this.configuration.collateralWalletAddress + : this.configuration.miningWalletAddress) + ?? addresses.FirstOrDefault().Address; + + this.logger.Log($"Choosing address {address}"); + return address; } public async Task OnChooseWallet(List wallets, NodeType nodeType) { - this.logger.Log($"Choosing wallet {wallets.FirstOrDefault()?.Name}"); - return wallets.FirstOrDefault()?.Name; + string? walletName = (nodeType == NodeType.MainChain + ? this.configuration.collateralWalletName + : this.configuration.miningWalletName) + ?? wallets.FirstOrDefault().Name; + + this.logger.Log($"Choosing {nodeType} wallet {walletName}"); + return walletName; } public async Task OnCreateWalletFailed(NodeType nodeType) diff --git a/src/AutomatedFlowTest/Configuration.cs b/src/AutomatedFlowTest/Configuration.cs index 3fe55bd..05b345e 100644 --- a/src/AutomatedFlowTest/Configuration.cs +++ b/src/AutomatedFlowTest/Configuration.cs @@ -5,27 +5,33 @@ public class Configuration { public FlowType flowType = FlowType.SetupNode; + public bool writeConsoleLog = true; + public string? logFilePath = null; + public NetworkType networkType = NetworkType.Testnet; public WalletSource collateralWalletSource = WalletSource.UseExistingWallet; public WalletSource miningWalletSource = WalletSource.UseExistingWallet; - public string collateralWalletName = "TestWallet"; - public string miningWalletName = "TestWallet"; + public bool confirmEULA = true; + public bool confirmNewFederationKey = true; + public bool confirmRunIfAlreadyMember = true; + public bool confirmMnemonic = true; + public bool confirmReenterPassword = true; + public string? collateralWalletName = "TestWallet"; + public string? miningWalletName = "TestWallet"; public string collateralWalletPassword = "12345"; public string miningWalletPassword = "12345"; - public string collateralWalletPassphrase = ""; public string miningWalletPassphrase = ""; - public string collateralWalletMnemonic = ""; public string miningWalletMnemonic = ""; - - public bool writeConsoleLog = true; - public string? logFilePath = null; + public string? collateralWalletAddress = null; + public string? miningWalletAddress = null; } + public enum FlowType { RunNode, SetupNode diff --git a/src/AutomatedFlowTest/data.json b/src/AutomatedFlowTest/example.json similarity index 65% rename from src/AutomatedFlowTest/data.json rename to src/AutomatedFlowTest/example.json index 92df29a..4ec415d 100644 --- a/src/AutomatedFlowTest/data.json +++ b/src/AutomatedFlowTest/example.json @@ -1,8 +1,15 @@ { "flowType": "SetupNode", + "writeConsoleLog": true, + "logFilePath": null, "networkType": "Testnet", "collateralWalletSource": "UseExistingWallet", "miningWalletSource": "UseExistingWallet", + "confirmEULA": true, + "confirmNewFederationKey": false, + "confirmRunIfAlreadyMember": true, + "confirmMnemonic": true, + "confirmReenterPassword": true, "collateralWalletName": "TestWallet", "miningWalletName": "TestWallet", "collateralWalletPassword": "12345", @@ -11,6 +18,6 @@ "miningWalletPassphrase": "", "collateralWalletMnemonic": "", "miningWalletMnemonic": "", - "writeConsoleLog": true, - "logFilePath": "C:\\Users\\Administrator\\Source\\Repos\\stratisproject\\MasternodeSetupTool\\src\\AutomatedFlowTest\\log.txt" + "collateralWalletAddress": null, + "miningWalletAddress": null } From 0eb1ec16e1d29da1f9275c1f3fdaf6c8ecaacd65 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 15:15:24 +0100 Subject: [PATCH 20/21] Make program iterate faster --- src/AutomatedFlowTest/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AutomatedFlowTest/Program.cs b/src/AutomatedFlowTest/Program.cs index df7492a..840cf8a 100644 --- a/src/AutomatedFlowTest/Program.cs +++ b/src/AutomatedFlowTest/Program.cs @@ -36,7 +36,7 @@ static void Main(string[] args) } await stateMachine.TickAsync(); - await Task.Delay(1000); + await Task.Delay(TimeSpan.FromMilliseconds(200)); } }).Wait(); } From 4207c15d0fde35c4567671d6172e803ae39d9b48 Mon Sep 17 00:00:00 2001 From: Lazar Prijovic Date: Fri, 22 Sep 2023 15:46:58 +0100 Subject: [PATCH 21/21] Update unit tests --- .../StateMachineUnitTests.cs | 43 +++++++++++++++++++ src/MasternodeSetupTool.Tests/UnitTest1.cs | 26 ----------- 2 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 src/MasternodeSetupTool.Tests/StateMachineUnitTests.cs delete mode 100644 src/MasternodeSetupTool.Tests/UnitTest1.cs diff --git a/src/MasternodeSetupTool.Tests/StateMachineUnitTests.cs b/src/MasternodeSetupTool.Tests/StateMachineUnitTests.cs new file mode 100644 index 0000000..7b5a727 --- /dev/null +++ b/src/MasternodeSetupTool.Tests/StateMachineUnitTests.cs @@ -0,0 +1,43 @@ +using Moq; +using NBitcoin; + +namespace MasternodeSetupTool.Tests +{ + public class Tests + { + private NetworkType networkType; + + private Mock registrationService; + private Mock stateHandler; + + private IStateHolder stateHolder; + private StateMachine stateMachine; + + [SetUp] + public void Setup() + { + this.networkType = NetworkType.Testnet; + + this.registrationService = new Mock(); + this.stateHandler = new Mock(); + + this.stateHolder = new DefaultStateHolder(); + this.stateMachine = new StateMachine(networkType, stateHandler.Object, registrationService.Object, stateHolder); + } + + [Test] + public async Task ShouldAskForEULA() + { + this.stateHolder.NextState = StateMachine.State.SetupMasterNode_Eula; + + this.stateHandler.Setup(h => h.OnAskForEULA().Result).Returns(true); + + await this.stateMachine.TickAsync(); + + Assert.That(this.stateHolder.NextState, Is.Not.Null); + + Assert.That(this.stateHolder.NextState, Is.Not.EqualTo(StateMachine.State.SetupMasterNode_Eula)); + Assert.That(this.stateHolder.NextState, Is.EqualTo(StateMachine.State.Setup_KeyPresent)); + } + } +} \ No newline at end of file diff --git a/src/MasternodeSetupTool.Tests/UnitTest1.cs b/src/MasternodeSetupTool.Tests/UnitTest1.cs deleted file mode 100644 index b77e2bd..0000000 --- a/src/MasternodeSetupTool.Tests/UnitTest1.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Moq; -using NBitcoin; - -namespace MasternodeSetupTool.Tests -{ - public class Tests - { - private NetworkType networkType; - private StateMachine stateMachine; - private Mock stateHandler; - - [SetUp] - public void Setup() - { - networkType = NetworkType.Testnet; - stateHandler = new Mock(); - stateMachine = new StateMachine(networkType, stateHandler.Object); - } - - [Test] - public async Task SetupNode() - { - - } - } -} \ No newline at end of file