From bdf007901ce925e15cc79ce447272b871c3f4213 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 8 Nov 2017 14:43:56 +0100 Subject: [PATCH 01/22] Remove AssetViewModel dependency from HomeViewModel --- neo-gui/UI/Home/AccountsViewModel.cs | 2 +- neo-gui/UI/Home/AssetsView.xaml | 18 +- neo-gui/UI/Home/AssetsView.xaml.cs | 14 -- neo-gui/UI/Home/AssetsViewModel.cs | 198 ++++++++++++++++-- neo-gui/UI/Home/HomeViewModel.cs | 184 ++-------------- neo-gui/UI/Messages/AddAssetMessage.cs | 12 ++ neo-gui/UI/Messages/ClearAssetsMessage.cs | 6 + .../UI/Messages/UpdateAssetsBalanceMessage.cs | 12 ++ .../Messages/WalletBalanceChangedMessage.cs | 6 + neo-gui/neo-gui.csproj | 3 + 10 files changed, 255 insertions(+), 200 deletions(-) create mode 100644 neo-gui/UI/Messages/AddAssetMessage.cs create mode 100644 neo-gui/UI/Messages/ClearAssetsMessage.cs create mode 100644 neo-gui/UI/Messages/UpdateAssetsBalanceMessage.cs diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index ad9ab1a7..8e6953a2 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -387,7 +387,7 @@ private async void DeleteAccount() ApplicationContext.Instance.CurrentWallet.DeleteAddress(scriptHash); await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Remove(accountToDelete)); - this.messagePublisher.Publish(new WalletBalanceChangedMessage()); + this.messagePublisher.Publish(new WalletBalanceChangedMessage(true)); } #endregion Account Menu Command Methods diff --git a/neo-gui/UI/Home/AssetsView.xaml b/neo-gui/UI/Home/AssetsView.xaml index 09b5d5ac..ec2d18f3 100644 --- a/neo-gui/UI/Home/AssetsView.xaml +++ b/neo-gui/UI/Home/AssetsView.xaml @@ -1,9 +1,15 @@ - + + diff --git a/neo-gui/UI/Home/AssetsView.xaml.cs b/neo-gui/UI/Home/AssetsView.xaml.cs index 7e97b218..3b673398 100644 --- a/neo-gui/UI/Home/AssetsView.xaml.cs +++ b/neo-gui/UI/Home/AssetsView.xaml.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Windows; using System.Windows.Input; using Neo.Properties; @@ -17,19 +16,6 @@ public AssetsView() InitializeComponent(); } - private void AssetsView_Loaded(object sender, RoutedEventArgs e) - { - this.AttachViewModel(); - } - - private void AttachViewModel() - { - // Check if view model has already been attached - if (this.viewModel != null) return; - - this.viewModel = this.DataContext as AssetsViewModel; - } - private void AssetList_DoubleClick(object sender, MouseButtonEventArgs e) { if (this.viewModel?.SelectedAsset == null) return; diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index d81b908c..f42bc1b1 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Windows; using System.Windows.Input; using Neo.Core; @@ -9,30 +10,33 @@ using Neo.Properties; using Neo.SmartContract; using Neo.UI.Base.Collections; +using Neo.UI.Base.Dispatching; using Neo.UI.Base.Helpers; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; using Neo.Wallets; namespace Neo.UI.Home { - public class AssetsViewModel : ViewModelBase + public class AssetsViewModel : + ViewModelBase, + IMessageHandler, + IMessageHandler, + IMessageHandler { + #region Private Fields private static readonly UInt160 RecycleScriptHash = new[] { (byte)OpCode.PUSHT }.ToScriptHash(); + private readonly IMessagePublisher messagePublisher; + private readonly IDispatcher dispatcher; private readonly Dictionary certificateQueryResultCache; private AssetItem selectedAsset; - - public AssetsViewModel() - { - this.certificateQueryResultCache = new Dictionary(); - - this.Assets = new ConcurrentObservableCollection(); - } + #endregion #region Properties - public ConcurrentObservableCollection Assets { get; } public AssetItem SelectedAsset @@ -63,7 +67,7 @@ public bool ViewCertificateEnabled var queryResult = GetCertificateQueryResult(this.SelectedAsset.State); if (queryResult == null) return false; - + return queryResult.Type == CertificateQueryResultType.Good || queryResult.Type == CertificateQueryResultType.Expired || queryResult.Type == CertificateQueryResultType.Invalid; @@ -74,19 +78,25 @@ public bool ViewCertificateEnabled (this.SelectedAsset.State == null || (this.SelectedAsset.State.AssetType != AssetType.GoverningToken && this.SelectedAsset.State.AssetType != AssetType.UtilityToken)); - #endregion Properties - #region Commands - public ICommand ViewCertificateCommand => new RelayCommand(this.ViewCertificate); public ICommand DeleteAssetCommand => new RelayCommand(this.DeleteAsset); - #endregion Commands + + #region Constructor + public AssetsViewModel(IMessagePublisher messagePublisher, IDispatcher dispatcher) + { + this.certificateQueryResultCache = new Dictionary(); + + this.Assets = new ConcurrentObservableCollection(); + this.messagePublisher = messagePublisher; + this.dispatcher = dispatcher; + } + #endregion #region Menu Command Methods - private void ViewCertificate() { if (this.SelectedAsset == null || this.SelectedAsset.State == null) return; @@ -121,10 +131,10 @@ private void DeleteAsset() TransactionHelper.SignAndShowInformation(transaction); } - #endregion Menu Command Methods - internal CertificateQueryResult GetCertificateQueryResult(AssetState asset) + #region Private Methods + private CertificateQueryResult GetCertificateQueryResult(AssetState asset) { CertificateQueryResult result; if (asset.AssetType == AssetType.GoverningToken || asset.AssetType == AssetType.UtilityToken) @@ -148,5 +158,159 @@ internal CertificateQueryResult GetCertificateQueryResult(AssetState asset) return result; } + #endregion + + #region IMessageHandler implementation + public void HandleMessage(UpdateAssetsBalanceMessage message) + { + var assetList = this.Assets.ConvertToList(); + if (message.BalanceChanged) + { + var coins = ApplicationContext.Instance.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); + var bonusAvailable = Blockchain.CalculateBonus(ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); + var bonusUnavailable = Blockchain.CalculateBonus(coins.Where(p => p.State.HasFlag(CoinState.Confirmed) && p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)).Select(p => p.Reference), Blockchain.Default.Height + 1); + var bonus = bonusAvailable + bonusUnavailable; + + var assets = coins.GroupBy(p => p.Output.AssetId, (k, g) => new + { + Asset = Blockchain.Default.GetAssetState(k), + Value = g.Sum(p => p.Output.Value), + Claim = k.Equals(Blockchain.UtilityToken.Hash) ? bonus : Fixed8.Zero + }).ToDictionary(p => p.Asset.AssetId); + + if (bonus != Fixed8.Zero && !assets.ContainsKey(Blockchain.UtilityToken.Hash)) + { + assets[Blockchain.UtilityToken.Hash] = new + { + Asset = Blockchain.Default.GetAssetState(Blockchain.UtilityToken.Hash), + Value = Fixed8.Zero, + Claim = bonus + }; + } + + foreach (var asset in assetList.Where(item => item.State != null)) + { + if (assets.ContainsKey(asset.State.AssetId)) continue; + + this.dispatcher.InvokeOnMainUIThread(() => this.Assets.Remove(asset)); + } + + foreach (var asset in assets.Values) + { + if (asset.Asset == null || asset.Asset.AssetId == null) continue; + + var valueText = asset.Value + (asset.Asset.AssetId.Equals(Blockchain.UtilityToken.Hash) ? $"+({asset.Claim})" : ""); + + var item = this.Assets.FirstOrDefault(a => a.State != null && a.State.AssetId.Equals(asset.Asset.AssetId)); + + if (item != null) + { + // Asset item already exists + item.Value = valueText; + } + else + { + // Add new asset item + string assetName; + switch (asset.Asset.AssetType) + { + case AssetType.GoverningToken: + assetName = "NEO"; + break; + + case AssetType.UtilityToken: + assetName = "NeoGas"; + break; + + default: + assetName = asset.Asset.GetName(); + break; + } + + var assetItem = new AssetItem + { + Name = assetName, + Type = asset.Asset.AssetType.ToString(), + Issuer = $"{Strings.UnknownIssuer}[{asset.Asset.Owner}]", + Value = valueText, + State = asset.Asset + }; + + /*this.Assets.Groups["unchecked"] + { + Name = asset.Asset.AssetId.ToString(), + Tag = asset.Asset, + UseItemStyleForSubItems = false + };*/ + + this.dispatcher.InvokeOnMainUIThread(() => + { + this.Assets.Add(assetItem); + }); + } + } + + this.messagePublisher.Publish(new WalletBalanceChangedMessage(true)); + } + + + foreach (var item in assetList)//.Groups["unchecked"].Items) + { + if (item.State == null) continue; + + var asset = item.State; + + var queryResult = this.GetCertificateQueryResult(asset); + + if (queryResult == null) continue; + + using (queryResult) + { + switch (queryResult.Type) + { + case CertificateQueryResultType.Querying: + case CertificateQueryResultType.QueryFailed: + break; + case CertificateQueryResultType.System: + //subitem.ForeColor = Color.Green; + item.Issuer = Strings.SystemIssuer; + break; + case CertificateQueryResultType.Invalid: + //subitem.ForeColor = Color.Red; + item.Issuer = $"[{Strings.InvalidCertificate}][{asset.Owner}]"; + break; + case CertificateQueryResultType.Expired: + //subitem.ForeColor = Color.Yellow; + item.Issuer = $"[{Strings.ExpiredCertificate}]{queryResult.Certificate.Subject}[{asset.Owner}]"; + break; + case CertificateQueryResultType.Good: + //subitem.ForeColor = Color.Black; + item.Issuer = $"{queryResult.Certificate.Subject}[{asset.Owner}]"; + break; + } + switch (queryResult.Type) + { + case CertificateQueryResultType.System: + case CertificateQueryResultType.Missing: + case CertificateQueryResultType.Invalid: + case CertificateQueryResultType.Expired: + case CertificateQueryResultType.Good: + //item.Group = listView2.Groups["checked"]; + break; + } + } + } + } + + public void HandleMessage(ClearAssetsMessage message) + { + this.Assets.Clear(); + } + + public void HandleMessage(AddAssetMessage message) + { + this.Assets.Add(message.AssetItem); + } + #endregion } } \ No newline at end of file diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index bb280b43..8ef421b4 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -43,10 +43,13 @@ public class HomeViewModel : IMessageHandler, IMessageHandler { - private readonly IMessagePublisher _messagePublisher; - private readonly IMessageSubscriber _messageSubscriber; + #region Private Fields + private readonly IMessagePublisher messagePublisher; + private readonly IMessageSubscriber messageSubscriber; private readonly IDispatcher dispatcher; + private readonly object uiUpdateTimerLock = new object(); + private bool balanceChanged = false; private bool checkNep5Balance = false; @@ -60,21 +63,19 @@ public class HomeViewModel : private bool newVersionVisible; private Timer uiUpdateTimer; - private readonly object uiUpdateTimerLock = new object(); + #endregion #region Constructor public HomeViewModel( IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, IDispatcher dispatcher, - AssetsViewModel assetsViewModel, TransactionsViewModel transactionsViewModel) { - this._messagePublisher = messagePublisher; - this._messageSubscriber = messageSubscriber; + this.messagePublisher = messagePublisher; + this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; - this.AssetsViewModel = assetsViewModel; this.TransactionsViewModel = transactionsViewModel; this.SetupUIUpdateTimer(); @@ -84,8 +85,6 @@ public HomeViewModel( #endregion #region Public Properties - public AssetsViewModel AssetsViewModel { get; } - public TransactionsViewModel TransactionsViewModel { get; } public bool WalletIsOpen => ApplicationContext.Instance.CurrentWallet != null; @@ -234,9 +233,9 @@ private void ChangeWallet(UserWallet wallet) this.dispatcher.InvokeOnMainUIThread(() => { - this._messagePublisher.Publish(new ClearAccountsMessage()); + this.messagePublisher.Publish(new ClearAccountsMessage()); + this.messagePublisher.Publish(new ClearAssetsMessage()); - this.AssetsViewModel.Assets.Clear(); this.TransactionsViewModel.Transactions.Clear(); }); @@ -252,10 +251,10 @@ private void ChangeWallet(UserWallet wallet) ApplicationContext.Instance.CurrentWallet.TransactionsChanged += CurrentWallet_TransactionsChanged; } - this._messagePublisher.Publish(new EnableMenuItemsMessage()); + this.messagePublisher.Publish(new EnableMenuItemsMessage()); NotifyPropertyChanged(nameof(this.WalletIsOpen)); - this._messagePublisher.Publish(new LoadWalletAddressesMessage()); + this.messagePublisher.Publish(new LoadWalletAddressesMessage()); balanceChanged = true; checkNep5Balance = true; @@ -450,144 +449,8 @@ private void UpdateAssetBalances() { if (ApplicationContext.Instance.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; - this._messagePublisher.Publish(new UpdateAcountListMessage()); - - var assetList = this.AssetsViewModel.Assets.ConvertToList(); - if (balanceChanged) - { - var coins = ApplicationContext.Instance.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); - var bonusAvailable = Blockchain.CalculateBonus(ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); - var bonusUnavailable = Blockchain.CalculateBonus(coins.Where(p => p.State.HasFlag(CoinState.Confirmed) && p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)).Select(p => p.Reference), Blockchain.Default.Height + 1); - var bonus = bonusAvailable + bonusUnavailable; - - var assets = coins.GroupBy(p => p.Output.AssetId, (k, g) => new - { - Asset = Blockchain.Default.GetAssetState(k), - Value = g.Sum(p => p.Output.Value), - Claim = k.Equals(Blockchain.UtilityToken.Hash) ? bonus : Fixed8.Zero - }).ToDictionary(p => p.Asset.AssetId); - - if (bonus != Fixed8.Zero && !assets.ContainsKey(Blockchain.UtilityToken.Hash)) - { - assets[Blockchain.UtilityToken.Hash] = new - { - Asset = Blockchain.Default.GetAssetState(Blockchain.UtilityToken.Hash), - Value = Fixed8.Zero, - Claim = bonus - }; - } - - foreach (var asset in assetList.Where(item => item.State != null)) - { - if (assets.ContainsKey(asset.State.AssetId)) continue; - - this.dispatcher.InvokeOnMainUIThread(() => this.AssetsViewModel.Assets.Remove(asset)); - } - - foreach (var asset in assets.Values) - { - if (asset.Asset == null || asset.Asset.AssetId == null) continue; - - var valueText = asset.Value + (asset.Asset.AssetId.Equals(Blockchain.UtilityToken.Hash) ? $"+({asset.Claim})" : ""); - - var item = this.AssetsViewModel.Assets.FirstOrDefault(a => a.State != null && a.State.AssetId.Equals(asset.Asset.AssetId)); - - if (item != null) - { - // Asset item already exists - item.Value = valueText; - } - else - { - // Add new asset item - string assetName; - switch (asset.Asset.AssetType) - { - case AssetType.GoverningToken: - assetName = "NEO"; - break; - - case AssetType.UtilityToken: - assetName = "NeoGas"; - break; - - default: - assetName = asset.Asset.GetName(); - break; - } - - var assetItem = new AssetItem - { - Name = assetName, - Type = asset.Asset.AssetType.ToString(), - Issuer = $"{Strings.UnknownIssuer}[{asset.Asset.Owner}]", - Value = valueText, - State = asset.Asset - }; - - /*this.Assets.Groups["unchecked"] - { - Name = asset.Asset.AssetId.ToString(), - Tag = asset.Asset, - UseItemStyleForSubItems = false - };*/ - - this.dispatcher.InvokeOnMainUIThread(() => - { - this.AssetsViewModel.Assets.Add(assetItem); - }); - } - } - balanceChanged = false; - } - - - foreach (var item in assetList)//.Groups["unchecked"].Items) - { - if (item.State == null) continue; - - var asset = item.State; - - var queryResult = this.AssetsViewModel.GetCertificateQueryResult(asset); - - if (queryResult == null) continue; - - using (queryResult) - { - switch (queryResult.Type) - { - case CertificateQueryResultType.Querying: - case CertificateQueryResultType.QueryFailed: - break; - case CertificateQueryResultType.System: - //subitem.ForeColor = Color.Green; - item.Issuer = Strings.SystemIssuer; - break; - case CertificateQueryResultType.Invalid: - //subitem.ForeColor = Color.Red; - item.Issuer = $"[{Strings.InvalidCertificate}][{asset.Owner}]"; - break; - case CertificateQueryResultType.Expired: - //subitem.ForeColor = Color.Yellow; - item.Issuer = $"[{Strings.ExpiredCertificate}]{queryResult.Certificate.Subject}[{asset.Owner}]"; - break; - case CertificateQueryResultType.Good: - //subitem.ForeColor = Color.Black; - item.Issuer = $"{queryResult.Certificate.Subject}[{asset.Owner}]"; - break; - } - switch (queryResult.Type) - { - case CertificateQueryResultType.System: - case CertificateQueryResultType.Missing: - case CertificateQueryResultType.Invalid: - case CertificateQueryResultType.Expired: - case CertificateQueryResultType.Good: - //item.Group = listView2.Groups["checked"]; - break; - } - } - } + this.messagePublisher.Publish(new UpdateAcountListMessage()); + this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); } @@ -635,19 +498,15 @@ await this.dispatcher.InvokeOnMainUIThread(() => } else { - this.AssetsViewModel.Assets.Add(new AssetItem + var assetItem = new AssetItem { Name = name, Type = "NEP-5", Issuer = $"ScriptHash:{scriptHash}", Value = valueText, - }); + }; - /*this.Assets.Groups["checked"] - { - Name = script_hash.ToString(), - UseItemStyleForSubItems = false - };*/ + this.messagePublisher.Publish(new AddAssetMessage(assetItem)); } }); } @@ -728,7 +587,8 @@ private async void RebuildIndex() { await this.dispatcher.InvokeOnMainUIThread(() => { - this.AssetsViewModel.Assets.Clear(); + this.messagePublisher.Publish(new ClearAssetsMessage()); + this.TransactionsViewModel.Transactions.Clear(); }); @@ -742,7 +602,7 @@ private void RestoreAccounts() var contracts = view.GetContracts(); - this._messagePublisher.Publish(new RestoreContractsMessage(contracts)); + this.messagePublisher.Publish(new RestoreContractsMessage(contracts)); } private void Exit() @@ -924,7 +784,7 @@ private static uint GetWalletHeight() public void OnLoad() { - this._messageSubscriber.Subscribe(this); + this.messageSubscriber.Subscribe(this); this.Load(); } @@ -941,7 +801,7 @@ public void HandleMessage(UpdateApplicationMessage message) public void HandleMessage(WalletBalanceChangedMessage message) { - this.balanceChanged = true; + this.balanceChanged = message.BalanceChanged; } #endregion } diff --git a/neo-gui/UI/Messages/AddAssetMessage.cs b/neo-gui/UI/Messages/AddAssetMessage.cs new file mode 100644 index 00000000..9946490c --- /dev/null +++ b/neo-gui/UI/Messages/AddAssetMessage.cs @@ -0,0 +1,12 @@ +namespace Neo.UI.Messages +{ + public class AddAssetMessage + { + public AssetItem AssetItem { get; private set; } + + public AddAssetMessage(AssetItem assetItem) + { + this.AssetItem = assetItem; + } + } +} diff --git a/neo-gui/UI/Messages/ClearAssetsMessage.cs b/neo-gui/UI/Messages/ClearAssetsMessage.cs new file mode 100644 index 00000000..3088b02b --- /dev/null +++ b/neo-gui/UI/Messages/ClearAssetsMessage.cs @@ -0,0 +1,6 @@ +namespace Neo.UI.Messages +{ + public class ClearAssetsMessage + { + } +} diff --git a/neo-gui/UI/Messages/UpdateAssetsBalanceMessage.cs b/neo-gui/UI/Messages/UpdateAssetsBalanceMessage.cs new file mode 100644 index 00000000..714dc8ba --- /dev/null +++ b/neo-gui/UI/Messages/UpdateAssetsBalanceMessage.cs @@ -0,0 +1,12 @@ +namespace Neo.UI.Messages +{ + public class UpdateAssetsBalanceMessage + { + public bool BalanceChanged { get; private set; } + + public UpdateAssetsBalanceMessage(bool balanceChanged) + { + this.BalanceChanged = balanceChanged; + } + } +} diff --git a/neo-gui/UI/Messages/WalletBalanceChangedMessage.cs b/neo-gui/UI/Messages/WalletBalanceChangedMessage.cs index 8f4fa18e..47b9e7a6 100644 --- a/neo-gui/UI/Messages/WalletBalanceChangedMessage.cs +++ b/neo-gui/UI/Messages/WalletBalanceChangedMessage.cs @@ -2,5 +2,11 @@ { public class WalletBalanceChangedMessage { + public bool BalanceChanged { get; private set; } + + public WalletBalanceChangedMessage(bool balanceChanged) + { + this.BalanceChanged = balanceChanged; + } } } diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 6ac93d08..5acb7859 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -385,11 +385,14 @@ + + + TxOutListBox.xaml From cb23038247bc5aee6d66760d8c7b8fad97349f2e Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 8 Nov 2017 14:53:06 +0100 Subject: [PATCH 02/22] when removing the AssetViewModel dependency from HomeView, one binding was forgot --- neo-gui/UI/Home/HomeView.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo-gui/UI/Home/HomeView.xaml b/neo-gui/UI/Home/HomeView.xaml index b3313760..20154196 100644 --- a/neo-gui/UI/Home/HomeView.xaml +++ b/neo-gui/UI/Home/HomeView.xaml @@ -104,7 +104,7 @@ - + From 7aed22c46500a75ac63525fc8853f4ff5224b8b3 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 8 Nov 2017 16:06:18 +0100 Subject: [PATCH 03/22] Remove TransactionsViewModel dependency from HomeViewModel --- neo-gui/UI/Home/HomeView.xaml | 2 +- neo-gui/UI/Home/HomeViewModel.cs | 19 +++----- neo-gui/UI/Home/TransactionsView.xaml | 18 +++++--- neo-gui/UI/Home/TransactionsView.xaml.cs | 14 ------ neo-gui/UI/Home/TransactionsViewModel.cs | 46 ++++++++++++++----- .../UI/Messages/ClearTransactionsMessage.cs | 6 +++ .../UI/Messages/UpdateTransactionsMessage.cs | 15 ++++++ neo-gui/neo-gui.csproj | 2 + 8 files changed, 77 insertions(+), 45 deletions(-) create mode 100644 neo-gui/UI/Messages/ClearTransactionsMessage.cs create mode 100644 neo-gui/UI/Messages/UpdateTransactionsMessage.cs diff --git a/neo-gui/UI/Home/HomeView.xaml b/neo-gui/UI/Home/HomeView.xaml index 20154196..3410e799 100644 --- a/neo-gui/UI/Home/HomeView.xaml +++ b/neo-gui/UI/Home/HomeView.xaml @@ -109,7 +109,7 @@ - + diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 8ef421b4..57f1c4f9 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -13,14 +13,12 @@ using System.Windows.Input; using MahApps.Metro.Controls.Dialogs; using Neo.Core; -using Neo.Cryptography; using Neo.Implementations.Blockchains.LevelDB; using Neo.Implementations.Wallets.EntityFramework; using Neo.IO; using Neo.Properties; using Neo.SmartContract; using Neo.VM; -using Neo.Wallets; using Neo.UI.Assets; using Neo.UI.Base.Dispatching; using Neo.UI.Base.Helpers; @@ -69,15 +67,12 @@ public class HomeViewModel : public HomeViewModel( IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, - IDispatcher dispatcher, - TransactionsViewModel transactionsViewModel) + IDispatcher dispatcher) { this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; - this.TransactionsViewModel = transactionsViewModel; - this.SetupUIUpdateTimer(); this.StartUIUpdateTimer(); @@ -85,8 +80,6 @@ public HomeViewModel( #endregion #region Public Properties - public TransactionsViewModel TransactionsViewModel { get; } - public bool WalletIsOpen => ApplicationContext.Instance.CurrentWallet != null; public string BlockHeight => $"{GetWalletHeight()}/{Blockchain.Default.Height}/{Blockchain.Default.HeaderHeight}"; @@ -235,8 +228,7 @@ private void ChangeWallet(UserWallet wallet) { this.messagePublisher.Publish(new ClearAccountsMessage()); this.messagePublisher.Publish(new ClearAssetsMessage()); - - this.TransactionsViewModel.Transactions.Clear(); + this.messagePublisher.Publish(new ClearTransactionsMessage()); }); ApplicationContext.Instance.CurrentWallet = wallet; @@ -272,7 +264,7 @@ private void CurrentWallet_TransactionsChanged(IEnumerable tran private void CurrentWallet_TransactionsChanged(object sender, IEnumerable transactions) { - this.TransactionsViewModel.UpdateTransactions(transactions); + this.messagePublisher.Publish(new UpdateTransactionsMessage(transactions)); } #endregion Wallet Methods @@ -588,8 +580,7 @@ private async void RebuildIndex() await this.dispatcher.InvokeOnMainUIThread(() => { this.messagePublisher.Publish(new ClearAssetsMessage()); - - this.TransactionsViewModel.Transactions.Clear(); + this.messagePublisher.Publish(new ClearTransactionsMessage()); }); ApplicationContext.Instance.CurrentWallet.Rebuild(); @@ -782,12 +773,14 @@ private static uint GetWalletHeight() return walletHeight; } + #region ILoadable Implementation public void OnLoad() { this.messageSubscriber.Subscribe(this); this.Load(); } + #endregion #region IMessageHandler implementation public void HandleMessage(UpdateApplicationMessage message) diff --git a/neo-gui/UI/Home/TransactionsView.xaml b/neo-gui/UI/Home/TransactionsView.xaml index 6acebe3c..a9c6b519 100644 --- a/neo-gui/UI/Home/TransactionsView.xaml +++ b/neo-gui/UI/Home/TransactionsView.xaml @@ -1,9 +1,15 @@ - + + diff --git a/neo-gui/UI/Home/TransactionsView.xaml.cs b/neo-gui/UI/Home/TransactionsView.xaml.cs index f1f0e051..f910f840 100644 --- a/neo-gui/UI/Home/TransactionsView.xaml.cs +++ b/neo-gui/UI/Home/TransactionsView.xaml.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Windows; using System.Windows.Input; using Neo.Properties; @@ -17,19 +16,6 @@ public TransactionsView() InitializeComponent(); } - private void TransactionsView_Loaded(object sender, RoutedEventArgs e) - { - this.AttachViewModel(); - } - - private void AttachViewModel() - { - // Check if view model has already been attached - if (this.viewModel != null) return; - - this.viewModel = this.DataContext as TransactionsViewModel; - } - private void TransactionList_DoubleClick(object sender, MouseButtonEventArgs e) { if (this.viewModel?.SelectedTransaction == null) return; diff --git a/neo-gui/UI/Home/TransactionsViewModel.cs b/neo-gui/UI/Home/TransactionsViewModel.cs index 18deab34..ab5c1fe6 100644 --- a/neo-gui/UI/Home/TransactionsViewModel.cs +++ b/neo-gui/UI/Home/TransactionsViewModel.cs @@ -5,23 +5,24 @@ using Neo.Implementations.Wallets.EntityFramework; using Neo.UI.Base.Collections; using Neo.UI.Base.Dispatching; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; namespace Neo.UI.Home { - public class TransactionsViewModel : ViewModelBase + public class TransactionsViewModel : + ViewModelBase, + IMessageHandler, + IMessageHandler { + #region Private Fields private readonly IDispatcher dispatcher; private TransactionItem selectedTransaction; + #endregion - public TransactionsViewModel(IDispatcher dispatcher) - { - this.dispatcher = dispatcher; - - this.Transactions = new ConcurrentObservableCollection(); - } - + #region Public Properties public ConcurrentObservableCollection Transactions { get; } public TransactionItem SelectedTransaction @@ -41,10 +42,20 @@ public TransactionItem SelectedTransaction } public bool CopyTransactionIdEnabled => this.SelectedTransaction != null; - + public ICommand CopyTransactionIdCommand => new RelayCommand(this.CopyTransactionId); + #endregion - + #region Constructor + public TransactionsViewModel(IDispatcher dispatcher) + { + this.dispatcher = dispatcher; + + this.Transactions = new ConcurrentObservableCollection(); + } + #endregion + + #region Private Methods private void CopyTransactionId() { if (this.SelectedTransaction == null) return; @@ -52,7 +63,7 @@ private void CopyTransactionId() Clipboard.SetDataObject(this.SelectedTransaction.Id); } - public void UpdateTransactions(IEnumerable transactions) + private void UpdateTransactions(IEnumerable transactions) { this.dispatcher.InvokeOnMainUIThread(() => { @@ -109,5 +120,18 @@ private int GetTransactionIndex(string transactionId) // Could not find transaction return -1; } + #endregion + + #region IMessageHandler Implementation + public void HandleMessage(ClearTransactionsMessage message) + { + this.Transactions.Clear(); + } + + public void HandleMessage(UpdateTransactionsMessage message) + { + this.UpdateTransactions(message.Transactions); + } + #endregion } } \ No newline at end of file diff --git a/neo-gui/UI/Messages/ClearTransactionsMessage.cs b/neo-gui/UI/Messages/ClearTransactionsMessage.cs new file mode 100644 index 00000000..36419015 --- /dev/null +++ b/neo-gui/UI/Messages/ClearTransactionsMessage.cs @@ -0,0 +1,6 @@ +namespace Neo.UI.Messages +{ + public class ClearTransactionsMessage + { + } +} diff --git a/neo-gui/UI/Messages/UpdateTransactionsMessage.cs b/neo-gui/UI/Messages/UpdateTransactionsMessage.cs new file mode 100644 index 00000000..87974528 --- /dev/null +++ b/neo-gui/UI/Messages/UpdateTransactionsMessage.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Neo.Implementations.Wallets.EntityFramework; + +namespace Neo.UI.Messages +{ + public class UpdateTransactionsMessage + { + public IEnumerable Transactions { get; private set; } + + public UpdateTransactionsMessage(IEnumerable transactions) + { + this.Transactions = transactions; + } + } +} diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 5acb7859..c59014ea 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -388,11 +388,13 @@ + + TxOutListBox.xaml From 2ee0045f71e88df9396212ddb5eb2f1d233be4b7 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Thu, 9 Nov 2017 00:24:44 +0100 Subject: [PATCH 04/22] First draft of the BlockChainController --- neo-gui/App.xaml.cs | 2 + neo-gui/ApplicationContext.cs | 3 + neo-gui/Controllers/BlockChainController.cs | 292 ++++++++++++++++++ .../ControllersRegistrationModule.cs | 17 + neo-gui/Controllers/IBlockChainController.cs | 12 + neo-gui/IApplicationContext.cs | 3 + neo-gui/Program.cs | 2 + neo-gui/UI/Messages/BlockProgressMessage.cs | 24 ++ .../UI/Messages/NewVersionAvailableMessage.cs | 12 + neo-gui/neo-gui.csproj | 5 + 10 files changed, 372 insertions(+) create mode 100644 neo-gui/Controllers/BlockChainController.cs create mode 100644 neo-gui/Controllers/ControllersRegistrationModule.cs create mode 100644 neo-gui/Controllers/IBlockChainController.cs create mode 100644 neo-gui/UI/Messages/BlockProgressMessage.cs create mode 100644 neo-gui/UI/Messages/NewVersionAvailableMessage.cs diff --git a/neo-gui/App.xaml.cs b/neo-gui/App.xaml.cs index d6a869b6..9f86ec0e 100644 --- a/neo-gui/App.xaml.cs +++ b/neo-gui/App.xaml.cs @@ -1,5 +1,6 @@ using System.Windows; using Autofac; +using Neo.Controllers; using Neo.UI; using Neo.UI.Base; using Neo.UI.Base.Themes; @@ -37,6 +38,7 @@ private static void BuildContainer() autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); + autoFacContainerBuilder.RegisterModule(); var container = autoFacContainerBuilder.Build(); diff --git a/neo-gui/ApplicationContext.cs b/neo-gui/ApplicationContext.cs index 97472079..aae69e31 100644 --- a/neo-gui/ApplicationContext.cs +++ b/neo-gui/ApplicationContext.cs @@ -1,6 +1,7 @@ using System; using Autofac; using Neo.Implementations.Wallets.EntityFramework; +using Neo.Network; namespace Neo { @@ -21,5 +22,7 @@ private ApplicationContext() public ILifetimeScope ContainerLifetimeScope { get; set; } public UserWallet CurrentWallet { get; set; } + + public LocalNode LocalNode { get; set; } } } \ No newline at end of file diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs new file mode 100644 index 00000000..1d863f9e --- /dev/null +++ b/neo-gui/Controllers/BlockChainController.cs @@ -0,0 +1,292 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using System.Timers; +using Neo.Core; +using Neo.Implementations.Blockchains.LevelDB; +using Neo.Implementations.Wallets.EntityFramework; +using Neo.IO; +using Neo.Properties; +using Neo.SmartContract; +using Neo.UI; +using Neo.UI.Base.Dispatching; +using Neo.UI.Base.Messages; +using Neo.UI.Messages; +using Neo.VM; + +namespace Neo.Controllers +{ + public class BlockChainController : IBlockChainController + { + #region Private Fields + private readonly object uiUpdateTimerLock = new object(); + private readonly IApplicationContext applicationContext; + private readonly IMessagePublisher messagePublisher; + private readonly IDispatcher dispatcher; + + private DateTime persistenceTime = DateTime.MinValue; + private Timer uiUpdateTimer; + private bool balanceChanged; + private bool checkNep5Balance; + #endregion + + #region Constructor + public BlockChainController( + IApplicationContext applicationContext, + IMessagePublisher messagePublisher, + IDispatcher dispatcher) + { + this.applicationContext = applicationContext; + this.messagePublisher = messagePublisher; + this.dispatcher = dispatcher; + + Task.Run(() => + { + CheckForNewerVersion(); + + ImportBlocksIfRequired(); + + Blockchain.PersistCompleted += Blockchain_PersistCompleted; + + // Start node + this.applicationContext.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); + }); + + lock (this.uiUpdateTimerLock) + { + if (this.uiUpdateTimer != null) + { + // Stop previous timer + this.uiUpdateTimer.Stop(); + + this.uiUpdateTimer.Elapsed -= this.UpdateWallet; + + this.uiUpdateTimer.Dispose(); + + this.uiUpdateTimer = null; + } + + var timer = new Timer + { + Interval = 500, + Enabled = true, + AutoReset = true + }; + + timer.Elapsed += this.UpdateWallet; + + this.uiUpdateTimer = timer; + } + } + #endregion + + #region Private Methods + private void UpdateWallet(object sender, ElapsedEventArgs e) + { + var persistenceSpan = DateTime.UtcNow - this.persistenceTime; + + this.UpdateBlockProgress(persistenceSpan); + + this.UpdateBalances(persistenceSpan); + } + + private void UpdateBlockProgress(TimeSpan persistenceSpan) + { + var blockProgressIndeterminate = false; + var blockProgress = 0; + + if (persistenceSpan < TimeSpan.Zero) persistenceSpan = TimeSpan.Zero; + + if (persistenceSpan > Blockchain.TimePerBlock) + { + blockProgressIndeterminate = true; + } + else + { + blockProgressIndeterminate = true; + blockProgress = persistenceSpan.Seconds; + } + + var blockHeight = $"{GetWalletHeight()}/{Blockchain.Default.Height}/{Blockchain.Default.HeaderHeight}"; + var nodeCount = this.applicationContext.LocalNode.RemoteNodeCount; + var blockStatus = $"{Strings.WaitingForNextBlock}:"; + + var blockProgressMessage = new BlockProgressMessage( + blockProgressIndeterminate, + blockProgress, + blockHeight, + nodeCount, + blockStatus); + this.messagePublisher.Publish(blockProgressMessage); + } + + private void UpdateBalances(TimeSpan persistenceSpan) + { + if (ApplicationContext.Instance.CurrentWallet == null) return; + + this.UpdateAssetBalances(); + + this.UpdateNEP5TokenBalances(persistenceSpan); + } + + private void UpdateAssetBalances() + { + if (ApplicationContext.Instance.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; + + this.messagePublisher.Publish(new UpdateAcountListMessage()); + this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); + } + + private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) + { + if (!checkNep5Balance) return; + + if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; + + // Update balances + var addresses = ApplicationContext.Instance.CurrentWallet.GetAddresses().ToArray(); + foreach (var s in Settings.Default.NEP5Watched) + { + var scriptHash = UInt160.Parse(s); + byte[] script; + using (var builder = new ScriptBuilder()) + { + foreach (var address in addresses) + { + builder.EmitAppCall(scriptHash, "balanceOf", address); + } + builder.Emit(OpCode.DEPTH, OpCode.PACK); + builder.EmitAppCall(scriptHash, "decimals"); + builder.EmitAppCall(scriptHash, "name"); + script = builder.ToArray(); + } + + var engine = ApplicationEngine.Run(script); + if (engine.State.HasFlag(VMState.FAULT)) continue; + + var name = engine.EvaluationStack.Pop().GetString(); + var decimals = (byte)engine.EvaluationStack.Pop().GetBigInteger(); + var amount = engine.EvaluationStack.Pop().GetArray().Aggregate(BigInteger.Zero, (x, y) => x + y.GetBigInteger()); + if (amount == 0) continue; + var balance = new BigDecimal(amount, decimals); + var valueText = balance.ToString(); + + await this.dispatcher.InvokeOnMainUIThread(() => + { + var item = (AssetItem)null; //this.GetAsset(scriptHash); + + if (item != null) + { + item.Value = valueText; + } + else + { + var assetItem = new AssetItem + { + Name = name, + Type = "NEP-5", + Issuer = $"ScriptHash:{scriptHash}", + Value = valueText, + }; + + this.messagePublisher.Publish(new AddAssetMessage(assetItem)); + } + }); + } + checkNep5Balance = false; + } + + private uint GetWalletHeight() + { + uint walletHeight = 0; + + if (this.applicationContext.CurrentWallet != null && + this.applicationContext.CurrentWallet.WalletHeight > 0) + { + // Set wallet height + walletHeight = this.applicationContext.CurrentWallet.WalletHeight - 1; + } + + return walletHeight; + } + + private void CheckForNewerVersion() + { + var latestVersion = VersionHelper.LatestVersion; + var currentVersion = VersionHelper.CurrentVersion; + + if (latestVersion == null || latestVersion <= currentVersion) return; + + this.messagePublisher.Publish(new NewVersionAvailableMessage($"{Strings.DownloadNewVersion}: {latestVersion}")); + } + + private void ImportBlocksIfRequired() + { + const string acc_path = "chain.acc"; + const string acc_zip_path = acc_path + ".zip"; + + // Check if blocks need importing + if (File.Exists(acc_path)) + { + // Import blocks + using (var fileStream = new FileStream(acc_path, FileMode.Open, FileAccess.Read, FileShare.None)) + { + ImportBlocks(fileStream); + } + File.Delete(acc_path); + } + else if (File.Exists(acc_zip_path)) + { + using (var fileStream = new FileStream(acc_zip_path, FileMode.Open, FileAccess.Read, FileShare.None)) + using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read)) + using (var zipStream = zip.GetEntry(acc_path).Open()) + { + ImportBlocks(zipStream); + } + File.Delete(acc_zip_path); + } + } + + private void ImportBlocks(Stream stream) + { + var blockchain = (LevelDBBlockchain)Blockchain.Default; + blockchain.VerifyBlocks = false; + using (var reader = new BinaryReader(stream)) + { + var count = reader.ReadUInt32(); + for (int height = 0; height < count; height++) + { + var array = reader.ReadBytes(reader.ReadInt32()); + + if (height <= Blockchain.Default.Height) continue; + + var block = array.AsSerializable(); + Blockchain.Default.AddBlock(block); + } + } + blockchain.VerifyBlocks = true; + } + + private void Blockchain_PersistCompleted(object sender, Block block) + { + this.persistenceTime = DateTime.UtcNow; + if (ApplicationContext.Instance.CurrentWallet != null) + { + this.checkNep5Balance = true; + + var coins = ApplicationContext.Instance.CurrentWallet.GetCoins(); + if (coins.Any(coin => !coin.State.HasFlag(CoinState.Spent) && + coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))) + { + balanceChanged = true; + } + } + + this.messagePublisher.Publish(new UpdateTransactionsMessage(Enumerable.Empty())); + } + #endregion + } +} diff --git a/neo-gui/Controllers/ControllersRegistrationModule.cs b/neo-gui/Controllers/ControllersRegistrationModule.cs new file mode 100644 index 00000000..f01b55b1 --- /dev/null +++ b/neo-gui/Controllers/ControllersRegistrationModule.cs @@ -0,0 +1,17 @@ +using Autofac; + +namespace Neo.Controllers +{ + public class ControllersRegistrationModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterType() + .As() + .SingleInstance(); + + base.Load(builder); + } + } +} diff --git a/neo-gui/Controllers/IBlockChainController.cs b/neo-gui/Controllers/IBlockChainController.cs new file mode 100644 index 00000000..1670c806 --- /dev/null +++ b/neo-gui/Controllers/IBlockChainController.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Controllers +{ + public interface IBlockChainController + { + } +} diff --git a/neo-gui/IApplicationContext.cs b/neo-gui/IApplicationContext.cs index f9f2f8ce..ad3a2502 100644 --- a/neo-gui/IApplicationContext.cs +++ b/neo-gui/IApplicationContext.cs @@ -1,5 +1,6 @@ using Autofac; using Neo.Implementations.Wallets.EntityFramework; +using Neo.Network; namespace Neo { @@ -8,5 +9,7 @@ public interface IApplicationContext ILifetimeScope ContainerLifetimeScope { get; set; } UserWallet CurrentWallet { get; set; } + + LocalNode LocalNode { get; set; } } } diff --git a/neo-gui/Program.cs b/neo-gui/Program.cs index 902ad223..6f9d34c4 100644 --- a/neo-gui/Program.cs +++ b/neo-gui/Program.cs @@ -36,6 +36,8 @@ public static void Main() { LocalNode.UpnpEnabled = true; + ApplicationContext.Instance.LocalNode = LocalNode; + // Start GUI normally var app = new App(); app.Run(); diff --git a/neo-gui/UI/Messages/BlockProgressMessage.cs b/neo-gui/UI/Messages/BlockProgressMessage.cs new file mode 100644 index 00000000..5fa889f4 --- /dev/null +++ b/neo-gui/UI/Messages/BlockProgressMessage.cs @@ -0,0 +1,24 @@ +namespace Neo.UI.Messages +{ + public class BlockProgressMessage + { + public bool BlockProgressIndeterminate { get; private set; } + + public int BlockProgress { get; private set; } + + public string BlockHeight { get; private set; } + + public int NodeCount { get; private set; } + + public string BlockStatus { get; set; } + + public BlockProgressMessage(bool blockProgressIndeterminate, int blockProgress, string blockHeight, int nodeCount, string blockStatus) + { + this.BlockProgressIndeterminate = blockProgressIndeterminate; + this.BlockProgress = blockProgress; + this.BlockHeight = blockHeight; + this.NodeCount = nodeCount; + this.BlockStatus = blockStatus; + } + } +} diff --git a/neo-gui/UI/Messages/NewVersionAvailableMessage.cs b/neo-gui/UI/Messages/NewVersionAvailableMessage.cs new file mode 100644 index 00000000..d361da1c --- /dev/null +++ b/neo-gui/UI/Messages/NewVersionAvailableMessage.cs @@ -0,0 +1,12 @@ +namespace Neo.UI.Messages +{ + public class NewVersionAvailableMessage + { + public string NewVersionLabel { get; private set; } + + public NewVersionAvailableMessage(string newVersionLabel) + { + this.NewVersionLabel = newVersionLabel; + } + } +} diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index c59014ea..49ec8d53 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -296,6 +296,9 @@ App.xaml + + + @@ -386,11 +389,13 @@ + + From fc1e4da23fdf7d04d75d9c156bbc069dd5898380 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Fri, 10 Nov 2017 14:22:21 +0100 Subject: [PATCH 05/22] WalletController created --- neo-gui/Controllers/IWalletController.cs | 9 +++++++++ neo-gui/Controllers/WalletController.cs | 17 +++++++++++++++++ neo-gui/neo-gui.csproj | 2 ++ 3 files changed, 28 insertions(+) create mode 100644 neo-gui/Controllers/IWalletController.cs create mode 100644 neo-gui/Controllers/WalletController.cs diff --git a/neo-gui/Controllers/IWalletController.cs b/neo-gui/Controllers/IWalletController.cs new file mode 100644 index 00000000..5de8682f --- /dev/null +++ b/neo-gui/Controllers/IWalletController.cs @@ -0,0 +1,9 @@ +namespace Neo.Controllers +{ + public interface IWalletController + { + void CreateWallet(string walletPath, string password); + + void OpenWallet(string walletPath, string password); + } +} diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs new file mode 100644 index 00000000..4748a1ef --- /dev/null +++ b/neo-gui/Controllers/WalletController.cs @@ -0,0 +1,17 @@ +namespace Neo.Controllers +{ + public class WalletController : IWalletController + { + #region IWalletController implementation + public void CreateWallet(string walletPath, string password) + { + throw new System.NotImplementedException(); + } + + public void OpenWallet(string walletPath, string password) + { + throw new System.NotImplementedException(); + } + #endregion + } +} diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 49ec8d53..7b006f25 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -299,6 +299,8 @@ + + From d3ae9f197f03448ce106210887787aef9969071b Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Sat, 11 Nov 2017 03:01:23 +0100 Subject: [PATCH 06/22] CreateWallet in WalletController implemented from code in HomeViewModel --- neo-gui/Controllers/WalletController.cs | 83 +++++++++++++++++++- neo-gui/UI/Base/Messages/MessagePublisher.cs | 14 +++- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index 4748a1ef..b44b6efb 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -1,16 +1,93 @@ -namespace Neo.Controllers +using System; +using System.Collections.Generic; +using Neo.Implementations.Wallets.EntityFramework; +using Neo.Properties; +using Neo.UI.Base.Messages; +using Neo.UI.Messages; + +namespace Neo.Controllers { public class WalletController : IWalletController { + #region Private Fields + private readonly IApplicationContext applicationContext; + private readonly IMessagePublisher messagePublisher; + + private UserWallet currentWallet; + + private bool balanceChanged; + private bool checkNep5Balance; + #endregion + + #region Constructor + public WalletController( + IApplicationContext applicationContext, + IMessagePublisher messagePublisher) + { + this.applicationContext = applicationContext; + this.messagePublisher = messagePublisher; + } + #endregion + #region IWalletController implementation public void CreateWallet(string walletPath, string password) { - throw new System.NotImplementedException(); + var newWallet = UserWallet.Create(walletPath, password); + + this.SetCurrentWallet(newWallet); + + Settings.Default.LastWalletPath = walletPath; + Settings.Default.Save(); } public void OpenWallet(string walletPath, string password) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); + } + #endregion + + #region Private Methods + private void SetCurrentWallet(UserWallet wallet) + { + if (this.currentWallet != null) + { + // Dispose current wallet + this.currentWallet.BalanceChanged -= this.CurrentWalletBalanceChanged; + this.currentWallet.TransactionsChanged -= this.CurrentWalletTransactionsChanged; + this.currentWallet.Dispose(); + } + + this.messagePublisher.Publish(new ClearAccountsMessage()); + this.messagePublisher.Publish(new ClearAssetsMessage()); + this.messagePublisher.Publish(new ClearTransactionsMessage()); + + this.currentWallet = wallet; + + if (this.currentWallet != null) + { + // Setup wallet + var transactions = this.currentWallet.LoadTransactions(); + this.messagePublisher.Publish(new UpdateTransactionsMessage(transactions)); + + this.currentWallet.BalanceChanged += this.CurrentWalletBalanceChanged; + this.currentWallet.TransactionsChanged += this.CurrentWalletTransactionsChanged; + } + + this.messagePublisher.Publish(new EnableMenuItemsMessage()); + this.messagePublisher.Publish(new LoadWalletAddressesMessage()); + + this.balanceChanged = true; + this.checkNep5Balance = true; + } + + private void CurrentWalletTransactionsChanged(object sender, IEnumerable transactions) + { + this.messagePublisher.Publish(new UpdateTransactionsMessage(transactions)); + } + + private void CurrentWalletBalanceChanged(object sender, EventArgs e) + { + this.balanceChanged = true; } #endregion } diff --git a/neo-gui/UI/Base/Messages/MessagePublisher.cs b/neo-gui/UI/Base/Messages/MessagePublisher.cs index ead81542..c5ed389f 100644 --- a/neo-gui/UI/Base/Messages/MessagePublisher.cs +++ b/neo-gui/UI/Base/Messages/MessagePublisher.cs @@ -1,22 +1,30 @@ -namespace Neo.UI.Base.Messages +using Neo.UI.Base.Dispatching; + +namespace Neo.UI.Base.Messages { public class MessagePublisher : IMessagePublisher { #region Private Fields private readonly IInternalMessageAggregator _messageAggregator; + private readonly IDispatcher dispatcher; #endregion #region Constructor - public MessagePublisher(IInternalMessageAggregator messageAggregator) + public MessagePublisher(IInternalMessageAggregator messageAggregator, IDispatcher dispatcher) { this._messageAggregator = messageAggregator; + this.dispatcher = dispatcher; } #endregion #region IMessagePublisher implementation public void Publish(T message) { - this._messageAggregator.Publish(message); + this.dispatcher.InvokeOnMainUIThread(() => + { + this._messageAggregator.Publish(message); + }); + } #endregion } From d7505bd80fb43ffc00edf3df8022467a9854bd5a Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Mon, 13 Nov 2017 00:15:22 +0100 Subject: [PATCH 07/22] * WalletController: OpenWallet code copy * DialogHelper created - Object that will abstract the creation and show dialogs. --- neo-gui/App.xaml.cs | 2 + neo-gui/Controllers/WalletController.cs | 62 ++++++++++++++++++- .../DialogResults/OpenWalletDialogResult.cs | 18 ++++++ neo-gui/DialogResults/YesOrNoDialogResult.cs | 12 ++++ neo-gui/Helpers/DialogHelper.cs | 19 ++++++ neo-gui/Helpers/DialogResult.cs | 7 +++ neo-gui/Helpers/HelpersRegistrationModule.cs | 16 +++++ neo-gui/Helpers/IDialogHelper.cs | 12 ++++ neo-gui/neo-gui.csproj | 6 ++ 9 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 neo-gui/DialogResults/OpenWalletDialogResult.cs create mode 100644 neo-gui/DialogResults/YesOrNoDialogResult.cs create mode 100644 neo-gui/Helpers/DialogHelper.cs create mode 100644 neo-gui/Helpers/DialogResult.cs create mode 100644 neo-gui/Helpers/HelpersRegistrationModule.cs create mode 100644 neo-gui/Helpers/IDialogHelper.cs diff --git a/neo-gui/App.xaml.cs b/neo-gui/App.xaml.cs index 9f86ec0e..239722f3 100644 --- a/neo-gui/App.xaml.cs +++ b/neo-gui/App.xaml.cs @@ -1,6 +1,7 @@ using System.Windows; using Autofac; using Neo.Controllers; +using Neo.Helpers; using Neo.UI; using Neo.UI.Base; using Neo.UI.Base.Themes; @@ -39,6 +40,7 @@ private static void BuildContainer() autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); + autoFacContainerBuilder.RegisterModule(); var container = autoFacContainerBuilder.Build(); diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index b44b6efb..a50653d6 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -1,5 +1,9 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using Neo.DialogResults; +using Neo.Helpers; using Neo.Implementations.Wallets.EntityFramework; using Neo.Properties; using Neo.UI.Base.Messages; @@ -10,6 +14,7 @@ namespace Neo.Controllers public class WalletController : IWalletController { #region Private Fields + private readonly IDialogHelper dialogHelper; private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; @@ -21,9 +26,11 @@ public class WalletController : IWalletController #region Constructor public WalletController( + IDialogHelper dialogHelper, IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.dialogHelper = dialogHelper; this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; } @@ -42,7 +49,36 @@ public void CreateWallet(string walletPath, string password) public void OpenWallet(string walletPath, string password) { - throw new NotImplementedException(); + var openWalletDialogResult = this.dialogHelper.ShowDialog("OpenWalletDialog"); + + // [TODO] why this verification? Why the magic string? + if (UserWallet.GetVersion(walletPath) < Version.Parse("1.3.5")) + { + var migrationApproved = this.dialogHelper.ShowDialog("ApproveWalletMigrationDialog"); + + if (!migrationApproved.Result.Yes) + { + return; + } + + this.MigrateWallet(walletPath); + this.dialogHelper.ShowDialog("WalletMigrationCompleteDialog"); + } + + var userWallet = this.OpenWalletWithPath(walletPath, password); + if (userWallet == null) + { + return; + } + + if (openWalletDialogResult.Result.OpenInRepairMode) + { + userWallet.Rebuild(); + } + this.SetCurrentWallet(userWallet); + + Settings.Default.LastWalletPath = walletPath; + Settings.Default.Save(); } #endregion @@ -89,6 +125,30 @@ private void CurrentWalletBalanceChanged(object sender, EventArgs e) { this.balanceChanged = true; } + + private void MigrateWallet(string walletPath) + { + var pathOld = Path.ChangeExtension(walletPath, ".old.db3"); + var pathNew = Path.ChangeExtension(walletPath, ".new.db3"); + UserWallet.Migrate(walletPath, pathNew); + File.Move(walletPath, pathOld); + File.Move(pathNew, walletPath); + } + + private UserWallet OpenWalletWithPath(string walletPath, string password) + { + try + { + return UserWallet.Open(walletPath, password); + + } + catch (CryptographicException) + { + this.dialogHelper.ShowDialog("WalletPasswordIncorrectDialog", Strings.PasswordIncorrect); + } + + return null; + } #endregion } } diff --git a/neo-gui/DialogResults/OpenWalletDialogResult.cs b/neo-gui/DialogResults/OpenWalletDialogResult.cs new file mode 100644 index 00000000..dfda6313 --- /dev/null +++ b/neo-gui/DialogResults/OpenWalletDialogResult.cs @@ -0,0 +1,18 @@ +namespace Neo.DialogResults +{ + public class OpenWalletDialogResult + { + public string WalletPath { get; private set; } + + public string Password { get; private set; } + + public bool OpenInRepairMode { get; private set; } + + public OpenWalletDialogResult(string walletPath, string password, bool openInRepairMode) + { + this.WalletPath = walletPath; + this.Password = password; + this.OpenInRepairMode = OpenInRepairMode; + } + } +} diff --git a/neo-gui/DialogResults/YesOrNoDialogResult.cs b/neo-gui/DialogResults/YesOrNoDialogResult.cs new file mode 100644 index 00000000..08aa62b4 --- /dev/null +++ b/neo-gui/DialogResults/YesOrNoDialogResult.cs @@ -0,0 +1,12 @@ +namespace Neo.DialogResults +{ + public class YesOrNoDialogResult + { + public bool Yes { get; private set; } + + public YesOrNoDialogResult(bool yes) + { + this.Yes = yes; + } + } +} diff --git a/neo-gui/Helpers/DialogHelper.cs b/neo-gui/Helpers/DialogHelper.cs new file mode 100644 index 00000000..355bf525 --- /dev/null +++ b/neo-gui/Helpers/DialogHelper.cs @@ -0,0 +1,19 @@ +using System; + +namespace Neo.Helpers +{ + public class DialogHelper : IDialogHelper + { + #region IDialogHelper implementation + public void ShowDialog(string dialogName, params string[] parameters) + { + + } + + public DialogResult ShowDialog(string dialogName, params string[] parameters) + { + return default(DialogResult); + } + #endregion + } +} diff --git a/neo-gui/Helpers/DialogResult.cs b/neo-gui/Helpers/DialogResult.cs new file mode 100644 index 00000000..c6580fcf --- /dev/null +++ b/neo-gui/Helpers/DialogResult.cs @@ -0,0 +1,7 @@ +namespace Neo.Helpers +{ + public class DialogResult + { + public T Result { get; set; } + } +} diff --git a/neo-gui/Helpers/HelpersRegistrationModule.cs b/neo-gui/Helpers/HelpersRegistrationModule.cs new file mode 100644 index 00000000..a3368a70 --- /dev/null +++ b/neo-gui/Helpers/HelpersRegistrationModule.cs @@ -0,0 +1,16 @@ +using Autofac; + +namespace Neo.Helpers +{ + public class HelpersRegistrationModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterType() + .As(); + + base.Load(builder); + } + } +} diff --git a/neo-gui/Helpers/IDialogHelper.cs b/neo-gui/Helpers/IDialogHelper.cs new file mode 100644 index 00000000..48b2b923 --- /dev/null +++ b/neo-gui/Helpers/IDialogHelper.cs @@ -0,0 +1,12 @@ +namespace Neo.Helpers +{ + /// + /// Interface of the DialogHelper that abstracts the usage of Dialog windows in the application. + /// + public interface IDialogHelper + { + void ShowDialog(string dialogName, params string[] parameters); + + DialogResult ShowDialog(string dialogName, params string[] parameters); + } +} diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 7b006f25..e035c330 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -300,7 +300,13 @@ + + + + + + From 0f3f8897de9789ce98ba2d91cc1792079b3308f7 Mon Sep 17 00:00:00 2001 From: Justin Gaffney Date: Tue, 14 Nov 2017 19:34:47 +1000 Subject: [PATCH 08/22] Decoupled some views from their respective view model classes using messages --- .../UI/Accounts/CreateLockAccountView.xaml.cs | 13 +- .../UI/Accounts/CreateLockAccountViewModel.cs | 19 +- .../CreateMultiSigContractView.xaml.cs | 13 +- .../CreateMultiSigContractViewModel.cs | 20 +-- ...teView.xaml => ImportCertificateView.xaml} | 4 +- .../UI/Accounts/ImportCertificateView.xaml.cs | 10 ++ ...Model.cs => ImportCertificateViewModel.cs} | 22 ++- .../Accounts/ImportCustomContractView.xaml.cs | 13 +- .../Accounts/ImportCustomContractViewModel.cs | 25 +-- .../UI/Accounts/ImportPrivateKeyView.xaml.cs | 10 +- .../UI/Accounts/ImportPrivateKeyViewModel.cs | 12 ++ .../UI/Accounts/SelectCertificateView.xaml.cs | 18 -- .../UI/Assets/AssetDistributionView.xaml.cs | 5 - .../UI/Assets/AssetDistributionViewModel.cs | 20 ++- .../UI/Assets/AssetRegistrationView.xaml.cs | 9 +- .../UI/Assets/AssetRegistrationViewModel.cs | 21 ++- neo-gui/UI/Base/Helpers/TransactionHelper.cs | 46 ----- neo-gui/UI/Base/Helpers/WindowHelper.cs | 29 ---- .../UI/Contracts/DeployContractView.xaml.cs | 13 +- .../UI/Contracts/DeployContractViewModel.cs | 21 ++- .../UI/Contracts/InvokeContractView.xaml.cs | 11 +- .../UI/Contracts/InvokeContractViewModel.cs | 19 +- neo-gui/UI/Home/AccountsViewModel.cs | 164 ++++++++---------- neo-gui/UI/Home/AssetsViewModel.cs | 12 +- neo-gui/UI/Home/HomeViewModel.cs | 154 +++++++--------- neo-gui/UI/Messages/AddContractMessage.cs | 18 ++ ...ractsMessage.cs => AddContractsMessage.cs} | 6 +- .../UI/Messages/ImportCertificateMessage.cs | 14 ++ .../UI/Messages/ImportPrivateKeyMessage.cs | 14 ++ neo-gui/UI/Messages/InvokeContractMessage.cs | 14 ++ ...ignTransactionAndShowInformationMessage.cs | 14 ++ neo-gui/UI/Transactions/SigningView.xaml.cs | 2 +- neo-gui/UI/Voting/ElectionView.xaml.cs | 9 +- neo-gui/UI/Voting/ElectionViewModel.cs | 18 +- neo-gui/UI/Voting/VotingView.xaml.cs | 9 +- neo-gui/UI/Voting/VotingViewModel.cs | 22 ++- neo-gui/UI/Wallets/ClaimViewModel.cs | 17 +- .../UI/Wallets/RestoreAccountsView.xaml.cs | 14 +- .../UI/Wallets/RestoreAccountsViewModel.cs | 20 ++- neo-gui/UI/Wallets/TransferView.xaml.cs | 6 - neo-gui/UI/Wallets/TransferViewModel.cs | 31 +++- neo-gui/neo-gui.csproj | 19 +- 42 files changed, 447 insertions(+), 503 deletions(-) rename neo-gui/UI/Accounts/{SelectCertificateView.xaml => ImportCertificateView.xaml} (91%) create mode 100644 neo-gui/UI/Accounts/ImportCertificateView.xaml.cs rename neo-gui/UI/Accounts/{SelectCertificateViewModel.cs => ImportCertificateViewModel.cs} (65%) delete mode 100644 neo-gui/UI/Accounts/SelectCertificateView.xaml.cs delete mode 100644 neo-gui/UI/Base/Helpers/TransactionHelper.cs delete mode 100644 neo-gui/UI/Base/Helpers/WindowHelper.cs create mode 100644 neo-gui/UI/Messages/AddContractMessage.cs rename neo-gui/UI/Messages/{RestoreContractsMessage.cs => AddContractsMessage.cs} (69%) create mode 100644 neo-gui/UI/Messages/ImportCertificateMessage.cs create mode 100644 neo-gui/UI/Messages/ImportPrivateKeyMessage.cs create mode 100644 neo-gui/UI/Messages/InvokeContractMessage.cs create mode 100644 neo-gui/UI/Messages/SignTransactionAndShowInformationMessage.cs diff --git a/neo-gui/UI/Accounts/CreateLockAccountView.xaml.cs b/neo-gui/UI/Accounts/CreateLockAccountView.xaml.cs index 14ce6ee5..52355b53 100644 --- a/neo-gui/UI/Accounts/CreateLockAccountView.xaml.cs +++ b/neo-gui/UI/Accounts/CreateLockAccountView.xaml.cs @@ -1,21 +1,10 @@ -using Neo.Wallets; - -namespace Neo.UI.Accounts +namespace Neo.UI.Accounts { public partial class CreateLockAccountView { - private readonly CreateLockAccountViewModel viewModel; - public CreateLockAccountView() { InitializeComponent(); - - this.viewModel = this.DataContext as CreateLockAccountViewModel; - } - - public VerificationContract GetContract() - { - return this.viewModel?.GetContract(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs b/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs index a5c28082..49c38e0b 100644 --- a/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs +++ b/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs @@ -8,7 +8,9 @@ using Neo.Cryptography.ECC; using Neo.Properties; using Neo.SmartContract; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; using Neo.Wallets; @@ -18,16 +20,19 @@ public class CreateLockAccountViewModel : ViewModelBase { private const int HoursInDay = 24; private const int MinutesInHour = 60; + + private readonly IMessagePublisher messagePublisher; private ECPoint selectedAccount; private DateTime unlockDate; private int unlockHour; private int unlockMinute; - private VerificationContract contract; - - public CreateLockAccountViewModel() + public CreateLockAccountViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + this.Accounts = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey).ToArray()); this.Hours = new List(); @@ -120,18 +125,14 @@ public int UnlockMinute private void Create() { - this.contract = this.GenerateContract(); + var contract = this.GenerateContract(); if (contract == null) return; + this.messagePublisher.Publish(new AddContractMessage(contract)); this.TryClose(); } - public VerificationContract GetContract() - { - return this.contract; - } - private VerificationContract GenerateContract() { if (this.SelectedAccount == null) return null; diff --git a/neo-gui/UI/Accounts/CreateMultiSigContractView.xaml.cs b/neo-gui/UI/Accounts/CreateMultiSigContractView.xaml.cs index 77a35ea8..5505d020 100644 --- a/neo-gui/UI/Accounts/CreateMultiSigContractView.xaml.cs +++ b/neo-gui/UI/Accounts/CreateMultiSigContractView.xaml.cs @@ -1,21 +1,10 @@ -using Neo.Wallets; - -namespace Neo.UI.Accounts +namespace Neo.UI.Accounts { public partial class CreateMultiSigContractView { - private readonly CreateMultiSigContractViewModel viewModel; - public CreateMultiSigContractView() { InitializeComponent(); - - this.viewModel = this.DataContext as CreateMultiSigContractViewModel; - } - - public VerificationContract GetContract() - { - return this.viewModel?.GetContract(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs b/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs index 3a3bd674..e67de9c3 100644 --- a/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs +++ b/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs @@ -7,13 +7,16 @@ using Neo.Cryptography.ECC; using Neo.Properties; using Neo.UI.Base.Dispatching; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.Wallets; namespace Neo.UI.Accounts { public class CreateMultiSigContractViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; private int minimumSignatureNumber; @@ -24,10 +27,11 @@ public class CreateMultiSigContractViewModel : ViewModelBase private string newPublicKey; - private VerificationContract contract; - - public CreateMultiSigContractViewModel(IDispatcher dispatcher) + public CreateMultiSigContractViewModel( + IMessagePublisher messagePublisher, + IDispatcher dispatcher) { + this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; this.PublicKeys = new ObservableCollection(); @@ -114,14 +118,15 @@ public string NewPublicKey private void Confirm() { - this.contract = this.GenerateContract(); + var contract = this.GenerateContract(); - if (this.contract == null) + if (contract == null) { MessageBox.Show(Strings.AddContractFailedMessage); return; } + this.messagePublisher.Publish(new AddContractMessage(contract)); this.TryClose(); } @@ -153,11 +158,6 @@ await this.dispatcher.InvokeOnMainUIThread(() => }); } - public VerificationContract GetContract() - { - return this.contract; - } - private VerificationContract GenerateContract() { var publicKeys = this.PublicKeys.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); diff --git a/neo-gui/UI/Accounts/SelectCertificateView.xaml b/neo-gui/UI/Accounts/ImportCertificateView.xaml similarity index 91% rename from neo-gui/UI/Accounts/SelectCertificateView.xaml rename to neo-gui/UI/Accounts/ImportCertificateView.xaml index 8a8727c4..f7cf6a1c 100644 --- a/neo-gui/UI/Accounts/SelectCertificateView.xaml +++ b/neo-gui/UI/Accounts/ImportCertificateView.xaml @@ -1,11 +1,11 @@ - diff --git a/neo-gui/UI/Accounts/ImportCertificateView.xaml.cs b/neo-gui/UI/Accounts/ImportCertificateView.xaml.cs new file mode 100644 index 00000000..4d836d10 --- /dev/null +++ b/neo-gui/UI/Accounts/ImportCertificateView.xaml.cs @@ -0,0 +1,10 @@ +namespace Neo.UI.Accounts +{ + public partial class ImportCertificateView + { + public ImportCertificateView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/neo-gui/UI/Accounts/SelectCertificateViewModel.cs b/neo-gui/UI/Accounts/ImportCertificateViewModel.cs similarity index 65% rename from neo-gui/UI/Accounts/SelectCertificateViewModel.cs rename to neo-gui/UI/Accounts/ImportCertificateViewModel.cs index 6a563f8c..39b4be89 100644 --- a/neo-gui/UI/Accounts/SelectCertificateViewModel.cs +++ b/neo-gui/UI/Accounts/ImportCertificateViewModel.cs @@ -2,16 +2,23 @@ using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Windows.Input; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; namespace Neo.UI.Accounts { - public class SelectCertificateViewModel : ViewModelBase + public class ImportCertificateViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private X509Certificate2 selectedCertificate; - public SelectCertificateViewModel() + public ImportCertificateViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + // Load certificates using (var store = new X509Store()) { @@ -42,6 +49,15 @@ public X509Certificate2 SelectedCertificate public bool OkEnabled => this.SelectedCertificate != null; - public ICommand OkCommand => new RelayCommand(this.TryClose); + public ICommand OkCommand => new RelayCommand(this.Ok); + + + private void Ok() + { + if (this.SelectedCertificate == null) return; + + this.messagePublisher.Publish(new ImportCertificateMessage(this.SelectedCertificate)); + this.TryClose(); + } } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/ImportCustomContractView.xaml.cs b/neo-gui/UI/Accounts/ImportCustomContractView.xaml.cs index 5f74af64..27d7f687 100644 --- a/neo-gui/UI/Accounts/ImportCustomContractView.xaml.cs +++ b/neo-gui/UI/Accounts/ImportCustomContractView.xaml.cs @@ -1,21 +1,10 @@ -using Neo.Wallets; - -namespace Neo.UI.Accounts +namespace Neo.UI.Accounts { public partial class ImportCustomContractView { - private readonly ImportCustomContractViewModel viewModel; - public ImportCustomContractView() { InitializeComponent(); - - this.viewModel = this.DataContext as ImportCustomContractViewModel; - } - - public VerificationContract GetContract() - { - return this.viewModel?.GetContract(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs b/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs index 0dd85aba..8c6bf10c 100644 --- a/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs +++ b/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs @@ -4,21 +4,26 @@ using Neo.Core; using Neo.Cryptography.ECC; using Neo.SmartContract; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.Wallets; namespace Neo.UI.Accounts { public class ImportCustomContractViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private ECPoint selectedRelatedAccount; private string parameterList; private string script; - private VerificationContract contract; - - public ImportCustomContractViewModel() + public ImportCustomContractViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + this.RelatedAccounts = new ObservableCollection( ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); @@ -85,14 +90,12 @@ public string Script private void Confirm() { - this.contract = this.GenerateContract(); + var contract = this.GenerateContract(); - this.TryClose(); - } + if (contract == null) return; - public VerificationContract GetContract() - { - return this.contract; + this.messagePublisher.Publish(new AddContractMessage(contract)); + this.TryClose(); } private VerificationContract GenerateContract() @@ -100,10 +103,10 @@ private VerificationContract GenerateContract() if (!this.ConfirmEnabled) return null; var publicKeyHash = this.SelectedRelatedAccount.EncodePoint(true).ToScriptHash(); - var parameterList = this.ParameterList.HexToBytes().Select(p => (ContractParameterType)p).ToArray(); + var parameters = this.ParameterList.HexToBytes().Select(p => (ContractParameterType)p).ToArray(); var redeemScript = this.Script.HexToBytes(); - return VerificationContract.Create(publicKeyHash, parameterList, redeemScript); + return VerificationContract.Create(publicKeyHash, parameters, redeemScript); } } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/ImportPrivateKeyView.xaml.cs b/neo-gui/UI/Accounts/ImportPrivateKeyView.xaml.cs index d12f44d7..b541742b 100644 --- a/neo-gui/UI/Accounts/ImportPrivateKeyView.xaml.cs +++ b/neo-gui/UI/Accounts/ImportPrivateKeyView.xaml.cs @@ -1,18 +1,10 @@ -using System.Collections.Generic; - -namespace Neo.UI.Accounts +namespace Neo.UI.Accounts { public partial class ImportPrivateKeyView { - private readonly ImportPrivateKeyViewModel viewModel; - public ImportPrivateKeyView() { InitializeComponent(); - - this.viewModel = this.DataContext as ImportPrivateKeyViewModel; } - - public IEnumerable WifStrings => this.viewModel?.WifStrings; } } \ No newline at end of file diff --git a/neo-gui/UI/Accounts/ImportPrivateKeyViewModel.cs b/neo-gui/UI/Accounts/ImportPrivateKeyViewModel.cs index e4498e46..bef7a3cb 100644 --- a/neo-gui/UI/Accounts/ImportPrivateKeyViewModel.cs +++ b/neo-gui/UI/Accounts/ImportPrivateKeyViewModel.cs @@ -1,12 +1,23 @@ using System.Collections.Generic; +using System.Linq; using System.Windows.Input; using Neo.UI.Base.Extensions; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; namespace Neo.UI.Accounts { public class ImportPrivateKeyViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + + public ImportPrivateKeyViewModel( + IMessagePublisher messagePublisher) + { + this.messagePublisher = messagePublisher; + } + private string privateKeyWif; public string PrivateKeyWif @@ -44,6 +55,7 @@ private void Ok() { if (!this.OkEnabled) return; + this.messagePublisher.Publish(new ImportPrivateKeyMessage(this.WifStrings.ToList())); this.TryClose(); } } diff --git a/neo-gui/UI/Accounts/SelectCertificateView.xaml.cs b/neo-gui/UI/Accounts/SelectCertificateView.xaml.cs deleted file mode 100644 index 64659850..00000000 --- a/neo-gui/UI/Accounts/SelectCertificateView.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Security.Cryptography.X509Certificates; - -namespace Neo.UI.Accounts -{ - public partial class SelectCertificateView - { - private readonly SelectCertificateViewModel viewModel; - - public SelectCertificateView() - { - InitializeComponent(); - - this.viewModel = this.DataContext as SelectCertificateViewModel; - } - - public X509Certificate2 SelectedCertificate => this.viewModel?.SelectedCertificate; - } -} \ No newline at end of file diff --git a/neo-gui/UI/Assets/AssetDistributionView.xaml.cs b/neo-gui/UI/Assets/AssetDistributionView.xaml.cs index e55bec83..dc826ef9 100644 --- a/neo-gui/UI/Assets/AssetDistributionView.xaml.cs +++ b/neo-gui/UI/Assets/AssetDistributionView.xaml.cs @@ -16,11 +16,6 @@ public AssetDistributionView(AssetState asset = null) this.viewModel?.SetAsset(asset); } - public IssueTransaction GetTransaction() - { - return this.viewModel?.GetTransaction(); - } - private void TxOutListBox_OnItemsChanged(object sender, EventArgs e) { this.viewModel?.UpdateConfirmButtonEnabled(); diff --git a/neo-gui/UI/Assets/AssetDistributionViewModel.cs b/neo-gui/UI/Assets/AssetDistributionViewModel.cs index 559afa34..6f0765f1 100644 --- a/neo-gui/UI/Assets/AssetDistributionViewModel.cs +++ b/neo-gui/UI/Assets/AssetDistributionViewModel.cs @@ -3,13 +3,16 @@ using System.Windows.Input; using Neo.Core; using Neo.UI.Base.Dispatching; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.Wallets; namespace Neo.UI.Assets { public class AssetDistributionViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; private AssetDescriptor asset; @@ -25,10 +28,11 @@ public class AssetDistributionViewModel : ViewModelBase private bool distributionEnabled; - private IssueTransaction transaction; - - public AssetDistributionViewModel(IDispatcher dispatcher) + public AssetDistributionViewModel( + IMessagePublisher messagePublisher, + IDispatcher dispatcher) { + this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; this.Items = new ObservableCollection(); @@ -152,8 +156,11 @@ public bool DistributionEnabled private void Confirm() { - this.transaction = this.GenerateTransaction(); + var transaction = this.GenerateTransaction(); + + if (transaction == null) return; + this.messagePublisher.Publish(new SignTransactionAndShowInformationMessage(transaction)); this.TryClose(); } @@ -204,11 +211,6 @@ private void UpdateAssetDetails() this.dispatcher.InvokeOnMainUIThread(() => this.Items.Clear()); } - internal IssueTransaction GetTransaction() - { - return this.transaction; - } - private IssueTransaction GenerateTransaction() { if (this.Asset == null) return null; diff --git a/neo-gui/UI/Assets/AssetRegistrationView.xaml.cs b/neo-gui/UI/Assets/AssetRegistrationView.xaml.cs index 97e153ef..b72783e3 100644 --- a/neo-gui/UI/Assets/AssetRegistrationView.xaml.cs +++ b/neo-gui/UI/Assets/AssetRegistrationView.xaml.cs @@ -1,6 +1,4 @@ -using Neo.Core; - -namespace Neo.UI.Assets +namespace Neo.UI.Assets { public partial class AssetRegistrationView { @@ -8,10 +6,5 @@ public AssetRegistrationView() { InitializeComponent(); } - - public InvocationTransaction GetTransaction() - { - return (this.DataContext as AssetRegistrationViewModel)?.GetTransaction(); - } } } \ No newline at end of file diff --git a/neo-gui/UI/Assets/AssetRegistrationViewModel.cs b/neo-gui/UI/Assets/AssetRegistrationViewModel.cs index 0cfadcec..2149e123 100644 --- a/neo-gui/UI/Assets/AssetRegistrationViewModel.cs +++ b/neo-gui/UI/Assets/AssetRegistrationViewModel.cs @@ -6,7 +6,9 @@ using Neo.Core; using Neo.Cryptography.ECC; using Neo.SmartContract; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; using Neo.Wallets; @@ -16,6 +18,8 @@ public class AssetRegistrationViewModel : ViewModelBase { private static readonly AssetType[] assetTypes = { AssetType.Share, AssetType.Token }; + private readonly IMessagePublisher messagePublisher; + private AssetType? selectedAssetType; private ECPoint selectedOwner; private string selectedAdmin; @@ -30,10 +34,11 @@ public class AssetRegistrationViewModel : ViewModelBase private bool formValid; - private InvocationTransaction transaction; - - public AssetRegistrationViewModel() + public AssetRegistrationViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + this.AssetTypes = new ObservableCollection(assetTypes); this.Owners = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); this.Admins = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Select(p => p.Address)); @@ -199,11 +204,6 @@ public int Precision public ICommand OkCommand => new RelayCommand(this.Ok); - public InvocationTransaction GetTransaction() - { - return this.transaction; - } - private InvocationTransaction GenerateTransaction() { var assetType = this.SelectedAssetType; @@ -256,8 +256,11 @@ private void Ok() if (!this.OkEnabled) return; - this.transaction = this.GenerateTransaction(); + var transaction = this.GenerateTransaction(); + + if (transaction == null) return; + this.messagePublisher.Publish(new InvokeContractMessage(transaction)); this.TryClose(); } } diff --git a/neo-gui/UI/Base/Helpers/TransactionHelper.cs b/neo-gui/UI/Base/Helpers/TransactionHelper.cs deleted file mode 100644 index 59ff40ed..00000000 --- a/neo-gui/UI/Base/Helpers/TransactionHelper.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Windows; -using Neo.Core; -using Neo.Properties; -using Neo.SmartContract; -using Neo.UI.Base.Dialogs; - -namespace Neo.UI.Base.Helpers -{ - public static class TransactionHelper - { - public static void SignAndShowInformation(Transaction tx) - { - if (tx == null) - { - MessageBox.Show(Strings.InsufficientFunds); - return; - } - - ContractParametersContext context; - try - { - context = new ContractParametersContext(tx); - } - catch (InvalidOperationException) - { - MessageBox.Show(Strings.UnsynchronizedBlock); - return; - } - - ApplicationContext.Instance.CurrentWallet.Sign(context); - - if (context.Completed) - { - context.Verifiable.Scripts = context.GetScripts(); - ApplicationContext.Instance.CurrentWallet.SaveTransaction(tx); - Program.LocalNode.Relay(tx); - InformationBox.Show(tx.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); - } - else - { - InformationBox.Show(context.ToString(), Strings.IncompletedSignatureMessage, Strings.IncompletedSignatureTitle); - } - } - } -} \ No newline at end of file diff --git a/neo-gui/UI/Base/Helpers/WindowHelper.cs b/neo-gui/UI/Base/Helpers/WindowHelper.cs deleted file mode 100644 index 3c36285c..00000000 --- a/neo-gui/UI/Base/Helpers/WindowHelper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Neo.UI.Base.Controls; - -namespace Neo.UI.Base.Helpers -{ - public static class WindowHelper - { - private static readonly Dictionary Windows = new Dictionary(); - - private static void Helper_WindowClosing(object sender, CancelEventArgs e) - { - Windows.Remove(sender.GetType()); - } - - public static void Show() where T : NeoWindow, new() - { - var type = typeof(T); - if (!Windows.ContainsKey(type)) - { - Windows.Add(type, new T()); - Windows[type].Closing += Helper_WindowClosing; - } - Windows[type].Show(); - Windows[type].Activate(); - } - } -} \ No newline at end of file diff --git a/neo-gui/UI/Contracts/DeployContractView.xaml.cs b/neo-gui/UI/Contracts/DeployContractView.xaml.cs index b7aef5d5..18eb5020 100644 --- a/neo-gui/UI/Contracts/DeployContractView.xaml.cs +++ b/neo-gui/UI/Contracts/DeployContractView.xaml.cs @@ -1,24 +1,13 @@ -using Neo.Core; - -namespace Neo.UI.Contracts +namespace Neo.UI.Contracts { /// /// Interaction logic for DeployContractView.xaml /// public partial class DeployContractView { - private readonly DeployContractViewModel viewModel; - public DeployContractView() { InitializeComponent(); - - this.viewModel = this.DataContext as DeployContractViewModel; - } - - public InvocationTransaction GetTransaction() - { - return this.viewModel?.GetTransaction(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Contracts/DeployContractViewModel.cs b/neo-gui/UI/Contracts/DeployContractViewModel.cs index 11b8b40b..d9914fcf 100644 --- a/neo-gui/UI/Contracts/DeployContractViewModel.cs +++ b/neo-gui/UI/Contracts/DeployContractViewModel.cs @@ -5,13 +5,17 @@ using System.Windows.Input; using Neo.Core; using Neo.SmartContract; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; namespace Neo.UI.Contracts { public class DeployContractViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private string name; private string version; private string author; @@ -23,7 +27,12 @@ public class DeployContractViewModel : ViewModelBase private string code; private bool needsStorage; - private InvocationTransaction transaction; + public DeployContractViewModel( + IMessagePublisher messagePublisher) + { + this.messagePublisher = messagePublisher; + } + public string Name { @@ -229,14 +238,12 @@ private void Load() private void Deploy() { - this.transaction = this.GenerateTransaction(); + var transaction = this.GenerateTransaction(); - this.TryClose(); - } + if (transaction == null) return; - public InvocationTransaction GetTransaction() - { - return this.transaction; + this.messagePublisher.Publish(new InvokeContractMessage(transaction)); + this.TryClose(); } private InvocationTransaction GenerateTransaction() diff --git a/neo-gui/UI/Contracts/InvokeContractView.xaml.cs b/neo-gui/UI/Contracts/InvokeContractView.xaml.cs index 966b06d8..16a27b6d 100644 --- a/neo-gui/UI/Contracts/InvokeContractView.xaml.cs +++ b/neo-gui/UI/Contracts/InvokeContractView.xaml.cs @@ -8,24 +8,15 @@ namespace Neo.UI.Contracts /// public partial class InvokeContractView { - private readonly InvokeContractViewModel viewModel; - - public InvokeContractView(InvocationTransaction baseTransaction = null) + public InvokeContractView(InvocationTransaction baseTransaction) { InitializeComponent(); - this.viewModel = this.DataContext as InvokeContractViewModel; - this.BaseTransaction = baseTransaction; } public InvocationTransaction BaseTransaction { get; } - public InvocationTransaction GetTransaction() - { - return this.viewModel?.GetTransaction(); - } - public void SetSelectedTab(int tabIndex) { if (tabIndex < 0 || tabIndex >= this.TabControl.Items.Count) throw new IndexOutOfRangeException(); diff --git a/neo-gui/UI/Contracts/InvokeContractViewModel.cs b/neo-gui/UI/Contracts/InvokeContractViewModel.cs index 7b2a2957..5986c518 100644 --- a/neo-gui/UI/Contracts/InvokeContractViewModel.cs +++ b/neo-gui/UI/Contracts/InvokeContractViewModel.cs @@ -9,7 +9,9 @@ using Neo.Properties; using Neo.SmartContract; using Neo.UI.Base.Controls; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; namespace Neo.UI.Contracts @@ -18,6 +20,8 @@ public class InvokeContractViewModel : ViewModelBase { private static readonly Fixed8 NetworkFee = Fixed8.FromDecimal(0.001m); + private readonly IMessagePublisher messagePublisher; + private InvocationTransaction transaction; private UInt160 scriptHash; @@ -35,8 +39,11 @@ public class InvokeContractViewModel : ViewModelBase private bool invokeEnabled; - - + public InvokeContractViewModel( + IMessagePublisher messagePublisher) + { + this.messagePublisher = messagePublisher; + } #region Public Properties @@ -308,7 +315,13 @@ private void Test() private void Invoke() { - // Close window so parent object can get the transaction + if (!this.InvokeEnabled) return; + + var tx = this.GetTransaction(); + + if (tx == null) return; + + this.messagePublisher.Publish(new SignTransactionAndShowInformationMessage(tx)); this.TryClose(); } diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index c95c5555..c58682a6 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -29,7 +29,10 @@ public class AccountsViewModel : IMessageHandler, IMessageHandler, IMessageHandler, - IMessageHandler + IMessageHandler, + IMessageHandler, + IMessageHandler, + IMessageHandler { private readonly IMessageSubscriber messageSubscriber; private readonly IMessagePublisher messagePublisher; @@ -189,61 +192,12 @@ private void ImportWifPrivateKey() { var view = new ImportPrivateKeyView(); view.ShowDialog(); - - var wifStrings = view.WifStrings; - - if (wifStrings == null) return; - - var wifStringList = wifStrings.ToList(); - - if (!wifStringList.Any()) return; - - // Import private keys - this.SelectedAccount = null; - - foreach (var wif in wifStringList) - { - KeyPair key; - try - { - key = ApplicationContext.Instance.CurrentWallet.Import(wif); - } - catch (FormatException) - { - // Skip WIF line - continue; - } - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } - } } - private async void ImportCertificate() + private void ImportCertificate() { - var view = new SelectCertificateView(); + var view = new ImportCertificateView(); view.ShowDialog(); - - if (view.SelectedCertificate == null) return; - - this.SelectedAccount = null; - - KeyPair key; - try - { - key = ApplicationContext.Instance.CurrentWallet.Import(view.SelectedCertificate); - } - catch - { - await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, "Certificate import failed!"); - return; - } - - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } } private void ImportWatchOnlyAddress() @@ -279,42 +233,18 @@ private void CreateMultiSignatureContract() { var view = new CreateMultiSigContractView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - ApplicationContext.Instance.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void CreateLockAddress() { var view = new CreateLockAccountView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - ApplicationContext.Instance.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void ImportCustomContract() { var view = new ImportCustomContractView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - ApplicationContext.Instance.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void ViewPrivateKey() @@ -342,23 +272,8 @@ private void ShowVotingDialog() { if (this.SelectedAccount?.Contract == null) return; - var contract = this.SelectedAccount.Contract; - - var view = new VotingView(contract.ScriptHash); + var view = new VotingView(this.SelectedAccount.Contract.ScriptHash); view.ShowDialog(); - - var transaction = view.GetTransaction(); - - if (transaction == null) return; - - var invokeContractView = new InvokeContractView(transaction); - invokeContractView.ShowDialog(); - - transaction = invokeContractView.GetTransaction(); - - if (transaction == null) return; - - TransactionHelper.SignAndShowInformation(transaction); } private void CopyAddressToClipboard() @@ -443,19 +358,82 @@ public void HandleMessage(LoadWalletAddressesMessage message) } } - public void HandleMessage(RestoreContractsMessage message) + public void HandleMessage(AddContractsMessage message) { if (message.Contracts == null || !message.Contracts.Any()) { return; } + this.SelectedAccount = null; + foreach (var contract in message.Contracts) { ApplicationContext.Instance.CurrentWallet.AddContract(contract); this.AddContract(contract, true); } } + + public void HandleMessage(AddContractMessage message) + { + if (message.Contract == null) return; + + this.SelectedAccount = null; + + ApplicationContext.Instance.CurrentWallet.AddContract(message.Contract); + this.AddContract(message.Contract, true); + } + + public void HandleMessage(ImportPrivateKeyMessage message) + { + if (message.WifStrings == null) return; + + if (!message.WifStrings.Any()) return; + + // Import private keys + this.SelectedAccount = null; + + foreach (var wif in message.WifStrings) + { + KeyPair key; + try + { + key = ApplicationContext.Instance.CurrentWallet.Import(wif); + } + catch (FormatException) + { + // Skip WIF line + continue; + } + foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + { + this.AddContract(contract, true); + } + } + } + + public async void HandleMessage(ImportCertificateMessage message) + { + if (message.SelectedCertificate == null) return; + + this.SelectedAccount = null; + + KeyPair key; + try + { + key = ApplicationContext.Instance.CurrentWallet.Import(message.SelectedCertificate); + } + catch + { + await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, "Certificate import failed!"); + return; + } + + foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + { + AddContract(contract, true); + } + } #endregion #region ILoadable implementation diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index d81b908c..c63858bf 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -9,8 +9,9 @@ using Neo.Properties; using Neo.SmartContract; using Neo.UI.Base.Collections; -using Neo.UI.Base.Helpers; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; using Neo.Wallets; @@ -20,12 +21,17 @@ public class AssetsViewModel : ViewModelBase { private static readonly UInt160 RecycleScriptHash = new[] { (byte)OpCode.PUSHT }.ToScriptHash(); + private readonly IMessagePublisher messagePublisher; + private readonly Dictionary certificateQueryResultCache; private AssetItem selectedAsset; - public AssetsViewModel() + public AssetsViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + this.certificateQueryResultCache = new Dictionary(); this.Assets = new ConcurrentObservableCollection(); @@ -119,7 +125,7 @@ private void DeleteAsset() } }, fee: Fixed8.Zero); - TransactionHelper.SignAndShowInformation(transaction); + this.messagePublisher.Publish(new SignTransactionAndShowInformationMessage(transaction)); } #endregion Menu Command Methods diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 6b6c1a99..67a0c4be 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -22,6 +22,7 @@ using Neo.SmartContract; using Neo.VM; using Neo.UI.Assets; +using Neo.UI.Base.Dialogs; using Neo.UI.Base.Dispatching; using Neo.UI.Base.Helpers; using Neo.UI.Base.MVVM; @@ -42,10 +43,11 @@ public class HomeViewModel : ViewModelBase, ILoadable, IMessageHandler, - IMessageHandler + IMessageHandler, + IMessageHandler { - private readonly IMessagePublisher _messagePublisher; - private readonly IMessageSubscriber _messageSubscriber; + private readonly IMessagePublisher messagePublisher; + private readonly IMessageSubscriber messageSubscriber; private readonly IDispatcher dispatcher; private bool balanceChanged = false; @@ -71,8 +73,8 @@ public HomeViewModel( AssetsViewModel assetsViewModel, TransactionsViewModel transactionsViewModel) { - this._messagePublisher = messagePublisher; - this._messageSubscriber = messageSubscriber; + this.messagePublisher = messagePublisher; + this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; this.AssetsViewModel = assetsViewModel; @@ -131,7 +133,7 @@ public int BlockProgress public ICommand RebuildIndexCommand => new RelayCommand(this.RebuildIndex); - public ICommand RestoreAccountsCommand => new RelayCommand(this.RestoreAccounts); + public ICommand RestoreAccountsCommand => new RelayCommand(RestoreAccounts); public ICommand ExitCommand => new RelayCommand(this.Exit); @@ -233,7 +235,7 @@ private void ChangeWallet(UserWallet wallet) this.dispatcher.InvokeOnMainUIThread(() => { - this._messagePublisher.Publish(new ClearAccountsMessage()); + this.messagePublisher.Publish(new ClearAccountsMessage()); this.AssetsViewModel.Assets.Clear(); this.TransactionsViewModel.Transactions.Clear(); @@ -251,10 +253,10 @@ private void ChangeWallet(UserWallet wallet) ApplicationContext.Instance.CurrentWallet.TransactionsChanged += CurrentWallet_TransactionsChanged; } - this._messagePublisher.Publish(new EnableMenuItemsMessage()); + this.messagePublisher.Publish(new EnableMenuItemsMessage()); NotifyPropertyChanged(nameof(this.WalletIsOpen)); - this._messagePublisher.Publish(new LoadWalletAddressesMessage()); + this.messagePublisher.Publish(new LoadWalletAddressesMessage()); balanceChanged = true; checkNep5Balance = true; @@ -467,7 +469,7 @@ private void UpdateAssetBalances() }; } - this._messagePublisher.Publish(new AccountBalancesChangedMessage()); + this.messagePublisher.Publish(new AccountBalancesChangedMessage()); foreach (var asset in assetList.Where(item => item.State != null)) { @@ -727,14 +729,10 @@ await this.dispatcher.InvokeOnMainUIThread(() => ApplicationContext.Instance.CurrentWallet.Rebuild(); } - private void RestoreAccounts() + private static void RestoreAccounts() { var view = new RestoreAccountsView(); view.ShowDialog(); - - var contracts = view.GetContracts(); - - this._messagePublisher.Publish(new RestoreContractsMessage(contracts)); } private void Exit() @@ -746,22 +744,6 @@ private static void Transfer() { var view = new TransferView(); view.ShowDialog(); - - var transaction = view.GetTransaction(); - - if (transaction == null) return; - - if (transaction is InvocationTransaction itx) - { - var invokeContractView = new InvokeContractView(itx); - invokeContractView.ShowDialog(); - - transaction = invokeContractView.GetTransaction(); - - if (transaction == null) return; - } - - TransactionHelper.SignAndShowInformation(transaction); } private static void ShowTransactionDialog() @@ -778,7 +760,8 @@ private static void ShowSigningDialog() private static void Claim() { - WindowHelper.Show(); + var view = new ClaimView(); + view.Show(); } private static void RequestCertificate() @@ -791,78 +774,24 @@ private static void AssetRegistration() { var assetRegistrationView = new AssetRegistrationView(); assetRegistrationView.ShowDialog(); - - var transactionResult = assetRegistrationView.GetTransaction(); - - if (transactionResult == null) return; - - - var invokeContractView = new InvokeContractView(transactionResult); - invokeContractView.ShowDialog(); - - transactionResult = invokeContractView.GetTransaction(); - - if (transactionResult == null) return; - - TransactionHelper.SignAndShowInformation(transactionResult); } private static void DistributeAsset() { var view = new AssetDistributionView(); view.ShowDialog(); - - var transaction = view.GetTransaction(); - - if (transaction == null) return; - - TransactionHelper.SignAndShowInformation(transaction); } private static void DeployContract() { var view = new DeployContractView(); view.ShowDialog(); - - var transactionResult = view.GetTransaction(); - - if (transactionResult == null) return; - - - var invokeContractView = new InvokeContractView(transactionResult); - invokeContractView.ShowDialog(); - - transactionResult = invokeContractView.GetTransaction(); - - if (transactionResult == null) return; - - - TransactionHelper.SignAndShowInformation(transactionResult); - } - - private static void InvokeContract() - { - var view = new InvokeContractView(); - view.ShowDialog(); } private static void ShowElectionDialog() { var electionView = new ElectionView(); electionView.ShowDialog(); - - var transactionResult = electionView.GetTransactionResult(); - - if (transactionResult == null) return; - - var invokeContractView = new InvokeContractView(transactionResult); - invokeContractView.ShowDialog(); - - transactionResult = invokeContractView.GetTransaction(); - - if (transactionResult == null) return; - - TransactionHelper.SignAndShowInformation(transactionResult); } private static void ShowSettings() @@ -883,7 +812,8 @@ private static void ShowOfficialWebsite() private static void ShowDeveloperTools() { - WindowHelper.Show(); + var view = new DeveloperToolsView(); + view.Show(); } private void ShowAboutNeoDialog() @@ -896,7 +826,6 @@ private void ShowAboutNeoDialog() private static void ShowUpdateDialog() { var dialog = new UpdateView(); - dialog.ShowDialog(); } @@ -916,11 +845,16 @@ private static uint GetWalletHeight() public void OnLoad() { - this._messageSubscriber.Subscribe(this); + this.messageSubscriber.Subscribe(this); this.Load(); } + private void InvokeContract() + { + this.messagePublisher.Publish(new InvokeContractMessage(null)); + } + #region IMessageHandler implementation public void HandleMessage(UpdateApplicationMessage message) { @@ -935,6 +869,48 @@ public void HandleMessage(WalletBalanceChangedMessage message) { this.balanceChanged = true; } + + public void HandleMessage(InvokeContractMessage message) + { + var invokeContractView = new InvokeContractView(message.Transaction); + invokeContractView.ShowDialog(); + } + // TODO Move these message handlers to a more appropriate place, they don't need to be in HomeViewModel + public void HandleMessage(SignTransactionAndShowInformationMessage message) + { + var transaction = message.Transaction; + + if (transaction == null) + { + MessageBox.Show(Strings.InsufficientFunds); + return; + } + + ContractParametersContext context; + try + { + context = new ContractParametersContext(transaction); + } + catch (InvalidOperationException) + { + MessageBox.Show(Strings.UnsynchronizedBlock); + return; + } + + ApplicationContext.Instance.CurrentWallet.Sign(context); + + if (context.Completed) + { + context.Verifiable.Scripts = context.GetScripts(); + ApplicationContext.Instance.CurrentWallet.SaveTransaction(transaction); + Program.LocalNode.Relay(transaction); + InformationBox.Show(transaction.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); + } + else + { + InformationBox.Show(context.ToString(), Strings.IncompletedSignatureMessage, Strings.IncompletedSignatureTitle); + } + } #endregion } } \ No newline at end of file diff --git a/neo-gui/UI/Messages/AddContractMessage.cs b/neo-gui/UI/Messages/AddContractMessage.cs new file mode 100644 index 00000000..7d17efd1 --- /dev/null +++ b/neo-gui/UI/Messages/AddContractMessage.cs @@ -0,0 +1,18 @@ +using Neo.Wallets; + +namespace Neo.UI.Messages +{ + public class AddContractMessage + { + #region Public Properties + public VerificationContract Contract { get; } + #endregion + + #region Constructor + public AddContractMessage(VerificationContract contract) + { + this.Contract = contract; + } + #endregion + } +} \ No newline at end of file diff --git a/neo-gui/UI/Messages/RestoreContractsMessage.cs b/neo-gui/UI/Messages/AddContractsMessage.cs similarity index 69% rename from neo-gui/UI/Messages/RestoreContractsMessage.cs rename to neo-gui/UI/Messages/AddContractsMessage.cs index 13ab9d99..4256f938 100644 --- a/neo-gui/UI/Messages/RestoreContractsMessage.cs +++ b/neo-gui/UI/Messages/AddContractsMessage.cs @@ -3,14 +3,14 @@ namespace Neo.UI.Messages { - public class RestoreContractsMessage + public class AddContractsMessage { #region Public Properties - public IEnumerable Contracts { get; private set; } + public IEnumerable Contracts { get; } #endregion #region Constructor - public RestoreContractsMessage(IEnumerable contracts) + public AddContractsMessage(IEnumerable contracts) { this.Contracts = contracts; } diff --git a/neo-gui/UI/Messages/ImportCertificateMessage.cs b/neo-gui/UI/Messages/ImportCertificateMessage.cs new file mode 100644 index 00000000..8662bf2c --- /dev/null +++ b/neo-gui/UI/Messages/ImportCertificateMessage.cs @@ -0,0 +1,14 @@ +using System.Security.Cryptography.X509Certificates; + +namespace Neo.UI.Messages +{ + public class ImportCertificateMessage + { + public ImportCertificateMessage(X509Certificate2 selectedCertificate) + { + this.SelectedCertificate = selectedCertificate; + } + + public X509Certificate2 SelectedCertificate { get; } + } +} \ No newline at end of file diff --git a/neo-gui/UI/Messages/ImportPrivateKeyMessage.cs b/neo-gui/UI/Messages/ImportPrivateKeyMessage.cs new file mode 100644 index 00000000..007dc8ac --- /dev/null +++ b/neo-gui/UI/Messages/ImportPrivateKeyMessage.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Neo.UI.Messages +{ + public class ImportPrivateKeyMessage + { + public List WifStrings { get; } + + public ImportPrivateKeyMessage(List wifStrings) + { + this.WifStrings = wifStrings; + } + } +} \ No newline at end of file diff --git a/neo-gui/UI/Messages/InvokeContractMessage.cs b/neo-gui/UI/Messages/InvokeContractMessage.cs new file mode 100644 index 00000000..01d1affd --- /dev/null +++ b/neo-gui/UI/Messages/InvokeContractMessage.cs @@ -0,0 +1,14 @@ +using Neo.Core; + +namespace Neo.UI.Messages +{ + public class InvokeContractMessage + { + public InvokeContractMessage(InvocationTransaction transaction) + { + this.Transaction = transaction; + } + + public InvocationTransaction Transaction { get; } + } +} \ No newline at end of file diff --git a/neo-gui/UI/Messages/SignTransactionAndShowInformationMessage.cs b/neo-gui/UI/Messages/SignTransactionAndShowInformationMessage.cs new file mode 100644 index 00000000..ea387c51 --- /dev/null +++ b/neo-gui/UI/Messages/SignTransactionAndShowInformationMessage.cs @@ -0,0 +1,14 @@ +using Neo.Core; + +namespace Neo.UI.Messages +{ + public class SignTransactionAndShowInformationMessage + { + public SignTransactionAndShowInformationMessage(Transaction transaction) + { + this.Transaction = transaction; + } + + public Transaction Transaction { get; } + } +} \ No newline at end of file diff --git a/neo-gui/UI/Transactions/SigningView.xaml.cs b/neo-gui/UI/Transactions/SigningView.xaml.cs index 3dab0a87..a70064b4 100644 --- a/neo-gui/UI/Transactions/SigningView.xaml.cs +++ b/neo-gui/UI/Transactions/SigningView.xaml.cs @@ -2,7 +2,7 @@ { public partial class SigningView { - internal SigningView() + public SigningView() { InitializeComponent(); } diff --git a/neo-gui/UI/Voting/ElectionView.xaml.cs b/neo-gui/UI/Voting/ElectionView.xaml.cs index a4ba07a2..e74ca9f4 100644 --- a/neo-gui/UI/Voting/ElectionView.xaml.cs +++ b/neo-gui/UI/Voting/ElectionView.xaml.cs @@ -1,6 +1,4 @@ -using Neo.Core; - -namespace Neo.UI.Voting +namespace Neo.UI.Voting { public partial class ElectionView { @@ -8,10 +6,5 @@ public ElectionView() { InitializeComponent(); } - - public InvocationTransaction GetTransactionResult() - { - return (this.DataContext as ElectionViewModel)?.Transaction; - } } } \ No newline at end of file diff --git a/neo-gui/UI/Voting/ElectionViewModel.cs b/neo-gui/UI/Voting/ElectionViewModel.cs index 724f58de..08e42fbc 100644 --- a/neo-gui/UI/Voting/ElectionViewModel.cs +++ b/neo-gui/UI/Voting/ElectionViewModel.cs @@ -5,17 +5,24 @@ using Neo.Cryptography.ECC; using Neo.UI.Base.Extensions; using Neo.SmartContract; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; namespace Neo.UI.Voting { public class ElectionViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private ECPoint selectedBookKeeper; - public ElectionViewModel() + public ElectionViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + if (ApplicationContext.Instance.CurrentWallet == null) return; // Load book keepers @@ -43,24 +50,21 @@ public ECPoint SelectedBookKeeper } } - public InvocationTransaction Transaction { get; private set; } - public bool OkEnabled => this.SelectedBookKeeper != null; public ICommand OkCommand => new RelayCommand(this.Ok); private void Ok() { - var transaction = this.GetTransaction(); + var transaction = this.GenerateTransaction(); if (transaction == null) return; - this.Transaction = transaction; - + this.messagePublisher.Publish(new InvokeContractMessage(transaction)); this.TryClose(); } - private InvocationTransaction GetTransaction() + private InvocationTransaction GenerateTransaction() { if (this.SelectedBookKeeper == null) return null; diff --git a/neo-gui/UI/Voting/VotingView.xaml.cs b/neo-gui/UI/Voting/VotingView.xaml.cs index bed1b86e..9d56aa91 100644 --- a/neo-gui/UI/Voting/VotingView.xaml.cs +++ b/neo-gui/UI/Voting/VotingView.xaml.cs @@ -1,6 +1,4 @@ -using Neo.Core; - -namespace Neo.UI.Voting +namespace Neo.UI.Voting { public partial class VotingView { @@ -12,10 +10,5 @@ public VotingView(UInt160 scriptHash) viewModel?.SetScriptHash(scriptHash); } - - public InvocationTransaction GetTransaction() - { - return (this.DataContext as VotingViewModel)?.GetTransaction(); - } } } \ No newline at end of file diff --git a/neo-gui/UI/Voting/VotingViewModel.cs b/neo-gui/UI/Voting/VotingViewModel.cs index b3529e78..441c4f28 100644 --- a/neo-gui/UI/Voting/VotingViewModel.cs +++ b/neo-gui/UI/Voting/VotingViewModel.cs @@ -2,7 +2,9 @@ using System.Windows.Input; using Neo.Core; using Neo.UI.Base.Extensions; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; using Neo.Wallets; @@ -10,11 +12,17 @@ namespace Neo.UI.Voting { public class VotingViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private UInt160 scriptHash; private string votes; - private InvocationTransaction transaction; + public VotingViewModel( + IMessagePublisher messagePublisher) + { + this.messagePublisher = messagePublisher; + } public string Address { get; private set; } @@ -54,11 +62,6 @@ public void SetScriptHash(UInt160 hash) NotifyPropertyChanged(nameof(this.Address)); } - public InvocationTransaction GetTransaction() - { - return this.transaction; - } - private InvocationTransaction GenerateTransaction() { using (var builder = new ScriptBuilder()) @@ -101,15 +104,16 @@ private InvocationTransaction GenerateTransaction() private void Ok() { - this.transaction = this.GenerateTransaction(); + var transaction = this.GenerateTransaction(); + if (transaction == null) return; + + this.messagePublisher.Publish(new InvokeContractMessage(transaction)); this.TryClose(); } private void Cancel() { - this.transaction = null; - this.TryClose(); } } diff --git a/neo-gui/UI/Wallets/ClaimViewModel.cs b/neo-gui/UI/Wallets/ClaimViewModel.cs index e987b095..982d571d 100644 --- a/neo-gui/UI/Wallets/ClaimViewModel.cs +++ b/neo-gui/UI/Wallets/ClaimViewModel.cs @@ -4,17 +4,27 @@ using System.Windows.Input; using Neo.UI.Base.Controls; using Neo.UI.Base.Helpers; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; namespace Neo.UI.Wallets { public class ClaimViewModel : ViewModelBase { + private readonly IMessagePublisher messagePublisher; + private Fixed8 availableGas = Fixed8.Zero; private Fixed8 unavailableGas = Fixed8.Zero; private bool claimEnabled; + public ClaimViewModel( + IMessagePublisher messagePublisher) + { + this.messagePublisher = messagePublisher; + } + #region Public Properties public Fixed8 AvailableGas @@ -118,8 +128,10 @@ private void CalculateBonusUnavailable(uint height) private void Claim() { var claims = ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference).ToArray(); + if (claims.Length == 0) return; - TransactionHelper.SignAndShowInformation(new ClaimTransaction + + var transaction = new ClaimTransaction { Claims = claims, Attributes = new TransactionAttribute[0], @@ -133,8 +145,9 @@ private void Claim() ScriptHash = ApplicationContext.Instance.CurrentWallet.GetChangeAddress() } } - }); + }; + this.messagePublisher.Publish(new SignTransactionAndShowInformationMessage(transaction)); this.TryClose(); } } diff --git a/neo-gui/UI/Wallets/RestoreAccountsView.xaml.cs b/neo-gui/UI/Wallets/RestoreAccountsView.xaml.cs index 4b26c7c6..b3073c15 100644 --- a/neo-gui/UI/Wallets/RestoreAccountsView.xaml.cs +++ b/neo-gui/UI/Wallets/RestoreAccountsView.xaml.cs @@ -1,22 +1,10 @@ -using System.Collections.Generic; -using Neo.Wallets; - -namespace Neo.UI.Wallets +namespace Neo.UI.Wallets { public partial class RestoreAccountsView { - private readonly RestoreAccountsViewModel viewModel; - public RestoreAccountsView() { InitializeComponent(); - - this.viewModel = this.DataContext as RestoreAccountsViewModel; - } - - public List GetContracts() - { - return this.viewModel?.GetContracts(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs b/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs index d151f869..1fde0637 100644 --- a/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs +++ b/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs @@ -1,18 +1,24 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Runtime.Remoting.Messaging; using System.Windows.Input; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.Wallets; namespace Neo.UI.Wallets { public class RestoreAccountsViewModel : ViewModelBase { - private List contracts; + private readonly IMessagePublisher messagePublisher; - public RestoreAccountsViewModel() + public RestoreAccountsViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + var keys = ApplicationContext.Instance.CurrentWallet.GetKeys(); keys = keys.Where(account => ApplicationContext.Instance.CurrentWallet.GetContracts(account.PublicKeyHash).All(contract => !contract.IsStandard)); @@ -34,15 +40,13 @@ internal void UpdateOkEnabled() private void Ok() { - this.contracts = this.GenerateContracts(); + var contracts = this.GenerateContracts(); + + if (contracts == null) return; + this.messagePublisher.Publish(new AddContractsMessage(contracts)); this.TryClose(); } - - public List GetContracts() - { - return this.contracts; - } private List GenerateContracts() { diff --git a/neo-gui/UI/Wallets/TransferView.xaml.cs b/neo-gui/UI/Wallets/TransferView.xaml.cs index 0bdad951..8b5f4a47 100644 --- a/neo-gui/UI/Wallets/TransferView.xaml.cs +++ b/neo-gui/UI/Wallets/TransferView.xaml.cs @@ -1,5 +1,4 @@ using System; -using Neo.Core; namespace Neo.UI.Wallets { @@ -18,10 +17,5 @@ private void TxOutListBox_OnItemsChanged(object sender, EventArgs e) { this.viewModel?.UpdateOkButtonEnabled(); } - - public Transaction GetTransaction() - { - return this.viewModel?.GetTransaction(); - } } } \ No newline at end of file diff --git a/neo-gui/UI/Wallets/TransferViewModel.cs b/neo-gui/UI/Wallets/TransferViewModel.cs index 491550f4..2066bce1 100644 --- a/neo-gui/UI/Wallets/TransferViewModel.cs +++ b/neo-gui/UI/Wallets/TransferViewModel.cs @@ -9,19 +9,24 @@ using Neo.Properties; using Neo.SmartContract; using Neo.UI.Base.Dialogs; +using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; +using Neo.UI.Messages; using Neo.VM; namespace Neo.UI.Wallets { public class TransferViewModel : ViewModelBase { - private string remark = string.Empty; + private readonly IMessagePublisher messagePublisher; - private Transaction transaction; + private string remark = string.Empty; - public TransferViewModel() + public TransferViewModel( + IMessagePublisher messagePublisher) { + this.messagePublisher = messagePublisher; + this.Items = new ObservableCollection(); } @@ -46,7 +51,20 @@ private void Remark() private void Ok() { - this.transaction = this.GenerateTransaction(); + var transaction = this.GenerateTransaction(); + + if (transaction == null) return; + + var invocationTransaction = transaction as InvocationTransaction; + + if (invocationTransaction != null) + { + this.messagePublisher.Publish(new InvokeContractMessage(invocationTransaction)); + } + else + { + this.messagePublisher.Publish(new SignTransactionAndShowInformationMessage(transaction)); + } this.TryClose(); } @@ -56,11 +74,6 @@ public void UpdateOkButtonEnabled() NotifyPropertyChanged(nameof(this.OkEnabled)); } - public Transaction GetTransaction() - { - return this.transaction; - } - private Transaction GenerateTransaction() { var cOutputs = this.Items.Where(p => p.AssetId is UInt160).GroupBy(p => new diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index c67bebab..e641c057 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -193,7 +193,7 @@ ..\packages\Replicon.Cryptography.SCrypt.1.1.6.13\lib\net40\Replicon.Cryptography.SCrypt.dll - + ..\packages\SQLitePCLRaw.bundle_green.1.1.7\lib\net45\SQLitePCLRaw.batteries_green.dll @@ -291,8 +291,8 @@ CreateMultiSigContractView.xaml - - SelectCertificateView.xaml + + ImportCertificateView.xaml @@ -302,7 +302,7 @@ CreateLockAccountView.xaml - + AssetDistributionView.xaml @@ -311,8 +311,6 @@ TransactionPropertyGrid.xaml - - @@ -351,9 +349,14 @@ + + + + - + + TxOutListBox.xaml @@ -668,7 +671,7 @@ - + MSBuild:Compile Designer From ef4e94874ba14716854d29002d7cbeaca57f3cbe Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 00:15:30 +0100 Subject: [PATCH 09/22] Replace Application.Instance calls with ApplicationContext that was injected in the constructor --- neo-gui/Controllers/BlockChainController.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index 1d863f9e..c1f0766b 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -49,7 +49,7 @@ public BlockChainController( ImportBlocksIfRequired(); - Blockchain.PersistCompleted += Blockchain_PersistCompleted; + Blockchain.PersistCompleted += this.BlockchainPersistCompleted; // Start node this.applicationContext.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); @@ -125,7 +125,7 @@ private void UpdateBlockProgress(TimeSpan persistenceSpan) private void UpdateBalances(TimeSpan persistenceSpan) { - if (ApplicationContext.Instance.CurrentWallet == null) return; + if (this.applicationContext.CurrentWallet == null) return; this.UpdateAssetBalances(); @@ -134,7 +134,7 @@ private void UpdateBalances(TimeSpan persistenceSpan) private void UpdateAssetBalances() { - if (ApplicationContext.Instance.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; + if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; this.messagePublisher.Publish(new UpdateAcountListMessage()); this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); @@ -147,7 +147,7 @@ private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; // Update balances - var addresses = ApplicationContext.Instance.CurrentWallet.GetAddresses().ToArray(); + var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); foreach (var s in Settings.Default.NEP5Watched) { var scriptHash = UInt160.Parse(s); @@ -270,14 +270,14 @@ private void ImportBlocks(Stream stream) blockchain.VerifyBlocks = true; } - private void Blockchain_PersistCompleted(object sender, Block block) + private void BlockchainPersistCompleted(object sender, Block block) { this.persistenceTime = DateTime.UtcNow; - if (ApplicationContext.Instance.CurrentWallet != null) + if (this.applicationContext.CurrentWallet != null) { this.checkNep5Balance = true; - var coins = ApplicationContext.Instance.CurrentWallet.GetCoins(); + var coins = this.applicationContext.CurrentWallet.GetCoins(); if (coins.Any(coin => !coin.State.HasFlag(CoinState.Spent) && coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))) { From ebd99f4368fa3a9aa78377b6348fb1ff95a5acda Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 11:24:52 +0100 Subject: [PATCH 10/22] Remove from solution already removed messages classes --- neo-gui/neo-gui.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 641696d8..ce6c9f90 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -193,7 +193,7 @@ ..\packages\Replicon.Cryptography.SCrypt.1.1.6.13\lib\net40\Replicon.Cryptography.SCrypt.dll - + ..\packages\SQLitePCLRaw.bundle_green.1.1.7\lib\net45\SQLitePCLRaw.batteries_green.dll @@ -360,7 +360,6 @@ - @@ -371,7 +370,6 @@ - From c92fd1878789e5a2796c7259a0276ea239df3bc1 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 11:32:13 +0100 Subject: [PATCH 11/22] Cleanup in AccountViewModel don't access the ApplicationContext using the Singleton but the constructor injection object --- neo-gui/Controllers/BlockChainController.cs | 2 +- neo-gui/UI/Home/AccountsViewModel.cs | 39 +++++++++++---------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index c1f0766b..cc5504e1 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -136,7 +136,7 @@ private void UpdateAssetBalances() { if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; - this.messagePublisher.Publish(new UpdateAcountListMessage()); + this.messagePublisher.Publish(new AccountBalancesChangedMessage()); this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); } diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index bad5e658..0680ada0 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -31,6 +31,7 @@ public class AccountsViewModel : IMessageHandler, IMessageHandler { + private readonly IApplicationContext applicationContext; private readonly IMessageSubscriber messageSubscriber; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -38,10 +39,12 @@ public class AccountsViewModel : private AccountItem selectedAccount; public AccountsViewModel( + IApplicationContext applicationContext, IMessageSubscriber messageSubscriber, IMessagePublisher messagePublisher, IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.messageSubscriber = messageSubscriber; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -74,7 +77,7 @@ public AccountItem SelectedAccount } } - public bool MenuItemsEnabled => ApplicationContext.Instance.CurrentWallet != null; + public bool MenuItemsEnabled => this.applicationContext.CurrentWallet != null; public bool ViewPrivateKeyEnabled => this.SelectedAccount != null && @@ -178,8 +181,8 @@ private async void AddContract(VerificationContract contract, bool selected = fa private void CreateNewKey() { this.SelectedAccount = null; - var key = ApplicationContext.Instance.CurrentWallet.CreateKey(); - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + var key = this.applicationContext.CurrentWallet.CreateKey(); + foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) { AddContract(contract, true); } @@ -206,14 +209,14 @@ private void ImportWifPrivateKey() KeyPair key; try { - key = ApplicationContext.Instance.CurrentWallet.Import(wif); + key = this.applicationContext.CurrentWallet.Import(wif); } catch (FormatException) { // Skip WIF line continue; } - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) { AddContract(contract, true); } @@ -232,7 +235,7 @@ private async void ImportCertificate() KeyPair key; try { - key = ApplicationContext.Instance.CurrentWallet.Import(view.SelectedCertificate); + key = this.applicationContext.CurrentWallet.Import(view.SelectedCertificate); } catch { @@ -240,7 +243,7 @@ private async void ImportCertificate() return; } - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) { AddContract(contract, true); } @@ -269,7 +272,7 @@ private void ImportWatchOnlyAddress() { continue; } - ApplicationContext.Instance.CurrentWallet.AddWatchOnly(scriptHash); + this.applicationContext.CurrentWallet.AddWatchOnly(scriptHash); AddAddress(scriptHash, true); } } @@ -284,7 +287,7 @@ private void CreateMultiSignatureContract() if (contract == null) return; - ApplicationContext.Instance.CurrentWallet.AddContract(contract); + this.applicationContext.CurrentWallet.AddContract(contract); this.SelectedAccount = null; AddContract(contract, true); } @@ -298,7 +301,7 @@ private void CreateLockAddress() if (contract == null) return; - ApplicationContext.Instance.CurrentWallet.AddContract(contract); + this.applicationContext.CurrentWallet.AddContract(contract); this.SelectedAccount = null; AddContract(contract, true); } @@ -312,7 +315,7 @@ private void ImportCustomContract() if (contract == null) return; - ApplicationContext.Instance.CurrentWallet.AddContract(contract); + this.applicationContext.CurrentWallet.AddContract(contract); this.SelectedAccount = null; AddContract(contract, true); } @@ -322,7 +325,7 @@ private void ViewPrivateKey() if (this.SelectedAccount?.Contract == null) return; var contract = this.SelectedAccount.Contract; - var key = ApplicationContext.Instance.CurrentWallet.GetKeyByScriptHash(contract.ScriptHash); + var key = this.applicationContext.CurrentWallet.GetKeyByScriptHash(contract.ScriptHash); var view = new ViewPrivateKeyView(key, contract.ScriptHash); view.ShowDialog(); @@ -385,7 +388,7 @@ private async void DeleteAccount() ? accountToDelete.ScriptHash : accountToDelete.Contract.ScriptHash; - ApplicationContext.Instance.CurrentWallet.DeleteAddress(scriptHash); + this.applicationContext.CurrentWallet.DeleteAddress(scriptHash); await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Remove(accountToDelete)); this.messagePublisher.Publish(new WalletBalanceChangedMessage(true)); @@ -395,7 +398,7 @@ private async void DeleteAccount() #region IMessageHandler implementation public void HandleMessage(AccountBalancesChangedMessage message) { - var coins = ApplicationContext.Instance.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); + var coins = this.applicationContext.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); if (coins == null) return; @@ -426,12 +429,12 @@ public void HandleMessage(ClearAccountsMessage message) public void HandleMessage(LoadWalletAddressesMessage message) { - if (ApplicationContext.Instance.CurrentWallet == null) return; + if (this.applicationContext.CurrentWallet == null) return; // Load accounts - foreach (var scriptHash in ApplicationContext.Instance.CurrentWallet.GetAddresses()) + foreach (var scriptHash in this.applicationContext.CurrentWallet.GetAddresses()) { - var contract = ApplicationContext.Instance.CurrentWallet.GetContract(scriptHash); + var contract = this.applicationContext.CurrentWallet.GetContract(scriptHash); if (contract == null) { this.AddAddress(scriptHash); @@ -452,7 +455,7 @@ public void HandleMessage(RestoreContractsMessage message) foreach (var contract in message.Contracts) { - ApplicationContext.Instance.CurrentWallet.AddContract(contract); + this.applicationContext.CurrentWallet.AddContract(contract); this.AddContract(contract, true); } } From df26fb79ad9d278e5b70b009a0fd561eecc1e7be Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 14:12:04 +0100 Subject: [PATCH 12/22] Remove singleton ApplicationContext from HomeViewModel and inject it in constructor. --- neo-gui/UI/Home/HomeViewModel.cs | 43 +++++++++++++++++--------------- neo-gui/neo-gui.csproj | 1 - 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 8db1e27b..dea44111 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -44,6 +44,7 @@ public class HomeViewModel : IMessageHandler { #region Private Fields + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IMessageSubscriber messageSubscriber; private readonly IDispatcher dispatcher; @@ -66,10 +67,12 @@ public class HomeViewModel : #region Constructor public HomeViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; @@ -79,7 +82,7 @@ public HomeViewModel( #endregion #region Public Properties - public bool WalletIsOpen => ApplicationContext.Instance.CurrentWallet != null; + public bool WalletIsOpen => this.applicationContext.CurrentWallet != null; public string BlockHeight => $"{GetWalletHeight()}/{Blockchain.Default.Height}/{Blockchain.Default.HeaderHeight}"; public int NodeCount => Program.LocalNode.RemoteNodeCount; @@ -199,11 +202,11 @@ public bool NewVersionVisible private void Blockchain_PersistCompleted(object sender, Block block) { this.persistenceTime = DateTime.UtcNow; - if (ApplicationContext.Instance.CurrentWallet != null) + if (this.applicationContext.CurrentWallet != null) { this.checkNep5Balance = true; - var coins = ApplicationContext.Instance.CurrentWallet.GetCoins(); + var coins = this.applicationContext.CurrentWallet.GetCoins(); if (coins.Any(coin => !coin.State.HasFlag(CoinState.Spent) && coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))) { @@ -215,12 +218,12 @@ private void Blockchain_PersistCompleted(object sender, Block block) private void ChangeWallet(UserWallet wallet) { - if (ApplicationContext.Instance.CurrentWallet != null) + if (this.applicationContext.CurrentWallet != null) { // Dispose current wallet - ApplicationContext.Instance.CurrentWallet.BalanceChanged -= CurrentWallet_BalanceChanged; - ApplicationContext.Instance.CurrentWallet.TransactionsChanged -= CurrentWallet_TransactionsChanged; - ApplicationContext.Instance.CurrentWallet.Dispose(); + this.applicationContext.CurrentWallet.BalanceChanged -= CurrentWallet_BalanceChanged; + this.applicationContext.CurrentWallet.TransactionsChanged -= CurrentWallet_TransactionsChanged; + this.applicationContext.CurrentWallet.Dispose(); } this.dispatcher.InvokeOnMainUIThread(() => @@ -230,16 +233,16 @@ private void ChangeWallet(UserWallet wallet) this.messagePublisher.Publish(new ClearTransactionsMessage()); }); - ApplicationContext.Instance.CurrentWallet = wallet; + this.applicationContext.CurrentWallet = wallet; - if (ApplicationContext.Instance.CurrentWallet != null) + if (this.applicationContext.CurrentWallet != null) { // Setup wallet - var transactions = ApplicationContext.Instance.CurrentWallet.LoadTransactions(); + var transactions = this.applicationContext.CurrentWallet.LoadTransactions(); CurrentWallet_TransactionsChanged(transactions); - ApplicationContext.Instance.CurrentWallet.BalanceChanged += CurrentWallet_BalanceChanged; - ApplicationContext.Instance.CurrentWallet.TransactionsChanged += CurrentWallet_TransactionsChanged; + this.applicationContext.CurrentWallet.BalanceChanged += CurrentWallet_BalanceChanged; + this.applicationContext.CurrentWallet.TransactionsChanged += CurrentWallet_TransactionsChanged; } this.messagePublisher.Publish(new EnableMenuItemsMessage()); @@ -422,7 +425,7 @@ private void UpdateBlockProgress(TimeSpan persistenceSpan) private void UpdateBalances(TimeSpan persistenceSpan) { - if (ApplicationContext.Instance.CurrentWallet == null) return; + if (this.applicationContext.CurrentWallet == null) return; this.UpdateAssetBalances(); @@ -431,7 +434,7 @@ private void UpdateBalances(TimeSpan persistenceSpan) private void UpdateAssetBalances() { - if (ApplicationContext.Instance.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; + if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; this.messagePublisher.Publish(new AccountBalancesChangedMessage()); this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); @@ -445,7 +448,7 @@ private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; // Update balances - var addresses = ApplicationContext.Instance.CurrentWallet.GetAddresses().ToArray(); + var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); foreach (var s in Settings.Default.NEP5Watched) { var scriptHash = UInt160.Parse(s); @@ -575,7 +578,7 @@ await this.dispatcher.InvokeOnMainUIThread(() => this.messagePublisher.Publish(new ClearTransactionsMessage()); }); - ApplicationContext.Instance.CurrentWallet.Rebuild(); + this.applicationContext.CurrentWallet.Rebuild(); } private void RestoreAccounts() @@ -751,15 +754,15 @@ private static void ShowUpdateDialog() dialog.ShowDialog(); } - private static uint GetWalletHeight() + private uint GetWalletHeight() { uint walletHeight = 0; - if (ApplicationContext.Instance.CurrentWallet != null && - ApplicationContext.Instance.CurrentWallet.WalletHeight > 0) + if (this.applicationContext.CurrentWallet != null && + this.applicationContext.CurrentWallet.WalletHeight > 0) { // Set wallet height - walletHeight = ApplicationContext.Instance.CurrentWallet.WalletHeight - 1; + walletHeight = this.applicationContext.CurrentWallet.WalletHeight - 1; } return walletHeight; diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 1c48900d..ce6c9f90 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -360,7 +360,6 @@ - From 078c34726c902cdef23e8670b62843fb0cc463bb Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 14:30:16 +0100 Subject: [PATCH 13/22] Remove extra-code added by mistake during the merge --- neo-gui/UI/Home/AccountsViewModel.cs | 73 ---------------------------- neo-gui/UI/Home/HomeViewModel.cs | 4 -- neo-gui/neo-gui.csproj | 1 - 3 files changed, 78 deletions(-) diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index 9a6259fe..24876031 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -195,61 +195,12 @@ private void ImportWifPrivateKey() { var view = new ImportPrivateKeyView(); view.ShowDialog(); - - var wifStrings = view.WifStrings; - - if (wifStrings == null) return; - - var wifStringList = wifStrings.ToList(); - - if (!wifStringList.Any()) return; - - // Import private keys - this.SelectedAccount = null; - - foreach (var wif in wifStringList) - { - KeyPair key; - try - { - key = this.applicationContext.CurrentWallet.Import(wif); - } - catch (FormatException) - { - // Skip WIF line - continue; - } - foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } - } } private void ImportCertificate() { var view = new ImportCertificateView(); view.ShowDialog(); - - if (view.SelectedCertificate == null) return; - - this.SelectedAccount = null; - - KeyPair key; - try - { - key = this.applicationContext.CurrentWallet.Import(view.SelectedCertificate); - } - catch - { - await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, "Certificate import failed!"); - return; - } - - foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } } private void ImportWatchOnlyAddress() @@ -285,42 +236,18 @@ private void CreateMultiSignatureContract() { var view = new CreateMultiSigContractView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - this.applicationContext.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void CreateLockAddress() { var view = new CreateLockAccountView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - this.applicationContext.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void ImportCustomContract() { var view = new ImportCustomContractView(); view.ShowDialog(); - - var contract = view.GetContract(); - - if (contract == null) return; - - this.applicationContext.CurrentWallet.AddContract(contract); - this.SelectedAccount = null; - AddContract(contract, true); } private void ViewPrivateKey() diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index bd1fa884..cf21b5df 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -587,10 +587,6 @@ private static void RestoreAccounts() { var view = new RestoreAccountsView(); view.ShowDialog(); - - var contracts = view.GetContracts(); - - this.messagePublisher.Publish(new RestoreContractsMessage(contracts)); } private void Exit() diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 88564957..5b371e4a 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -371,7 +371,6 @@ - From d9ccd10d669814299df3061d2d1496a6876f3da5 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 14:43:14 +0100 Subject: [PATCH 14/22] This object is declared as Singleton, therefore the lock in not necessary. This class will be created only once. --- neo-gui/Controllers/BlockChainController.cs | 36 +++++++++------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index 7422d1e7..57f37a7c 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -22,7 +22,6 @@ namespace Neo.Controllers public class BlockChainController : IBlockChainController { #region Private Fields - private readonly object uiUpdateTimerLock = new object(); private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -55,31 +54,28 @@ public BlockChainController( this.applicationContext.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); }); - lock (this.uiUpdateTimerLock) + + if (this.uiUpdateTimer != null) { - if (this.uiUpdateTimer != null) - { - // Stop previous timer - this.uiUpdateTimer.Stop(); - - this.uiUpdateTimer.Elapsed -= this.UpdateWallet; + // Stop previous timer + this.uiUpdateTimer.Stop(); - this.uiUpdateTimer.Dispose(); + this.uiUpdateTimer.Elapsed -= this.UpdateWallet; - this.uiUpdateTimer = null; - } + this.uiUpdateTimer.Dispose(); - var timer = new Timer - { - Interval = 500, - Enabled = true, - AutoReset = true - }; + this.uiUpdateTimer = null; + } - timer.Elapsed += this.UpdateWallet; + var timer = new Timer + { + Interval = 500, + Enabled = true, + AutoReset = true + }; - this.uiUpdateTimer = timer; - } + timer.Elapsed += this.UpdateWallet; + this.uiUpdateTimer = timer; } #endregion From 05bea27a01dffc0e83842e0f1f2a4fa42c11b073 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 15:43:55 +0100 Subject: [PATCH 15/22] WalletController is been injected in the BlockChainController instead to access through ApplicationContext --- neo-gui/Controllers/BlockChainController.cs | 27 +++++++++++++-------- neo-gui/Controllers/IWalletController.cs | 14 ++++++++++- neo-gui/Controllers/WalletController.cs | 16 ++++++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index 57f37a7c..36f09cf7 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -22,6 +22,7 @@ namespace Neo.Controllers public class BlockChainController : IBlockChainController { #region Private Fields + private readonly IWalletController walletController; private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -34,10 +35,12 @@ public class BlockChainController : IBlockChainController #region Constructor public BlockChainController( + IWalletController walletController, IApplicationContext applicationContext, IMessagePublisher messagePublisher, IDispatcher dispatcher) { + this.walletController = walletController; this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -94,7 +97,10 @@ private void UpdateBlockProgress(TimeSpan persistenceSpan) var blockProgressIndeterminate = false; var blockProgress = 0; - if (persistenceSpan < TimeSpan.Zero) persistenceSpan = TimeSpan.Zero; + if (persistenceSpan < TimeSpan.Zero) + { + persistenceSpan = TimeSpan.Zero; + } if (persistenceSpan > Blockchain.TimePerBlock) { @@ -121,7 +127,7 @@ private void UpdateBlockProgress(TimeSpan persistenceSpan) private void UpdateBalances(TimeSpan persistenceSpan) { - if (this.applicationContext.CurrentWallet == null) return; + if (!this.walletController.IsWalletOpen) return; this.UpdateAssetBalances(); @@ -130,7 +136,7 @@ private void UpdateBalances(TimeSpan persistenceSpan) private void UpdateAssetBalances() { - if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; + if (this.walletController.WalletWeight > Blockchain.Default.Height + 1) return; this.messagePublisher.Publish(new AccountBalancesChangedMessage()); @@ -144,7 +150,7 @@ private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; // Update balances - var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); + var addresses = this.walletController.GetAddresses(); foreach (var s in Settings.Default.NEP5Watched) { @@ -201,11 +207,11 @@ private uint GetWalletHeight() { uint walletHeight = 0; - if (this.applicationContext.CurrentWallet != null && - this.applicationContext.CurrentWallet.WalletHeight > 0) + if (this.walletController.IsWalletOpen && + this.walletController.WalletWeight > 0) { // Set wallet height - walletHeight = this.applicationContext.CurrentWallet.WalletHeight - 1; + walletHeight = this.walletController.WalletWeight - 1; } return walletHeight; @@ -271,13 +277,14 @@ private void ImportBlocks(Stream stream) private void BlockchainPersistCompleted(object sender, Block block) { this.persistenceTime = DateTime.UtcNow; - if (this.applicationContext.CurrentWallet != null) + if (this.walletController.IsWalletOpen) { this.checkNep5Balance = true; - var coins = this.applicationContext.CurrentWallet.GetCoins(); + var coins = this.walletController.GetCoins(); - if (coins.Any(coin => !coin.State.HasFlag(CoinState.Spent) && + if (coins.Any( + coin => !coin.State.HasFlag(CoinState.Spent) && coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))) { balanceChanged = true; diff --git a/neo-gui/Controllers/IWalletController.cs b/neo-gui/Controllers/IWalletController.cs index 5de8682f..d140406b 100644 --- a/neo-gui/Controllers/IWalletController.cs +++ b/neo-gui/Controllers/IWalletController.cs @@ -1,9 +1,21 @@ -namespace Neo.Controllers +using System.Collections; +using System.Collections.Generic; +using Neo.Wallets; + +namespace Neo.Controllers { public interface IWalletController { + bool IsWalletOpen { get; } + + uint WalletWeight { get; } + void CreateWallet(string walletPath, string password); void OpenWallet(string walletPath, string password); + + IEnumerable GetAddresses(); + + IEnumerable GetCoins(); } } diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index a50653d6..80c947bc 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -8,6 +8,7 @@ using Neo.Properties; using Neo.UI.Base.Messages; using Neo.UI.Messages; +using Neo.Wallets; namespace Neo.Controllers { @@ -37,6 +38,10 @@ public WalletController( #endregion #region IWalletController implementation + public bool IsWalletOpen => this.currentWallet != null; + + public uint WalletWeight => this.currentWallet.WalletHeight; + public void CreateWallet(string walletPath, string password) { var newWallet = UserWallet.Create(walletPath, password); @@ -80,6 +85,17 @@ public void OpenWallet(string walletPath, string password) Settings.Default.LastWalletPath = walletPath; Settings.Default.Save(); } + + public IEnumerable GetAddresses() + { + return this.currentWallet.GetAddresses(); + } + + public IEnumerable GetCoins() + { + // TODO - ISSUE #37 [AboimPinto]: at this point the return should not be a object from the NEO assemblies but a DTO only know by the application with only the necessary fields. + return this.currentWallet.GetCoins(); + } #endregion #region Private Methods From 45931c7585bd9e502b1aa6e6a8188409d18b5d06 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Wed, 15 Nov 2017 16:53:33 +0100 Subject: [PATCH 16/22] The access to the singleton Application.Instance is not removed from all the ViewModels. IApplicationContext has been injected in this classes. --- neo-gui/Controllers/BlockChainController.cs | 1 - .../UI/Accounts/CreateLockAccountViewModel.cs | 7 +++++-- .../Accounts/CreateMultiSigContractViewModel.cs | 5 ++++- .../Accounts/ImportCustomContractViewModel.cs | 7 +++++-- neo-gui/UI/Assets/AssetDistributionViewModel.cs | 5 ++++- neo-gui/UI/Assets/AssetRegistrationViewModel.cs | 9 ++++++--- neo-gui/UI/Contracts/InvokeContractViewModel.cs | 5 ++++- .../Development/ContractParametersViewModel.cs | 8 ++++++-- .../Development/TransactionBuilderViewModel.cs | 16 ++++++++++------ neo-gui/UI/Home/AccountsViewModel.cs | 10 +++++----- neo-gui/UI/Home/AssetsViewModel.cs | 12 +++++++----- neo-gui/UI/Home/HomeViewModel.cs | 4 ++-- neo-gui/UI/Transactions/BulkPayViewModel.cs | 10 +++++++--- neo-gui/UI/Transactions/PayToViewModel.cs | 10 +++++++--- neo-gui/UI/Transactions/SigningViewModel.cs | 10 ++++++++-- neo-gui/UI/Voting/ElectionViewModel.cs | 10 ++++++---- .../Wallets/CertificateApplicationViewModel.cs | 12 +++++++----- neo-gui/UI/Wallets/ChangePasswordViewModel.cs | 9 ++++++++- neo-gui/UI/Wallets/ClaimViewModel.cs | 11 +++++++---- neo-gui/UI/Wallets/RestoreAccountsViewModel.cs | 7 +++++-- neo-gui/UI/Wallets/TradeViewModel.cs | 17 +++++++++++------ neo-gui/UI/Wallets/TransferViewModel.cs | 7 +++++-- 22 files changed, 129 insertions(+), 63 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index 36f09cf7..e8da554e 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -139,7 +139,6 @@ private void UpdateAssetBalances() if (this.walletController.WalletWeight > Blockchain.Default.Height + 1) return; this.messagePublisher.Publish(new AccountBalancesChangedMessage()); - this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); } diff --git a/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs b/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs index 49c38e0b..856c2d4b 100644 --- a/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs +++ b/neo-gui/UI/Accounts/CreateLockAccountViewModel.cs @@ -20,7 +20,8 @@ public class CreateLockAccountViewModel : ViewModelBase { private const int HoursInDay = 24; private const int MinutesInHour = 60; - + + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private ECPoint selectedAccount; @@ -29,11 +30,13 @@ public class CreateLockAccountViewModel : ViewModelBase private int unlockMinute; public CreateLockAccountViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; - this.Accounts = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey).ToArray()); + this.Accounts = new ObservableCollection(this.applicationContext.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => this.applicationContext.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey).ToArray()); this.Hours = new List(); diff --git a/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs b/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs index e67de9c3..eca0a505 100644 --- a/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs +++ b/neo-gui/UI/Accounts/CreateMultiSigContractViewModel.cs @@ -16,6 +16,7 @@ namespace Neo.UI.Accounts { public class CreateMultiSigContractViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -28,9 +29,11 @@ public class CreateMultiSigContractViewModel : ViewModelBase public CreateMultiSigContractViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher, IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -164,7 +167,7 @@ private VerificationContract GenerateContract() foreach (var publicKey in publicKeys) { - var key = ApplicationContext.Instance.CurrentWallet.GetKey(publicKey.EncodePoint(true).ToScriptHash()); + var key = this.applicationContext.CurrentWallet.GetKey(publicKey.EncodePoint(true).ToScriptHash()); if (key == null) continue; diff --git a/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs b/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs index 8c6bf10c..fcfbc00a 100644 --- a/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs +++ b/neo-gui/UI/Accounts/ImportCustomContractViewModel.cs @@ -13,6 +13,7 @@ namespace Neo.UI.Accounts { public class ImportCustomContractViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private ECPoint selectedRelatedAccount; @@ -20,13 +21,15 @@ public class ImportCustomContractViewModel : ViewModelBase private string script; public ImportCustomContractViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.RelatedAccounts = new ObservableCollection( - ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => - ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); + this.applicationContext.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => + this.applicationContext.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); } public ObservableCollection RelatedAccounts { get; } diff --git a/neo-gui/UI/Assets/AssetDistributionViewModel.cs b/neo-gui/UI/Assets/AssetDistributionViewModel.cs index 6f0765f1..197806d4 100644 --- a/neo-gui/UI/Assets/AssetDistributionViewModel.cs +++ b/neo-gui/UI/Assets/AssetDistributionViewModel.cs @@ -12,6 +12,7 @@ namespace Neo.UI.Assets { public class AssetDistributionViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -29,9 +30,11 @@ public class AssetDistributionViewModel : ViewModelBase private bool distributionEnabled; public AssetDistributionViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher, IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -214,7 +217,7 @@ private void UpdateAssetDetails() private IssueTransaction GenerateTransaction() { if (this.Asset == null) return null; - return ApplicationContext.Instance.CurrentWallet.MakeTransaction(new IssueTransaction + return this.applicationContext.CurrentWallet.MakeTransaction(new IssueTransaction { Version = 1, Outputs = this.Items.GroupBy(p => p.ScriptHash).Select(g => new TransactionOutput diff --git a/neo-gui/UI/Assets/AssetRegistrationViewModel.cs b/neo-gui/UI/Assets/AssetRegistrationViewModel.cs index 2149e123..ae99735f 100644 --- a/neo-gui/UI/Assets/AssetRegistrationViewModel.cs +++ b/neo-gui/UI/Assets/AssetRegistrationViewModel.cs @@ -18,6 +18,7 @@ public class AssetRegistrationViewModel : ViewModelBase { private static readonly AssetType[] assetTypes = { AssetType.Share, AssetType.Token }; + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private AssetType? selectedAssetType; @@ -35,14 +36,16 @@ public class AssetRegistrationViewModel : ViewModelBase private bool formValid; public AssetRegistrationViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.AssetTypes = new ObservableCollection(assetTypes); - this.Owners = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); - this.Admins = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Select(p => p.Address)); - this.Issuers = new ObservableCollection(ApplicationContext.Instance.CurrentWallet.GetContracts().Select(p => p.Address)); + this.Owners = new ObservableCollection(this.applicationContext.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => this.applicationContext.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey)); + this.Admins = new ObservableCollection(this.applicationContext.CurrentWallet.GetContracts().Select(p => p.Address)); + this.Issuers = new ObservableCollection(this.applicationContext.CurrentWallet.GetContracts().Select(p => p.Address)); } public ObservableCollection AssetTypes { get; } diff --git a/neo-gui/UI/Contracts/InvokeContractViewModel.cs b/neo-gui/UI/Contracts/InvokeContractViewModel.cs index 5986c518..ffd6f30f 100644 --- a/neo-gui/UI/Contracts/InvokeContractViewModel.cs +++ b/neo-gui/UI/Contracts/InvokeContractViewModel.cs @@ -20,6 +20,7 @@ public class InvokeContractViewModel : ViewModelBase { private static readonly Fixed8 NetworkFee = Fixed8.FromDecimal(0.001m); + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private InvocationTransaction transaction; @@ -40,8 +41,10 @@ public class InvokeContractViewModel : ViewModelBase private bool invokeEnabled; public InvokeContractViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; } @@ -188,7 +191,7 @@ public InvocationTransaction GetTransaction() var transactionFee = this.transaction.Gas.Equals(Fixed8.Zero) ? NetworkFee : Fixed8.Zero; - return ApplicationContext.Instance.CurrentWallet.MakeTransaction(new InvocationTransaction + return this.applicationContext.CurrentWallet.MakeTransaction(new InvocationTransaction { Version = transaction.Version, Script = transaction.Script, diff --git a/neo-gui/UI/Development/ContractParametersViewModel.cs b/neo-gui/UI/Development/ContractParametersViewModel.cs index 5a46b449..e4ef0dec 100644 --- a/neo-gui/UI/Development/ContractParametersViewModel.cs +++ b/neo-gui/UI/Development/ContractParametersViewModel.cs @@ -16,6 +16,7 @@ namespace Neo.UI.Development { public class ContractParametersViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IDispatcher dispatcher; private ContractParametersContext context; @@ -29,8 +30,11 @@ public class ContractParametersViewModel : ViewModelBase private bool showEnabled; private bool broadcastVisible; - public ContractParametersViewModel(IDispatcher dispatcher) + public ContractParametersViewModel( + IApplicationContext applicationContext, + IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.dispatcher = dispatcher; this.ScriptHashAddresses = new ObservableCollection(); @@ -44,7 +48,7 @@ public ObservableCollection Parameters { var emptyCollection = new ObservableCollection(); - if (ApplicationContext.Instance.CurrentWallet == null) return emptyCollection; + if (this.applicationContext.CurrentWallet == null) return emptyCollection; if (string.IsNullOrEmpty(this.SelectedScriptHashAddress)) return emptyCollection; diff --git a/neo-gui/UI/Development/TransactionBuilderViewModel.cs b/neo-gui/UI/Development/TransactionBuilderViewModel.cs index 319afef6..5ff84599 100644 --- a/neo-gui/UI/Development/TransactionBuilderViewModel.cs +++ b/neo-gui/UI/Development/TransactionBuilderViewModel.cs @@ -16,12 +16,11 @@ namespace Neo.UI.Development { public class TransactionBuilderViewModel : ViewModelBase { - private TransactionType selectedTransactionType; + private readonly IApplicationContext applicationContext; + private TransactionType selectedTransactionType; private TransactionWrapper transactionWrapper; - - public TransactionType[] TransactionTypes => new[] { TransactionType.ContractTransaction, @@ -69,9 +68,14 @@ public object TransactionWrapper public bool SidePanelEnabled => this.TransactionWrapper != null; - public bool SetupOutputsEnabled => ApplicationContext.Instance.CurrentWallet != null; + public bool SetupOutputsEnabled => this.applicationContext.CurrentWallet != null; - public bool FindUnspentCoinsEnabled => ApplicationContext.Instance.CurrentWallet != null; + public bool FindUnspentCoinsEnabled => this.applicationContext.CurrentWallet != null; + + public TransactionBuilderViewModel(IApplicationContext applicationContext) + { + this.applicationContext = applicationContext; + } #region Commands @@ -145,7 +149,7 @@ private void SetupOutputs() private void FindUnspentCoins() { var wrapper = (TransactionWrapper)this.TransactionWrapper; - var transaction = ApplicationContext.Instance.CurrentWallet.MakeTransaction(wrapper.Unwrap()); + var transaction = this.applicationContext.CurrentWallet.MakeTransaction(wrapper.Unwrap()); if (transaction == null) { MessageBox.Show(Strings.InsufficientFunds); diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index 24876031..3cccbfcc 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -383,7 +383,7 @@ public void HandleMessage(AddContractMessage message) this.SelectedAccount = null; - ApplicationContext.Instance.CurrentWallet.AddContract(message.Contract); + this.applicationContext.CurrentWallet.AddContract(message.Contract); this.AddContract(message.Contract, true); } @@ -401,14 +401,14 @@ public void HandleMessage(ImportPrivateKeyMessage message) KeyPair key; try { - key = ApplicationContext.Instance.CurrentWallet.Import(wif); + key = this.applicationContext.CurrentWallet.Import(wif); } catch (FormatException) { // Skip WIF line continue; } - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) { this.AddContract(contract, true); } @@ -424,7 +424,7 @@ public async void HandleMessage(ImportCertificateMessage message) KeyPair key; try { - key = ApplicationContext.Instance.CurrentWallet.Import(message.SelectedCertificate); + key = this.applicationContext.CurrentWallet.Import(message.SelectedCertificate); } catch { @@ -432,7 +432,7 @@ public async void HandleMessage(ImportCertificateMessage message) return; } - foreach (var contract in ApplicationContext.Instance.CurrentWallet.GetContracts(key.PublicKeyHash)) + foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) { AddContract(contract, true); } diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index 76390e19..53cbe179 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -11,7 +11,6 @@ using Neo.SmartContract; using Neo.UI.Base.Collections; using Neo.UI.Base.Dispatching; -using Neo.UI.Base.Helpers; using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; using Neo.UI.Messages; @@ -29,6 +28,7 @@ public class AssetsViewModel : #region Private Fields private static readonly UInt160 RecycleScriptHash = new[] { (byte)OpCode.PUSHT }.ToScriptHash(); + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; private readonly Dictionary certificateQueryResultCache; @@ -37,8 +37,10 @@ public class AssetsViewModel : #endregion public AssetsViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.certificateQueryResultCache = new Dictionary(); @@ -121,12 +123,12 @@ private void DeleteAsset() { if (this.SelectedAsset == null || this.SelectedAsset.State == null) return; - var value = ApplicationContext.Instance.CurrentWallet.GetAvailable(this.SelectedAsset.State.AssetId); + var value = this.applicationContext.CurrentWallet.GetAvailable(this.SelectedAsset.State.AssetId); if (MessageBox.Show($"{Strings.DeleteAssetConfirmationMessage}\n{string.Join("\n", $"{this.SelectedAsset.State.GetName()}:{value}")}", Strings.DeleteConfirmation, MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) != MessageBoxResult.Yes) return; - var transaction = ApplicationContext.Instance.CurrentWallet.MakeTransaction(new ContractTransaction + var transaction = this.applicationContext.CurrentWallet.MakeTransaction(new ContractTransaction { Outputs = new[] { @@ -176,8 +178,8 @@ public void HandleMessage(UpdateAssetsBalanceMessage message) var assetList = this.Assets.ConvertToList(); if (message.BalanceChanged) { - var coins = ApplicationContext.Instance.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); - var bonusAvailable = Blockchain.CalculateBonus(ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); + var coins = this.applicationContext.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); + var bonusAvailable = Blockchain.CalculateBonus(this.applicationContext.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); var bonusUnavailable = Blockchain.CalculateBonus(coins.Where(p => p.State.HasFlag(CoinState.Confirmed) && p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)).Select(p => p.Reference), Blockchain.Default.Height + 1); var bonus = bonusAvailable + bonusUnavailable; diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index cf21b5df..1f18e8c9 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -753,12 +753,12 @@ public void HandleMessage(SignTransactionAndShowInformationMessage message) return; } - ApplicationContext.Instance.CurrentWallet.Sign(context); + this.applicationContext.CurrentWallet.Sign(context); if (context.Completed) { context.Verifiable.Scripts = context.GetScripts(); - ApplicationContext.Instance.CurrentWallet.SaveTransaction(transaction); + this.applicationContext.CurrentWallet.SaveTransaction(transaction); Program.LocalNode.Relay(transaction); InformationBox.Show(transaction.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); } diff --git a/neo-gui/UI/Transactions/BulkPayViewModel.cs b/neo-gui/UI/Transactions/BulkPayViewModel.cs index 88deea12..ad9b77c0 100644 --- a/neo-gui/UI/Transactions/BulkPayViewModel.cs +++ b/neo-gui/UI/Transactions/BulkPayViewModel.cs @@ -12,6 +12,7 @@ namespace Neo.UI.Transactions { public class BulkPayViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IDispatcher dispatcher; private bool assetSelectionEnabled; @@ -22,8 +23,11 @@ public class BulkPayViewModel : ViewModelBase private TxOutListBoxItem[] outputs; - public BulkPayViewModel(IDispatcher dispatcher) + public BulkPayViewModel( + IApplicationContext applicationContext, + IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.dispatcher = dispatcher; this.Assets = new ObservableCollection(); @@ -62,7 +66,7 @@ public AssetDescriptor SelectedAsset } public string AssetBalance => this.SelectedAsset == null ? string.Empty - : ApplicationContext.Instance.CurrentWallet.GetAvailable(this.SelectedAsset.AssetId).ToString(); + : this.applicationContext.CurrentWallet.GetAvailable(this.SelectedAsset.AssetId).ToString(); public string AddressesAndAmounts { @@ -97,7 +101,7 @@ internal void Load(AssetDescriptor asset = null) else { // Add first-class assets to list - foreach (var assetId in ApplicationContext.Instance.CurrentWallet.FindUnspentCoins() + foreach (var assetId in this.applicationContext.CurrentWallet.FindUnspentCoins() .Select(p => p.Output.AssetId).Distinct()) { this.Assets.Add(new AssetDescriptor(assetId)); diff --git a/neo-gui/UI/Transactions/PayToViewModel.cs b/neo-gui/UI/Transactions/PayToViewModel.cs index 0cc35cff..21661306 100644 --- a/neo-gui/UI/Transactions/PayToViewModel.cs +++ b/neo-gui/UI/Transactions/PayToViewModel.cs @@ -11,6 +11,7 @@ namespace Neo.UI.Transactions { public class PayToViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IDispatcher dispatcher; private bool assetSelectionEnabled; @@ -23,8 +24,11 @@ public class PayToViewModel : ViewModelBase private TxOutListBoxItem output; - public PayToViewModel(IDispatcher dispatcher) + public PayToViewModel( + IApplicationContext applicationContext, + IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.dispatcher = dispatcher; this.Assets = new ObservableCollection(); @@ -63,7 +67,7 @@ public AssetDescriptor SelectedAsset } public string AssetBalance => this.SelectedAsset == null ? string.Empty - : ApplicationContext.Instance.CurrentWallet.GetAvailable(this.SelectedAsset.AssetId).ToString(); + : this.applicationContext.CurrentWallet.GetAvailable(this.SelectedAsset.AssetId).ToString(); public bool PayToAddressReadOnly { @@ -159,7 +163,7 @@ public void Load(AssetDescriptor asset = null, UInt160 scriptHash = null) else { // Add first-class assets to list - foreach (var assetId in ApplicationContext.Instance.CurrentWallet.FindUnspentCoins() + foreach (var assetId in this.applicationContext.CurrentWallet.FindUnspentCoins() .Select(p => p.Output.AssetId).Distinct()) { this.Assets.Add(new AssetDescriptor(assetId)); diff --git a/neo-gui/UI/Transactions/SigningViewModel.cs b/neo-gui/UI/Transactions/SigningViewModel.cs index 40801711..453cb6a3 100644 --- a/neo-gui/UI/Transactions/SigningViewModel.cs +++ b/neo-gui/UI/Transactions/SigningViewModel.cs @@ -10,9 +10,10 @@ namespace Neo.UI.Transactions { public class SigningViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; + private string input; private ContractParametersContext output; - private bool broadcastVisible; public string Input @@ -51,6 +52,11 @@ public bool BroadcastVisible public ICommand CloseCommand => new RelayCommand(this.TryClose); + public SigningViewModel(IApplicationContext applicationContext) + { + this.applicationContext = applicationContext; + } + private void Sign() { if (string.IsNullOrEmpty(this.Input)) @@ -70,7 +76,7 @@ private void Sign() return; } - if (!ApplicationContext.Instance.CurrentWallet.Sign(context)) + if (!this.applicationContext.CurrentWallet.Sign(context)) { MessageBox.Show(Strings.SigningFailedKeyNotFoundMessage); return; diff --git a/neo-gui/UI/Voting/ElectionViewModel.cs b/neo-gui/UI/Voting/ElectionViewModel.cs index 08e42fbc..ae544d9b 100644 --- a/neo-gui/UI/Voting/ElectionViewModel.cs +++ b/neo-gui/UI/Voting/ElectionViewModel.cs @@ -3,7 +3,6 @@ using System.Windows.Input; using Neo.Core; using Neo.Cryptography.ECC; -using Neo.UI.Base.Extensions; using Neo.SmartContract; using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; @@ -14,20 +13,23 @@ namespace Neo.UI.Voting { public class ElectionViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private ECPoint selectedBookKeeper; public ElectionViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; - if (ApplicationContext.Instance.CurrentWallet == null) return; + if (this.applicationContext.CurrentWallet == null) return; // Load book keepers - var bookKeepers = ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => - ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey); + var bookKeepers = this.applicationContext.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => + this.applicationContext.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey); this.BookKeepers = new ObservableCollection(bookKeepers); } diff --git a/neo-gui/UI/Wallets/CertificateApplicationViewModel.cs b/neo-gui/UI/Wallets/CertificateApplicationViewModel.cs index 9f2aedac..f166f57d 100644 --- a/neo-gui/UI/Wallets/CertificateApplicationViewModel.cs +++ b/neo-gui/UI/Wallets/CertificateApplicationViewModel.cs @@ -11,17 +11,19 @@ namespace Neo.UI.Wallets { public class CertificateApplicationViewModel : ViewModelBase { - private ECPoint selectedPublicKey; + private readonly IApplicationContext applicationContext; + private ECPoint selectedPublicKey; private string cn; private string c; private string s; private string serialNumber; - public CertificateApplicationViewModel() + public CertificateApplicationViewModel(IApplicationContext applicationContext) { - this.PublicKeys = ApplicationContext.Instance.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => - ApplicationContext.Instance.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey).ToArray(); + this.PublicKeys = this.applicationContext.CurrentWallet.GetContracts().Where(p => p.IsStandard).Select(p => + this.applicationContext.CurrentWallet.GetKey(p.PublicKeyHash).PublicKey).ToArray(); + this.applicationContext = applicationContext; } public ECPoint[] PublicKeys { get; } @@ -128,7 +130,7 @@ private void RequestCertificate() if (saveFileDialog.ShowDialog() != true) return; var point = this.SelectedPublicKey; - var key = ApplicationContext.Instance.CurrentWallet.GetKey(point); + var key = this.applicationContext.CurrentWallet.GetKey(point); var publicKey = point.EncodePoint(false).Skip(1).ToArray(); byte[] privateKey; diff --git a/neo-gui/UI/Wallets/ChangePasswordViewModel.cs b/neo-gui/UI/Wallets/ChangePasswordViewModel.cs index 6d231e86..8db38251 100644 --- a/neo-gui/UI/Wallets/ChangePasswordViewModel.cs +++ b/neo-gui/UI/Wallets/ChangePasswordViewModel.cs @@ -7,6 +7,8 @@ namespace Neo.UI.Wallets { public class ChangePasswordViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; + private string oldPassword; private string newPassword; private string reEnteredNewPassword; @@ -20,6 +22,11 @@ public class ChangePasswordViewModel : ViewModelBase public ICommand CancelCommand => new RelayCommand(this.Cancel); + public ChangePasswordViewModel(IApplicationContext applicationContext) + { + this.applicationContext = applicationContext; + } + public void UpdateOldPassword(string updatedPassword) { this.oldPassword = updatedPassword; @@ -53,7 +60,7 @@ private void ChangePassword() return; } - var changedSuccessfully = ApplicationContext.Instance.CurrentWallet.ChangePassword(this.oldPassword, this.newPassword); + var changedSuccessfully = this.applicationContext.CurrentWallet.ChangePassword(this.oldPassword, this.newPassword); if (changedSuccessfully) { diff --git a/neo-gui/UI/Wallets/ClaimViewModel.cs b/neo-gui/UI/Wallets/ClaimViewModel.cs index 982d571d..0ceafd33 100644 --- a/neo-gui/UI/Wallets/ClaimViewModel.cs +++ b/neo-gui/UI/Wallets/ClaimViewModel.cs @@ -12,6 +12,7 @@ namespace Neo.UI.Wallets { public class ClaimViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private Fixed8 availableGas = Fixed8.Zero; @@ -20,8 +21,10 @@ public class ClaimViewModel : ViewModelBase private bool claimEnabled; public ClaimViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; } @@ -92,7 +95,7 @@ private void Blockchain_PersistCompleted(object sender, Block block) private void CalculateBonusAvailable() { - var bonusAvailable = Blockchain.CalculateBonus(ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); + var bonusAvailable = Blockchain.CalculateBonus(this.applicationContext.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference)); this.AvailableGas = bonusAvailable; if (bonusAvailable == Fixed8.Zero) @@ -103,7 +106,7 @@ private void CalculateBonusAvailable() private void CalculateBonusUnavailable(uint height) { - var unspent = ApplicationContext.Instance.CurrentWallet.FindUnspentCoins() + var unspent = this.applicationContext.CurrentWallet.FindUnspentCoins() .Where(p => p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)) .Select(p => p.Reference); @@ -127,7 +130,7 @@ private void CalculateBonusUnavailable(uint height) private void Claim() { - var claims = ApplicationContext.Instance.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference).ToArray(); + var claims = this.applicationContext.CurrentWallet.GetUnclaimedCoins().Select(p => p.Reference).ToArray(); if (claims.Length == 0) return; @@ -142,7 +145,7 @@ private void Claim() { AssetId = Blockchain.UtilityToken.Hash, Value = Blockchain.CalculateBonus(claims), - ScriptHash = ApplicationContext.Instance.CurrentWallet.GetChangeAddress() + ScriptHash = this.applicationContext.CurrentWallet.GetChangeAddress() } } }; diff --git a/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs b/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs index 1fde0637..bffb8288 100644 --- a/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs +++ b/neo-gui/UI/Wallets/RestoreAccountsViewModel.cs @@ -12,16 +12,19 @@ namespace Neo.UI.Wallets { public class RestoreAccountsViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; public RestoreAccountsViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; - var keys = ApplicationContext.Instance.CurrentWallet.GetKeys(); + var keys = this.applicationContext.CurrentWallet.GetKeys(); - keys = keys.Where(account => ApplicationContext.Instance.CurrentWallet.GetContracts(account.PublicKeyHash).All(contract => !contract.IsStandard)); + keys = keys.Where(account => this.applicationContext.CurrentWallet.GetContracts(account.PublicKeyHash).All(contract => !contract.IsStandard)); this.Accounts = new ObservableCollection(keys.Select(p => VerificationContract.CreateSignatureContract(p.PublicKey)).Select(p => new SelectableVerificationContract(this, p))); } diff --git a/neo-gui/UI/Wallets/TradeViewModel.cs b/neo-gui/UI/Wallets/TradeViewModel.cs index b976bf31..acc562cb 100644 --- a/neo-gui/UI/Wallets/TradeViewModel.cs +++ b/neo-gui/UI/Wallets/TradeViewModel.cs @@ -29,6 +29,7 @@ public class TradeViewModel : ViewModelBase private bool mergeEnabled; private UInt160 scriptHash; + private readonly IApplicationContext applicationContext; public TradeViewModel(IDispatcher dispatcher) { @@ -136,6 +137,10 @@ public bool MergeEnabled public ICommand MergeCommand => new RelayCommand(this.Merge); + public TradeViewModel(IApplicationContext applicationContext) + { + this.applicationContext = applicationContext; + } public override void OnWindowAttached(NeoWindow window) { @@ -151,7 +156,7 @@ private void Initiate() { var txOutputs = this.Items.Select(p => p.ToTxOutput()); - var tx = ApplicationContext.Instance.CurrentWallet.MakeTransaction(new ContractTransaction + var tx = this.applicationContext.CurrentWallet.MakeTransaction(new ContractTransaction { Outputs = txOutputs.ToArray() }, fee: Fixed8.Zero); @@ -195,7 +200,7 @@ private async void Validate() try { - if (inputs.Select(p => Blockchain.Default.GetTransaction(p.PrevHash).Outputs[p.PrevIndex].ScriptHash).Distinct().Any(p => ApplicationContext.Instance.CurrentWallet.ContainsAddress(p))) + if (inputs.Select(p => Blockchain.Default.GetTransaction(p.PrevHash).Outputs[p.PrevIndex].ScriptHash).Distinct().Any(p => this.applicationContext.CurrentWallet.ContainsAddress(p))) { await DialogCoordinator.Instance.ShowMessageAsync(this, Strings.Failed, Strings.TradeFailedInvalidDataMessage); return; @@ -207,7 +212,7 @@ private async void Validate() return; } - outputs = outputs.Where(p => ApplicationContext.Instance.CurrentWallet.ContainsAddress(p.ScriptHash)); + outputs = outputs.Where(p => this.applicationContext.CurrentWallet.ContainsAddress(p.ScriptHash)); var verificationView = new TradeVerificationView(outputs); verificationView.ShowDialog(); @@ -235,13 +240,13 @@ private void Merge() }); } - ApplicationContext.Instance.CurrentWallet.Sign(context); + this.applicationContext.CurrentWallet.Sign(context); if (context.Completed) { context.Verifiable.Scripts = context.GetScripts(); var tx = (ContractTransaction)context.Verifiable; - ApplicationContext.Instance.CurrentWallet.SaveTransaction(tx); + this.applicationContext.CurrentWallet.SaveTransaction(tx); Program.LocalNode.Relay(tx); InformationBox.Show(tx.Hash.ToString(), Strings.TradeSuccessMessage, Strings.TradeSuccessCaption); } @@ -275,7 +280,7 @@ private JObject RequestToJson(ContractTransaction tx) { ["vin"] = tx.Inputs.Select(p => p.ToJson()).ToArray(), ["vout"] = tx.Outputs.Select((p, i) => p.ToJson((ushort)i)).ToArray(), - ["change_address"] = Wallet.ToAddress(ApplicationContext.Instance.CurrentWallet.GetChangeAddress()) + ["change_address"] = Wallet.ToAddress(this.applicationContext.CurrentWallet.GetChangeAddress()) }; return json; } diff --git a/neo-gui/UI/Wallets/TransferViewModel.cs b/neo-gui/UI/Wallets/TransferViewModel.cs index 2066bce1..15d977a1 100644 --- a/neo-gui/UI/Wallets/TransferViewModel.cs +++ b/neo-gui/UI/Wallets/TransferViewModel.cs @@ -18,13 +18,16 @@ namespace Neo.UI.Wallets { public class TransferViewModel : ViewModelBase { + private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private string remark = string.Empty; public TransferViewModel( + IApplicationContext applicationContext, IMessagePublisher messagePublisher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.Items = new ObservableCollection(); @@ -94,7 +97,7 @@ private Transaction GenerateTransaction() } else { - var addresses = ApplicationContext.Instance.CurrentWallet.GetAddresses().ToArray(); + var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); var sAttributes = new HashSet(); using (var builder = new ScriptBuilder()) { @@ -183,7 +186,7 @@ private Transaction GenerateTransaction() if (tx is ContractTransaction ctx) { - tx = ApplicationContext.Instance.CurrentWallet.MakeTransaction(ctx); + tx = this.applicationContext.CurrentWallet.MakeTransaction(ctx); } return tx; From 78475ce1655d81845033c9fc40e932a0103fb1d7 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Thu, 16 Nov 2017 01:35:28 +0100 Subject: [PATCH 17/22] First atempt to wire the BlockChainController in the app. Cannot connect to the peers --- neo-gui/App.config | 13 +- neo-gui/App.xaml.cs | 6 +- neo-gui/ApplicationContext.cs | 6 - neo-gui/Controllers/BlockChainController.cs | 37 +- .../ControllersRegistrationModule.cs | 5 + neo-gui/Controllers/IBlockChainController.cs | 1 + neo-gui/Controllers/IWalletController.cs | 4 +- neo-gui/Controllers/WalletController.cs | 12 +- neo-gui/NeoGuiRegistrationModule.cs | 17 + neo-gui/Program.cs | 9 +- .../ContractParametersViewModel.cs | 2 +- neo-gui/UI/Home/AccountsViewModel.cs | 16 +- neo-gui/UI/Home/AssetsViewModel.cs | 18 +- neo-gui/UI/Home/HomeViewModel.cs | 412 +++++++++--------- neo-gui/UI/Transactions/SigningViewModel.cs | 2 +- neo-gui/UI/Wallets/TradeViewModel.cs | 2 +- neo-gui/neo-gui.csproj | 1 + 17 files changed, 298 insertions(+), 265 deletions(-) create mode 100644 neo-gui/NeoGuiRegistrationModule.cs diff --git a/neo-gui/App.config b/neo-gui/App.config index b77e06ec..1a3bd84d 100644 --- a/neo-gui/App.config +++ b/neo-gui/App.config @@ -1,12 +1,12 @@ - + -
+
- + @@ -14,19 +14,18 @@ True - + True - + - + diff --git a/neo-gui/App.xaml.cs b/neo-gui/App.xaml.cs index 239722f3..f7f77679 100644 --- a/neo-gui/App.xaml.cs +++ b/neo-gui/App.xaml.cs @@ -21,7 +21,10 @@ internal App(bool updateIsRequired = false) BuildContainer(); - this.MainWindow = updateIsRequired ? (Window) new UpdateView() : new HomeView(); + var blockChainController = ApplicationContext.Instance.ContainerLifetimeScope.Resolve(typeof(IBlockChainController)) as IBlockChainController; + blockChainController.StartLocalNode(); + + this.MainWindow = updateIsRequired ? (Window)new UpdateView() : new HomeView(); } protected override void OnStartup(StartupEventArgs e) @@ -37,6 +40,7 @@ private static void BuildContainer() { var autoFacContainerBuilder = new ContainerBuilder(); + autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); diff --git a/neo-gui/ApplicationContext.cs b/neo-gui/ApplicationContext.cs index aae69e31..9f0ff7de 100644 --- a/neo-gui/ApplicationContext.cs +++ b/neo-gui/ApplicationContext.cs @@ -10,13 +10,7 @@ public class ApplicationContext : IApplicationContext #region Singleton Pattern private static readonly Lazy lazyInstance = new Lazy(() => new ApplicationContext()); - // Prevent other classes from calling constructor - private ApplicationContext() - { - } - public static ApplicationContext Instance => lazyInstance.Value; - #endregion public ILifetimeScope ContainerLifetimeScope { get; set; } diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index e8da554e..b0f5f351 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -9,6 +9,7 @@ using Neo.Implementations.Blockchains.LevelDB; using Neo.Implementations.Wallets.EntityFramework; using Neo.IO; +using Neo.Network; using Neo.Properties; using Neo.SmartContract; using Neo.UI; @@ -27,6 +28,8 @@ public class BlockChainController : IBlockChainController private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; + private LocalNode localNode; + private DateTime persistenceTime = DateTime.MinValue; private Timer uiUpdateTimer; private bool balanceChanged; @@ -44,6 +47,17 @@ public BlockChainController( this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; + } + #endregion + + #region IBlockChainController implementation + public void StartLocalNode() + { + this.localNode = new LocalNode + { + UpnpEnabled = true + }; + this.applicationContext.LocalNode = this.localNode; Task.Run(() => { @@ -54,31 +68,17 @@ public BlockChainController( Blockchain.PersistCompleted += this.BlockchainPersistCompleted; // Start node - this.applicationContext.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); + this.localNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); }); - - if (this.uiUpdateTimer != null) - { - // Stop previous timer - this.uiUpdateTimer.Stop(); - - this.uiUpdateTimer.Elapsed -= this.UpdateWallet; - - this.uiUpdateTimer.Dispose(); - - this.uiUpdateTimer = null; - } - - var timer = new Timer + this.uiUpdateTimer = new Timer { Interval = 500, Enabled = true, AutoReset = true }; - timer.Elapsed += this.UpdateWallet; - this.uiUpdateTimer = timer; + this.uiUpdateTimer.Elapsed += this.UpdateWallet; } #endregion @@ -88,7 +88,6 @@ private void UpdateWallet(object sender, ElapsedEventArgs e) var persistenceSpan = DateTime.UtcNow - this.persistenceTime; this.UpdateBlockProgress(persistenceSpan); - this.UpdateBalances(persistenceSpan); } @@ -113,7 +112,7 @@ private void UpdateBlockProgress(TimeSpan persistenceSpan) } var blockHeight = $"{GetWalletHeight()}/{Blockchain.Default.Height}/{Blockchain.Default.HeaderHeight}"; - var nodeCount = this.applicationContext.LocalNode.RemoteNodeCount; + var nodeCount = this.localNode.RemoteNodeCount; var blockStatus = $"{Strings.WaitingForNextBlock}:"; var blockProgressMessage = new BlockProgressMessage( diff --git a/neo-gui/Controllers/ControllersRegistrationModule.cs b/neo-gui/Controllers/ControllersRegistrationModule.cs index f01b55b1..7e78dc9c 100644 --- a/neo-gui/Controllers/ControllersRegistrationModule.cs +++ b/neo-gui/Controllers/ControllersRegistrationModule.cs @@ -11,6 +11,11 @@ protected override void Load(ContainerBuilder builder) .As() .SingleInstance(); + builder + .RegisterType() + .As() + .SingleInstance(); + base.Load(builder); } } diff --git a/neo-gui/Controllers/IBlockChainController.cs b/neo-gui/Controllers/IBlockChainController.cs index 1670c806..97ab5e4f 100644 --- a/neo-gui/Controllers/IBlockChainController.cs +++ b/neo-gui/Controllers/IBlockChainController.cs @@ -8,5 +8,6 @@ namespace Neo.Controllers { public interface IBlockChainController { + void StartLocalNode(); } } diff --git a/neo-gui/Controllers/IWalletController.cs b/neo-gui/Controllers/IWalletController.cs index d140406b..aee9db79 100644 --- a/neo-gui/Controllers/IWalletController.cs +++ b/neo-gui/Controllers/IWalletController.cs @@ -12,10 +12,12 @@ public interface IWalletController void CreateWallet(string walletPath, string password); - void OpenWallet(string walletPath, string password); + void OpenWallet(string walletPath, string password, bool repairMode); IEnumerable GetAddresses(); IEnumerable GetCoins(); + + VerificationContract GetContract(UInt160 scriptHash); } } diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index 80c947bc..9fe8bd71 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -52,10 +52,8 @@ public void CreateWallet(string walletPath, string password) Settings.Default.Save(); } - public void OpenWallet(string walletPath, string password) + public void OpenWallet(string walletPath, string password, bool repairMode) { - var openWalletDialogResult = this.dialogHelper.ShowDialog("OpenWalletDialog"); - // [TODO] why this verification? Why the magic string? if (UserWallet.GetVersion(walletPath) < Version.Parse("1.3.5")) { @@ -76,7 +74,7 @@ public void OpenWallet(string walletPath, string password) return; } - if (openWalletDialogResult.Result.OpenInRepairMode) + if (repairMode) { userWallet.Rebuild(); } @@ -96,6 +94,12 @@ public IEnumerable GetCoins() // TODO - ISSUE #37 [AboimPinto]: at this point the return should not be a object from the NEO assemblies but a DTO only know by the application with only the necessary fields. return this.currentWallet.GetCoins(); } + + public VerificationContract GetContract(UInt160 scriptHash) + { + // TODO - ISSUE #37 [AboimPinto]: at this point the return should not be a object from the NEO assemblies but a DTO only know by the application with only the necessary fields. + return this.currentWallet.GetContract(scriptHash); + } #endregion #region Private Methods diff --git a/neo-gui/NeoGuiRegistrationModule.cs b/neo-gui/NeoGuiRegistrationModule.cs new file mode 100644 index 00000000..1303f0e8 --- /dev/null +++ b/neo-gui/NeoGuiRegistrationModule.cs @@ -0,0 +1,17 @@ +using Autofac; + +namespace Neo +{ + public class NeoGuiRegistrationModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterType() + .As() + .SingleInstance(); + + base.Load(builder); + } + } +} diff --git a/neo-gui/Program.cs b/neo-gui/Program.cs index 6f9d34c4..dfd85dd9 100644 --- a/neo-gui/Program.cs +++ b/neo-gui/Program.cs @@ -2,15 +2,12 @@ using System.IO; using Neo.Core; using Neo.Implementations.Blockchains.LevelDB; -using Neo.Network; using Neo.Properties; namespace Neo { internal static class Program { - public static LocalNode LocalNode; - [STAThread] public static void Main() { @@ -25,6 +22,7 @@ public static void Main() return; } + // TODO: [AboimPinto] All this code related with NEO BlockChain should be inside the BlockChainController. // Install root certificate if (!RootCertificate.InstallCertificate()) return; @@ -32,12 +30,7 @@ public static void Main() PeerState.TryLoad(); using (Blockchain.RegisterBlockchain(new LevelDBBlockchain(Settings.Default.DataDirectoryPath))) // Setup blockchain - using (LocalNode = new LocalNode()) // Setup node { - LocalNode.UpnpEnabled = true; - - ApplicationContext.Instance.LocalNode = LocalNode; - // Start GUI normally var app = new App(); app.Run(); diff --git a/neo-gui/UI/Development/ContractParametersViewModel.cs b/neo-gui/UI/Development/ContractParametersViewModel.cs index e4ef0dec..9930c80f 100644 --- a/neo-gui/UI/Development/ContractParametersViewModel.cs +++ b/neo-gui/UI/Development/ContractParametersViewModel.cs @@ -202,7 +202,7 @@ private void Broadcast() var inventory = (IInventory) context.Verifiable; - Program.LocalNode.Relay(inventory); + this.applicationContext.LocalNode.Relay(inventory); InformationBox.Show(inventory.Hash.ToString(), Strings.RelaySuccessText, Strings.RelaySuccessTitle); } diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index 3cccbfcc..8e92fe98 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -6,6 +6,7 @@ using System.Windows.Input; using MahApps.Metro.Controls.Dialogs; +using Neo.Controllers; using Neo.Core; using Neo.Properties; using Neo.UI.Accounts; @@ -35,6 +36,7 @@ public class AccountsViewModel : IMessageHandler { private readonly IApplicationContext applicationContext; + private readonly IWalletController walletController; private readonly IMessageSubscriber messageSubscriber; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; @@ -43,11 +45,13 @@ public class AccountsViewModel : public AccountsViewModel( IApplicationContext applicationContext, + IWalletController walletController, IMessageSubscriber messageSubscriber, IMessagePublisher messagePublisher, IDispatcher dispatcher) { this.applicationContext = applicationContext; + this.walletController = walletController; this.messageSubscriber = messageSubscriber; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -80,7 +84,7 @@ public AccountItem SelectedAccount } } - public bool MenuItemsEnabled => this.applicationContext.CurrentWallet != null; + public bool MenuItemsEnabled => this.walletController.IsWalletOpen; public bool ViewPrivateKeyEnabled => this.SelectedAccount != null && @@ -313,7 +317,9 @@ private async void DeleteAccount() #region IMessageHandler implementation public void HandleMessage(AccountBalancesChangedMessage message) { - var coins = this.applicationContext.CurrentWallet?.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent)).ToList(); + var coins = this.walletController.GetCoins() + .Where(p => !p.State.HasFlag(CoinState.Spent)) + .ToList(); if (coins == null) return; @@ -344,12 +350,12 @@ public void HandleMessage(ClearAccountsMessage message) public void HandleMessage(LoadWalletAddressesMessage message) { - if (this.applicationContext.CurrentWallet == null) return; + if (!this.walletController.IsWalletOpen) return; // Load accounts - foreach (var scriptHash in this.applicationContext.CurrentWallet.GetAddresses()) + foreach (var scriptHash in this.walletController.GetAddresses()) { - var contract = this.applicationContext.CurrentWallet.GetContract(scriptHash); + var contract = this.walletController.GetContract(scriptHash); if (contract == null) { this.AddAddress(scriptHash); diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index 53cbe179..2c42fa08 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -36,18 +36,6 @@ public class AssetsViewModel : private AssetItem selectedAsset; #endregion - public AssetsViewModel( - IApplicationContext applicationContext, - IMessagePublisher messagePublisher) - { - this.applicationContext = applicationContext; - this.messagePublisher = messagePublisher; - - this.certificateQueryResultCache = new Dictionary(); - - this.Assets = new ConcurrentObservableCollection(); - } - #region Properties public ConcurrentObservableCollection Assets { get; } @@ -100,11 +88,11 @@ public bool ViewCertificateEnabled #region Constructor public AssetsViewModel(IMessagePublisher messagePublisher, IDispatcher dispatcher) { - this.certificateQueryResultCache = new Dictionary(); - - this.Assets = new ConcurrentObservableCollection(); this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; + + this.certificateQueryResultCache = new Dictionary(); + this.Assets = new ConcurrentObservableCollection(); } #endregion diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 1f18e8c9..dd333232 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -35,6 +35,7 @@ using Neo.UI.Voting; using Neo.UI.Base.Messages; using Timer = System.Timers.Timer; +using Neo.Controllers; namespace Neo.UI.Home { @@ -43,9 +44,11 @@ public class HomeViewModel : ILoadable, IMessageHandler, IMessageHandler, - IMessageHandler + IMessageHandler, + IMessageHandler { #region Private Fields + private readonly IWalletController walletController; private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IMessageSubscriber messageSubscriber; @@ -65,29 +68,57 @@ public class HomeViewModel : private readonly object uiUpdateLock = new object(); private Timer uiUpdateTimer; + private string blockHeight; + private int nodeCount; + private string blockStatus; #endregion #region Constructor public HomeViewModel( + IWalletController walletController, IApplicationContext applicationContext, IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, IDispatcher dispatcher) { + this.walletController = walletController; this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; - this.SetupUIUpdateTimer(); + //this.SetupUIUpdateTimer(); } #endregion #region Public Properties public bool WalletIsOpen => this.applicationContext.CurrentWallet != null; - public string BlockHeight => $"{GetWalletHeight()}/{Blockchain.Default.Height}/{Blockchain.Default.HeaderHeight}"; - public int NodeCount => Program.LocalNode.RemoteNodeCount; + public string BlockHeight + { + get + { + return this.blockHeight; + } + set + { + this.blockHeight = value; + this.NotifyPropertyChanged(nameof(this.BlockHeight)); + } + } + + public int NodeCount + { + get + { + return this.nodeCount; + } + set + { + this.nodeCount = value; + this.NotifyPropertyChanged(nameof(this.NodeCount)); + } + } public bool BlockProgressIndeterminate { @@ -195,7 +226,18 @@ public bool NewVersionVisible } // TODO Update property to return actual status - public string BlockStatus => Strings.WaitingForNextBlock + ":"; + public string BlockStatus + { + get + { + return this.blockStatus; + } + set + { + this.blockStatus = value; + this.NotifyPropertyChanged(nameof(this.BlockStatus)); + } + } #endregion New Version Properties @@ -322,20 +364,20 @@ private void ImportBlocks(Stream stream) blockchain.VerifyBlocks = true; } - private void Load() - { - Task.Run(() => - { - CheckForNewerVersion(); + //private void Load() + //{ + // Task.Run(() => + // { + // CheckForNewerVersion(); - ImportBlocksIfRequired(); + // ImportBlocksIfRequired(); - Blockchain.PersistCompleted += Blockchain_PersistCompleted; + // Blockchain.PersistCompleted += Blockchain_PersistCompleted; - // Start node - Program.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); - }); - } + // // Start node + // Program.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); + // }); + //} public void Close() { @@ -358,149 +400,149 @@ private void CheckForNewerVersion() #region UI Update Methods - private void SetupUIUpdateTimer() - { - if (this.uiUpdateTimer != null) - { - // Stop previous timer - this.uiUpdateTimer.Stop(); - - this.uiUpdateTimer.Elapsed -= this.UpdateUI; - - this.uiUpdateTimer.Dispose(); - - this.uiUpdateTimer = null; - } - - var timer = new Timer - { - Interval = 500, - Enabled = true, - AutoReset = true - }; - - timer.Elapsed += this.UpdateUI; - - this.uiUpdateTimer = timer; - - // Start timer - this.uiUpdateTimer.Start(); - } - - private void UpdateUI(object sender, ElapsedEventArgs e) - { - // Only update UI if it is not already being updated - if (!Monitor.TryEnter(this.uiUpdateLock)) return; - - try - { - var persistenceSpan = DateTime.UtcNow - this.persistenceTime; - - this.UpdateBlockProgress(persistenceSpan); - - this.UpdateBalances(persistenceSpan); - } - finally - { - Monitor.Exit(this.uiUpdateLock); - } - } - - private void UpdateBlockProgress(TimeSpan persistenceSpan) - { - if (persistenceSpan < TimeSpan.Zero) persistenceSpan = TimeSpan.Zero; - - if (persistenceSpan > Blockchain.TimePerBlock) - { - this.BlockProgressIndeterminate = true; - } - else - { - this.BlockProgressIndeterminate = true; - this.BlockProgress = persistenceSpan.Seconds; - } - - NotifyPropertyChanged(nameof(this.BlockHeight)); - NotifyPropertyChanged(nameof(this.NodeCount)); - NotifyPropertyChanged(nameof(this.BlockStatus)); - } - - private void UpdateBalances(TimeSpan persistenceSpan) - { - if (this.applicationContext.CurrentWallet == null) return; - - this.UpdateAssetBalances(); - - this.UpdateNEP5TokenBalances(persistenceSpan); - } + //private void SetupUIUpdateTimer() + //{ + // if (this.uiUpdateTimer != null) + // { + // // Stop previous timer + // this.uiUpdateTimer.Stop(); + + // this.uiUpdateTimer.Elapsed -= this.UpdateUI; + + // this.uiUpdateTimer.Dispose(); + + // this.uiUpdateTimer = null; + // } + + // var timer = new Timer + // { + // Interval = 500, + // Enabled = true, + // AutoReset = true + // }; + + // timer.Elapsed += this.UpdateUI; + + // this.uiUpdateTimer = timer; + + // // Start timer + // this.uiUpdateTimer.Start(); + //} + + //private void UpdateUI(object sender, ElapsedEventArgs e) + //{ + // // Only update UI if it is not already being updated + // if (!Monitor.TryEnter(this.uiUpdateLock)) return; + + // try + // { + // var persistenceSpan = DateTime.UtcNow - this.persistenceTime; + + // this.UpdateBlockProgress(persistenceSpan); + + // this.UpdateBalances(persistenceSpan); + // } + // finally + // { + // Monitor.Exit(this.uiUpdateLock); + // } + //} + + //private void UpdateBlockProgress(TimeSpan persistenceSpan) + //{ + // if (persistenceSpan < TimeSpan.Zero) persistenceSpan = TimeSpan.Zero; + + // if (persistenceSpan > Blockchain.TimePerBlock) + // { + // this.BlockProgressIndeterminate = true; + // } + // else + // { + // this.BlockProgressIndeterminate = true; + // this.BlockProgress = persistenceSpan.Seconds; + // } + + // NotifyPropertyChanged(nameof(this.BlockHeight)); + // NotifyPropertyChanged(nameof(this.NodeCount)); + // NotifyPropertyChanged(nameof(this.BlockStatus)); + //} + + //private void UpdateBalances(TimeSpan persistenceSpan) + //{ + // if (this.applicationContext.CurrentWallet == null) return; + + // this.UpdateAssetBalances(); + + // this.UpdateNEP5TokenBalances(persistenceSpan); + //} - private void UpdateAssetBalances() - { - if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; - - this.messagePublisher.Publish(new AccountBalancesChangedMessage()); - this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); - } - - - private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) - { - if (!checkNep5Balance) return; - - if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; - - // Update balances - var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); - foreach (var s in Settings.Default.NEP5Watched) - { - var scriptHash = UInt160.Parse(s); - byte[] script; - using (var builder = new ScriptBuilder()) - { - foreach (var address in addresses) - { - builder.EmitAppCall(scriptHash, "balanceOf", address); - } - builder.Emit(OpCode.DEPTH, OpCode.PACK); - builder.EmitAppCall(scriptHash, "decimals"); - builder.EmitAppCall(scriptHash, "name"); - script = builder.ToArray(); - } - - var engine = ApplicationEngine.Run(script); - if (engine.State.HasFlag(VMState.FAULT)) continue; - - var name = engine.EvaluationStack.Pop().GetString(); - var decimals = (byte)engine.EvaluationStack.Pop().GetBigInteger(); - var amount = engine.EvaluationStack.Pop().GetArray().Aggregate(BigInteger.Zero, (x, y) => x + y.GetBigInteger()); - if (amount == 0) continue; - var balance = new BigDecimal(amount, decimals); - var valueText = balance.ToString(); - - await this.dispatcher.InvokeOnMainUIThread(() => - { - var item = (AssetItem) null; //this.GetAsset(scriptHash); - - if (item != null) - { - item.Value = valueText; - } - else - { - var assetItem = new AssetItem - { - Name = name, - Type = "NEP-5", - Issuer = $"ScriptHash:{scriptHash}", - Value = valueText, - }; - - this.messagePublisher.Publish(new AddAssetMessage(assetItem)); - } - }); - } - checkNep5Balance = false; - } + //private void UpdateAssetBalances() + //{ + // if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; + + // this.messagePublisher.Publish(new AccountBalancesChangedMessage()); + // this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); + //} + + + //private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) + //{ + // if (!checkNep5Balance) return; + + // if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; + + // // Update balances + // var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); + // foreach (var s in Settings.Default.NEP5Watched) + // { + // var scriptHash = UInt160.Parse(s); + // byte[] script; + // using (var builder = new ScriptBuilder()) + // { + // foreach (var address in addresses) + // { + // builder.EmitAppCall(scriptHash, "balanceOf", address); + // } + // builder.Emit(OpCode.DEPTH, OpCode.PACK); + // builder.EmitAppCall(scriptHash, "decimals"); + // builder.EmitAppCall(scriptHash, "name"); + // script = builder.ToArray(); + // } + + // var engine = ApplicationEngine.Run(script); + // if (engine.State.HasFlag(VMState.FAULT)) continue; + + // var name = engine.EvaluationStack.Pop().GetString(); + // var decimals = (byte)engine.EvaluationStack.Pop().GetBigInteger(); + // var amount = engine.EvaluationStack.Pop().GetArray().Aggregate(BigInteger.Zero, (x, y) => x + y.GetBigInteger()); + // if (amount == 0) continue; + // var balance = new BigDecimal(amount, decimals); + // var valueText = balance.ToString(); + + // await this.dispatcher.InvokeOnMainUIThread(() => + // { + // var item = (AssetItem) null; //this.GetAsset(scriptHash); + + // if (item != null) + // { + // item.Value = valueText; + // } + // else + // { + // var assetItem = new AssetItem + // { + // Name = name, + // Type = "NEP-5", + // Issuer = $"ScriptHash:{scriptHash}", + // Value = valueText, + // }; + + // this.messagePublisher.Publish(new AddAssetMessage(assetItem)); + // } + // }); + // } + // checkNep5Balance = false; + //} #endregion UI Update Methods @@ -515,50 +557,18 @@ private void CreateWallet() if (string.IsNullOrEmpty(walletPath) || string.IsNullOrEmpty(password)) return; - var wallet = UserWallet.Create(walletPath, password); - - this.ChangeWallet(wallet); - Settings.Default.LastWalletPath = walletPath; - Settings.Default.Save(); + this.walletController.CreateWallet(walletPath, password); } - private async void OpenWallet() + private void OpenWallet() { var view = new OpenWalletView(); view.ShowDialog(); + //var openWalletDialogResult = this.dialogHelper.ShowDialog("OpenWalletDialog"); if (!view.GetWalletOpenInfo(out var walletPath, out var password, out var repairMode)) return; - - if (UserWallet.GetVersion(walletPath) < Version.Parse("1.3.5")) - { - var migrateApproved = await DialogCoordinator.Instance.ShowMessageAsync(this, - Strings.MigrateWalletCaption, Strings.MigrateWalletMessage, - MessageDialogStyle.AffirmativeAndNegative); - - if (migrateApproved != MessageDialogResult.Affirmative) return; - var pathOld = Path.ChangeExtension(walletPath, ".old.db3"); - var pathNew = Path.ChangeExtension(walletPath, ".new.db3"); - UserWallet.Migrate(walletPath, pathNew); - File.Move(walletPath, pathOld); - File.Move(pathNew, walletPath); - - await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, $"{Strings.MigrateWalletSucceedMessage}\n{pathOld}"); - } - UserWallet wallet; - try - { - wallet = UserWallet.Open(walletPath, password); - } - catch (CryptographicException) - { - await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, Strings.PasswordIncorrect); - return; - } - if (repairMode) wallet.Rebuild(); - ChangeWallet(wallet); - Settings.Default.LastWalletPath = walletPath; - Settings.Default.Save(); + this.walletController.OpenWallet(walletPath, password, repairMode); } public void CloseWallet() @@ -702,7 +712,7 @@ public void OnLoad() { this.messageSubscriber.Subscribe(this); - this.Load(); + //this.Load(); } #endregion @@ -731,6 +741,7 @@ public void HandleMessage(InvokeContractMessage message) var invokeContractView = new InvokeContractView(message.Transaction); invokeContractView.ShowDialog(); } + // TODO Move these message handlers to a more appropriate place, they don't need to be in HomeViewModel public void HandleMessage(SignTransactionAndShowInformationMessage message) { @@ -759,7 +770,7 @@ public void HandleMessage(SignTransactionAndShowInformationMessage message) { context.Verifiable.Scripts = context.GetScripts(); this.applicationContext.CurrentWallet.SaveTransaction(transaction); - Program.LocalNode.Relay(transaction); + this.applicationContext.LocalNode.Relay(transaction); InformationBox.Show(transaction.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); } else @@ -767,6 +778,15 @@ public void HandleMessage(SignTransactionAndShowInformationMessage message) InformationBox.Show(context.ToString(), Strings.IncompletedSignatureMessage, Strings.IncompletedSignatureTitle); } } + + public void HandleMessage(BlockProgressMessage message) + { + this.BlockProgressIndeterminate = message.BlockProgressIndeterminate; + this.BlockProgress = message.BlockProgress; + this.BlockHeight = message.BlockHeight; + this.NodeCount = message.NodeCount; + this.BlockStatus = message.BlockStatus; + } #endregion } } \ No newline at end of file diff --git a/neo-gui/UI/Transactions/SigningViewModel.cs b/neo-gui/UI/Transactions/SigningViewModel.cs index 453cb6a3..2086595a 100644 --- a/neo-gui/UI/Transactions/SigningViewModel.cs +++ b/neo-gui/UI/Transactions/SigningViewModel.cs @@ -105,7 +105,7 @@ private void Broadcast() var inventory = (IInventory) this.output.Verifiable; - Program.LocalNode.Relay(inventory); + this.applicationContext.LocalNode.Relay(inventory); InformationBox.Show(inventory.Hash.ToString(), Strings.RelaySuccessText, Strings.RelaySuccessTitle); diff --git a/neo-gui/UI/Wallets/TradeViewModel.cs b/neo-gui/UI/Wallets/TradeViewModel.cs index acc562cb..484d7f27 100644 --- a/neo-gui/UI/Wallets/TradeViewModel.cs +++ b/neo-gui/UI/Wallets/TradeViewModel.cs @@ -247,7 +247,7 @@ private void Merge() context.Verifiable.Scripts = context.GetScripts(); var tx = (ContractTransaction)context.Verifiable; this.applicationContext.CurrentWallet.SaveTransaction(tx); - Program.LocalNode.Relay(tx); + this.applicationContext.LocalNode.Relay(tx); InformationBox.Show(tx.Hash.ToString(), Strings.TradeSuccessMessage, Strings.TradeSuccessCaption); } else diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 5b371e4a..2e0f9b15 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -273,6 +273,7 @@ + From 9a1364e0f34f9206eb517a750770f5375e6b0727 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Thu, 16 Nov 2017 11:25:00 +0100 Subject: [PATCH 18/22] * Added some technical debts issues * Wallet and blockchain code removed from HomeViewModel * ApplicationContext injection removed from HomeViewModel --- neo-gui/Controllers/BlockChainController.cs | 5 + neo-gui/Controllers/IBlockChainController.cs | 8 +- neo-gui/Controllers/IWalletController.cs | 10 + neo-gui/Controllers/WalletController.cs | 26 +- neo-gui/UI/Home/HomeView.xaml.cs | 8 +- neo-gui/UI/Home/HomeViewModel.cs | 371 +------------------ 6 files changed, 61 insertions(+), 367 deletions(-) diff --git a/neo-gui/Controllers/BlockChainController.cs b/neo-gui/Controllers/BlockChainController.cs index b0f5f351..fbfe11ca 100644 --- a/neo-gui/Controllers/BlockChainController.cs +++ b/neo-gui/Controllers/BlockChainController.cs @@ -80,6 +80,11 @@ public void StartLocalNode() this.uiUpdateTimer.Elapsed += this.UpdateWallet; } + + public void Relay(Transaction transaction) + { + this.localNode.Relay(transaction); + } #endregion #region Private Methods diff --git a/neo-gui/Controllers/IBlockChainController.cs b/neo-gui/Controllers/IBlockChainController.cs index 97ab5e4f..38865851 100644 --- a/neo-gui/Controllers/IBlockChainController.cs +++ b/neo-gui/Controllers/IBlockChainController.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Neo.Core; namespace Neo.Controllers { public interface IBlockChainController { void StartLocalNode(); + + void Relay(Transaction transaction); } } diff --git a/neo-gui/Controllers/IWalletController.cs b/neo-gui/Controllers/IWalletController.cs index aee9db79..cf591d8d 100644 --- a/neo-gui/Controllers/IWalletController.cs +++ b/neo-gui/Controllers/IWalletController.cs @@ -1,5 +1,7 @@ using System.Collections; using System.Collections.Generic; +using Neo.Core; +using Neo.SmartContract; using Neo.Wallets; namespace Neo.Controllers @@ -14,6 +16,14 @@ public interface IWalletController void OpenWallet(string walletPath, string password, bool repairMode); + void CloseWallet(); + + void RebuildWalletIndexes(); + + void SaveTransaction(Transaction transaction); + + void Sign(ContractParametersContext context); + IEnumerable GetAddresses(); IEnumerable GetCoins(); diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index 9fe8bd71..1c4f0098 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.IO; using System.Security.Cryptography; +using Neo.Core; using Neo.DialogResults; using Neo.Helpers; using Neo.Implementations.Wallets.EntityFramework; using Neo.Properties; +using Neo.SmartContract; using Neo.UI.Base.Messages; using Neo.UI.Messages; using Neo.Wallets; @@ -84,6 +86,26 @@ public void OpenWallet(string walletPath, string password, bool repairMode) Settings.Default.Save(); } + public void CloseWallet() + { + this.SetCurrentWallet(null); + } + + public void RebuildWalletIndexes() + { + this.currentWallet.Rebuild(); + } + + public void SaveTransaction(Transaction transaction) + { + this.currentWallet.SaveTransaction(transaction); + } + + public void Sign(ContractParametersContext context) + { + this.currentWallet.Sign(context); + } + public IEnumerable GetAddresses() { return this.currentWallet.GetAddresses(); @@ -105,7 +127,7 @@ public VerificationContract GetContract(UInt160 scriptHash) #region Private Methods private void SetCurrentWallet(UserWallet wallet) { - if (this.currentWallet != null) + if (this.IsWalletOpen) { // Dispose current wallet this.currentWallet.BalanceChanged -= this.CurrentWalletBalanceChanged; @@ -119,7 +141,7 @@ private void SetCurrentWallet(UserWallet wallet) this.currentWallet = wallet; - if (this.currentWallet != null) + if (this.IsWalletOpen) { // Setup wallet var transactions = this.currentWallet.LoadTransactions(); diff --git a/neo-gui/UI/Home/HomeView.xaml.cs b/neo-gui/UI/Home/HomeView.xaml.cs index df5585d4..93b97d2d 100644 --- a/neo-gui/UI/Home/HomeView.xaml.cs +++ b/neo-gui/UI/Home/HomeView.xaml.cs @@ -7,18 +7,16 @@ namespace Neo.UI.Home /// public partial class HomeView { - private readonly HomeViewModel viewModel; - public HomeView() { InitializeComponent(); - - this.viewModel = this.DataContext as HomeViewModel; } public void Window_Closing(object sender, CancelEventArgs e) { - this.viewModel?.Close(); + // TODO - Issue #42 [AboimPinto] - the closing of the windows event should go to ViewModel through the IUnloadable interface. + //var viewModel = this.DataContext as HomeViewModel; + //viewModel?.Close(); } } } \ No newline at end of file diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index dd333232..9b61141a 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -1,29 +1,14 @@ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Numerics; using System.Reflection; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -using System.Timers; using System.Windows; using System.Windows.Input; using MahApps.Metro.Controls.Dialogs; -using Neo.Core; -using Neo.Implementations.Blockchains.LevelDB; -using Neo.Implementations.Wallets.EntityFramework; -using Neo.IO; using Neo.Properties; using Neo.SmartContract; -using Neo.VM; using Neo.UI.Assets; using Neo.UI.Base.Dialogs; using Neo.UI.Base.Dispatching; -using Neo.UI.Base.Helpers; using Neo.UI.Base.MVVM; using Neo.UI.Contracts; using Neo.UI.Development; @@ -34,7 +19,6 @@ using Neo.UI.Wallets; using Neo.UI.Voting; using Neo.UI.Base.Messages; -using Timer = System.Timers.Timer; using Neo.Controllers; namespace Neo.UI.Home @@ -48,26 +32,20 @@ public class HomeViewModel : IMessageHandler { #region Private Fields + private readonly IBlockChainController blockChainController; private readonly IWalletController walletController; - private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private readonly IMessageSubscriber messageSubscriber; private readonly IDispatcher dispatcher; private bool balanceChanged = false; - private bool checkNep5Balance = false; - private DateTime persistenceTime = DateTime.MinValue; - private bool blockProgressIndeterminate; private int blockProgress; - private string newVersionLabel; private bool newVersionVisible; - private readonly object uiUpdateLock = new object(); - private Timer uiUpdateTimer; private string blockHeight; private int nodeCount; private string blockStatus; @@ -75,24 +53,22 @@ public class HomeViewModel : #region Constructor public HomeViewModel( + IBlockChainController blockChainController, IWalletController walletController, - IApplicationContext applicationContext, IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, IDispatcher dispatcher) { + this.blockChainController = blockChainController; this.walletController = walletController; - this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; - - //this.SetupUIUpdateTimer(); } #endregion #region Public Properties - public bool WalletIsOpen => this.applicationContext.CurrentWallet != null; + public bool WalletIsOpen => this.walletController.IsWalletOpen; public string BlockHeight { @@ -241,311 +217,6 @@ public string BlockStatus #endregion New Version Properties - #region Wallet Methods - - private void Blockchain_PersistCompleted(object sender, Block block) - { - this.persistenceTime = DateTime.UtcNow; - if (this.applicationContext.CurrentWallet != null) - { - this.checkNep5Balance = true; - - var coins = this.applicationContext.CurrentWallet.GetCoins(); - if (coins.Any(coin => !coin.State.HasFlag(CoinState.Spent) && - coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))) - { - balanceChanged = true; - } - } - CurrentWallet_TransactionsChanged(Enumerable.Empty()); - } - - private void ChangeWallet(UserWallet wallet) - { - if (this.applicationContext.CurrentWallet != null) - { - // Dispose current wallet - this.applicationContext.CurrentWallet.BalanceChanged -= CurrentWallet_BalanceChanged; - this.applicationContext.CurrentWallet.TransactionsChanged -= CurrentWallet_TransactionsChanged; - this.applicationContext.CurrentWallet.Dispose(); - } - - this.dispatcher.InvokeOnMainUIThread(() => - { - this.messagePublisher.Publish(new ClearAccountsMessage()); - this.messagePublisher.Publish(new ClearAssetsMessage()); - this.messagePublisher.Publish(new ClearTransactionsMessage()); - }); - - this.applicationContext.CurrentWallet = wallet; - - if (this.applicationContext.CurrentWallet != null) - { - // Setup wallet - var transactions = this.applicationContext.CurrentWallet.LoadTransactions(); - - CurrentWallet_TransactionsChanged(transactions); - this.applicationContext.CurrentWallet.BalanceChanged += CurrentWallet_BalanceChanged; - this.applicationContext.CurrentWallet.TransactionsChanged += CurrentWallet_TransactionsChanged; - } - - this.messagePublisher.Publish(new EnableMenuItemsMessage()); - NotifyPropertyChanged(nameof(this.WalletIsOpen)); - - this.messagePublisher.Publish(new LoadWalletAddressesMessage()); - - balanceChanged = true; - checkNep5Balance = true; - } - - private void CurrentWallet_BalanceChanged(object sender, EventArgs e) - { - balanceChanged = true; - } - - private void CurrentWallet_TransactionsChanged(IEnumerable transactions) - { - Application.Current.Dispatcher.BeginInvoke(new Action>(CurrentWallet_TransactionsChanged), null, transactions); - } - - private void CurrentWallet_TransactionsChanged(object sender, IEnumerable transactions) - { - this.messagePublisher.Publish(new UpdateTransactionsMessage(transactions)); - } - - #endregion Wallet Methods - - #region Blockchain Methods - - private void ImportBlocksIfRequired() - { - const string acc_path = "chain.acc"; - const string acc_zip_path = acc_path + ".zip"; - - // Check if blocks need importing - if (File.Exists(acc_path)) - { - // Import blocks - using (var fileStream = new FileStream(acc_path, FileMode.Open, FileAccess.Read, FileShare.None)) - { - ImportBlocks(fileStream); - } - File.Delete(acc_path); - } - else if (File.Exists(acc_zip_path)) - { - using (var fileStream = new FileStream(acc_zip_path, FileMode.Open, FileAccess.Read, FileShare.None)) - using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read)) - using (var zipStream = zip.GetEntry(acc_path).Open()) - { - ImportBlocks(zipStream); - } - File.Delete(acc_zip_path); - } - } - - private void ImportBlocks(Stream stream) - { - var blockchain = (LevelDBBlockchain)Blockchain.Default; - blockchain.VerifyBlocks = false; - using (var reader = new BinaryReader(stream)) - { - var count = reader.ReadUInt32(); - for (int height = 0; height < count; height++) - { - var array = reader.ReadBytes(reader.ReadInt32()); - - if (height <= Blockchain.Default.Height) continue; - - var block = array.AsSerializable(); - Blockchain.Default.AddBlock(block); - } - } - blockchain.VerifyBlocks = true; - } - - //private void Load() - //{ - // Task.Run(() => - // { - // CheckForNewerVersion(); - - // ImportBlocksIfRequired(); - - // Blockchain.PersistCompleted += Blockchain_PersistCompleted; - - // // Start node - // Program.LocalNode.Start(Settings.Default.NodePort, Settings.Default.WsPort); - // }); - //} - - public void Close() - { - Blockchain.PersistCompleted -= Blockchain_PersistCompleted; - this.CloseWallet(); - } - - private void CheckForNewerVersion() - { - var latestVersion = VersionHelper.LatestVersion; - var currentVersion = VersionHelper.CurrentVersion; - - if (latestVersion == null || latestVersion <= currentVersion) return; - - this.NewVersionLabel = $"{Strings.DownloadNewVersion}: {latestVersion}"; - this.NewVersionVisible = true; - } - - #endregion Blockchain Methods - - #region UI Update Methods - - //private void SetupUIUpdateTimer() - //{ - // if (this.uiUpdateTimer != null) - // { - // // Stop previous timer - // this.uiUpdateTimer.Stop(); - - // this.uiUpdateTimer.Elapsed -= this.UpdateUI; - - // this.uiUpdateTimer.Dispose(); - - // this.uiUpdateTimer = null; - // } - - // var timer = new Timer - // { - // Interval = 500, - // Enabled = true, - // AutoReset = true - // }; - - // timer.Elapsed += this.UpdateUI; - - // this.uiUpdateTimer = timer; - - // // Start timer - // this.uiUpdateTimer.Start(); - //} - - //private void UpdateUI(object sender, ElapsedEventArgs e) - //{ - // // Only update UI if it is not already being updated - // if (!Monitor.TryEnter(this.uiUpdateLock)) return; - - // try - // { - // var persistenceSpan = DateTime.UtcNow - this.persistenceTime; - - // this.UpdateBlockProgress(persistenceSpan); - - // this.UpdateBalances(persistenceSpan); - // } - // finally - // { - // Monitor.Exit(this.uiUpdateLock); - // } - //} - - //private void UpdateBlockProgress(TimeSpan persistenceSpan) - //{ - // if (persistenceSpan < TimeSpan.Zero) persistenceSpan = TimeSpan.Zero; - - // if (persistenceSpan > Blockchain.TimePerBlock) - // { - // this.BlockProgressIndeterminate = true; - // } - // else - // { - // this.BlockProgressIndeterminate = true; - // this.BlockProgress = persistenceSpan.Seconds; - // } - - // NotifyPropertyChanged(nameof(this.BlockHeight)); - // NotifyPropertyChanged(nameof(this.NodeCount)); - // NotifyPropertyChanged(nameof(this.BlockStatus)); - //} - - //private void UpdateBalances(TimeSpan persistenceSpan) - //{ - // if (this.applicationContext.CurrentWallet == null) return; - - // this.UpdateAssetBalances(); - - // this.UpdateNEP5TokenBalances(persistenceSpan); - //} - - //private void UpdateAssetBalances() - //{ - // if (this.applicationContext.CurrentWallet.WalletHeight > Blockchain.Default.Height + 1) return; - - // this.messagePublisher.Publish(new AccountBalancesChangedMessage()); - // this.messagePublisher.Publish(new UpdateAssetsBalanceMessage(this.balanceChanged)); - //} - - - //private async void UpdateNEP5TokenBalances(TimeSpan persistenceSpan) - //{ - // if (!checkNep5Balance) return; - - // if (persistenceSpan <= TimeSpan.FromSeconds(2)) return; - - // // Update balances - // var addresses = this.applicationContext.CurrentWallet.GetAddresses().ToArray(); - // foreach (var s in Settings.Default.NEP5Watched) - // { - // var scriptHash = UInt160.Parse(s); - // byte[] script; - // using (var builder = new ScriptBuilder()) - // { - // foreach (var address in addresses) - // { - // builder.EmitAppCall(scriptHash, "balanceOf", address); - // } - // builder.Emit(OpCode.DEPTH, OpCode.PACK); - // builder.EmitAppCall(scriptHash, "decimals"); - // builder.EmitAppCall(scriptHash, "name"); - // script = builder.ToArray(); - // } - - // var engine = ApplicationEngine.Run(script); - // if (engine.State.HasFlag(VMState.FAULT)) continue; - - // var name = engine.EvaluationStack.Pop().GetString(); - // var decimals = (byte)engine.EvaluationStack.Pop().GetBigInteger(); - // var amount = engine.EvaluationStack.Pop().GetArray().Aggregate(BigInteger.Zero, (x, y) => x + y.GetBigInteger()); - // if (amount == 0) continue; - // var balance = new BigDecimal(amount, decimals); - // var valueText = balance.ToString(); - - // await this.dispatcher.InvokeOnMainUIThread(() => - // { - // var item = (AssetItem) null; //this.GetAsset(scriptHash); - - // if (item != null) - // { - // item.Value = valueText; - // } - // else - // { - // var assetItem = new AssetItem - // { - // Name = name, - // Type = "NEP-5", - // Issuer = $"ScriptHash:{scriptHash}", - // Value = valueText, - // }; - - // this.messagePublisher.Publish(new AddAssetMessage(assetItem)); - // } - // }); - // } - // checkNep5Balance = false; - //} - - #endregion UI Update Methods - #region Main Menu Command Methods private void CreateWallet() @@ -573,7 +244,8 @@ private void OpenWallet() public void CloseWallet() { - this.ChangeWallet(null); + this.walletController.CloseWallet(); + //this.ChangeWallet(null); } private static void ChangePassword() @@ -590,7 +262,7 @@ await this.dispatcher.InvokeOnMainUIThread(() => this.messagePublisher.Publish(new ClearTransactionsMessage()); }); - this.applicationContext.CurrentWallet.Rebuild(); + this.walletController.RebuildWalletIndexes(); } private static void RestoreAccounts() @@ -671,6 +343,7 @@ private static void CheckForHelp() private static void ShowOfficialWebsite() { + // TODO Issue #40: this static call need to be abstract Process.Start("https://neo.org/"); } @@ -693,26 +366,10 @@ private static void ShowUpdateDialog() dialog.ShowDialog(); } - private uint GetWalletHeight() - { - uint walletHeight = 0; - - if (this.applicationContext.CurrentWallet != null && - this.applicationContext.CurrentWallet.WalletHeight > 0) - { - // Set wallet height - walletHeight = this.applicationContext.CurrentWallet.WalletHeight - 1; - } - - return walletHeight; - } - #region ILoadable Implementation public void OnLoad() { this.messageSubscriber.Subscribe(this); - - //this.Load(); } #endregion @@ -736,13 +393,14 @@ public void HandleMessage(WalletBalanceChangedMessage message) this.balanceChanged = message.BalanceChanged; } + // TODO [AboimPinto] #38: HomeViewModel doesn isn't the message receiver of this message. I could not find any class that is the receiver of this message. This need to be reviewed. public void HandleMessage(InvokeContractMessage message) { var invokeContractView = new InvokeContractView(message.Transaction); invokeContractView.ShowDialog(); } - // TODO Move these message handlers to a more appropriate place, they don't need to be in HomeViewModel + // TODO: Issue: #39 - Move these message handlers to a more appropriate place, they don't need to be in HomeViewModel public void HandleMessage(SignTransactionAndShowInformationMessage message) { var transaction = message.Transaction; @@ -764,13 +422,16 @@ public void HandleMessage(SignTransactionAndShowInformationMessage message) return; } - this.applicationContext.CurrentWallet.Sign(context); + this.walletController.Sign(context); if (context.Completed) { context.Verifiable.Scripts = context.GetScripts(); - this.applicationContext.CurrentWallet.SaveTransaction(transaction); - this.applicationContext.LocalNode.Relay(transaction); + + // TODO [AboimPinto] this method should be added to the WalletController or the BlockChainController + this.walletController.SaveTransaction(transaction); + this.blockChainController.Relay(transaction); + InformationBox.Show(transaction.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle); } else From c1c1ad661e6359a1d7ad90260be77224fa7e7c7d Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Thu, 16 Nov 2017 15:33:05 +0100 Subject: [PATCH 19/22] All the code related with wallet access goes not throuh WalletController in AccountsViewModel --- .../AccountItemExtensionMethods.cs | 14 + neo-gui/Controllers/IWalletController.cs | 11 +- neo-gui/Controllers/WalletController.cs | 211 +++++++++++- neo-gui/UI/Home/AccountsView.xaml | 3 +- neo-gui/UI/Home/AccountsViewModel.cs | 301 ++++-------------- .../UI/Messages/AccountItemsChangedMessage.cs | 14 + neo-gui/neo-gui.csproj | 2 + 7 files changed, 305 insertions(+), 251 deletions(-) create mode 100644 neo-gui/Controllers/AccountItemExtensionMethods.cs create mode 100644 neo-gui/UI/Messages/AccountItemsChangedMessage.cs diff --git a/neo-gui/Controllers/AccountItemExtensionMethods.cs b/neo-gui/Controllers/AccountItemExtensionMethods.cs new file mode 100644 index 00000000..aae4c391 --- /dev/null +++ b/neo-gui/Controllers/AccountItemExtensionMethods.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; +using Neo.UI; + +namespace Neo.Controllers +{ + public static class AccountItemExtensionMethods + { + public static AccountItem GetAccountItemForAddress(this IEnumerable accountItems, string address) + { + return accountItems.FirstOrDefault(x => x.Address.Equals(address)); + } + } +} diff --git a/neo-gui/Controllers/IWalletController.cs b/neo-gui/Controllers/IWalletController.cs index cf591d8d..d890274a 100644 --- a/neo-gui/Controllers/IWalletController.cs +++ b/neo-gui/Controllers/IWalletController.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using Neo.Core; using Neo.SmartContract; using Neo.Wallets; @@ -24,10 +23,18 @@ public interface IWalletController void Sign(ContractParametersContext context); + void CreateNewKey(); // TODO - Issue #43 [AboimPinto] - this method will create a new key or new NEO address? Please review the name. + IEnumerable GetAddresses(); IEnumerable GetCoins(); VerificationContract GetContract(UInt160 scriptHash); + + void ImportWatchOnlyAddress(string addressToImport); + + KeyPair GetKeyByScriptHash(UInt160 scriptHash); + + void DeleteAddress(UInt160 scriptHash); } } diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index 1c4f0098..ba3ebb45 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; using Neo.Core; using Neo.DialogResults; @@ -8,34 +9,41 @@ using Neo.Implementations.Wallets.EntityFramework; using Neo.Properties; using Neo.SmartContract; +using Neo.UI; using Neo.UI.Base.Messages; using Neo.UI.Messages; using Neo.Wallets; namespace Neo.Controllers { - public class WalletController : IWalletController + public class WalletController : + IWalletController, + IMessageHandler, + IMessageHandler, + IMessageHandler, + IMessageHandler { #region Private Fields private readonly IDialogHelper dialogHelper; - private readonly IApplicationContext applicationContext; private readonly IMessagePublisher messagePublisher; private UserWallet currentWallet; private bool balanceChanged; private bool checkNep5Balance; + + private IList accounts; #endregion #region Constructor public WalletController( IDialogHelper dialogHelper, - IApplicationContext applicationContext, IMessagePublisher messagePublisher) { this.dialogHelper = dialogHelper; - this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; + + this.accounts = new List(); } #endregion @@ -56,9 +64,10 @@ public void CreateWallet(string walletPath, string password) public void OpenWallet(string walletPath, string password, bool repairMode) { - // [TODO] why this verification? Why the magic string? + // TODO [AboimPinto] - why this verification? Why the magic string? if (UserWallet.GetVersion(walletPath) < Version.Parse("1.3.5")) { + // TODO - Issue #44 - [AboimPinto] - DialogHelper is not implemented yet. var migrationApproved = this.dialogHelper.ShowDialog("ApproveWalletMigrationDialog"); if (!migrationApproved.Result.Yes) @@ -122,6 +131,130 @@ public VerificationContract GetContract(UInt160 scriptHash) // TODO - ISSUE #37 [AboimPinto]: at this point the return should not be a object from the NEO assemblies but a DTO only know by the application with only the necessary fields. return this.currentWallet.GetContract(scriptHash); } + + public void CreateNewKey() + { + var newKey = this.currentWallet.CreateKey(); + + var contractsForKey = this.currentWallet.GetContracts(newKey.PublicKeyHash); + foreach(var contract in contractsForKey) + { + this.AddContract(contract); + } + } + + public void ImportWatchOnlyAddress(string addressToImport) + { + using (var reader = new StringReader(addressToImport)) + { + while (true) + { + var address = reader.ReadLine(); + if (address == null) break; + address = address.Trim(); + if (string.IsNullOrEmpty(address)) continue; + UInt160 scriptHash; + try + { + scriptHash = Wallet.ToScriptHash(address); + } + catch (FormatException) + { + continue; + } + this.currentWallet.AddWatchOnly(scriptHash); + this.AddAddress(scriptHash); + } + } + } + + public KeyPair GetKeyByScriptHash(UInt160 scriptHash) + { + return this.currentWallet.GetKeyByScriptHash(scriptHash); + } + + public void DeleteAddress(UInt160 scriptHash) + { + var accountItemToDelete = this.accounts.Single(x => x.Address.Equals(scriptHash)); + + this.currentWallet.DeleteAddress(scriptHash); + this.accounts.Remove(accountItemToDelete); + + this.messagePublisher.Publish(new AccountItemsChangedMessage(this.accounts)); + } + #endregion + + #region IMessageHandler implementation + public void HandleMessage(AddContractsMessage message) + { + if (message.Contracts == null || !message.Contracts.Any()) + { + return; + } + + foreach (var contract in message.Contracts) + { + this.currentWallet.AddContract(contract); + this.AddContract(contract); + } + } + + public void HandleMessage(AddContractMessage message) + { + if (message.Contract == null) + { + return; + } + + this.currentWallet.AddContract(message.Contract); + this.AddContract(message.Contract); + } + + public void HandleMessage(ImportPrivateKeyMessage message) + { + if (message.WifStrings == null) return; + + if (!message.WifStrings.Any()) return; + + foreach (var wif in message.WifStrings) + { + KeyPair key; + try + { + key = this.currentWallet.Import(wif); + } + catch (FormatException) + { + // Skip WIF line + continue; + } + foreach (var contract in this.currentWallet.GetContracts(key.PublicKeyHash)) + { + this.AddContract(contract); + } + } + } + + public void HandleMessage(ImportCertificateMessage message) + { + if (message.SelectedCertificate == null) return; + + KeyPair key; + try + { + key = this.currentWallet.Import(message.SelectedCertificate); + } + catch + { + //await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, "Certificate import failed!"); + return; + } + + foreach (var contract in this.currentWallet.GetContracts(key.PublicKeyHash)) + { + this.AddContract(contract); + } + } #endregion #region Private Methods @@ -152,7 +285,7 @@ private void SetCurrentWallet(UserWallet wallet) } this.messagePublisher.Publish(new EnableMenuItemsMessage()); - this.messagePublisher.Publish(new LoadWalletAddressesMessage()); + this.LoadWallet(); this.balanceChanged = true; this.checkNep5Balance = true; @@ -191,6 +324,72 @@ private UserWallet OpenWalletWithPath(string walletPath, string password) return null; } + + private void LoadWallet() + { + if (!this.IsWalletOpen) return; + + foreach (var walletAddress in this.GetAddresses()) + { + var contract = this.GetContract(walletAddress); + if (contract == null) + { + this.AddAddress(walletAddress); + } + else + { + this.AddContract(contract); + } + } + } + + private void AddAddress(UInt160 scriptHash) + { + var address = Wallet.ToAddress(scriptHash); + var accountItemForAddress = this.accounts.GetAccountItemForAddress(address); + + if (accountItemForAddress == null) + { + var newAccountItem = new AccountItem + { + Address = address, + Type = AccountType.WatchOnly, + Neo = Fixed8.Zero, + Gas = Fixed8.Zero, + ScriptHash = scriptHash + }; + + this.accounts.Add(newAccountItem); + } + + this.messagePublisher.Publish(new AccountItemsChangedMessage(this.accounts)); + } + + private void AddContract(VerificationContract contract) + { + var accountItemForAddress = this.accounts.GetAccountItemForAddress(contract.Address); + + if (accountItemForAddress?.ScriptHash != null) // [AboimPinto] what this logic mean? + { + this.accounts.Remove(accountItemForAddress); + } + + if (accountItemForAddress == null) + { + var newAccountItem = new AccountItem + { + Address = contract.Address, + Type = contract.IsStandard ? AccountType.Standard : AccountType.NonStandard, + Neo = Fixed8.Zero, + Gas = Fixed8.Zero, + Contract = contract + }; + + this.accounts.Add(newAccountItem); + } + + this.messagePublisher.Publish(new AccountItemsChangedMessage(this.accounts)); + } #endregion } } diff --git a/neo-gui/UI/Home/AccountsView.xaml b/neo-gui/UI/Home/AccountsView.xaml index ea7d385a..3e22041e 100644 --- a/neo-gui/UI/Home/AccountsView.xaml +++ b/neo-gui/UI/Home/AccountsView.xaml @@ -7,7 +7,8 @@ xmlns:properties="clr-namespace:Neo.Properties" xmlns:markupExtensions="clr-namespace:Neo.UI.MarkupExtensions" dialogs:DialogParticipation.Register="{Binding}" - ItemsSource="{Binding Accounts}" SelectedItem="{Binding SelectedAccount}" + ItemsSource="{Binding Accounts}" + SelectedItem="{Binding SelectedAccount}" DataContext="{markupExtensions:DataContextBinding ViewModel=local:AccountsViewModel}" MouseDoubleClick="AccountList_DoubleClick"> diff --git a/neo-gui/UI/Home/AccountsViewModel.cs b/neo-gui/UI/Home/AccountsViewModel.cs index 8e92fe98..be35f3a7 100644 --- a/neo-gui/UI/Home/AccountsViewModel.cs +++ b/neo-gui/UI/Home/AccountsViewModel.cs @@ -1,22 +1,17 @@ using System; -using System.IO; +using System.Collections.ObjectModel; using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Input; - -using MahApps.Metro.Controls.Dialogs; using Neo.Controllers; using Neo.Core; using Neo.Properties; using Neo.UI.Accounts; -using Neo.UI.Base.Collections; using Neo.UI.Base.Dialogs; using Neo.UI.Base.Dispatching; -using Neo.UI.Base.Helpers; using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; -using Neo.UI.Contracts; using Neo.UI.Messages; using Neo.UI.Voting; using Neo.Wallets; @@ -29,40 +24,20 @@ public class AccountsViewModel : IMessageHandler, IMessageHandler, IMessageHandler, - IMessageHandler, - IMessageHandler, - IMessageHandler, - IMessageHandler, - IMessageHandler + IMessageHandler { - private readonly IApplicationContext applicationContext; + #region Private Fields private readonly IWalletController walletController; private readonly IMessageSubscriber messageSubscriber; private readonly IMessagePublisher messagePublisher; - private readonly IDispatcher dispatcher; private AccountItem selectedAccount; - - public AccountsViewModel( - IApplicationContext applicationContext, - IWalletController walletController, - IMessageSubscriber messageSubscriber, - IMessagePublisher messagePublisher, - IDispatcher dispatcher) - { - this.applicationContext = applicationContext; - this.walletController = walletController; - this.messageSubscriber = messageSubscriber; - this.messagePublisher = messagePublisher; - this.dispatcher = dispatcher; - - this.Accounts = new ConcurrentObservableCollection(); - } + #endregion #region Properties public Action NotifyBalanceChangedAction { get; set; } - public ConcurrentObservableCollection Accounts { get; } + public ObservableCollection Accounts { get; private set; } public AccountItem SelectedAccount { @@ -107,7 +82,6 @@ public AccountItem SelectedAccount #endregion Properties #region Commands - public ICommand CreateNewAddressCommand => new RelayCommand(this.CreateNewKey); public ICommand ImportWifPrivateKeyCommand => new RelayCommand(this.ImportWifPrivateKey); @@ -125,74 +99,77 @@ public AccountItem SelectedAccount public ICommand ShowVotingDialogCommand => new RelayCommand(this.ShowVotingDialog); public ICommand CopyAddressToClipboardCommand => new RelayCommand(this.CopyAddressToClipboard); public ICommand DeleteAccountCommand => new RelayCommand(this.DeleteAccount); - #endregion Command - public AccountItem GetAccount(string address) + #region Constructor + public AccountsViewModel( + IWalletController walletController, + IMessageSubscriber messageSubscriber, + IMessagePublisher messagePublisher) { - return this.Accounts.FirstOrDefault(account => account.Address == address); - } + this.walletController = walletController; + this.messageSubscriber = messageSubscriber; + this.messagePublisher = messagePublisher; + this.Accounts = new ObservableCollection(); + } + #endregion - private async void AddAddress(UInt160 scriptHash, bool selected = false) + #region IMessageHandler implementation + public void HandleMessage(AccountBalancesChangedMessage message) { - var address = Wallet.ToAddress(scriptHash); - var item = this.GetAccount(address); + var coins = this.walletController.GetCoins() + .Where(p => !p.State.HasFlag(CoinState.Spent)) + .ToList(); + + if (coins == null) return; - if (item == null) + var balanceNeo = coins.Where(p => p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)).GroupBy(p => p.Output.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Output.Value)); + var balanceGas = coins.Where(p => p.Output.AssetId.Equals(Blockchain.UtilityToken.Hash)).GroupBy(p => p.Output.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Output.Value)); + + var accountsList = this.Accounts.ToList(); + + foreach (var account in accountsList) { - item = new AccountItem - { - Address = address, - Type = AccountType.WatchOnly, - Neo = Fixed8.Zero, - Gas = Fixed8.Zero, - ScriptHash = scriptHash - }; - - await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Add(item)); + var scriptHash = Wallet.ToScriptHash(account.Address); + var neo = balanceNeo.ContainsKey(scriptHash) ? balanceNeo[scriptHash] : Fixed8.Zero; + var gas = balanceGas.ContainsKey(scriptHash) ? balanceGas[scriptHash] : Fixed8.Zero; + account.Neo = neo; + account.Gas = gas; } - - this.SelectedAccount = selected ? item : null; } - private async void AddContract(VerificationContract contract, bool selected = false) + public void HandleMessage(EnableMenuItemsMessage message) { - var item = this.GetAccount(contract.Address); + this.NotifyPropertyChanged(nameof(this.MenuItemsEnabled)); + } - if (item?.ScriptHash != null) - { - var account = item; - await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Remove(account)); - item = null; - } + public void HandleMessage(ClearAccountsMessage message) + { + this.Accounts.Clear(); + } - if (item == null) + public void HandleMessage(AccountItemsChangedMessage message) + { + this.Accounts.Clear(); + foreach(var accountItem in message.Accounts) { - item = new AccountItem - { - Address = contract.Address, - Type = contract.IsStandard ? AccountType.Standard : AccountType.NonStandard, - Neo = Fixed8.Zero, - Gas = Fixed8.Zero, - Contract = contract - }; - - await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Add(item)); + this.Accounts.Add(accountItem); } + } + #endregion - this.SelectedAccount = selected ? item : null; + #region ILoadable implementation + public void OnLoad() + { + this.messageSubscriber.Subscribe(this); } + #endregion - #region Account Menu Command Methods + #region Private Methods private void CreateNewKey() { - this.SelectedAccount = null; - var key = this.applicationContext.CurrentWallet.CreateKey(); - foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } + this.walletController.CreateNewKey(); } private void ImportWifPrivateKey() @@ -213,27 +190,7 @@ private void ImportWatchOnlyAddress() if (string.IsNullOrEmpty(text)) return; - using (var reader = new StringReader(text)) - { - while (true) - { - var address = reader.ReadLine(); - if (address == null) break; - address = address.Trim(); - if (string.IsNullOrEmpty(address)) continue; - UInt160 scriptHash; - try - { - scriptHash = Wallet.ToScriptHash(address); - } - catch (FormatException) - { - continue; - } - this.applicationContext.CurrentWallet.AddWatchOnly(scriptHash); - AddAddress(scriptHash, true); - } - } + this.walletController.ImportWatchOnlyAddress(text); } private void CreateMultiSignatureContract() @@ -259,7 +216,7 @@ private void ViewPrivateKey() if (this.SelectedAccount?.Contract == null) return; var contract = this.SelectedAccount.Contract; - var key = this.applicationContext.CurrentWallet.GetKeyByScriptHash(contract.ScriptHash); + var key = this.walletController.GetKeyByScriptHash(contract.ScriptHash); var view = new ViewPrivateKeyView(key, contract.ScriptHash); view.ShowDialog(); @@ -294,7 +251,7 @@ private void CopyAddressToClipboard() catch (ExternalException) { } } - private async void DeleteAccount() + private void DeleteAccount() { if (this.SelectedAccount == null) return; @@ -306,150 +263,10 @@ private async void DeleteAccount() var scriptHash = accountToDelete.ScriptHash != null ? accountToDelete.ScriptHash : accountToDelete.Contract.ScriptHash; - - this.applicationContext.CurrentWallet.DeleteAddress(scriptHash); - await this.dispatcher.InvokeOnMainUIThread(() => this.Accounts.Remove(accountToDelete)); + this.walletController.DeleteAddress(scriptHash); this.messagePublisher.Publish(new WalletBalanceChangedMessage(true)); } #endregion Account Menu Command Methods - - #region IMessageHandler implementation - public void HandleMessage(AccountBalancesChangedMessage message) - { - var coins = this.walletController.GetCoins() - .Where(p => !p.State.HasFlag(CoinState.Spent)) - .ToList(); - - if (coins == null) return; - - var balanceNeo = coins.Where(p => p.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)).GroupBy(p => p.Output.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Output.Value)); - var balanceGas = coins.Where(p => p.Output.AssetId.Equals(Blockchain.UtilityToken.Hash)).GroupBy(p => p.Output.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Output.Value)); - - var accountsList = this.Accounts.ToList(); - - foreach (var account in accountsList) - { - var scriptHash = Wallet.ToScriptHash(account.Address); - var neo = balanceNeo.ContainsKey(scriptHash) ? balanceNeo[scriptHash] : Fixed8.Zero; - var gas = balanceGas.ContainsKey(scriptHash) ? balanceGas[scriptHash] : Fixed8.Zero; - account.Neo = neo; - account.Gas = gas; - } - } - - public void HandleMessage(EnableMenuItemsMessage message) - { - this.NotifyPropertyChanged(nameof(this.MenuItemsEnabled)); - } - - public void HandleMessage(ClearAccountsMessage message) - { - this.Accounts.Clear(); - } - - public void HandleMessage(LoadWalletAddressesMessage message) - { - if (!this.walletController.IsWalletOpen) return; - - // Load accounts - foreach (var scriptHash in this.walletController.GetAddresses()) - { - var contract = this.walletController.GetContract(scriptHash); - if (contract == null) - { - this.AddAddress(scriptHash); - } - else - { - this.AddContract(contract); - } - } - } - - public void HandleMessage(AddContractsMessage message) - { - if (message.Contracts == null || !message.Contracts.Any()) - { - return; - } - - this.SelectedAccount = null; - - foreach (var contract in message.Contracts) - { - this.applicationContext.CurrentWallet.AddContract(contract); - this.AddContract(contract, true); - } - } - - public void HandleMessage(AddContractMessage message) - { - if (message.Contract == null) return; - - this.SelectedAccount = null; - - this.applicationContext.CurrentWallet.AddContract(message.Contract); - this.AddContract(message.Contract, true); - } - - public void HandleMessage(ImportPrivateKeyMessage message) - { - if (message.WifStrings == null) return; - - if (!message.WifStrings.Any()) return; - - // Import private keys - this.SelectedAccount = null; - - foreach (var wif in message.WifStrings) - { - KeyPair key; - try - { - key = this.applicationContext.CurrentWallet.Import(wif); - } - catch (FormatException) - { - // Skip WIF line - continue; - } - foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - this.AddContract(contract, true); - } - } - } - - public async void HandleMessage(ImportCertificateMessage message) - { - if (message.SelectedCertificate == null) return; - - this.SelectedAccount = null; - - KeyPair key; - try - { - key = this.applicationContext.CurrentWallet.Import(message.SelectedCertificate); - } - catch - { - await DialogCoordinator.Instance.ShowMessageAsync(this, string.Empty, "Certificate import failed!"); - return; - } - - foreach (var contract in this.applicationContext.CurrentWallet.GetContracts(key.PublicKeyHash)) - { - AddContract(contract, true); - } - } - #endregion - - #region ILoadable implementation - public void OnLoad() - { - this.messageSubscriber.Subscribe(this); - } - #endregion } } \ No newline at end of file diff --git a/neo-gui/UI/Messages/AccountItemsChangedMessage.cs b/neo-gui/UI/Messages/AccountItemsChangedMessage.cs new file mode 100644 index 00000000..f2d2dd26 --- /dev/null +++ b/neo-gui/UI/Messages/AccountItemsChangedMessage.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Neo.UI.Messages +{ + public class AccountItemsChangedMessage + { + public IEnumerable Accounts { get; private set; } + + public AccountItemsChangedMessage(IEnumerable accounts) + { + this.Accounts = accounts; + } + } +} diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 2e0f9b15..793211cb 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -261,6 +261,7 @@ App.xaml + @@ -359,6 +360,7 @@ + From 3ff0741e66a985d8393439d1763a412cf880d00f Mon Sep 17 00:00:00 2001 From: Justin Gaffney Date: Fri, 17 Nov 2017 17:33:26 +1000 Subject: [PATCH 20/22] Set application context in view in constructor --- neo-gui/UI/Home/AssetsViewModel.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index 2c42fa08..752f9861 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -86,8 +86,9 @@ public bool ViewCertificateEnabled #endregion Commands #region Constructor - public AssetsViewModel(IMessagePublisher messagePublisher, IDispatcher dispatcher) + public AssetsViewModel(IApplicationContext applicationContext, IMessagePublisher messagePublisher, IDispatcher dispatcher) { + this.applicationContext = applicationContext; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -313,4 +314,4 @@ public void HandleMessage(AddAssetMessage message) } #endregion } -} \ No newline at end of file +} From 8a6e1ee57fd3b0531107e2019779cf458bbc536e Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Fri, 17 Nov 2017 12:35:13 +0100 Subject: [PATCH 21/22] ExternalProcessHelper was create to wrapper the calls Process.Start that start a external process to the application. --- neo-gui/Helpers/ExternalProcessHelper.cs | 16 ++++++++++++++++ neo-gui/Helpers/HelpersRegistrationModule.cs | 4 ++++ neo-gui/Helpers/IExternalProcessHelper.cs | 7 +++++++ neo-gui/UI/Home/AssetsViewModel.cs | 13 ++++++++++--- neo-gui/UI/Home/HomeViewModel.cs | 11 +++++++---- neo-gui/UI/Updater/UpdateViewModel.cs | 14 +++++++++----- neo-gui/neo-gui.csproj | 2 ++ 7 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 neo-gui/Helpers/ExternalProcessHelper.cs create mode 100644 neo-gui/Helpers/IExternalProcessHelper.cs diff --git a/neo-gui/Helpers/ExternalProcessHelper.cs b/neo-gui/Helpers/ExternalProcessHelper.cs new file mode 100644 index 00000000..10080aa4 --- /dev/null +++ b/neo-gui/Helpers/ExternalProcessHelper.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics; +using System.Reflection; + +namespace Neo.Helpers +{ + public class ExternalProcessHelper : IExternalProcessHelper + { + #region IExternalProcessHelper implementation + public void OpenInExternalBrowser(string url) + { + Process.Start(url); + } + #endregion + } +} diff --git a/neo-gui/Helpers/HelpersRegistrationModule.cs b/neo-gui/Helpers/HelpersRegistrationModule.cs index a3368a70..9e8b0a6f 100644 --- a/neo-gui/Helpers/HelpersRegistrationModule.cs +++ b/neo-gui/Helpers/HelpersRegistrationModule.cs @@ -10,6 +10,10 @@ protected override void Load(ContainerBuilder builder) .RegisterType() .As(); + builder + .RegisterType() + .As(); + base.Load(builder); } } diff --git a/neo-gui/Helpers/IExternalProcessHelper.cs b/neo-gui/Helpers/IExternalProcessHelper.cs new file mode 100644 index 00000000..1abbc4e7 --- /dev/null +++ b/neo-gui/Helpers/IExternalProcessHelper.cs @@ -0,0 +1,7 @@ +namespace Neo.Helpers +{ + public interface IExternalProcessHelper + { + void OpenInExternalBrowser(string url); + } +} diff --git a/neo-gui/UI/Home/AssetsViewModel.cs b/neo-gui/UI/Home/AssetsViewModel.cs index 752f9861..ceae27dc 100644 --- a/neo-gui/UI/Home/AssetsViewModel.cs +++ b/neo-gui/UI/Home/AssetsViewModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Windows; @@ -7,6 +6,7 @@ using Neo.Core; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Helpers; using Neo.Properties; using Neo.SmartContract; using Neo.UI.Base.Collections; @@ -29,6 +29,7 @@ public class AssetsViewModel : private static readonly UInt160 RecycleScriptHash = new[] { (byte)OpCode.PUSHT }.ToScriptHash(); private readonly IApplicationContext applicationContext; + private readonly IExternalProcessHelper extertenalProcessHelper; private readonly IMessagePublisher messagePublisher; private readonly IDispatcher dispatcher; private readonly Dictionary certificateQueryResultCache; @@ -86,9 +87,14 @@ public bool ViewCertificateEnabled #endregion Commands #region Constructor - public AssetsViewModel(IApplicationContext applicationContext, IMessagePublisher messagePublisher, IDispatcher dispatcher) + public AssetsViewModel( + IApplicationContext applicationContext, + IExternalProcessHelper extertenalProcessHelper, + IMessagePublisher messagePublisher, + IDispatcher dispatcher) { this.applicationContext = applicationContext; + this.extertenalProcessHelper = extertenalProcessHelper; this.messagePublisher = messagePublisher; this.dispatcher = dispatcher; @@ -105,7 +111,8 @@ private void ViewCertificate() var hash = Contract.CreateSignatureRedeemScript(this.SelectedAsset.State.Owner).ToScriptHash(); var address = Wallet.ToAddress(hash); var path = Path.Combine(Settings.Default.CertCachePath, $"{address}.cer"); - Process.Start(path); + + this.extertenalProcessHelper.OpenInExternalBrowser(path); } private void DeleteAsset() diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 9b61141a..7b9de7e9 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -20,6 +20,7 @@ using Neo.UI.Voting; using Neo.UI.Base.Messages; using Neo.Controllers; +using Neo.Helpers; namespace Neo.UI.Home { @@ -34,6 +35,7 @@ public class HomeViewModel : #region Private Fields private readonly IBlockChainController blockChainController; private readonly IWalletController walletController; + private readonly IExternalProcessHelper extertenalProcessHelper; private readonly IMessagePublisher messagePublisher; private readonly IMessageSubscriber messageSubscriber; private readonly IDispatcher dispatcher; @@ -54,13 +56,15 @@ public class HomeViewModel : #region Constructor public HomeViewModel( IBlockChainController blockChainController, - IWalletController walletController, + IWalletController walletController, + IExternalProcessHelper extertenalProcessHelper, IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, IDispatcher dispatcher) { this.blockChainController = blockChainController; this.walletController = walletController; + this.extertenalProcessHelper = extertenalProcessHelper; this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; this.dispatcher = dispatcher; @@ -341,10 +345,9 @@ private static void CheckForHelp() // TODO Implement } - private static void ShowOfficialWebsite() + private void ShowOfficialWebsite() { - // TODO Issue #40: this static call need to be abstract - Process.Start("https://neo.org/"); + this.extertenalProcessHelper.OpenInExternalBrowser("https://neo.org/"); } private static void ShowDeveloperTools() diff --git a/neo-gui/UI/Updater/UpdateViewModel.cs b/neo-gui/UI/Updater/UpdateViewModel.cs index 6c79cf60..705a433b 100644 --- a/neo-gui/UI/Updater/UpdateViewModel.cs +++ b/neo-gui/UI/Updater/UpdateViewModel.cs @@ -1,10 +1,10 @@ using System; using System.ComponentModel; -using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Net; using System.Windows.Input; +using Neo.Helpers; using Neo.UI.Base.Messages; using Neo.UI.Base.MVVM; using Neo.UI.Messages; @@ -19,6 +19,7 @@ public class UpdateViewModel : ViewModelBase private readonly WebClient web = new WebClient(); + private readonly IExternalProcessHelper externalProcessHelper; private readonly IMessagePublisher messagePublisher; private readonly Version latestVersion; @@ -28,8 +29,11 @@ public class UpdateViewModel : ViewModelBase private bool buttonsEnabled; - public UpdateViewModel(IMessagePublisher messagePublisher) + public UpdateViewModel( + IExternalProcessHelper externalProcessHelper, + IMessagePublisher messagePublisher) { + this.externalProcessHelper = externalProcessHelper; this.messagePublisher = messagePublisher; // Setup update information @@ -83,14 +87,14 @@ public bool ButtonsEnabled public ICommand CloseCommand => new RelayCommand(this.TryClose); - private static void GoToOfficialWebsite() + private void GoToOfficialWebsite() { - Process.Start("https://neo.org/"); + this.externalProcessHelper.OpenInExternalBrowser("https://neo.org/"); } private void GoToDownloadPage() { - Process.Start(this.downloadUrl); + this.externalProcessHelper.OpenInExternalBrowser(this.downloadUrl); } #region Update Downloader Methods diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index 793211cb..5e3ce4ea 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -270,6 +270,8 @@ + + From 5f08f974316a665de5ca3a724fcf35df7a4dc1f3 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Sat, 18 Nov 2017 20:46:54 +0100 Subject: [PATCH 22/22] Basic implementation of IDialogHelper and usage to in HomeViewModel to open OpenWalletView #44 --- neo-gui/App.xaml.cs | 3 +++ neo-gui/Controllers/WalletController.cs | 6 ++--- neo-gui/Helpers/DialogHelper.cs | 29 +++++++++++++++++----- neo-gui/Helpers/IDialog.cs | 9 +++++++ neo-gui/Helpers/IDialogHelper.cs | 4 +-- neo-gui/Helpers/IDialogViewModel.cs | 11 +++++++++ neo-gui/UI/DialogsRegistrationModule.cs | 19 ++++++++++++++ neo-gui/UI/Home/HomeViewModel.cs | 13 +++++----- neo-gui/UI/Wallets/OpenWalletView.xaml | 23 +++++++++-------- neo-gui/UI/Wallets/OpenWalletView.xaml.cs | 4 ++- neo-gui/UI/Wallets/OpenWalletViewModel.cs | 30 +++++++++++++++++++++-- neo-gui/neo-gui.csproj | 6 +++++ 12 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 neo-gui/Helpers/IDialog.cs create mode 100644 neo-gui/Helpers/IDialogViewModel.cs create mode 100644 neo-gui/UI/DialogsRegistrationModule.cs diff --git a/neo-gui/App.xaml.cs b/neo-gui/App.xaml.cs index 110d9a93..6a840460 100644 --- a/neo-gui/App.xaml.cs +++ b/neo-gui/App.xaml.cs @@ -86,9 +86,12 @@ private static void BuildContainer() autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); autoFacContainerBuilder.RegisterModule(); + autoFacContainerBuilder.RegisterModule(); var container = autoFacContainerBuilder.Build(); + var applicationContext = container.Resolve(); + applicationContext.ContainerLifetimeScope = container.BeginLifetimeScope(); ApplicationContext.Instance.ContainerLifetimeScope = container.BeginLifetimeScope(); } } diff --git a/neo-gui/Controllers/WalletController.cs b/neo-gui/Controllers/WalletController.cs index 7cf9eb5f..75d56acf 100644 --- a/neo-gui/Controllers/WalletController.cs +++ b/neo-gui/Controllers/WalletController.cs @@ -75,13 +75,13 @@ public void OpenWallet(string walletPath, string password, bool repairMode) // TODO - Issue #44 - [AboimPinto] - DialogHelper is not implemented yet. var migrationApproved = this.dialogHelper.ShowDialog("ApproveWalletMigrationDialog"); - if (!migrationApproved.Result.Yes) + if (!migrationApproved.Yes) { return; } this.MigrateWallet(walletPath); - this.dialogHelper.ShowDialog("WalletMigrationCompleteDialog"); + //this.dialogHelper.ShowDialog("WalletMigrationCompleteDialog"); } var userWallet = this.OpenWalletWithPath(walletPath, password); @@ -461,7 +461,7 @@ private UserWallet OpenWalletWithPath(string walletPath, string password) } catch (CryptographicException) { - this.dialogHelper.ShowDialog("WalletPasswordIncorrectDialog", Strings.PasswordIncorrect); + //this.dialogHelper.ShowDialog("WalletPasswordIncorrectDialog", Strings.PasswordIncorrect); } return null; diff --git a/neo-gui/Helpers/DialogHelper.cs b/neo-gui/Helpers/DialogHelper.cs index 355bf525..ab9269e1 100644 --- a/neo-gui/Helpers/DialogHelper.cs +++ b/neo-gui/Helpers/DialogHelper.cs @@ -1,18 +1,35 @@ -using System; +using Autofac; namespace Neo.Helpers { public class DialogHelper : IDialogHelper { - #region IDialogHelper implementation - public void ShowDialog(string dialogName, params string[] parameters) - { + #region Private Fields + private readonly IApplicationContext applicationContext; + #endregion + #region Constructor + public DialogHelper(IApplicationContext applicationContext) + { + this.applicationContext = applicationContext; } + #endregion - public DialogResult ShowDialog(string dialogName, params string[] parameters) + #region IDialogHelper implementation + public T ShowDialog(params string[] parameters) { - return default(DialogResult); + T dialogResult = default(T); + + var view = this.applicationContext.ContainerLifetimeScope.Resolve>(); + var viewModel = view.DataContext as IDialogViewModel; + viewModel.SetDialogResult += (sender, e) => + { + dialogResult = e; + }; + + view.ShowDialog(); + + return dialogResult; } #endregion } diff --git a/neo-gui/Helpers/IDialog.cs b/neo-gui/Helpers/IDialog.cs new file mode 100644 index 00000000..83dc0a46 --- /dev/null +++ b/neo-gui/Helpers/IDialog.cs @@ -0,0 +1,9 @@ +namespace Neo.Helpers +{ + public interface IDialog + { + object DataContext { get; set; } + + bool? ShowDialog(); + } +} diff --git a/neo-gui/Helpers/IDialogHelper.cs b/neo-gui/Helpers/IDialogHelper.cs index 48b2b923..303a963c 100644 --- a/neo-gui/Helpers/IDialogHelper.cs +++ b/neo-gui/Helpers/IDialogHelper.cs @@ -5,8 +5,6 @@ /// public interface IDialogHelper { - void ShowDialog(string dialogName, params string[] parameters); - - DialogResult ShowDialog(string dialogName, params string[] parameters); + T ShowDialog(params string[] parameters); } } diff --git a/neo-gui/Helpers/IDialogViewModel.cs b/neo-gui/Helpers/IDialogViewModel.cs new file mode 100644 index 00000000..dbf2ff21 --- /dev/null +++ b/neo-gui/Helpers/IDialogViewModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace Neo.Helpers +{ + public interface IDialogViewModel + { + event EventHandler SetDialogResult; + + TDialogResult DialogResult { get; } + } +} diff --git a/neo-gui/UI/DialogsRegistrationModule.cs b/neo-gui/UI/DialogsRegistrationModule.cs new file mode 100644 index 00000000..04b6ebe4 --- /dev/null +++ b/neo-gui/UI/DialogsRegistrationModule.cs @@ -0,0 +1,19 @@ +using Autofac; +using Neo.DialogResults; +using Neo.Helpers; +using Neo.UI.Wallets; + +namespace Neo.UI +{ + public class DialogsRegistrationModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterType() + .As>(); + + base.Load(builder); + } + } +} diff --git a/neo-gui/UI/Home/HomeViewModel.cs b/neo-gui/UI/Home/HomeViewModel.cs index 8f958394..163d7789 100644 --- a/neo-gui/UI/Home/HomeViewModel.cs +++ b/neo-gui/UI/Home/HomeViewModel.cs @@ -21,6 +21,7 @@ using Neo.UI.Base.Messages; using Neo.Controllers; using Neo.Helpers; +using Neo.DialogResults; namespace Neo.UI.Home { @@ -36,6 +37,7 @@ public class HomeViewModel : #region Private Fields private readonly IBlockChainController blockChainController; private readonly IWalletController walletController; + private readonly IDialogHelper dialogHelper; private readonly IExternalProcessHelper extertenalProcessHelper; private readonly IMessagePublisher messagePublisher; private readonly IMessageSubscriber messageSubscriber; @@ -58,6 +60,7 @@ public class HomeViewModel : public HomeViewModel( IBlockChainController blockChainController, IWalletController walletController, + IDialogHelper dialogHelper, IExternalProcessHelper extertenalProcessHelper, IMessagePublisher messagePublisher, IMessageSubscriber messageSubscriber, @@ -65,6 +68,7 @@ public HomeViewModel( { this.blockChainController = blockChainController; this.walletController = walletController; + this.dialogHelper = dialogHelper; this.extertenalProcessHelper = extertenalProcessHelper; this.messagePublisher = messagePublisher; this.messageSubscriber = messageSubscriber; @@ -238,13 +242,8 @@ private void CreateWallet() private void OpenWallet() { - var view = new OpenWalletView(); - view.ShowDialog(); - - //var openWalletDialogResult = this.dialogHelper.ShowDialog("OpenWalletDialog"); - if (!view.GetWalletOpenInfo(out var walletPath, out var password, out var repairMode)) return; - - this.walletController.OpenWallet(walletPath, password, repairMode); + var openWalletDialogresult = this.dialogHelper.ShowDialog(); + this.walletController.OpenWallet(openWalletDialogresult.WalletPath, openWalletDialogresult.Password, openWalletDialogresult.OpenInRepairMode); } public void CloseWallet() diff --git a/neo-gui/UI/Wallets/OpenWalletView.xaml b/neo-gui/UI/Wallets/OpenWalletView.xaml index 8d266d0d..dc5fbb20 100644 --- a/neo-gui/UI/Wallets/OpenWalletView.xaml +++ b/neo-gui/UI/Wallets/OpenWalletView.xaml @@ -1,13 +1,16 @@ - + diff --git a/neo-gui/UI/Wallets/OpenWalletView.xaml.cs b/neo-gui/UI/Wallets/OpenWalletView.xaml.cs index 2c9b5afe..9fc9983c 100644 --- a/neo-gui/UI/Wallets/OpenWalletView.xaml.cs +++ b/neo-gui/UI/Wallets/OpenWalletView.xaml.cs @@ -1,6 +1,8 @@ using System.IO; using System.Windows; using System.Windows.Controls; +using Neo.DialogResults; +using Neo.Helpers; using Neo.Properties; namespace Neo.UI.Wallets @@ -8,7 +10,7 @@ namespace Neo.UI.Wallets /// /// Interaction logic for OpenWalletView.xaml /// - public partial class OpenWalletView + public partial class OpenWalletView : IDialog { private readonly OpenWalletViewModel viewModel; diff --git a/neo-gui/UI/Wallets/OpenWalletViewModel.cs b/neo-gui/UI/Wallets/OpenWalletViewModel.cs index cec47546..75a6a99a 100644 --- a/neo-gui/UI/Wallets/OpenWalletViewModel.cs +++ b/neo-gui/UI/Wallets/OpenWalletViewModel.cs @@ -1,17 +1,23 @@ -using System.Windows.Input; +using System; +using System.Windows.Input; using Microsoft.Win32; +using Neo.DialogResults; +using Neo.Helpers; using Neo.UI.Base.MVVM; namespace Neo.UI.Wallets { - public class OpenWalletViewModel : ViewModelBase + public class OpenWalletViewModel : ViewModelBase, IDialogViewModel { + #region Private Fields private string walletPath; private string password; private bool repairMode; private bool confirmed; + #endregion + #region Public Properties public string WalletPath { get => this.walletPath; @@ -57,7 +63,15 @@ public bool ConfirmEnabled public ICommand GetWalletPathCommand => new RelayCommand(this.GetWalletPath); public ICommand ConfirmCommand => new RelayCommand(this.Confirm); + #endregion + #region IDialogViewModel implementation + public event EventHandler SetDialogResult; + + public OpenWalletDialogResult DialogResult { get; private set; } + #endregion + + #region Public Methods public void UpdatePassword(string updatedPassword) { this.password = updatedPassword; @@ -80,7 +94,9 @@ public bool GetWalletOpenInfo(out string path, out string walletPassword, out bo return true; } + #endregion + #region Private Methods private void GetWalletPath() { var openFileDialog = new OpenFileDialog @@ -101,7 +117,17 @@ private void Confirm() this.confirmed = true; + if (this.SetDialogResult != null) + { + var dialogResult = new OpenWalletDialogResult( + this.WalletPath, + this.password, + this.RepairMode); + this.SetDialogResult(this, dialogResult); + } + this.TryClose(); } + #endregion } } \ No newline at end of file diff --git a/neo-gui/neo-gui.csproj b/neo-gui/neo-gui.csproj index e7b1348d..a1b593bf 100644 --- a/neo-gui/neo-gui.csproj +++ b/neo-gui/neo-gui.csproj @@ -271,6 +271,8 @@ + + @@ -344,6 +346,7 @@ + TransactionsView.xaml @@ -809,6 +812,9 @@ MSBuild:Compile + + +