Browse Source

Major Refactoring (phase 1):
- Split FRMWallet into various sub-forms
- Added toolbar in status bar for locking wallet, connectivity toggling
and diplay sync dialog
- Untangled a lot of the inter-form dependencies, simplified structure

Herman Schoenfeld 7 years ago
parent
commit
313571d634
42 changed files with 5183 additions and 2302 deletions
  1. 19 39
      PascalCoinWalletLazarus.dpr
  2. 87 19
      PascalCoinWalletLazarus.lpi
  3. 366 0
      PascalCoinWalletLazarus.lpi.bak
  4. BIN
      Resources/WalletImages/network-off-16x16.png
  5. BIN
      Resources/WalletImages/network-on-16x16.png
  6. BIN
      Resources/WalletImages/sync-16x16.png
  7. BIN
      Resources/WalletImages/wallet-locked-16x16.png
  8. BIN
      Resources/WalletImages/wallet-unlocked-16x16.png
  9. 520 0
      Units/Forms/UFRMAccountExplorer.lfm
  10. 827 0
      Units/Forms/UFRMAccountExplorer.pas
  11. 69 0
      Units/Forms/UFRMBlockExplorer.lfm
  12. 81 0
      Units/Forms/UFRMBlockExplorer.pas
  13. 40 0
      Units/Forms/UFRMLogs.lfm
  14. 66 0
      Units/Forms/UFRMLogs.pas
  15. 90 0
      Units/Forms/UFRMMessages.lfm
  16. 181 0
      Units/Forms/UFRMMessages.pas
  17. 70 0
      Units/Forms/UFRMNodes.lfm
  18. 234 0
      Units/Forms/UFRMNodes.pas
  19. 18 7
      Units/Forms/UFRMOperation.pas
  20. 79 0
      Units/Forms/UFRMOperationExplorer.lfm
  21. 133 0
      Units/Forms/UFRMOperationExplorer.pas
  22. 69 0
      Units/Forms/UFRMPendingOperations.lfm
  23. 76 0
      Units/Forms/UFRMPendingOperations.pas
  24. 367 0
      Units/Forms/UFRMSyncronizationDialog.lfm
  25. 178 0
      Units/Forms/UFRMSyncronizationDialog.pas
  26. 106 1170
      Units/Forms/UFRMWallet.lfm
  27. 145 984
      Units/Forms/UFRMWallet.pas
  28. 5 21
      Units/Forms/UFRMWalletKeys.pas
  29. 26 0
      Units/Forms/UFRMWalletKeys2.pas
  30. 1034 0
      Units/Forms/UUserInterface.pas
  31. 31 22
      Units/PascalCoin/UAccounts.pas
  32. 3 2
      Units/PascalCoin/UBaseTypes.pas
  33. 1 1
      Units/PascalCoin/UBlockChain.pas
  34. 1 1
      Units/PascalCoin/UConst.pas
  35. 12 2
      Units/PascalCoin/UNetProtocol.pas
  36. 1 1
      Units/PascalCoin/UNode.pas
  37. 5 5
      Units/PascalCoin/UThread.pas
  38. 8 1
      Units/PascalCoin/UWalletKeys.pas
  39. 4 4
      Units/PascalCoin/config.inc
  40. 78 22
      Units/Utils/UCommon.pas
  41. 134 0
      Units/Utils/UCommonUI.pas
  42. 19 1
      Units/Utils/UGridUtils.pas

+ 19 - 39
PascalCoinWalletLazarus.dpr

@@ -5,52 +5,32 @@ program PascalCoinWalletLazarus;
 {$ENDIF}
 {$ENDIF}
 
 
 uses
 uses
-{$IFnDEF FPC}
-{$ELSE}
+{$IFDEF FPC}
   {$IFDEF LINUX}cthreads,{$ENDIF}
   {$IFDEF LINUX}cthreads,{$ENDIF}
   Interfaces,
   Interfaces,
 {$ENDIF}
 {$ENDIF}
+  sysutils,
+  UOpenSSL,
+  UCrypto,
   Forms,
   Forms,
-  UBlockChain in 'Units\PascalCoin\UBlockChain.pas',
-  UCrypto in 'Units\PascalCoin\UCrypto.pas',
-  UTime in 'Units\PascalCoin\UTime.pas',
-  UWalletKeys in 'Units\PascalCoin\UWalletKeys.pas',
-  UOpTransaction in 'Units\PascalCoin\UOpTransaction.pas',
-  UNetProtocol in 'Units\PascalCoin\UNetProtocol.pas',
-  UAccounts in 'Units\PascalCoin\UAccounts.pas',
-  UConst in 'Units\PascalCoin\UConst.pas',
-  UThread in 'Units\PascalCoin\UThread.pas',
-  ULog in 'Units\PascalCoin\ULog.pas',
-  UNode in 'Units\PascalCoin\UNode.pas',
-  UECIES in 'Units\PascalCoin\UECIES.pas',
-  UAES in 'Units\PascalCoin\UAES.pas',
-  UFRMWallet in 'Units\Forms\UFRMWallet.pas' {FRMWallet},
-  UFileStorage in 'Units\PascalCoin\UFileStorage.pas',
-  UCommon in 'Units\PascalCoin\Utils\UCommon.pas',
-  UFolderHelper in 'Units\Utils\UFolderHelper.pas',
-  UAppParams in 'Units\Utils\UAppParams.pas',
-  UGridUtils in 'Units\Utils\UGridUtils.pas',
-  UWizard in 'Units\Utils\UWizard.pas',
-  UFRMPascalCoinWalletConfig in 'Units\Forms\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
-  UFRMAbout in 'Units\Forms\UFRMAbout.pas' {FRMAbout},
-  UFRMOperation in 'Units\Forms\UFRMOperation.pas' {FRMOperation},
-  UFRMWalletKeys in 'Units\Forms\UFRMWalletKeys.pas' {FRMWalletKeys},
-  UFRMNewPrivateKeyType in 'Units\Forms\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
-  UFRMPayloadDecoder in 'Units\Forms\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
-  UFRMNodesIp in 'Units\Forms\UFRMNodesIp.pas' {FRMNodesIp},
-  UTCPIP in 'Units\PascalCoin\UTCPIP.pas',
-  UJSONFunctions in 'Units\Utils\UJSONFunctions.pas',
-  URPC in 'Units\PascalCoin\URPC.pas',
-  UPoolMining in 'Units\PascalCoin\UPoolMining.pas',
-  UOpenSSL in 'Units\PascalCoin\UOpenSSL.pas',
-  UOpenSSLdef in 'Units\PascalCoin\UOpenSSLdef.pas';
-
-{$R *.res}
+  UFRMWallet,
+  UUserInterface;
 
 
+//{$R *.res}
+var
+   mainForm : TFRMWallet;
 begin
 begin
+  // Start OpenSSL dll
+  if Not LoadSSLCrypt then
+    raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
+  TCrypto.InitCrypto;
+
   Application.Initialize;
   Application.Initialize;
-  Application.MainFormOnTaskbar := True;
+  {$IFDEF WINDOWS}{$Warnings OFF}
+  Application.MainFormOnTaskBar := True;
+  {$Warnings ON}{$ENDIF}
   Application.Title := 'Pascal Coin Wallet, Miner & Explorer';
   Application.Title := 'Pascal Coin Wallet, Miner & Explorer';
-  Application.CreateForm(TFRMWallet, FRMWallet);
+  Application.CreateForm(TFRMWallet, mainForm);
+  TUserInterface.Start(mainForm);
   Application.Run;
   Application.Run;
 end.
 end.

+ 87 - 19
PascalCoinWalletLazarus.lpi

@@ -38,7 +38,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="44">
+    <Units Count="54">
       <Unit0>
       <Unit0>
         <Filename Value="PascalCoinWalletLazarus.dpr"/>
         <Filename Value="PascalCoinWalletLazarus.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -211,46 +211,110 @@
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit35>
       </Unit35>
       <Unit36>
       <Unit36>
-        <Filename Value="Units\Utils\UCommon.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit36>
-      <Unit37>
         <Filename Value="Units\Utils\UWizard.pas"/>
         <Filename Value="Units\Utils\UWizard.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="WizardHostForm"/>
         <ComponentName Value="WizardHostForm"/>
         <HasResources Value="True"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
         <ResourceBaseClass Value="Form"/>
-      </Unit37>
-      <Unit38>
+      </Unit36>
+      <Unit37>
         <Filename Value="Units\Utils\generics.collections.pas"/>
         <Filename Value="Units\Utils\generics.collections.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.Collections"/>
         <UnitName Value="Generics.Collections"/>
-      </Unit38>
-      <Unit39>
+      </Unit37>
+      <Unit38>
         <Filename Value="Units\Utils\generics.defaults.pas"/>
         <Filename Value="Units\Utils\generics.defaults.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.Defaults"/>
         <UnitName Value="Generics.Defaults"/>
-      </Unit39>
-      <Unit40>
+      </Unit38>
+      <Unit39>
         <Filename Value="Units\Utils\generics.hashes.pas"/>
         <Filename Value="Units\Utils\generics.hashes.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.Hashes"/>
         <UnitName Value="Generics.Hashes"/>
-      </Unit40>
-      <Unit41>
+      </Unit39>
+      <Unit40>
         <Filename Value="Units\Utils\generics.helpers.pas"/>
         <Filename Value="Units\Utils\generics.helpers.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.Helpers"/>
         <UnitName Value="Generics.Helpers"/>
-      </Unit41>
-      <Unit42>
+      </Unit40>
+      <Unit41>
         <Filename Value="Units\Utils\generics.memoryexpanders.pas"/>
         <Filename Value="Units\Utils\generics.memoryexpanders.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.MemoryExpanders"/>
         <UnitName Value="Generics.MemoryExpanders"/>
-      </Unit42>
-      <Unit43>
+      </Unit41>
+      <Unit42>
         <Filename Value="Units\Utils\generics.strings.pas"/>
         <Filename Value="Units\Utils\generics.strings.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="Generics.Strings"/>
         <UnitName Value="Generics.Strings"/>
+      </Unit42>
+      <Unit43>
+        <Filename Value="Units\Forms\UFRMMessages.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMMessages"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit43>
       </Unit43>
+      <Unit44>
+        <Filename Value="Units\Forms\UFRMNodes.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMNodes"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit44>
+      <Unit45>
+        <Filename Value="Units\Forms\UFRMLogs.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMLogs"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit45>
+      <Unit46>
+        <Filename Value="Units\Forms\UFRMOperationExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMOperationExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit46>
+      <Unit47>
+        <Filename Value="Units\Forms\UFRMBlockExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMBlockExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit47>
+      <Unit48>
+        <Filename Value="Units\Forms\UFRMPendingOperations.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMPendingOperations"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit48>
+      <Unit49>
+        <Filename Value="Units\Forms\UFRMAccountExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAccountExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit49>
+      <Unit50>
+        <Filename Value="Units\Forms\UFRMSyncronizationDialog.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMSyncronizationDialog"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit50>
+      <Unit51>
+        <Filename Value="Units\Forms\UUserInterface.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit51>
+      <Unit52>
+        <Filename Value="Units\Utils\UCommonUI.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit52>
+      <Unit53>
+        <Filename Value="Units\Utils\UCommon.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit53>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>
@@ -271,7 +335,7 @@
     </Parsing>
     </Parsing>
     <CodeGeneration>
     <CodeGeneration>
       <Optimizations>
       <Optimizations>
-        <OptimizationLevel Value="2"/>
+        <OptimizationLevel Value="0"/>
       </Optimizations>
       </Optimizations>
     </CodeGeneration>
     </CodeGeneration>
     <Linking>
     <Linking>
@@ -282,7 +346,11 @@
       </Options>
       </Options>
     </Linking>
     </Linking>
     <Other>
     <Other>
-      <CustomOptions Value="-dBorland -dVer150 -dDelphi7 -dCompiler6_Up -dPUREPASCAL"/>
+      <Verbosity>
+        <ShowHintsForUnusedUnitsInMainSrc Value="True"/>
+        <ShowHintsForSenderNotUsed Value="True"/>
+      </Verbosity>
+      <CustomOptions Value="-dBorland -dVer150 -dDelphi7 -dCompiler6_Up -dPUREPASCAL -vD"/>
     </Other>
     </Other>
   </CompilerOptions>
   </CompilerOptions>
   <Debugging>
   <Debugging>

+ 366 - 0
PascalCoinWalletLazarus.lpi.bak

@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="10"/>
+    <PathDelim Value="\"/>
+    <General>
+      <Flags>
+        <MainUnitHasUsesSectionForAllUnits Value="False"/>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="PascalCoinWalletLazarus"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+      <Icon Value="0"/>
+    </General>
+    <i18n>
+      <EnableI18N LFM="False"/>
+    </i18n>
+    <VersionInfo>
+      <StringTable ProductVersion=""/>
+    </VersionInfo>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+    </PublishOptions>
+    <RunParams>
+      <local>
+        <FormatVersion Value="1"/>
+      </local>
+    </RunParams>
+    <RequiredPackages Count="1">
+      <Item1>
+        <PackageName Value="LCL"/>
+      </Item1>
+    </RequiredPackages>
+    <Units Count="52">
+      <Unit0>
+        <Filename Value="PascalCoinWalletLazarus.dpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="Units\PascalCoin\UBlockChain.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit1>
+      <Unit2>
+        <Filename Value="Units\PascalCoin\UCrypto.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit2>
+      <Unit3>
+        <Filename Value="Units\PascalCoin\UTime.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit3>
+      <Unit4>
+        <Filename Value="Units\PascalCoin\UWalletKeys.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit4>
+      <Unit5>
+        <Filename Value="Units\PascalCoin\UOpTransaction.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit5>
+      <Unit6>
+        <Filename Value="Units\PascalCoin\UNetProtocol.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit6>
+      <Unit7>
+        <Filename Value="Units\PascalCoin\UAccounts.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit7>
+      <Unit8>
+        <Filename Value="Units\PascalCoin\UConst.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit8>
+      <Unit9>
+        <Filename Value="Units\PascalCoin\UThread.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit9>
+      <Unit10>
+        <Filename Value="Units\PascalCoin\ULog.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit10>
+      <Unit11>
+        <Filename Value="Units\PascalCoin\UNode.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit11>
+      <Unit12>
+        <Filename Value="Units\PascalCoin\UECIES.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit12>
+      <Unit13>
+        <Filename Value="Units\Forms\UFRMWallet.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMWallet"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit13>
+      <Unit14>
+        <Filename Value="Units\Utils\UFolderHelper.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit14>
+      <Unit15>
+        <Filename Value="Units\Utils\UAppParams.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit15>
+      <Unit16>
+        <Filename Value="Units\Utils\UGridUtils.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit16>
+      <Unit17>
+        <Filename Value="Units\Forms\UFRMPascalCoinWalletConfig.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMPascalCoinWalletConfig"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit17>
+      <Unit18>
+        <Filename Value="Units\Forms\UFRMAbout.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAbout"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit18>
+      <Unit19>
+        <Filename Value="Units\Forms\UFRMOperation.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMOperation"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit19>
+      <Unit20>
+        <Filename Value="Units\Forms\UFRMWalletKeys.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMWalletKeys"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit20>
+      <Unit21>
+        <Filename Value="Units\Forms\UFRMNewPrivateKeyType.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMNewPrivateKeyType"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit21>
+      <Unit22>
+        <Filename Value="Units\Forms\UFRMPayloadDecoder.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMPayloadDecoder"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit22>
+      <Unit23>
+        <Filename Value="Units\Forms\UFRMNodesIp.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMNodesIp"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit23>
+      <Unit24>
+        <Filename Value="Units\PascalCoin\UTCPIP.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit24>
+      <Unit25>
+        <Filename Value="Units\Utils\UJSONFunctions.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit25>
+      <Unit26>
+        <Filename Value="Units\PascalCoin\URPC.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit26>
+      <Unit27>
+        <Filename Value="Units\PascalCoin\UPoolMining.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit27>
+      <Unit28>
+        <Filename Value="Units\PascalCoin\UOpenSSL.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit28>
+      <Unit29>
+        <Filename Value="Units\PascalCoin\UOpenSSLdef.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit29>
+      <Unit30>
+        <Filename Value="Units\PascalCoin\UFileStorage.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit30>
+      <Unit31>
+        <Filename Value="Units\PascalCoin\config.inc"/>
+        <IsPartOfProject Value="True"/>
+      </Unit31>
+      <Unit32>
+        <Filename Value="Units\PascalCoin\UAES.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit32>
+      <Unit33>
+        <Filename Value="Units\PascalCoin\UChunk.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit33>
+      <Unit34>
+        <Filename Value="Units\Forms\UFRMAccountSelect.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAccountSelect"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit34>
+      <Unit35>
+        <Filename Value="Units\PascalCoin\UBaseTypes.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit35>
+      <Unit36>
+        <Filename Value="Units\Utils\UCommon.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit36>
+      <Unit37>
+        <Filename Value="Units\Utils\UWizard.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="WizardHostForm"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit37>
+      <Unit38>
+        <Filename Value="Units\Utils\generics.collections.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.Collections"/>
+      </Unit38>
+      <Unit39>
+        <Filename Value="Units\Utils\generics.defaults.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.Defaults"/>
+      </Unit39>
+      <Unit40>
+        <Filename Value="Units\Utils\generics.hashes.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.Hashes"/>
+      </Unit40>
+      <Unit41>
+        <Filename Value="Units\Utils\generics.helpers.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.Helpers"/>
+      </Unit41>
+      <Unit42>
+        <Filename Value="Units\Utils\generics.memoryexpanders.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.MemoryExpanders"/>
+      </Unit42>
+      <Unit43>
+        <Filename Value="Units\Utils\generics.strings.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="Generics.Strings"/>
+      </Unit43>
+      <Unit44>
+        <Filename Value="Units\Forms\UFRMMessages.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMMessages"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit44>
+      <Unit45>
+        <Filename Value="Units\Forms\UFRMNodes.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMNodes"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit45>
+      <Unit46>
+        <Filename Value="Units\Forms\UFRMLogs.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMLogs"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit46>
+      <Unit47>
+        <Filename Value="Units\Forms\UFRMOperationExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMOperationExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit47>
+      <Unit48>
+        <Filename Value="Units\Forms\UFRMBlockExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMBlockExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit48>
+      <Unit49>
+        <Filename Value="Units\Forms\UFRMPendingOperations.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMPendingOperations"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit49>
+      <Unit50>
+        <Filename Value="Units\Forms\UFRMAccountExplorer.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAccountExplorer"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit50>
+      <Unit51>
+        <Filename Value="Units\Forms\UFRMSyncronizationDialog.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMSyncronizationDialog"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit51>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="PascalCoinWalletLazarus"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="Synapse\lib;Units\Forms;Units\PascalCoin;Units\Utils;Units\SQLite3"/>
+      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <SyntaxMode Value="Delphi"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <Optimizations>
+        <OptimizationLevel Value="0"/>
+      </Optimizations>
+    </CodeGeneration>
+    <Linking>
+      <Options>
+        <Win32>
+          <GraphicApplication Value="True"/>
+        </Win32>
+      </Options>
+    </Linking>
+    <Other>
+      <Verbosity>
+        <ShowHintsForUnusedUnitsInMainSrc Value="True"/>
+        <ShowHintsForSenderNotUsed Value="True"/>
+      </Verbosity>
+      <CustomOptions Value="-vd
+-dBorland
+-dVer150
+-dDelphi7
+-dCompiler6_Up
+-dPUREPASCAL"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

BIN
Resources/WalletImages/network-off-16x16.png


BIN
Resources/WalletImages/network-on-16x16.png


BIN
Resources/WalletImages/sync-16x16.png


BIN
Resources/WalletImages/wallet-locked-16x16.png


BIN
Resources/WalletImages/wallet-unlocked-16x16.png


+ 520 - 0
Units/Forms/UFRMAccountExplorer.lfm

@@ -0,0 +1,520 @@
+object FRMAccountExplorer: TFRMAccountExplorer
+  Left = 264
+  Height = 471
+  Top = 67
+  Width = 816
+  Caption = 'Accounts Explorer'
+  ClientHeight = 451
+  ClientWidth = 816
+  Menu = meAccountExplorerMenu
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '1.6.4.0'
+  object Splitter1: TSplitter
+    Left = 380
+    Height = 385
+    Top = 66
+    Width = 5
+  end
+  object pnlMyAccountsTop: TPanel
+    Left = 0
+    Height = 66
+    Top = 0
+    Width = 816
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 66
+    ClientWidth = 816
+    TabOrder = 0
+    object Label18: TLabel
+      Left = 11
+      Height = 15
+      Top = 40
+      Width = 69
+      Caption = 'Find account'
+      ParentColor = False
+    end
+    object cbMyPrivateKeys: TComboBox
+      Left = 308
+      Height = 23
+      Top = 5
+      Width = 411
+      ItemHeight = 15
+      OnChange = cbMyPrivateKeysChange
+      Style = csDropDownList
+      TabOrder = 0
+    end
+    object cbExploreMyAccounts: TCheckBox
+      Left = 11
+      Height = 19
+      Top = 9
+      Width = 255
+      Caption = 'Explore accounts with one of my Wallet Keys'
+      OnChange = cbExploreMyAccountsChange
+      TabOrder = 1
+    end
+    object ebFindAccountNumber: TEdit
+      Left = 95
+      Height = 23
+      Top = 33
+      Width = 83
+      OnChange = ebFindAccountNumberChange
+      OnExit = ebFindAccountNumberExit
+      TabOrder = 3
+    end
+    object bbChangeKeyName: TBitBtn
+      Left = 733
+      Height = 25
+      Top = 5
+      Width = 126
+      Caption = 'Change Key name'
+      OnClick = bbChangeKeyNameClick
+      TabOrder = 2
+    end
+    object cbFilterAccounts: TCheckBox
+      Left = 308
+      Height = 19
+      Top = 35
+      Width = 157
+      Caption = 'Filter accounts by balance'
+      OnChange = cbFilterAccountsChange
+      TabOrder = 4
+    end
+    object ebFilterAccountByBalanceMin: TEdit
+      Left = 472
+      Height = 23
+      Hint = 'Min balance'
+      Top = 33
+      Width = 83
+      OnExit = ebFilterAccountByBalanceMinExit
+      OnKeyPress = ebFilterAccountByBalanceMinKeyPress
+      TabOrder = 5
+    end
+    object ebFilterAccountByBalanceMax: TEdit
+      Left = 568
+      Height = 23
+      Hint = 'Max balance'
+      Top = 33
+      Width = 83
+      OnExit = ebFilterAccountByBalanceMinExit
+      OnKeyPress = ebFilterAccountByBalanceMinKeyPress
+      TabOrder = 6
+    end
+    object sbSearchAccount: TSpeedButton
+      Left = 184
+      Height = 23
+      Top = 33
+      Width = 24
+      Glyph.Data = {
+        36030000424D3803000000000000360000002800000010000000100000000100
+        18000000000000000000120B0000120B00000000000000000000FF00FF4A667C
+        BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF
+        2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF
+        00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3
+        B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD
+        EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF
+        FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1
+        C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF
+        FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF
+        E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4
+        C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4
+        C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8
+        DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890
+        86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF
+      }
+      OnClick = sbSearchAccountClick
+    end
+  end
+  object pnlAccounts: TPanel
+    Left = 0
+    Height = 385
+    Top = 66
+    Width = 380
+    Align = alLeft
+    BevelOuter = bvNone
+    ClientHeight = 385
+    ClientWidth = 380
+    TabOrder = 1
+    object dgAccounts: TDrawGrid
+      Left = 0
+      Height = 351
+      Top = 0
+      Width = 380
+      Align = alClient
+      ExtendedSelect = False
+      TabOrder = 0
+      TitleFont.Color = clWindowText
+      TitleFont.Height = -11
+      TitleFont.Name = 'Tahoma'
+      OnClick = dgAccountsClick
+    end
+    object pnlAccountsInfo: TPanel
+      Left = 0
+      Height = 34
+      Top = 351
+      Width = 380
+      Align = alBottom
+      BevelOuter = bvNone
+      ClientHeight = 34
+      ClientWidth = 380
+      TabOrder = 1
+      object Label17: TLabel
+        Left = 5
+        Height = 15
+        Top = 10
+        Width = 53
+        Caption = 'Accounts:'
+        ParentColor = False
+      end
+      object Label19: TLabel
+        Left = 136
+        Height = 15
+        Top = 10
+        Width = 44
+        Caption = 'Balance:'
+        ParentColor = False
+      end
+      object lblAccountsCount: TLabel
+        Left = 60
+        Height = 15
+        Top = 10
+        Width = 18
+        Caption = '000'
+        ParentColor = False
+      end
+      object lblAccountsBalance: TLabel
+        Left = 200
+        Height = 15
+        Top = 10
+        Width = 18
+        Caption = '000'
+        ParentColor = False
+      end
+      object bbAccountsRefresh: TBitBtn
+        Left = 302
+        Height = 25
+        Top = 6
+        Width = 75
+        Anchors = [akTop, akRight]
+        Caption = 'Refresh'
+        Glyph.Data = {
+          36030000424D3603000000000000360000002800000010000000100000000100
+          18000000000000030000120B0000120B00000000000000000000FF00FFFF00FF
+          C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6
+          A4C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEFCFBFEFCFBFEFCFBFEFCFBFE
+          FCFBFEFCFBFEFCFBFEFCFBFEFCFBFEFCFBC2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FEFCFBFEFCFBFEFCFBFEFCFBD8EBD6018A02018A02D8EBD6FEFCFBFEFC
+          FBC2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEFBF7FEFBF7018A02D8EAD201
+          8A02D8EAD2D8EAD2018A02FEFBF7FEFBF7C2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FEF9F4FEF9F4018A02018A02D8E8D0FEF9F4FEF9F4D8E8D0FEF9F4FEF9
+          F4C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEF7F0FEF7F0018A02018A0201
+          8A02FEF7F0FEF7F0FEF7F0FEF7F0FEF7F0C2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FEF5ECFEF5ECFEF5ECFEF5ECFEF5EC018A02018A02018A02FEF5ECFEF5
+          ECC2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEF3E9FEF3E9D8E3C7FEF3E9FE
+          F3E9D8E3C7018A02018A02FEF3E9FEF3E9C2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FFF1E5FFF1E5018A02D9E2C3D9E2C3018A02D9E2C3018A02FFF1E5FFF1
+          E5C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FFF0E2FFF0E2D9E1C1018A0201
+          8A02D9E1C1DDCFC2DDCFC2DDCFC2DDCFC2C2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FFEEDEFFEEDEFFEEDEFFEEDEFFEEDEFFEEDEC5B5A9C3B4A8C2B3A7C1B2
+          A6C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FFECDAFFECDAFFECDAFFECDAFF
+          ECDAFFECDAB0A296B0A296B0A296B0A296C2A6A4FF00FFFF00FFFF00FFFF00FF
+          C2A6A4FFEAD7FFEAD7FFEAD7FFEAD7FFEAD7C9B9ACFBF8F4FBF8F4E6DAD9C2A6
+          A4FF00FFFF00FFFF00FFFF00FFFF00FFC2A6A4FFE8D3FFE8D3FFE8D3FFE8D3FF
+          E8D3C9B9ACFBF8F4DFCEC7C2A6A4FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+          C2A6A4FFE6D0FFE6D0FFE6D0FFE6D0FFE6D0C9B9ACDFCEC7C2A6A4FF00FFFF00
+          FFFF00FFFF00FFFF00FFFF00FFFF00FFC2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2
+          A6A4C2A6A4C2A6A4FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        }
+        OnClick = bbAccountsRefreshClick
+        TabOrder = 0
+      end
+    end
+  end
+  object pcAccountsOptions: TPageControl
+    Left = 385
+    Height = 385
+    Top = 66
+    Width = 431
+    ActivePage = tsMultiSelectAccounts
+    Align = alClient
+    TabIndex = 1
+    TabOrder = 2
+    object tsAccountOperations: TTabSheet
+      Caption = 'Operations of selected Account'
+      ClientHeight = 355
+      ClientWidth = 470
+      object dgAccountOperations: TDrawGrid
+        Left = 0
+        Height = 355
+        Top = 0
+        Width = 470
+        Align = alClient
+        ExtendedSelect = False
+        TabOrder = 0
+        TitleFont.Color = clWindowText
+        TitleFont.Height = -11
+        TitleFont.Name = 'Tahoma'
+        OnClick = dgAccountOperationsClick
+        OnDblClick = dgAccountOperationsClick
+      end
+    end
+    object tsMultiSelectAccounts: TTabSheet
+      Caption = 'Selected accounts for massive operations'
+      ClientHeight = 357
+      ClientWidth = 423
+      ImageIndex = 1
+      object dgSelectedAccounts: TDrawGrid
+        Left = 41
+        Height = 300
+        Top = 31
+        Width = 273
+        Align = alLeft
+        Anchors = [akTop, akLeft, akRight, akBottom]
+        ExtendedSelect = False
+        TabOrder = 0
+        TitleFont.Color = clWindowText
+        TitleFont.Height = -11
+        TitleFont.Name = 'Tahoma'
+      end
+      object pnlSelectedAccountsTop: TPanel
+        Left = 0
+        Height = 31
+        Top = 0
+        Width = 423
+        Align = alTop
+        BevelOuter = bvNone
+        ClientHeight = 31
+        ClientWidth = 423
+        Font.Color = clWindowText
+        Font.Height = -13
+        Font.Name = 'Tahoma'
+        Font.Style = [fsBold]
+        ParentFont = False
+        TabOrder = 1
+        object Label15: TLabel
+          Left = 41
+          Height = 16
+          Top = 4
+          Width = 361
+          Caption = 'Select multiple accounts to execute massive operations'
+          ParentColor = False
+        end
+      end
+      object pnlSelectedAccountsBottom: TPanel
+        Left = 0
+        Height = 26
+        Top = 331
+        Width = 423
+        Align = alBottom
+        BevelOuter = bvNone
+        ClientHeight = 26
+        ClientWidth = 423
+        TabOrder = 2
+        object Label20: TLabel
+          Left = 41
+          Height = 15
+          Top = 6
+          Width = 53
+          Caption = 'Accounts:'
+          ParentColor = False
+        end
+        object lblSelectedAccountsCount: TLabel
+          Left = 96
+          Height = 15
+          Top = 6
+          Width = 18
+          Caption = '000'
+          ParentColor = False
+        end
+        object Label22: TLabel
+          Left = 156
+          Height = 15
+          Top = 6
+          Width = 97
+          Caption = 'Accounts Balance:'
+          ParentColor = False
+        end
+        object lblSelectedAccountsBalance: TLabel
+          Left = 250
+          Height = 15
+          Top = 6
+          Width = 18
+          Caption = '000'
+          ParentColor = False
+        end
+      end
+      object pnlSelectedAccountsLeft: TPanel
+        Left = 0
+        Height = 300
+        Top = 31
+        Width = 41
+        Align = alLeft
+        BevelOuter = bvNone
+        ClientHeight = 300
+        ClientWidth = 41
+        TabOrder = 3
+        object sbSelectedAccountsAdd: TSpeedButton
+          Left = 2
+          Height = 31
+          Top = 0
+          Width = 33
+          Caption = '>'
+          OnClick = sbSelectedAccountsAddClick
+        end
+        object sbSelectedAccountsAddAll: TSpeedButton
+          Left = 2
+          Height = 31
+          Top = 37
+          Width = 33
+          Caption = '>>'
+          OnClick = sbSelectedAccountsAddAllClick
+        end
+        object sbSelectedAccountsDel: TSpeedButton
+          Left = 2
+          Height = 31
+          Top = 74
+          Width = 33
+          Caption = '<'
+          OnClick = sbSelectedAccountsDelClick
+        end
+        object sbSelectedAccountsDelAll: TSpeedButton
+          Left = 2
+          Height = 31
+          Top = 111
+          Width = 33
+          Caption = '<<'
+          OnClick = sbSelectedAccountsDelAllClick
+        end
+      end
+      object bbSelectedAccountsOperation: TBitBtn
+        Left = 320
+        Height = 61
+        Top = 31
+        Width = 75
+        Anchors = [akTop, akRight]
+        Caption = 'Operations'
+        Glyph.Data = {
+          F6060000424DF606000000000000360000002800000018000000180000000100
+          180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+          FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+          FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+          00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+          FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C8518FF00FFFF00FFFF00FFFF00
+          FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+          9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C8518
+          FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+          FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+          00FF0C85181399220C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+          34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF0C85180C85180C85180C85
+          180C85180C85180C85180C85181DAC31139A220C8518FF00FFFF00FFFF00FFFF
+          00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE0C8518
+          57E38851DD7E4AD77443D0693BC95E34C1522BBA4725B33C1EAE33149B230C85
+          18FF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+          DEFE77DEFE0C85185EE89059E48953DE804CD87645D16C3DCA6035C2542DBB49
+          26B53F1FAF35149B250C8518FF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+          F38FE8FF7CE2FE7CE3FE7CE3FE0C851861EB955FE9925AE58B54DF824DD97846
+          D26D3ECB6237C4562FBD4C27B64021B037159B250C8518FF00FFFF00FF019ACF
+          60CAEF1FA8D85EC1E1C2E6ED8ACEE08FCFE18ECFE10C851861EB9561EB955FEA
+          935CE58D56E0844FDB7A48D47040CD6538C65931BF4D1DA3320C8518FF00FFFF
+          00FFFF00FF019ACF65CFF53EB7E52CA9D4C5EFF8ACF3FEA5F2FFA5F2FF0C8518
+          61EB9561EB9561EB9561EB945CE68E57E18650DC7C49D57242CE6727AD410C85
+          18FF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F32A9CC673CBE7D6FEFDB1
+          FBFDB2FBFD0C85180C85180C85180C85180C85180C85180C85180C851852DD7F
+          32B6500C851898FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB748E
+          A224A8D5B9E7F3D5F5F9D5F6F9D6F6FADCFAFBCDFDFCB9FCFCAFFAFCB0FAFCB1
+          FAFC0C85183ABE5C0C85189FFCFFA4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+          8BDBFF5FCDFFB7898973C3DD18A2D218A2D216A2D215A1D21AA4D391D7EBEBFE
+          FDDBFDFCC5FBFBC2FBFB0C85180C851883E4F3B6FDFFBAFFFFB5FCFD019ACFFF
+          00FFFF00FF019ACF99E2FF67D3FFB88989FEF5ECFDF3EBF0EFEAE5EBE8D6E5E6
+          A4D2E025A6D34DB9DDE5F8FBF5FDFCEBFCFB0C8518C4FBFF9CE4F2DAFEFFD9FE
+          FFE3FFFFADE9F5019ACFFF00FF019ACF9FE9FF70DCFFB88989FEF3E9FFF2E6FE
+          F3E9FEF3E9FEF3E9FEF3E9D4E4E439ADD422A5D49DD8ECF1F9FBEEEFEFE9FDFF
+          CEEEF7F8FFFFF7FFFFFEFFFFE9F9FD019ACFFF00FF019ACFA7EFFF76E5FFB889
+          89FFF2E5FFF0E2FFF2E5FFF2E5FFF2E5FFF2E5FFF2E5EAEBE38EC9DA44B0D501
+          9ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACFFF00FF019ACF
+          ABF6FF7EEDFFB88989FFF0E2FFEFDFFFF0E2FFF0E2FFF0E2FFF0E2FFF0E2FEEE
+          E0FBECDEFAEBDEF6E6D9B8898993F7FF019ACFFF00FFFF00FFFF00FFFF00FFFF
+          00FFFF00FF019ACFC7FFFF82F5FFB88989FFEEDFFFECDBFFEEDFFFEEDFFFEEDF
+          FFEEDFF9E8D9DECCC1D9CABDCFBDB4C8B3ACB88989B5FFFF019ACFFF00FFFF00
+          FFFF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFFB88989FFECDBFFEBD8FF
+          ECDBFFECDBFFECDBFFECDBF5E2D2C4ABA7C2A8A5BBA39FC2AFA9B88989019ACF
+          FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFB889
+          89FFEBD8FFEAD5FFEBD8FFEBD8FFEBD8FFEBD8FFEBD8D9C8C5FEFEFDFEF6EFDE
+          C9C0B88989FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+          FF00FF019ACFB88989FFE9D5FFE8D3FFE9D5FFE9D5FFE9D5FFE9D5FFE9D5C6AD
+          A9FEF8F2E8D4CACD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+          00FFFF00FFFF00FFFF00FFFF00FFB88989FFE7D1FFE7D0FFE7D1FFE7D1FFE7D1
+          FFE7D1E7CEBFD3BFB9E8D5CCCD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+          FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB88989FFE6CFFFE6CFFF
+          E6CFFFE6CFFFE6CFFFE6CFD5BBB2E0CCC5CD9999FF00FFFF00FFFF00FFFF00FF
+          FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB889
+          89B88989B88989B88989B88989B88989B88989B88989B88989FF00FFFF00FFFF
+          00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        }
+        Layout = blGlyphTop
+        OnClick = bbSelectedAccountsOperationClick
+        TabOrder = 4
+      end
+    end
+  end
+  object meAccountExplorerMenu: TMainMenu
+    left = 240
+    top = 16
+    object miTools: TMenuItem
+      Caption = 'Tools'
+      object miNewOperation: TMenuItem
+        Caption = 'New Operation'
+        OnClick = miNewOperationClick
+      end
+      object N1: TMenuItem
+        Caption = '-'
+      end
+      object miFindAccount: TMenuItem
+        Caption = 'Find account'
+        ShortCut = 16454
+      end
+      object miAccountInformation: TMenuItem
+        Caption = 'Account Information'
+        ShortCut = 112
+        OnClick = MiAccountInformationClick
+      end
+      object N2: TMenuItem
+        Caption = '-'
+      end
+      object miAddAccountToSelected: TMenuItem
+        Caption = 'Add account to selected'
+        ShortCut = 117
+        OnClick = miAddAccountToSelectedClick
+      end
+      object miRemoveAccountFromSelected: TMenuItem
+        Caption = 'Remove account from selected'
+        ShortCut = 118
+        OnClick = miRemoveAccountFromSelectedClick
+      end
+      object N3: TMenuItem
+        Caption = '-'
+      end
+      object miFindPreviousAccountWithHighBalance: TMenuItem
+        Caption = 'Find previous account with high balance'
+        ShortCut = 16498
+        OnClick = miFindPreviousAccountWithHighBalanceClick
+      end
+      object miFindNextAccountWithHighBalance: TMenuItem
+        Caption = 'Find next account with high balance'
+        ShortCut = 114
+        OnClick = miFindNextAccountWithHighBalanceClick
+      end
+    end
+  end
+end

+ 827 - 0
Units/Forms/UFRMAccountExplorer.pas

@@ -0,0 +1,827 @@
+unit UFRMAccountExplorer;
+
+{$mode delphi}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
+  ExtCtrls, StdCtrls, Buttons, Grids, Menus, UGridUtils, UNode, UAccounts, UBlockChain;
+
+type
+
+  { TFRMAccountExplorer }
+
+  TFRMAccountExplorer = class(TForm)
+    bbAccountsRefresh: TBitBtn;
+    bbAccountsRefresh1: TBitBtn;
+    bbAccountsRefresh2: TBitBtn;
+    bbChangeKeyName: TBitBtn;
+    bbChangeKeyName1: TBitBtn;
+    bbChangeKeyName2: TBitBtn;
+    bbSelectedAccountsOperation: TBitBtn;
+    bbSelectedAccountsOperation1: TBitBtn;
+    bbSelectedAccountsOperation2: TBitBtn;
+    cbExploreMyAccounts: TCheckBox;
+    cbExploreMyAccounts1: TCheckBox;
+    cbExploreMyAccounts2: TCheckBox;
+    cbFilterAccounts: TCheckBox;
+    cbFilterAccounts1: TCheckBox;
+    cbFilterAccounts2: TCheckBox;
+    cbMyPrivateKeys: TComboBox;
+    cbMyPrivateKeys1: TComboBox;
+    cbMyPrivateKeys2: TComboBox;
+    dgAccountOperations: TDrawGrid;
+    dgAccountOperations1: TDrawGrid;
+    dgAccountOperations2: TDrawGrid;
+    dgAccounts: TDrawGrid;
+    dgAccounts1: TDrawGrid;
+    dgAccounts2: TDrawGrid;
+    dgSelectedAccounts: TDrawGrid;
+    dgSelectedAccounts1: TDrawGrid;
+    dgSelectedAccounts2: TDrawGrid;
+    ebFilterAccountByBalanceMax: TEdit;
+    ebFilterAccountByBalanceMax1: TEdit;
+    ebFilterAccountByBalanceMax2: TEdit;
+    ebFilterAccountByBalanceMin: TEdit;
+    ebFilterAccountByBalanceMin1: TEdit;
+    ebFilterAccountByBalanceMin2: TEdit;
+    ebFindAccountNumber: TEdit;
+    ebFindAccountNumber1: TEdit;
+    ebFindAccountNumber2: TEdit;
+    Label15: TLabel;
+    Label16: TLabel;
+    Label17: TLabel;
+    Label18: TLabel;
+    Label19: TLabel;
+    Label20: TLabel;
+    Label21: TLabel;
+    Label22: TLabel;
+    Label23: TLabel;
+    Label24: TLabel;
+    Label25: TLabel;
+    Label26: TLabel;
+    Label27: TLabel;
+    Label28: TLabel;
+    Label29: TLabel;
+    Label30: TLabel;
+    Label31: TLabel;
+    Label32: TLabel;
+    lblAccountsBalance: TLabel;
+    lblAccountsBalance1: TLabel;
+    lblAccountsBalance2: TLabel;
+    lblAccountsCount: TLabel;
+    lblAccountsCount1: TLabel;
+    lblAccountsCount2: TLabel;
+    lblSelectedAccountsBalance: TLabel;
+    lblSelectedAccountsBalance1: TLabel;
+    lblSelectedAccountsBalance2: TLabel;
+    lblSelectedAccountsCount: TLabel;
+    lblSelectedAccountsCount1: TLabel;
+    lblSelectedAccountsCount2: TLabel;
+    meAccountExplorerMenu : TMainMenu;
+    miFindAccount: TMenuItem;
+    miTools: TMenuItem;
+    pcAccountsOptions: TPageControl;
+    pcAccountsOptions1: TPageControl;
+    pcAccountsOptions2: TPageControl;
+    pnlAccounts: TPanel;
+    pnlAccounts1: TPanel;
+    pnlAccounts2: TPanel;
+    pnlAccountsInfo: TPanel;
+    pnlAccountsInfo1: TPanel;
+    pnlAccountsInfo2: TPanel;
+    pnlMyAccountsTop: TPanel;
+    pnlMyAccountsTop1: TPanel;
+    pnlMyAccountsTop2: TPanel;
+    pnlSelectedAccountsBottom: TPanel;
+    pnlSelectedAccountsBottom1: TPanel;
+    pnlSelectedAccountsBottom2: TPanel;
+    pnlSelectedAccountsLeft: TPanel;
+    pnlSelectedAccountsLeft1: TPanel;
+    pnlSelectedAccountsLeft2: TPanel;
+    pnlSelectedAccountsTop: TPanel;
+    pnlSelectedAccountsTop1: TPanel;
+    pnlSelectedAccountsTop2: TPanel;
+    sbSearchAccount: TSpeedButton;
+    sbSearchAccount1: TSpeedButton;
+    sbSearchAccount2: TSpeedButton;
+    sbSelectedAccountsAdd: TSpeedButton;
+    sbSelectedAccountsAdd1: TSpeedButton;
+    sbSelectedAccountsAdd2: TSpeedButton;
+    sbSelectedAccountsAddAll: TSpeedButton;
+    sbSelectedAccountsAddAll1: TSpeedButton;
+    sbSelectedAccountsAddAll2: TSpeedButton;
+    sbSelectedAccountsDel: TSpeedButton;
+    sbSelectedAccountsDel1: TSpeedButton;
+    sbSelectedAccountsDel2: TSpeedButton;
+    sbSelectedAccountsDelAll: TSpeedButton;
+    sbSelectedAccountsDelAll1: TSpeedButton;
+    sbSelectedAccountsDelAll2: TSpeedButton;
+    Splitter1: TSplitter;
+    Splitter2: TSplitter;
+    Splitter3: TSplitter;
+    tsAccountOperations: TTabSheet;
+    tsAccountOperations1: TTabSheet;
+    tsAccountOperations2: TTabSheet;
+    tsMultiSelectAccounts: TTabSheet;
+    tsMultiSelectAccounts1: TTabSheet;
+    tsMultiSelectAccounts2: TTabSheet;
+    tsMyAccounts: TTabSheet;
+    tsMyAccounts1: TTabSheet;
+    tsMyAccounts2: TTabSheet;
+    procedure bbAccountsRefreshClick(Sender: TObject);
+    procedure bbChangeKeyNameClick(Sender: TObject);
+    procedure bbSelectedAccountsOperationClick(Sender: TObject);
+    procedure cbExploreMyAccountsChange(Sender: TObject);
+    procedure cbFilterAccountsChange(Sender: TObject);
+    procedure cbMyPrivateKeysChange(Sender: TObject);
+    procedure dgAccountOperationsClick(Sender: TObject);
+    procedure dgAccountsClick(Sender: TObject);
+    procedure ebFilterAccountByBalanceMinExit(Sender: TObject);
+    procedure ebFilterAccountByBalanceMinKeyPress(Sender: TObject; var Key: char);
+    procedure ebFindAccountNumberChange(Sender: TObject);
+    procedure ebFindAccountNumberExit(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure miAccountInformationClick(Sender: TObject);
+    procedure miAddAccountToSelectedClick(Sender: TObject);
+    procedure miFindNextAccountWithHighBalanceClick(Sender: TObject);
+    procedure miFindPreviousAccountWithHighBalanceClick(Sender: TObject);
+    procedure miNewOperationClick(Sender: TObject);
+    procedure miRemoveAccountFromSelectedClick(Sender: TObject);
+    procedure sbSearchAccountClick(Sender: TObject);
+    function DoUpdateAccountsFilter: Boolean;
+    procedure sbSelectedAccountsAddAllClick(Sender: TObject);
+    procedure sbSelectedAccountsAddClick(Sender: TObject);
+    procedure sbSelectedAccountsDelAllClick(Sender: TObject);
+    procedure sbSelectedAccountsDelClick(Sender: TObject);
+    procedure UpdateAccounts(RefreshData : Boolean);
+    procedure OnSelectedAccountsGridUpdated(Sender: TObject);
+  private
+    { private declarations }
+    FUpdating : boolean;
+    FAccountsGrid : TAccountsGrid;  //HS
+    FOperationsAccountGrid : TOperationsGrid; // HS
+    FSelectedAccountsGrid : TAccountsGrid; //HS
+    FOrderedAccountsKeyList : TOrderedAccountKeysList; //HS
+    FMinAccountBalance : Int64;
+    FMaxAccountBalance : Int64;
+    Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
+    Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
+    procedure OnPrivateKeysChanged(Sender: TObject);
+  public
+    { public declarations }
+    procedure OnSelectedAccountChanged;
+    procedure Refresh;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses UFRMAccountSelect, UConst, UFRMOperation,
+     UWalletKeys, UCrypto, UFRMMemoText, UUserInterface, UCommon;
+
+{ TFRMAccountExplorer }
+
+{%region Form life-cycle}
+
+procedure TFRMAccountExplorer.FormCreate(Sender: TObject);
+begin
+  FMinAccountBalance := 0;
+  FMaxAccountBalance := CT_MaxWalletAmount;
+  FOrderedAccountsKeyList := Nil;
+  FAccountsGrid := TAccountsGrid.Create(Self);
+  FAccountsGrid.DrawGrid := dgAccounts;
+  FAccountsGrid.Node := TUserInterface.Node;
+  FAccountsGrid.AllowMultiSelect := True;
+  FSelectedAccountsGrid := TAccountsGrid.Create(Self);
+  FSelectedAccountsGrid.DrawGrid :=dgSelectedAccounts;
+  FSelectedAccountsGrid.Node := TUserInterface.Node;
+  FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
+  FOperationsAccountGrid := TOperationsGrid.Create(Self);
+  FOperationsAccountGrid.DrawGrid := dgAccountOperations;
+  FOperationsAccountGrid.Node := TUserInterface.Node;
+  FOperationsAccountGrid.MustShowAlwaysAnAccount := true;
+  pcAccountsOptions.ActivePage := tsAccountOperations;
+
+  // Subscribe to wallet events
+  TUserInterface.WalletKeys.OnChanged.Add(OnPrivateKeysChanged);
+
+  FUpdating := false;
+end;
+
+procedure TFRMAccountExplorer.FormDestroy(Sender: TObject);
+begin
+  // Unsubscribe from wallet events
+  TUserInterface.WalletKeys.OnChanged.Remove(OnPrivateKeysChanged);
+
+  // Nullify fields
+  FOperationsAccountGrid.Node := Nil;
+  FAccountsGrid.Node := Nil;
+  FSelectedAccountsGrid.Node := Nil;
+  FAccountsGrid.Node := Nil;
+
+  // Note: grids themselves are collected with Self (TComponent dependency)
+end;
+
+{%endregion}
+
+{%region Form methods}
+
+procedure TFRMAccountExplorer.Refresh;
+Var i,last_i : Integer;
+  wk : TWalletKey;
+  s : AnsiString;
+begin
+  If (Not Assigned(FOrderedAccountsKeyList)) And (Assigned(TUserInterface.Node)) Then begin
+    FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(TUserInterface.Node.Bank.SafeBox,false);
+  end;
+  if (cbMyPrivateKeys.ItemIndex>=0) then last_i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex])
+  else last_i := -1;
+  cbMyPrivateKeys.items.BeginUpdate;
+  Try
+    cbMyPrivateKeys.Items.Clear;
+    For i:=0 to TUserInterface.WalletKeys.Count-1 do begin
+      wk := TUserInterface.WalletKeys.Key[i];
+      if assigned(FOrderedAccountsKeyList) then begin
+        FOrderedAccountsKeyList.AddAccountKey(wk.AccountKey);
+      end;
+      if (wk.Name='') then begin
+        s := 'Sha256='+TCrypto.ToHexaString( TCrypto.DoSha256( TAccountComp.AccountKey2RawString(wk.AccountKey) ) );
+      end else begin
+        s := wk.Name;
+      end;
+      if Not Assigned(wk.PrivateKey) then s := s + '(*)';
+      cbMyPrivateKeys.Items.AddObject(s,TObject(i));
+    end;
+    cbMyPrivateKeys.Sorted := true;
+    cbMyPrivateKeys.Sorted := false;
+    cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));
+  Finally
+    cbMyPrivateKeys.Items.EndUpdate;
+  End;
+  last_i := cbMyPrivateKeys.Items.IndexOfObject(TObject(last_i));
+  if last_i<0 then last_i := 0;
+  if cbMyPrivateKeys.Items.Count>last_i then cbMyPrivateKeys.ItemIndex := last_i
+  else if cbMyPrivateKeys.Items.Count>=0 then cbMyPrivateKeys.ItemIndex := 0;
+end;
+
+
+{%endregion}
+
+{%region For auxillary methods}
+
+procedure TFRMAccountExplorer.FillAccountInformation(const Strings: TStrings; Const AccountNumber: Cardinal);
+Var account : TAccount;
+  s : String;
+begin
+//AntonB AccountNumber is type LongWord not cand negative
+//  if AccountNumber<0 then exit;
+  account := TUserInterface.Node.Operations.SafeBoxTransaction.Account(AccountNumber);
+  if account.name<>'' then s:='Name: '+account.name
+  else s:='';
+  Strings.Add(Format('Account: %s %s Type:%d',[TAccountComp.AccountNumberToAccountTxtNumber(AccountNumber),s,account.account_type]));
+  Strings.Add('');
+  Strings.Add(Format('Current balance: %s',[TAccountComp.FormatMoney(account.balance)]));
+  Strings.Add('');
+  Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_block,TUserInterface.Node.Bank.BlocksCount-account.updated_block]));
+  Strings.Add(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(account.accountInfo.accountKey.EC_OpenSSL_NID)]));
+  Strings.Add(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.accountKey)]));
+  if TAccountComp.IsAccountForSale(account.accountInfo) then begin
+    Strings.Add('');
+    Strings.Add('** Account is for sale: **');
+    Strings.Add(Format('Price: %s',[TAccountComp.FormatMoney(account.accountInfo.price)]));
+    Strings.Add(Format('Seller account (where to pay): %s',[TAccountComp.AccountNumberToAccountTxtNumber(account.accountInfo.account_to_pay)]));
+    if TAccountComp.IsAccountForSaleAcceptingTransactions(account.accountInfo) then begin
+      Strings.Add('');
+      Strings.Add('** Private sale **');
+      Strings.Add(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.new_publicKey)]));
+      Strings.Add('');
+      if TAccountComp.IsAccountLocked(account.accountInfo,TUserInterface.Node.Bank.BlocksCount) then begin
+        Strings.Add(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
+          [account.accountInfo.locked_until_block,TUserInterface.Node.Bank.BlocksCount,account.accountInfo.locked_until_block-TUserInterface.Node.Bank.BlocksCount]));
+      end else begin
+        Strings.Add(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
+          [account.accountInfo.locked_until_block,TUserInterface.Node.Bank.BlocksCount]));
+      end;
+    end;
+  end;
+end;
+
+procedure TFRMAccountExplorer.FillOperationInformation(const Strings: TStrings; Const OperationResume: TOperationResume);
+begin
+  If (not OperationResume.valid) then exit;
+  If OperationResume.Block<TUserInterface.Node.Bank.BlocksCount then
+    if (OperationResume.NOpInsideBlock>=0) then begin
+      Strings.Add(Format('Block: %d/%d',[OperationResume.Block,OperationResume.NOpInsideBlock]))
+    end else begin
+      Strings.Add(Format('Block: %d',[OperationResume.Block]))
+    end
+  else Strings.Add('** Pending operation not included on blockchain **');
+  Strings.Add(Format('%s',[OperationResume.OperationTxt]));
+  Strings.Add(Format('OpType:%d Subtype:%d',[OperationResume.OpType,OperationResume.OpSubtype]));
+  Strings.Add(Format('Operation Hash (ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash)]));
+  If (OperationResume.OperationHash_OLD<>'') then begin
+    Strings.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash_OLD)]));
+  end;
+  if (OperationResume.OriginalPayload<>'') then begin
+    Strings.Add(Format('Payload length:%d',[length(OperationResume.OriginalPayload)]));
+    If OperationResume.PrintablePayload<>'' then begin
+      Strings.Add(Format('Payload (human): %s',[OperationResume.PrintablePayload]));
+    end;
+    Strings.Add(Format('Payload (Hexadecimal): %s',[TCrypto.ToHexaString(OperationResume.OriginalPayload)]));
+  end;
+  If OperationResume.Balance>=0 then begin
+    Strings.Add(Format('Final balance: %s',[TAccountComp.FormatMoney(OperationResume.Balance)]));
+  end;
+end;
+
+procedure TFRMAccountExplorer.UpdateAccounts(RefreshData : Boolean);
+Var accl : TOrderedCardinalList;
+  l : TOrderedCardinalList;
+  i,j,k : Integer;
+  c  : Cardinal;
+  applyfilter : Boolean;
+  acc : TAccount;
+begin
+//  with FRMWallet do begin
+  If Not Assigned(FOrderedAccountsKeyList) Then exit;
+  if Not RefreshData then begin
+    dgAccounts.Invalidate;
+    exit;
+  end;
+  applyfilter := (cbFilterAccounts.Checked) and ((FMinAccountBalance>0) Or (FMaxAccountBalance<CT_MaxWalletAmount));
+  FAccountsGrid.ShowAllAccounts := (Not cbExploreMyAccounts.Checked) And (not applyfilter);
+  if Not FAccountsGrid.ShowAllAccounts then begin
+    accl := FAccountsGrid.LockAccountsList;
+    Try
+      accl.Clear;
+      if cbExploreMyAccounts.Checked then begin
+        if cbMyPrivateKeys.ItemIndex<0 then exit;
+        if cbMyPrivateKeys.ItemIndex=0 then begin
+          // All keys in the wallet
+          for i := 0 to TUserInterface.WalletKeys.Count - 1 do begin
+            j := FOrderedAccountsKeyList.IndexOfAccountKey(TUserInterface.WalletKeys[i].AccountKey);
+            if (j>=0) then begin
+              l := FOrderedAccountsKeyList.AccountKeyList[j];
+              for k := 0 to l.Count - 1 do begin
+                if applyfilter then begin
+                  acc := TUserInterface.Node.Bank.SafeBox.Account(l.Get(k));
+                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                end else accl.Add(l.Get(k));
+              end;
+            end;
+          end;
+        end else begin
+          i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
+          if (i>=0) And (i<TUserInterface.WalletKeys.Count) then begin
+            j := FOrderedAccountsKeyList.IndexOfAccountKey(TUserInterface.WalletKeys[i].AccountKey);
+            if (j>=0) then begin
+              l := FOrderedAccountsKeyList.AccountKeyList[j];
+              for k := 0 to l.Count - 1 do begin
+                if applyfilter then begin
+                  acc := TUserInterface.Node.Bank.SafeBox.Account(l.Get(k));
+                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                end else accl.Add(l.Get(k));
+              end;
+            end;
+          end;
+        end;
+      end else begin
+        // There is a filter... check every account...
+        c := 0;
+        while (c<TUserInterface.Node.Bank.SafeBox.AccountsCount) do begin
+          acc := TUserInterface.Node.Bank.SafeBox.Account(c);
+          if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+          inc(c);
+        end;
+      end;
+    Finally
+      FAccountsGrid.UnlockAccountsList;
+    End;
+    lblAccountsCount.Caption := inttostr(accl.Count);
+  end else begin
+    lblAccountsCount.Caption := inttostr(TUserInterface.Node.Bank.AccountsCount);
+  end;
+  bbChangeKeyName.Enabled := cbExploreMyAccounts.Checked;
+  // Show Totals:
+  lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
+  OnSelectedAccountChanged;
+//  end
+end;
+
+function TFRMAccountExplorer.DoUpdateAccountsFilter: Boolean;
+Var bmin,bmax:Int64;
+  doupd : Boolean;
+begin
+//  with FRMWallet do
+//  begin
+  if FUpdating then exit;
+  FUpdating := true;
+  Try
+    If Not TAccountComp.TxtToMoney(ebFilterAccountByBalanceMin.Text,bmin) then bmin := 0;
+    If not TAccountComp.TxtToMoney(ebFilterAccountByBalanceMax.Text,bmax) then bmax := CT_MaxWalletAmount;
+    if (bmax<bmin) or (bmax=0) then bmax := CT_MaxWalletAmount;
+    if bmin>bmax then bmin := 0;
+    doupd := (bmin<>FMinAccountBalance) Or (bmax<>FMaxAccountBalance);
+    FMinAccountBalance := bmin;
+    FMaxAccountBalance := bmax;
+    if bmin>0 then
+      ebFilterAccountByBalanceMin.Text:=TAccountComp.FormatMoney(bmin)
+    else ebFilterAccountByBalanceMin.Text := '';
+    if bmax<CT_MaxWalletAmount then
+      ebFilterAccountByBalanceMax.Text := TAccountComp.FormatMoney(bmax)
+    else ebFilterAccountByBalanceMax.Text := '';
+    if cbFilterAccounts.Checked then begin
+      ebFilterAccountByBalanceMin.ParentFont := true;
+      ebFilterAccountByBalanceMax.ParentFont := true;
+    end else begin
+      ebFilterAccountByBalanceMin.font.Color := clDkGray;
+      ebFilterAccountByBalanceMax.font.Color := clDkGray;
+    end;
+  Finally
+    FUpdating := false;
+  End;
+  if doupd then UpdateAccounts(true);
+  Result := doupd;
+  //end;
+end;
+
+{%endregion}
+
+{%region Event Handlers: Blockchain }
+
+procedure TFRMAccountExplorer.OnPrivateKeysChanged(Sender: TObject);
+begin
+  Refresh;
+end;
+
+procedure TFRMAccountExplorer.OnSelectedAccountChanged;
+Var accn : Int64;
+begin
+  accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  FOperationsAccountGrid.AccountNumber := accn;
+end;
+
+{%endregion}
+
+{%region Event Handlers: Combo Boxes}
+
+procedure TFRMAccountExplorer.cbExploreMyAccountsChange(Sender: TObject);
+begin
+//  with FRMWallet do
+//  begin
+    cbMyPrivateKeys.Enabled := cbExploreMyAccounts.Checked;
+    UpdateAccounts(true);
+    OnSelectedAccountChanged;
+//  end;
+end;
+
+procedure TFRMAccountExplorer.cbFilterAccountsChange(Sender: TObject);
+begin
+  If not DoUpdateAccountsFilter then UpdateAccounts(true);
+end;
+
+procedure TFRMAccountExplorer.cbMyPrivateKeysChange(Sender: TObject);
+begin
+  UpdateAccounts(true);
+end;
+
+{%endregion}
+
+{%region Event Handlers: Buttons}
+
+procedure TFRMAccountExplorer.bbChangeKeyNameClick(Sender: TObject);
+var i : Integer;
+  nameString : String;
+begin
+  if (cbMyPrivateKeys.ItemIndex<0) then  exit;
+  i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
+  if (i<0) Or (i>=TUserInterface.WalletKeys.Count) then raise Exception.Create('Must select a Key');
+  name := TUserInterface.WalletKeys.Key[i].Name;
+  if InputQuery('Change Key name','Input new name',nameString) then begin
+    TUserInterface.WalletKeys.SetName(i,name);
+  end;
+end;
+
+procedure TFRMAccountExplorer.bbAccountsRefreshClick(Sender: TObject);
+begin
+  UpdateAccounts(true);
+end;
+
+procedure TFRMAccountExplorer.sbSelectedAccountsAddAllClick(Sender: TObject);
+Var lsource,ltarget : TOrderedCardinalList;
+  i : Integer;
+begin
+//  with FRMWallet do
+//  begin
+  lsource := FAccountsGrid.LockAccountsList;
+  Try
+    ltarget := FSelectedAccountsGrid.LockAccountsList;
+    Try
+      for i := 0 to lsource.Count-1 do begin
+        if TUserInterface.WalletKeys.IndexOfAccountKey(TUserInterface.Node.Bank.SafeBox.Account(lsource.Get(i)).accountInfo.accountKey)<0 then raise Exception.Create(Format('You cannot operate with account %d because private key not found in your wallet',[lsource.Get(i)]));
+        ltarget.Add(lsource.Get(i));
+      end;
+    Finally
+      FSelectedAccountsGrid.UnlockAccountsList;
+    End;
+  Finally
+    FAccountsGrid.UnlockAccountsList;
+  End;
+
+//  end;
+end;
+
+procedure TFRMAccountExplorer.sbSelectedAccountsAddClick(Sender: TObject);
+Var l, selected : TOrderedCardinalList;
+  an : Int64;
+  i : Integer;
+begin
+//  with FRMWallet do
+//  begin
+  an := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  if (an<0) then raise Exception.Create('No account selected');
+  if TUserInterface.WalletKeys.IndexOfAccountKey(TUserInterface.Node.Bank.SafeBox.Account(an).accountInfo.accountkey)<0 then
+    raise Exception.Create(Format('You cannot add %s account because private key not found in your wallet.'#10+#10+'You''re not the owner!',
+      [TAccountComp.AccountNumberToAccountTxtNumber(an)]));
+  // Add
+  l := FSelectedAccountsGrid.LockAccountsList;
+  selected := TOrderedCardinalList.Create;
+  Try
+    FAccountsGrid.SelectedAccounts(selected);
+    for i := 0 to selected.Count-1 do begin
+      l.Add(selected.Get(i));
+    end;
+  Finally
+    selected.Free;
+    FSelectedAccountsGrid.UnlockAccountsList;
+  End;
+//  end;
+end;
+
+procedure TFRMAccountExplorer.sbSelectedAccountsDelAllClick(Sender: TObject);
+Var l : TOrderedCardinalList;
+begin
+  l := FSelectedAccountsGrid.LockAccountsList;
+  try
+    l.Clear;
+  finally
+    FSelectedAccountsGrid.UnlockAccountsList;
+  end;
+end;
+
+procedure TFRMAccountExplorer.sbSelectedAccountsDelClick(Sender: TObject);
+Var an : Int64;
+  l : TOrderedCardinalList;
+begin
+//  with FRMWallet do
+//  begin
+  l := FSelectedAccountsGrid.LockAccountsList;
+  try
+    an := FSelectedAccountsGrid.AccountNumber(dgSelectedAccounts.Row);
+    if an>=0 then l.Remove(an);
+  finally
+    FSelectedAccountsGrid.UnlockAccountsList;
+  end;
+//  end;
+end;
+
+procedure TFRMAccountExplorer.bbSelectedAccountsOperationClick(Sender: TObject);
+var l : TOrderedCardinalList;
+begin
+//  with FRMWallet do
+//  begin
+  TUserInterface.CheckNodeIsReady;
+  if FSelectedAccountsGrid.AccountsCount<=0 then raise Exception.Create('Must select at least 1 account');
+  With TFRMOperation.Create(Self) do
+  Try
+    l := FSelectedAccountsGrid.LockAccountsList;
+    try
+      SenderAccounts.CopyFrom(l);
+    finally
+      FSelectedAccountsGrid.UnlockAccountsList;
+    end;
+    DefaultFee := TUserInterface.AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    WalletKeys := TUserInterface.WalletKeys;
+    ShowModal;
+  Finally
+    Free;
+  End;
+//  end;
+end;
+
+procedure TFRMAccountExplorer.sbSearchAccountClick(Sender: TObject);
+Var F : TFRMAccountSelect;
+begin
+  F := TFRMAccountSelect.Create(Self);
+  try
+    F.Node := TUserInterface.Node;
+    F.WalletKeys := TUserInterface.WalletKeys;
+    F.ShowModal;
+  finally
+    F.Free;
+  end;
+end;
+
+{%endregion}
+
+{%region Event Handlers: Text Boxes}
+
+procedure TFRMAccountExplorer.ebFilterAccountByBalanceMinExit(Sender: TObject);
+begin
+  DoUpdateAccountsFilter;
+end;
+
+procedure TFRMAccountExplorer.ebFilterAccountByBalanceMinKeyPress(Sender: TObject; var Key: char);
+begin
+  if key=#13 then DoUpdateAccountsFilter;
+end;
+
+procedure TFRMAccountExplorer.ebFindAccountNumberChange(Sender: TObject);
+Var an : Cardinal =0;
+begin
+  if Trim(ebFindAccountNumber.Text)='' then begin
+    ebFindAccountNumber.Color := clWindow;
+    ebFindAccountNumber.Font.Color := clDkGray;
+  end else if TAccountComp.AccountTxtNumberToAccountNumber(ebFindAccountNumber.Text,an) then begin
+    ebFindAccountNumber.Color := clWindow;
+    //HS if FRMWallet.FAccountsGrid.MoveRowToAccount(an) then begin
+    if FAccountsGrid.MoveRowToAccount(an) then begin
+      ebFindAccountNumber.Font.Color := clWindowText;
+    end else begin
+      ebFindAccountNumber.Font.Color := clRed;
+    end;
+  end else begin
+    // Invalid value
+    ebFindAccountNumber.Color := clRed;
+    ebFindAccountNumber.Font.Color := clWindowText;
+  end;
+end;
+
+procedure TFRMAccountExplorer.ebFindAccountNumberExit(Sender: TObject);
+begin
+  ebFindAccountNumber.Text := '';
+end;
+
+{%endregion}
+
+{%region Event Handlers: Data Grid}
+
+procedure TFRMAccountExplorer.dgAccountOperationsClick(Sender: TObject);
+begin
+//  with FRMWallet do
+  FOperationsAccountGrid.ShowModalDecoder(TUserInterface.WalletKeys,TUserInterface.AppParams);
+end;
+
+procedure TFRMAccountExplorer.dgAccountsClick(Sender: TObject);
+begin
+  OnSelectedAccountChanged;
+end;
+
+procedure TFRMAccountExplorer.OnSelectedAccountsGridUpdated(Sender: TObject);
+begin
+  lblSelectedAccountsCount.Caption := Inttostr(FSelectedAccountsGrid.AccountsCount);
+  lblSelectedAccountsBalance.Caption := TAccountComp.FormatMoney( FSelectedAccountsGrid.AccountsBalance );
+end;
+
+{%endregion}
+
+{%region Events Handlers: Menu Items}
+
+procedure TFRMAccountExplorer.miNewOperationClick(Sender: TObject);
+var targetAccounts : TOrderedCardinalList;
+begin
+  targetAccounts := TOrderedCardinalList.Create;
+  try
+    If FAccountsGrid.SelectedAccounts(targetAccounts) = 0
+      then raise Exception.Create('No row selected');
+
+    TUserInterface.ShowNewOperationDialog(Self, targetAccounts, TUserInterface.AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
+  finally
+     targetAccounts.Free;
+  end;
+end;
+
+procedure TFRMAccountExplorer.miFindPreviousAccountWithHighBalanceClick(Sender: TObject);
+Var an  : Cardinal;
+  an64 : Int64;
+  start : TAccount;
+begin
+//  with FWallet do begin
+    an64 := FAccountsGrid.AccountNumber(dgAccounts.Row);
+    if an64<0 then an := TUserInterface.Node.Bank.SafeBox.AccountsCount-1
+    else an := an64;
+    If an>= TUserInterface.Node.Bank.SafeBox.AccountsCount then exit;
+    start := TUserInterface.Node.Bank.SafeBox.Account(an);
+    while (an>0)  do begin
+      if TUserInterface.Node.Bank.SafeBox.Account(an).balance>start.balance then break
+      else dec(an);
+    end;
+    if (TUserInterface.Node.Bank.SafeBox.Account(an).balance>start.balance) then FAccountsGrid.MoveRowToAccount(an)
+    else raise Exception.Create('Not found any account lower than '+TAccountComp.AccountNumberToAccountTxtNumber(start.account)+' with balance higher than '+
+      TAccountComp.FormatMoney(start.balance));
+//    end;
+end;
+
+procedure TFRMAccountExplorer.miFindNextAccountWithHighBalanceClick(Sender: TObject);
+Var an  : Cardinal;
+  an64 : Int64;
+  start : TAccount;
+begin
+//  with FRMWallet do begin
+  an64 := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  if an64<0 then an := 0
+  else an := an64;
+  If an>=TUserInterface.Node.Bank.SafeBox.AccountsCount then exit;
+  start := TUserInterface.Node.Bank.SafeBox.Account(an);
+  while (an<TUserInterface.Node.Bank.SafeBox.AccountsCount)  do begin
+    if TUserInterface.Node.Bank.SafeBox.Account(an).balance>start.balance then break
+    else inc(an);
+  end;
+  if (an<TUserInterface.Node.Bank.SafeBox.AccountsCount) then FAccountsGrid.MoveRowToAccount(an)
+  else raise Exception.Create('Not found any account higher than '+TAccountComp.AccountNumberToAccountTxtNumber(start.account)+' with balance higher than '+
+    TAccountComp.FormatMoney(start.balance));
+//  end;
+end;
+
+procedure TFRMAccountExplorer.miAccountInformationClick(Sender: TObject);
+Var F : TFRMMemoText;
+  accn : Int64 =-1;
+  title : String;
+  //account : TAccount;
+  strings : TStrings;
+  i : Integer;
+  opr : TOperationResume;
+begin
+//  with FRMWallet do begin
+  accn := -1;
+  title := '';
+  strings := TStringList.Create;
+  try
+    opr := CT_TOperationResume_NUL;
+    //HS
+    ////AntonB if PageControl.ActivePage=tsOperations then begin
+    //  if not (FOperationsExplorerGrid.DrawGrid = nil) then begin
+    //  i := FOperationsExplorerGrid.DrawGrid.Row;
+    //  if (i>0) and (i<=FOperationsExplorerGrid.OperationsResume.Count) then begin
+    //    opr := FOperationsExplorerGrid.OperationsResume.OperationResume[i-1];
+    //  end;
+    //AntonB end else if PageControl.ActivePage=tsPendingOperations then begin
+    //  end else if not (FPendingOperationsGrid.DrawGrid = nil) then begin
+    //  i := FPendingOperationsGrid.DrawGrid.Row;
+    //  if (i>0) and (i<=FPendingOperationsGrid.OperationsResume.Count) then begin
+    //    opr := FPendingOperationsGrid.OperationsResume.OperationResume[i-1];
+    //  end;
+    //end else //if PageControl.ActivePage=tsMyAccounts then
+    begin
+      accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
+      if accn<0 then raise Exception.Create('Select an account');
+      FillAccountInformation(strings,accn);
+      title := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(accn)+' info';
+      i := FOperationsAccountGrid.DrawGrid.Row;
+      if (i>0) and (i<=FOperationsAccountGrid.OperationsResume.Count) then begin
+        opr := FOperationsAccountGrid.OperationsResume.OperationResume[i-1];
+      end;
+    end;
+    If (opr.valid) then begin
+      if accn>=0 then strings.Add('')
+      else title := 'Operation info';
+      strings.Add('Operation info:');
+      FillOperationInformation(strings,opr);
+    end else if accn<0 then Raise Exception.Create('No info available');
+    F := TFRMMemoText.Create(Self);
+    Try
+      F.Caption := title;
+      F.Memo.Lines.Assign(strings);
+      F.ShowModal;
+    Finally
+      F.Free;
+    End;
+  finally
+    strings.free;
+  end;
+//  end;
+end;
+
+procedure TFRMAccountExplorer.miAddAccountToSelectedClick(Sender: TObject);
+begin
+   // in memory for not exit program - Application.Exit - auto free mem not need control free manual for this send Self!
+  pcAccountsOptions.ActivePage := tsMultiSelectAccounts;
+  sbSelectedAccountsAddClick(Sender);
+end;
+
+procedure TFRMAccountExplorer.miRemoveAccountFromSelectedClick(Sender: TObject);
+begin
+  Self.pcAccountsOptions.ActivePage := Self.tsMultiSelectAccounts;
+  Self.sbSelectedAccountsDelClick(Sender);
+end;
+
+{%endregion}
+
+
+end.
+

+ 69 - 0
Units/Forms/UFRMBlockExplorer.lfm

@@ -0,0 +1,69 @@
+object FRMBlockExplorer: TFRMBlockExplorer
+  Left = 87
+  Height = 444
+  Top = 135
+  Width = 864
+  Caption = 'Block Explorer'
+  ClientHeight = 424
+  ClientWidth = 864
+  Menu = BlockExplorerMenu
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '1.6.4.0'
+  object Panel2: TPanel
+    Left = 0
+    Height = 41
+    Top = 0
+    Width = 864
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 41
+    ClientWidth = 864
+    TabOrder = 0
+    object Label9: TLabel
+      Left = 11
+      Height = 15
+      Top = 10
+      Width = 112
+      Caption = 'Filter by blocks range'
+      ParentColor = False
+    end
+    object ebBlockChainBlockEnd: TEdit
+      Left = 185
+      Height = 23
+      Top = 7
+      Width = 57
+      OnExit = ebBlockChainBlockStartExit
+      OnKeyPress = ebBlockChainBlockStartKeyPress
+      TabOrder = 0
+    end
+  end
+  object dgBlockChainExplorer: TDrawGrid
+    Left = 0
+    Height = 383
+    Top = 41
+    Width = 864
+    Align = alClient
+    ExtendedSelect = False
+    TabOrder = 1
+    TitleFont.Color = clWindowText
+    TitleFont.Height = -11
+    TitleFont.Name = 'Tahoma'
+  end
+  object ebBlockChainBlockStart: TEdit
+    Left = 125
+    Height = 23
+    Top = 7
+    Width = 57
+    OnExit = ebBlockChainBlockStartExit
+    OnKeyPress = ebBlockChainBlockStartKeyPress
+    TabOrder = 2
+  end
+  object BlockExplorerMenu: TMainMenu
+    left = 560
+    top = 8
+    object miTools: TMenuItem
+      Caption = 'Tools'
+    end
+  end
+end

+ 81 - 0
Units/Forms/UFRMBlockExplorer.pas

@@ -0,0 +1,81 @@
+unit UFRMBlockExplorer;
+
+{$mode delphi}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
+  StdCtrls, Grids, Menus, UGridUtils;
+
+type
+
+  { TFRMBlockExplorer }
+
+  TFRMBlockExplorer = class(TForm)
+    dgBlockChainExplorer: TDrawGrid;
+    ebBlockChainBlockEnd: TEdit;
+    ebBlockChainBlockStart: TEdit;
+    Label9: TLabel;
+    BlockExplorerMenu: TMainMenu;
+    miTools: TMenuItem;
+    Panel2: TPanel;
+    procedure ebBlockChainBlockStartExit(Sender: TObject);
+    procedure ebBlockChainBlockStartKeyPress(Sender: TObject;
+      var Key: Char);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender:TObject);
+  private
+    FUpdating : boolean;
+    FBlockChainGrid : TBlockChainGrid;
+
+  public
+    { public declarations }
+  end;
+
+var
+  FRMBlockExplorer: TFRMBlockExplorer = nil;
+
+implementation
+uses UFRMWallet, UUserInterface;
+
+{$R *.lfm}
+procedure TFRMBlockExplorer.ebBlockChainBlockStartExit(Sender: TObject);
+var bstart,bend : Int64;
+begin
+  If not FUpdating then
+  Try
+    FUpdating := True;
+    bstart := StrToInt64Def(ebBlockChainBlockStart.Text,-1);
+    bend := StrToInt64Def(ebBlockChainBlockEnd.Text,-1);
+    FBlockChainGrid.SetBlocks(bstart,bend);
+    if FBlockChainGrid.BlockStart>=0 then
+      ebBlockChainBlockStart.Text := Inttostr(FBlockChainGrid.BlockStart) else ebBlockChainBlockStart.Text := '';
+    if FBlockChainGrid.BlockEnd>=0 then
+      ebBlockChainBlockEnd.Text := Inttostr(FBlockChainGrid.BlockEnd) else ebBlockChainBlockEnd.Text := '';
+  Finally
+    FUpdating := false;
+  End;
+end;
+
+procedure TFRMBlockExplorer.ebBlockChainBlockStartKeyPress(Sender: TObject;
+  var Key: Char);
+begin
+  if key=#13 then  ebBlockChainBlockStartExit(Nil);
+end;
+
+procedure TFRMBlockExplorer.FormCreate(Sender: TObject);
+begin
+  FBlockChainGrid := TBlockChainGrid.Create(Self);
+  FBlockChainGrid.DrawGrid := dgBlockChainExplorer;
+  FBlockChainGrid.Node := TUserInterface.Node;
+  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
+  FUpdating := false;
+end;
+
+procedure TFRMBlockExplorer.FormDestroy(Sender:TObject);
+begin
+  FreeAndNil(FBlockChainGrid);
+end;
+
+end.

+ 40 - 0
Units/Forms/UFRMLogs.lfm

@@ -0,0 +1,40 @@
+object FRMLogs: TFRMLogs
+  Left = 973
+  Height = 466
+  Top = 333
+  Width = 868
+  Caption = 'Logs'
+  ClientHeight = 466
+  ClientWidth = 868
+  OnCreate = FormCreate
+  LCLVersion = '1.6.4.0'
+  object pnlTopLogs: TPanel
+    Left = 0
+    Height = 41
+    Top = 0
+    Width = 868
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 41
+    ClientWidth = 868
+    TabOrder = 0
+    object cbShowDebugLogs: TCheckBox
+      Left = 15
+      Height = 17
+      Top = 10
+      Width = 105
+      Caption = 'Show Debug Logs'
+      TabOrder = 0
+    end
+  end
+  object memoLogs: TMemo
+    Left = 0
+    Height = 425
+    Top = 41
+    Width = 868
+    Align = alClient
+    ScrollBars = ssBoth
+    TabOrder = 1
+    WordWrap = False
+  end
+end

+ 66 - 0
Units/Forms/UFRMLogs.pas

@@ -0,0 +1,66 @@
+unit UFRMLogs;
+
+{$mode delphi}
+
+interface
+
+uses
+  {$IFnDEF FPC}
+    pngimage, Windows, AppEvnts, ShlObj,
+  {$ELSE}
+    LCLIntf,
+  {$ENDIF}
+    SysUtils, Classes, Graphics, Controls, Forms,
+    Dialogs, ExtCtrls,  StdCtrls,
+    ULog, UBlockChain;
+
+type
+
+  { TFRMLogs }
+
+  TFRMLogs = class(TForm)
+    cbShowDebugLogs: TCheckBox;
+    memoLogs: TMemo;
+    pnlTopLogs: TPanel;
+    procedure FormCreate(Sender: TObject);
+    procedure OnNewLog(logtype : TLogType; Time : TDateTime; ThreadID : Cardinal; Const sender, logtext : AnsiString);
+  private
+    { private declarations }
+  public
+    { public declarations }
+  end;
+
+var
+  FRMLogs: TFRMLogs = nil;
+
+implementation
+uses  UUserInterface;
+{$R *.lfm}
+
+{ TFRMLogs }
+
+procedure TFRMLogs.OnNewLog(logtype: TLogType; Time : TDateTime; ThreadID : Cardinal; const sender,logtext: AnsiString);
+Var s : AnsiString;
+begin
+  if (logtype=ltdebug) And (Not cbShowDebugLogs.Checked) then exit;
+  if ThreadID=MainThreadID then s := ' MAIN:' else s:=' TID:';
+  if MemoLogs.Lines.Count>300 then begin
+    // Limit max lines in logs...
+    memoLogs.Lines.BeginUpdate;
+    try
+      while memoLogs.Lines.Count>250 do memoLogs.Lines.Delete(0);
+    finally
+      memoLogs.Lines.EndUpdate;
+    end;
+  end;
+  memoLogs.Lines.Add(formatDateTime('dd/mm/yyyy hh:nn:ss.zzz',Time)+s+IntToHex(ThreadID,8)+' ['+CT_LogType[Logtype]+'] <'+sender+'> '+logtext);
+  //
+end;
+
+procedure TFRMLogs.FormCreate(Sender: TObject);
+begin
+  TUserInterface.Log.OnNewLog := OnNewLog;
+end;
+
+end.
+

+ 90 - 0
Units/Forms/UFRMMessages.lfm

@@ -0,0 +1,90 @@
+object FRMMessages: TFRMMessages
+  Left = 934
+  Height = 441
+  Top = 495
+  Width = 874
+  Caption = 'Messages'
+  ClientHeight = 441
+  ClientWidth = 874
+  OnActivate = FormActivate
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '1.6.4.0'
+  object Label11: TLabel
+    Left = 15
+    Height = 13
+    Top = 151
+    Width = 51
+    Caption = 'Messages:'
+    ParentColor = False
+  end
+  object Label12: TLabel
+    Left = 315
+    Height = 13
+    Top = 11
+    Width = 85
+    Caption = 'Message to send:'
+    ParentColor = False
+  end
+  object Label13: TLabel
+    Left = 15
+    Height = 13
+    Top = 11
+    Width = 109
+    Caption = 'Available Connections:'
+    ParentColor = False
+  end
+  object Label14: TLabel
+    Left = 410
+    Height = 13
+    Top = 11
+    Width = 361
+    Caption = '(Messages will be encrypted, so only dest connection will be able to read it)'
+    Font.Color = clGrayText
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    ParentColor = False
+    ParentFont = False
+  end
+  object lbNetConnections: TListBox
+    Left = 15
+    Height = 96
+    Top = 30
+    Width = 275
+    ItemHeight = 0
+    MultiSelect = True
+    ScrollWidth = 273
+    TabOrder = 0
+  end
+  object bbSendAMessage: TButton
+    Left = 315
+    Height = 25
+    Top = 101
+    Width = 525
+    Caption = 'Send a Message'
+    OnClick = bbSendAMessageClick
+    TabOrder = 1
+  end
+  object memoMessages: TMemo
+    Left = 15
+    Height = 253
+    Top = 170
+    Width = 821
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Font.Color = clWindowText
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 2
+  end
+  object memoMessageToSend: TMemo
+    Left = 315
+    Height = 61
+    Top = 30
+    Width = 525
+    TabOrder = 3
+    WantReturns = False
+  end
+end

+ 181 - 0
Units/Forms/UFRMMessages.pas

@@ -0,0 +1,181 @@
+unit UFRMMessages;
+
+{$mode delphi}
+
+interface
+
+uses
+  {$IFnDEF FPC}
+    pngimage, Windows, AppEvnts, ShlObj,
+  {$ELSE}
+    LCLIntf, LCLType,
+  {$ENDIF}
+  SysUtils, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, Menus,
+  UNode, UNetProtocol, UCrypto, UFRMWallet,UConst;
+
+type
+
+  { TFRMMessages }
+
+  TFRMMessages = class(TForm)
+    bbSendAMessage: TButton;
+    Label11: TLabel;
+    Label12: TLabel;
+    Label13: TLabel;
+    Label14: TLabel;
+    lbNetConnections: TListBox;
+    memoMessages: TMemo;
+    memoMessageToSend: TMemo;
+    procedure bbSendAMessageClick(Sender: TObject);
+    procedure FormActivate(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FMessagesUnreadCount : Integer;
+    { private declarations }
+    procedure UpdateAvailableConnections;
+
+  public
+    { public declarations }
+    Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
+  end;
+
+var
+  FRMMessages: TFRMMessages = nil;
+
+implementation
+uses UFRMSyncronizationDialog, UUserInterface;// after implementation!
+{$R *.lfm}
+
+{ TFRMMessages }
+
+procedure TFRMMessages.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+Var s : String;
+begin
+  inc(FMessagesUnreadCount);
+  if Assigned(NetConnection) then begin
+    s := DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr;
+    memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr+' Length '+inttostr(Length(MessageData))+' bytes');
+    memoMessages.Lines.Add('RECEIVED> '+MessageData);
+    //HS if FRMWallet.FAppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
+    if TUserInterface.AppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
+      s := DateTimeToStr(now)+' Message from '+NetConnection.ClientRemoteAddr+#10+
+         'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
+      if TCrypto.IsHumanReadable(MessageData) then begin
+         s := s + MessageData;
+      end else begin
+         s := s +'Value in hexadecimal:'+#10+
+              TCrypto.ToHexaString(MessageData);
+      end;
+      Application.MessageBox(PChar(s),PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+    end;
+  end else begin
+    memoMessages.Lines.Add(DateTimeToStr(now)+' Internal message: '+MessageData);
+  end;
+  if FMessagesUnreadCount>1 then TUserInterface.MessagesNotificationText := Format('You have received %d messages',[FMessagesUnreadCount])
+  else TUserInterface.MessagesNotificationText := 'You have received 1 message';
+end;
+
+procedure TFRMMessages.UpdateAvailableConnections;
+Var i : integer;
+ NC : TNetConnection;
+ l : TList;
+begin
+  if Not TNetData.NetData.NetConnections.TryLockList(100,l) then exit;
+  try
+    lbNetConnections.Items.BeginUpdate;
+    Try
+      lbNetConnections.Items.Clear;
+      for i := 0 to l.Count - 1 do begin
+        NC := l[i];
+        if NC.Connected then begin
+          if NC is TNetServerClient then begin
+            if Not NC.IsMyselfServer then begin
+              lbNetConnections.Items.AddObject(Format('Client: IP:%s',[NC.ClientRemoteAddr]),NC);
+            end;
+          end else begin
+            if Not NC.IsMyselfServer then begin
+              lbNetConnections.Items.AddObject(Format('Server: IP:%s',[NC.ClientRemoteAddr]),NC);
+            end;
+          end;
+        end;
+      end;
+    Finally
+      lbNetConnections.Items.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.NetConnections.UnlockList;
+  end;
+end;
+
+procedure TFRMMessages.bbSendAMessageClick(Sender: TObject);
+Var basem,m : String;
+  them, errors : AnsiString;
+  i,n : Integer;
+  nc : TNetConnection;
+begin
+  TUserInterface.CheckNodeIsReady;//CheckIsReady;
+  if (lbNetConnections.SelCount<=0) Or (lbNetConnections.ItemIndex<0) then raise Exception.Create('Select at least one connection');
+  if lbNetConnections.SelCount<=0 then n := 1
+  else n := lbNetConnections.SelCount;
+
+  basem := memoMessageToSend.Lines.Text;
+  m := '';
+  // Clear non valid characters:
+  for i := 1 to length(basem) do begin
+    if basem[i] in [#32..#127] then m := m + basem[i]
+    else m:=m+'.';
+  end;
+
+  if trim(m)='' then raise Exception.Create('No message');
+
+  if Application.MessageBox(PChaR('Send this message to '+inttostr(n)+' nodes?'+#10+
+    'NOTE: Sending unauthorized messages will be considered spam and you will be banned'+#10+
+    #10+
+    'Message: '+#10+
+    m),PChar(Application.Title),MB_ICONQUESTION+MB_YESNO+MB_DEFBUTTON1)<>IdYes then exit;
+  them := m;
+  if n>1 then begin
+    for i := 0 to lbNetConnections.Items.Count - 1 do begin
+      if lbNetConnections.Selected[i] then begin
+        nc := TNetConnection(lbNetconnections.Items.Objects[i]);
+        if TNetData.NetData.ConnectionExistsAndActive(nc) then begin
+          TUserInterface.Node.SendNodeMessage(nc,m,errors);
+          memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.ClientRemoteAddr+' > '+m);
+        end;
+      end;
+    end;
+  end else begin
+    nc := TNetConnection(lbNetconnections.Items.Objects[lbNetconnections.ItemIndex]);
+    if TNetData.NetData.ConnectionExistsAndActive(nc) then begin
+      TUserInterface.Node.SendNodeMessage(nc,m,errors);
+      memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.ClientRemoteAddr+' > '+m);
+    end;
+  end;
+
+  Application.MessageBox(PChaR('Message sent to '+inttostr(n)+' nodes'+#10+
+    'Message: '+#10+m),PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+end;
+
+procedure TFRMMessages.FormActivate(Sender: TObject);
+begin
+  UpdateAvailableConnections;
+  //HS FNodeNotifyEvents.Node := TUserInterface.Node;
+  TUserInterface.MessagesNotificationText := '';
+end;
+
+procedure TFRMMessages.FormCreate(Sender: TObject);
+begin
+  //HS FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  //HS FNodeNotifyEvents.OnBlocksChanged := TUserInterface.Wallet.OnNewAccount; //TODO: may be need move this function
+  //HS FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
+end;
+
+procedure TFRMMessages.FormDestroy(Sender: TObject);
+begin
+  //HS FNodeNotifyEvents.Node := Nil;
+  //HS FreeAndNil(FNodeNotifyEvents);
+end;
+
+end.

+ 70 - 0
Units/Forms/UFRMNodes.lfm

@@ -0,0 +1,70 @@
+object FRMNodes: TFRMNodes
+  Left = 971
+  Height = 444
+  Top = 399
+  Width = 861
+  Caption = 'Nodes'
+  ClientHeight = 444
+  ClientWidth = 861
+  OnCreate = FormCreate
+  LCLVersion = '1.6.4.0'
+  object Label3: TLabel
+    Left = 15
+    Height = 13
+    Top = 15
+    Width = 96
+    Caption = 'Active Connections:'
+    ParentColor = False
+  end
+  object Label6: TLabel
+    Left = 15
+    Height = 13
+    Top = 295
+    Width = 198
+    Anchors = [akLeft, akRight, akBottom]
+    Caption = 'Known Node Servers:'
+    ParentColor = False
+  end
+  object Label7: TLabel
+    Left = 15
+    Height = 13
+    Top = 191
+    Width = 99
+    Anchors = [akLeft, akRight, akBottom]
+    Caption = 'Blacklisted Nodes'
+    ParentColor = False
+  end
+  object memoNetConnections: TMemo
+    Left = 15
+    Height = 149
+    Top = 34
+    Width = 830
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 0
+    WordWrap = False
+  end
+  object memoNetServers: TMemo
+    Left = 15
+    Height = 116
+    Top = 314
+    Width = 830
+    Anchors = [akLeft, akRight, akBottom]
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 1
+    WordWrap = False
+  end
+  object memoNetBlackLists: TMemo
+    Left = 16
+    Height = 79
+    Top = 210
+    Width = 829
+    Anchors = [akLeft, akRight, akBottom]
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 2
+    WordWrap = False
+  end
+end

+ 234 - 0
Units/Forms/UFRMNodes.pas

@@ -0,0 +1,234 @@
+unit UFRMNodes;
+
+{$mode delphi}
+
+interface
+
+uses
+  {$IFnDEF FPC}
+    pngimage, Windows, AppEvnts, ShlObj,
+  {$ELSE}
+    LCLIntf, LCLType,
+  {$ENDIF}
+  Messages, SysUtils, Classes, Graphics, Controls, Forms,
+  Dialogs, ExtCtrls, StdCtrls,
+  ULog,  UBlockChain, UNode, Menus,  UNetProtocol,  UFRMWallet;
+
+type
+
+  { TFRMNodes }
+
+  TFRMNodes = class(TForm)
+    Label3: TLabel;
+    Label6: TLabel;
+    Label7: TLabel;
+    memoNetBlackLists: TMemo;
+    memoNetConnections: TMemo;
+    memoNetServers: TMemo;
+    procedure FormCreate(Sender: TObject);
+  private
+    { private declarations }
+    FMustProcessNetConnectionUpdated : Boolean;
+    procedure CM_NetConnectionUpdated(var Msg: TMessage); message CM_PC_NetConnectionUpdated;
+  public
+    { public declarations }
+
+    // TODO - refactor this out with TNotifyManyEvent so form subscribes directly to event
+    procedure OnNetConnectionsUpdated;
+    procedure OnNetBlackListUpdated;
+    procedure OnNetNodeServersUpdated;
+  end;
+
+var
+  FRMNodes: TFRMNodes = nil;
+
+implementation
+
+Uses UTime;
+
+{$R *.lfm}
+
+{ TFRMNodes }
+
+procedure TFRMNodes.FormCreate(Sender: TObject);
+begin
+end;
+
+procedure TFRMNodes.CM_NetConnectionUpdated(var Msg: TMessage);
+Const CT_BooleanToString : Array[Boolean] of String = ('False','True');
+Var i : integer;
+ NC : TNetConnection;
+ l : TList;
+ sClientApp, sLastConnTime : String;
+ strings, sNSC, sRS, sDisc : TStrings;
+ hh,nn,ss,ms : Word;
+begin
+  Try
+    if Not TNetData.NetData.NetConnections.TryLockList(100,l) then exit;
+    try
+      strings := memoNetConnections.Lines;
+      sNSC := TStringList.Create;
+      sRS := TStringList.Create;
+      sDisc := TStringList.Create;
+      strings.BeginUpdate;
+      Try
+        for i := 0 to l.Count - 1 do begin
+          NC := l[i];
+          If NC.Client.BytesReceived>0 then begin
+            sClientApp := '['+IntToStr(NC.NetProtocolVersion.protocol_version)+'-'+IntToStr(NC.NetProtocolVersion.protocol_available)+'] '+NC.ClientAppVersion;
+          end else begin
+            sClientApp := '(no data)';
+          end;
+
+          if NC.Connected then begin
+            if NC.Client.LastCommunicationTime>1000 then begin
+              DecodeTime(now - NC.Client.LastCommunicationTime,hh,nn,ss,ms);
+              if (hh=0) and (nn=0) And (ss<10) then begin
+                sLastConnTime := ' - Last comunication <10 sec.';
+              end else begin
+                sLastConnTime := Format(' - Last comunication %.2dm%.2ds',[(hh*60)+nn,ss]);
+              end;
+            end else begin
+              sLastConnTime := '';
+            end;
+            if NC is TNetServerClient then begin
+              sNSC.Add(Format('Client: IP:%s Block:%d Sent/Received:%d/%d Bytes - %s - Time offset %d - Active since %s %s',
+                [NC.ClientRemoteAddr,NC.RemoteOperationBlock.block,NC.Client.BytesSent,NC.Client.BytesReceived,sClientApp,NC.TimestampDiff,DateTimeElapsedTime(NC.CreatedTime),sLastConnTime]));
+            end else begin
+              if NC.IsMyselfServer then sNSC.Add(Format('MySelf IP:%s Sent/Received:%d/%d Bytes - %s - Time offset %d - Active since %s %s',
+                [NC.ClientRemoteAddr,NC.Client.BytesSent,NC.Client.BytesReceived,sClientApp,NC.TimestampDiff,DateTimeElapsedTime(NC.CreatedTime),sLastConnTime]))
+              else begin
+                sRS.Add(Format('Remote Server: IP:%s Block:%d Sent/Received:%d/%d Bytes - %s - Time offset %d - Active since %s %s',
+                [NC.ClientRemoteAddr,NC.RemoteOperationBlock.block,NC.Client.BytesSent,NC.Client.BytesReceived,sClientApp,NC.TimestampDiff,DateTimeElapsedTime(NC.CreatedTime),sLastConnTime]));
+              end;
+            end;
+          end else begin
+            if NC is TNetServerClient then begin
+              sDisc.Add(Format('Disconnected client: IP:%s - %s',[NC.ClientRemoteAddr,sClientApp]));
+            end else if NC.IsMyselfServer then begin
+              sDisc.Add(Format('Disconnected MySelf IP:%s - %s',[NC.ClientRemoteAddr,sClientApp]));
+            end else begin
+              sDisc.Add(Format('Disconnected Remote Server: IP:%s %s - %s',[NC.ClientRemoteAddr,CT_BooleanToString[NC.Connected],sClientApp]));
+            end;
+          end;
+        end;
+        strings.Clear;
+        strings.Add(Format('Connections Updated %s Clients:%d Servers:%d (valid servers:%d)',[DateTimeToStr(now),sNSC.Count,sRS.Count,TNetData.NetData.NetStatistics.ServersConnectionsWithResponse]));
+        strings.AddStrings(sRS);
+        strings.AddStrings(sNSC);
+        if sDisc.Count>0 then begin
+          strings.Add('');
+          strings.Add('Disconnected connections: '+Inttostr(sDisc.Count));
+          strings.AddStrings(sDisc);
+        end;
+      Finally
+        strings.EndUpdate;
+        sNSC.Free;
+        sRS.Free;
+        sDisc.Free;
+      End;
+      //CheckMining;
+    finally
+      TNetData.NetData.NetConnections.UnlockList;
+    end;
+  Finally
+    FMustProcessNetConnectionUpdated := false;
+  End;
+end;
+
+procedure TFRMNodes.OnNetNodeServersUpdated;
+Var i : integer;
+ P : PNodeServerAddress;
+ l : TList;
+ strings : TStrings;
+ s : String;
+begin
+  l := TNetData.NetData.NodeServersAddresses.LockList;
+  try
+    strings := memoNetServers.Lines;
+    strings.BeginUpdate;
+    Try
+      strings.Clear;
+      strings.Add('NodeServers Updated: '+DateTimeToStr(now) +' Count: '+inttostr(l.Count));
+      for i := 0 to l.Count - 1 do begin
+        P := l[i];
+        if Not (P^.is_blacklisted) then begin
+          s := Format('Server IP:%s:%d',[P^.ip,P^.port]);
+          if Assigned(P.netConnection) then begin
+            If P.last_connection>0 then  s := s+ ' ** ACTIVE **'
+            else s := s+' ** TRYING TO CONNECT **';
+          end;
+          if P.its_myself then begin
+            s := s+' ** NOT VALID ** '+P.BlackListText;
+          end;
+          if P.last_connection>0 then begin
+            s := s + ' Last connection: '+DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection)));
+          end;
+          if P.last_connection_by_server>0 then begin
+            s := s + ' Last server connection: '+DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection_by_server)));
+          end;
+          if (P.last_attempt_to_connect>0) then begin
+            s := s + ' Last attempt to connect: '+DateTimeToStr(P^.last_attempt_to_connect);
+          end;
+          if (P.total_failed_attemps_to_connect>0) then begin
+            s := s + ' (Attempts: '+inttostr(P^.total_failed_attemps_to_connect)+')';
+          end;
+
+          strings.Add(s);
+        end;
+      end;
+    Finally
+      strings.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.NodeServersAddresses.UnlockList;
+  end;
+end;
+
+procedure TFRMNodes.OnNetBlackListUpdated;
+Const CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
+Var i,j,n : integer;
+ P : PNodeServerAddress;
+ l : TList;
+ strings : TStrings;
+begin
+  l := TNetData.NetData.NodeServersAddresses.LockList;
+  try
+    strings := memoNetBlackLists.Lines;
+    strings.BeginUpdate;
+    Try
+      strings.Clear;
+      strings.Add('BlackList Updated: '+DateTimeToStr(now)+' by TID:'+IntToHex(TThread.CurrentThread.ThreadID,8));
+      j := 0; n:=0;
+      for i := 0 to l.Count - 1 do begin
+        P := l[i];
+        if (P^.is_blacklisted) then begin
+          inc(n);
+          if Not P^.its_myself then begin
+            inc(j);
+            strings.Add(Format('Blacklist IP:%s:%d LastConnection:%s Reason: %s',
+              [
+               P^.ip,P^.port,
+               DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection))),P^.BlackListText]));
+          end;
+        end;
+      end;
+      Strings.Add(Format('Total Blacklisted IPs: %d (Total %d)',[j,n]));
+    Finally
+      strings.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.NodeServersAddresses.UnlockList;
+  end;
+end;
+
+procedure TFRMNodes.OnNetConnectionsUpdated;
+begin
+  // TODO - refactor this ugly infinite loop check out
+  if FMustProcessNetConnectionUpdated then exit;
+  FMustProcessNetConnectionUpdated := true;
+  PostMessage(Self.Handle,CM_PC_NetConnectionUpdated,0,0);  // Ends up calling CM_NetConnectionUpdated
+end;
+
+
+end.

+ 18 - 7
Units/Forms/UFRMOperation.pas

@@ -202,7 +202,9 @@ Var errors : AnsiString;
   _newOwnerPublicKey : TECDSA_Public;
   _newOwnerPublicKey : TECDSA_Public;
   _newName : TRawBytes;
   _newName : TRawBytes;
   _newType : Word;
   _newType : Word;
-  _changeName, _changeType, _V2 : Boolean;
+  _changeName, _changeType, _V2, _executeSigner  : Boolean;
+  _senderAccounts : array of Cardinal;
+label loop_start;
 begin
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
@@ -216,11 +218,14 @@ begin
     operationstxt := '';
     operationstxt := '';
     operation_to_string := '';
     operation_to_string := '';
 
 
+    // Compile FSenderAccounts into a reorderable array
+    _senderAccounts := FSenderAccounts.ToArray;
+
     // Loop through each sender account
     // Loop through each sender account
-    // TODO: must process Signer account last if included in FSenderAccounts (not necessarily ordered enumeration)
-    for iAcc := 0 to FSenderAccounts.Count - 1 do begin
+    for iAcc := 0 to Length(_senderAccounts) - 1 do begin
+loop_start:
       op := Nil;
       op := Nil;
-      account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
+      account := FNode.Operations.SafeBoxTransaction.Account(_senderAccounts[iAcc]);
       If Not UpdatePayload(account, errors) then
       If Not UpdatePayload(account, errors) then
         raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
         raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
       i := WalletKeys.IndexOfAccountKey(account.accountInfo.accountKey);
       i := WalletKeys.IndexOfAccountKey(account.accountInfo.accountKey);
@@ -236,7 +241,7 @@ begin
       if PageControlOpType.ActivePage = tsTransaction then begin
       if PageControlOpType.ActivePage = tsTransaction then begin
         {%region Operation: Transaction}
         {%region Operation: Transaction}
         if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
         if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
-        if FSenderAccounts.Count>1 then begin
+        if Length(_senderAccounts) > 1 then begin
           if account.balance>0 then begin
           if account.balance>0 then begin
             if account.balance>DefaultFee then begin
             if account.balance>DefaultFee then begin
               _amount := account.balance - DefaultFee;
               _amount := account.balance - DefaultFee;
@@ -259,6 +264,12 @@ begin
         {%region Operation: Change Private Key}
         {%region Operation: Change Private Key}
         if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if _V2 then begin
         if _V2 then begin
+          // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+          if (iAcc < Length(_senderAccounts) - 1) AND (account.account = signerAccount.account) then begin
+            TArrayTool<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1); // ensure signer account processed last
+            goto loop_start; // TODO: remove ugly hack with refactoring!
+          end;
+
           // Maintain correct signer fee distribution
           // Maintain correct signer fee distribution
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
@@ -319,10 +330,10 @@ begin
 
 
     if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
     if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
 
 
-    if (FSenderAccounts.Count>1) then begin
+    if (Length(_senderAccounts)>1) then begin
       if PageControlOpType.ActivePage = tsTransaction then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
       if PageControlOpType.ActivePage = tsTransaction then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
       else auxs:='';
       else auxs:='';
-      if Application.MessageBox(PChar('Execute '+Inttostr(FSenderAccounts.Count)+' operations?'+#10+
+      if Application.MessageBox(PChar('Execute '+Inttostr(Length(_senderAccounts))+' operations?'+#10+
         'Operation: '+operationstxt+#10+
         'Operation: '+operationstxt+#10+
         auxs+
         auxs+
         'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),
         'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),

+ 79 - 0
Units/Forms/UFRMOperationExplorer.lfm

@@ -0,0 +1,79 @@
+object FRMOperationExplorer: TFRMOperationExplorer
+  Left = 190
+  Height = 444
+  Top = 287
+  Width = 864
+  Caption = 'Operations Explorer'
+  ClientHeight = 424
+  ClientWidth = 864
+  Menu = OperationsExplorerMenu
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '1.6.4.0'
+  object dgOperationsExplorer: TDrawGrid
+    Left = 0
+    Height = 383
+    Top = 41
+    Width = 864
+    Align = alClient
+    ExtendedSelect = False
+    TabOrder = 0
+    TitleFont.Color = clWindowText
+    TitleFont.Height = -11
+    TitleFont.Name = 'Tahoma'
+    OnClick = dgOperationsExplorerClick
+  end
+  object Panel1: TPanel
+    Left = 0
+    Height = 41
+    Top = 0
+    Width = 864
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 41
+    ClientWidth = 864
+    TabOrder = 1
+    object Label2: TLabel
+      Left = 11
+      Height = 15
+      Top = 10
+      Width = 112
+      Caption = 'Filter by blocks range'
+      ParentColor = False
+    end
+  end
+  object ebFilterOperationsStartBlock: TEdit
+    Left = 125
+    Height = 23
+    Top = 7
+    Width = 57
+    OnExit = ebFilterOperationsAccountExit
+    OnKeyPress = ebFilterOperationsAccountKeyPress
+    TabOrder = 2
+  end
+  object ebFilterOperationsEndBlock: TEdit
+    Left = 185
+    Height = 23
+    Top = 7
+    Width = 57
+    OnExit = ebFilterOperationsAccountExit
+    OnKeyPress = ebFilterOperationsAccountKeyPress
+    TabOrder = 3
+  end
+  object OperationsExplorerMenu: TMainMenu
+    left = 560
+    object miTools: TMenuItem
+      Caption = 'Tools'
+      object miFindOperationByOpHash: TMenuItem
+        Caption = 'Find Operation by OpHash'
+        ShortCut = 116
+        OnClick = miFindOperationByOpHashClick
+      end
+      object miDecodePayload: TMenuItem
+        Caption = 'Decode Payload Operations Explorer'
+        ShortCut = 113
+        OnClick = miDecodePayloadClick
+      end
+    end
+  end
+end

+ 133 - 0
Units/Forms/UFRMOperationExplorer.pas

@@ -0,0 +1,133 @@
+unit UFRMOperationExplorer;
+
+{$mode delphi}
+
+interface
+
+uses
+  {$IFnDEF FPC}
+    pngimage, Windows, AppEvnts, ShlObj,
+  {$ELSE}
+    LCLIntf, LCLType,
+  {$ENDIF}
+    SysUtils, Controls, Forms,
+    Dialogs, ExtCtrls, StdCtrls,
+    Grids, Menus, Classes, UGridUtils, UConst;
+
+type
+
+  { TFRMOperationExplorer }
+
+  TFRMOperationExplorer = class(TForm)
+    dgOperationsExplorer: TDrawGrid;
+    ebFilterOperationsEndBlock: TEdit;
+    ebFilterOperationsStartBlock: TEdit;
+    Label2: TLabel;
+    OperationsExplorerMenu: TMainMenu;
+    miDecodePayload: TMenuItem;
+    miFindOperationByOpHash: TMenuItem;
+    miTools: TMenuItem;
+    Panel1: TPanel;
+    procedure dgOperationsExplorerClick(Sender: TObject);
+    procedure ebFilterOperationsAccountExit(Sender: TObject);
+    procedure ebFilterOperationsAccountKeyPress(Sender: TObject; var Key: Char);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure miDecodePayloadClick(Sender: TObject);
+    procedure miFindOperationByOpHashClick(Sender: TObject);
+
+  private
+    { private declarations }
+    FUpdating : boolean;
+    FOperationsExplorerGrid : TOperationsGrid;
+  public
+    { public declarations }
+  end;
+
+var
+  FRMOperationExplorer: TFRMOperationExplorer = nil;
+
+implementation
+
+{$R *.lfm}
+
+uses UUserInterface, UFRMPayloadDecoder, UBlockChain;
+
+procedure TFRMOperationExplorer.FormCreate(Sender: TObject);
+begin
+  FOperationsExplorerGrid := TOperationsGrid.Create(Self);
+  FOperationsExplorerGrid.Node := TUserInterface.Node;
+  FOperationsExplorerGrid.DrawGrid := dgOperationsExplorer;
+  FOperationsExplorerGrid.AccountNumber := -1;
+  FOperationsExplorerGrid.PendingOperations := False;
+  FUpdating := false;
+end;
+
+procedure TFRMOperationExplorer.FormDestroy(Sender: TObject);
+begin
+  FOperationsExplorerGrid.Node := Nil;
+  // Note: grids themselves are collected with Self (TComponent dependency)
+end;
+
+procedure TFRMOperationExplorer.miDecodePayloadClick(Sender: TObject);
+begin
+  // TODO - move ShowModalDecoder to TUserInterface
+  FOperationsExplorerGrid.ShowModalDecoder(TUserInterface.WalletKeys, TUserInterface.AppParams);
+end;
+
+
+procedure TFRMOperationExplorer.miFindOperationByOpHashClick(Sender: TObject);
+var
+  FRM : TFRMPayloadDecoder;
+  oph : String;
+begin
+  //TODO - refactor out with TUserInterface.ShowOperationInfoDialog(oph);
+  oph := '';
+  if Not InputQuery('Search operation by OpHash','Insert Operation Hash value (OpHash)',oph) then exit;
+  FRM := TFRMPayloadDecoder.Create(Self);
+  try
+    FRM.Init(CT_TOperationResume_NUL, TUserInterface.WalletKeys,TUserInterface.AppParams);
+    FRM.DoFind(oph);
+    FRM.ShowModal;
+
+
+
+  finally
+
+    FRM.Free;
+  end;
+end;
+
+
+
+procedure TFRMOperationExplorer.ebFilterOperationsAccountExit(Sender: TObject);
+Var bstart,bend : Int64;
+begin
+  If not FUpdating then
+  Try
+    FUpdating := True;// move to finally
+    bstart := StrToInt64Def(ebFilterOperationsStartBlock.Text,-1);
+    if bstart>=0 then ebFilterOperationsStartBlock.Text := Inttostr(bstart) else ebFilterOperationsStartBlock.Text := '';
+    bend := StrToInt64Def(ebFilterOperationsEndBlock.Text,-1);
+    if bend>=0 then ebFilterOperationsEndBlock.Text := Inttostr(bend) else ebFilterOperationsEndBlock.Text := '';
+      FOperationsExplorerGrid.SetBlocks(bstart,bend);
+  Finally
+
+
+    FUpdating := false;
+  End;
+end;
+
+procedure TFRMOperationExplorer.dgOperationsExplorerClick(Sender: TObject);
+begin
+  //with FRMWallet do
+    FOperationsExplorerGrid.ShowModalDecoder(TUserInterface.WalletKeys, TUserInterface.AppParams);
+end;
+
+procedure TFRMOperationExplorer.ebFilterOperationsAccountKeyPress(Sender: TObject; var Key: Char);
+begin
+  if key=#13 then  ebFilterOperationsAccountExit(Nil);
+end;
+
+end.
+

+ 69 - 0
Units/Forms/UFRMPendingOperations.lfm

@@ -0,0 +1,69 @@
+object FRMPendingOperations: TFRMPendingOperations
+  Left = 283
+  Height = 357
+  Top = 313
+  Width = 860
+  Caption = 'Pending Operations'
+  ClientHeight = 337
+  ClientWidth = 860
+  Menu = PendingOperationsMenu
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '1.6.4.0'
+  object pnlPendingOperations: TPanel
+    Left = 0
+    Height = 86
+    Top = 0
+    Width = 860
+    Align = alTop
+    BevelOuter = bvNone
+    BorderWidth = 10
+    ClientHeight = 86
+    ClientWidth = 860
+    TabOrder = 0
+    object Label10: TLabel
+      Left = 10
+      Height = 66
+      Top = 10
+      Width = 840
+      Align = alClient
+      AutoSize = False
+      Caption = 'Here you can see Operations transmited/received from other nodes that will be included in next block. There is no guarantee that other nodes will include them when mining, so it''s important that you mine too to help include Operations to the main BlockChain'
+      Font.Color = clWindowText
+      Font.Height = -16
+      Font.Name = 'Tahoma'
+      ParentColor = False
+      ParentFont = False
+      WordWrap = True
+    end
+  end
+  object dgPendingOperations: TDrawGrid
+    Left = 0
+    Height = 251
+    Top = 86
+    Width = 860
+    Align = alClient
+    ExtendedSelect = False
+    TabOrder = 1
+    TitleFont.Color = clWindowText
+    TitleFont.Height = -11
+    TitleFont.Name = 'Tahoma'
+    OnDblClick = dgPendingOperationsDblClick
+  end
+  object PendingOperationsMenu: TMainMenu
+    left = 696
+    top = 48
+    object miTools: TMenuItem
+      Caption = 'Tools'
+      object miFindOperationbyOpHash: TMenuItem
+        Caption = 'Find Operation by OpHash'
+        ShortCut = 116
+        OnClick = miFindOperationbyOpHashClick
+      end
+      object miDecodePayLoad: TMenuItem
+        Caption = 'Decode Payload Pending Operations'
+        OnClick = miDecodePayLoadClick
+      end
+    end
+  end
+end

+ 76 - 0
Units/Forms/UFRMPendingOperations.pas

@@ -0,0 +1,76 @@
+unit UFRMPendingOperations;
+
+{$mode delphi}
+
+interface
+
+uses
+  Classes, Forms, Grids,
+  ExtCtrls, StdCtrls, Menus, UGridUtils;
+
+type
+
+  { TFRMPendingOperations }
+
+  TFRMPendingOperations = class(TForm)
+    dgPendingOperations: TDrawGrid;
+    Label10: TLabel;
+    PendingOperationsMenu: TMainMenu;
+    miDecodePayLoad: TMenuItem;
+    miFindOperationbyOpHash: TMenuItem;
+    miTools: TMenuItem;
+    pnlPendingOperations: TPanel;
+    procedure dgPendingOperationsDblClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure miDecodePayLoadClick(Sender: TObject);
+    procedure miFindOperationbyOpHashClick(Sender: TObject);
+  private
+    { private declarations }
+    FPendingOperationsGrid : TOperationsGrid;
+  public
+    { public declarations }
+  end;
+
+var
+  FRMPendingOperations: TFRMPendingOperations = nil;
+
+implementation
+uses UFRMWallet, UUserInterface;
+
+{$R *.lfm}
+
+{ TFRMPendingOperations }
+
+procedure TFRMPendingOperations.FormCreate(Sender: TObject);
+begin
+  FPendingOperationsGrid := TOperationsGrid.Create(Self);
+  FPendingOperationsGrid.Node := TUserInterface.Node;
+  FPendingOperationsGrid.DrawGrid := dgPendingOperations;
+  FPendingOperationsGrid.AccountNumber := -1; // all
+  FPendingOperationsGrid.PendingOperations := true;
+end;
+
+procedure TFRMPendingOperations.FormDestroy(Sender: TObject);
+begin
+   FPendingOperationsGrid.Node := Nil;
+   // Note: grids themselves are collected wisth Self (TComponent dependency)
+end;
+
+procedure TFRMPendingOperations.miDecodePayLoadClick(Sender: TObject);
+begin
+
+end;
+
+procedure TFRMPendingOperations.miFindOperationbyOpHashClick(Sender: TObject);
+begin
+
+end;
+
+procedure TFRMPendingOperations.dgPendingOperationsDblClick(Sender: TObject);
+begin
+   FPendingOperationsGrid.ShowModalDecoder(TUserInterface.WalletKeys, TUserInterface.AppParams);
+end;
+
+end.
+

+ 367 - 0
Units/Forms/UFRMSyncronizationDialog.lfm

@@ -0,0 +1,367 @@
+object FRMSyncronizationDialog: TFRMSyncronizationDialog
+  Left = 482
+  Height = 92
+  Top = 282
+  Width = 867
+  AutoSize = True
+  BorderIcons = []
+  BorderStyle = bsToolWindow
+  Caption = 'Synchronization'
+  ClientHeight = 92
+  ClientWidth = 867
+  FormStyle = fsStayOnTop
+  LCLVersion = '1.6.4.0'
+  object pnlTop: TPanel
+    Left = 0
+    Height = 91
+    Top = 0
+    Width = 867
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 91
+    ClientWidth = 867
+    TabOrder = 0
+    object Image1: TImage
+      Left = 15
+      Height = 62
+      Top = 15
+      Width = 62
+      AutoSize = True
+      Picture.Data = {
+        1754506F727461626C654E6574776F726B477261706869630A0F000089504E47
+        0D0A1A0A0000000D494844520000003E0000003E080600000073C1A844000000
+        0473424954080808087C086488000000097048597300000B1200000B1201D2DD
+        7EFC0000001C74455874536F6674776172650041646F62652046697265776F72
+        6B7320435336E8BCB28C00000016744558744372656174696F6E2054696D6500
+        30362F30372F313610A27E9700000E62494441546881CD9B7D9C54D579C7BFE7
+        CEECEEC0BE22B02F2CB033B3C2F2EA0ED0801491B56068ADBCA40511492DD12A
+        A468C57CAC298444AC1113623EB15A6391A8D40815431B8C2FA9489301444113
+        1844C00576980576D91D0177667760999DB9A77FDCBB77EFECBCEDABEDEFF339
+        9FB973EFB9E79CDF739EE7B9CF7913524AFA0B42881B80BF016600A5C020200B
+        50F424F51401424003E0050E00FF25A5FCBCDFDAD6D7C485100B818780C9D936
+        6BFD8D15458D55134B2C958EC1B9F3A69655267B2F100A07F71EBB70E6A8EF72
+        F3EF3EAD8FEE3D76C1DE165107000781E7A494EFF7693BFB82B8106234F028F0
+        0D4751EEC97B6EAD883C78FB841BF2B333F37A53AEB72158F7FADE9A331B7E75
+        7870A8353200D80ABC28A53CDBEB36F786B810620CB0D6A288998B66387D1BEE
+        FEDA2867715E696F1B9508DE8660DDA2A7767B0F7B2F8E045E03364929CFF5B4
+        BC1E1117420C02FE1158B96466F9914DAB664EEE6DEF7615260194038F4B295F
+        EC4939DD262E84A804B60FC9B35D39F8D38585FDD5C3E970B0DA7F72FE13EF85
+        FC81ABA7815552CA2FBAF37EB7880B21165B14F1E4FABBA634AE5B32F9A6EE36
+        361D3CDE4B3485AE612FCAC55E980B80CFDF8CAFB19982EC2C5CCEC171EF2CF9
+        F16EF71B1F7887032BA594FFD3D5BA94AE641242D884103FC81D90F183FD1B17
+        C8F4A40508058405146B6C128AF63C01566FFE905BD6BECD96DD278D7B5B769F
+        E496B56FB37AF38709DFD9FEDD3955BFF9FEDC904511BF1442ACEC0A1FE82271
+        E0896C9BF5CE73AF2C1B39ADA27074C21C4268C42C5960CD02AB2D41CAEAF8B5
+        649904A1C173E612404CCFBA8FD60350357158D2C6CD9B5A56B97FE382E61C5B
+        C63F74957C4AE2428801428817726C19B7D56DF9666942072614B06480A59D98
+        89744622F29D8460C9024B264D57DA0884C200869A83A6EA00F6A2DCB8AACD98
+        565138FAFC9665A5B90332560921D60B21327B4C1C7838DB669D757ECBB2E109
+        492B197AC36D897BD992E6BF21A82C3CBEA051ACB9C76BFD2DBA3072D23415F2
+        B333F3CEBDB26C648E2D6331B03455DEA4CE4D08B1386F60E69AB32FDF551E47
+        5A283A69ABF6AB5841B1E836ADFF0A85E54FFE0A5FC3971DEF49F385D4FF6BD7
+        E7BF085253DF44CE800CA6940F05244DA1304774F57F6CE91443282EE7E018AD
+        E88C83D5FE93331E7DD31A55E5FDC91C5E42E24288511645ECDABF714138CEA6
+        8502964C4DBD0DD266E765D105A3206E7C3869E37A8B4AC760562F98C8F2D989
+        5DCE5B1FD71E99FFC47BB940A594B2A5F3F338551742E4015BBF7DDBB8DAE4A4
+        CD292B36E92AEFF6F43AAA4C8923672EF1AD67DCD8EFDD86C77B29EEF9BCA965
+        9577DCE43C0BFC520831A0F3F34436FE9DC2FC01D6E756CC981573570813D98C
+        8E6B6B668CADB6DBAFAF3198A0E8BE47ADBF85490FFDA7E1FDCDD8FEDD395585
+        F903CA4860EF31C4F561E4CA8F9E5E5018578A92614A995A4AD6F3D62C7C0D4D
+        7DC7AE0B58F8E42EE30B60C66FBE3F371B7854085164BEDFB9C7D72D99597E22
+        2E0C55AC1DCECC92614A26D2E65EB764E1FE43BF0DA51322100AB3FAC58FE2EE
+        4FAB281C3DC939C40F3C60BE6F38372184D3A288BD97B6FD6D6EAC17171D0187
+        35CB445427AD98D4BF5D408A85823F594AA03994B091F9B9D9B8C6D8D13CBBE4
+        7CE3256ACEF9C91998C5948A1180CAF9C6266AEA2F775B00675E5A1AE7F1BD0D
+        C1BAF2FB5EB7A239BA4688EDF1EF2D9AE13C15F7E952F44F94D9735B4C6A1FD3
+        FB9A709A426D494903B8C695E3DEF613DCAFFD08F7AB3FE4F659DAA76AF6D4B1
+        B8373D8CFBE70FB06EF96C00460CCDE3D78F2D60568AC8CD8C67DE3C1A77CF59
+        9C573AC939E414F06D8396E9F9E20D777F6D54DC5B8A0584D524800C530C9E41
+        BC97CFC473F27CCAC6554D9F1CE3283DD5B59A40C6D80DE1B6FB08E7B0412CBC
+        A902F7C6453C347F625AE2893C3CC08E35731CC0FD31C4851077388A728FC5D9
+        B631D050620394CEDFEE4E42F09C3893B271F691238C50154B269ECF7D3A7187
+        519EFBB05713D224871E1B587866C54CCAD24470EDF17E67388BF34AB36DD626
+        21C4748338B0FA9E5B2B2271B985253625251FABF29E13A753132F1B61984B53
+        4B2B81E62BDAFDD222A30EDF052DE2B3175F6704440885E5B32B5296DD1EEF27
+        C23F2D725D0256012842080BE07AF0F60937C41357B4EFB7503A7ADFF84D2200
+        2503DFF986948DAB9AF9A7BA63B4E2F9BCD6B8EF1A576E945BDBA039367BC975
+        3175271A93771577CDBADE01FC851042518049D9366B6DE2919799B41223F9D8
+        7BB182D8F3E11F92565E39616C4CA8EB3951A3DD1FE330CA72FFB1633C5E35D9
+        691AC30B7CFEB8E83306B32694247DE62CCE2BCDB02A2D80430196DE5851D498
+        90B45E59CC35ED5A904028C282AF2E756FDBCB46EA31BDE6307DE7B5AAEDC38B
+        0D82BE0B5A6F97150F32699D9692392FA3FC34C3D79BC7979C011629C0F43B6F
+        2EB7C56769AFACF33526219885A391F77C762A65C5AE1B26749041C1734CEB5D
+        4DCDB57A7CF51735122566B5D6EAD97920B5E34C3561A13D2FB100D3ACC0B0A2
+        820129E2CB04D3444224BE063C9F9D4859B146BC43909E639AA05C639D467DEE
+        4FB4A8AF6ACAF531F53FF3EB43299D1740D5C4E4AA0E50E9189C0B14588141F3
+        A6969525CFDA3E76360D5F63AED598BC69894F1CA7BD2F254D812081667DA261
+        787B282DF1D56B13A6F692EB681FC47B6A1A7878D39E94652F98664F394E07B8
+        797C8903685580046A8ED1386375CB206F4A52D5F3A820A320557C67EB52566C
+        1F51026A146414CFD18E78DE35C661D4597B41B3637BF12090929D1F9CA0EA3B
+        DB52960BB07AC184B47974276EB302D6C45924A0EAA4CC044D8958D2A8518E1C
+        AB4E5AE9AC193742B40DD43650238676548E751AE5B83FFECCC8EFBB7099AACD
+        EFB2C793DAAE41EBED74F66D824D00AA7CEBFEC4F3BD964CD37C996986D49829
+        B5C50C60DC1F1FE796250F242CAA3F51569883E7D9451464A79C5F3420E6BD18
+        4DD2DB3A8CDEEC94543D8908A8FA373CAAE03B973A46EF0FE46767B2F37B73BB
+        4CBA1D0A104DFA54464155759B543B08CB28A8119011ED574FE9ECBBAF919F9D
+        897BC3BC9E4473AA156D513E71CF4B69EAE5881678A851CD468502510B9AEC34
+        4B711FF0F49C453751E918CCCE755F4FEBC593E08A15080642E170D2D54E43AD
+        75F2BA5A77C4EDC2F896B7879FFD89FCEC4C56CF9FC8FABBA6F4A698162BF0C5
+        DE63172249772B18BD6DD1ED59271B8D5D036B0A868C51567FA0AC3087E5B32B
+        58BD6062B7EDD98CB73EAE3D029A8A9F3DEABB9C3B6F6A8A18468DE8443BC7E6
+        0223C038EE4DDDF06143B19716D2BE9070FAEC05EAFC5F32A42087F1CE61804A
+        C3C520D567FDD832AD4C1B5B8ACB39047B610E55E30B713906F598AC19FEC0D5
+        56E00B2B70E0779FD6FFD9DA3B2625CF2D555DCD15888AF86752E2F93C35F1F5
+        7FFFD72C9F3FD38801AAEEDD409DFF4B562D9EC5FABF9B0BD136D6FFE2B73CFE
+        F26EA68D2DC5FDD33B2112866858F3297D84FFD853D30AECB5026FEF3D76E19E
+        B46FA8914EA3341D06F1DA646F02602FCA83482BA082AAE2A9D6161C5CD71741
+        E41AA811DC87B4098CAACAE1A640A7EF48031CACF60F05762AC0F1B6889AE76D
+        08A6FF1645DB40D57B211AD61A1C0D43F41ABEFAD4C3C52A975D231EB9465353
+        138196AB80BE32DA5E46FBACCBD06CFD5EDF920E84C2C196D6B6A152CA538A94
+        B215D8B76DCFE9F47121688D899AC847AF41F41A7B3CC955BD72D4309DB496CC
+        66E1720ED6CB0853DB1800C03E74A04EBA6FB7A23DFFCEB14F817DD031E7B6E9
+        473B3C5D8F02D4B60ED29130BE3A7FCAECF6A2824EC43535AF2C2F32B4A05DCD
+        01AAC6EB4EB08FF1D2FBD556E025E8085C76855A2343BC0DC1BA2E6FE6512346
+        48EB3999DA4A5CE54375FBD6465F0BA73B71399651909DA1094F8DE2B2E7F3FB
+        A7E6771AE6F61DBC0DC13A6F43B01870834E5C4A191142FC6CD153BB6F3BF42F
+        7FD5F55D4C5285A88AE774EAE92697E33A9DB8F68E7D880DFBE0422D4688B482
+        1AA5C026A89A50DC33565DC0DA573F3905EC97525E81D805857F3DECBD38A14B
+        4EAE133C3517533E77D9F34DAA7EADE33A7A4DB3E57EEAE5760442E1E08EFDDE
+        E180B127CE202EA56C067EB1E8A9DD5D737226245AA534C33E382B96ACDAA68F
+        01FA6F03B1192B9EDF7728AACAADE6ADA09D574B9F3DECBD587AB0DA7F926EE0
+        4892D50B689FEEFD6A082682B72158B77D5FCD04E0DFCCF76388EB7B43D7CF7F
+        E2BDE42B7E9D906841DE8C74D3BDFD8DE98FBCE9071E9152C638A2443B2276F8
+        03576B96FC78B7BB2B05A79BE077397ABEF2D15B6C78E3F007FEC0D54629E5BF
+        777E16475CF77AF7BFF1817774FB4826157C8DA9EDBB374B3EBDC1C16AFFC9C7
+        B6FD7120DADEF93824DCE726A5FC1258F48D27770D4C67EFE954DDE51CD2C5A6
+        F61D02A17070CEBA772291A8FAB8943261FB936EF093527E1455E5DA39EBDE89
+        0642E1A43B79922DCB823669D09BB1734F1008858323BEB5B5B6A5B5ED15E09D
+        64F952EE6C9452EE68696DDB317CF9D6BA44E49B42E1942B1B5FB57DEBA4CF36
+        5F6DFBB994F2692965D2F9C42E6DDB1642ACCC1B98B962D73FDF36D0BCF7AD29
+        14C6E34D1EBC98B75FF73702A17070F8F2AD752DAD6D6F4829D7A7CBDFE5FDEA
+        4288FB142136EC5CF7F5BA54876AFE2F70B0DA7F72EE63EF360742E15780CD52
+        CAD40B6C748FB802CC069E5E32B3FCF2EB8FCEAEEA556BFB080F6EDABFE78577
+        8F5F1F55E57D52CADF76F5BD9E1CCD7002CFE7D83246EEFEE15F5A93EE5FEF67
+        781B8275D31F79D3EF0F5C6D04BE29A54C3D13D2093D3E85A49F2F7B619273C8
+        E91D6BE638BEAAB329815038B8E2F97D87B6EFAB71001B81D7A494DDDE3FDADB
+        E35783D0760C3E34C939E4F88E35739CFD79FC6AEDAB9F9CDAB1DF6B8FAA7207
+        F0EC577EFC2AAE106D9FE8DDC0BD39B60C75CD62D7A53B6F2EEFB5160442E1E0
+        736F7FF6E9CBEF575BCF34368F0236A311BED0EB36F7C311CB29685BAA6EC9B4
+        2A6D33C797D48F1B39885B5DA505378F2F71245BB1E97CC4F240756361A83552
+        0C7C023CFDFFF28865D2C2852803FE1CED50ED686018900B64A2CDFE48200C5C
+        035A8046A006F83DF0DF40BD94FD334BF1BFCAF3090585D7E9BD000000004945
+        4E44AE426082
+      }
+    end
+    object lblCurrentBlockCaption: TLabel
+      Left = 90
+      Height = 14
+      Top = 11
+      Width = 77
+      Caption = 'Total Blocks:'
+      ParentColor = False
+    end
+    object lblCurrentBlock: TLabel
+      Left = 167
+      Height = 14
+      Top = 11
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object lblCurrentBlockTimeCaption: TLabel
+      Left = 90
+      Height = 14
+      Top = 26
+      Width = 115
+      Caption = 'Current Block Age:'
+      ParentColor = False
+    end
+    object lblCurrentBlockTime: TLabel
+      Left = 207
+      Height = 14
+      Top = 26
+      Width = 100
+      Caption = '000 seconds ago'
+      ParentColor = False
+    end
+    object lblOperationsPendingCaption: TLabel
+      Left = 90
+      Height = 14
+      Top = 41
+      Width = 123
+      Caption = 'Pending Operations:'
+      ParentColor = False
+    end
+    object lblOperationsPending: TLabel
+      Left = 214
+      Height = 14
+      Top = 41
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object lblMiningStatusCaption: TLabel
+      Left = 90
+      Height = 14
+      Top = 56
+      Width = 83
+      Caption = 'Miner Clients:'
+      ParentColor = False
+    end
+    object lblMinersClients: TLabel
+      Left = 177
+      Height = 14
+      Top = 56
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object lblCurrentDifficultyCaption: TLabel
+      Left = 429
+      Height = 14
+      Top = 11
+      Width = 93
+      Caption = 'Current Target:'
+      ParentColor = False
+    end
+    object lblCurrentDifficulty: TLabel
+      Left = 526
+      Height = 14
+      Top = 11
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object lblTimeAverage: TLabel
+      Left = 395
+      Height = 14
+      Top = 26
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object Label4: TLabel
+      Left = 309
+      Height = 14
+      Top = 26
+      Width = 85
+      Caption = 'Time average:'
+      ParentColor = False
+    end
+    object Label8: TLabel
+      Left = 90
+      Height = 14
+      Top = 70
+      Width = 78
+      Caption = 'Node Status:'
+      ParentColor = False
+    end
+    object lblNodeStatus: TLabel
+      Left = 168
+      Height = 14
+      Top = 70
+      Width = 15
+      Caption = '???'
+      ParentColor = False
+    end
+    object Label5: TLabel
+      Left = 309
+      Height = 14
+      Top = 11
+      Width = 60
+      Caption = 'Accounts:'
+      ParentColor = False
+    end
+    object lblCurrentAccounts: TLabel
+      Left = 371
+      Height = 14
+      Top = 11
+      Width = 21
+      Caption = '000'
+      ParentColor = False
+    end
+    object lblTimeAverageAux: TLabel
+      Left = 395
+      Height = 13
+      Top = 41
+      Width = 21
+      Caption = '000'
+      Font.Color = clGray
+      Font.Height = -11
+      Font.Name = 'Tahoma'
+      ParentColor = False
+      ParentFont = False
+    end
+    object Label16: TLabel
+      Left = 360
+      Height = 13
+      Top = 56
+      Width = 90
+      Caption = 'Blocks Found:'
+      Font.Color = clWindowText
+      Font.Height = -11
+      Font.Name = 'Tahoma'
+      Font.Style = [fsBold]
+      ParentColor = False
+      ParentFont = False
+    end
+    object lblBlocksFound: TLabel
+      Left = 450
+      Height = 13
+      Hint = 'Blocks found while Miner is running...'
+      Top = 56
+      Width = 24
+      Caption = '000'
+      Font.Color = clWindowText
+      Font.Height = -11
+      Font.Name = 'Tahoma'
+      Font.Style = [fsBold]
+      ParentColor = False
+      ParentFont = False
+      ParentShowHint = False
+      ShowHint = True
+    end
+    object lblReceivedMessages: TLabel
+      Cursor = crHandPoint
+      Left = 360
+      Height = 22
+      Top = 66
+      Width = 210
+      Caption = 'Received Messages'
+      Font.Color = clRed
+      Font.Height = -19
+      Font.Name = 'Tahoma'
+      Font.Style = [fsBold]
+      ParentColor = False
+      ParentFont = False
+      Visible = False
+      OnClick = lblReceivedMessagesClick
+    end
+    object lblBuild: TLabel
+      Left = 743
+      Height = 22
+      Top = 3
+      Width = 56
+      Caption = 'Build'
+      Font.Color = clWindowText
+      Font.Height = -19
+      Font.Name = 'Tahoma'
+      Font.Style = [fsBold]
+      ParentColor = False
+      ParentFont = False
+    end
+    object HideButton: TButton
+      Left = 776
+      Height = 27
+      Top = 65
+      Width = 91
+      Cancel = True
+      Caption = 'Hide'
+      Default = True
+      Enabled = False
+      ModalResult = 11
+      OnClick = HideButtonClick
+      TabOrder = 0
+    end
+  end
+end

+ 178 - 0
Units/Forms/UFRMSyncronizationDialog.pas

@@ -0,0 +1,178 @@
+unit UFRMSyncronizationDialog;
+
+{$mode delphi}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
+  StdCtrls;
+
+type
+
+  { TFRMSyncronizationDialog }
+
+  TFRMSyncronizationDialog = class(TForm)
+    HideButton:TButton;
+    Image1:TImage;
+    Label16:TLabel;
+    Label4:TLabel;
+    Label5:TLabel;
+    Label8:TLabel;
+    lblBlocksFound:TLabel;
+    lblBuild:TLabel;
+    lblCurrentAccounts:TLabel;
+    lblCurrentBlock:TLabel;
+    lblCurrentBlockCaption:TLabel;
+    lblCurrentBlockTime:TLabel;
+    lblCurrentBlockTimeCaption:TLabel;
+    lblCurrentDifficulty:TLabel;
+    lblCurrentDifficultyCaption:TLabel;
+    lblMinersClients:TLabel;
+    lblMiningStatusCaption:TLabel;
+    lblNodeStatus:TLabel;
+    lblOperationsPending:TLabel;
+    lblOperationsPendingCaption:TLabel;
+    lblReceivedMessages:TLabel;
+    lblTimeAverage:TLabel;
+    lblTimeAverageAux:TLabel;
+    pnlTop:TPanel;
+    procedure HideButtonClick(Sender:TObject);
+    procedure lblReceivedMessagesClick(Sender:TObject);
+  private
+    { private declarations }
+    FMinersBlocksFound: Integer;
+  public
+    { public declarations }
+    procedure UpdateNodeStatus;
+    procedure UpdateBlockChainState;
+    procedure SetMinersBlocksFound(const Value: Integer);
+    Property MinersBlocksFound : Integer read FMinersBlocksFound write SetMinersBlocksFound;
+  end;
+
+var
+  //FRMSyncronizationDialog: TFRMSyncronizationDialog = nil;
+  FRMSyncronizationDialogIsFirstOpen:boolean =true;  //TODO update
+
+implementation
+uses UNetProtocol,UTime,UConst, UUserInterface;
+{$R *.lfm}
+
+procedure TFRMSyncronizationDialog.lblReceivedMessagesClick(Sender:TObject);
+begin
+  TUserInterface.ShowMessagesForm;
+end;
+
+procedure TFRMSyncronizationDialog.HideButtonClick(Sender:TObject);
+begin
+  Hide;
+end;
+
+procedure TFRMSyncronizationDialog.UpdateNodeStatus;
+Var status : AnsiString;
+//  FNode=FRMWallet.FNode;
+begin
+ // with FRMWallet do begin
+  if not TUserInterface.Started then exit;
+
+  If Not Assigned(TUserInterface.Node) then begin
+    lblNodeStatus.Font.Color := clRed;
+    lblNodeStatus.Caption := 'Initializing...';
+  end else begin
+    If TUserInterface.Node.IsReady(status) then begin
+      if TNetData.NetData.NetStatistics.ActiveConnections>0 then begin
+        lblNodeStatus.Font.Color := clGreen;
+        if TNetData.NetData.IsDiscoveringServers then begin
+          lblNodeStatus.Caption := 'Discovering servers';
+        end else if TNetData.NetData.IsGettingNewBlockChainFromClient then begin
+          lblNodeStatus.Caption := 'Obtaining new blockchain';
+        end else begin
+          lblNodeStatus.Caption := 'Running';
+        end;
+      end else begin
+        lblNodeStatus.Font.Color := clRed;
+        lblNodeStatus.Caption := 'Alone in the world...';
+      end;
+    end else begin
+      lblNodeStatus.Font.Color := clRed;
+      lblNodeStatus.Caption := status;
+    end;
+  end;
+
+// this rewrite   -- TODO
+//  If Assigned(FBackgroundPanel) then begin
+//    FBackgroundPanel.Font.Color:=lblNodeStatus.Font.Color;
+//    FBackgroundPanel.Caption:='Please wait until finished: '+lblNodeStatus.Caption;
+  //end;
+end;
+
+procedure TFRMSyncronizationDialog.UpdateBlockChainState;
+Var
+  isMining : boolean;
+//  hr : Int64;
+  i,mc : Integer;
+  s : String;
+  mtl : TList;
+  f, favg : real;
+begin
+  if not TUserInterface.Started then exit;
+  UpdateNodeStatus;
+  mc := 0;
+  if Assigned(TUserInterface.Node) then begin
+    if TUserInterface.Node.Bank.BlocksCount>0 then begin
+      lblCurrentBlock.Caption :=  Inttostr(TUserInterface.Node.Bank.BlocksCount)+' (0..'+Inttostr(TUserInterface.Node.Bank.BlocksCount-1)+')'; ;
+    end else lblCurrentBlock.Caption :=  '(none)';
+    lblCurrentAccounts.Caption := Inttostr(TUserInterface.Node.Bank.AccountsCount);
+    lblCurrentBlockTime.Caption := UnixTimeToLocalElapsedTime(TUserInterface.Node.Bank.LastOperationBlock.timestamp);
+    lblOperationsPending.Caption := Inttostr(TUserInterface.Node.Operations.Count);
+    lblCurrentDifficulty.Caption := InttoHex(TUserInterface.Node.Operations.OperationBlock.compact_target,8);
+    favg := TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
+    f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
+    lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
+    if favg>=CT_NewLineSecondsAvg then begin
+      lblTimeAverage.Font.Color := clNavy;
+    end else begin
+      lblTimeAverage.Font.Color := clOlive;
+    end;
+    lblTimeAverageAux.Caption := Format('Last %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec.',[
+        CT_CalcNewTargetBlocksAverage * 2 ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage * 2)),
+        ((CT_CalcNewTargetBlocksAverage * 3) DIV 2) ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage((CT_CalcNewTargetBlocksAverage * 3) DIV 2)),
+        ((CT_CalcNewTargetBlocksAverage DIV 4)*3),FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(((CT_CalcNewTargetBlocksAverage DIV 4)*3))),
+        CT_CalcNewTargetBlocksAverage DIV 2,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 2)),
+        CT_CalcNewTargetBlocksAverage DIV 4,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 4))]);
+  end else begin
+    isMining := false;
+    lblCurrentBlock.Caption := '';
+    lblCurrentAccounts.Caption := '';
+    lblCurrentBlockTime.Caption := '';
+    lblOperationsPending.Caption := '';
+    lblCurrentDifficulty.Caption := '';
+    lblTimeAverage.Caption := '';
+    lblTimeAverageAux.Caption := '';
+  end;
+  if (Assigned(TUserInterface.PoolMiningServer)) And (TUserInterface.PoolMiningServer.Active) then begin
+    If TUserInterface.PoolMiningServer.ClientsCount>0 then begin
+      lblMinersClients.Caption := IntToStr(TUserInterface.PoolMiningServer.ClientsCount)+' connected JSON-RPC clients';
+      lblMinersClients.Font.Color := clNavy;
+    end else begin
+      lblMinersClients.Caption := 'No JSON-RPC clients';
+      lblMinersClients.Font.Color := clDkGray;
+    end;
+    MinersBlocksFound := TUserInterface.PoolMiningServer.ClientsWins;
+  end else begin
+    MinersBlocksFound := 0;
+    lblMinersClients.Caption := 'JSON-RPC server not active';
+    lblMinersClients.Font.Color := clRed;
+  end;
+  lblBuild.Caption := 'Build: '+CT_ClientAppVersion;
+end;
+
+procedure TFRMSyncronizationDialog.SetMinersBlocksFound(const Value: Integer);
+begin
+  FMinersBlocksFound := Value;
+  lblBlocksFound.Caption := Inttostr(Value);
+  if Value>0 then lblBlocksFound.Font.Color := clGreen
+  else lblBlocksFound.Font.Color := clDkGray;
+end;
+end.
+

File diff suppressed because it is too large
+ 106 - 1170
Units/Forms/UFRMWallet.lfm


File diff suppressed because it is too large
+ 145 - 984
Units/Forms/UFRMWallet.pas


+ 5 - 21
Units/Forms/UFRMWalletKeys.pas

@@ -91,7 +91,7 @@ uses
 {$ELSE}
 {$ELSE}
   LCLIntf, LCLType,
   LCLIntf, LCLType,
 {$ENDIF}
 {$ENDIF}
-  UCrypto, UAccounts, UFRMNewPrivateKeyType, UAES;
+  UCrypto, UAccounts, UUserInterface, UFRMNewPrivateKeyType, UAES;
 
 
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   {$R *.dfm}
   {$R *.dfm}
@@ -392,30 +392,13 @@ begin
 end;
 end;
 
 
 procedure TFRMWalletKeys.bbUpdatePasswordClick(Sender: TObject);
 procedure TFRMWalletKeys.bbUpdatePasswordClick(Sender: TObject);
-Var s,s2 : String;
 begin
 begin
   if FWalletKeys.IsValidPassword then begin
   if FWalletKeys.IsValidPassword then begin
-    s := ''; s2 := '';
-    if Not InputQuery('Change password','Enter new password',s) then exit;
-    if trim(s)<>s then raise Exception.Create('Password cannot start or end with a space character');
-    if Not InputQuery('Change password','Enter new password again',s2) then exit;
-    if s<>s2 then raise Exception.Create('Two passwords are different!');
-
-    FWalletKeys.WalletPassword := s;
-    Application.MessageBox(PChar('Password changed!'+#10+#10+
-      'Please note that your new password is "'+s+'"'+#10+#10+
-      '(If you lose this password, you will lose your wallet forever!)'),
-      PChar(Application.Title),MB_ICONWARNING+MB_OK);
-    UpdateWalletKeys;
+    TUserInterface.ChangeWalletPassword(Self, FWalletKeys);
   end else begin
   end else begin
-    s := '';
-    Repeat
-      if Not InputQuery('Wallet password','Enter wallet password',s) then exit;
-      FWalletKeys.WalletPassword := s;
-      if Not FWalletKeys.IsValidPassword then Application.MessageBox(PChar('Invalid password'),PChar(Application.Title),MB_ICONERROR+MB_OK);
-    Until FWalletKeys.IsValidPassword;
-    UpdateWalletKeys;
+    TUserInterface.UnlockWallet(Self, FWalletKeys);
   end;
   end;
+  UpdateWalletKeys;
 end;
 end;
 
 
 procedure TFRMWalletKeys.CheckIsWalletKeyValidPassword;
 procedure TFRMWalletKeys.CheckIsWalletKeyValidPassword;
@@ -574,3 +557,4 @@ begin
 end;
 end;
 
 
 end.
 end.
+

+ 26 - 0
Units/Forms/UFRMWalletKeys2.pas

@@ -0,0 +1,26 @@
+unit UFRMWalletKeys2;
+
+{$mode delphi}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
+
+type
+  TFRMWalletKeys2 = class(TForm)
+  private
+    { private declarations }
+  public
+    { public declarations }
+  end;
+
+var
+  FRMWalletKeys2: TFRMWalletKeys2;
+
+implementation
+
+{$R *.lfm}
+
+end.
+

+ 1034 - 0
Units/Forms/UUserInterface.pas

@@ -0,0 +1,1034 @@
+unit UUserInterface;
+
+{$mode delphi}
+
+interface
+
+uses
+  SysUtils, Classes, Forms, Controls, Windows, ExtCtrls,
+  UAccounts, UNode, UWalletKeys, UAppParams, UConst, UFolderHelper, UGridUtils, URPC, UPoolMining, ULog, UThread, UNetProtocol, UCrypto,
+  UFRMWallet, UFRMSyncronizationDialog, UFRMAccountExplorer, UFRMPendingOperations, UFRMOperation,
+  UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys;
+
+type
+  { Forward Declarations }
+  TLoadSafeBoxThread = class;
+
+  { TMinerPrivateKey }
+
+  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
+
+  { TUserInterface }
+
+  TUserInterface = class
+    private
+      FUILock : TPCCriticalSection; static;
+      FStarted : boolean; static;
+      FAppParams : TAppParams; static;
+      FWallet : TFRMWallet; static;
+      FSyncronizationDialog : TFRMSyncronizationDialog; static;
+      FAccountExplorer : TFRMAccountExplorer; static;
+      FPendingOperationForm : TFRMPendingOperations; static;
+      FOperationsExplorerForm : TFRMOperation; static;
+      FBlockExplorerForm : TFRMBlockExplorer; static;
+      FLogsForm : TFRMLogs; static;
+      FNodesForm : TFRMNodes; static;
+      FMessagesForm : TFRMMessages; static;
+
+      FIsActivated : Boolean; static;
+      FMinerPrivateKeyType : TMinerPrivateKey; static;
+      FWalletKeys : TWalletKeysExt; static;
+
+      FRPCServer : TRPCServer; static;
+      FMustProcessWalletChanged : Boolean; static;
+      FPoolMiningServer : TPoolMiningServer; static;
+
+      FUpdating : Boolean; static;
+      FLog : TLog; static;
+      FNode : TNode; static;
+      TimerUpdateStatus: TTimer; static;
+      FTrayIcon: TTrayIcon; static;
+      FNodeNotifyEvents : TNodeNotifyEvents; static;
+
+      FStatusBar0Text : AnsiString; static;
+      FStatusBar1Text : AnsiString; static;
+      FStatusBar2Text : AnsiString; static;
+      FMessagesNotificationText : AnsiString; static;
+
+      class procedure RefreshConnectionStatusDisplay;
+      class function GetAccountKeyForMiner: TAccountKey;
+      class procedure SetStatusBar0Text(text : AnsiString); static;
+      class procedure SetStatusBar1Text(text : AnsiString); static;
+      class procedure SetStatusBar2Text(text : AnsiString); static;
+      class procedure SetMessagesNotificationText(text : AnsiString); static;
+      class procedure TrayIconDblClick(Sender: TObject);
+
+      class procedure FinishedLoadingApp;
+      class procedure LoadAppParams;
+      class procedure SaveAppParams;
+      class procedure NotifyConfigChanged;
+
+
+      // Handlers
+      class procedure TimerUpdateStatusTimer(Sender: TObject);
+
+      // Blockchain event handlers. TODO: refactor this out with TNotifyEvents
+      // so forms that need these messages subscribe directly
+      class procedure OnAccountsChanged(Sender: TObject);
+      class procedure OnNewAccount(Sender: TObject);
+      class procedure OnReceivedHelloMessage(Sender: TObject);
+      class procedure OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+      class procedure OnNetStatisticsChanged(Sender: TObject);
+      class procedure OnNetConnectionsUpdated(Sender: TObject);
+      class procedure OnNetNodeServersUpdated(Sender: TObject);
+      class procedure OnNetBlackListUpdated(Sender: TObject);
+      class procedure OnMiningServerNewBlockFound(Sender: TObject);
+    public
+      class property Started : boolean read FStarted;
+      class property Node : TNode read FNode;
+      class property Log : TLog read FLog;
+      class property AppParams : TAppParams read FAppParams;
+      class property PoolMiningServer : TPoolMiningServer read FPoolMiningServer;
+      class property WalletKeys : TWalletKeysExt read FWalletKeys;
+      class property StatusBar0Text : AnsiString read FStatusBar0Text write SetStatusBar0Text;
+      class property StatusBar1Text : AnsiString read FStatusBar1Text write SetStatusBar1Text;
+      class property StatusBar2Text : AnsiString read FStatusBar2Text write SetStatusBar2Text;
+      class property MessagesNotificationText : AnsiString read FMessagesNotificationText write SetMessagesNotificationText;
+      class procedure Start(mainForm : TForm);
+      class procedure Quit;
+      class procedure CheckNodeIsReady;
+      // Show Dialogs
+      class procedure ShowAboutBox(parentForm : TForm);
+      class procedure ShowOptionsDialog(parentForm: TForm);
+      class procedure ShowNewOperationDialog(parentForm : TForm; accounts : TOrderedCardinalList; defaultFee : Cardinal);
+      class procedure ShowSyncronizationDialog(parentForm : TForm);
+      class procedure ShowWalletKeysDialog(parentForm : TForm);
+      class procedure ShowNodeIPDialog(parentForm : TForm);
+      class procedure ShowAccountInformationDialog(parentForm : TForm; account : UInt64);
+      class procedure ShowPrivateKeysDialog(parentForm: TForm);
+      class procedure UnlockWallet(parentForm: TForm;  walletKeys : TWalletKeys);
+      class procedure ChangeWalletPassword(parentForm: TForm; walletKeys : TWalletKeys);
+
+      // Show sub-forms
+      class procedure ShowAccountExplorer;
+      class procedure ShowBlockExplorer;
+      class procedure ShowOperationsExplorer;
+      class procedure ShowPendingOperations;
+      class procedure ShowMessagesForm;
+      class procedure ShowNodesForm;
+      class procedure ShowLogsForm;
+  end;
+
+  { TLoadSafeBoxThread }
+  TLoadSafeBoxThread = Class(TPCThread)
+  protected
+    procedure BCExecute; override;
+  End;
+
+implementation
+
+uses Dialogs, UCommon, UCommonUI, UOpenSSL, UFileStorage, UTime, UFRMAbout, UFRMNodesIp, UFRMPascalCoinWalletConfig ;
+
+{%region UI Lifecyle}
+
+class procedure TUserInterface.Start(mainForm : TForm);
+Var ips : AnsiString;
+  nsarr : TNodeServerAddressArray;
+begin
+  inherited;
+  if FIsActivated then exit;
+  FIsActivated := true;
+  try
+    // Create UI lock
+    FUILock := TPCCriticalSection.Create('TUserInterface.UILock');
+
+    // Initialise field defaults
+    FIsActivated := false;
+    FStarted := false;
+    FRPCServer := Nil;
+    FNode := Nil;
+    FPoolMiningServer := Nil;
+    FNodeNotifyEvents := nil;
+    FUpdating := false;
+    FStatusBar0Text := '';
+    FStatusBar1Text := '';
+    FStatusBar2Text := '';
+    FMessagesNotificationText := '';
+
+    // Create main form and try icon
+    FWallet := mainForm as TFRMWallet;
+    if (FWallet = nil)
+      then raise Exception.Create('Main form is not TWallet');
+    FTrayIcon := TTrayIcon.Create(TUserInterface.FWallet);
+    FTrayIcon.OnDblClick := TrayIconDblClick;
+    FTrayIcon.Visible := true;
+    FTrayIcon.Hint := FWallet.Caption;
+    FTrayIcon.BalloonTitle := 'Restoring the window.';
+    FTrayIcon.BalloonHint := 'Double click the system tray icon to restore Pascal Coin';
+    FTrayIcon.BalloonFlags := bfInfo;
+    TimerUpdateStatus := TTimer.Create(TUserInterface.FWallet);
+    TimerUpdateStatus.Enabled := false;
+
+    // Create log
+    FLog := TLog.Create(FWallet);
+    FLog.SaveTypes := [];
+
+    // Create data directories
+    If Not ForceDirectories(TFolderHelper.GetPascalCoinDataFolder) then
+      raise Exception.Create('Cannot create dir: '+TFolderHelper.GetPascalCoinDataFolder);
+
+    // Open AppParams
+    TUserInterface.FAppParams := TAppParams.Create(FWallet);
+    TUserInterface.FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+
+    // Open Wallet
+    Try
+      FWalletKeys := TWalletKeysExt.Create(FWallet);  // On Activate, this will be populated
+      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+    Except
+      On E:Exception do begin
+        E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
+        Raise;
+      end;
+    End;
+
+    // Load peer list
+    ips := FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].GetAsString('');
+    TNode.DecodeIpStringToNodeServerAddressArray(ips,nsarr);
+    TNetData.NetData.DiscoverFixedServersOnly(nsarr);
+    setlength(nsarr,0);
+
+    // Start Node
+    FNode := TNode.Node;
+    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    FNode.PeerCache := FAppParams.ParamByName[CT_PARAM_PeerCache].GetAsString('')+';'+CT_Discover_IPs;
+
+    // Subscribe to Node events (TODO refactor with FNotifyEvents)
+    FNodeNotifyEvents := TNodeNotifyEvents.Create(FWallet);
+    FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
+    FNodeNotifyEvents.OnNodeMessageEvent :=  OnNodeMessageEvent;
+
+    // Start RPC server
+    FRPCServer := TRPCServer.Create;
+    FRPCServer.WalletKeys := WalletKeys;
+    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
+    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+    WalletKeys.SafeBox := FNode.Bank.SafeBox;
+
+    // Initialise Database
+    FNode.Bank.StorageClass := TFileStorage;
+    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+    TFileStorage(FNode.Bank.Storage).Initialize;
+
+    // Init Grid
+    //FAccountsGrid.Node := FNode;
+    //HS FSelectedAccountsGrid.Node := FNode;
+    //HS FAccountsGrid.Node := FNode;
+    //HS FOperationsAccountGrid.Node := FNode;
+
+    // Reading database
+    TLoadSafeBoxThread.Create(false).FreeOnTerminate := true;
+
+    // Init
+    TNetData.NetData.OnReceivedHelloMessage := OnReceivedHelloMessage;
+    TNetData.NetData.OnStatisticsChanged := OnNetStatisticsChanged;
+    TNetData.NetData.OnNetConnectionsUpdated := OnNetConnectionsUpdated;
+    TNetData.NetData.OnNodeServersUpdated := OnNetNodeServersUpdated;
+    TNetData.NetData.OnBlackListUpdated := OnNetBlackListUpdated;
+
+    // Start refresh timer
+    TimerUpdateStatus.OnTimer := TimerUpdateStatusTimer;
+    TimerUpdateStatus.Interval := 1000;
+    TimerUpdateStatus.Enabled := true;
+
+
+    // Load app params
+    LoadAppParams;
+
+    // open the sync dialog
+    FSyncronizationDialog.UpdateBlockChainState;   //TODO fix this work-flow
+    RefreshConnectionStatusDisplay;
+
+    // Setup tray icon
+
+    // HS ???
+    //FMinersBlocksFound := 0;
+
+  // Disable wallet form
+    FWallet.Enabled:=false;
+    FStarted := true;
+  Except
+    On E:Exception do begin
+      E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
+      Application.MessageBox(PChar(E.Message),PChar(Application.Title),MB_ICONERROR+MB_OK);
+      Halt;
+    end;
+  end;
+
+
+  // Notify accounts again?
+  OnAccountsChanged(FWallet);
+
+  // Refresh status bar since may not have been displayed
+  SetStatusBar0Text(FStatusBar0Text);
+  SetStatusBar0Text(FStatusBar1Text);
+  SetStatusBar0Text(FStatusBar2Text);
+  SetMessagesNotificationText(FMessagesNotificationText);
+
+  // Show sync dialog
+  ShowSyncronizationDialog(FWallet);
+
+  // Show about box if first time load
+  if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
+    FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
+    ShowAboutBox(nil);
+  end;
+end;
+
+class procedure TUserInterface.Quit;
+Var i : Integer;
+  step : String;
+begin
+  TLog.NewLog(ltinfo,Classname,'Destroying form - START');
+  Try
+    FreeAndNil(FRPCServer);
+    FreeAndNil(FPoolMiningServer);
+    step := 'Saving params';
+    SaveAppParams;
+    FreeAndNil(FAppParams);
+
+    step := 'Assigning nil events';
+    FLog.OnNewLog :=Nil;
+
+    TNetData.NetData.OnReceivedHelloMessage := Nil;
+    TNetData.NetData.OnStatisticsChanged := Nil;
+    TNetData.NetData.OnNetConnectionsUpdated := Nil;
+    TNetData.NetData.OnNodeServersUpdated := Nil;
+    TNetData.NetData.OnBlackListUpdated := Nil;
+    //step := 'Destroying NodeNotifyEvents';
+    //FreeAndNil(FNodeNotifyEvents);
+    //
+    step := 'Assigning Nil to TNetData';
+    TNetData.NetData.OnReceivedHelloMessage := Nil;
+    TNetData.NetData.OnStatisticsChanged := Nil;
+
+    step := 'Desactivating Node';
+    TNode.Node.NetServer.Active := false;
+    FNode := Nil;
+
+    TNetData.NetData.Free;
+
+    step := 'Processing messages 1';
+    Application.ProcessMessages;
+
+    step := 'Destroying Node';
+    TNode.Node.Free;
+
+    step := 'Destroying Wallet';
+    FreeAndNil(FWalletKeys);
+
+    step := 'Processing messages 2';
+    Application.ProcessMessages;
+    step := 'Destroying stringslist';
+
+    // TODO - refactor out to TNotifyManyEvents
+    FNodeNotifyEvents.Node := Nil;
+    FreeAndNil(FNodeNotifyEvents);
+
+    step := 'Destroying UI graph';
+    FWallet.Destroy; // Destroys root wallet, non-modal forms and all their attached components
+    FWallet := nil;
+    FMessagesForm := nil;
+    FAccountExplorer := nil;
+    FTrayIcon := nil;
+    FreeAndNil(FUILock);
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error destroying Form step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
+    end;
+  End;
+  TLog.NewLog(ltinfo,Classname,'Destroying form - END');
+  FreeAndNil(FLog);
+  Sleep(100);
+end;
+
+class procedure TUserInterface.FinishedLoadingApp;
+begin
+  FPoolMiningServer := TPoolMiningServer.Create;
+  FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+  FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
+  FPoolMiningServer.MinerPayload := FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('');
+  FNode.Operations.AccountKey := GetAccountKeyForMiner;
+  FPoolMiningServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
+  FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
+
+  //HS review
+//  FRMSyncronizationDialog.Hide; Herman May be, need close this window after load?
+  FSyncronizationDialog.BorderIcons:=[biSystemMenu];
+  FSyncronizationDialog.HideButton.Enabled:=true;
+  FWallet.Enabled:=True;
+
+  // Refresh UI
+  OnAccountsChanged(FWallet);
+end;
+
+{%endregion}
+
+{%region Show Dialogs}
+
+class procedure TUserInterface.ShowAboutBox(parentForm : TForm);
+begin
+  with TFRMAbout.Create(parentForm) do
+  try
+    ShowModal;
+  finally
+    Free;
+  end;
+end;
+
+class procedure TUserInterface.ShowOptionsDialog(parentForm: TForm);
+begin
+  With TFRMPascalCoinWalletConfig.Create(parentForm) do
+  try
+    AppParams := FAppParams;
+    WalletKeys := FWalletKeys;
+    if ShowModal=MrOk then begin
+      SaveAppParams;
+      NotifyConfigChanged;
+    end;
+  finally
+    Free;
+  end;
+end;
+
+// TODO - refactor with accounts as ARRAY
+class procedure TUserInterface.ShowNewOperationDialog(parentForm : TForm; accounts : TOrderedCardinalList; defaultFee : Cardinal);
+begin
+  If accounts.Count = 0 then raise Exception.Create('No sender accounts provided');
+  CheckNodeIsReady;
+  With TFRMOperation.Create(parentForm) do
+  Try
+    SenderAccounts.CopyFrom(accounts);
+    DefaultFee := defaultFee;
+    WalletKeys := FWalletKeys;
+    ShowModal;
+  Finally
+    Free;
+  End;
+end;
+
+class procedure TUserInterface.ShowSyncronizationDialog(parentForm : TForm);
+begin
+  try
+    FUILock.Acquire;
+    if FSyncronizationDialog = nil then begin
+      FSyncronizationDialog:=TFRMSyncronizationDialog.Create(FWallet);
+      FSyncronizationDialog.Visible := false;
+      FSyncronizationDialog.Show;
+      FRMSyncronizationDialogIsFirstOpen:=false;
+    end;
+    FWallet.SetSubFormCoordinate(FSyncronizationDialog);
+    FSyncronizationDialog.Visible := true;;
+  finally
+    FUILock.Release;
+  end;
+
+  // SyncDialog is always created, sometimes invisible
+  //
+  //SetSubFormCoordinate(FRMSyncronizationDialog);
+  //FRMSyncronizationDialog.Visible:=not FRMSyncronizationDialog.Visible;
+  FSyncronizationDialog.Visible := true;
+end;
+
+class procedure TUserInterface.ShowWalletKeysDialog(parentForm : TForm);
+var FRM : TFRMWalletKeys;
+begin
+  FRM := TFRMWalletKeys.Create(parentForm);
+  Try
+    FRM.WalletKeys := FWalletKeys;
+    FRM.ShowModal;
+  Finally
+    FRM.Free;
+  End;
+end;
+
+class procedure TUserInterface.ShowNodeIPDialog(parentForm : TForm);
+Var FRM : TFRMNodesIp;
+begin
+  FRM := TFRMNodesIp.Create(parentForm);
+  Try
+    FRM.AppParams := TUserInterface.AppParams;
+    FRM.ShowModal;
+  Finally
+    FRM.Free;
+  End;
+end;
+
+class procedure TUserInterface.ShowAccountInformationDialog(parentForm : TForm; account : UInt64);
+begin
+  raise Exception.Create('Not Implemented');
+  //HS ACCOUNT INFORMATION HERRE
+  //class procedure TFRMAccountExplorer.MiAccountInformationClick(Sender: TObject);
+  //Var F : TFRMMemoText;
+  //  accn : Int64 =-1;
+  //  title : String;
+  //  //account : TAccount;
+  //  strings : TStrings;
+  //  i : Integer;
+  //  opr : TOperationResume;
+  //begin
+  ////  with FRMWallet do begin
+  //  accn := -1;
+  //  title := '';
+  //  strings := TStringList.Create;
+  //  try
+  //    opr := CT_TOperationResume_NUL;
+  //    //AntonB if PageControl.ActivePage=tsOperations then begin
+  //      if not (FOperationsExplorerGrid.DrawGrid = nil) then begin
+  //      i := FOperationsExplorerGrid.DrawGrid.Row;
+  //      if (i>0) and (i<=FOperationsExplorerGrid.OperationsResume.Count) then begin
+  //        opr := FOperationsExplorerGrid.OperationsResume.OperationResume[i-1];
+  //      end;
+  //    //AntonB end else if PageControl.ActivePage=tsPendingOperations then begin
+  //      end else if not (FPendingOperationsGrid.DrawGrid = nil) then begin
+  //      i := FPendingOperationsGrid.DrawGrid.Row;
+  //      if (i>0) and (i<=FPendingOperationsGrid.OperationsResume.Count) then begin
+  //        opr := FPendingOperationsGrid.OperationsResume.OperationResume[i-1];
+  //      end;
+  //    end else //if PageControl.ActivePage=tsMyAccounts then
+  //    begin
+  //      accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  //      if accn<0 then raise Exception.Create('Select an account');
+  //      FillAccountInformation(strings,accn);
+  //      title := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(accn)+' info';
+  //      i := FOperationsAccountGrid.DrawGrid.Row;
+  //      if (i>0) and (i<=FOperationsAccountGrid.OperationsResume.Count) then begin
+  //        opr := FOperationsAccountGrid.OperationsResume.OperationResume[i-1];
+  //      end;
+  //    end;
+  //    If (opr.valid) then begin
+  //      if accn>=0 then strings.Add('')
+  //      else title := 'Operation info';
+  //      strings.Add('Operation info:');
+  //      FillOperationInformation(strings,opr);
+  //    end else if accn<0 then Raise Exception.Create('No info available');
+  //    F := TFRMMemoText.Create(Self);
+  //    Try
+  //      F.Caption := title;
+  //      F.Memo.Lines.Assign(strings);
+  //      F.ShowModal;
+  //    Finally
+  //      F.Free;
+  //    End;
+  //  finally
+  //    strings.free;
+  //  end;
+  //  end;
+  //end;
+
+end;
+
+class procedure TUserInterface.ShowPrivateKeysDialog(parentForm: TForm);
+Var FRM : TFRMWalletKeys;
+begin
+  FRM := TFRMWalletKeys.Create(parentForm);
+  Try
+    FRM.WalletKeys := FWalletKeys;
+    FRM.ShowModal;
+    //UpdatePrivateKeys; // Should receive event from FWalletKeys.OnChanged subscription
+  Finally
+    FRM.Free;
+  End;
+end;
+
+class procedure TUserInterface.ChangeWalletPassword(parentForm: TForm; walletKeys : TWalletKeys);
+Var s,s2 : String;
+begin
+  if walletKeys = nil then walletKeys := FWalletKeys;
+  s := ''; s2 := '';
+  if Not InputQuery('Change password','Enter new password',s) then exit;
+  if trim(s)<>s then raise Exception.Create('Password cannot start or end with a space character');
+  if Not InputQuery('Change password','Enter new password again',s2) then exit;
+  if s<>s2 then raise Exception.Create('Two passwords are different!');
+
+  walletKeys.WalletPassword := s;
+  Application.MessageBox(PChar('Password changed!'+#10+#10+
+    'Please note that your new password is "'+s+'"'+#10+#10+
+    '(If you lose this password, you will lose your wallet forever!)'),
+    PChar(Application.Title),MB_ICONWARNING+MB_OK);
+  //UpdateWalletKeys;
+end;
+
+class procedure TUserInterface.UnlockWallet(parentForm: TForm; walletKeys : TWalletKeys);
+Var s : String;
+begin
+  if walletKeys = nil then walletKeys := FWalletKeys;
+  s := '';
+  Repeat
+    if Not InputQuery('Wallet password','Enter wallet password',s) then exit;
+    walletKeys.WalletPassword := s;
+    if Not walletKeys.IsValidPassword then Application.MessageBox(PChar('Invalid password'),PChar(Application.Title),MB_ICONERROR+MB_OK);
+  Until walletKeys.IsValidPassword;
+  //UpdateWalletKeys;
+end;
+
+{%endregion}
+
+{%region Show Forms}
+
+class procedure TUserInterface.ShowAccountExplorer;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FAccountExplorer) then begin
+       FAccountExplorer := TFRMAccountExplorer.Create(FWallet);
+       FWallet.SetSubFormCoordinate(FAccountExplorer);
+    end;
+    FAccountExplorer.Refresh;
+    FAccountExplorer.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowBlockExplorer;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FBlockExplorerForm) then begin
+       FBlockExplorerForm := TFRMBlockExplorer.Create(FWallet);
+       FWallet.SetSubFormCoordinate(FBlockExplorerForm);
+    end;
+    FBlockExplorerForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowOperationsExplorer;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FOperationsExplorerForm) then begin
+      FOperationsExplorerForm := TFRMOperation.Create(FWallet);
+      FWallet.SetSubFormCoordinate(FOperationsExplorerForm);
+    end;
+    FOperationsExplorerForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowPendingOperations;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FPendingOperationForm) then begin
+      FPendingOperationForm := TFRMPendingOperations.Create(FWallet);
+      FWallet.SetSubFormCoordinate(FPendingOperationForm);
+    end;
+    FPendingOperationForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowMessagesForm;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FMessagesForm) then begin
+       FMessagesForm := TFRMMessages.Create(FWallet);
+       FWallet.SetSubFormCoordinate(FMessagesForm);
+    end;
+    FMessagesForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowNodesForm;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FNodesForm) then begin
+       FNodesForm := TFRMNodes.Create(FWallet);
+       FWallet.SetSubFormCoordinate(FNodesForm);
+    end;
+    FNodesForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.ShowLogsForm;
+begin
+  try
+    FUILock.Acquire;
+    if not Assigned(FLogsForm) then begin
+       FLogsForm := TFRMLogs.Create(FWallet);
+       FWallet.SetSubFormCoordinate(FLogsForm);
+    end;
+    FLogsForm.Show;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+{%endregion}
+
+{%region Public methods}
+
+class procedure TUserInterface.CheckNodeIsReady;
+Var errorMessage : AnsiString;
+begin
+  //HS if Not Assigned(FNode) then Abort;
+
+  if Not TNode.Node.IsReady(errorMessage) then begin
+    Raise Exception.Create('You cannot do this operation now:'+#10+#10+errorMessage);
+  end;
+end;
+
+class function TUserInterface.GetAccountKeyForMiner: TAccountKey;
+Var PK : TECPrivateKey;
+  i : Integer;
+  PublicK : TECDSA_Public;
+begin
+  Result := CT_TECDSA_Public_Nul;
+  if Not Assigned(FWalletKeys) then exit;
+  if Not Assigned(FAppParams) then exit;
+  case FMinerPrivateKeyType of
+    mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
+    mpk_Selected: begin
+      PublicK := TAccountComp.RawString2Accountkey(FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsString(''));
+    end;
+  else
+    // Random
+    PublicK := CT_TECDSA_Public_Nul;
+    if FWalletKeys.Count>0 then PublicK := FWalletKeys.Key[Random(FWalletKeys.Count)].AccountKey;
+  end;
+  i := FWalletKeys.IndexOfAccountKey(PublicK);
+  if i>=0 then begin
+    if (FWalletKeys.Key[i].CryptedKey='') then i:=-1;
+  end;
+  if i<0 then begin
+    PK := TECPrivateKey.Create;
+    try
+      PK.GenerateRandomPrivateKey(CT_Default_EC_OpenSSL_NID);
+      FWalletKeys.AddPrivateKey('New for miner '+DateTimeToStr(Now), PK);
+      PublicK := PK.PublicKey;
+    finally
+      PK.Free;
+    end;
+  end;
+  Result := PublicK;
+end;
+
+class procedure TUserInterface.RefreshConnectionStatusDisplay;
+var errors : AnsiString;
+begin
+  FUILock.Acquire;
+  Try
+    FSyncronizationDialog.UpdateNodeStatus;
+    OnNetStatisticsChanged(FWallet);
+    if Assigned(FNode) then begin
+      if FNode.IsBlockChainValid(errors) then begin
+        StatusBar2Text := Format('Last account time:%s',
+         [FormatDateTime('dd/mm/yyyy hh:nn:ss',UnivDateTime2LocalDateTime(UnixToUnivDateTime( FNode.Bank.LastOperationBlock.timestamp )))]);
+      end else begin
+        StatusBar2Text := 'NO BLOCKCHAIN: '+errors;
+      end;
+    end else begin
+      StatusBar2Text := '';
+    end;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+{%endregion}
+
+{%region Auxillary methods}
+
+//HS
+class procedure TUserInterface.SaveAppParams;
+Var ms : TMemoryStream;
+  s : AnsiString;
+begin
+  // Disabled in V2 (Herman)
+  FAppParams.ParamByName[CT_PARAM_GridAccountsStream].SetAsString('');
+  //ms := TMemoryStream.Create;
+  //Try
+  //  AccountExplorer.AccountsGrid.SaveToStream(ms);
+  //  ms.Position := 0;
+  //  setlength(s,ms.Size);
+  //  ms.ReadBuffer(s[1],ms.Size);
+  //  FAppParams.ParamByName[CT_PARAM_GridAccountsStream].SetAsString(s);
+  //Finally
+  //  ms.Free;
+  //End;
+end;
+
+class procedure TUserInterface.LoadAppParams;
+Var //ms : TMemoryStream;
+  s : AnsiString;
+  fvi : TFileVersionInfo;
+begin
+  // Disabled in V2 (HS)
+  //ms := TMemoryStream.Create;
+  //Try
+  //  s := FAppParams.ParamByName[CT_PARAM_GridAccountsStream].GetAsString('');
+  //  ms.WriteBuffer(s[1],length(s));
+  //  ms.Position := 0;
+  //  // Disabled on V2: FAccountsGrid.LoadFromStream(ms);
+  //Finally
+  //  ms.Free;
+  //End;
+  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
+    // New configuration... assigning a new random value
+    fvi := TFolderHelper.GetTFileVersionInfo(Application.ExeName);
+    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+
+      fvi.InternalName+' Build:'+fvi.FileVersion);
+  end;
+  NotifyConfigChanged;
+end;
+
+class procedure TUserInterface.NotifyConfigChanged;
+Var wa : Boolean;
+  i : Integer;
+begin
+// AntonB logs form not visible now at start and OnNewLog := Nil
+//  tsLogs.TabVisible := FAppParams.ParamByName[CT_PARAM_ShowLogs].GetAsBoolean(false);
+//  if (Not tsLogs.TabVisible) then begin
+//    FLog.OnNewLog := Nil;
+//    if PageControl.ActivePage = tsLogs then PageControl.ActivePage := tsMyAccounts;
+//  end else FLog.OnNewLog := OnNewLog;
+  if FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false) then begin
+    if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
+    else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
+    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
+  end else begin
+    FLog.SaveTypes := [];
+    FLog.FileName := '';
+  end;
+  if Assigned(FNode) then begin
+    wa := FNode.NetServer.Active;
+    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    FNode.NetServer.Active := wa;
+    FNode.Operations.BlockPayload := FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('');
+    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
+  end;
+  if Assigned(FPoolMiningServer) then begin
+    if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin
+      FPoolMiningServer.Active := false;
+      FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+    end;
+    FPoolMiningServer.Active :=FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
+    FPoolMiningServer.UpdateAccountAndPayload(GetAccountKeyForMiner,FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString(''));
+  end;
+  if Assigned(FRPCServer) then begin
+    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
+    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+  end;
+  i := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random));
+  if (i>=Integer(Low(TMinerPrivatekey))) And (i<=Integer(High(TMinerPrivatekey))) then FMinerPrivateKeyType := TMinerPrivateKey(i)
+  else FMinerPrivateKeyType := mpk_Random;
+end;
+
+class procedure TUserInterface.SetStatusBar0Text(text : AnsiString);
+begin
+  FStatusBar0Text := text;
+  if Assigned(FWallet) then
+    FWallet.sbFooterBar.Panels[0].Text := FStatusBar0Text;
+end;
+
+class procedure TUserInterface.SetStatusBar1Text(text : AnsiString);
+begin
+  FStatusBar1Text := text;
+  if Assigned(FWallet) then
+    FWallet.sbFooterBar.Panels[1].Text := text;
+end;
+
+class procedure TUserInterface.SetStatusBar2Text(text : AnsiString);
+begin
+  FStatusBar2Text := text;
+  if Assigned(FWallet) then
+    FWallet.sbFooterBar.Panels[2].Text := text;
+end;
+
+class procedure TUserInterface.SetMessagesNotificationText(text : AnsiString); static;
+begin
+  FMessagesNotificationText := text;
+  if Assigned(FSyncronizationDialog) then begin
+    if (text = '') then
+      FSyncronizationDialog.lblReceivedMessages.Visible := false;
+    FSyncronizationDialog.lblReceivedMessages.Caption := text;
+  end;
+end;
+
+{%endregion}
+
+{%region Handlers -- TODO: many need to be refactored out with TNotifyManyEvent}
+
+class procedure TUserInterface.OnAccountsChanged(Sender: TObject);
+begin
+  FUILock.Acquire;
+  Try
+    if Assigned(FAccountExplorer) then
+      FAccountExplorer.UpdateAccounts(true);
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnNewAccount(Sender: TObject);
+begin
+  FUILock.Acquire;
+  try
+    try
+      if Assigned(FAccountExplorer) then
+        FAccountExplorer.UpdateAccounts(false);
+      FSyncronizationDialog.UpdateBlockChainState;
+    except
+      On E:Exception do begin
+        E.Message := 'Error at OnNewAccount '+E.Message;
+        Raise;
+      end;
+    end;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+begin
+  FUILock.Acquire;
+  Try
+    if Assigned(FMessagesForm) then
+      FMessagesForm.OnNodeMessageEvent(NetConnection, MessageData);
+  finally
+      FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnReceivedHelloMessage(Sender: TObject);
+Var nsarr : TNodeServerAddressArray;
+  i : Integer;
+  s : AnsiString;
+begin
+  // No lock required
+  //CheckMining;
+  // Update node servers Peer Cache
+  nsarr := TNetData.NetData.GetValidNodeServers(true,0);
+  s := '';
+  for i := low(nsarr) to High(nsarr) do begin
+    if (s<>'') then s := s+';';
+    s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
+  end;
+  FAppParams.ParamByName[CT_PARAM_PeerCache].SetAsString(s);
+  TNode.Node.PeerCache := s;
+end;
+
+class procedure TUserInterface.OnNetStatisticsChanged(Sender: TObject);
+Var NS : TNetStatistics;
+begin
+  FUILock.Acquire;   // TODO - lock may not be required
+  Try
+    //CheckMining;
+    if Assigned(FNode) then begin
+      If FNode.NetServer.Active then begin
+        StatusBar0Text := 'Active (Port '+Inttostr(FNode.NetServer.Port)+')';
+      end else StatusBar0Text := 'Server stopped';
+      NS := TNetData.NetData.NetStatistics;
+      StatusBar1Text := Format('Connections:%d Clients:%d Servers:%d - Rcvd:%d Kb Send:%d Kb',
+        [NS.ActiveConnections,NS.ClientsConnections,NS.ServersConnections,NS.BytesReceived DIV 1024,NS.BytesSend DIV 1024]);
+    end else begin
+      StatusBar0Text := '';
+      StatusBar1Text := '';
+    end;
+  finally
+      FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnNetConnectionsUpdated(Sender: TObject);
+begin
+  try
+    FUILock.Acquire;
+    if Assigned(FNodesForm) then
+      FNodesForm.OnNetConnectionsUpdated;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnNetNodeServersUpdated(Sender: TObject);
+begin
+  try
+    FUILock.Acquire;
+    if Assigned(FNodesForm) then
+      FNodesForm.OnNetNodeServersUpdated;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnNetBlackListUpdated(Sender: TObject);
+begin
+  try
+    FUILock.Acquire;
+    if Assigned(FNodesForm) then
+      FNodesForm.OnNetBlackListUpdated;
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnMiningServerNewBlockFound(Sender: TObject);
+begin
+  // No lock required
+  FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
+end;
+
+class procedure TUserInterface.TimerUpdateStatusTimer(Sender: TObject);
+begin
+  Try
+    RefreshConnectionStatusDisplay;
+    FSyncronizationDialog.UpdateBlockChainState;
+    // UpdateNodeStatus; AntonB in UpdateBlockChainState call UpdateNodeStatus
+  Except
+    On E:Exception do begin
+      E.Message := 'Exception at TimerUpdate '+E.ClassName+': '+E.Message;
+      TLog.NewLog(lterror,ClassName,E.Message);
+    end;
+  End;
+end;
+
+class procedure TUserInterface.TrayIconDblClick(Sender: TObject);
+begin
+  FTrayIcon.Visible := False;
+  TimerUpdateStatus.Enabled := true;
+  FWallet.Show();
+  FWallet.WindowState := wsNormal;
+  Application.BringToFront();
+end;
+
+{%endregion}
+
+
+{ TUserInterfaceStartupThread }
+
+procedure TLoadSafeBoxThread.BCExecute;
+begin
+  // Read Operations saved from disk
+  TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+  TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+  TNode.Node.NetServer.Active := true;
+  Synchronize( TUserInterface.FinishedLoadingApp );
+end;
+
+finalization
+  TUserInterface.FWallet.Destroy;
+
+end.
+

+ 31 - 22
Units/PascalCoin/UAccounts.pas

@@ -142,6 +142,7 @@ Type
     accumulatedWork : UInt64; // Accumulated work (previous + target) this value can be calculated.
     accumulatedWork : UInt64; // Accumulated work (previous + target) this value can be calculated.
   end;
   end;
 
 
+  TCardinalsArray = Array of Cardinal;
 
 
   { Estimated TAccount size:
   { Estimated TAccount size:
     4 + 200 (max aprox) + 8 + 4 + 4 = 220 max aprox
     4 + 200 (max aprox) + 8 + 4 + 4 = 220 max aprox
@@ -169,6 +170,7 @@ Type
     Procedure Enable;
     Procedure Enable;
     Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
     Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
     Procedure CopyFrom(Sender : TOrderedCardinalList);
     Procedure CopyFrom(Sender : TOrderedCardinalList);
+    Function ToArray : TCardinalsArray;
   End;
   End;
 
 
   TPCSafeBox = Class;
   TPCSafeBox = Class;
@@ -229,8 +231,6 @@ Type
   // happens only when a new BlockChain is included. After this, a new "SafeBoxHash"
   // happens only when a new BlockChain is included. After this, a new "SafeBoxHash"
   // is created, so each SafeBox has a unique SafeBoxHash
   // is created, so each SafeBox has a unique SafeBoxHash
 
 
-  TCardinalsArray = Array of Cardinal;
-
   { TPCSafeBox }
   { TPCSafeBox }
 
 
   TPCSafeBox = Class
   TPCSafeBox = Class
@@ -345,12 +345,12 @@ Type
 
 
 Const
 Const
   CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:'';y:'');reward:0;fee:0;protocol_version:0;
   CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:'';y:'');reward:0;fee:0;protocol_version:0;
-    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:'';operations_hash:'';proof_of_work:'');
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:'';initial_safe_box_hash:'';operations_hash:'';proof_of_work:'');
   CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));
   CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));
   CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0);
   CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0);
   CT_BlockAccount_NUL : TBlockAccount = (
   CT_BlockAccount_NUL : TBlockAccount = (
     blockchainInfo:(block:0;account_key:(EC_OpenSSL_NID:0;x:'';y:'');reward:0;fee:0;protocol_version:0;
     blockchainInfo:(block:0;account_key:(EC_OpenSSL_NID:0;x:'';y:'');reward:0;fee:0;protocol_version:0;
-    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:'';operations_hash:'';proof_of_work:'');
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:'';initial_safe_box_hash:'';operations_hash:'';proof_of_work:'');
     accounts:(
     accounts:(
     (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0),
     (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0),
     (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0),
     (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:'';y:'');locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:'';y:''));balance:0;updated_block:0;n_operation:0;name:'';account_type:0;previous_updated_block:0),
@@ -1123,11 +1123,11 @@ begin
     exit;
     exit;
   end;
   end;
   try
   try
-    If pos(DecimalSeparator,moneytxt)<=0 then begin
+    If pos(DefaultFormatSettings.DecimalSeparator,moneytxt)<=0 then begin
       // No decimal separator, consider ThousandSeparator as a decimal separator
       // No decimal separator, consider ThousandSeparator as a decimal separator
-      s := StringReplace(moneytxt,ThousandSeparator,DecimalSeparator,[rfReplaceAll]);
+      s := StringReplace(moneytxt,DefaultFormatSettings.ThousandSeparator,DefaultFormatSettings.DecimalSeparator,[rfReplaceAll]);
     end else begin
     end else begin
-      s := StringReplace(moneytxt,ThousandSeparator,'',[rfReplaceAll]);
+      s := StringReplace(moneytxt,DefaultFormatSettings.ThousandSeparator,'',[rfReplaceAll]);
     end;
     end;
     money := Round( StrToFloat(s)*10000 );
     money := Round( StrToFloat(s)*10000 );
     Result := true;
     Result := true;
@@ -1305,7 +1305,7 @@ function TPCSafeBox.Account(account_number: Cardinal): TAccount;
 var b : Cardinal;
 var b : Cardinal;
 begin
 begin
   b := account_number DIV CT_AccountsPerBlock;
   b := account_number DIV CT_AccountsPerBlock;
-  if (b<0) Or (b>=FBlockAccountsList.Count) then raise Exception.Create('Invalid account: '+IntToStr(account_number));
+  if (b>=FBlockAccountsList.Count) then raise Exception.Create('Invalid account: '+IntToStr(account_number));
   ToTAccount(PBlockAccount(FBlockAccountsList.Items[b])^.accounts[account_number MOD CT_AccountsPerBlock],account_number,Result);
   ToTAccount(PBlockAccount(FBlockAccountsList.Items[b])^.accounts[account_number MOD CT_AccountsPerBlock],account_number,Result);
 end;
 end;
 
 
@@ -1375,7 +1375,7 @@ end;
 
 
 function TPCSafeBox.Block(block_number: Cardinal): TBlockAccount;
 function TPCSafeBox.Block(block_number: Cardinal): TBlockAccount;
 begin
 begin
-  if (block_number<0) Or (block_number>=FBlockAccountsList.Count) then raise Exception.Create('Invalid block number: '+inttostr(block_number));
+  if (block_number>=FBlockAccountsList.Count) then raise Exception.Create('Invalid block number: '+inttostr(block_number));
   ToTBlockAccount(PBlockAccount(FBlockAccountsList.Items[block_number])^,block_number,Result);
   ToTBlockAccount(PBlockAccount(FBlockAccountsList.Items[block_number])^,block_number,Result);
 end;
 end;
 
 
@@ -1451,7 +1451,7 @@ begin
         Result := 1;
         Result := 1;
         exit;
         exit;
       end;
       end;
-      if (block_number<0) Or (block_number>=FBlockAccountsList.Count) then raise Exception.Create('Invalid block number: '+inttostr(block_number));
+      if (block_number>=FBlockAccountsList.Count) then raise Exception.Create('Invalid block number: '+inttostr(block_number));
       if (Previous_blocks_average<=0) then raise Exception.Create('Dev error 20161016-1');
       if (Previous_blocks_average<=0) then raise Exception.Create('Dev error 20161016-1');
       if (Previous_blocks_average>block_number) then Previous_blocks_average := block_number;
       if (Previous_blocks_average>block_number) then Previous_blocks_average := block_number;
       //
       //
@@ -1501,13 +1501,13 @@ procedure TPCSafeBox.CheckMemory;
     in order to free memory not used. Tested with FPC 3.0 }
     in order to free memory not used. Tested with FPC 3.0 }
 {$IFDEF FPC}
 {$IFDEF FPC}
 Var sb : TPCSafeBox;
 Var sb : TPCSafeBox;
-  tc : Cardinal;
+  tc : QWord;
 {$ENDIF}
 {$ENDIF}
 begin
 begin
   {$IFDEF FPC}
   {$IFDEF FPC}
   StartThreadSafe;
   StartThreadSafe;
   try
   try
-    tc := GetTickCount;
+    tc := GetTickCount64;
     sb := TPCSafeBox.Create;
     sb := TPCSafeBox.Create;
     try
     try
       sb.CopyFrom(Self);
       sb.CopyFrom(Self);
@@ -1516,7 +1516,7 @@ begin
     finally
     finally
       sb.Free;
       sb.Free;
     end;
     end;
-    tc := GetTickCount - tc;
+    tc := GetTickCount64 - tc;
     TLog.NewLog(ltDebug,Classname,'Checked memory '+IntToStr(tc)+' miliseonds');
     TLog.NewLog(ltDebug,Classname,'Checked memory '+IntToStr(tc)+' miliseonds');
   finally
   finally
     EndThreadSave;
     EndThreadSave;
@@ -2442,9 +2442,9 @@ begin
     errors := 'Invalid integrity in accounts transaction';
     errors := 'Invalid integrity in accounts transaction';
     exit;
     exit;
   end;
   end;
-  if (buyer<0) Or (buyer>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
-     (account_to_buy<0) Or (account_to_buy>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
-     (seller<0) Or (seller>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) then begin
+  if (buyer>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
+     (account_to_buy>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
+     (seller>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) then begin
      errors := 'Invalid account number on buy';
      errors := 'Invalid account number on buy';
      exit;
      exit;
   end;
   end;
@@ -2670,8 +2670,8 @@ begin
     errors := 'Invalid integrity in accounts transaction';
     errors := 'Invalid integrity in accounts transaction';
     exit;
     exit;
   end;
   end;
-  if (sender<0) Or (sender>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
-     (target<0) Or (target>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) then begin
+  if (sender>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
+     (target>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) then begin
      errors := 'Invalid sender or target on transfer';
      errors := 'Invalid sender or target on transfer';
      exit;
      exit;
   end;
   end;
@@ -2732,8 +2732,8 @@ Var i : Integer;
 begin
 begin
   Result := false;
   Result := false;
   errors := '';
   errors := '';
-  if (signer_account<0) Or (signer_account>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
-     (target_account<0) Or (target_account>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Then begin
+  if (signer_account>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
+     (target_account>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Then begin
      errors := 'Invalid account';
      errors := 'Invalid account';
      exit;
      exit;
   end;
   end;
@@ -2898,7 +2898,7 @@ Type
 
 
 function SortOrdered(Item1, Item2: Pointer): Integer;
 function SortOrdered(Item1, Item2: Pointer): Integer;
 begin
 begin
-   Result := PtrInt(Item1) - PtrInt(Item2);
+   Result := PtrUInt(Item1) - PtrUInt(Item2);
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
@@ -3144,7 +3144,7 @@ begin
   while L <= H do
   while L <= H do
   begin
   begin
     I := (L + H) shr 1;
     I := (L + H) shr 1;
-    C := Int64(FOrderedList[I]) - Int64(Value);
+    C := PtrUInt(FOrderedList[I]) - (Value);
     if C < 0 then L := I + 1 else
     if C < 0 then L := I + 1 else
     begin
     begin
       H := I - 1;
       H := I - 1;
@@ -3182,6 +3182,15 @@ begin
   end;
   end;
 end;
 end;
 
 
+Function TOrderedCardinalList.ToArray : TCardinalsArray;
+var i : integer;
+begin
+  SetLength(Result, self.Count);
+  for i := 0 to self.Count - 1 do
+    Result[i] := Self.Get(i);
+end;
+
+
 { TOrderedRawList }
 { TOrderedRawList }
 
 
 Type TRawListData = Record
 Type TRawListData = Record

+ 3 - 2
Units/PascalCoin/UBaseTypes.pas

@@ -41,7 +41,7 @@ Type
     class function T32BytesToRawBytes(const source : T32Bytes) : TDynRawBytes; overload;
     class function T32BytesToRawBytes(const source : T32Bytes) : TDynRawBytes; overload;
     class function TRawBytesTo32Left0Padded(const source : TDynRawBytes) : T32Bytes;
     class function TRawBytesTo32Left0Padded(const source : TDynRawBytes) : T32Bytes;
     class function Copy(const source : T32bytes; start, length : Integer) : ShortString; overload;
     class function Copy(const source : T32bytes; start, length : Integer) : ShortString; overload;
-    class function Copy(const source : T256RawBytes; var dest : T256RawBytes) : ShortString; overload;
+    class function Copy(const source: T256RawBytes; var dest: T256RawBytes):T256RawBytes;
     class function To256RawBytes(const source : TRawBytes) : T256RawBytes; overload;
     class function To256RawBytes(const source : TRawBytes) : T256RawBytes; overload;
     class procedure To256RawBytes(const source : TRawBytes; var dest : T256RawBytes); overload;
     class procedure To256RawBytes(const source : TRawBytes; var dest : T256RawBytes); overload;
     class function ToRawBytes(const source : T256RawBytes) : TRawBytes; overload;
     class function ToRawBytes(const source : T256RawBytes) : TRawBytes; overload;
@@ -100,13 +100,14 @@ begin
   move(source[start],Result[1],length);
   move(source[start],Result[1],length);
 end;
 end;
 
 
-class function TBaseType.Copy(const source: T256RawBytes; var dest: T256RawBytes): ShortString;
+class function TBaseType.Copy(const source: T256RawBytes; var dest: T256RawBytes):T256RawBytes;
 var i : Integer;
 var i : Integer;
 begin
 begin
   SetLength(dest,length(source));
   SetLength(dest,length(source));
   for i:=0 to high(dest) do begin
   for i:=0 to high(dest) do begin
     dest[i] := source[i];
     dest[i] := source[i];
   end;
   end;
+  Exit(dest);
 end;
 end;
 
 
 class function TBaseType.To256RawBytes(const source: TRawBytes): T256RawBytes;
 class function TBaseType.To256RawBytes(const source: TRawBytes): T256RawBytes;

+ 1 - 1
Units/PascalCoin/UBlockChain.pas

@@ -712,7 +712,7 @@ begin
         FLastOperationBlock.initial_safe_box_hash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash); // Genesis hash
         FLastOperationBlock.initial_safe_box_hash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash); // Genesis hash
       end;
       end;
     finally
     finally
-      FBankLock.Release;
+      FBankLock.Release; // AntonB If fast close Wallet after open this Error string
     end;
     end;
     for i := 0 to FNotifyList.Count - 1 do begin
     for i := 0 to FNotifyList.Count - 1 do begin
       TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
       TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;

+ 1 - 1
Units/PascalCoin/UConst.pas

@@ -142,7 +142,7 @@ Const
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
 
 
   CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}2{$ELSE}{$ENDIF}{$ENDIF};
   CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}2{$ELSE}{$ENDIF}{$ENDIF};
-  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}5000{$ELSE}{$IFDEF TESTNET}50{$ELSE}{$ENDIF}{$ENDIF};
+  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}5000{$ELSE}{$IFDEF TESTNET}50000{$ELSE}{$ENDIF}{$ENDIF};
 
 
   // App Params
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';

+ 12 - 2
Units/PascalCoin/UNetProtocol.pas

@@ -19,14 +19,14 @@ unit UNetProtocol;
 
 
 interface
 interface
 
 
-Uses
+uses
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   Windows,
   Windows,
 {$ELSE}
 {$ELSE}
   {LCLIntf, LCLType, LMessages,}
   {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
 {$ENDIF}
   UBlockChain, Classes, SysUtils, UAccounts, UThread,
   UBlockChain, Classes, SysUtils, UAccounts, UThread,
-  UCrypto, UTCPIP, SyncObjs;
+  UCrypto, UTCPIP, SyncObjs, UCommon;
 
 
 {$I config.inc}
 {$I config.inc}
 
 
@@ -194,6 +194,7 @@ Type
     FRegisteredRequests : TPCThreadList;
     FRegisteredRequests : TPCThreadList;
     FIsDiscoveringServers : Boolean;
     FIsDiscoveringServers : Boolean;
     FIsGettingNewBlockChainFromClient : Boolean;
     FIsGettingNewBlockChainFromClient : Boolean;
+    FOnConnectivityChanged : TNotifyManyEvent;
     FOnNetConnectionsUpdated: TNotifyEvent;
     FOnNetConnectionsUpdated: TNotifyEvent;
     FOnNodeServersUpdated: TNotifyEvent;
     FOnNodeServersUpdated: TNotifyEvent;
     FOnBlackListUpdated: TNotifyEvent;
     FOnBlackListUpdated: TNotifyEvent;
@@ -256,11 +257,13 @@ Type
     Property MaxRemoteOperationBlock : TOperationBlock read FMaxRemoteOperationBlock;
     Property MaxRemoteOperationBlock : TOperationBlock read FMaxRemoteOperationBlock;
     Property NodePrivateKey : TECPrivateKey read FNodePrivateKey;
     Property NodePrivateKey : TECPrivateKey read FNodePrivateKey;
     Function GetValidNodeServers(OnlyWhereIConnected : Boolean; Max : Integer): TNodeServerAddressArray;
     Function GetValidNodeServers(OnlyWhereIConnected : Boolean; Max : Integer): TNodeServerAddressArray;
+    property OnConnectivityChanged : TNotifyManyEvent read FOnConnectivityChanged;
     Property OnNetConnectionsUpdated : TNotifyEvent read FOnNetConnectionsUpdated write FOnNetConnectionsUpdated;
     Property OnNetConnectionsUpdated : TNotifyEvent read FOnNetConnectionsUpdated write FOnNetConnectionsUpdated;
     Property OnNodeServersUpdated : TNotifyEvent read FOnNodeServersUpdated write FOnNodeServersUpdated;
     Property OnNodeServersUpdated : TNotifyEvent read FOnNodeServersUpdated write FOnNodeServersUpdated;
     Property OnBlackListUpdated : TNotifyEvent read FOnBlackListUpdated write FOnBlackListUpdated;
     Property OnBlackListUpdated : TNotifyEvent read FOnBlackListUpdated write FOnBlackListUpdated;
     Property OnReceivedHelloMessage : TNotifyEvent read FOnReceivedHelloMessage write FOnReceivedHelloMessage;
     Property OnReceivedHelloMessage : TNotifyEvent read FOnReceivedHelloMessage write FOnReceivedHelloMessage;
     Property OnStatisticsChanged : TNotifyEvent read FOnStatisticsChanged write FOnStatisticsChanged;
     Property OnStatisticsChanged : TNotifyEvent read FOnStatisticsChanged write FOnStatisticsChanged;
+    procedure NotifyConnectivityChanged;
     Procedure NotifyNetConnectionUpdated;
     Procedure NotifyNetConnectionUpdated;
     Procedure NotifyNodeServersUpdated;
     Procedure NotifyNodeServersUpdated;
     Procedure NotifyBlackListUpdated;
     Procedure NotifyBlackListUpdated;
@@ -639,6 +642,7 @@ begin
   SetLength(FFixedServers,0);
   SetLength(FFixedServers,0);
   FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
   FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
   FNetStatistics := CT_TNetStatistics_NUL;
   FNetStatistics := CT_TNetStatistics_NUL;
+  FOnConnectivityChanged := nil;
   FOnStatisticsChanged := Nil;
   FOnStatisticsChanged := Nil;
   FOnNetConnectionsUpdated := Nil;
   FOnNetConnectionsUpdated := Nil;
   FOnNodeServersUpdated := Nil;
   FOnNodeServersUpdated := Nil;
@@ -1566,6 +1570,11 @@ begin
   FNetDataNotifyEventsThread.FNotifyOnBlackListUpdated := true;
   FNetDataNotifyEventsThread.FNotifyOnBlackListUpdated := true;
 end;
 end;
 
 
+procedure TNetData.NotifyConnectivityChanged;
+begin
+  FOnConnectivityChanged.Invoke(Self);
+end;
+
 procedure TNetData.NotifyNetConnectionUpdated;
 procedure TNetData.NotifyNetConnectionUpdated;
 begin
 begin
   FNetDataNotifyEventsThread.FNotifyOnNetConnectionsUpdated := true;
   FNetDataNotifyEventsThread.FNotifyOnNetConnectionsUpdated := true;
@@ -1644,6 +1653,7 @@ end;
 procedure TNetData.SetNetConnectionsActive(const Value: Boolean);
 procedure TNetData.SetNetConnectionsActive(const Value: Boolean);
 begin
 begin
   FNetConnectionsActive := Value;
   FNetConnectionsActive := Value;
+  NotifyConnectivityChanged;
   if FNetConnectionsActive then DiscoverServers
   if FNetConnectionsActive then DiscoverServers
   else DisconnectClients;
   else DisconnectClients;
 end;
 end;

+ 1 - 1
Units/PascalCoin/UNode.pas

@@ -88,7 +88,7 @@ Type
     Procedure DisableNewBlocks;
     Procedure DisableNewBlocks;
     Procedure EnableNewBlocks;
     Procedure EnableNewBlocks;
     Property NodeLogFilename : AnsiString read GetNodeLogFilename write SetNodeLogFilename;
     Property NodeLogFilename : AnsiString read GetNodeLogFilename write SetNodeLogFilename;
-    Property OperationSequenceLock : TPCCriticalSection read FOperationSequenceLock;
+    Property OperationSequenceLock : TPCCriticalSection read FOperationSequenceLock;   // TODO - refactor out, put in URPC directly (since only client)
   End;
   End;
 
 
   TNodeNotifyEvents = Class;
   TNodeNotifyEvents = Class;

+ 5 - 5
Units/PascalCoin/UThread.pas

@@ -56,7 +56,7 @@ Type
   TPCThread = Class(TThread)
   TPCThread = Class(TThread)
   private
   private
     FDebugStep: String;
     FDebugStep: String;
-    FStartTickCount : Cardinal;
+    FStartTickCount : QWord;
   protected
   protected
     procedure DoTerminate; override;
     procedure DoTerminate; override;
     procedure Execute; override;
     procedure Execute; override;
@@ -120,7 +120,7 @@ procedure TPCThread.Execute;
 Var l : TList;
 Var l : TList;
   i : Integer;
   i : Integer;
 begin
 begin
-  FStartTickCount := GetTickCount;
+  FStartTickCount := GetTickCount64;
   FDebugStep := '';
   FDebugStep := '';
   i := _threads.Add(Self);
   i := _threads.Add(Self);
   try
   try
@@ -228,7 +228,7 @@ begin
     list.BeginUpdate;
     list.BeginUpdate;
     list.Clear;
     list.Clear;
     for i := 0 to l.Count - 1 do begin
     for i := 0 to l.Count - 1 do begin
-      list.Add(Format('%.2d/%.2d <%s> Time:%s sec - Step: %s',[i+1,l.Count,TPCThread(l[i]).ClassName,FormatFloat('0.000',(GetTickCount-TPCThread(l[i]).FStartTickCount) / 1000),TPCThread(l[i]).DebugStep] ));
+      list.Add(Format('%.2d/%.2d <%s> Time:%s sec - Step: %s',[i+1,l.Count,TPCThread(l[i]).ClassName,FormatFloat('0.000',(GetTickCount64-TPCThread(l[i]).FStartTickCount) / 1000),TPCThread(l[i]).DebugStep] ));
     end;
     end;
     list.EndUpdate;
     list.EndUpdate;
   finally
   finally
@@ -244,7 +244,7 @@ Var tc : Cardinal;
   s : String;
   s : String;
   {$ENDIF}
   {$ENDIF}
 begin
 begin
-  tc := GetTickCount;
+  tc := GetTickCount64;
   if MaxWaitMilliseconds>60000 then MaxWaitMilliseconds := 60000;
   if MaxWaitMilliseconds>60000 then MaxWaitMilliseconds := 60000;
   {$IFDEF HIGHLOG}
   {$IFDEF HIGHLOG}
   lockWatingForCounter := Lock.WaitingForCounter;
   lockWatingForCounter := Lock.WaitingForCounter;
@@ -254,7 +254,7 @@ begin
   Repeat
   Repeat
     Result := Lock.TryEnter;
     Result := Lock.TryEnter;
     if Not Result then sleep(1);
     if Not Result then sleep(1);
-  Until (Result) Or (GetTickCount > (tc + MaxWaitMilliseconds));
+  Until (Result) Or (GetTickCount64 > (tc + MaxWaitMilliseconds));
   {$IFDEF HIGHLOG}
   {$IFDEF HIGHLOG}
   if Not Result then begin
   if Not Result then begin
     tc2 := GetTickCount;
     tc2 := GetTickCount;

+ 8 - 1
Units/PascalCoin/UWalletKeys.pas

@@ -41,6 +41,7 @@ Type
     FWalletFileName: AnsiString;
     FWalletFileName: AnsiString;
     FIsReadingStream : Boolean;
     FIsReadingStream : Boolean;
     FOnChanged: TNotifyManyEvent;
     FOnChanged: TNotifyManyEvent;
+    function GetHasPassword : boolean;
     function GetKey(index: Integer): TWalletKey;
     function GetKey(index: Integer): TWalletKey;
     procedure SetWalletPassword(const Value: AnsiString);
     procedure SetWalletPassword(const Value: AnsiString);
     Procedure GeneratePrivateKeysFromPassword;
     Procedure GeneratePrivateKeysFromPassword;
@@ -53,6 +54,7 @@ Type
     Procedure LoadFromStream(Stream : TStream);
     Procedure LoadFromStream(Stream : TStream);
     Procedure SaveToStream(Stream : TStream);
     Procedure SaveToStream(Stream : TStream);
     Property IsValidPassword : Boolean read FIsValidPassword;
     Property IsValidPassword : Boolean read FIsValidPassword;
+    property HasPassword : boolean read GetHasPassword;
     Property WalletPassword : AnsiString read FWalletPassword write SetWalletPassword;
     Property WalletPassword : AnsiString read FWalletPassword write SetWalletPassword;
     Function AddPrivateKey(Const Name : AnsiString; ECPrivateKey : TECPrivateKey) : Integer; virtual;
     Function AddPrivateKey(Const Name : AnsiString; ECPrivateKey : TECPrivateKey) : Integer; virtual;
     Function AddPublicKey(Const Name : AnsiString; ECDSA_Public : TECDSA_Public) : Integer; virtual;
     Function AddPublicKey(Const Name : AnsiString; ECDSA_Public : TECDSA_Public) : Integer; virtual;
@@ -99,6 +101,11 @@ Const
 
 
 Type PWalletKey = ^TWalletKey;
 Type PWalletKey = ^TWalletKey;
 
 
+function TWalletKeys.GetHasPassword : boolean;
+begin
+  Result := NOT ((IsValidPassword = True) AND (WalletPassword = ''));
+end;
+
 function TWalletKeys.AddPrivateKey(Const Name : AnsiString; ECPrivateKey: TECPrivateKey): Integer;
 function TWalletKeys.AddPrivateKey(Const Name : AnsiString; ECPrivateKey: TECPrivateKey): Integer;
 Var P : PWalletKey;
 Var P : PWalletKey;
   s : AnsiString;
   s : AnsiString;
@@ -305,7 +312,7 @@ end;
 function TWalletKeys.LockWallet: Boolean;
 function TWalletKeys.LockWallet: Boolean;
 begin
 begin
   // Return true when wallet has a password, locking it. False if there password is empty string
   // Return true when wallet has a password, locking it. False if there password is empty string
-  FWalletPassword := '';
+  FWalletPassword := ''; //AntonB - this clear Password on lock - is block lock
   GeneratePrivateKeysFromPassword;
   GeneratePrivateKeysFromPassword;
   Result := Not IsValidPassword;
   Result := Not IsValidPassword;
   FOnChanged.Invoke(Self);
   FOnChanged.Invoke(Self);

+ 4 - 4
Units/PascalCoin/config.inc

@@ -4,7 +4,7 @@
   or visit http://www.opensource.org/licenses/mit-license.php.
   or visit http://www.opensource.org/licenses/mit-license.php.
 
 
   This unit is a part of Pascal Coin, a P2P crypto currency without need of
   This unit is a part of Pascal Coin, a P2P crypto currency without need of
-  historical operations.
+  historical miPendingOperations.
 
 
   If you like it, consider a donation using BitCoin:
   If you like it, consider a donation using BitCoin:
   16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
   16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
@@ -29,13 +29,13 @@
   {.$DEFINE Synapse_OpenSSLv10}
   {.$DEFINE Synapse_OpenSSLv10}
   {.$DEFINE Synapse_OpenSSLv11}
   {.$DEFINE Synapse_OpenSSLv11}
 
 
-  {$DEFINE PRODUCTION}
-  {.$DEFINE TESTNET}
+  {.$DEFINE PRODUCTION}
+  {$DEFINE TESTNET}
 
 
   // For GUI: Allows to show average time on blockchain explorer
   // For GUI: Allows to show average time on blockchain explorer
   {.$DEFINE SHOW_AVERAGE_TIME_STATS}
   {.$DEFINE SHOW_AVERAGE_TIME_STATS}
 
 
-  // For special use: Allows to store in a buffer, operations not proceded due to n_operation value invalid
+  // For special use: Allows to store in a buffer, miPendingOperations not proceded due to n_operation value invalid
   {.$DEFINE BufferOfFutureOperations}
   {.$DEFINE BufferOfFutureOperations}
   
   
   // HighLog will result in a higher log generation
   // HighLog will result in a higher log generation

+ 78 - 22
Units/Utils/UCommon.pas

@@ -16,6 +16,7 @@
 
 
 unit UCommon;
 unit UCommon;
 
 
+
 {$IFDEF FPC}
 {$IFDEF FPC}
   {$MODE Delphi}
   {$MODE Delphi}
 {$ENDIF}
 {$ENDIF}
@@ -23,10 +24,11 @@ unit UCommon;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Controls, FGL, Generics.Collections, Generics.Defaults;
+  Classes, SysUtils, Forms, Controls, FGL, Generics.Collections, Generics.Defaults;
 
 
 { GLOBAL FUNCTIONS }
 { GLOBAL FUNCTIONS }
 
 
+
 { Converts a string to hexidecimal format }
 { Converts a string to hexidecimal format }
 function String2Hex(const Buffer: AnsiString): AnsiString;
 function String2Hex(const Buffer: AnsiString): AnsiString;
 
 
@@ -43,6 +45,8 @@ function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string)
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
 
 
+function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
+
 { DateTime functions }
 { DateTime functions }
 function TimeStamp : AnsiString;
 function TimeStamp : AnsiString;
 function UtcTimeStamp : AnsiString;
 function UtcTimeStamp : AnsiString;
@@ -78,6 +82,11 @@ type
       class procedure Remove(var Values : TArray<T>; const Item : T; const Comparer : IEqualityComparer<T>); overload; static;
       class procedure Remove(var Values : TArray<T>; const Item : T; const Comparer : IEqualityComparer<T>); overload; static;
       class procedure Remove(var Values : TArray<T>; const Item : T); overload; static;
       class procedure Remove(var Values : TArray<T>; const Item : T); overload; static;
       class procedure RemoveAt(var Values : TArray<T>; ItemIndex : SizeInt); static;
       class procedure RemoveAt(var Values : TArray<T>; ItemIndex : SizeInt); static;
+      class procedure Append(var Arr: TArray<T>; Value: T);
+      class procedure Prepend(var Arr: TArray<T>; Value: T);
+      class procedure InsertAt(var Values : TArray<T>; ItemIndex : SizeInt; const Item : T);
+      class procedure Swap(var Values : array of T; Item1Index, Item2Index : SizeInt);
+      class procedure MoveItem(var Values : array of T; FromIndex, ToIndex : SizeInt);
       class function Concat(const Arrays: array of TArray<T>): TArray<T>; static;
       class function Concat(const Arrays: array of TArray<T>): TArray<T>; static;
       class function Create(const a : T; const b : T) : TArray<T>; static;
       class function Create(const a : T; const b : T) : TArray<T>; static;
       class function ToArray(Enumerable: TEnumerable<T>; Count: SizeInt): TArray<T>; static;
       class function ToArray(Enumerable: TEnumerable<T>; Count: SizeInt): TArray<T>; static;
@@ -93,10 +102,6 @@ type
     procedure Invoke(sender : TObject);
     procedure Invoke(sender : TObject);
   end;
   end;
 
 
-  { Controls Helpers }
-  TWinControlHelper = class helper for TWinControl
-    procedure RemoveAllControls(destroy : boolean);
-  end;
 
 
 implementation
 implementation
 
 
@@ -111,7 +116,6 @@ begin
     Result := LowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
     Result := LowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
 end;
 end;
 
 
-
 function BinStrComp(const Str1, Str2: AnsiString): integer;
 function BinStrComp(const Str1, Str2: AnsiString): integer;
 var Str1Len, Str2Len, i : Integer;
 var Str1Len, Str2Len, i : Integer;
 begin
 begin
@@ -202,6 +206,19 @@ begin
     Result := AFalseResult;
     Result := AFalseResult;
 end;
 end;
 
 
+{ Value Clipping }
+
+function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
+begin
+  if AValue < MinValue then
+    Result := MinValue
+  else if AValue > MaxValue then
+    Result := MaxValue
+  else
+    Result := AValue
+end;
+
+
 { DateTime functions }
 { DateTime functions }
 function TimeStamp : AnsiString;
 function TimeStamp : AnsiString;
 begin
 begin
@@ -308,6 +325,61 @@ begin
   SetLength(Values, Length(Values) - 1);
   SetLength(Values, Length(Values) - 1);
 end;
 end;
 
 
+class procedure TArrayTool<T>.Append(var Arr: TArray<T>; Value: T);
+begin
+  SetLength(Arr, Length(Arr)+1);
+  Arr[High(Arr)] := Value;
+end;
+
+class procedure TArrayTool<T>.Prepend(var Arr: TArray<T>; Value: T);
+var i : Integer;
+begin
+  SetLength(Arr, Length(Arr)+1);
+  for i := High(Arr)-1 downto Low(Arr) do
+    Arr[i+1] := Arr[i];
+  Arr[Low(Arr)] := Value;
+end;
+
+class procedure TArrayTool<T>.InsertAt(var Values : TArray<T>; ItemIndex : SizeInt; const Item : T);
+var i : Integer;
+begin
+  if (ItemIndex < Low(Values)) OR (ItemIndex > High(Values)) then Raise Exception.Create('Invalid Parameter: ItemIndex out of bounds');
+  SetLength(Values, Length(Values)+1);
+  for i := High(Values)-1 downto ItemIndex do
+    Values[i+1] := Values[i];
+  Values[ItemIndex] := Item;
+end;
+
+class procedure TArrayTool<T>.Swap(var Values : array of T; Item1Index, Item2Index : SizeInt);
+var temp : T; len, recSize : SizeInt; itemSize : SizeInt;
+begin
+  len := Length(Values);
+  recSize := SizeOf(T);
+  if (Item1Index < 0) OR (Item1Index > len) then Raise Exception.Create('Invalid Parameter: Item1Index out of bounds');
+  if (Item2Index < 0) OR (Item2Index > len) then Raise Exception.Create('Invalid Parameter: Item2Index out of bounds');
+  temp := Values[Item1Index];
+  Values[Item1Index] := Values[Item2Index];
+  Values[Item2Index] := temp;
+end;
+
+class procedure TArrayTool<T>.MoveItem(var Values : array of T; FromIndex, ToIndex : SizeInt);
+var i : Integer; item : T;
+begin
+  if (FromIndex < Low(Values)) OR (FromIndex > High(Values)) then Raise Exception.Create('Invalid Parameter: FromIndex out of bounds');
+  if (ToIndex < Low(Values)) OR (ToIndex > High(Values)) then Raise Exception.Create('Invalid Parameter: ToIndex out of bounds');
+
+  item := Values[FromIndex];
+  if FromIndex < ToIndex then begin
+    for i := FromIndex + 1 to ToIndex do
+      Values[i - 1] := Values[i];
+    Values[ToIndex] := item;
+  end else if FromIndex > ToIndex then begin
+    for i := FromIndex - 1 downto ToIndex do
+      Values[i + 1] := Values[i];
+    Values[ToIndex] := item;
+  end;
+end;
+
 class function TArrayTool<T>.Concat(const Arrays: array of TArray<T>): TArray<T>;
 class function TArrayTool<T>.Concat(const Arrays: array of TArray<T>): TArray<T>;
 var
 var
   i, k, LIndex, LLength: Integer;
   i, k, LIndex, LLength: Integer;
@@ -372,21 +444,5 @@ end;
 
 
 {%endregion}
 {%endregion}
 
 
-{%region TWinControlHelper }
-
-procedure TWinControlHelper.RemoveAllControls(destroy : boolean);
-var
-  control : TControl;
-begin
-  while self.ControlCount > 0 do begin
-    control := self.Controls[0];
-    self.RemoveControl(control);
-    if destroy then control.Destroy;
-  end;
-end;
-
-{%endregion}
-
-
 end.
 end.
 
 

+ 134 - 0
Units/Utils/UCommonUI.pas

@@ -0,0 +1,134 @@
+{
+  Copyright (c) 2017 The PascalCoin Project
+  Copyright (c) 2017 Sphere 10 Software
+
+  Author: Herman Schoenfeld <[email protected]>
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This code has been donated to The PascalCoin Project by Sphere 10 Software (www.sphere10.com)
+  who retains independent copyright.
+
+  Additional Credits:
+    <contributors add yourselves here>
+}
+
+unit UCommonUI;
+
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, FGL, Generics.Collections, Generics.Defaults, syncobjs;
+
+type
+  TApplicationForm = class(TForm)
+    private
+      FActivatedCount : UInt32;
+      FActivateFirstTime : TNotifyEvent;
+      procedure NotifyActivateFirstTime;
+    protected
+      FUILock : TCriticalSection;
+      procedure DoCreate; override;
+      procedure DoDestroy; override;
+      procedure Activate; override;
+      procedure ActivateFirstTime; virtual;
+    published
+      property OnActivateFirstTime : TNotifyEvent read FActivateFirstTime write FActivateFirstTime;
+  end;
+
+  { TWinControlHelper }
+
+  TWinControlHelper = class helper for TWinControl
+    procedure RemoveAllControls(destroy : boolean);
+  end;
+
+  { TFormHelper }
+
+  TFormHelper = class helper for TForm
+    procedure SetSubFormCoordinate(SubForm: TForm);
+  end;
+
+
+implementation
+
+{%region TApplicationForm}
+
+procedure TApplicationForm.DoCreate;
+begin
+  inherited;
+  FUILock := TCriticalSection.Create;
+  FActivatedCount := 0;
+end;
+
+procedure TApplicationForm.DoDestroy;
+begin
+  inherited;
+  FUILock.Destroy;
+  FUILock := nil;
+end;
+
+procedure TApplicationForm.Activate;
+begin
+  inherited;
+  inc(FActivatedCount);
+  if (FActivatedCount = 1) then
+    NotifyActivateFirstTime;
+end;
+
+procedure TApplicationForm.ActivateFirstTime;
+begin;
+end;
+
+
+procedure TApplicationForm.NotifyActivateFirstTime;
+begin
+  ActivateFirstTime;
+  if Assigned(FActivateFirstTime) then
+    FActivateFirstTime(Self);
+end;
+
+{%endregion}
+
+{%region TWinControlHelper}
+
+procedure TWinControlHelper.RemoveAllControls(destroy : boolean);
+var
+  control : TControl;
+begin
+  while self.ControlCount > 0 do begin
+    control := self.Controls[0];
+    self.RemoveControl(control);
+    if destroy then control.Destroy;
+  end;
+end;
+
+{%endregion}
+
+{%region TFormHelper}
+
+//Form is shown centered over parent form
+//Show the position of the subform
+procedure TFormHelper.SetSubFormCoordinate(SubForm: TForm);
+var TopLeft:TPoint;
+begin
+  // TODO this needs to be changed
+//  Subform.Position:=poOwnerFormCenter; - test for only use center of from
+// On Linux ClientToScreen work only if window show as resut need call from OnAcivate from linux correct show.
+// on window shows correct in OnShow
+  TopLeft:= Self.ClientToScreen(Point(Self.Left,Self.Top));
+  Subform.Top   :=TopLeft.y;
+  Subform.Left  :=TopLeft.x;
+  Subform.Height:= Self.Height-27;
+  Subform.Width := Self.Width-9;
+end;
+
+{%endregion}
+
+end.
+

+ 19 - 1
Units/Utils/UGridUtils.pas

@@ -26,7 +26,7 @@ uses
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
 {$ENDIF}
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams,
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams,
-  UWalletKeys, UCrypto;
+  UWalletKeys, UCrypto, UPoolMining, URPC;
 
 
 Type
 Type
   // TAccountsGrid implements a visual integration of TDrawGrid
   // TAccountsGrid implements a visual integration of TDrawGrid
@@ -75,6 +75,7 @@ Type
     Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     Property AllowMultiSelect : Boolean read FAllowMultiSelect write SetAllowMultiSelect;
     Property AllowMultiSelect : Boolean read FAllowMultiSelect write SetAllowMultiSelect;
     Function SelectedAccounts(accounts : TOrderedCardinalList) : Integer;
     Function SelectedAccounts(accounts : TOrderedCardinalList) : Integer;
+    Function SelectedAccountsCount : Integer;
   End;
   End;
 
 
   TOperationsGrid = Class(TComponent)
   TOperationsGrid = Class(TComponent)
@@ -544,6 +545,7 @@ begin
   Stream.Write(j,sizeof(j));
   Stream.Write(j,sizeof(j));
 end;
 end;
 
 
+// HS TODO - refactor out TOrderedCardialList. Not a necessary consideration here.
 function TAccountsGrid.SelectedAccounts(accounts: TOrderedCardinalList): Integer;
 function TAccountsGrid.SelectedAccounts(accounts: TOrderedCardinalList): Integer;
 var i64 : Int64;
 var i64 : Int64;
   i : Integer;
   i : Integer;
@@ -564,6 +566,22 @@ begin
   Result := accounts.Count;
   Result := accounts.Count;
 end;
 end;
 
 
+function TAccountsGrid.SelectedAccountsCount : Integer;
+var i64 : Int64; i : Integer;
+begin
+   Result := 0;
+   if not assigned(FDrawGrid) then exit;
+   if FAllowMultiSelect then begin
+      for i := FDrawGrid.Selection.Top to FDrawGrid.Selection.Bottom do begin
+        i64 := AccountNumber(i);
+        if i64>=0 then Inc(Result);
+      end;
+    end else begin
+      i64 := AccountNumber(DrawGrid.Row);
+      if i64>=0 then Inc(Result);
+    end;
+end;
+
 procedure TAccountsGrid.SetAllowMultiSelect(const Value: Boolean);
 procedure TAccountsGrid.SetAllowMultiSelect(const Value: Boolean);
 begin
 begin
   FAllowMultiSelect := Value;
   FAllowMultiSelect := Value;

Some files were not shown because too many files changed in this diff