Browse Source

Build 2.0

PascalCoin 8 years ago
parent
commit
25dff89b6c
45 changed files with 8268 additions and 1967 deletions
  1. 6 1
      PascalCoinWallet.dpr
  2. BIN
      PascalCoinWallet.res
  3. 17 2
      PascalCoinWalletLazarus.lpi
  4. 9 7
      Units/Forms/UFRMAbout.lfm
  5. 3 2
      Units/Forms/UFRMAbout.pas
  6. 101 0
      Units/Forms/UFRMAccountInfo.dfm
  7. 29 0
      Units/Forms/UFRMAccountInfo.pas
  8. 262 0
      Units/Forms/UFRMAccountSelect.dfm
  9. 258 0
      Units/Forms/UFRMAccountSelect.lfm
  10. 470 0
      Units/Forms/UFRMAccountSelect.pas
  11. 66 0
      Units/Forms/UFRMMemoText.dfm
  12. 58 0
      Units/Forms/UFRMMemoText.lfm
  13. 46 0
      Units/Forms/UFRMMemoText.pas
  14. 1 0
      Units/Forms/UFRMNewPrivateKeyType.lfm
  15. 4 3
      Units/Forms/UFRMNodesIp.lfm
  16. 741 252
      Units/Forms/UFRMOperation.dfm
  17. 730 273
      Units/Forms/UFRMOperation.lfm
  18. 798 337
      Units/Forms/UFRMOperation.pas
  19. 1 0
      Units/Forms/UFRMPascalCoinWalletConfig.lfm
  20. 4 4
      Units/Forms/UFRMPayloadDecoder.pas
  21. 220 0
      Units/Forms/UFRMSaleAccounts.dfm
  22. 245 0
      Units/Forms/UFRMSaleAccounts.pas
  23. 42 3
      Units/Forms/UFRMWallet.dfm
  24. 57 17
      Units/Forms/UFRMWallet.lfm
  25. 173 14
      Units/Forms/UFRMWallet.pas
  26. 6 7
      Units/Forms/UFRMWalletKeys.lfm
  27. 81 38
      Units/Forms/UFRMWalletKeys.pas
  28. 639 122
      Units/PascalCoin/UAccounts.pas
  29. 245 0
      Units/PascalCoin/UBaseTypes.pas
  30. 193 400
      Units/PascalCoin/UBlockChain.pas
  31. 169 0
      Units/PascalCoin/UChunk.pas
  32. 50 15
      Units/PascalCoin/UConst.pas
  33. 26 3
      Units/PascalCoin/UCrypto.pas
  34. 329 114
      Units/PascalCoin/UFileStorage.pas
  35. 2 3
      Units/PascalCoin/ULog.pas
  36. 310 129
      Units/PascalCoin/UNetProtocol.pas
  37. 65 28
      Units/PascalCoin/UNode.pas
  38. 720 65
      Units/PascalCoin/UOpTransaction.pas
  39. 1 1
      Units/PascalCoin/UPoolMinerThreads.pas
  40. 19 17
      Units/PascalCoin/UPoolMining.pas
  41. 920 48
      Units/PascalCoin/URPC.pas
  42. 12 9
      Units/PascalCoin/UThread.pas
  43. 2 1
      Units/PascalCoin/config.inc
  44. 4 4
      Units/PascalCoin/upcdaemon.pas
  45. 134 48
      Units/Utils/UGridUtils.pas

+ 6 - 1
PascalCoinWallet.dpr

@@ -32,7 +32,12 @@ uses
   UFileStorage in 'Units\PascalCoin\UFileStorage.pas',
   UOpenSSL in 'Units\PascalCoin\UOpenSSL.pas',
   UOpenSSLdef in 'Units\PascalCoin\UOpenSSLdef.pas',
-  UAES in 'Units\PascalCoin\UAES.pas';
+  UAES in 'Units\PascalCoin\UAES.pas',
+  UFRMAccountSelect in 'Units\Forms\UFRMAccountSelect.pas' {FRMAccountSelect},
+  UFRMAccountInfo in 'Units\Forms\UFRMAccountInfo.pas' {FRMAccountInfo},
+  UFRMMemoText in 'Units\Forms\UFRMMemoText.pas' {FRMMemoText},
+  UChunk in 'Units\PascalCoin\UChunk.pas',
+  UBaseTypes in 'Units\PascalCoin\UBaseTypes.pas';
 
 {$R *.res}
 

BIN
PascalCoinWallet.res


+ 17 - 2
PascalCoinWalletLazarus.lpi

@@ -38,7 +38,7 @@
         <PackageName Value="LCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="34">
+    <Units Count="37">
       <Unit0>
         <Filename Value="PascalCoinWalletLazarus.dpr"/>
         <IsPartOfProject Value="True"/>
@@ -199,6 +199,21 @@
         <Filename Value="Units\PascalCoin\UAES.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit33>
+      <Unit34>
+        <Filename Value="Units\PascalCoin\UChunk.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit34>
+      <Unit35>
+        <Filename Value="Units\Forms\UFRMAccountSelect.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAccountSelect"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit35>
+      <Unit36>
+        <Filename Value="Units\PascalCoin\UBaseTypes.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit36>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -209,7 +224,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="Synapse\lib;Units\Forms;Units\PascalCoin;Units\Utils"/>
+      <OtherUnitFiles Value="Synapse\lib;Units\Forms;Units\PascalCoin;Units\Utils;Units\SQLite3"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Parsing>

+ 9 - 7
Units/Forms/UFRMAbout.lfm

@@ -1,7 +1,7 @@
 object FRMAbout: TFRMAbout
-  Left = 291
+  Left = 764
   Height = 405
-  Top = 184
+  Top = 516
   Width = 585
   ActiveControl = bbClose
   BorderIcons = [biSystemMenu]
@@ -15,7 +15,7 @@ object FRMAbout: TFRMAbout
   Font.Name = 'Tahoma'
   OnCreate = FormCreate
   Position = poOwnerFormCenter
-  LCLVersion = '1.6.0.4'
+  LCLVersion = '1.6.4.0'
   object Image1: TImage
     Left = 15
     Height = 62
@@ -244,10 +244,11 @@ object FRMAbout: TFRMAbout
     Width = 478
     BorderStyle = bsNone
     Lines.Strings = (
-      'Copyright (c) 2016 Albert Molina'
+      'Copyright (c) 2016 - 2017 Albert Molina'
       ''
-      'This software is a Node of the Pascal Coin P2P Cryptocurrency. A Crypto currency without '
-      'need of historical operations. It can be used to Mine and Explore blocks and operations.'
+      'Pascal Coin is P2P cryptocurrency without the need for historical operations. This software '
+      'comprises a node within the Pascal Coin network and can be used to Mine and Explore blocks and '
+      'operations.'
       ''
       'Distributed under the MIT software license, see the accompanying file LICENSE  or visit '
       'http://www.opensource.org/licenses/mit-license.php.'
@@ -266,8 +267,9 @@ object FRMAbout: TFRMAbout
     )
     ParentColor = True
     ReadOnly = True
-    ScrollBars = ssAutoBoth
+    ScrollBars = ssVertical
     TabOrder = 0
+    WordWrap = False
   end
   object bbClose: TBitBtn
     Left = 456

+ 3 - 2
Units/Forms/UFRMAbout.pas

@@ -60,7 +60,7 @@ uses
   ShellApi,
 {$ELSE}
 {$ENDIF}
-  UFolderHelper, UConst;
+  UFolderHelper, UConst, UNode;
 
 {$IFnDEF FPC}
   {$R *.dfm}
@@ -79,7 +79,7 @@ begin
   fvi := TFolderHelper.GetTFileVersionInfo(Application.ExeName);
   lblBuild.Caption :=  'Build: '+fvi.FileVersion;
   {$ENDIF}
-  lblProtocolVersion.Caption := Format('BlockChain Protocol: %d (%d)  -  Net Protocol: %d (%d)',[CT_BlockChain_Protocol_Version,CT_BlockChain_Protocol_Available,
+  lblProtocolVersion.Caption := Format('BlockChain Protocol: %d (%d)  -  Net Protocol: %d (%d)',[TNode.Node.Bank.SafeBox.CurrentProtocol,CT_BlockChain_Protocol_Available,
     CT_NetProtocol_Version, CT_NetProtocol_Available]);
 end;
 
@@ -93,6 +93,7 @@ begin
   OpenURL(TLabel(Sender).Caption);
 end;
 
+
 procedure TFRMAbout.OpenURL(Url: String);
 begin
   {$IFDEF FPC}

+ 101 - 0
Units/Forms/UFRMAccountInfo.dfm

@@ -0,0 +1,101 @@
+object FRMAccountInfo: TFRMAccountInfo
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  Caption = 'Account Information'
+  ClientHeight = 300
+  ClientWidth = 635
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poOwnerFormCenter
+  PixelsPerInch = 96
+  TextHeight = 13
+  object Label1: TLabel
+    Left = 25
+    Top = 26
+    Width = 39
+    Height = 13
+    Caption = 'Account'
+  end
+  object lblAccount: TLabel
+    Left = 77
+    Top = 21
+    Width = 97
+    Height = 19
+    Caption = '0000000-00'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object Label6: TLabel
+    Left = 24
+    Top = 56
+    Width = 47
+    Height = 13
+    Caption = 'Public key'
+  end
+  object lblBalance: TLabel
+    Left = 252
+    Top = 21
+    Width = 97
+    Height = 19
+    Caption = '0000000-00'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object Label3: TLabel
+    Left = 200
+    Top = 26
+    Width = 37
+    Height = 13
+    Caption = 'Balance'
+  end
+  object Label2: TLabel
+    Left = 25
+    Top = 91
+    Width = 26
+    Height = 13
+    Caption = 'State'
+  end
+  object Label4: TLabel
+    Left = 77
+    Top = 86
+    Width = 200
+    Height = 19
+    Caption = 'Normal/For sale/Private'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object ebPublicKey: TEdit
+    Left = 77
+    Top = 51
+    Width = 533
+    Height = 25
+    Ctl3D = False
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentCtl3D = False
+    ParentFont = False
+    TabOrder = 0
+    Text = 'ebPublicKey'
+  end
+end

+ 29 - 0
Units/Forms/UFRMAccountInfo.pas

@@ -0,0 +1,29 @@
+unit UFRMAccountInfo;
+
+interface
+
+uses
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls;
+
+type
+  TFRMAccountInfo = class(TForm)
+    Label1: TLabel;
+    lblAccount: TLabel;
+    Label6: TLabel;
+    lblBalance: TLabel;
+    Label3: TLabel;
+    ebPublicKey: TEdit;
+    Label2: TLabel;
+    Label4: TLabel;
+  private
+    { Private declarations }
+  public
+    { Public declarations }
+  end;
+
+implementation
+
+{$R *.dfm}
+
+end.

+ 262 - 0
Units/Forms/UFRMAccountSelect.dfm

@@ -0,0 +1,262 @@
+object FRMAccountSelect: TFRMAccountSelect
+  Left = 332
+  Top = 227
+  BorderIcons = [biSystemMenu, biMaximize]
+  Caption = 'Accounts'
+  ClientHeight = 322
+  ClientWidth = 657
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = True
+  Position = poOwnerFormCenter
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  PixelsPerInch = 96
+  TextHeight = 13
+  object pnlAccountsTop: TPanel
+    Left = 451
+    Top = 0
+    Width = 206
+    Height = 282
+    Align = alRight
+    BevelOuter = bvNone
+    TabOrder = 0
+    ExplicitLeft = 331
+    object cbOnlyForSale: TCheckBox
+      Left = 10
+      Top = 65
+      Width = 81
+      Height = 19
+      Caption = 'Only for sale'
+      TabOrder = 2
+    end
+    object bbSearch: TBitBtn
+      Left = 50
+      Top = 236
+      Width = 91
+      Height = 36
+      Caption = 'Search'
+      DoubleBuffered = True
+      Glyph.Data = {
+        F6060000424DF606000000000000360000002800000018000000180000000100
+        180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFABA7A68C8888C09596FF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFACA8A786868A2A669A4B7BA0C09494FF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF9B9D9D5492C22E99FF1B7C
+        CB557A97C19595FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3DADFF
+        57C1FF4DB1FF3196FA197CC9537997C19495FF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FF41ABFF5DBFFE4DAFFF3196FA197BC85A7A97C29594FF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF42ABFE5CC0FE4DAFFF3096FA19
+        7AC8587A96C19494FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF42AC
+        FE5CC0FE4DAFFF2E94FA187AC75F7C97BB9396FF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FF44ADFE5CBFFE4DB0FF2D94F81979C6437397FF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF46AEFE5CBFFE4CB0FF3F97ED
+        577B94FF00FFFF00FFFF00FFAE837EAE837EAE837EAE837EAE837EFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF46
+        AFFE66C5FFA3CCF1BEB3AB867170A9807AAE837EE5D8BDFFFFE2FFFFE2FFFFE0
+        EEDFC6AE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFDCBFB8C69E8ED8BDA3FFFFDDFFFFDDFF
+        FFDBFFFFDAFFFFDBFFFFDDFFFFE9DEC9B7AE837EFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE857FDEC0A5FFF3
+        C1FCF1C8FFFFDAFFFFD9FFFFDAFFFFE0FFFFE4FFFFEFFFFFFFDDCAC8AE837EFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        AE837EFFEFB9F5D4A5FBF0C6FFFFDBFFFFDAFFFFDDFFFFEBFFFFF5FFFFFBFFFF
+        FDFFFFFEAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFAE837EECD5B4F8D6A2F2C997FCEDC4FFFFDBFFFFDAFFFFE0FFFFF0
+        FFFFFEFFFFFDFFFFF5FFFFEBE1D3B8AE837EFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFAE837EFFF3BFF4CB95F0C18EF9E5BAFEFDD7FF
+        FFDCFFFFDFFFFFECFFFFF8FFFFF8FFFFECFFFFE0FFFFE3AE837EFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EFFF4C0F3C994EEB8
+        84F5D5A5FDF3CBFFFFDCFFFFDDFFFFE2FFFFE8FFFFE9FFFFE2FFFFDDFFFFE3AE
+        837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837E
+        FFF5C3F6D19CEEB27DF1C593F7E0B3FDF7D0FFFFDCFFFFDDFFFFDDFFFFDDFFFF
+        DBFFFFDAFFFFE1AE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFAE837EECD5B4FDE6B2F3C996F2C792F3D09CF8E3B5FCF5CDFFFFDA
+        FFFFDCFFFFDBFFFFDBFFFFDCE2D3B8AE837EFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EFFFED0FCF0D6F8E0BEF4CF9DF3
+        CD9AF6D8AAFBEABFFDF4CBFCF7CFFEF8D1FFFFDEAE837EFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EE0CAB2FFFF
+        FFFEF9F2F8E1BEF1C48DEEBA85F1C491F4D1A1F8DEB0FFF7C6DDC7AEAE837EFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFAE837ED9C6C0FFFFFFFFF7D7F8DAA6F4CB95F5CD99F9D9A6FFF1BBDEC2
+        A9AE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFAE837EAE837EE8D5B4FFF9C5FFF5C0FFF5C2
+        EED8B7AE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EAE
+        837EAE837EAE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FF}
+      ParentDoubleBuffered = False
+      TabOrder = 10
+      OnClick = bbSearchClick
+    end
+    object cbOnlyForPrivateSaleToMe: TCheckBox
+      Left = 10
+      Top = 111
+      Width = 148
+      Height = 19
+      Caption = 'Only for private sale to me'
+      TabOrder = 4
+    end
+    object cbAccountsBalance: TCheckBox
+      Left = 10
+      Top = 134
+      Width = 104
+      Height = 19
+      Caption = 'Accounts Balance'
+      TabOrder = 5
+    end
+    object cbMyAccounts: TCheckBox
+      Left = 10
+      Top = 10
+      Width = 81
+      Height = 19
+      Caption = 'My Accounts'
+      TabOrder = 0
+      OnClick = cbMyAccountsClick
+    end
+    object cbMyPrivateKeys: TComboBox
+      Left = 10
+      Top = 35
+      Width = 176
+      Height = 21
+      Style = csDropDownList
+      TabOrder = 1
+      OnChange = cbMyPrivateKeysChange
+    end
+    object cbOnlyForPublicSale: TCheckBox
+      Left = 10
+      Top = 88
+      Width = 111
+      Height = 19
+      Caption = 'Only for public sale'
+      TabOrder = 3
+    end
+    object ebMinBalance: TEdit
+      Left = 16
+      Top = 160
+      Width = 73
+      Height = 21
+      TabOrder = 6
+      OnExit = ebMinBalanceExit
+    end
+    object ebMaxBalance: TEdit
+      Left = 103
+      Top = 160
+      Width = 73
+      Height = 21
+      TabOrder = 7
+      OnExit = ebMaxBalanceExit
+    end
+    object cbAccountsName: TCheckBox
+      Left = 10
+      Top = 186
+      Width = 93
+      Height = 19
+      Caption = 'Accounts name'
+      TabOrder = 8
+    end
+    object ebAccountName: TEdit
+      Left = 18
+      Top = 209
+      Width = 158
+      Height = 21
+      TabOrder = 9
+    end
+  end
+  object pnlBottom: TPanel
+    Left = 0
+    Top = 282
+    Width = 657
+    Height = 40
+    Align = alBottom
+    BevelOuter = bvNone
+    TabOrder = 1
+    ExplicitWidth = 537
+    DesignSize = (
+      657
+      40)
+    object Label17: TLabel
+      Left = 5
+      Top = 10
+      Width = 48
+      Height = 13
+      Caption = 'Accounts:'
+      Color = clBtnFace
+      ParentColor = False
+    end
+    object lblAccountsCount: TLabel
+      Left = 60
+      Top = 10
+      Width = 18
+      Height = 13
+      Caption = '000'
+      Color = clBtnFace
+      ParentColor = False
+    end
+    object Label19: TLabel
+      Left = 136
+      Top = 10
+      Width = 41
+      Height = 13
+      Caption = 'Balance:'
+      Color = clBtnFace
+      ParentColor = False
+    end
+    object lblAccountsBalance: TLabel
+      Left = 200
+      Top = 10
+      Width = 18
+      Height = 13
+      Caption = '000'
+      Color = clBtnFace
+      ParentColor = False
+    end
+    object bbSelect: TBitBtn
+      Left = 450
+      Top = 5
+      Width = 95
+      Height = 30
+      Anchors = [akTop, akRight]
+      Caption = 'Select'
+      DoubleBuffered = True
+      Kind = bkOK
+      ParentDoubleBuffered = False
+      TabOrder = 0
+      ExplicitLeft = 330
+    end
+    object bbCancel: TBitBtn
+      Left = 554
+      Top = 5
+      Width = 95
+      Height = 30
+      Anchors = [akTop, akRight]
+      DoubleBuffered = True
+      Kind = bkCancel
+      ParentDoubleBuffered = False
+      TabOrder = 1
+      ExplicitLeft = 434
+    end
+  end
+  object dgAccounts: TDrawGrid
+    Left = 0
+    Top = 0
+    Width = 451
+    Height = 282
+    Align = alClient
+    TabOrder = 2
+    ExplicitWidth = 331
+  end
+end

+ 258 - 0
Units/Forms/UFRMAccountSelect.lfm

@@ -0,0 +1,258 @@
+object FRMAccountSelect: TFRMAccountSelect
+  Left = 534
+  Height = 322
+  Top = 308
+  Width = 601
+  BorderIcons = [biSystemMenu, biMaximize]
+  Caption = 'Accounts'
+  ClientHeight = 322
+  ClientWidth = 601
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  Position = poOwnerFormCenter
+  LCLVersion = '1.6.0.4'
+  object pnlAccountsTop: TPanel
+    Left = 395
+    Height = 282
+    Top = 0
+    Width = 206
+    Align = alRight
+    BevelOuter = bvNone
+    ClientHeight = 282
+    ClientWidth = 206
+    TabOrder = 0
+    object cbOnlyForSale: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 65
+      Width = 81
+      Caption = 'Only for sale'
+      TabOrder = 0
+    end
+    object bbSearch: TBitBtn
+      Left = 56
+      Height = 36
+      Top = 240
+      Width = 86
+      Caption = 'Search'
+      Glyph.Data = {
+        F6060000424DF606000000000000360000002800000018000000180000000100
+        180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFABA7A68C8888C09596FF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFACA8A786868A2A669A4B7BA0C09494FF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF9B9D9D5492C22E99FF1B7C
+        CB557A97C19595FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3DADFF
+        57C1FF4DB1FF3196FA197CC9537997C19495FF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FF41ABFF5DBFFE4DAFFF3196FA197BC85A7A97C29594FF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF42ABFE5CC0FE4DAFFF3096FA19
+        7AC8587A96C19494FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF42AC
+        FE5CC0FE4DAFFF2E94FA187AC75F7C97BB9396FF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FF44ADFE5CBFFE4DB0FF2D94F81979C6437397FF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF46AEFE5CBFFE4CB0FF3F97ED
+        577B94FF00FFFF00FFFF00FFAE837EAE837EAE837EAE837EAE837EFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF46
+        AFFE66C5FFA3CCF1BEB3AB867170A9807AAE837EE5D8BDFFFFE2FFFFE2FFFFE0
+        EEDFC6AE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFDCBFB8C69E8ED8BDA3FFFFDDFFFFDDFF
+        FFDBFFFFDAFFFFDBFFFFDDFFFFE9DEC9B7AE837EFF00FFFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE857FDEC0A5FFF3
+        C1FCF1C8FFFFDAFFFFD9FFFFDAFFFFE0FFFFE4FFFFEFFFFFFFDDCAC8AE837EFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        AE837EFFEFB9F5D4A5FBF0C6FFFFDBFFFFDAFFFFDDFFFFEBFFFFF5FFFFFBFFFF
+        FDFFFFFEAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFAE837EECD5B4F8D6A2F2C997FCEDC4FFFFDBFFFFDAFFFFE0FFFFF0
+        FFFFFEFFFFFDFFFFF5FFFFEBE1D3B8AE837EFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFAE837EFFF3BFF4CB95F0C18EF9E5BAFEFDD7FF
+        FFDCFFFFDFFFFFECFFFFF8FFFFF8FFFFECFFFFE0FFFFE3AE837EFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EFFF4C0F3C994EEB8
+        84F5D5A5FDF3CBFFFFDCFFFFDDFFFFE2FFFFE8FFFFE9FFFFE2FFFFDDFFFFE3AE
+        837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837E
+        FFF5C3F6D19CEEB27DF1C593F7E0B3FDF7D0FFFFDCFFFFDDFFFFDDFFFFDDFFFF
+        DBFFFFDAFFFFE1AE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFAE837EECD5B4FDE6B2F3C996F2C792F3D09CF8E3B5FCF5CDFFFFDA
+        FFFFDCFFFFDBFFFFDBFFFFDCE2D3B8AE837EFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EFFFED0FCF0D6F8E0BEF4CF9DF3
+        CD9AF6D8AAFBEABFFDF4CBFCF7CFFEF8D1FFFFDEAE837EFF00FFFF00FFFF00FF
+        FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EE0CAB2FFFF
+        FFFEF9F2F8E1BEF1C48DEEBA85F1C491F4D1A1F8DEB0FFF7C6DDC7AEAE837EFF
+        00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+        FF00FFAE837ED9C6C0FFFFFFFFF7D7F8DAA6F4CB95F5CD99F9D9A6FFF1BBDEC2
+        A9AE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+        00FFFF00FFFF00FFFF00FFFF00FFAE837EAE837EE8D5B4FFF9C5FFF5C0FFF5C2
+        EED8B7AE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+        FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAE837EAE
+        837EAE837EAE837EAE837EFF00FFFF00FFFF00FFFF00FFFF00FF
+      }
+      OnClick = bbSearchClick
+      TabOrder = 1
+    end
+    object cbOnlyForPrivateSaleToMe: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 111
+      Width = 148
+      Caption = 'Only for private sale to me'
+      TabOrder = 2
+    end
+    object cbAccountsBalance: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 134
+      Width = 104
+      Caption = 'Accounts Balance'
+      TabOrder = 3
+    end
+    object cbMyAccounts: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 10
+      Width = 81
+      Caption = 'My Accounts'
+      OnChange = cbMyAccountsChange
+      OnClick = cbMyAccountsClick
+      TabOrder = 4
+    end
+    object cbMyPrivateKeys: TComboBox
+      Left = 10
+      Height = 21
+      Top = 35
+      Width = 176
+      ItemHeight = 13
+      OnChange = cbMyPrivateKeysChange
+      Style = csDropDownList
+      TabOrder = 5
+    end
+    object cbOnlyForPublicSale: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 88
+      Width = 111
+      Caption = 'Only for public sale'
+      TabOrder = 6
+    end
+    object ebMinBalance: TEdit
+      Left = 16
+      Height = 21
+      Top = 160
+      Width = 73
+      OnExit = ebMinBalanceExit
+      TabOrder = 7
+    end
+    object ebMaxBalance: TEdit
+      Left = 103
+      Height = 21
+      Top = 160
+      Width = 73
+      OnExit = ebMaxBalanceExit
+      TabOrder = 8
+    end
+    object cbAccountsName: TCheckBox
+      Left = 10
+      Height = 19
+      Top = 186
+      Width = 93
+      Caption = 'Accounts name'
+      TabOrder = 9
+    end
+    object ebAccountName: TEdit
+      Left = 18
+      Height = 21
+      Top = 209
+      Width = 158
+      TabOrder = 10
+    end
+  end
+  object pnlBottom: TPanel
+    Left = 0
+    Height = 40
+    Top = 282
+    Width = 601
+    Align = alBottom
+    BevelOuter = bvNone
+    ClientHeight = 40
+    ClientWidth = 601
+    TabOrder = 1
+    object bbSelect: TBitBtn
+      Left = 394
+      Height = 30
+      Top = 5
+      Width = 95
+      Anchors = [akTop, akRight]
+      Caption = 'Select'
+      Default = True
+      Kind = bkOK
+      ModalResult = 1
+      TabOrder = 0
+    end
+    object bbCancel: TBitBtn
+      Left = 498
+      Height = 30
+      Top = 5
+      Width = 95
+      Anchors = [akTop, akRight]
+      Cancel = True
+      Caption = 'Cancel'
+      Kind = bkCancel
+      ModalResult = 2
+      TabOrder = 1
+    end
+    object Label17: TLabel
+      Left = 5
+      Height = 13
+      Top = 10
+      Width = 48
+      Caption = 'Accounts:'
+      ParentColor = False
+    end
+    object lblAccountsCount: TLabel
+      Left = 60
+      Height = 13
+      Top = 10
+      Width = 18
+      Caption = '000'
+      ParentColor = False
+    end
+    object Label19: TLabel
+      Left = 136
+      Height = 13
+      Top = 10
+      Width = 41
+      Caption = 'Balance:'
+      ParentColor = False
+    end
+    object lblAccountsBalance: TLabel
+      Left = 200
+      Height = 13
+      Top = 10
+      Width = 18
+      Caption = '000'
+      ParentColor = False
+    end
+  end
+  object dgAccounts: TDrawGrid
+    Left = 0
+    Height = 282
+    Top = 0
+    Width = 395
+    Align = alClient
+    ExtendedSelect = False
+    TabOrder = 2
+    TitleFont.Color = clWindowText
+    TitleFont.Height = -11
+    TitleFont.Name = 'Tahoma'
+  end
+end

+ 470 - 0
Units/Forms/UFRMAccountSelect.pas

@@ -0,0 +1,470 @@
+unit UFRMAccountSelect;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{ Copyright (c) 2016 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  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
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+uses
+{$IFnDEF FPC}
+  Windows,
+{$ELSE}
+  LCLIntf, LCLType, LMessages,
+{$ENDIF}
+  Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, UAccounts, Grids, StdCtrls, Buttons, ExtCtrls, UWalletKeys, UNode,
+  UGridUtils, UConst, UThread;
+
+const
+  CT_AS_MyAccounts = $0001;
+  CT_AS_OnlyForSale = $0002;
+
+type
+  { TSearchThread }
+  TSearchValues = Record
+    SafeBox : TPCSafeBox;
+    inWalletKeys : TWalletKeys;
+    inAccountKey : TAccountKey;
+    onlyForSale,
+    onlyForPublicSale,
+    onlyForPrivateSaleToMe : Boolean;
+    minBal,maxBal : Int64;
+    searchName : AnsiString;
+  end;
+
+  TSearchProcedure = procedure(Const searchFound : TCardinalsArray; const searchValues : TSearchValues) of object;
+
+  TSearchThread = Class(TPCThread)
+  private
+    FIsReadyForSearch: Boolean;
+    FOnSearchFinished: TSearchProcedure;
+    FDoStopSearch : Boolean;
+    FStartSearch : Boolean;
+    FAccounts : TCardinalsArray;
+    FSearchValues : TSearchValues;
+    procedure SetIsReadyForSearch(AValue: Boolean);
+  protected
+    procedure BCExecute; override;
+    Procedure DoNotifySearchFinished;
+  public
+    property IsReadyForSearch : Boolean read FIsReadyForSearch write SetIsReadyForSearch;
+    Property OnSearchFinished : TSearchProcedure read FOnSearchFinished write FOnSearchFinished;
+    Procedure DoSearch(Const newSearchValues : TSearchValues);
+    Procedure StopSearch;
+  end;
+
+  { TFRMAccountSelect }
+
+  TFRMAccountSelect = class(TForm)
+    cbAccountsName: TCheckBox;
+    ebMinBalance: TEdit;
+    ebMaxBalance: TEdit;
+    ebAccountName: TEdit;
+    Label17: TLabel;
+    Label19: TLabel;
+    lblAccountsBalance: TLabel;
+    lblAccountsCount: TLabel;
+    pnlAccountsTop: TPanel;
+    cbOnlyForSale: TCheckBox;
+    bbSearch: TBitBtn;
+    cbOnlyForPrivateSaleToMe: TCheckBox;
+    pnlBottom: TPanel;
+    dgAccounts: TDrawGrid;
+    bbSelect: TBitBtn;
+    bbCancel: TBitBtn;
+    cbAccountsBalance: TCheckBox;
+    cbMyAccounts: TCheckBox;
+    cbMyPrivateKeys: TComboBox;
+    cbOnlyForPublicSale: TCheckBox;
+    procedure bbSearchClick(Sender: TObject);
+    procedure cbMyAccountsChange(Sender: TObject);
+    procedure cbMyAccountsClick(Sender: TObject);
+    procedure cbMyPrivateKeysChange(Sender: TObject);
+    procedure ebMaxBalanceExit(Sender: TObject);
+    procedure ebMinBalanceExit(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FAllowSelect: Boolean;
+    FDefaultAccount: Int64;
+    FFilters: Integer;
+    FWalletKeys: TWalletKeys;
+    FNode: TNode;
+    FSafeBox : TPCSafeBox;
+    FAccountsGrid : TAccountsGrid;
+    FSearchThread : TSearchThread;
+    { Private declarations }
+    procedure SetAllowSelect(AValue: Boolean);
+    procedure SetDefaultAccount(AValue: Int64);
+    procedure SetFilters(AValue: Integer);
+    procedure SetNode(const Value: TNode);
+    procedure SetWalletKeys(const Value: TWalletKeys);
+    Procedure SearchFiltered;
+    Procedure UpdateControls;
+    procedure OnSearchFinished(Const searchFound : TCardinalsArray; const searchValues : TSearchValues);
+    procedure OnAccountsGridUpdated(Sender : TObject);
+  protected
+    FAccounts : TOrderedAccountList;
+  public
+    { Public declarations }
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+    Property Node : TNode read FNode write SetNode;
+    Property Filters : Integer read FFilters write SetFilters;
+    Property DefaultAccount : Int64 read FDefaultAccount write SetDefaultAccount;
+    Function GetSelected : Int64;
+    Property AllowSelect : Boolean read FAllowSelect write SetAllowSelect;
+  end;
+
+implementation
+
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
+
+Uses strutils;
+
+{ TSearchThread }
+
+procedure TSearchThread.SetIsReadyForSearch(AValue: Boolean);
+begin
+  if FIsReadyForSearch=AValue then Exit;
+  FIsReadyForSearch:=AValue;
+end;
+
+procedure TSearchThread.BCExecute;
+  procedure SearchFilteredInThread;
+  Var c,maxC : Cardinal;
+    account : TAccount;
+    isValid : Boolean;
+    validAccKey : Boolean;
+    errors : AnsiString;
+    i : Integer;
+  begin
+    SetLength(FAccounts,0);
+    c := 0;
+    maxC := FSearchValues.SafeBox.AccountsCount-1;
+    validAccKey := TAccountComp.IsValidAccountKey(FSearchValues.inAccountKey,errors);
+    while (c<=maxC) And (Not Terminated) And (Not FDoStopSearch) do begin
+      account := FSearchValues.SafeBox.Account(c);
+      isValid := True;
+      If validAccKey then begin
+        isValid := TAccountComp.EqualAccountKeys(account.accountInfo.accountKey,FSearchValues.inAccountKey);
+      end else if (Assigned(FSearchValues.inWalletKeys)) then begin
+        isValid := FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.accountKey)>=0;
+      end;
+      If isValid And (FSearchValues.onlyForSale) then begin
+        isValid := TAccountComp.IsAccountForSale(account.accountInfo);
+      end;
+      If IsValid and (FSearchValues.onlyForPublicSale) then begin
+        isValid := (TAccountComp.IsAccountForSale(account.accountInfo)) And (Not TAccountComp.IsAccountForSaleAcceptingTransactions(account.accountInfo));
+      end;
+      If IsValid and (FSearchValues.onlyForPrivateSaleToMe) then begin
+        isValid := (TAccountComp.IsAccountForSaleAcceptingTransactions(account.accountInfo)) And
+          (Assigned(FSearchValues.inWalletKeys)) And (FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.new_publicKey)>=0);
+      end;
+      If IsValid then begin
+        IsValid := (account.balance>=FSearchValues.minBal) And ((FSearchValues.maxBal<0) Or (account.balance<=FSearchValues.maxBal));
+      end;
+      If IsValid And (FSearchValues.searchName<>'') then begin
+        i := ansipos(FSearchValues.searchName,account.name);
+        IsValid := i>0;
+      end;
+      //
+      if IsValid then begin
+        setLength(FAccounts,length(FAccounts)+1);
+        FAccounts[high(FAccounts)] := c;
+      end;
+      inc(c);
+    end;
+    If (Not Terminated) And (Not FDoStopSearch) then begin
+      Synchronize(DoNotifySearchFinished);
+    end;
+  end;
+begin
+  FIsReadyForSearch := True;
+  FDoStopSearch := False;
+  FStartSearch := False;
+  while (Not Terminated) do begin
+    FIsReadyForSearch := True;
+    If (FStartSearch) then begin
+      FStartSearch := False;
+      FIsReadyForSearch:=False;
+      FDoStopSearch:=False;
+      SearchFilteredInThread;
+    end;
+  end;
+end;
+
+procedure TSearchThread.DoNotifySearchFinished;
+begin
+  If Assigned(FOnSearchFinished) then begin
+    FOnSearchFinished(FAccounts,FSearchValues);
+  end;
+end;
+
+procedure TSearchThread.DoSearch(const newSearchValues: TSearchValues);
+begin
+  FDoStopSearch := True;
+  FStartSearch := False;
+  While (Not FIsReadyForSearch) do sleep(1);
+  FSearchValues := newSearchValues;
+  FStartSearch := True;
+end;
+
+procedure TSearchThread.StopSearch;
+begin
+  FDoStopSearch := True;
+  FStartSearch := False;
+  While (Not FIsReadyForSearch) do sleep(1);
+end;
+
+{ TFRMAccountSelect }
+
+procedure TFRMAccountSelect.SetDefaultAccount(AValue: Int64);
+begin
+  if FDefaultAccount=AValue then Exit;
+  FDefaultAccount:=AValue;
+end;
+
+procedure TFRMAccountSelect.SetAllowSelect(AValue: Boolean);
+begin
+  FAllowSelect:=AValue;
+  bbSelect.Visible:=AValue;
+  bbSelect.Enabled:=AValue;
+end;
+
+procedure TFRMAccountSelect.SetFilters(AValue: Integer);
+begin
+  FFilters:=AValue;
+  cbMyAccounts.Checked := (AValue and CT_AS_MyAccounts) = CT_AS_MyAccounts;
+  cbOnlyForSale.Checked := (AValue and CT_AS_OnlyForSale) = CT_AS_OnlyForSale;
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.FormCreate(Sender: TObject);
+begin
+  FSearchThread := TSearchThread.Create(false);
+  FSearchThread.OnSearchFinished := OnSearchFinished;
+  FAccounts := TOrderedAccountList.Create;
+  FWalletKeys := Nil;
+  FNode := Nil;
+  FSafeBox:=Nil;
+  FFilters:=0;
+  FDefaultAccount:=-1;
+  FAccountsGrid := TAccountsGrid.Create(Self);
+  FAccountsGrid.DrawGrid := dgAccounts;
+  FAccountsGrid.OnUpdated:=OnAccountsGridUpdated;
+  //
+  cbMyAccounts.OnClick:=cbMyAccountsClick;
+  cbMyPrivateKeys.OnClick:=cbMyAccountsClick;
+  cbOnlyForSale.OnClick:=cbMyAccountsClick;
+  cbOnlyForPrivateSaleToMe.OnClick:=cbMyAccountsClick;
+  cbOnlyForPublicSale.OnClick:=cbMyAccountsClick;
+  cbAccountsBalance.OnClick:=cbMyAccountsClick;
+  cbAccountsName.OnClick:=cbMyAccountsClick;
+  UpdateControls;
+  AllowSelect:=False;
+end;
+
+procedure TFRMAccountSelect.bbSearchClick(Sender: TObject);
+begin
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.cbMyAccountsChange(Sender: TObject);
+begin
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.cbMyAccountsClick(Sender: TObject);
+begin
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.cbMyPrivateKeysChange(Sender: TObject);
+begin
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.ebMaxBalanceExit(Sender: TObject);
+begin
+  if ebMaxBalance.text<>'' then begin
+    cbAccountsBalance.Checked:=True;
+    SearchFiltered;
+  end;
+end;
+
+procedure TFRMAccountSelect.ebMinBalanceExit(Sender: TObject);
+begin
+  if ebMinBalance.text<>'' then begin
+    cbAccountsBalance.Checked:=True;
+    SearchFiltered;
+  end;
+end;
+
+procedure TFRMAccountSelect.FormDestroy(Sender: TObject);
+begin
+  FSearchThread.Terminate;
+  FSearchThread.WaitFor;
+  FSearchThread.Free;
+  FAccounts.Free;
+end;
+
+procedure TFRMAccountSelect.SetNode(const Value: TNode);
+begin
+  FNode := Value;
+  FSafeBox := FNode.Bank.SafeBox;
+  UpdateControls;
+end;
+
+procedure TFRMAccountSelect.SetWalletKeys(const Value: TWalletKeys);
+begin
+  FWalletKeys := Value;
+  UpdateControls;
+  SearchFiltered;
+end;
+
+procedure TFRMAccountSelect.SearchFiltered;
+Var
+  i : Integer;
+  searchValues : TSearchValues;
+begin
+  FSearchThread.StopSearch;
+  searchValues.SafeBox := FSafeBox;
+  searchValues.inAccountKey := CT_AccountInfo_NUL.accountKey;
+  searchValues.inWalletKeys := Nil;
+  If (cbMyAccounts.Checked) And (Assigned(FWalletKeys)) then begin
+    If (cbMyPrivateKeys.ItemIndex<=0) then begin
+      // All
+      searchValues.inWalletKeys := FWalletKeys;
+    end else begin
+      i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
+      if (i>=0) and (i<FWalletKeys.Count) then searchValues.inAccountKey := FWalletKeys.Key[i].AccountKey;
+    end;
+    cbMyPrivateKeys.ParentFont:=True;
+  end else begin
+    cbMyPrivateKeys.Font.Color := clGray;
+  end;
+  searchValues.onlyForSale := (cbOnlyForSale.Checked);
+  searchValues.onlyForPrivateSaleToMe := (cbOnlyForPrivateSaleToMe.Checked);
+  If searchValues.onlyForPrivateSaleToMe then begin
+    searchValues.inWalletKeys := FWalletKeys;
+  end;
+  searchValues.onlyForPublicSale := (cbOnlyForPublicSale.Checked);
+  If cbAccountsBalance.Checked then begin
+    If not TAccountComp.TxtToMoney(ebMinBalance.Text,searchValues.minBal) then Raise Exception.Create('Invalid Min Balance');
+    ebMinBalance.Text:=TAccountComp.FormatMoney(searchValues.minBal);
+    If Trim(ebMaxBalance.Text)='' then begin
+      ebMaxBalance.Text:='';
+      searchValues.maxBal:=-1;
+    end else begin
+      If not TAccountComp.TxtToMoney(ebMaxBalance.Text,searchValues.maxBal) then Raise Exception.Create('Invalid Max Balance');
+      ebMaxBalance.Text:=TAccountComp.FormatMoney(searchValues.maxBal);
+    end;
+    ebMinBalance.ParentFont:=True;
+    ebMaxBalance.ParentFont:=True;
+  end else begin
+    searchValues.minBal:=0;
+    searchValues.maxBal:=-1;
+    ebMinBalance.Font.Color:=clGray;
+    ebMaxBalance.Font.Color:=clGray;
+  end;
+  if (cbAccountsName.Checked) then begin
+    searchValues.searchName := LowerCase(Trim(ebAccountName.Text));
+    ebAccountName.ParentFont:=True;
+  end else begin
+    searchValues.searchName:='';
+    ebAccountName.Font.Color := clGray;
+  end;
+  If (searchValues.inAccountKey.EC_OpenSSL_NID=0) AND (searchValues.inWalletKeys=Nil) And (searchValues.maxBal<0) And (searchValues.minBal<=0) And
+     (Not searchValues.onlyForPrivateSaleToMe) And (Not searchValues.onlyForPublicSale) And (Not searchValues.onlyForSale) And
+     (searchValues.searchName='') then begin
+    FAccountsGrid.ShowAllAccounts:=True;
+    lblAccountsCount.Caption := IntToStr(FAccountsGrid.Node.Bank.SafeBox.AccountsCount);
+    lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
+  end else begin
+    FAccountsGrid.ShowAllAccounts:=False;
+    FSearchThread.DoSearch(searchValues);
+  end;
+end;
+
+procedure TFRMAccountSelect.UpdateControls;
+var i : Integer;
+begin
+  cbMyAccounts.Enabled:=Assigned(FWalletKeys);
+  If not Assigned(FWalletKeys) then begin
+    cbMyAccounts.Checked:=False;
+    cbMyPrivateKeys.Enabled:=False;
+    cbMyPrivateKeys.Clear;
+  end else begin
+    cbMyPrivateKeys.Enabled:=True;
+    cbMyPrivateKeys.Items.BeginUpdate;
+    try
+      cbMyPrivateKeys.Items.Clear;
+      For i:=0 to FWalletKeys.Count-1 do begin
+        cbMyPrivateKeys.Items.AddObject(FWalletKeys.Key[i].Name,TObject(i));
+      end;
+      cbMyPrivateKeys.Sorted:=True;
+      cbMyPrivateKeys.Sorted:=False;
+      cbMyPrivateKeys.items.InsertObject(0,'(All)',TObject(-1));
+    finally
+      cbMyPrivateKeys.Items.EndUpdate;
+    end;
+  end;
+end;
+
+procedure TFRMAccountSelect.OnSearchFinished(const searchFound: TCardinalsArray; const searchValues: TSearchValues);
+Var l : TOrderedCardinalList;
+  i, foundpos : Integer;
+begin
+  foundpos := -1;
+  l := FAccountsGrid.LockAccountsList;
+  try
+    l.Clear;
+    for i:=0 to High(searchFound) do begin
+      l.Add(searchFound[i]);
+      If searchFound[i]=FDefaultAccount then foundpos := i;
+    end;
+    lblAccountsCount.Caption := inttostr(l.Count);
+  finally
+    FAccountsGrid.UnlockAccountsList;
+  end;
+  If foundpos>=0 then begin
+    FAccountsGrid.DrawGrid.Row := foundpos + 1;
+  end;
+end;
+
+procedure TFRMAccountSelect.OnAccountsGridUpdated(Sender: TObject);
+begin
+  lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
+end;
+
+function TFRMAccountSelect.GetSelected: Int64;
+Var ocl : TOrderedCardinalList;
+begin
+  Result := -1;
+  ocl := TOrderedCardinalList.Create;
+  try
+    If FAccountsGrid.SelectedAccounts(ocl)=1 then Result := ocl.Get(0);
+  finally
+    ocl.Free;
+  end;
+end;
+
+end.

+ 66 - 0
Units/Forms/UFRMMemoText.dfm

@@ -0,0 +1,66 @@
+object FRMMemoText: TFRMMemoText
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  Caption = 'Information'
+  ClientHeight = 428
+  ClientWidth = 745
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poOwnerFormCenter
+  OnCreate = FormCreate
+  PixelsPerInch = 96
+  TextHeight = 13
+  object pnlBottom: TPanel
+    Left = 0
+    Top = 373
+    Width = 745
+    Height = 55
+    Align = alBottom
+    BevelOuter = bvNone
+    TabOrder = 0
+    ExplicitTop = 245
+    ExplicitWidth = 619
+    DesignSize = (
+      745
+      55)
+    object bbCancel: TBitBtn
+      Left = 620
+      Top = 14
+      Width = 116
+      Height = 31
+      Anchors = [akTop, akRight]
+      DoubleBuffered = True
+      Kind = bkCancel
+      ParentDoubleBuffered = False
+      TabOrder = 0
+      ExplicitLeft = 494
+    end
+  end
+  object Memo: TMemo
+    Left = 0
+    Top = 0
+    Width = 745
+    Height = 373
+    Align = alClient
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = []
+    Lines.Strings = (
+      'Memo')
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 1
+    WordWrap = False
+    ExplicitWidth = 619
+    ExplicitHeight = 245
+  end
+end

+ 58 - 0
Units/Forms/UFRMMemoText.lfm

@@ -0,0 +1,58 @@
+object FRMMemoText: TFRMMemoText
+  Left = 410
+  Height = 428
+  Top = 234
+  Width = 745
+  BorderIcons = [biSystemMenu]
+  Caption = 'Information'
+  ClientHeight = 428
+  ClientWidth = 745
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  OnCreate = FormCreate
+  Position = poOwnerFormCenter
+  LCLVersion = '1.6.0.4'
+  object pnlBottom: TPanel
+    Left = 0
+    Height = 55
+    Top = 373
+    Width = 745
+    Align = alBottom
+    BevelOuter = bvNone
+    ClientHeight = 55
+    ClientWidth = 745
+    TabOrder = 0
+    object bbCancel: TBitBtn
+      Left = 620
+      Height = 31
+      Top = 14
+      Width = 116
+      Anchors = [akTop, akRight]
+      Cancel = True
+      Caption = 'Close'
+      Kind = bkCancel
+      ModalResult = 2
+      TabOrder = 0
+    end
+  end
+  object Memo: TMemo
+    Left = 0
+    Height = 373
+    Top = 0
+    Width = 745
+    Align = alClient
+    Font.Color = clWindowText
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Lines.Strings = (
+      'Memo'
+    )
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 1
+    WordWrap = False
+  end
+end

+ 46 - 0
Units/Forms/UFRMMemoText.pas

@@ -0,0 +1,46 @@
+unit UFRMMemoText;
+
+interface
+
+uses
+{$IFnDEF FPC}
+  Windows,
+{$ELSE}
+  LCLIntf, LCLType, LMessages,
+{$ENDIF}
+  Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, Buttons, ExtCtrls;
+
+type
+  TFRMMemoText = class(TForm)
+    pnlBottom: TPanel;
+    Memo: TMemo;
+    bbCancel: TBitBtn;
+    procedure FormCreate(Sender: TObject);
+  private
+    { Private declarations }
+  public
+    { Public declarations }
+    Procedure InitData(Title : String; text : String);
+  end;
+
+implementation
+
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
+
+procedure TFRMMemoText.FormCreate(Sender: TObject);
+begin
+  Memo.Clear;
+end;
+
+procedure TFRMMemoText.InitData(Title, text: String);
+begin
+  Caption := Title;
+  Memo.Lines.Text := text;
+end;
+
+end.

+ 1 - 0
Units/Forms/UFRMNewPrivateKeyType.lfm

@@ -88,6 +88,7 @@ object FRMNewPrivateKeyType: TFRMNewPrivateKeyType
     Top = 181
     Width = 75
     Cancel = True
+    Caption = 'Cancel'
     Kind = bkCancel
     ModalResult = 2
     TabOrder = 3

+ 4 - 3
Units/Forms/UFRMNodesIp.lfm

@@ -5,7 +5,7 @@ object FRMNodesIp: TFRMNodesIp
   Width = 334
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
-  Caption = 'Nodes IP'
+  Caption = 'Node IP Addresses'
   ClientHeight = 367
   ClientWidth = 334
   Color = clBtnFace
@@ -20,7 +20,7 @@ object FRMNodesIp: TFRMNodesIp
     Height = 13
     Top = 25
     Width = 143
-    Caption = 'Available Nodes Ip to connect'
+    Caption = 'Availble Node IP''s to connect:'
     ParentColor = False
   end
   object memoNodesIp: TMemo
@@ -69,6 +69,7 @@ object FRMNodesIp: TFRMNodesIp
     Top = 317
     Width = 75
     Cancel = True
+    Caption = 'Cancel'
     Kind = bkCancel
     ModalResult = 2
     TabOrder = 2
@@ -78,7 +79,7 @@ object FRMNodesIp: TFRMNodesIp
     Height = 19
     Top = 285
     Width = 201
-    Caption = 'Try to connect ONLY with this servers'
+    Caption = 'Connect ONLY to these nodes'
     OnClick = cbTryOnlyWithThisServersClick
     TabOrder = 3
   end

File diff suppressed because it is too large
+ 741 - 252
Units/Forms/UFRMOperation.dfm


File diff suppressed because it is too large
+ 730 - 273
Units/Forms/UFRMOperation.lfm


File diff suppressed because it is too large
+ 798 - 337
Units/Forms/UFRMOperation.pas


+ 1 - 0
Units/Forms/UFRMPascalCoinWalletConfig.lfm

@@ -118,6 +118,7 @@ object FRMPascalCoinWalletConfig: TFRMPascalCoinWalletConfig
     Top = 482
     Width = 75
     Kind = bkOK
+    ModalResult = 1
     OnClick = bbOkClick
     TabOrder = 16
   end

+ 4 - 4
Units/Forms/UFRMPayloadDecoder.pas

@@ -156,7 +156,7 @@ begin
       If not TNode.Node.FindOperation(pcops,r,b,opbi) then begin
         raise Exception.Create('Value is not a valid OpHash');
       end;
-      If not TPCOperation.OperationToOperationResume(b,pcops.Operation[opbi],pcops.Operation[opbi].SenderAccount,opr) then begin
+      If not TPCOperation.OperationToOperationResume(b,pcops.Operation[opbi],pcops.Operation[opbi].SignerAccount,opr) then begin
         raise Exception.Create('Internal error 20161114-1');
       end;
       opr.NOpInsideBlock:=opbi;
@@ -284,13 +284,13 @@ begin
     if Value.Amount>0 then lblAmount.Font.Color := clGreen
     else if Value.Amount=0 then lblAmount.Font.Color := clGray
     else lblAmount.Font.Color := clRed;
-    If (Value.SenderAccount>=0) And (Value.DestAccount>=0) then begin
+    If (Value.SignerAccount>=0) And (Value.DestAccount>=0) then begin
       lblSenderCaption.Caption := 'Sender:';
-      lblSender.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Value.SenderAccount);
+      lblSender.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Value.SignerAccount);
       lblReceiverCaption.Visible := true;
       lblReceiver.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Value.DestAccount);
       lblReceiver.Visible := true;
-      lblFeeCaption.Visible := Value.AffectedAccount=Value.SenderAccount;
+      lblFeeCaption.Visible := Value.AffectedAccount=Value.SignerAccount;
       lblFee.Visible := lblFeeCaption.Visible;
       lblReceiverInfo.Visible := Not lblFee.Visible;
     end else begin

+ 220 - 0
Units/Forms/UFRMSaleAccounts.dfm

@@ -0,0 +1,220 @@
+object FRMSaleAccounts: TFRMSaleAccounts
+  Left = 0
+  Top = 0
+  BorderStyle = bsSingle
+  Caption = 'Sale accounts'
+  ClientHeight = 312
+  ClientWidth = 604
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poOwnerFormCenter
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  PixelsPerInch = 96
+  TextHeight = 13
+  object lblAccountCaption: TLabel
+    Left = 25
+    Top = 20
+    Width = 43
+    Height = 13
+    Caption = 'Account:'
+  end
+  object Label1: TLabel
+    Left = 25
+    Top = 95
+    Width = 50
+    Height = 13
+    Caption = 'Sale price:'
+  end
+  object Label2: TLabel
+    Left = 202
+    Top = 95
+    Width = 87
+    Height = 13
+    Caption = 'Locked until block:'
+  end
+  object Label3: TLabel
+    Left = 25
+    Top = 119
+    Width = 142
+    Height = 13
+    Caption = 'Seller account (where to pay)'
+  end
+  object lblAccountBalance: TLabel
+    Left = 432
+    Top = 15
+    Width = 149
+    Height = 19
+    Caption = 'lblAccountBalance'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clGreen
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object lblAccountsCount: TLabel
+    Left = 387
+    Top = 58
+    Width = 18
+    Height = 13
+    Caption = 'XXX'
+  end
+  object lblBalanceCaption: TLabel
+    Left = 387
+    Top = 20
+    Width = 41
+    Height = 13
+    Caption = 'Balance:'
+  end
+  object memoAccounts: TMemo
+    Left = 74
+    Top = 12
+    Width = 307
+    Height = 59
+    Ctl3D = False
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    Lines.Strings = (
+      '123456-11 (0.0000); 123456-11 (0.0000); '
+      '123456-11 (0.0000); 123456-11 (0.0000); ')
+    ParentCtl3D = False
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssVertical
+    TabOrder = 1
+  end
+  object ebSenderAccount: TEdit
+    Left = 74
+    Top = 12
+    Width = 97
+    Height = 27
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+    TabOrder = 0
+    Text = 'ebSenderAccount'
+  end
+  object ebSalePrice: TEdit
+    Left = 81
+    Top = 92
+    Width = 86
+    Height = 21
+    TabOrder = 2
+    Text = 'Edit1'
+  end
+  object ebLockedUntilBlock: TEdit
+    Left = 295
+    Top = 92
+    Width = 86
+    Height = 21
+    TabOrder = 3
+    Text = 'ebLockedUntilBlock'
+  end
+  object gbSellOptions: TGroupBox
+    Left = 25
+    Top = 150
+    Width = 516
+    Height = 101
+    Caption = ' Sell options: '
+    TabOrder = 4
+    object lblNewOwnerPublicKey: TLabel
+      Left = 25
+      Top = 66
+      Width = 111
+      Height = 13
+      Caption = 'New owner'#39's public key'
+    end
+    object rbEveryoneCanBuy: TRadioButton
+      Left = 10
+      Top = 20
+      Width = 113
+      Height = 17
+      Caption = 'Everyone can buy'
+      TabOrder = 0
+    end
+    object rbReservedForAPublickKey: TRadioButton
+      Left = 10
+      Top = 43
+      Width = 301
+      Height = 17
+      Caption = 'Reserved for only one public key (private)'
+      TabOrder = 1
+    end
+    object ebNewOwnerPublicKey: TEdit
+      Left = 148
+      Top = 63
+      Width = 331
+      Height = 21
+      TabOrder = 2
+      Text = 'ebDestAccount'
+    end
+  end
+  object ebSellerAccount: TEdit
+    Left = 181
+    Top = 116
+    Width = 86
+    Height = 21
+    TabOrder = 5
+    Text = 'Edit1'
+  end
+  object bbExecute: TBitBtn
+    Left = 277
+    Top = 261
+    Width = 130
+    Height = 31
+    Action = actExecute
+    Caption = 'Execute (F12)'
+    DoubleBuffered = True
+    Glyph.Data = {
+      DE010000424DDE01000000000000760000002800000024000000120000000100
+      0400000000006801000000000000000000001000000000000000000000000000
+      80000080000000808000800000008000800080800000C0C0C000808080000000
+      FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
+      3333333333333333333333330000333333333333333333333333F33333333333
+      00003333344333333333333333388F3333333333000033334224333333333333
+      338338F3333333330000333422224333333333333833338F3333333300003342
+      222224333333333383333338F3333333000034222A22224333333338F338F333
+      8F33333300003222A3A2224333333338F3838F338F33333300003A2A333A2224
+      33333338F83338F338F33333000033A33333A222433333338333338F338F3333
+      0000333333333A222433333333333338F338F33300003333333333A222433333
+      333333338F338F33000033333333333A222433333333333338F338F300003333
+      33333333A222433333333333338F338F00003333333333333A22433333333333
+      3338F38F000033333333333333A223333333333333338F830000333333333333
+      333A333333333333333338330000333333333333333333333333333333333333
+      0000}
+    NumGlyphs = 2
+    ParentDoubleBuffered = False
+    TabOrder = 6
+  end
+  object bbCancel: TBitBtn
+    Left = 425
+    Top = 261
+    Width = 116
+    Height = 31
+    DoubleBuffered = True
+    Kind = bkCancel
+    ParentDoubleBuffered = False
+    TabOrder = 7
+  end
+  object ActionList: TActionList
+    Left = 140
+    Top = 350
+    object actExecute: TAction
+      Caption = 'Execute (F12)'
+      ShortCut = 123
+      OnExecute = actExecuteExecute
+    end
+  end
+end

+ 245 - 0
Units/Forms/UFRMSaleAccounts.pas

@@ -0,0 +1,245 @@
+unit UFRMSaleAccounts;
+
+interface
+
+uses
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UAccounts, Buttons, ActnList;
+
+type
+  TFRMSaleAccounts = class(TForm)
+    lblAccountCaption: TLabel;
+    ebSenderAccount: TEdit;
+    memoAccounts: TMemo;
+    Label1: TLabel;
+    ebSalePrice: TEdit;
+    Label2: TLabel;
+    ebLockedUntilBlock: TEdit;
+    gbSellOptions: TGroupBox;
+    rbEveryoneCanBuy: TRadioButton;
+    rbReservedForAPublickKey: TRadioButton;
+    lblNewOwnerPublicKey: TLabel;
+    ebNewOwnerPublicKey: TEdit;
+    Label3: TLabel;
+    ebSellerAccount: TEdit;
+    bbExecute: TBitBtn;
+    bbCancel: TBitBtn;
+    lblAccountBalance: TLabel;
+    lblAccountsCount: TLabel;
+    lblBalanceCaption: TLabel;
+    ActionList: TActionList;
+    actExecute: TAction;
+    procedure FormCreate(Sender: TObject);
+    procedure actExecuteExecute(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FDisabled : Boolean;
+    FSenderAccounts: TOrderedCardinalList;
+    FWalletKeys: TWalletKeys;
+    FOldOnChanged : TNotifyEvent;
+    { Private declarations }
+    Procedure UpdateAccountsInfo;
+    procedure SetWalletKeys(const Value: TWalletKeys);
+  public
+    { Public declarations }
+    Property SenderAccounts : TOrderedCardinalList read FSenderAccounts;
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+  end;
+
+
+implementation
+
+uses UNode;
+
+{$R *.dfm}
+
+procedure TFRMSaleAccounts.actExecuteExecute(Sender: TObject);
+Var errors : AnsiString;
+  P : PAccount;
+  i,iAcc : Integer;
+  wk : TWalletKey;
+  ops : TOperationsHashTree;
+  op : TPCOperation;
+  account : TAccount;
+  operation_to_string, operationstxt, auxs : String;
+  _amount,_fee, _totalamount, _totalfee : Int64;
+  dooperation : Boolean;
+begin
+  if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
+  If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
+  ops := TOperationsHashTree.Create;
+  Try
+    _totalamount := 0;
+    _totalfee := 0;
+    operationstxt := '';
+    operation_to_string := '';
+    for iAcc := 0 to FSenderAccounts.Count - 1 do begin
+      op := Nil;
+      account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
+      If Not UpdatePayload(account, errors) then
+        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
+      i := WalletKeys.IndexOfAccountKey(account.accountInfo.accountKey);
+      if i<0 then begin
+        Raise Exception.Create('Sender account private key not found in Wallet');
+      end;
+
+      wk := WalletKeys.Key[i];
+      dooperation := true;
+      //
+      if rbTransaction.Checked then begin
+        // Amount
+        _amount := 0;
+        _fee := 0;
+        if FSenderAccounts.Count>1 then begin
+          if account.balance>0 then begin
+            if account.balance>fee then begin
+              _amount := account.balance - fee;
+              _fee := fee;
+            end else begin
+              _amount := account.balance;
+              _fee := 0;
+            end;
+          end else dooperation := false;
+        end else begin
+          _amount := FTxAmount;
+          _fee := fee;
+        end;
+        if dooperation then begin
+          op := TOpTransaction.CreateTransaction(account.account,account.n_operation+1,FTxDestAccount,wk.PrivateKey,_amount,_fee,FEncodedPayload);
+          inc(_totalamount,_amount);
+          inc(_totalfee,_fee);
+        end;
+        operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(FTxDestAccount);
+      end else if rbChangeKey.Checked then begin
+        i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+        if (i<0) Or (i>=WalletKeys.Count) then raise Exception.Create('Invalid selected key');
+        FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+        if account.balance>fee then _fee := fee
+        else _fee := 0;
+        op := TOpChangeKey.Create(account.account,account.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,_fee,FEncodedPayload);
+        inc(_totalfee,_fee);
+        operationstxt := 'Change private key to '+wk.Name;
+      end else if rbTransferToANewOwner.Checked then begin
+        if account.balance>fee then _fee := fee
+        else _fee := 0;
+        op := TOpChangeKey.Create(account.account,account.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,_fee,FEncodedPayload);
+        operationstxt := 'Transfer to a new owner with key type '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
+        inc(_totalfee,_fee);
+      end else begin
+        raise Exception.Create('No operation selected');
+      end;
+      if Assigned(op) And (dooperation) then begin
+        ops.AddOperationToHashTree(op);
+        if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
+        operation_to_string := operation_to_string + op.ToString;
+      end;
+      FreeAndNil(op);
+    end;
+    if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
+
+    if (FSenderAccounts.Count>1) then begin
+      if rbTransaction.Checked then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
+      else auxs:='';
+      if Application.MessageBox(PChar('Execute '+Inttostr(FSenderAccounts.Count)+' operations?'+#10+
+        'Operation: '+operationstxt+#10+
+        auxs+
+        'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
+    end else begin
+      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+operation_to_string+#10+#10+'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
+    end;
+    i := FNode.AddOperations(nil,ops,Nil,errors);
+    if (i=ops.OperationsCount) then begin
+      Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+      ModalResult := MrOk;
+    end else if (i>0) then begin
+      Application.MessageBox(PChar('One or more of your operations has not been executed:'+#10+
+        'Errors:'+#10+
+        errors+#10+#10+
+        'Total successfully executed operations: '+inttostr(i)),PChar(Application.Title),MB_OK+MB_ICONWARNING);
+      ModalResult := MrOk;
+    end else begin
+      raise Exception.Create(errors);
+    end;
+  Finally
+    ops.Free;
+  End;
+end;
+
+procedure TFRMSaleAccounts.FormCreate(Sender: TObject);
+begin
+  FSenderAccounts := TOrderedCardinalList.Create;
+  ebSenderAccount.Text := '';
+  ebSalePrice.Text := '';
+  ebLockedUntilBlock.Text := '';
+  ebSellerAccount.Text := '';
+  ebNewOwnerPublicKey.Text := '';
+  memoAccounts.lines.Clear;
+  FDisabled := false;
+
+end;
+
+procedure TFRMSaleAccounts.FormDestroy(Sender: TObject);
+begin
+  FSenderAccounts.Free;
+end;
+
+procedure TFRMSaleAccounts.SetWalletKeys(const Value: TWalletKeys);
+begin
+  if FWalletKeys=Value then exit;
+  if Assigned(FWalletKeys) then FWalletKeys.OnChanged := FOldOnChanged;
+  FWalletKeys := Value;
+  if Assigned(FWalletKeys) then begin
+    FOldOnChanged := FWalletKeys.OnChanged;
+    FWalletKeys.OnChanged := OnWalletKeysChanged;
+  end;
+  UpdateWalletKeys;
+end;
+
+procedure TFRMSaleAccounts.UpdateAccountsInfo;
+Var ld : Boolean;
+  i : Integer;
+  balance : int64;
+  acc : TAccount;
+  accountstext : String;
+begin
+  ld := FDisabled;
+  FDisabled := true;
+  Try
+    lblAccountCaption.Caption := 'Account';
+    lblAccountsCount.Visible := false;
+    lblAccountsCount.caption := inttostr(senderAccounts.Count)+' accounts';
+    balance := 0;
+    if SenderAccounts.Count<=0 then begin
+      ebSenderAccount.Text := '';
+      memoAccounts.Visible := false;
+      ebSenderAccount.Visible := true;
+    end else if SenderAccounts.Count=1 then begin
+      ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
+      memoAccounts.Visible := false;
+      ebSenderAccount.Visible := true;
+      balance := TNode.Node.Operations.SafeBoxTransaction.Account(SenderAccounts.Get(0)).balance;
+    end else begin
+      // Multiple sender accounts
+      lblAccountCaption.Caption := 'Accounts';
+      lblAccountsCount.Visible := true;
+      ebSenderAccount.Visible := false;
+      accountstext := '';
+      for i := 0 to SenderAccounts.Count - 1 do begin
+         acc := TNode.Node.Operations.SafeBoxTransaction.Account(SenderAccounts.Get(i));
+         balance := balance + acc.balance;
+         if (accountstext<>'') then accountstext:=accountstext+'; ';
+         accountstext := accountstext+TAccountComp.AccountNumberToAccountTxtNumber(acc.account)+' ('+TAccountComp.FormatMoney(acc.balance)+')';
+      end;
+      memoAccounts.Lines.Text := accountstext;
+      memoAccounts.Visible := true;
+    end;
+    ebSenderAccount.Enabled := ebSenderAccount.Visible;
+    lblAccountBalance.Caption := TAccountComp.FormatMoney(balance);
+  Finally
+    FDisabled := ld;
+  End;
+end;
+
+end.

+ 42 - 3
Units/Forms/UFRMWallet.dfm

@@ -397,6 +397,40 @@ object FRMWallet: TFRMWallet
           Height = 13
           Caption = 'Find account'
         end
+        object sbSearchAccount: TSpeedButton
+          Left = 176
+          Top = 33
+          Width = 23
+          Height = 22
+          Glyph.Data = {
+            36030000424D3603000000000000360000002800000010000000100000000100
+            18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
+            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
         object cbMyPrivateKeys: TComboBox
           Left = 260
           Top = 7
@@ -575,7 +609,7 @@ object FRMWallet: TFRMWallet
         Top = 66
         Width = 458
         Height = 338
-        ActivePage = tsMultiSelectAccounts
+        ActivePage = tsAccountOperations
         Align = alClient
         TabOrder = 2
         object tsAccountOperations: TTabSheet
@@ -1191,7 +1225,7 @@ object FRMWallet: TFRMWallet
   end
   object MainMenu: TMainMenu
     Left = 165
-    Top = 160
+    Top = 155
     object miProject: TMenuItem
       Caption = 'Project'
       object miPrivatekeys: TMenuItem
@@ -1241,6 +1275,11 @@ object FRMWallet: TFRMWallet
         ShortCut = 16454
         OnClick = MiFindaccountClick
       end
+      object MiAccountInformation: TMenuItem
+        Caption = 'Account Information'
+        ShortCut = 112
+        OnClick = MiAccountInformationClick
+      end
       object N2: TMenuItem
         Caption = '-'
       end
@@ -1285,7 +1324,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Top = 180
     Bitmap = {
-      494C010102000800F40110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010102000800100210003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 57 - 17
Units/Forms/UFRMWallet.lfm

@@ -178,8 +178,8 @@ object FRMWallet: TFRMWallet
       Left = 90
       Height = 13
       Top = 26
-      Width = 89
-      Caption = 'Current Block age:'
+      Width = 90
+      Caption = 'Current Block Age:'
       ParentColor = False
     end
     object lblCurrentBlockTime: TLabel
@@ -210,8 +210,8 @@ object FRMWallet: TFRMWallet
       Left = 90
       Height = 13
       Top = 56
-      Width = 68
-      Caption = 'Miners clients:'
+      Width = 65
+      Caption = 'Miner Clients:'
       ParentColor = False
     end
     object lblMinersClients: TLabel
@@ -302,8 +302,8 @@ object FRMWallet: TFRMWallet
       Left = 360
       Height = 13
       Top = 56
-      Width = 74
-      Caption = 'Blocks found:'
+      Width = 76
+      Caption = 'Blocks Found:'
       Font.Color = clWindowText
       Font.Height = -11
       Font.Name = 'Tahoma'
@@ -332,8 +332,8 @@ object FRMWallet: TFRMWallet
       Left = 360
       Height = 23
       Top = 66
-      Width = 185
-      Caption = 'Received messages'
+      Width = 184
+      Caption = 'Received Messages'
       Font.Color = clRed
       Font.Height = -19
       Font.Name = 'Tahoma'
@@ -388,7 +388,7 @@ object FRMWallet: TFRMWallet
     TabOrder = 2
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
-      Caption = 'Accounts Explorer'
+      Caption = 'Account Explorer'
       ClientHeight = 440
       ClientWidth = 857
       object Splitter1: TSplitter
@@ -481,6 +481,41 @@ object FRMWallet: TFRMWallet
           OnKeyPress = ebFilterAccountByBalanceMinKeyPress
           TabOrder = 6
         end
+        object sbSearchAccount: TSpeedButton
+          Left = 176
+          Height = 22
+          Top = 32
+          Width = 23
+          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
@@ -859,7 +894,7 @@ object FRMWallet: TFRMWallet
       end
     end
     object tsBlockChain: TTabSheet
-      Caption = 'BlockChain Explorer'
+      Caption = 'Block Explorer'
       ClientHeight = 440
       ClientWidth = 857
       ImageIndex = 1
@@ -988,7 +1023,7 @@ object FRMWallet: TFRMWallet
           Height = 19
           Top = 10
           Width = 102
-          Caption = 'Show Debug logs'
+          Caption = 'Show Debug Logs'
           TabOrder = 0
         end
       end
@@ -1013,7 +1048,7 @@ object FRMWallet: TFRMWallet
         Height = 13
         Top = 15
         Width = 177
-        Caption = 'Active Net Connections of this Node:'
+        Caption = 'Active Connections:'
         ParentColor = False
       end
       object Label6: TLabel
@@ -1022,7 +1057,7 @@ object FRMWallet: TFRMWallet
         Top = 291
         Width = 198
         Anchors = [akLeft, akRight, akBottom]
-        Caption = 'Available or possible Node Servers:'
+        Caption = 'Known Node Servers:'
         ParentColor = False
       end
       object Label7: TLabel
@@ -1031,7 +1066,7 @@ object FRMWallet: TFRMWallet
         Top = 187
         Width = 99
         Anchors = [akLeft, akRight, akBottom]
-        Caption = 'BlackList of Nodes'
+        Caption = 'Blacklisted Nodes'
         ParentColor = False
       end
       object memoNetConnections: TMemo
@@ -1094,7 +1129,7 @@ object FRMWallet: TFRMWallet
         Height = 13
         Top = 11
         Width = 107
-        Caption = 'Available connections:'
+        Caption = 'Available Connections:'
         ParentColor = False
       end
       object Label14: TLabel
@@ -1176,7 +1211,7 @@ object FRMWallet: TFRMWallet
     object miProject: TMenuItem
       Caption = 'Project'
       object miPrivatekeys: TMenuItem
-        Caption = 'Private keys'
+        Caption = 'Private Keys'
         ShortCut = 16464
         OnClick = miPrivatekeysClick
       end
@@ -1189,7 +1224,7 @@ object FRMWallet: TFRMWallet
         OnClick = miOptionsClick
       end
       object IPnodes1: TMenuItem
-        Caption = 'IP nodes'
+        Caption = 'Available Node List'
         OnClick = IPnodes1Click
       end
       object N1: TMenuItem
@@ -1222,6 +1257,11 @@ object FRMWallet: TFRMWallet
         ShortCut = 16454
         OnClick = MiFindaccountClick
       end
+      object MiAccountInformation: TMenuItem
+        Caption = 'Account Information'
+        ShortCut = 112
+        OnClick = MiAccountInformationClick
+      end
       object N2: TMenuItem
         Caption = '-'
       end

+ 173 - 14
Units/Forms/UFRMWallet.pas

@@ -19,17 +19,19 @@ unit UFRMWallet;
 
 interface
 
+{$I ./../PascalCoin/config.inc}
+
 uses
 {$IFnDEF FPC}
-  pngimage, ADODB, Windows, AppEvnts, ShlObj,
+  pngimage, Windows, AppEvnts, ShlObj,
 {$ELSE}
-  sqldb, LCLIntf, LCLType, LMessages,
+  LCLIntf, LCLType, LMessages,
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, ExtCtrls, ComCtrls, UWalletKeys, StdCtrls,
   ULog, Grids, UAppParams,
   UBlockChain, UNode, UGridUtils, UAccounts, Menus, ImgList,
-  UNetProtocol, UCrypto, Buttons, UPoolMining, URPC;
+  UNetProtocol, UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect;
 
 Const
   CM_PC_WalletKeysChanged = WM_USER + 1;
@@ -43,6 +45,7 @@ type
   TFRMWallet = class(TForm)
     pnlTop: TPanel;
     Image1: TImage;
+    sbSearchAccount: TSpeedButton;
     StatusBar: TStatusBar;
     PageControl: TPageControl;
     tsMyAccounts: TTabSheet;
@@ -165,8 +168,10 @@ type
     dgBlockChainExplorer: TDrawGrid;
     dgOperationsExplorer: TDrawGrid;
     MiFindOperationbyOpHash: TMenuItem;
+    MiAccountInformation: TMenuItem;
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
+    procedure sbSearchAccountClick(Sender: TObject);
     procedure TimerUpdateStatusTimer(Sender: TObject);
     procedure cbMyPrivateKeysChange(Sender: TObject);
     procedure dgAccountsClick(Sender: TObject);
@@ -210,11 +215,15 @@ type
       var Key: Char);
     procedure cbFilterAccountsClick(Sender: TObject);
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
+    procedure MiAccountInformationClick(Sender: TObject);
   private
+    FBackgroundPanel : TPanel;
     FMinersBlocksFound: Integer;
     procedure SetMinersBlocksFound(const Value: Integer);
     Procedure CheckIsReady;
     Procedure FinishedLoadingApp;
+    Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
+    Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
   protected
     { Private declarations }
     FNode : TNode;
@@ -286,7 +295,7 @@ implementation
 
 Uses UFolderHelper, UOpenSSL, UOpenSSLdef, UConst, UTime, UFileStorage,
   UThread, UOpTransaction, UECIES, UFRMPascalCoinWalletConfig,
-  UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp;
+  UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText;
 
 Type
   TThreadActivate = Class(TPCThread)
@@ -425,7 +434,7 @@ begin
     finally
       FSelectedAccountsGrid.UnlockAccountsList;
     end;
-    Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     WalletKeys := FWalletKeys;
     ShowModal;
   Finally
@@ -504,7 +513,7 @@ Var isready : AnsiString;
 begin
   if Not Assigned(FNode) then Abort;
 
-  if Not FNode.Node.IsReady(isready) then begin
+  if Not FNode.IsReady(isready) then begin
     Raise Exception.Create('You cannot do this operation now:'+#10+#10+isready);
   end;
 end;
@@ -618,7 +627,7 @@ begin
   UpdateAccounts(true);
 end;
 
-Function TFRMWallet.DoUpdateAccountsFilter : Boolean;
+function TFRMWallet.DoUpdateAccountsFilter: Boolean;
 Var m,bmin,bmax:Int64;
   doupd : Boolean;
 begin
@@ -741,6 +750,75 @@ begin
   FNode.Operations.AccountKey := GetAccountKeyForMiner;
   FPoolMiningServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
   FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
+  If Assigned(FBackgroundPanel) then begin
+    FreeAndNil(FBackgroundPanel);
+  end;
+  PageControl.Visible:=True;
+  PageControl.Enabled:=True;
+end;
+
+procedure TFRMWallet.FillAccountInformation(const Strings: TStrings; Const AccountNumber: Cardinal);
+Var account : TAccount;
+  s : String;
+begin
+  if AccountNumber<0 then exit;
+  account := FNode.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,FNode.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,FNode.Bank.BlocksCount) then begin
+        Strings.Add(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount,account.accountInfo.locked_until_block-FNode.Bank.BlocksCount]));
+      end else begin
+        Strings.Add(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
+      end;
+    end;
+  end;
+end;
+
+procedure TFRMWallet.FillOperationInformation(const Strings: TStrings; Const OperationResume: TOperationResume);
+begin
+  If (not OperationResume.valid) then exit;
+  If OperationResume.Block<FNode.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;
 
 function TFRMWallet.ForceMining: Boolean;
@@ -751,6 +829,7 @@ end;
 procedure TFRMWallet.FormCreate(Sender: TObject);
 Var i : Integer;
 begin
+  FBackgroundPanel := Nil;
   FMustProcessWalletChanged := false;
   FMustProcessNetConnectionUpdated := false;
   FRPCServer := Nil;
@@ -784,7 +863,7 @@ begin
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid.DrawGrid := dgAccounts;
-  FAccountsGrid.AllowMultiSelect := true;
+  FAccountsGrid.AllowMultiSelect := True;
   FSelectedAccountsGrid := TAccountsGrid.Create(Self);
   FSelectedAccountsGrid.DrawGrid := dgSelectedAccounts;
   FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
@@ -820,6 +899,15 @@ begin
   TrayIcon.BalloonFlags := bfInfo;
   MinersBlocksFound := 0;
   lblBuild.Caption := 'Build: '+CT_ClientAppVersion;
+  {$IFDEF TESTNET}
+  Image1.visible := false;
+  {$ENDIF}
+  PageControl.Enabled := False;
+  PageControl.Visible := False;
+  FBackgroundPanel := TPanel.Create(Self);
+  FBackgroundPanel.Parent:=Self;
+  FBackgroundPanel.Align:=alClient;
+  FBackgroundPanel.Font.Size:=15;
 end;
 
 procedure TFRMWallet.FormDestroy(Sender: TObject);
@@ -891,6 +979,19 @@ begin
   Sleep(100);
 end;
 
+procedure TFRMWallet.sbSearchAccountClick(Sender: TObject);
+Var F : TFRMAccountSelect;
+begin
+  F := TFRMAccountSelect.Create(Self);
+  try
+    F.Node := FNode;
+    F.WalletKeys := FWalletKeys;
+    F.ShowModal;
+  finally
+    F.Free;
+  end;
+end;
+
 function TFRMWallet.GetAccountKeyForMiner: TAccountKey;
 Var PK : TECPrivateKey;
   i : Integer;
@@ -953,7 +1054,7 @@ begin
     s := FAppParams.ParamByName[CT_PARAM_GridAccountsStream].GetAsString('');
     ms.WriteBuffer(s[1],length(s));
     ms.Position := 0;
-    FAccountsGrid.LoadFromStream(ms);
+    // Disabled on V2: FAccountsGrid.LoadFromStream(ms);
   Finally
     ms.Free;
   End;
@@ -963,6 +1064,7 @@ begin
     FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+
       fvi.InternalName+' Build:'+fvi.FileVersion);
   end;
+  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
   UpdateConfigChanged;
 end;
 
@@ -976,6 +1078,59 @@ begin
   end;
 end;
 
+procedure TFRMWallet.MiAccountInformationClick(Sender: TObject);
+Var F : TFRMMemoText;
+  accn : Int64;
+  s,title : String;
+  account : TAccount;
+  strings : TStrings;
+  i : Integer;
+  opr : TOperationResume;
+begin
+  accn := -1;
+  title := '';
+  strings := TStringList.Create;
+  try
+    opr := CT_TOperationResume_NUL;
+    if PageControl.ActivePage=tsOperations then begin
+      i := FOperationsExplorerGrid.DrawGrid.Row;
+      if (i>0) and (i<=FOperationsExplorerGrid.OperationsResume.Count) then begin
+        opr := FOperationsExplorerGrid.OperationsResume.OperationResume[i-1];
+      end;
+    end else if PageControl.ActivePage=tsPendingOperations 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;
+
 procedure TFRMWallet.MiAddaccounttoSelectedClick(Sender: TObject);
 begin
   PageControl.ActivePage := tsMyAccounts;
@@ -1086,7 +1241,7 @@ begin
     finally
       l.Free;
     end;
-    Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     WalletKeys := FWalletKeys;
     ShowModal;
   Finally
@@ -1386,7 +1541,7 @@ begin
     ltarget := FSelectedAccountsGrid.LockAccountsList;
     Try
       for i := 0 to lsource.Count-1 do begin
-        if FWalletKeys.IndexOfAccountKey(FNode.Bank.SafeBox.Account(lsource.Get(i)).accountkey)<0 then raise Exception.Create(Format('You cannot operate with account %d because private key not found in your wallet',[lsource.Get(i)]));
+        if FWalletKeys.IndexOfAccountKey(FNode.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
@@ -1404,7 +1559,7 @@ Var l, selected : TOrderedCardinalList;
 begin
   an := FAccountsGrid.AccountNumber(dgAccounts.Row);
   if (an<0) then raise Exception.Create('No account selected');
-  if FWalletKeys.IndexOfAccountKey(FNode.Bank.SafeBox.Account(an).accountkey)<0 then
+  if FWalletKeys.IndexOfAccountKey(FNode.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
@@ -1412,12 +1567,12 @@ begin
   selected := TOrderedCardinalList.Create;
   Try
     FAccountsGrid.SelectedAccounts(selected);
-    for i := 0 to selected.Count - 1 do begin
+    for i := 0 to selected.Count-1 do begin
       l.Add(selected.Get(i));
     end;
   Finally
-    FSelectedAccountsGrid.UnlockAccountsList;
     selected.Free;
+    FSelectedAccountsGrid.UnlockAccountsList;
   End;
 end;
 
@@ -1723,6 +1878,10 @@ begin
       lblNodeStatus.Caption := status;
     end;
   end;
+  If Assigned(FBackgroundPanel) then begin
+    FBackgroundPanel.Font.Color:=lblNodeStatus.Font.Color;
+    FBackgroundPanel.Caption:='Please wait until finished: '+lblNodeStatus.Caption;
+  end;
 end;
 
 procedure TFRMWallet.UpdateOperations;

+ 6 - 7
Units/Forms/UFRMWalletKeys.lfm

@@ -14,12 +14,12 @@ object FRMWalletKeys: TFRMWalletKeys
   Font.Name = 'Tahoma'
   OnCreate = FormCreate
   Position = poOwnerFormCenter
-  LCLVersion = '1.6.0.4'
+  LCLVersion = '1.6.4.0'
   object lblEncryptionTypeCaption: TLabel
     Left = 30
     Height = 13
     Top = 298
-    Width = 93
+    Width = 80
     Caption = 'Encryption type:'
     ParentColor = False
   end
@@ -27,7 +27,7 @@ object FRMWalletKeys: TFRMWalletKeys
     Left = 125
     Height = 13
     Top = 298
-    Width = 63
+    Width = 54
     Caption = '000000000'
     ParentColor = False
   end
@@ -35,7 +35,7 @@ object FRMWalletKeys: TFRMWalletKeys
     Left = 30
     Height = 13
     Top = 317
-    Width = 60
+    Width = 51
     Caption = 'Key name:'
     ParentColor = False
   end
@@ -52,7 +52,7 @@ object FRMWalletKeys: TFRMWalletKeys
     Left = 30
     Height = 13
     Top = 336
-    Width = 67
+    Width = 58
     Caption = 'Private key:'
     ParentColor = False
   end
@@ -60,7 +60,7 @@ object FRMWalletKeys: TFRMWalletKeys
     Left = 30
     Height = 13
     Top = 355
-    Width = 92
+    Width = 80
     Caption = '(In hexa format)'
     ParentColor = False
   end
@@ -88,7 +88,6 @@ object FRMWalletKeys: TFRMWalletKeys
     OnClick = lbWalletKeysClick
     ScrollWidth = 344
     TabOrder = 0
-    TopIndex = -1
   end
   object bbExportPrivateKey: TBitBtn
     Left = 382

+ 81 - 38
Units/Forms/UFRMWalletKeys.pas

@@ -23,7 +23,7 @@ uses
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, UWalletKeys, Buttons,
   {$IFDEF FPC}LMessages,{$ENDIF}
-  clipbrd;
+  clipbrd, UConst;
 
 Const
   CM_PC_WalletKeysChanged = {$IFDEF FPC}LM_USER{$ELSE}WM_USER{$ENDIF} + 1;
@@ -272,49 +272,92 @@ var s : String;
  EC : TECPrivateKey;
  i : Integer;
  wk : TWalletKey;
-begin
-  CheckIsWalletKeyValidPassword;
-  if Not Assigned(WalletKeys) then exit;
-  if InputQuery('Import private key','Insert the password protected private key',s) then begin
-    if (trim(s)='') then raise Exception.Create('No valid key');
-    enc := TCrypto.HexaToRaw(trim(s));
-    if (enc='') then raise Exception.Create('Invalid text... You must enter an hexadecimal value ("0".."9" or "A".."F"');
-    Repeat
-      s := '';
-      desenc := '';
-      if InputQuery('Import private key','Enter the password:',s) then begin
-        If (TAESComp.EVP_Decrypt_AES256(enc,s,desenc)) then begin
-          if (desenc<>'') then begin
-            EC := TECPrivateKey.ImportFromRaw(desenc);
-            Try
-              if Not Assigned(EC) then begin
-                if Application.MessageBox(PChar('Invalid password or corrupted data!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then exit;
-                desenc := '';
-              end else begin
-                i := WalletKeys.IndexOfAccountKey(EC.PublicKey);
-                if (i>=0) then begin
-                  wk := WalletKeys.Key[i];
-                  if Assigned(wk.PrivateKey) And (Assigned(wk.PrivateKey.PrivateKey)) then raise Exception.Create('This key is already in your wallet!');
-                end;
-                s := 'Imported '+DateTimeToStr(now);
-                s := InputBox('Set a name','Name for this private key:',s);
-                i := WalletKeys.AddPrivateKey(s,EC);
-                Application.MessageBox(PChar('Imported private key'),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+ parseResult : Boolean;
+
+  function ParseRawKey(EC_OpenSSL_NID : Word) : boolean;
+  begin
+    FreeAndNil(EC); ParseRawKey := False;
+    EC := TECPrivateKey.Create;
+    Try
+      EC.SetPrivateKeyFromHexa(EC_OpenSSL_NID, TCrypto.ToHexaString(enc));
+      ParseRawKey := True;
+    Except
+      On E:Exception do begin
+        FreeAndNil(EC);
+        Raise;
+      end;
+    end;
+  end;
+
+  function ParseEncryptedKey : boolean;
+  begin
+      Repeat
+        s := '';
+        desenc := '';
+        if InputQuery('Import private key','Enter the password:',s) then begin
+          If (TAESComp.EVP_Decrypt_AES256(enc,s,desenc)) then begin
+            if (desenc<>'') then begin
+              EC := TECPrivateKey.ImportFromRaw(desenc);
+              ParseEncryptedKey := True;
+              Exit;
+            end else begin
+              if Application.MessageBox(PChar('Invalid password!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then begin
+                ParseEncryptedKey := False;
+                Exit;
               end;
-            Finally
-              EC.Free;
-            End;
+              desenc := '';
+            end;
           end else begin
-            if Application.MessageBox(PChar('Invalid password!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then exit;
-            desenc := '';
+            if Application.MessageBox(PChar('Invalid password or corrupted data!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then begin
+              ParseEncryptedKey := False;
+              Exit;
+            end;
           end;
         end else begin
-          if Application.MessageBox(PChar('Invalid password or corrupted data!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then exit;
+          ParseEncryptedKey := false;
+          Exit;
         end;
-      end else exit;
-    Until (desenc<>'');
-    UpdateWalletKeys;
+      Until (desenc<>'');
+  end;
 
+begin
+  EC := Nil;
+  CheckIsWalletKeyValidPassword;
+  if Not Assigned(WalletKeys) then exit;
+  if InputQuery('Import private key','Insert the password protected private key or raw private key',s) then begin
+    s := trim(s);
+    if (s='') then raise Exception.Create('No valid key');
+    enc := TCrypto.HexaToRaw(s);
+    if (enc='') then raise Exception.Create('Invalid text... You must enter an hexadecimal value ("0".."9" or "A".."F")');
+    case Length(enc) of
+         32: parseResult := ParseRawKey(CT_NID_secp256k1);
+         35,36: parseResult := ParseRawKey(CT_NID_sect283k1);
+         48: parseResult := ParseRawKey(CT_NID_secp384r1);
+         65,66: parseResult := ParseRawKey(CT_NID_secp521r1);
+         64, 80, 96: parseResult := ParseEncryptedKey;
+         else Exception.Create('Invalidly formatted private key string. Ensure it is an encrypted private key export or raw private key hexstring.');
+    end;
+    if (parseResult = False) then
+       exit;
+    Try
+      // EC is assigned by ParseRawKey/ImportEncryptedKey
+      if Not Assigned(EC) then begin
+        Application.MessageBox(PChar('Invalid password and/or corrupted data!'),'', MB_OK);
+        exit;
+      end;
+      i := WalletKeys.IndexOfAccountKey(EC.PublicKey);
+      if (i>=0) then begin
+        wk := WalletKeys.Key[i];
+        if Assigned(wk.PrivateKey) And (Assigned(wk.PrivateKey.PrivateKey)) then raise Exception.Create('This key is already in your wallet!');
+      end;
+      s := 'Imported '+DateTimeToStr(now);
+      s := InputBox('Set a name','Name for this private key:',s);
+      i := WalletKeys.AddPrivateKey(s,EC);
+      Application.MessageBox(PChar('Imported private key'),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+    Finally
+      EC.Free;
+    End;
+    UpdateWalletKeys;
   end;
 end;
 

File diff suppressed because it is too large
+ 639 - 122
Units/PascalCoin/UAccounts.pas


+ 245 - 0
Units/PascalCoin/UBaseTypes.pas

@@ -0,0 +1,245 @@
+unit UBaseTypes;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{ Copyright (c) 2017 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  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
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+uses
+  Classes, SysUtils;
+
+Type
+  // Raw data in a maximum 65k bytes
+  TDynRawBytes = Array of Byte;
+  // Raw data in a maximum 256 bytes
+  T256RawBytes = TDynRawBytes;
+
+  // Fixed 32 bytes length (or empty)
+  T32Bytes = Array[0..31] of byte;
+
+  TRawBytes = AnsiString;
+
+  { TBaseType }
+
+  TBaseType = Class
+  public
+    class procedure T32BytesToRawBytes(const source : T32Bytes; var dest : TDynRawBytes); overload;
+    class function T32BytesToRawBytes(const source : T32Bytes) : TDynRawBytes; overload;
+    class function TRawBytesTo32Left0Padded(const source : TDynRawBytes) : T32Bytes;
+    class function Copy(const source : T32bytes; start, length : Integer) : ShortString; overload;
+    class function Copy(const source : T256RawBytes; var dest : T256RawBytes) : ShortString; overload;
+    class function To256RawBytes(const source : TRawBytes) : T256RawBytes; overload;
+    class procedure To256RawBytes(const source : TRawBytes; var dest : T256RawBytes); overload;
+    class function ToRawBytes(const source : T256RawBytes) : TRawBytes; overload;
+    class procedure ToRawBytes(const source : T256RawBytes; var dest: TRawBytes); overload;
+    class function ToRawBytes(const source : T32Bytes) : TRawBytes; overload;
+    class procedure ToRawBytes(const source : T32Bytes; var dest: TRawBytes); overload;
+    class function To32Bytes(const source : TRawBytes) : T32Bytes; overload;
+    class procedure To32Bytes(const source : TRawBytes; var dest: T32Bytes); overload;
+    class procedure Fill0(var dest : T32Bytes);
+    class function IsEmpty(const value : T32Bytes) : Boolean;
+    class procedure Concat(const addBytes : T32Bytes; var target : TDynRawBytes); overload;
+    class procedure Concat(const leftBytes,rightBytes : T32Bytes; var target : TDynRawBytes); overload;
+    class function Equals(const v1,v2 : T32Bytes) : Boolean; overload;
+    class function Equals(const v1,v2 : TDynRawBytes) : Boolean; overload;
+    class function Higher(const vHigh,vLow : T32Bytes) : Boolean;
+    class function Compare(const leftBytes,rightBytes : T32Bytes) : Integer;
+  end;
+
+implementation
+
+{ TBaseType }
+
+{$IFnDEF FPC}
+procedure FillByte(var X; count : Integer; value : Byte);
+begin
+  FillChar(X,count,value);
+end;
+{$ENDIF}
+
+class procedure TBaseType.T32BytesToRawBytes(const source: T32Bytes; var dest: TDynRawBytes);
+begin
+  SetLength(dest,32);
+  Move(source[0],dest[0],32);
+end;
+
+class function TBaseType.T32BytesToRawBytes(const source: T32Bytes): TDynRawBytes;
+begin
+  T32BytesToRawBytes(source,Result);
+end;
+
+class function TBaseType.TRawBytesTo32Left0Padded(const source: TDynRawBytes): T32Bytes;
+var i : Integer;
+begin
+  FillByte(Result,32,0);
+  i := 0;
+  while (i<32) And (i<=high(source)) do begin
+    Result[i+32-length(source)] := source[i];
+    inc(i);
+  end;
+end;
+
+class function TBaseType.Copy(const source: T32bytes; start, length: Integer): ShortString;
+begin
+  if (length+start)>32 then raise Exception.Create('ERROR DEV 20170601-1');
+  SetLength(Result,length);
+  move(source[start],Result[1],length);
+end;
+
+class function TBaseType.Copy(const source: T256RawBytes; var dest: T256RawBytes): ShortString;
+var i : Integer;
+begin
+  SetLength(dest,length(source));
+  for i:=0 to high(dest) do begin
+    dest[i] := source[i];
+  end;
+end;
+
+class function TBaseType.To256RawBytes(const source: TRawBytes): T256RawBytes;
+begin
+  SetLength(Result,length(source));
+  move(source[1],Result[0],length(source));
+end;
+
+class procedure TBaseType.To256RawBytes(const source: TRawBytes; var dest: T256RawBytes);
+begin
+  SetLength(dest,length(source));
+  move(source[1],dest[0],length(source));
+end;
+
+class function TBaseType.ToRawBytes(const source: T256RawBytes): TRawBytes;
+begin
+  SetLength(Result,length(source));
+  move(source[0],Result[1],length(source));
+end;
+
+class procedure TBaseType.ToRawBytes(const source: T256RawBytes; var dest: TRawBytes);
+begin
+  SetLength(dest,length(source));
+  move(source[0],dest[1],length(source));
+end;
+
+class function TBaseType.ToRawBytes(const source: T32Bytes): TRawBytes;
+begin
+  SetLength(Result,length(source));
+  move(source[0],Result[1],length(source));
+end;
+
+class procedure TBaseType.ToRawBytes(const source: T32Bytes; var dest: TRawBytes);
+begin
+  SetLength(dest,length(source));
+  move(source[0],dest[1],length(source));
+end;
+
+class function TBaseType.To32Bytes(const source: TRawBytes): T32Bytes;
+begin
+  To32Bytes(source,Result);
+end;
+
+class procedure TBaseType.To32Bytes(const source: TRawBytes; var dest: T32Bytes);
+var i : Integer;
+begin
+  FillByte(dest[0],32,0);
+  i := length(source);
+  if (i>32) then i:=32;
+  move(source[1],dest[0],i);
+end;
+
+class procedure TBaseType.Fill0(var dest: T32Bytes);
+begin
+  FillByte(dest[0],32,0);
+end;
+
+class function TBaseType.IsEmpty(const value: T32Bytes): Boolean;
+Var i : Integer;
+begin
+  For i:=0 to 31 do begin
+    if value[i]<>0 then begin
+      Result := False;
+      Exit;
+    end;
+  end;
+  Result := True;
+end;
+
+class procedure TBaseType.Concat(const addBytes: T32Bytes; var target: TDynRawBytes);
+begin
+  SetLength(target,length(target)+32);
+  move(addBytes,target[length(target)-32],32);
+end;
+
+class procedure TBaseType.Concat(const leftBytes, rightBytes: T32Bytes; var target: TDynRawBytes);
+begin
+  SetLength(target,64);
+  move(leftBytes,target[0],32);
+  move(rightBytes,target[32],32);
+end;
+
+class function TBaseType.Equals(const v1, v2: T32Bytes) : Boolean;
+Var i : Integer;
+begin
+  for i:=0 to 31 do begin
+    If v1[i]<>v2[i] then begin
+      Result := False;
+      Exit;
+    end;
+  end;
+  Result := True;
+end;
+
+class function TBaseType.Equals(const v1, v2: TDynRawBytes): Boolean;
+Var i : Integer;
+begin
+  If Length(v1)<>Length(v2) then begin
+    Result := False;
+    Exit;
+  end;
+  for i:=0 to high(v1) do begin
+    If v1[i]<>v2[i] then begin
+      Result := False;
+      Exit;
+    end;
+  end;
+  Result := True;
+end;
+
+class function TBaseType.Higher(const vHigh, vLow: T32Bytes): Boolean;
+Var i : Integer;
+begin
+  for i:=0 to 31 do begin
+    If vHigh[i]<vLow[i] then begin
+      Result := False;
+      Exit;
+    end else if vHigh[i]>vLow[i] then begin
+      Result := True;
+      Exit;
+    end;
+  end;
+  Result := False; // No higher, equal
+end;
+
+class function TBaseType.Compare(const leftBytes, rightBytes: T32Bytes): Integer;
+var i : Integer;
+begin
+  for i:=0 to 31 do begin
+    Result := leftBytes[i] - rightBytes[i];
+    if Result<>0 then exit;
+  end;
+end;
+
+end.
+

File diff suppressed because it is too large
+ 193 - 400
Units/PascalCoin/UBlockChain.pas


+ 169 - 0
Units/PascalCoin/UChunk.pas

@@ -0,0 +1,169 @@
+unit UChunk;
+
+{$IFDEF FPC}
+  {$mode delphi}
+{$ENDIF}
+
+{ Copyright (c) 2016 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  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
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+uses
+  Classes, SysUtils,  ZLib, {$IFDEF FPC} zStream, {$ENDIF}
+  UAccounts, ULog, UConst, UCrypto;
+
+type
+
+  { TPCChunk }
+
+  TPCChunk = Class
+  private
+  public
+    class function SaveSafeBoxChunkFromSafeBox(SafeBoxStream, DestStream : TStream; fromBlock, toBlock : Cardinal; var errors : AnsiString) : Boolean;
+    class function LoadSafeBoxFromChunk(Chunk, DestStream : TStream; var safeBoxHeader : TPCSafeBoxHeader; var errors : AnsiString) : Boolean;
+  end;
+
+implementation
+
+{ TPCChunk }
+
+class function TPCChunk.SaveSafeBoxChunkFromSafeBox(SafeBoxStream, DestStream : TStream; fromBlock, toBlock: Cardinal; var errors : AnsiString) : Boolean;
+Var
+  c: Cardinal;
+  cs : Tcompressionstream;
+  auxStream : TStream;
+  iPosSize, iAux : Int64;
+  initialSbPos : Int64;
+  sbHeader : TPCSafeBoxHeader;
+begin
+  Result := false; errors := '';
+  // Chunk struct:
+  // - Header:
+  //   - Magic value  (fixed AnsiString)
+  //   - SafeBox version (2 bytes)
+  //   - Uncompressed size (4 bytes)
+  //   - Compressed size (4 bytes)
+  // - Data:
+  //   - Compressed data using ZLib
+  initialSbPos :=SafeBoxStream.Position;
+  Try
+    If Not TPCSafeBox.LoadSafeBoxStreamHeader(SafeBoxStream,sbHeader) then begin
+      errors := 'SafeBoxStream is not a valid SafeBox!';
+      exit;
+    end;
+    If (sbHeader.startBlock>fromBlock) Or (sbHeader.endBlock<ToBlock) Or (fromBlock>toBlock) then begin
+      errors := Format('Cannot save a chunk from %d to %d on a stream with %d to %d!',[fromBlock,toBlock,sbHeader.startBlock,sbHeader.endBlock]);
+      exit;
+    end;
+    TLog.NewLog(ltDebug,ClassName,Format('Saving safebox chunk from %d to %d (current blockscount: %d)',[FromBlock,ToBlock,sbHeader.blocksCount]));
+
+    // Header:
+    TStreamOp.WriteAnsiString(DestStream,CT_SafeBoxChunkIdentificator);
+    DestStream.Write(CT_SafeBoxBankVersion,SizeOf(CT_SafeBoxBankVersion));
+    //
+    auxStream := TMemoryStream.Create;
+    try
+      SafeBoxStream.Position:=initialSbPos;
+      If Not TPCSafeBox.CopySafeBoxStream(SafeBoxStream,auxStream,fromBlock,toBlock,errors) then exit;
+      auxStream.Position:=0;
+      // Save uncompressed size
+      c := auxStream.Size;
+      DestStream.Write(c,SizeOf(c));
+      // Save compressed size ... later
+      iPosSize := DestStream.Position;
+      c := $FFFFFFFF;
+      DestStream.Write(c,SizeOf(c)); // Save 4 random bytes, latter will be changed
+      //
+      // Zip it and add to Stream
+      cs := Tcompressionstream.create(cldefault,DestStream);
+      try
+        cs.CopyFrom(auxStream,auxStream.Size); // compressing
+      finally
+        cs.Free;
+      end;
+    finally
+      auxStream.Free;
+    end;
+    //
+    iAux := DestStream.Position;
+    c := DestStream.Position - iPosSize - 4; // Save data size
+    DestStream.Position:=iPosSize;
+    DestStream.Write(c,SizeOf(c));
+    DestStream.Position := iAux; // Back to last position
+    Result := True; errors := '';
+  finally
+    SafeBoxStream.Position:=initialSbPos;
+  end;
+end;
+
+class function TPCChunk.LoadSafeBoxFromChunk(Chunk, DestStream: TStream; var safeBoxHeader : TPCSafeBoxHeader; var errors: AnsiString): Boolean;
+var s : AnsiString;
+  w : Word;
+  cUncompressed, cCompressed : Cardinal;
+  ds : Tdecompressionstream;
+  dbuff : Array[1..2048] of byte;
+  r : Integer;
+  destInitialPos, auxPos : Int64;
+begin
+  Result := false;
+  safeBoxHeader := CT_PCSafeBoxHeader_NUL;
+  // Header:
+  errors := 'Invalid stream header';
+  TStreamOp.ReadAnsiString(Chunk,s);
+  If (s<>CT_SafeBoxChunkIdentificator) then begin
+    exit;
+  end;
+  Chunk.Read(w,sizeof(w));
+  if (w<>CT_SafeBoxBankVersion) then begin
+    errors := errors + ' Invalid version '+IntToStr(w);
+    exit;
+  end;
+  // Size
+  Chunk.Read(cUncompressed,SizeOf(cUncompressed)); // Uncompressed size
+  Chunk.Read(cCompressed,SizeOf(cCompressed)); // Compressed size
+  if (Chunk.Size - Chunk.Position < cCompressed) then begin
+    errors := Format('Not enough LZip bytes Stream.size:%d Stream.position:%d (avail %d) LZipSize:%d',[Chunk.Size,Chunk.Position,Chunk.Size - Chunk.Position,cCompressed]);
+    exit;
+  end;
+  //
+  destInitialPos:=DestStream.Position;
+  ds := Tdecompressionstream.create(Chunk);
+  try
+    repeat
+      r := ds.read(dbuff,SizeOf(dbuff));
+      if (r>0) then begin
+        DestStream.Write(dbuff,r);
+      end;
+    until r < SizeOf(dbuff);
+    //auxStream.CopyFrom(Stream,cCompressed);
+  finally
+    ds.Free;
+  end;
+  If (DestStream.Size-destInitialPos)<>cUncompressed then begin
+    errors := Format('Uncompressed size:%d <> saved:%d',[(DestStream.Size-destInitialPos),cUncompressed]);
+    exit;
+  end;
+
+  auxPos := DestStream.Position;
+  DestStream.Position:=destInitialPos;
+  If Not TPCSafeBox.LoadSafeBoxStreamHeader(DestStream,safeBoxHeader) then begin
+    errors:= 'Invalid extracted stream!';
+    exit;
+  end;
+  DestStream.Position:=auxPos;
+  Result := true;
+end;
+
+end.
+

+ 50 - 15
Units/PascalCoin/UConst.pas

@@ -19,10 +19,8 @@ unit UConst;
 
 interface
 
-Uses UOpenSSLdef;
 {$I config.inc}
 
-
 {$IFNDEF FPC}
   // See http://wiki.freepascal.org/Code_Conversion_Guide
 type
@@ -57,14 +55,17 @@ Const
   CT_WaitNewBlocksBeforeTransaction = 100;
 
   CT_RecoverFoundsWaitInactiveCount = 420480;  // After 4 years... if an account has no operations, money will be a reward for a miner!
+  CT_MaxFutureBlocksLockedAccount = 105120; // Maximum future blocks an account can be locked
 
-  CT_MaxTransactionAmount = 1000000000000;
+  CT_MaxTransactionAmount = 1000000000000; // ... can be deleted
   CT_MaxTransactionFee = 100000000;
-  CT_MaxWalletAmount = 10000000000000;
+  CT_MaxWalletAmount = 10000000000000; // ... can be deleted
   //
   CT_MinCompactTarget: Cardinal = {$IFDEF PRODUCTION}$19000000{$ELSE}{$IFDEF TESTNET}$19000000{$ELSE}{$ENDIF}{$ENDIF}; // First compact target of block 0
 
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
+  CT_CalcNewTargetLimitChange_SPLIT = 10;
+
   CT_MaxAccount : Cardinal = $FFFFFFFF;
   CT_MaxBlock : Cardinal = $FFFFFFFF;
 
@@ -77,21 +78,33 @@ Const
 
   CT_MaxClientsConnected = 100;
 
-  CT_BankToDiskEveryNBlocks = 100; // Build 1.5 changed from 500 to 100; // Build 1.3.0 Changed from 1000 to 500
+  CT_BankToDiskEveryNBlocks = {$IFDEF PRODUCTION}100{$ELSE}10{$ENDIF}; // Build 1.5 changed from 500 to 100;
+
+  CT_NID_secp256k1 = 714;
+  CT_NID_secp384r1 = 715;
+  CT_NID_sect283k1 = 729;
+  CT_NID_secp521r1 = 716;
+
+  CT_Default_EC_OpenSSL_NID = CT_NID_secp256k1;
 
-  CT_Default_EC_OpenSSL_NID = NID_secp256k1;
+  CT_AccountInfo_ForSale = 1000;
+
+  CT_PROTOCOL_1 = 1;
+  CT_PROTOCOL_2 = 2;
+  CT_BlockChain_Protocol_Available: Word = $0002; // Protocol 2 flag
+  CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}600{$ENDIF};
 
-  CT_BlockChain_Protocol_Version: Word = $0001; // Version 1
-  CT_BlockChain_Protocol_Available: Word = $0001; // Build 1.4 Protocol available changed 0->1
 
   CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$0A04FFFF{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
 
   CT_NetProtocol_Version: Word = $0005; // Version 1.5.4 only allows net protocol version 5  (introduced on 1.5.0)
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
-  CT_NetProtocol_Available: Word = $0005;  // Remember, >= NetProtocol_version !!!
+  CT_NetProtocol_Available: Word = $0006;  // Remember, >= NetProtocol_version !!!
+
+  CT_MaxAccountOperationsPerBlockWithoutFee = 1;
 
-  CT_SafeBoxBankVersion : Word = 2;
+  CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
 
   CT_MagicIdentificator: AnsiString = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET'{$ENDIF}; //
 
@@ -99,18 +112,40 @@ Const
   CT_Op_Transaction = $01;
   CT_Op_Changekey = $02;
   CT_Op_Recover = $03;
-
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.5.6'{$ELSE}{$IFDEF TESTNET}'TESTNET 1.5.6'{$ELSE}{$ENDIF}{$ENDIF};
+  // Protocol 2 new operations
+  CT_Op_ListAccountForSale = $04;
+  CT_Op_DelistAccount = $05;
+  CT_Op_BuyAccount = $06;
+  CT_Op_ChangeKeySigned = $07;
+  CT_Op_ChangeAccountInfo = $08;
+
+  CT_OpSubtype_TransactionSender          = 11;
+  CT_OpSubtype_TransactionReceiver        = 12;
+  CT_OpSubtype_BuyTransactionBuyer        = 13;
+  CT_OpSubtype_BuyTransactionTarget       = 14;
+  CT_OpSubtype_BuyTransactionSeller       = 15;
+  CT_OpSubtype_ChangeKey                  = 21;
+  CT_OpSubtype_Recover                    = 31;
+  CT_OpSubtype_ListAccountForPublicSale   = 41;
+  CT_OpSubtype_ListAccountForPrivateSale  = 42;
+  CT_OpSubtype_DelistAccount              = 51;
+  CT_OpSubtype_BuyAccountBuyer            = 61;
+  CT_OpSubtype_BuyAccountTarget           = 62;
+  CT_OpSubtype_BuyAccountSeller           = 63;
+  CT_OpSubtype_ChangeKeySigned            = 71;
+  CT_OpSubtype_ChangeAccountInfo          = 81;
+
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'2.0.0'{$ELSE}{$IFDEF TESTNET}'TESTNET 2.0.0'{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
 
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
 
-  CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}5{$ELSE}{$ENDIF}{$ENDIF};
-  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}5000{$ELSE}{$IFDEF TESTNET}10{$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};
 
   // App Params
-  CT_PARAM_GridAccountsStream = 'GridAccountsStream';
+  CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';
   CT_PARAM_DefaultFee = 'DefaultFee';
   CT_PARAM_InternetServerPort = 'InternetServerPort';

+ 26 - 3
Units/PascalCoin/UCrypto.pas

@@ -71,7 +71,9 @@ Type
     Class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     Class function DoSha256(const TheMessage : AnsiString) : TRawBytes; overload;
     Class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; Var ResultSha256 : TRawBytes); overload;
-    Class function DoRipeMD160(const TheMessage : AnsiString) : TRawBytes;
+    Class function DoRipeMD160_HEXASTRING(const TheMessage : AnsiString) : TRawBytes; overload;
+    Class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
+    Class function DoRipeMD160AsRaw(const TheMessage : AnsiString) : TRawBytes; overload;
     Class function PrivateKey2Hexa(Key : PEC_KEY) : AnsiString;
     Class function ECDSASign(Key : PEC_KEY; const digest : AnsiString) : TECDSA_SIG;
     Class function ECDSAVerify(EC_OpenSSL_NID : Word; PubKey : EC_POINT; const digest : AnsiString; Signature : TECDSA_SIG) : Boolean; overload;
@@ -227,7 +229,12 @@ begin
         PAC := BN_bn2hex(BNx);
         try
           Result := TECPrivateKey.Create;
-          Result.SetPrivateKeyFromHexa(ECID,PAC);
+          Try
+            Result.SetPrivateKeyFromHexa(ECID,PAC);
+          Except
+            FreeAndNil(Result);
+            Raise;
+          end;
         finally
           OpenSSL_free(PAC);
         end;
@@ -325,7 +332,7 @@ begin
   SHA256(PS,32,PS);
 end;
 
-class function TCrypto.DoRipeMD160(const TheMessage: AnsiString): TRawBytes;
+class function TCrypto.DoRipeMD160_HEXASTRING(const TheMessage: AnsiString): TRawBytes;
 Var PS : PAnsiChar;
   PC : PAnsiChar;
   i : Integer;
@@ -341,6 +348,22 @@ begin
   FreeMem(PS,33);
 end;
 
+class function TCrypto.DoRipeMD160AsRaw(p: PAnsiChar; plength: Cardinal): TRawBytes;
+Var PS : PAnsiChar;
+begin
+  SetLength(Result,20);
+  PS := @Result[1];
+  RIPEMD160(p,plength,PS);
+end;
+
+class function TCrypto.DoRipeMD160AsRaw(const TheMessage: AnsiString): TRawBytes;
+Var PS : PAnsiChar;
+begin
+  SetLength(Result,20);
+  PS := @Result[1];
+  RIPEMD160(PAnsiChar(TheMessage),Length(TheMessage),PS);
+end;
+
 class function TCrypto.DoSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
 Var PS : PAnsiChar;
 begin

+ 329 - 114
Units/PascalCoin/UFileStorage.pas

@@ -20,7 +20,7 @@ unit UFileStorage;
 interface
 
 uses
-  Classes, UBlockChain, SyncObjs, UThread;
+  Classes, UBlockChain, SyncObjs, UThread, UAccounts, UCrypto;
 {$I config.inc}
 
 Type
@@ -43,14 +43,15 @@ Type
     FBlockHeadersFirstBytePosition : TArrayOfInt64;
     FDatabaseFolder: AnsiString;
     FBlockChainFileName : AnsiString;
-    Function StreamReadBlockHeader(Stream: TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block: Cardinal; var BlockHeader : TBlockHeader): Boolean;
-    Function StreamBlockRead(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
-    Function StreamBlockSave(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
+    Function StreamReadBlockHeader(Stream: TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal; CanSearchBackward : Boolean; var BlockHeader : TBlockHeader): Boolean;
+    Function StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
+    Function StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function GetFolder(Const AOrphan : TOrphan): AnsiString;
-    Function GetBlockHeaderFirstBytePosition(Stream : TStream; Block : Cardinal; var StreamBlockHeaderStartPos : Int64; var BlockHeaderFirstBlock : Cardinal) : Boolean;
+    Function GetBlockHeaderFirstBytePosition(Stream : TStream; Block : Cardinal; CanInitialize : Boolean; var iBlockHeaders : Integer; var BlockHeaderFirstBlock : Cardinal) : Boolean;
     Function GetBlockHeaderFixedSize : Int64;
     procedure SetDatabaseFolder(const Value: AnsiString);
     Procedure ClearStream;
+    Procedure GrowStreamUntilPos(Stream : TStream; newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
   protected
     procedure SetReadOnly(const Value: Boolean); override;
     procedure SetOrphan(const Value: TOrphan); override;
@@ -63,17 +64,21 @@ Type
     Function BlockExists(Block : Cardinal) : Boolean; override;
     Function LockBlockChainStream : TFileStream;
     Procedure UnlockBlockChainStream;
-    Function LoadBankFileInfo(Const Filename : AnsiString; var BlocksCount : Cardinal) : Boolean;
+    Function LoadBankFileInfo(Const Filename : AnsiString; var safeBoxHeader : TPCSafeBoxHeader) : Boolean;
     function GetFirstBlockNumber: Int64; override;
     function GetLastBlockNumber: Int64; override;
     function DoInitialize : Boolean; override;
+    Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; override;
+    Procedure DoEraseStorage; override;
   public
     Constructor Create(AOwner : TComponent); Override;
     Destructor Destroy; Override;
-    Class Function GetBankFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+    Class Function GetSafeboxCheckpointingFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
     Property DatabaseFolder : AnsiString read FDatabaseFolder write SetDatabaseFolder;
     Procedure CopyConfiguration(Const CopyFrom : TStorage); override;
     Procedure SetBlockChainFile(BlockChainFileName : AnsiString);
+    Function HasUpgradedToVersion2 : Boolean; override;
+    Procedure CleanupVersion1Data; override;
   End;
 
 implementation
@@ -116,15 +121,16 @@ Const CT_TBlockHeader_NUL : TBlockHeader = (BlockNumber:0;StreamBlockRelStartPos
   }
 
 function TFileStorage.BlockExists(Block: Cardinal): Boolean;
-Var  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+Var  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
   stream : TStream;
   BlockHeader : TBlockHeader;
 begin
   Result := false;
   stream := LockBlockChainStream;
   try
-    if Not GetBlockHeaderFirstBytePosition(stream,Block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
-    if not StreamReadBlockHeader(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,BlockHeader) then exit;
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    if not StreamReadBlockHeader(stream,iBlockHeaders,BlockHeaderFirstBlock,Block,False,BlockHeader) then exit;
     Result := (BlockHeader.BlockNumber = Block) And
         (((BlockHeader.BlockNumber MOD CT_GroupBlockSize)=0) OR (BlockHeader.StreamBlockRelStartPos>0)) And
         (BlockHeader.BlockSize>0);
@@ -141,6 +147,26 @@ begin
   SetLength(FBlockHeadersFirstBytePosition,0);
 end;
 
+procedure TFileStorage.GrowStreamUntilPos(Stream : TStream; newPos: Int64; DeleteDataStartingAtCurrentPos: Boolean);
+Var null_buff : Array[1..CT_GroupBlockSize] of Byte;
+  i,antPos,antSize : Int64;
+begin
+  antPos := Stream.Position;
+  antSize := Stream.Size;
+  if Not DeleteDataStartingAtCurrentPos then begin
+    Stream.Position := Stream.Size;
+  end;
+  if (stream.Position<newPos) then begin
+    FillChar(null_buff,length(null_buff),0);
+    while (Stream.Position<newPos) do begin
+      i := newPos - Stream.Position;
+      if i>length(null_buff) then i := length(null_buff);
+      Stream.WriteBuffer(null_buff,i);
+    end;
+  end;
+  Stream.Position := newPos;
+end;
+
 procedure TFileStorage.CopyConfiguration(const CopyFrom: TStorage);
 begin
   inherited;
@@ -170,32 +196,21 @@ end;
 
 procedure TFileStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
 Var stream : TStream;
-  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
   _Header : TBlockHeader;
   _intBlockIndex : Cardinal;
   p : Int64;
-  Procedure GrowUntilPos(newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
-  Var b : Byte;
-  begin
-    b := 0;
-    if Not DeleteDataStartingAtCurrentPos then begin
-      Stream.Position := Stream.Size;
-    end;
-    While (Stream.Position<newPos) do begin
-      Stream.Write(b,1);
-    end;
-    Stream.Position := newPos;
-  end;
 begin
   stream := LockBlockChainStream;
   Try
-    if Not GetBlockHeaderFirstBytePosition(stream,StartingDeleteBlock,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
-    If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,StartingDeleteBlock,_Header) then exit;
+    if Not GetBlockHeaderFirstBytePosition(stream,StartingDeleteBlock,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    If Not StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,StartingDeleteBlock,True,_Header) then exit;
     _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
     p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
     Stream.Position:=p;
     // Write null data until end of header
-    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
+    GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[iBlockHeaders] + GetBlockHeaderFixedSize,true);
     // End Stream at _Header
     Stream.Size := Stream.Position + _Header.StreamBlockRelStartPos;
   Finally
@@ -214,21 +229,95 @@ begin
   End;
 end;
 
+function TFileStorage.DoCreateSafeBoxStream(blockCount: Cardinal): TStream;
+var fn : TFilename;
+  err : AnsiString;
+begin
+  Result := Nil;
+  fn := GetSafeboxCheckpointingFileName(GetFolder(Orphan),blockCount);
+  If (fn<>'') and (FileExists(fn)) then begin
+    Result := TFileStream.Create(fn,fmOpenRead);
+  end;
+  If Not Assigned(Result) then begin
+    err := 'Cannot load SafeBoxStream (block:'+IntToStr(blockCount)+') file:'+fn;
+    TLog.NewLog(ltError,ClassName,err);
+  end;
+end;
+
+procedure TFileStorage.DoEraseStorage;
+Var stream : TStream;
+begin
+  stream := LockBlockChainStream;
+  try
+    stream.Size:=0; // Erase
+    ClearStream;
+  finally
+    UnlockBlockChainStream;
+  end;
+end;
+
 function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
 Var stream : TStream;
-  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
 begin
   Result := False;
   stream := LockBlockChainStream;
   Try
-    if Not GetBlockHeaderFirstBytePosition(stream,Block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
-    Result := StreamBlockRead(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,Operations);
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockRead(stream,iBlockHeaders,BlockHeaderFirstBlock,Block,Operations);
   Finally
     UnlockBlockChainStream;
   End;
 end;
 
 function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
+  Procedure DoCopyFile(sourcefn,destfn : AnsiString);
+  var sourceFS, destFS : TFileStream;
+  Begin
+    if Not FileExists(sourcefn) then Raise Exception.Create('Source file not found: '+sourcefn);
+    sourceFS := TFileStream.Create(sourcefn,fmOpenRead+fmShareDenyNone);
+    try
+      sourceFS.Position:=0;
+      destFS := TFileStream.Create(destfn,fmCreate+fmShareDenyWrite);
+      try
+        destFS.Size:=0;
+        destFS.CopyFrom(sourceFS,sourceFS.Size);
+      finally
+        destFS.Free;
+      end;
+    finally
+      sourceFS.Free;
+    end;
+  end;
+
+  Procedure DoCopySafebox;
+  var sr: TSearchRec;
+    FileAttrs: Integer;
+    folder : AnsiString;
+    sourcefn,destfn : AnsiString;
+  begin
+    FileAttrs := faArchive;
+    folder := GetFolder(Orphan);
+    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
+      repeat
+        if (sr.Attr and FileAttrs) = FileAttrs then begin
+          sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
+          destfn := GetFolder('')+PathDelim+sr.Name;
+          TLog.NewLog(ltInfo,ClassName,'Copying safebox file '+sourcefn+' to '+destfn);
+          Try
+            DoCopyFile(sourcefn,destfn);
+          Except
+            On E:Exception do begin
+              TLog.NewLog(ltError,Classname,'Error copying file: ('+E.ClassName+') '+E.Message);
+            end;
+          End;
+        end;
+      until FindNext(sr) <> 0;
+      FindClose(sr);
+    end;
+  End;
+
 Var db : TFileStorage;
   i : Integer;
   ops : TPCOperationsComp;
@@ -252,12 +341,17 @@ begin
           b := Start_Block;
           while LoadBlockChainBlock(ops,b) do begin
             inc(b);
+            TLog.NewLog(ltDebug,Classname,'Moving block from "'+Orphan+'" to "'+DestOrphan+'" '+TPCOperationsComp.OperationBlockToText(ops.OperationBlock));
             db.SaveBlockChainBlock(ops);
           end;
           TLog.NewLog(ltdebug,Classname,'Moved blockchain from "'+Orphan+'" to "'+DestOrphan+'" from block '+inttostr(Start_Block)+' to '+inttostr(b-1));
         finally
           ops.Free;
         end;
+        // If DestOrphan is empty, then copy possible updated safebox (because, perhaps current saved safebox is from invalid blockchain)
+        if (DestOrphan='') And (Orphan<>'') then begin
+          DoCopySafebox;
+        end;
       finally
         if db is TFileStorage then TFileStorage(db).UnlockBlockChainStream;
       end;
@@ -281,7 +375,8 @@ var
     fs : TFileStream;
     ms : TMemoryStream;
     errors : AnsiString;
-    blockscount, c : Cardinal;
+    blockscount : Cardinal;
+    sbHeader : TPCSafeBoxHeader;
 begin
   LockBlockChainStream;
   Try
@@ -289,14 +384,15 @@ begin
     folder := GetFolder(Orphan);
     filename := '';
     blockscount := 0;
-    if SysUtils.FindFirst(folder+PathDelim+'*.bank', FileAttrs, sr) = 0 then begin
+    if SysUtils.FindFirst(folder+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           auxfn := folder+PathDelim+sr.Name;
-          If LoadBankFileInfo(auxfn,c) then begin
-            if ((c<=max_block) AND (c>blockscount)) then begin
+          If LoadBankFileInfo(auxfn,sbHeader) then begin
+            if (((max_block<0) Or (sbHeader.blocksCount<=max_block)) AND (sbHeader.blocksCount>blockscount)) And
+              (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
               filename := auxfn;
-              blockscount := c;
+              blockscount := sbHeader.blocksCount;
             end;
           end;
         end;
@@ -312,7 +408,7 @@ begin
           ms.CopyFrom(fs,0);
           fs.Position := 0;
           ms.Position := 0;
-          if not Bank.LoadBankFromStream(ms,errors) then begin
+          if not Bank.LoadBankFromStream(ms,False,errors) then begin
             TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
           end;
         Finally
@@ -333,14 +429,15 @@ var fs: TFileStream;
     ms : TMemoryStream;
 begin
   Result := true;
-  bankfilename := GetBankFileName(GetFolder(Orphan),Bank.BlocksCount);
+  bankfilename := GetSafeboxCheckpointingFileName(GetFolder(Orphan),Bank.BlocksCount);
   if (bankfilename<>'') then begin
+    TLog.NewLog(ltInfo,ClassName,'Saving Safebox blocks:'+IntToStr(Bank.BlocksCount)+' file:'+bankfilename);
     fs := TFileStream.Create(bankfilename,fmCreate);
     try
       fs.Size := 0;
       ms := TMemoryStream.Create;
       try
-        Bank.SaveBankToStream(ms);
+        Bank.SafeBox.SaveSafeBoxToAStream(ms,0,Bank.SafeBox.BlocksCount-1);
         ms.Position := 0;
         fs.Position := 0;
         fs.CopyFrom(ms,0);
@@ -355,7 +452,8 @@ end;
 
 function TFileStorage.DoSaveBlockChain(Operations: TPCOperationsComp): Boolean;
 Var stream : TStream;
-  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
 begin
   Result := False;
   stream := LockBlockChainStream;
@@ -368,28 +466,28 @@ begin
       end;
       TLog.NewLog(ltdebug,Classname,Format('Saving Block %d on a newer stream, stream first position=%d',[Operations.OperationBlock.block,FStreamFirstBlockNumber]));
     end;
-    if Not GetBlockHeaderFirstBytePosition(stream,Operations.OperationBlock.block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
-    Result := StreamBlockSave(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Operations);
+    if Not GetBlockHeaderFirstBytePosition(stream,Operations.OperationBlock.block,True,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockSave(stream,iBlockHeaders,BlockHeaderFirstBlock,Operations);
   Finally
     UnlockBlockChainStream;
   End;
   if Assigned(Bank) then SaveBank;
 end;
 
-class function TFileStorage.GetBankFileName(const BaseDataFolder: AnsiString;
-  block: Cardinal): AnsiString;
-Var c : Cardinal;
-  folder : AnsiString;
+Const CT_SafeboxsToStore = 10;
+
+class function TFileStorage.GetSafeboxCheckpointingFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
 begin
   Result := '';
   If not ForceDirectories(BaseDataFolder) then exit;
-  // We will store last 5 banks
-  Result := BaseDataFolder + PathDelim+'bank'+ inttostr((block DIV CT_BankToDiskEveryNBlocks) MOD 5)+'.bank';
+  // We will store checkpointing
+  Result := BaseDataFolder + PathDelim+'checkpoint'+ inttostr((block DIV CT_BankToDiskEveryNBlocks) MOD CT_SafeboxsToStore)+'.safebox';
 end;
 
-function TFileStorage.GetBlockHeaderFirstBytePosition(Stream : TStream; Block: Cardinal; var StreamBlockHeaderStartPos: Int64; var BlockHeaderFirstBlock: Cardinal): Boolean;
-var iPos,start : Cardinal;
+function TFileStorage.GetBlockHeaderFirstBytePosition(Stream : TStream; Block: Cardinal; CanInitialize : Boolean; var iBlockHeaders : Integer; var BlockHeaderFirstBlock: Cardinal): Boolean;
+var iPos,start, nCurrBlock : Cardinal;
   bh : TBlockHeader;
+  null_buff : Array[1..(CT_GroupBlockSize * CT_SizeOfBlockHeader)] of Byte;
 begin
   Result := false;
   if Block<FStreamFirstBlockNumber then begin
@@ -401,30 +499,76 @@ begin
     if Length(FBlockHeadersFirstBytePosition)>0 then begin
       start := High(FBlockHeadersFirstBytePosition);
     end else begin
-      // Initialize and start at 0
-      SetLength(FBlockHeadersFirstBytePosition,1);
-      FBlockHeadersFirstBytePosition[0] := 0;
-      start := 0;
+      If CanInitialize then begin
+        // Initialize and start at 0
+        SetLength(FBlockHeadersFirstBytePosition,1);
+        FBlockHeadersFirstBytePosition[0] := 0;
+        start := 0;
+      end else exit;
     end;
     while (start<iPos) do begin
       // Read last start position
       if (Stream.Size<(FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize)) then begin
-        // This position not exists... This is a Fatal error due must find previos block!
-        TLog.NewLog(ltError,Classname,Format('Stream size %d is lower than BlockHeader[%d] position %d + BlockHeaderSize %d',
-          [Stream.size,start,FBlockHeadersFirstBytePosition[start],GetBlockHeaderFixedSize]));
-        exit;
+        // This position not exists...
+        If (CanInitialize) then begin
+          GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[start],false);
+          // Save BlockHeader values (initialized to 0)
+          FillChar(null_buff,length(null_buff),0);
+          Stream.WriteBuffer(null_buff,length(null_buff));
+        end else begin
+          // This is a Fatal error due must find previos block!
+          TLog.NewLog(ltError,Classname,Format('Stream size %d is lower than BlockHeader[%d] position %d + BlockHeaderSize %d',
+            [Stream.size,start,FBlockHeadersFirstBytePosition[start],GetBlockHeaderFixedSize]));
+          exit;
+        end;
       end;
       Stream.Position := FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize - CT_SizeOfBlockHeader;
-      // Read last Header
-      Stream.Read(bh.BlockNumber,SizeOf(bh.BlockNumber));
-      Stream.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
-      Stream.Read(bh.BlockSize,sizeof(bh.BlockSize));
+      // Read last saved Header
+      nCurrBlock := FStreamFirstBlockNumber + ((start+1) * CT_GroupBlockSize) - 1;
+      Repeat
+        Stream.Read(bh.BlockNumber,SizeOf(bh.BlockNumber));
+        Stream.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
+        Stream.Read(bh.BlockSize,sizeof(bh.BlockSize));
+        If (bh.BlockNumber<>nCurrBlock) then begin
+          if (bh.BlockNumber<>0) Or (bh.StreamBlockRelStartPos<>0) Or (bh.BlockSize<>0) then begin
+            TLog.NewLog(ltError,ClassName,Format('Fatal error. Found a Tblockheader with no 0 values searching for block:%d at nCurrBlock:%d - Number:%d RelStartPos:%d Size:%d',[block,nCurrBlock,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize]));
+            exit;
+          end;
+          if ((start=0) And (nCurrBlock>FStreamFirstBlockNumber))
+             Or
+             ((start>0) And (nCurrBlock>(FStreamFirstBlockNumber + ((start) * CT_GroupBlockSize)))) then begin
+            dec(nCurrBlock);
+            // Positioning for new read:
+            Stream.Seek(Int64(CT_SizeOfBlockHeader)*(-2),soFromCurrent);
+          end else begin
+            break; // End of blockheader!
+          end;
+        end;
+      until (bh.BlockNumber>0);
+      // Positioning!
+      Stream.Position := FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize;
+      //
       SetLength(FBlockHeadersFirstBytePosition,length(FBlockHeadersFirstBytePosition)+1);
-      FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position + bh.StreamBlockRelStartPos + bh.BlockSize;
+      if bh.BlockNumber>0 then begin
+        FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position + bh.StreamBlockRelStartPos + bh.BlockSize;
+      end else begin
+        // Not found a block, starting at last pos
+        FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position;
+      end;
       inc(start);
+
+      // Check if blockheader size is ok:
+      if (CanInitialize) And (Stream.Size<(FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize)) then begin
+        Stream.Position := FBlockHeadersFirstBytePosition[start];
+        TLog.NewLog(ltInfo,ClassName,Format('Increasing size for blockheader %d at pos:%d (current stream pos %d size %d) to position:%d',
+          [start,FBlockHeadersFirstBytePosition[start],Stream.Position,Stream.Size,
+          FBlockHeadersFirstBytePosition[start]+GetBlockHeaderFixedSize]));
+        GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[start]+GetBlockHeaderFixedSize,true);
+      end;
+
     end;
   End;
-  StreamBlockHeaderStartPos := FBlockHeadersFirstBytePosition[iPos];
+  iBlockHeaders := iPos;
   BlockHeaderFirstBlock := FStreamFirstBlockNumber + (iPos * CT_GroupBlockSize);
   Result := true;
 end;
@@ -452,16 +596,16 @@ begin
   Result := FStreamLastBlockNumber;
 end;
 
-function TFileStorage.LoadBankFileInfo(const Filename: AnsiString; var BlocksCount: Cardinal): Boolean;
+function TFileStorage.LoadBankFileInfo(const Filename: AnsiString; var safeBoxHeader : TPCSafeBoxHeader) : Boolean;
 var fs: TFileStream;
 begin
   Result := false;
-  BlocksCount:=0;
+  safeBoxHeader := CT_PCSafeBoxHeader_NUL;
   If Not FileExists(Filename) then exit;
   fs := TFileStream.Create(Filename,fmOpenRead);
   try
     fs.Position:=0;
-    Result := Bank.LoadBankStreamHeader(fs,BlocksCount);
+    Result := Bank.SafeBox.LoadSafeBoxStreamHeader(fs,safeBoxHeader);
   finally
     fs.Free;
   end;
@@ -509,31 +653,47 @@ function TFileStorage.LockBlockChainStream: TFileStream;
           mem.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
           mem.Read(bh.BlockSize,sizeof(bh.BlockSize));
           if (i=0) And (iPos=0) then begin
-              FStreamFirstBlockNumber := bh.BlockNumber;
-              FStreamLastBlockNumber := bh.BlockNumber;
+            FStreamFirstBlockNumber := bh.BlockNumber;
+            FStreamLastBlockNumber := bh.BlockNumber;
             if (0<>bh.StreamBlockRelStartPos) then begin
               errors := Format('Invalid first block start rel pos %d',[bh.StreamBlockRelStartPos]);
               result := false;
               exit;
             end;
+            lastbh := bh;
           end else begin
+            // Protocol 2: We can find blocks not saved, with all values to 0
             if (bh.BlockNumber=0) then begin
-              // End here
-              break;
-            end;
-            if (lastbh.BlockNumber+1<>bh.BlockNumber) or
-              ((lastbh.StreamBlockRelStartPos+lastbh.BlockSize<>bh.StreamBlockRelStartPos) And (i>0)) Or
-              ((0<>bh.StreamBlockRelStartPos) And (i=0)) then begin
-              errors := Format('Invalid check on block header. iPos=%d i=%d Number=%d relstart=%d size=%d',[iPos,i,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize]);
-              result := false;
-              exit;
+              // This is an "empty" block. Check that ok
+              If (bh.BlockNumber<>0) Or (bh.StreamBlockRelStartPos<>0) Or (bh.BlockSize<>0) then begin
+                errors := Format('Invalid empty block on block header. iPos=%d i=%d BlockNumber=%d relstart=%d size=%d - Last block:%d BlockNumber=%d relstart=%d size=%d',
+                [iPos,i,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize,
+                 FStreamLastBlockNumber,
+                 lastbh.BlockNumber,lastbh.StreamBlockRelStartPos,lastbh.BlockSize]);
+                result := false;
+                exit;
+              end;
+              // Ok, inc blocknumber
+              inc(lastbh.BlockNumber);
             end else begin
-              FStreamLastBlockNumber := bh.BlockNumber;
+              if (lastbh.BlockNumber+1<>bh.BlockNumber) or
+                ((lastbh.StreamBlockRelStartPos+lastbh.BlockSize<>bh.StreamBlockRelStartPos) And (i>0)) Or
+                ((0<>bh.StreamBlockRelStartPos) And (i=0)) then begin
+                errors := Format('Invalid check on block header. iPos=%d i=%d BlockNumber=%d relstart=%d size=%d - Last block:%d BlockNumber=%d relstart=%d size=%d',
+                  [iPos,i,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize,FStreamLastBlockNumber,
+                   lastbh.BlockNumber,lastbh.StreamBlockRelStartPos,lastbh.BlockSize]);
+                result := false;
+                exit;
+              end else begin
+                FStreamLastBlockNumber := bh.BlockNumber;
+                lastbh := bh;
+              end;
             end;
           end;
-          lastbh := bh;
         end;
         iPos := iPos + GetBlockHeaderFixedSize + lastbh.StreamBlockRelStartPos + lastBh.BlockSize;
+        lastbh.StreamBlockRelStartPos:=0;
+        lastbh.BlockSize:=0;
       end;
       Result := true;
     Finally
@@ -567,7 +727,7 @@ begin
       // Init stream
       If Not InitStreamInfo(FBlockChainStream,errors) then begin
         TLog.NewLog(lterror,ClassName,errors);
-        raise Exception.Create('Error reading File: '+errors);
+        raise Exception.Create('Error reading File: '+fn+#10+'Errors:'+#10+errors);
       end;
     end;
   Except
@@ -602,7 +762,7 @@ begin
   ClearStream;
 end;
 
-function TFileStorage.StreamBlockRead(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
+function TFileStorage.StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
 Var p : Int64;
   errors : AnsiString;
   streamFirstBlock,
@@ -610,17 +770,19 @@ Var p : Int64;
   _intBlockIndex : Cardinal;
   _Header : TBlockHeader;
   _ops : TStream;
+  _StreamBlockHeaderStartPos : Int64;
 begin
   Result := False;
-  If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,_Header) then exit;
+  If Not StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,Block,False,_Header) then exit;
 
   // Calculating block position
-  p := (StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
+  _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+  p := (_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
      (_Header.StreamBlockRelStartPos);
   if Stream.Size<(p + _Header.BlockSize) then begin
     TLog.NewLog(ltError,Classname,Format(
       'Invalid stream size. Block %d need to be at relative %d after %d = %d BlockSize:%d (Size %d)',
-      [Block,_Header.StreamBlockRelStartPos,(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize),p,_Header.BlockSize,Stream.Size]));
+      [Block,_Header.StreamBlockRelStartPos,(_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize),p,_Header.BlockSize,Stream.Size]));
     exit;
   end;
   Stream.Position := p;
@@ -648,40 +810,30 @@ begin
 end;
 
 
-function TFileStorage.StreamBlockSave(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
-  Procedure GrowUntilPos(newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
-  Var null_buff : Array[1..CT_GroupBlockSize] of Byte;
-    i : Int64;
-  begin
-    if Not DeleteDataStartingAtCurrentPos then begin
-      Stream.Position := Stream.Size;
-    end;
-    if (stream.Position<newPos) then begin
-      FillChar(null_buff,length(null_buff),0);
-      while (Stream.Position<newPos) do begin
-        i := newPos - Stream.Position;
-        if i>length(null_buff) then i := length(null_buff);
-        Stream.WriteBuffer(null_buff,i);
-      end;
-    end;
-    Stream.Position := newPos;
-  end;
+function TFileStorage.StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
 Var p : Int64;
   c : Cardinal;
   _Header, _HeaderPrevious : TBlockHeader;
   _intBlockIndex : Cardinal;
   _ops : TStream;
+  _StreamBlockHeaderStartPos : Int64;
+  {$IFDEF HIGHLOG}s : String;{$ENDIF}
 begin
   Result := false;
   _Header := CT_TBlockHeader_NUL;
   _Header.BlockNumber := Operations.OperationBlock.block;
   if BlockHeaderFirstBlock>_Header.BlockNumber then raise Exception.Create('Dev error 20160917-3')
   else if BlockHeaderFirstBlock<_Header.BlockNumber then begin
-    Result := StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,_Header.BlockNumber-1,_HeaderPrevious);
+    Result := StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,_Header.BlockNumber-1,True,_HeaderPrevious);
+    // If true then Stream is positioned on blockheader for current block
     if not Result then begin
       raise Exception.Create('Cannot found header of previous block '+inttostr(Operations.OperationBlock.block));
     end;
-    _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
+    If (_Header.BlockNumber MOD CT_GroupBlockSize)=0 then begin
+      _Header.StreamBlockRelStartPos := 0;
+    end else begin
+      _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
+    end;
   end else begin
     // First block of the stream
     Result := true;
@@ -694,21 +846,30 @@ begin
     // Positioning until Header Position to save Header data
     _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
     p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
-    GrowUntilPos(StreamBlockHeaderStartPos + p,false);
+    _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+    {$IFDEF HIGHLOG}s := Format('Saving block header (block %d) at position %d',[_Header.BlockNumber,Stream.Position]);{$ENDIF}
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + p,false);
     // Save Header
     Stream.Write(_Header.BlockNumber,sizeof(_Header.BlockNumber));
     Stream.Write(_Header.StreamBlockRelStartPos,sizeof(_Header.StreamBlockRelStartPos));
     c := _Header.BlockSize + sizeof(c);
     Stream.Write(c,sizeof(_Header.BlockSize));
     // Positioning until Header end
-    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
     // And now positioning until Data:
-    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize + _Header.StreamBlockRelStartPos, false );
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize + _Header.StreamBlockRelStartPos, false );
+    {$IFDEF HIGHLOG}
+    s := s + Format(' saving content at position %d (size %d)',[Stream.Position,_Header.BlockSize]);
+    TLog.NewLog(ltInfo,ClassName,s);
+    {$ENDIF}
     // Save stream size
     Stream.Write(_Header.BlockSize,sizeof(_Header.BlockSize));
     // Save Data
     _ops.Position := 0;
     Stream.CopyFrom(_ops,_ops.Size);
+    // End Stream here
+    Stream.Size := Stream.Position;
+    //
     FStreamLastBlockNumber := Operations.OperationBlock.block;
   Finally
     _ops.Free;
@@ -716,23 +877,57 @@ begin
 end;
 
 function TFileStorage.StreamReadBlockHeader(Stream: TStream;
-  StreamBlockHeaderStartPos: Int64; BlockHeaderFirstBlock, Block: Cardinal;
+  iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal;
+  CanSearchBackward : Boolean;
   var BlockHeader: TBlockHeader): Boolean;
+Var iBlock : Cardinal;
+  _iBlockHeaders : Integer;
+  _StreamBlockHeaderStartPos: Int64;
+  _BlockHeaderFirstBlock : Cardinal;
 begin
   Result := false;
   BlockHeader := CT_TBlockHeader_NUL;
   if (BlockHeaderFirstBlock>Block) then raise Exception.Create('Dev error 20160917-1');
   if (BlockHeaderFirstBlock+CT_GroupBlockSize)<Block then raise Exception.Create('Dev error 20160917-2');
-  if Stream.Size< (StreamBlockHeaderStartPos + (GetBlockHeaderFixedSize)) then begin
-    // Not log... it's normal when finding block   TLog.NewLog(ltError,Classname,Format('Invalid stream size %d < (%d + %d) Reading block %d',[Stream.Size,StreamBlockHeaderStartPos,GetBlockHeaderFixedSize,Block]));
+  _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+  if Stream.Size< (_StreamBlockHeaderStartPos + (GetBlockHeaderFixedSize)) then begin
+    // Not log... it's normal when finding block
+    TLog.NewLog(ltError,Classname,Format('Invalid stream size %d < (%d + %d) Reading block %d',[Stream.Size,_StreamBlockHeaderStartPos,GetBlockHeaderFixedSize,Block]));
     exit;
   end;
-  Stream.Position := StreamBlockHeaderStartPos + (CT_SizeOfBlockHeader*(Block-BlockHeaderFirstBlock));
+  iBlock := Block;
+  _iBlockHeaders:=iBlockHeaders;
+  _BlockHeaderFirstBlock:=BlockHeaderFirstBlock;
   // Reading block header
-  If Stream.Read(BlockHeader.BlockNumber,sizeof(BlockHeader.BlockNumber))<sizeof(BlockHeader.BlockNumber) then exit;
-  If Stream.Read(BlockHeader.StreamBlockRelStartPos,sizeof(BlockHeader.StreamBlockRelStartPos))<sizeof(BlockHeader.StreamBlockRelStartPos) then exit;
-  If Stream.Read(BlockHeader.BlockSize,sizeof(BlockHeader.BlockSize))<sizeof(BlockHeader.BlockSize) then exit;
-  Result := (BlockHeader.BlockNumber = Block);
+  Repeat
+    _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[_iBlockHeaders];
+    Stream.Position := _StreamBlockHeaderStartPos + (CT_SizeOfBlockHeader*(iBlock-_BlockHeaderFirstBlock));
+    If Stream.Read(BlockHeader.BlockNumber,sizeof(BlockHeader.BlockNumber))<sizeof(BlockHeader.BlockNumber) then exit;
+    If Stream.Read(BlockHeader.StreamBlockRelStartPos,sizeof(BlockHeader.StreamBlockRelStartPos))<sizeof(BlockHeader.StreamBlockRelStartPos) then exit;
+    If Stream.Read(BlockHeader.BlockSize,sizeof(BlockHeader.BlockSize))<sizeof(BlockHeader.BlockSize) then exit;
+    Result := (BlockHeader.BlockNumber = iBlock);
+    If (Not Result) And (CanSearchBackward) then begin
+      If (iBlock>_BlockHeaderFirstBlock) then dec(iBlock)
+      else begin
+        // Search on previous header...
+        If (iBlockHeaders>0) then begin
+          dec(_iBlockHeaders);
+          dec(_BlockHeaderFirstBlock,CT_GroupBlockSize);
+        end else begin
+          break;
+        end;
+      end;
+    end;
+  Until Result Or (Not CanSearchBackward);
+  // Positioning
+  Stream.Position := FBlockHeadersFirstBytePosition[iBlockHeaders] + (CT_SizeOfBlockHeader*(Block+1-BlockHeaderFirstBlock));
+  If Result then begin // For Backward searching...
+    BlockHeader.BlockNumber:=Block;
+    If (_iBlockHeaders<>iBlockHeaders) then begin
+      BlockHeader.BlockSize:=0;
+      BlockHeader.StreamBlockRelStartPos:=0;
+    end;
+  end;
 end;
 
 procedure TFileStorage.UnlockBlockChainStream;
@@ -740,4 +935,24 @@ begin
   FStorageLock.Release;
 end;
 
+function TFileStorage.HasUpgradedToVersion2: Boolean;
+var searchRec: TSearchRec;
+begin
+  HasUpgradedToVersion2 := SysUtils.FindFirst( GetFolder(Orphan)+PathDelim+'*.safebox', faArchive, searchRec) = 0;
+  FindClose(searchRec);
+end;
+
+procedure TFileStorage.CleanupVersion1Data;
+var
+  folder : AnsiString;
+  searchRec : TSearchRec;
+begin
+  folder := GetFolder(Orphan);
+  if SysUtils.FindFirst( folder+PathDelim+'*.bank', faArchive, searchRec) = 0 then
+  repeat
+    SysUtils.DeleteFile(folder+PathDelim+searchRec.Name);
+  until FindNext(searchRec) <> 0;
+  FindClose(searchRec);
+end;
+
 end.

+ 2 - 3
Units/PascalCoin/ULog.pas

@@ -90,7 +90,6 @@ Type PLogData = ^TLogData;
 { TLog }
 
 constructor TLog.Create(AOwner: TComponent);
-Var l : TList;
 begin
   FLock := TCriticalSection.Create;
   FProcessGlobalLogs := true;
@@ -222,6 +221,8 @@ Var l : TList;
   i : Integer;
   P : PLogData;
 begin
+  If Not Assigned(FLog) then Exit;
+  If Not Assigned(FLog.FOnNewLog) then Exit;
   // This event is thread safe and will do OnNewLog on main thread
   l := FLog.FLogDataList.LockList;
   try
@@ -245,7 +246,5 @@ end;
 initialization
   _logs := Nil;
 finalization
-  {$IFnDEF FPC}
   FreeAndNil(_logs);
-  {$ENDIF}
 end.

+ 310 - 129
Units/PascalCoin/UNetProtocol.pas

@@ -42,6 +42,7 @@ Const
   CT_NetOp_GetOperationsBlock = $0005; // Sends from and to. Receive a number of OperationsBlock to check
   CT_NetOp_NewBlock = $0011;
   CT_NetOp_AddOperations = $0020;
+  CT_NetOp_GetSafeBox = $0021;         // V2 Protocol: Allows to send/receive Safebox in chunk parts
 
 
   CT_NetError_InvalidProtocolVersion = $0001;
@@ -49,6 +50,7 @@ Const
   CT_NetError_InvalidDataBufferInfo = $0010;
   CT_NetError_InternalServerError = $0011;
   CT_NetError_InvalidNewAccount = $0012;
+  CT_NetError_SafeboxNotFound = $00020;
 
 Type
   {
@@ -268,8 +270,11 @@ Type
     Property NetworkAdjustedTime : TNetworkAdjustedTime read FNetworkAdjustedTime;
   End;
 
+  { TNetConnection }
+
   TNetConnection = Class(TComponent)
   private
+    FIsConnecting: Boolean;
     FTcpIpClient : TNetTcpIpClient;
     FRemoteOperationBlock : TOperationBlock;
     FRemoteAccumulatedWork : UInt64;
@@ -305,6 +310,7 @@ Type
     Procedure DoProcess_GetOperationsBlock_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_NewBlock(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_AddOperations(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_GetSafeBox_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure SetClient(Const Value : TNetTcpIpClient);
     Function ReadTcpClientBuffer(MaxWaitMiliseconds : Cardinal; var HeaderData : TNetHeaderData; BufferData : TStream) : Boolean;
     Procedure DisconnectInvalidClient(ItsMyself : Boolean; Const why : AnsiString);
@@ -318,6 +324,7 @@ Type
     Destructor Destroy; override;
     Function ConnectTo(ServerIP: String; ServerPort:Word) : Boolean;
     Property Connected : Boolean read GetConnected write SetConnected;
+    Property IsConnecting : Boolean read FIsConnecting;
     Function Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
     Function Send_NewBlockFound(Const NewBlock : TPCOperationsComp) : Boolean;
     Function Send_GetBlocks(StartAddress, quantity : Cardinal; var request_id : Cardinal) : Boolean;
@@ -390,7 +397,7 @@ Const
 implementation
 
 uses
-  UConst, ULog, UNode, UTime, UECIES;
+  UConst, ULog, UNode, UTime, UECIES, UChunk;
 
 Const
   CT_NetTransferType : Array[TNetTransferType] of AnsiString = ('Unknown','Request','Response','Autosend');
@@ -671,6 +678,7 @@ begin
   FOnReceivedHelloMessage := Nil;
 
   // First destroy ThreadCheckConnections to prevent a call to "DiscoverServers"
+  TLog.NewLog(ltInfo,ClassName,'ThreadCheckConnections terminating...');
   FThreadCheckConnections.Terminate;
   FThreadCheckConnections.WaitFor;
   FreeAndNil(FThreadCheckConnections);
@@ -683,6 +691,7 @@ begin
       tdc.Terminate;
       tdc.WaitFor;
       tdc.Free;
+      TLog.NewLog(ltInfo,ClassName,'TThreadDiscoverConnection finished');
     end;
   Until Not Assigned(tdc);
 
@@ -914,7 +923,7 @@ begin
   try
     for i := 0 to L.Count - 1 do begin
       Result := TNetConnection( l[i] );
-      If TAccountComp.Equal(Result.FClientPublicKey,Sender.FClientPublicKey) And (Sender<>Result) then exit;
+      If TAccountComp.EqualAccountKeys(Result.FClientPublicKey,Sender.FClientPublicKey) And (Sender<>Result) then exit;
     end;
   finally
     FNetConnections.UnlockList;
@@ -950,7 +959,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     noperation : Integer;
   begin
     Result := false;
-    BlocksList.Count := 0;
+    BlocksList.Clear;
     if (Connection.FRemoteOperationBlock.block<block_end) then block_end := Connection.FRemoteOperationBlock.block;
     // First receive operations from
     SendData := TMemoryStream.Create;
@@ -999,7 +1008,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     OperationBlock := CT_OperationBlock_NUL;
     BlocksList := TList.Create;
     try
-      Result := Do_GetOperationsBlock(TNode.Node.Bank,block,block,MaxWaitMilliseconds,false,BlocksList);
+      Result := Do_GetOperationsBlock(TNode.Node.Bank,block,block,MaxWaitMilliseconds,True,BlocksList);
       if (Result) And (BlocksList.Count=1) then begin
         OperationBlock := TPCOperationsComp(BlocksList[0]).OperationBlock;
       end;
@@ -1012,8 +1021,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
   Function FindLastSameBlockByOperationsBlock(min,max : Cardinal; var OperationBlock : TOperationBlock) : Boolean;
   var i : Integer;
     ant_nblock : Int64;
-    myops : TPCOperationsComp;
-    auxBlock : TOperationBlock;
+    auxBlock, sbBlock : TOperationBlock;
     distinctmax,distinctmin : Cardinal;
     BlocksList : TList;
   Begin
@@ -1025,30 +1033,24 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         If Not Do_GetOperationsBlock(Nil,min,max,5000,true,BlocksList) then exit;
         distinctmin := min;
         distinctmax := max;
-        myops := TPCOperationsComp.Create(TNode.Node.Bank);
-        try
-          ant_nblock := -1;
-          for i := 0 to BlocksList.Count - 1 do begin
-            auxBlock := TPCOperationsComp(BlocksList[i]).OperationBlock;
-            // Protection of invalid clients:
-            if (auxBlock.block<min) Or (auxBlock.block>max) Or (auxBlock.block=ant_nblock) then begin
-              Connection.DisconnectInvalidClient(false,'Invalid response... '+inttostr(min)+'<'+inttostr(auxBlock.block)+'<'+inttostr(max)+' ant:'+inttostr(ant_nblock));
-              exit;
-            end;
-            ant_nblock := auxBlock.block;
-            //
-            If Not TNode.Node.Bank.LoadOperations(myops,auxBlock.block) then exit;
-
-            if ((myops.OperationBlock.proof_of_work = auxBlock.proof_of_work) And (myops.OperationBlock.nonce = auxBlock.nonce)) then begin
-              distinctmin := auxBlock.block;
-              OperationBlock := auxBlock;
-            end else begin
-              if auxBlock.block<=distinctmax then
-                distinctmax := auxBlock.block-1;
-            end;
+        ant_nblock := -1;
+        for i := 0 to BlocksList.Count - 1 do begin
+          auxBlock := TPCOperationsComp(BlocksList[i]).OperationBlock;
+          // Protection of invalid clients:
+          if (auxBlock.block<min) Or (auxBlock.block>max) Or (auxBlock.block=ant_nblock) then begin
+            Connection.DisconnectInvalidClient(false,'Invalid response... '+inttostr(min)+'<'+inttostr(auxBlock.block)+'<'+inttostr(max)+' ant:'+inttostr(ant_nblock));
+            exit;
+          end;
+          ant_nblock := auxBlock.block;
+          //
+          sbBlock := TNode.Node.Bank.SafeBox.Block(auxBlock.block).blockchainInfo;
+          if TPCOperationsComp.EqualsOperationBlock(sbBlock,auxBlock) then begin
+            distinctmin := auxBlock.block;
+            OperationBlock := auxBlock;
+          end else begin
+            if auxBlock.block<=distinctmax then
+              distinctmax := auxBlock.block-1;
           end;
-        finally
-          myops.Free;
         end;
         min := distinctmin;
         max := distinctmax;
@@ -1067,9 +1069,11 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     i : Integer;
     tempfolder : AnsiString;
     OpComp,OpExecute : TPCOperationsComp;
+    oldBlockchainOperations : TOperationsHashTree;
+    opsResume : TOperationsResumeList;
     newBlock : TBlockAccount;
     errors : AnsiString;
-    start : Cardinal;
+    start,start_c : Cardinal;
     finished : Boolean;
     Bank : TPCBank;
     ms : TMemoryStream;
@@ -1091,6 +1095,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         start := 0;
         start_block := 0;
       end;
+      start_c := start;
       Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
       Bank.Storage.ReadOnly := false;
       // Receive new blocks:
@@ -1098,7 +1103,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       repeat
         BlocksList := TList.Create;
         try
-          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,90000,false,BlocksList);
+          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,30000,false,BlocksList);
           i := 0;
           while (i<BlocksList.Count) And (Not finished) do begin
             OpComp := TPCOperationsComp(BlocksList[i]);
@@ -1135,17 +1140,52 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       // Before of version 1.5 was: "if Bank.BlocksCount>TNode.Node.Bank.BlocksCount then ..."
       // Starting on version 1.5 is: "if Bank.WORK > MyBank.WORK then ..."
       if Bank.SafeBox.WorkSum > TNode.Node.Bank.SafeBox.WorkSum then begin
-        TNode.Node.DisableNewBlocks;
-        Try
-          // I'm an orphan blockchain...
-          TLog.NewLog(ltinfo,CT_LogSender,'New valid blockchain found. My block count='+inttostr(TNode.Node.Bank.BlocksCount)+' work: '+IntToStr(TNode.Node.Bank.SafeBox.WorkSum)+
-            ' found count='+inttostr(Bank.BlocksCount)+' work: '+IntToStr(Bank.SafeBox.WorkSum)+' starting at block '+inttostr(start_block));
-          TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,Inttostr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)),Nil);
-          Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan,TNode.Node.Bank.Storage);
-          TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
-        Finally
-          TNode.Node.EnableNewBlocks;
-        End;
+        oldBlockchainOperations := TOperationsHashTree.Create;
+        try
+          TNode.Node.DisableNewBlocks;
+          Try
+            // I'm an orphan blockchain...
+            TLog.NewLog(ltinfo,CT_LogSender,'New valid blockchain found. My block count='+inttostr(TNode.Node.Bank.BlocksCount)+' work: '+IntToStr(TNode.Node.Bank.SafeBox.WorkSum)+
+              ' found count='+inttostr(Bank.BlocksCount)+' work: '+IntToStr(Bank.SafeBox.WorkSum)+' starting at block '+inttostr(start_block));
+            if TNode.Node.Bank.BlocksCount>0 then begin
+              OpExecute := TPCOperationsComp.Create(Nil);
+              try
+                for start:=start_c to TNode.Node.Bank.BlocksCount-1 do begin
+                  If TNode.Node.Bank.LoadOperations(OpExecute,start) then begin
+                    for i:=0 to OpExecute.Count-1 do begin
+                      oldBlockchainOperations.AddOperationToHashTree(OpExecute.Operation[i]);
+                    end;
+                    TLog.NewLog(ltInfo,CT_LogSender,'Recovered '+IntToStr(OpExecute.Count)+' operations from block '+IntToStr(start));
+                  end else begin
+                    TLog.NewLog(ltError,CT_LogSender,'Fatal error: Cannot read block '+IntToStr(start));
+                  end;
+                end;
+              finally
+                OpExecute.Free;
+              end;
+            end;
+            TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,Inttostr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)),Nil);
+            Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan,TNode.Node.Bank.Storage);
+            TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+          Finally
+            TNode.Node.EnableNewBlocks;
+          End;
+          // Finally add new operations:
+          // Rescue old operations from old blockchain to new blockchain
+          If oldBlockchainOperations.OperationsCount>0 then begin
+            TLog.NewLog(ltInfo,CT_LogSender,Format('Executing %d operations from block %d to %d',
+             [oldBlockchainOperations.OperationsCount,start_c,TNode.Node.Bank.BlocksCount-1]));
+            opsResume := TOperationsResumeList.Create;
+            Try
+              i := TNode.Node.AddOperations(Connection,oldBlockchainOperations,opsResume,errors);
+              TLog.NewLog(ltInfo,CT_LogSender,Format('Executed %d/%d operations. Returned errors: %s',[i,oldBlockchainOperations.OperationsCount,errors]));
+            finally
+              opsResume.Free;
+            end;
+          end else TLog.NewLog(ltInfo,CT_LogSender,Format('No operations from block %d to %d',[start_c,TNode.Node.Bank.BlocksCount-1]));
+        finally
+          oldBlockchainOperations.Free;
+        end;
       end else begin
         if (Not IsAScam) And (Connection.FRemoteAccumulatedWork > TNode.Node.Bank.SafeBox.WorkSum) then begin
           // Possible scammer!
@@ -1159,6 +1199,72 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     end;
   End;
 
+  Function DownloadSafeBox(IsMyBlockchainValid : Boolean) : Boolean;
+  Var c,_blockcount,request_id : Cardinal;
+    SendData, ReceiveData : TStream;
+    op : TOperationBlock;
+    headerdata : TNetHeaderData;
+    safeBoxHeader : TPCSafeBoxHeader;
+    errors : AnsiString;
+  Begin
+    Result := False;
+    // Will try to download penultimate saved safebox
+    _blockcount := ((Connection.FRemoteOperationBlock.block DIV CT_BankToDiskEveryNBlocks)-1) * CT_BankToDiskEveryNBlocks;
+    SendData := TMemoryStream.Create;
+    ReceiveData := TMemoryStream.Create;
+    try
+      If not Do_GetOperationBlock(_blockcount,5000,op) then begin
+        Connection.DisconnectInvalidClient(false,Format('Cannot obtain operation block %d for downloading safebox',[_blockcount]));
+        exit;
+      end;
+      // Obtain safebox
+      SendData.Write(_blockcount,SizeOf(_blockcount)); // 4 bytes for blockcount
+      TStreamOp.WriteAnsiString(SendData,op.initial_safe_box_hash);
+      c := 0;
+      SendData.Write(c,SizeOf(c));
+      c := _blockcount-1;
+      SendData.Write(c,SizeOf(c));
+      TLog.NewLog(ltdebug,CT_LogSender,Format('Sending %s from block %d to %d (Total: %d)',
+        [TNetData.OperationToText(CT_NetOp_GetSafeBox),0,_blockcount-1,_blockcount]));
+      request_id := TNetData.NetData.NewRequestId;
+      if Connection.DoSendAndWaitForResponse(CT_NetOp_GetSafeBox,request_id,SendData,ReceiveData,60000,headerdata) then begin
+        if HeaderData.is_error then exit;
+        SendData.Size:=0;
+        If Not TPCChunk.LoadSafeBoxFromChunk(ReceiveData,SendData,safeBoxHeader,errors) then begin
+          Connection.DisconnectInvalidClient(false,'Invalid received chunk: '+errors);
+          exit;
+        end;
+        TNode.Node.DisableNewBlocks;
+        try
+          TNode.Node.Bank.SafeBox.StartThreadSafe;
+          try
+            SendData.Position:=0;
+            If TNode.Node.Bank.LoadBankFromStream(SendData,True,errors) then begin
+              TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
+              If Not IsMyBlockchainValid then begin
+                TNode.Node.Bank.Storage.EraseStorage;
+              end;
+              Connection.Send_GetBlocks(TNode.Node.Bank.BlocksCount,100,request_id);
+              Result := true;
+            end else begin
+              Connection.DisconnectInvalidClient(false,'Cannot load from stream! '+errors);
+              exit;
+            end;
+          finally
+            TNode.Node.Bank.SafeBox.EndThreadSave;
+          end;
+        finally
+          TNode.Node.EnableNewBlocks;
+        end;
+      end else begin
+        TLog.NewLog(lterror,CT_LogSender,Format('No received response for %s',[TNetData.OperationToText(CT_NetOp_GetSafeBox)]));
+      end;
+    finally
+      SendData.Free;
+      ReceiveData.free;
+    end;
+  end;
+
 var rid : Cardinal;
   bufferdata : TMemoryStream;
   headerdata : TNetHeaderData;
@@ -1179,7 +1285,11 @@ begin
     FMaxRemoteOperationBlock := Connection.FRemoteOperationBlock;
     if TNode.Node.Bank.BlocksCount=0 then begin
       TLog.NewLog(ltdebug,CT_LogSender,'I have no blocks');
-      Connection.Send_GetBlocks(0,10,rid);
+      If Connection.FRemoteOperationBlock.protocol_version>=CT_PROTOCOL_2 then begin
+        DownloadSafeBox(False);
+      end else begin
+        Connection.Send_GetBlocks(0,10,rid);
+      end;
       exit;
     end;
     TLog.NewLog(ltdebug,CT_LogSender,'Starting GetNewBlockChainFromClient at client:'+Connection.ClientRemoteAddr+
@@ -1190,18 +1300,22 @@ begin
     If Not Do_GetOperationBlock(my_op.block,5000,client_op) then begin
       TLog.NewLog(lterror,CT_LogSender,'Cannot receive information about my block ('+inttostr(my_op.block)+')...');
       // Disabled at Build 1.0.6 >  Connection.DisconnectInvalidClient(false,'Cannot receive information about my block ('+inttostr(my_op.block)+')... Invalid client. Disconnecting');
-      exit;
+      Exit;
     end;
 
     if (NOT TPCOperationsComp.EqualsOperationBlock(my_op,client_op)) then begin
       TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is not equal... received: '+TPCOperationsComp.OperationBlockToText(client_op)+' My: '+TPCOperationsComp.OperationBlockToText(my_op));
       if Not FindLastSameBlockByOperationsBlock(0,client_op.block,client_op) then begin
         TLog.NewLog(ltinfo,CT_LogSender,'No found base block to start process... Receiving ALL');
-        GetNewBank(-1);
+        If (Connection.FRemoteOperationBlock.protocol_version>=CT_PROTOCOL_2) then begin
+          DownloadSafeBox(False);
+        end else begin
+          GetNewBank(-1);
+        end;
       end else begin
         TLog.NewLog(ltinfo,CT_LogSender,'Found base new block: '+TPCOperationsComp.OperationBlockToText(client_op));
         // Move operations to orphan folder... (temporal... waiting for a confirmation)
-        GetNewBank(client_op.block);
+        GetNewBank(client_op.block+1);
       end;
     end else begin
       TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is ok! Need to download new blocks starting at '+inttostr(my_op.block+1));
@@ -1408,6 +1522,7 @@ begin
     CT_NetOp_GetOperationsBlock : Result := 'GET OPERATIONS BLOCK';
     CT_NetOp_NewBlock : Result := 'NEW BLOCK';
     CT_NetOp_AddOperations : Result := 'ADD OPERATIONS';
+    CT_NetOp_GetSafeBox : Result := 'GET SAFEBOX';
   else Result := 'UNKNOWN OPERATION '+Inttohex(operation,4);
   end;
 end;
@@ -1609,39 +1724,46 @@ Var Pnsa : PNodeServerAddress;
   lns : TList;
   i : Integer;
 begin
-  if Client.Connected then Client.Disconnect;
-  lns := TNetData.NetData.NodeServersAddresses.LockList;
-  try
-    i := TNetData.NetData.IndexOfNetClient(lns,ServerIp,ServerPort);
-    if (i>=0) then Pnsa := lns[i]
-    else Pnsa := Nil;
-    if Assigned(Pnsa) then Pnsa^.netConnection := Self;
-  finally
-    TNetData.NetData.NodeServersAddresses.UnlockList;
-  end;
-
-  TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+  If FIsConnecting then Exit;
   Try
-    Client.RemoteHost := ServerIP;
-    if ServerPort<=0 then ServerPort := CT_NetServer_Port;
-    Client.RemotePort := ServerPort;
-    TLog.NewLog(ltDebug,Classname,'Trying to connect to a server at: '+ClientRemoteAddr);
-    TNetData.NetData.NotifyNetConnectionUpdated;
-    Result := Client.Connect;
-  Finally
-    FNetLock.Release;
-  End;
-  if Result then begin
-    TLog.NewLog(ltDebug,Classname,'Connected to a possible server at: '+ClientRemoteAddr);
-    Result := Send_Hello(ntp_request,TNetData.NetData.NewRequestId);
-  end else begin
-    TLog.NewLog(ltDebug,Classname,'Cannot connect to a server at: '+ClientRemoteAddr);
+    FIsConnecting:=True;
+    if Client.Connected then Client.Disconnect;
+    lns := TNetData.NetData.NodeServersAddresses.LockList;
+    try
+      i := TNetData.NetData.IndexOfNetClient(lns,ServerIp,ServerPort);
+      if (i>=0) then Pnsa := lns[i]
+      else Pnsa := Nil;
+      if Assigned(Pnsa) then Pnsa^.netConnection := Self;
+    finally
+      TNetData.NetData.NodeServersAddresses.UnlockList;
+    end;
+
+    TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+    Try
+      Client.RemoteHost := ServerIP;
+      if ServerPort<=0 then ServerPort := CT_NetServer_Port;
+      Client.RemotePort := ServerPort;
+      TLog.NewLog(ltDebug,Classname,'Trying to connect to a server at: '+ClientRemoteAddr);
+      TNetData.NetData.NotifyNetConnectionUpdated;
+      Result := Client.Connect;
+    Finally
+      FNetLock.Release;
+    End;
+    if Result then begin
+      TLog.NewLog(ltDebug,Classname,'Connected to a possible server at: '+ClientRemoteAddr);
+      Result := Send_Hello(ntp_request,TNetData.NetData.NewRequestId);
+    end else begin
+      TLog.NewLog(ltDebug,Classname,'Cannot connect to a server at: '+ClientRemoteAddr);
+    end;
+  finally
+    FIsConnecting:=False;
   end;
 end;
 
 constructor TNetConnection.Create(AOwner: TComponent);
 begin
   inherited;
+  FIsConnecting:=False;
   FIsDownloadingBlocks := false;
   FHasReceivedData := false;
   FNetProtocolVersion.protocol_version := 0; // 0 = unknown
@@ -1758,7 +1880,7 @@ begin
   TNetData.NetData.NotifyNodeServersUpdated;
 end;
 
-Procedure TNetConnection.DoProcessBuffer;
+procedure TNetConnection.DoProcessBuffer;
 Var HeaderData : TNetHeaderData;
   ms : TMemoryStream;
   ops : AnsiString;
@@ -1821,7 +1943,7 @@ begin
       if Not Assigned(opclass) then exit;
       op := opclass.Create;
       Try
-        op.LoadFromStream(DataBuffer);
+        op.LoadFromNettransfer(DataBuffer);
         operations.AddOperationToHashTree(op);
       Finally
         op.Free;
@@ -1926,7 +2048,7 @@ begin
 end;
 
 procedure TNetConnection.DoProcess_GetBlocks_Response(HeaderData: TNetHeaderData; DataBuffer: TStream);
-  var op, localop : TPCOperationsComp;
+  var op : TPCOperationsComp;
     opcount,i : Cardinal;
     newBlockAccount : TBlockAccount;
   errors : AnsiString;
@@ -1938,6 +2060,10 @@ begin
       errors := 'Not response';
       exit;
     end;
+    If HeaderData.is_error then begin
+      DoDisconnect := false;
+      exit; //
+    end;
     // DataBuffer contains: from and to
     errors := 'Invalid structure';
     op := TPCOperationsComp.Create(nil);
@@ -1961,14 +2087,9 @@ begin
           end else begin
             // Is not a valid entry????
             // Perhaps an orphan blockchain: Me or Client!
-            localop := TPCOperationsComp.Create(nil);
-            Try
-              TNode.Node.Bank.LoadOperations(localop,TNode.Node.Bank.BlocksCount-1);
-              TLog.NewLog(ltinfo,Classname,'Distinct operation block found! My:'+
-                  TPCOperationsComp.OperationBlockToText(localop.OperationBlock)+' remote:'+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' Errors: '+errors);
-            Finally
-              localop.Free;
-            End;
+            TLog.NewLog(ltinfo,Classname,'Distinct operation block found! My:'+
+                TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.SafeBox.Block(TNode.Node.Bank.BlocksCount-1).blockchainInfo)+
+                ' remote:'+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' Errors: '+errors);
           end;
         end else begin
           // Receiving an unexpected operationblock
@@ -1995,10 +2116,10 @@ end;
 procedure TNetConnection.DoProcess_GetOperationsBlock_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 Const CT_Max_Positions = 10;
 Var inc_b,b,b_start,b_end, total_b:Cardinal;
-  op : TPCOperationsComp;
   db,msops : TMemoryStream;
   errors, blocksstr : AnsiString;
   DoDisconnect : Boolean;
+  ob : TOperationBlock;
 begin
   blocksstr := '';
   DoDisconnect := true;
@@ -2034,35 +2155,33 @@ begin
     if (b_end>=TNode.Node.Bank.BlocksCount) then b_end := TNode.Node.Bank.BlocksCount-1;
     inc_b := ((b_end - b_start) DIV CT_Max_Positions)+1;
     msops := TMemoryStream.Create;
-    op := TPCOperationsComp.Create(TNode.Node.Bank);
-     try
-       b := b_start;
-       total_b := 0;
-       repeat
-         If TNode.Node.bank.LoadOperations(op,b) then begin
-           op.SaveBlockToStream(true,msops);
-           blocksstr := blocksstr + inttostr(b)+',';
-           b := b + inc_b;
-           inc(total_b);
-         end else begin
-           errors := 'Operations of block:'+inttostr(b)+' not found';
-           SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,errors);
-           exit;
-         end;
-       until (b > b_end);
-       db := TMemoryStream.Create;
-       try
-         db.Write(total_b,4);
-         db.WriteBuffer(msops.Memory^,msops.Size);
-         Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
-       finally
-         db.Free;
-       end;
-     finally
-       msops.Free;
-       op.Free;
-     end;
-     TLog.NewLog(ltdebug,Classname,'Sending '+inttostr(total_b)+' operations block from block '+inttostr(b_start)+' to '+inttostr(b_end)+' '+blocksstr);
+    try
+      b := b_start;
+      total_b := 0;
+      repeat
+        ob := TNode.Node.Bank.SafeBox.Block(b).blockchainInfo;
+        If TPCOperationsComp.SaveOperationBlockToStream(ob,msops) then begin
+          blocksstr := blocksstr + inttostr(b)+',';
+          b := b + inc_b;
+          inc(total_b);
+        end else begin
+          errors := 'ERROR DEV 20170522-1 block:'+inttostr(b);
+          SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,errors);
+          exit;
+        end;
+      until (b > b_end);
+      db := TMemoryStream.Create;
+      try
+       db.Write(total_b,4);
+       db.WriteBuffer(msops.Memory^,msops.Size);
+       Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
+      finally
+       db.Free;
+      end;
+    finally
+      msops.Free;
+    end;
+    TLog.NewLog(ltdebug,Classname,'Sending '+inttostr(total_b)+' operations block from block '+inttostr(b_start)+' to '+inttostr(b_end)+' '+blocksstr);
   finally
     if DoDisconnect then begin
       DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
@@ -2070,6 +2189,62 @@ begin
   end;
 end;
 
+procedure TNetConnection.DoProcess_GetSafeBox_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
+Var _blockcount : Cardinal;
+    _safeboxHash : TRawBytes;
+    _from,_to : Cardinal;
+  sbStream : TStream;
+  responseStream : TStream;
+  antPos : Int64;
+  sbHeader : TPCSafeBoxHeader;
+  errors : AnsiString;
+begin
+  {
+  This call is used to obtain a chunk of the safebox
+  Request:
+  BlockCount (4 bytes) - The safebox checkpoint
+  SafeboxHash (AnsiString) - The safeboxhash of that checkpoint
+  StartPos (4 bytes) - The start index (0..BlockCount-1)
+  EndPos   (4 bytes) - The final index (0..BlockCount-1)
+    If valid info:
+      - If available will return a LZIP chunk of safebox
+      - If not available (requesting for an old safebox) will retun not available
+    If not valid will disconnect
+  }
+  DataBuffer.Read(_blockcount,SizeOf(_blockcount));
+  TStreamOp.ReadAnsiString(DataBuffer,_safeboxHash);
+  DataBuffer.Read(_from,SizeOf(_from));
+  DataBuffer.Read(_to,SizeOf(_to));
+  //
+  sbStream := TNode.Node.Bank.Storage.CreateSafeBoxStream(_blockcount);
+  try
+    responseStream := TMemoryStream.Create;
+    try
+      If Not Assigned(sbStream) then begin
+        SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Safebox for block %d not found',[_blockcount]));
+        exit;
+      end;
+      antPos := sbStream.Position;
+      TPCSafeBox.LoadSafeBoxStreamHeader(sbStream,sbHeader);
+      If sbHeader.safeBoxHash<>_safeboxHash then begin
+        DisconnectInvalidClient(false,Format('Invalid safeboxhash on GetSafeBox request (Real:%s > Requested:%s)',[TCrypto.ToHexaString(sbHeader.safeBoxHash),TCrypto.ToHexaString(_safeboxHash)]));
+        exit;
+      end;
+      // Response:
+      sbStream.Position:=antPos;
+      If not TPCChunk.SaveSafeBoxChunkFromSafeBox(sbStream,responseStream,_from,_to,errors) then begin
+        TLog.NewLog(ltError,Classname,'Error saving chunk: '+errors);
+        exit;
+      end;
+      // Sending
+      Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,responseStream);
+    finally
+    end;
+  finally
+    FreeAndNil(sbStream);
+  end;
+end;
+
 procedure TNetConnection.DoProcess_Hello(HeaderData: TNetHeaderData; DataBuffer: TStream);
 var op, myLastOp : TPCOperationsComp;
     errors : AnsiString;
@@ -2119,7 +2294,7 @@ Begin
     if (connection_has_a_server>0) And (Not SameText(Client.RemoteHost,'localhost')) And (Not SameText(Client.RemoteHost,'127.0.0.1'))
       And (Not SameText('192.168.',Copy(Client.RemoteHost,1,8)))
       And (Not SameText('10.',Copy(Client.RemoteHost,1,3)))
-      And (Not TAccountComp.Equal(FClientPublicKey,TNetData.NetData.FNodePrivateKey.PublicKey)) then begin
+      And (Not TAccountComp.EqualAccountKeys(FClientPublicKey,TNetData.NetData.FNodePrivateKey.PublicKey)) then begin
       nsa := CT_TNodeServerAddress_NUL;
       nsa.ip := Client.RemoteHost;
       nsa.port := connection_has_a_server;
@@ -2162,7 +2337,7 @@ Begin
         if (HeaderData.header_type=ntp_request) then begin
           Send_Hello(ntp_response,HeaderData.request_id);
         end;
-        if (TAccountComp.Equal(FClientPublicKey,TNetData.NetData.FNodePrivateKey.PublicKey)) then begin
+        if (TAccountComp.EqualAccountKeys(FClientPublicKey,TNetData.NetData.FNodePrivateKey.PublicKey)) then begin
           DisconnectInvalidClient(true,'MySelf disconnecting...');
           exit;
         end;
@@ -2374,6 +2549,11 @@ begin
                   CT_NetOp_AddOperations : Begin
                     DoProcess_AddOperations(HeaderData,ReceiveDataBuffer);
                   End;
+                  CT_NetOp_GetSafeBox : Begin
+                    if HeaderData.header_type=ntp_request then
+                      DoProcess_GetSafeBox_Request(HeaderData,ReceiveDataBuffer)
+                    else DisconnectInvalidClient(false,'Received '+TNetData.HeaderDataToText(HeaderData));
+                  end
                 else
                   DisconnectInvalidClient(false,'Invalid operation: '+TNetData.HeaderDataToText(HeaderData));
                 end;
@@ -2611,7 +2791,8 @@ begin
   end;
 end;
 
-procedure TNetConnection.SendError(NetTranferType: TNetTransferType; operation, request_id, error_code: Integer; error_text: AnsiString);
+procedure TNetConnection.SendError(NetTranferType: TNetTransferType; operation,
+  request_id: Integer; error_code: Integer; error_text: AnsiString);
 var buffer : TStream;
 begin
   buffer := TMemoryStream.Create;
@@ -2651,14 +2832,14 @@ begin
         for i := 0 to FBufferToSendOperations.OperationsCount-1 do begin
           optype := FBufferToSendOperations.GetOperation(i).OpType;
           data.Write(optype,1);
-          FBufferToSendOperations.GetOperation(i).SaveToStream(data);
+          FBufferToSendOperations.GetOperation(i).SaveToNettransfer(data);
         end;
         Send(ntp_autosend,CT_NetOp_AddOperations,0,request_id,data);
         FBufferToSendOperations.ClearHastThree;
       finally
         data.Free;
       end;
-    end;
+    end else TLog.NewLog(ltdebug,ClassName,'Not sending any operations to '+ClientRemoteAddr+' ('+IntToStr(Operations.OperationsCount)+')');
   finally
     FNetLock.Release;
   end;
@@ -2717,7 +2898,6 @@ function TNetConnection.Send_Hello(NetTranferType : TNetTransferType; request_id
       }
 var data : TStream;
   i : Integer;
-  op : TPCOperationsComp;
   nsa : TNodeServerAddress;
   nsarr : TNodeServerAddressArray;
   w : Word;
@@ -2742,13 +2922,7 @@ begin
     currunixtimestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
     data.Write(currunixtimestamp,4);
     // Save last operations block
-    op := TPCOperationsComp.Create(nil);
-    try
-      if (TNode.Node.Bank.BlocksCount>0) then TNode.Node.Bank.LoadOperations(op,TNode.Node.Bank.BlocksCount-1);
-      op.SaveBlockToStream(true,data);
-    finally
-      op.free;
-    end;
+    TPCOperationsComp.SaveOperationBlockToStream(TNode.Node.Bank.LastOperationBlock,data);
     nsarr := TNetData.NetData.GetValidNodeServers(true,20);
     i := length(nsarr);
     data.Write(i,4);
@@ -2788,7 +2962,8 @@ begin
   End;
 end;
 
-function TNetConnection.Send_NewBlockFound(Const NewBlock : TPCOperationsComp) : Boolean;
+function TNetConnection.Send_NewBlockFound(const NewBlock: TPCOperationsComp
+  ): Boolean;
 var data : TStream;
   request_id : Integer;
 begin
@@ -2975,7 +3150,7 @@ begin
     end;
     if Terminated then exit;
   Finally
-    if not ok then begin
+    if (not ok) And (Not Terminated) then begin
       DebugStep := 'Destroying non connected';
       NC.FinalizeConnection;
     end;
@@ -3216,7 +3391,9 @@ begin
       try
         FTerminatedAllConnections := l.Count=0;
         for i := 0 to l.Count-1 do begin
-          If (TObject(l[i]) is TNetClient) And (not TNetConnection(l[i]).Connected) And (TNetConnection(l[i]).FDoFinalizeConnection) then begin
+          If (TObject(l[i]) is TNetClient) And (not TNetConnection(l[i]).Connected)
+            And (TNetConnection(l[i]).FDoFinalizeConnection)
+            And (Not TNetConnection(l[i]).IsConnecting) then begin
             l_to_del.Add(l[i]);
           end;
         end;
@@ -3399,4 +3576,8 @@ begin
   end;
 end;
 
+initialization
+  _NetData := Nil;
+finalization
+  FreeAndNil(_NetData);
 end.

+ 65 - 28
Units/PascalCoin/UNode.pas

@@ -162,7 +162,7 @@ begin
   Result := false;
   errors := '';
   if FDisabledsNewBlocksCount>0 then begin
-    TLog.NewLog(ltinfo,Classname,Format('Cannot Add new BlockChain due is adding disabled - Connection:%s NewBlock:%s',[
+    TLog.NewLog(lterror,Classname,Format('Cannot Add new BlockChain due is adding disabled - Connection:%s NewBlock:%s',[
     Inttohex(PtrInt(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock)]));
     errors := 'Adding blocks is disabled';
     exit;
@@ -228,6 +228,8 @@ begin
           if (j=0) Or (j=Bank.LastBlockFound.OperationBlock.block) then begin
             // Only will "re-send" operations that where received on last block and not included in this one
             opsht.AddOperationToHashTree(FOperations.Operation[i]);
+            // Add to sent operations
+            FSentOperations.Add(FOperations.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
           end else begin
             TLog.NewLog(ltError,ClassName,'Sanitized operation not included (j='+IntToStr(j)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);
           end;
@@ -241,7 +243,7 @@ begin
         // Clean sent operations buffer
         j := 0;
         for i := FSentOperations.Count-1 downto 0 do begin
-          If (FSentOperations.GetTag(i)<Bank.LastBlockFound.OperationBlock.block-3) then begin
+          If (FSentOperations.GetTag(i)<Bank.LastBlockFound.OperationBlock.block-Random(5)+1) then begin
             FSentOperations.Delete(i);
             inc(j);
           end;
@@ -320,31 +322,49 @@ begin
       for j := 0 to Operations.OperationsCount-1 do begin
         ActOp := Operations.GetOperation(j);
         If (FOperations.OperationsHashTree.IndexOfOperation(ActOp)<0) And (FSentOperations.GetTag(ActOp.Sha256)=0) then begin
-          // Buffer to prevent cyclic sending new on 1.5.4
-          FSentOperations.Add(ActOp.Sha256,FOperations.OperationBlock.block);
-          if (FOperations.AddOperation(true,ActOp,e)) then begin
-            inc(Result);
-            valids_operations.AddOperationToHashTree(ActOp);
-            TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),Operations.OperationsCount,ActOp.ToString]));
-            if Assigned(OperationsResult) then begin
-              TPCOperation.OperationToOperationResume(0,ActOp,ActOp.SenderAccount,OPR);
-              OPR.NOpInsideBlock:=FOperations.Count-1;
-              OPR.Balance := FOperations.SafeBoxTransaction.Account(ActOp.SenderAccount).balance;
-              OperationsResult.Add(OPR);
-            end;
-          end else begin
+          // Protocol 2 limitation: In order to prevent spam of operations without Fee, will protect it
+          If (ActOp.OperationFee=0) And (Bank.SafeBox.CurrentProtocol>=CT_PROTOCOL_2) And
+             (FOperations.OperationsHashTree.CountOperationsBySameSignerWithoutFee(ActOp.SignerAccount)>=CT_MaxAccountOperationsPerBlockWithoutFee) then begin
+            e := Format('Account %s zero fee operations per block limit:%d',[TAccountComp.AccountNumberToAccountTxtNumber(ActOp.SignerAccount),CT_MaxAccountOperationsPerBlockWithoutFee]);
             if (errors<>'') then errors := errors+' ';
             errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(Operations.OperationsCount)+':'+e;
             TLog.NewLog(ltdebug,Classname,Format('AddOperation invalid/duplicated %d/%d: %s  - Error:%s',
               [(j+1),Operations.OperationsCount,ActOp.ToString,e]));
             if Assigned(OperationsResult) then begin
-              TPCOperation.OperationToOperationResume(0,ActOp,ActOp.SenderAccount,OPR);
+              TPCOperation.OperationToOperationResume(0,ActOp,ActOp.SignerAccount,OPR);
               OPR.valid := false;
               OPR.NOpInsideBlock:=-1;
               OPR.OperationHash := '';
               OPR.errors := e;
               OperationsResult.Add(OPR);
             end;
+          end else begin
+            // Buffer to prevent cyclic sending new on 1.5.4
+            FSentOperations.Add(ActOp.Sha256,FOperations.OperationBlock.block);
+            if (FOperations.AddOperation(true,ActOp,e)) then begin
+              inc(Result);
+              valids_operations.AddOperationToHashTree(ActOp);
+              TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),Operations.OperationsCount,ActOp.ToString]));
+              if Assigned(OperationsResult) then begin
+                TPCOperation.OperationToOperationResume(0,ActOp,ActOp.SignerAccount,OPR);
+                OPR.NOpInsideBlock:=FOperations.Count-1;
+                OPR.Balance := FOperations.SafeBoxTransaction.Account(ActOp.SignerAccount).balance;
+                OperationsResult.Add(OPR);
+              end;
+            end else begin
+              if (errors<>'') then errors := errors+' ';
+              errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(Operations.OperationsCount)+':'+e;
+              TLog.NewLog(ltdebug,Classname,Format('AddOperation invalid/duplicated %d/%d: %s  - Error:%s',
+                [(j+1),Operations.OperationsCount,ActOp.ToString,e]));
+              if Assigned(OperationsResult) then begin
+                TPCOperation.OperationToOperationResume(0,ActOp,ActOp.SignerAccount,OPR);
+                OPR.valid := false;
+                OPR.NOpInsideBlock:=-1;
+                OPR.OperationHash := '';
+                OPR.errors := e;
+                OperationsResult.Add(OPR);
+              end;
+            end;
           end;
         end else begin
           {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('AddOperation made before %d/%d: %s',[(j+1),Operations.OperationsCount,ActOp.ToString]));{$ENDIF}
@@ -615,18 +635,19 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
           And (block_number >= (account_number DIV CT_AccountsPerBlock))
           And (nOpsCounter <= EndOperation) do begin
           last_block_number := block_number;
-          next_block_number := 0;
+          next_block_number := block_number;
           l.Clear;
           If not Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
-            TLog.NewLog(lterror,ClassName,'Error searching for block '+inttostr(block_number));
+            TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(block_number)+' not found. Cannot read operations');
             exit;
           end;
           opc.OperationsHashTree.GetOperationsAffectingAccount(account_number,l);
           for i := l.Count - 1 downto 0 do begin
             op := opc.Operation[PtrInt(l.Items[i])];
             if (i=0) then begin
-              If op.SenderAccount=account_number then next_block_number := op.Previous_Sender_updated_block
-              else next_block_number := op.Previous_Destination_updated_block;
+              If op.SignerAccount=account_number then next_block_number := op.Previous_Signer_updated_block
+              else if (op.DestinationAccount=account_number) then next_block_number := op.Previous_Destination_updated_block
+              else if (op.SellerAccount=account_number) then next_block_number:=op.Previous_Seller_updated_block;
             end;
             If TPCOperation.OperationToOperationResume(block_number,Op,account_number,OPR) then begin
               OPR.NOpInsideBlock := Op.tag; // Note: Used Op.tag to include operation index inside a list
@@ -686,6 +707,7 @@ var account,n_operation : Cardinal;
   i : Integer;
   op : TPCOperation;
   initial_block, aux_block : Cardinal;
+  opHashValid, opHash_OLD : TRawBytes;
 begin
   Result := False;
   // Decode OperationHash
@@ -698,8 +720,12 @@ begin
     FOperations.Lock;
     Try
       For i:=0 to FOperations.Count-1 do begin
-        If (FOperations.Operation[i].SenderAccount=account) then begin
-          If (TPCOperation.OperationHash(FOperations.Operation[i],0)=OperationHash) then begin
+        op := FOperations.Operation[i];
+        If (op.SignerAccount=account) then begin
+          opHashValid := TPCOperation.OperationHashValid(op,0);
+          opHash_OLD := TPCOperation.OperationHash_OLD(op,0);
+          If (opHashValid=OperationHash) or
+            ((FBank.BlocksCount<CT_Protocol_Upgrade_v2_MinBlock) And (opHash_OLD=OperationHash)) then begin
             operation_block_index:=i;
             OperationComp.CopyFrom(FOperations);
             Result := true;
@@ -721,21 +747,32 @@ begin
     If Not Bank.LoadOperations(OperationComp,block) then exit;
     For i:=OperationComp.Count-1 downto 0 do begin
       op := OperationComp.Operation[i];
-      if (op.SenderAccount=account) then begin
+      if (op.SignerAccount=account) then begin
         If (op.N_Operation<n_operation) then exit; // n_operation is greaten than found
         If (op.N_Operation=n_operation) then begin
           // Possible candidate or dead
-          If TPCOperation.OperationHash(op,initial_block)=OperationHash then begin
+          opHashValid := TPCOperation.OperationHashValid(op,initial_block);
+          If (opHashValid=OperationHash) then begin
             operation_block_index:=i;
             Result := true;
             exit;
+          end else if (block<CT_Protocol_Upgrade_v2_MinBlock) then begin
+            opHash_OLD := TPCOperation.OperationHash_OLD(op,initial_block);
+            if (opHash_OLD=OperationHash) then begin
+              operation_block_index:=i;
+              Result := true;
+              exit;
+            end else exit; // Not found!
           end else exit; // Not found!
         end;
-        If op.Previous_Sender_updated_block>block then exit;
-        block := op.Previous_Sender_updated_block;
-      end else if op.IsDestinationAccount(account) then begin
+        If op.Previous_Signer_updated_block>block then exit;
+        block := op.Previous_Signer_updated_block;
+      end else if op.DestinationAccount=account then begin
         If op.Previous_Destination_updated_block > block then exit;
         block := op.Previous_Destination_updated_block;
+      end else if op.SellerAccount=account then begin
+        If op.Previous_Seller_updated_block > block then exit;
+        block := op.Previous_Seller_updated_block;
       end;
     end;
     if (block>=aux_block) then exit; // Error... not found a valid block positioning
@@ -847,7 +884,7 @@ begin
   if FNode=Value then exit;
   if Assigned(FNode) then begin
     FNode.RemoveFreeNotification(Self);
-    FNode.FNotifyList.Add(Self);
+    FNode.FNotifyList.Remove(Self);
   end;
   FNode := Value;
   if Assigned(FNode) then begin

File diff suppressed because it is too large
+ 720 - 65
Units/PascalCoin/UOpTransaction.pas


+ 1 - 1
Units/PascalCoin/UPoolMinerThreads.pas

@@ -380,7 +380,7 @@ begin
         auxXXXXX := minervfw;
         auxXXXXX.target:= ((((auxXXXXX.target AND $FF000000) SHR 24)-FTestingPoWLeftBits) SHL 24) + (minervfw.target AND $00FFFFFF);
         If auxXXXXX.target<CT_MinCompactTarget then auxXXXXX.target:=CT_MinCompactTarget;
-        auxXXXXX.target_pow:=TPCBank.TargetFromCompact(auxXXXXX.target);
+        auxXXXXX.target_pow:=TPascalCoinProtocol.TargetFromCompact(auxXXXXX.target);
         TCustomMinerDeviceThread(l[i]).SetMinerValuesForWork(auxXXXXX);
       end else begin
         TCustomMinerDeviceThread(l[i]).SetMinerValuesForWork(minervfw);

+ 19 - 17
Units/PascalCoin/UPoolMining.pas

@@ -553,13 +553,15 @@ begin
       P^.OperationsComp.timestamp := P^.SentMinTimestamp; // Best practices 1.5.3
       OpB := P^.OperationsComp.OperationBlock;
       if (OpB.block<>0) And (OpB.block <> (FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.block+1)) then begin
-        TLog.NewLog(ltError,ClassName,'ERROR DEV 20170228-1 '+TPCOperationsComp.OperationBlockToText(OpB)+'<>'+TPCOperationsComp.OperationBlockToText(FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock));
+        // A new block is generated meanwhile ... do not include
+        TLog.NewLog(ltDebug,ClassName,'Generated a new block meanwhile ... '+TPCOperationsComp.OperationBlockToText(OpB)+'<>'+TPCOperationsComp.OperationBlockToText(FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock));
         P^.OperationsComp.Free;
         Dispose(P);
-        raise Exception.Create('ERROR DEV 20170228-1');
+        P := Nil;
+      end else begin
+        i := l.Add(P);
+        TLog.NewLog(ltDebug,ClassName,'Added new job '+IntToStr(i+1)+'/'+IntToStr(l.Count));
       end;
-      i := l.Add(P);
-      TLog.NewLog(ltDebug,ClassName,'Added new job '+IntToStr(i+1)+'/'+IntToStr(l.Count));
     end;
     // Clean buffer jobs
     while (l.Count>CT_MAX_BUFFER_JOBS) do begin
@@ -724,7 +726,7 @@ begin
       try
         if (Not (TPCOperationsComp.EqualsOperationBlock(FMinerOperations.OperationBlock,MasterOp.OperationBlock))) then begin
           FMinerOperations.Clear(true);
-          if MasterOp.Count>=0 then begin
+          if MasterOp.Count>0 then begin
             // First round: Select with fee > 0
             i := 0;
             while (tree.OperationsCount<MaxOperationsPerBlock) And (i<MasterOp.OperationsHashTree.OperationsCount) do begin
@@ -740,7 +742,7 @@ begin
             while (tree.OperationsCount<MaxOperationsPerBlock) And (i<MasterOp.OperationsHashTree.OperationsCount) And (j<Max0FeeOperationsPerBlock) do begin
               op := MasterOp.OperationsHashTree.GetOperation(i);
               if op.OperationFee=0 then begin
-                DoAdd(op,false);
+                DoAdd(op,True);
                 inc(j);
               end;
               inc(i);
@@ -810,7 +812,6 @@ begin
     end;
     _timestamp := params.AsCardinal('timestamp',0);
     _nOnce := params.AsCardinal('nonce',0);
-    _targetPoW := FNodeNotifyEvents.Node.Bank.GetActualTargetHash;
     l := FPoolJobs.LockList;
     Try
       i := l.Count-1;
@@ -821,6 +822,7 @@ begin
         If (P^.SentMinTimestamp<=_timestamp) then begin
           P^.OperationsComp.timestamp := _timestamp;
           P^.OperationsComp.nonce := _nOnce;
+          _targetPoW := FNodeNotifyEvents.Node.Bank.SafeBox.GetActualTargetHash(P^.OperationsComp.OperationBlock.protocol_version=CT_PROTOCOL_2);
           if (P^.OperationsComp.OperationBlock.proof_of_work<=_targetPoW) then begin
             // Candidate!
             nbOperations := TPCOperationsComp.Create(Nil);
@@ -962,7 +964,7 @@ begin
     params.GetAsVariant('payload_start').Value := TCrypto.ToHexaString( Operations.OperationBlock.block_payload );
     params.GetAsVariant('part3').Value := TCrypto.ToHexaString( Operations.PoW_Digest_Part3 );
     params.GetAsVariant('target').Value := Operations.OperationBlock.compact_target;
-    params.GetAsVariant('target_pow').Value := TCrypto.ToHexaString(TPCBank.TargetFromCompact(Operations.OperationBlock.compact_target));
+    params.GetAsVariant('target_pow').Value := TCrypto.ToHexaString(TPascalCoinProtocol.TargetFromCompact(Operations.OperationBlock.compact_target));
 
     ts := TNetData.NetData.NetworkAdjustedTime.GetAdjustedTime;
     if (ts<FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp) then begin
@@ -1019,7 +1021,7 @@ end;
 
 procedure TPoolMiningServer.SetMinerAccountKey(const Value: TAccountKey);
 begin
-  if TAccountComp.Equal(FMinerAccountKey,Value) then exit;
+  if TAccountComp.EqualAccountKeys(FMinerAccountKey,Value) then exit;
   FMinerAccountKey := Value;
   TLog.NewLog(ltdebug,ClassName,'Assigning Miner account key to: '+TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(Value)));
   CaptureNewJobAndSendToMiners;
@@ -1179,28 +1181,28 @@ Var _t : Cardinal;
 begin
   FMinerValuesForWork := Value;
   If FStratum_Target_PoW<>'' then begin
-    FMinerValuesForWork.target:=TPCBank.TargetToCompact(FStratum_Target_PoW);
-    FMinerValuesForWork.target_pow:=TPCBank.TargetFromCompact(FMinerValuesForWork.target);
+    FMinerValuesForWork.target:=TPascalCoinProtocol.TargetToCompact(FStratum_Target_PoW);
+    FMinerValuesForWork.target_pow:=TPascalCoinProtocol.TargetFromCompact(FMinerValuesForWork.target);
   end else begin
     // Check that target and target_pow are equal!
-    _t_pow := TPCBank.TargetFromCompact(FMinerValuesForWork.target);
+    _t_pow := TPascalCoinProtocol.TargetFromCompact(FMinerValuesForWork.target);
     if (length(FMinerValuesForWork.target_pow)=32) then begin
-      _t := TPCBank.TargetToCompact(FMinerValuesForWork.target_pow);
+      _t := TPascalCoinProtocol.TargetToCompact(FMinerValuesForWork.target_pow);
       if (FMinerValuesForWork.target<CT_MinCompactTarget) then begin
         // target has no valid value... assigning compact_target!
-        FMinerValuesForWork.target:=TPCBank.TargetToCompact(_t_pow);
+        FMinerValuesForWork.target:=TPascalCoinProtocol.TargetToCompact(_t_pow);
       end else if (_t_pow<>FMinerValuesForWork.target_pow) Or (_t<>FMinerValuesForWork.target) then begin
         TLog.NewLog(ltError,Classname,'Received bad values for target and target_pow!');
         If (FMinerValuesForWork.target<CT_MinCompactTarget) then begin
-          FMinerValuesForWork.target_pow:=TPCBank.TargetFromCompact(FMinerValuesForWork.target);
+          FMinerValuesForWork.target_pow:=TPascalCoinProtocol.TargetFromCompact(FMinerValuesForWork.target);
         end else begin
-          FMinerValuesForWork.target:=TPCBank.TargetToCompact(_t_pow);
+          FMinerValuesForWork.target:=TPascalCoinProtocol.TargetToCompact(_t_pow);
         end;
       end;
     end else begin
       if (FMinerValuesForWork.target<CT_MinCompactTarget) then begin
         // target_pow has no value... assigning target!
-        FMinerValuesForWork.target_pow:=TPCBank.TargetFromCompact(FMinerValuesForWork.target);
+        FMinerValuesForWork.target_pow:=TPascalCoinProtocol.TargetFromCompact(FMinerValuesForWork.target);
       end else begin
         // Invalid target and compact_target
         FMinerValuesForWork.target := CT_TMinerValuesForWork_NULL.target;

File diff suppressed because it is too large
+ 920 - 48
Units/PascalCoin/URPC.pas


+ 12 - 9
Units/PascalCoin/UThread.pas

@@ -40,9 +40,11 @@ Type
   public
     Constructor Create(const AName : String);
     Destructor Destroy; override;
+    {$IFDEF HIGHLOG}
     procedure Acquire; override;
     procedure Release; override;
-    function TryEnter: Boolean;
+    function TryEnter: Boolean; override;
+    {$ENDIF}
     Property CurrentThread : Cardinal read FCurrentThread;
     Property WaitingForCounter : Integer read FWaitingForCounter;
     Property StartedTimestamp : Cardinal read FStartedTimestamp;
@@ -64,7 +66,6 @@ Type
     Class function ThreadCount : Integer;
     Class function GetThread(index : Integer) : TPCThread;
     Class function GetThreadByClass(tclass : TPCThreadClass; Exclude : TObject) : TPCThread;
-    Class function TerminateAllThreads(tclass: TPCThreadClass) : Integer;
     Class Procedure ProtectEnterCriticalSection(Const Sender : TObject; var Lock : TPCCriticalSection);
     Class Function TryProtectEnterCriticalSection(Const Sender : TObject; MaxWaitMilliseconds : Cardinal; var Lock : TPCCriticalSection) : Boolean;
     Class Procedure ThreadsListInfo(list: TStrings);
@@ -182,14 +183,13 @@ end;
 
 class procedure TPCThread.ProtectEnterCriticalSection(Const Sender : TObject; var Lock: TPCCriticalSection);
 begin
+  {$IFDEF HIGHLOG}
   if Not Lock.TryEnter then begin
     Lock.Acquire;
   end;
-end;
-
-class function TPCThread.TerminateAllThreads(tclass: TPCThreadClass): Integer;
-begin
-
+  {$ELSE}
+  Lock.Acquire;
+  {$ENDIF}
 end;
 
 class function TPCThread.ThreadClassFound(tclass: TPCThreadClass; Exclude : TObject): Integer;
@@ -336,6 +336,7 @@ end;
 
 { TPCCriticalSection }
 
+{$IFDEF HIGHLOG}
 procedure TPCCriticalSection.Acquire;
 Var continue, logged : Boolean;
   startTC : Cardinal;
@@ -372,7 +373,9 @@ begin
   end;
   FCurrentThread := TThread.CurrentThread.ThreadID;
   FStartedTimestamp := GetTickCount;
+  inherited;
 end;
+{$ENDIF}
 
 constructor TPCCriticalSection.Create(const AName : String);
 begin
@@ -391,6 +394,7 @@ begin
   inherited;
 end;
 
+{$IFDEF HIGHLOG}
 procedure TPCCriticalSection.Release;
 begin
   FCurrentThread := 0;
@@ -418,12 +422,11 @@ begin
     FCounterLock.Release;
   end;
 end;
+{$ENDIF}
 
 initialization
   _threads := TPCThreadList.Create('GLOBAL_THREADS');
 finalization
-  {$IFnDEF FPC}
   FreeAndNil(_threads);
-  {$ENDIF}
 end.
 

+ 2 - 1
Units/PascalCoin/config.inc

@@ -31,7 +31,8 @@
 
   {$DEFINE PRODUCTION}
   {.$DEFINE TESTNET}
-  
+
+  {.$DEFINE SHOW_AVERAGE_TIME_STATS}
   
   // HighLog will result in a higher log generation
   {.$DEFINE HIGHLOG}

+ 4 - 4
Units/PascalCoin/upcdaemon.pas

@@ -148,8 +148,8 @@ var
             FWalletKeys.AddPrivateKey('RANDOM NEW BY DAEMON '+FormatDateTime('yyyy-mm-dd hh:nn:dd',now),ECPK);
             pubkey := ECPK.PublicKey;
             FIniFile.WriteString(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINER_B58_PUBLICKEY,
-              TAccountComp.AccountKeyToExport(pubkey));
-            TLog.NewLog(ltInfo,ClassName, 'Generated new pubkey for miner: '+TAccountComp.AccountKeyToExport(pubkey));
+              TAccountComp.AccountPublicKeyExport(pubkey));
+            TLog.NewLog(ltInfo,ClassName, 'Generated new pubkey for miner: '+TAccountComp.AccountPublicKeyExport(pubkey));
           finally
             ECPK.Free;
           end;
@@ -157,7 +157,7 @@ var
       end else begin
         // pubkey is mine?
         if (FWalletKeys.IndexOfAccountKey(pubkey)<0) then begin
-          TLog.NewLog(lterror,classname,'WARNING: Using a public key without private key in wallet! '+TAccountComp.AccountKeyToExport(pubkey));
+          TLog.NewLog(lterror,classname,'WARNING: Using a public key without private key in wallet! '+TAccountComp.AccountPublicKeyExport(pubkey));
         end;
       end;
       i := FWalletKeys.IndexOfAccountKey(pubkey);
@@ -168,7 +168,7 @@ var
       end;
       maxconnections:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINER_MAX_CONNECTIONS,1000);
       TLog.NewLog(ltinfo,ClassName,Format('Activating RPC Miner Server on port %d, name "%s", max conections %d and public key %s',
-        [port,s,maxconnections,TAccountComp.AccountKeyToExport(pubkey)]));
+        [port,s,maxconnections,TAccountComp.AccountPublicKeyExport(pubkey)]));
       FMinerServer := TPoolMiningServer.Create;
       FMinerServer.UpdateAccountAndPayload(pubkey,s);
       FMinerServer.Port:=port;

+ 134 - 48
Units/Utils/UGridUtils.pas

@@ -31,7 +31,7 @@ uses
 Type
   // TAccountsGrid implements a visual integration of TDrawGrid
   // to show accounts information
-  TAccountColumnType = (act_account_number,act_account_key,act_balance,act_updated,act_n_operation,act_updated_state);
+  TAccountColumnType = (act_account_number,act_account_key,act_balance,act_updated,act_n_operation,act_updated_state,act_name,act_type);
   TAccountColumn = Record
     ColumnType : TAccountColumnType;
     width : Integer;
@@ -115,6 +115,7 @@ Type
     Property BlockStart : Int64 read FBlockStart write SetBlockStart;
     Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
     Procedure SetBlocks(bstart,bend : Int64);
+    Property OperationsResume : TOperationsResumeList read FOperationsResume;
   End;
 
   TBlockChainData = Record
@@ -122,7 +123,7 @@ Type
     Timestamp : Cardinal;
     BlockProtocolVersion,
     BlockProtocolAvailable : Word;
-    OperationsCount : Cardinal;
+    OperationsCount : Integer;
     Volume : Int64;
     Reward, Fee : Int64;
     Target : Cardinal;
@@ -131,9 +132,18 @@ Type
     PoW : TRawBytes;
     SafeBoxHash : TRawBytes;
     AccumulatedWork : UInt64;
+    TimeAverage200 : Real;
+    TimeAverage150 : Real;
+    TimeAverage100 : Real;
+    TimeAverage75 : Real;
+    TimeAverage50 : Real;
+    TimeAverage25 : Real;
+    TimeAverage10 : Real;
   End;
   TBlockChainDataArray = Array of TBlockChainData;
 
+  { TBlockChainGrid }
+
   TBlockChainGrid = Class(TComponent)
   private
     FBlockChainDataArray : TBlockChainDataArray;
@@ -143,6 +153,7 @@ Type
     FDrawGrid: TDrawGrid;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FHashRateAverageBlocksCount: Integer;
+    FShowTimeAverageColumns: Boolean;
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
     procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
@@ -152,7 +163,9 @@ Type
     procedure SetDrawGrid(const Value: TDrawGrid);
     procedure SetMaxBlocks(const Value: Integer);
     procedure SetNode(const Value: TNode);
-    procedure SetHashRateAverageBlocksCount(const Value: Integer); public
+    procedure SetHashRateAverageBlocksCount(const Value: Integer);
+    procedure SetShowTimeAverageColumns(AValue: Boolean);
+ public
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   public
@@ -166,10 +179,11 @@ Type
     Procedure SetBlocks(bstart,bend : Int64);
     Property MaxBlocks : Integer read FMaxBlocks write SetMaxBlocks;
     Property HashRateAverageBlocksCount : Integer read FHashRateAverageBlocksCount write SetHashRateAverageBlocksCount;
+    Property ShowTimeAverageColumns : Boolean read FShowTimeAverageColumns write SetShowTimeAverageColumns;
   End;
 
 Const
-  CT_TBlockChainData_NUL : TBlockChainData = (Block:0;Timestamp:0;BlockProtocolVersion:0;BlockProtocolAvailable:0;OperationsCount:0;Volume:0;Reward:0;Fee:0;Target:0;HashRateKhs:0;MinerPayload:'';PoW:'';SafeBoxHash:'';AccumulatedWork:0);
+  CT_TBlockChainData_NUL : TBlockChainData = (Block:0;Timestamp:0;BlockProtocolVersion:0;BlockProtocolAvailable:0;OperationsCount:-1;Volume:-1;Reward:0;Fee:0;Target:0;HashRateKhs:0;MinerPayload:'';PoW:'';SafeBoxHash:'';AccumulatedWork:0;TimeAverage200:0;TimeAverage150:0;TimeAverage100:0;TimeAverage75:0;TimeAverage50:0;TimeAverage25:0;TimeAverage10:0);
 
 
 implementation
@@ -181,7 +195,7 @@ uses
 { TAccountsGrid }
 
 Const CT_ColumnHeader : Array[TAccountColumnType] Of String =
-  ('Account N.','Key','Balance','Updated','N Oper.','State');
+  ('Account N.','Key','Balance','Updated','N Oper.','State','Name','Type');
 
 function TAccountsGrid.AccountNumber(GridRow: Integer): Int64;
 begin
@@ -206,15 +220,17 @@ begin
   FShowAllAccounts := false;
   FAccountsList := TOrderedCardinalList.Create;
   FDrawGrid := Nil;
-  SetLength(FColumns,4);
+  SetLength(FColumns,5);
   FColumns[0].ColumnType := act_account_number;
-  FColumns[0].width := 80;
-  FColumns[1].ColumnType := act_balance;
-  FColumns[1].width := 100;
-  FColumns[2].ColumnType := act_n_operation;
-  FColumns[2].width := 50;
-  FColumns[3].ColumnType := act_updated_state;
+  FColumns[0].width := 70;
+  FColumns[1].ColumnType := act_name;
+  FColumns[1].width := 110;
+  FColumns[2].ColumnType := act_balance;
+  FColumns[2].width := 70;
+  FColumns[3].ColumnType := act_n_operation;
   FColumns[3].width := 50;
+  FColumns[4].ColumnType := act_updated_state;
+  FColumns[4].width := 50;
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
 end;
@@ -437,7 +453,7 @@ begin
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         act_account_key : Begin
-          s := Tcrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountkey));
+          s := Tcrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountInfo.accountKey));
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
         End;
         act_balance : Begin
@@ -462,20 +478,40 @@ begin
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         act_updated_state : Begin
-          if TAccountComp.IsAccountBlockedByProtocol(account.account,Node.Bank.BlocksCount) then begin
-            DrawGrid.Canvas.Brush.Color := clRed;
-            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
-          end else if ndiff=0 then begin
-            DrawGrid.Canvas.Brush.Color := RGB(255,128,0);
-            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
-          end else if ndiff<=8 then begin
-            DrawGrid.Canvas.Brush.Color := FromColorToColor(RGB(253,250,115),ColorToRGB(clGreen),ndiff-1,8-1);
-            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          if TAccountComp.IsAccountForSale(account.accountInfo) then begin
+            // Show price for sale
+            s := TAccountComp.FormatMoney(account.accountInfo.price);
+            if TAccountComp.IsAccountForSaleAcceptingTransactions(account.accountInfo) then begin
+              if TAccountComp.IsAccountLocked(account.accountInfo,Node.Bank.BlocksCount) then begin
+                DrawGrid.Canvas.Font.Color := clNavy;
+              end else begin
+                DrawGrid.Canvas.Font.Color := clRed;
+              end;
+            end else begin
+              DrawGrid.Canvas.Font.Color := clGrayText
+            end;
+            Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
           end else begin
-            DrawGrid.Canvas.Brush.Color := clGreen;
+            if TAccountComp.IsAccountBlockedByProtocol(account.account,Node.Bank.BlocksCount) then begin
+              DrawGrid.Canvas.Brush.Color := clRed;
+            end else if ndiff=0 then begin
+              DrawGrid.Canvas.Brush.Color := RGB(255,128,0);
+            end else if ndiff<=8 then begin
+              DrawGrid.Canvas.Brush.Color := FromColorToColor(RGB(253,250,115),ColorToRGB(clGreen),ndiff-1,8-1);
+            end else begin
+              DrawGrid.Canvas.Brush.Color := clGreen;
+            end;
             DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
           end;
         End;
+        act_name : Begin
+          s := account.name;
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
+        end;
+        act_type : Begin
+          s := IntToStr(account.account_type);
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
+        end;
       else
         s := '(???)';
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);
@@ -660,6 +696,11 @@ begin
     InflateRect(Rect,-2,-1);
     if (ARow<=FOperationsResume.Count) then begin
       opr := FOperationsResume.OperationResume[ARow-1];
+      If (opr.AffectedAccount=opr.SignerAccount) then begin
+      end else begin
+        if (gdSelected in State) or (gdFocused in State) then begin
+        end else DrawGrid.Canvas.font.Color := clGrayText;
+      end;
       if ACol=0 then begin
         if opr.time=0 then s := '(Pending)'
         else s := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime(opr.time)));
@@ -838,10 +879,10 @@ begin
     if FPendingOperations then begin
       for i := Node.Operations.Count - 1 downto 0 do begin
         Op := Node.Operations.OperationsHashTree.GetOperation(i);
-        If TPCOperation.OperationToOperationResume(0,Op,Op.SenderAccount,OPR) then begin
+        If TPCOperation.OperationToOperationResume(0,Op,Op.SignerAccount,OPR) then begin
           OPR.NOpInsideBlock := i;
-          OPR.Block := Node.Operations.OperationBlock.block;
-          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
+          OPR.Block := Node.Bank.BlocksCount;
+          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SignerAccount).balance;
           FOperationsResume.Add(OPR);
         end;
       end;
@@ -876,7 +917,7 @@ begin
               FOperationsResume.Add(OPR);
               // Reverse operations inside a block
               for i := opc.Count - 1 downto 0 do begin
-                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],opc.Operation[i].SenderAccount,opr) then begin
+                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],opc.Operation[i].SignerAccount,opr) then begin
                   opr.NOpInsideBlock := i;
                   opr.Block := bend;
                   opr.time := opc.OperationBlock.timestamp;
@@ -927,6 +968,7 @@ begin
   FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
   FHashRateAverageBlocksCount := 50;
   SetLength(FBlockChainDataArray,0);
+  FShowTimeAverageColumns:=False;
 end;
 
 destructor TBlockChainGrid.Destroy;
@@ -950,7 +992,8 @@ begin
   DrawGrid.FixedRows := 1;
   DrawGrid.DefaultDrawing := true;
   DrawGrid.FixedCols := 0;
-  DrawGrid.ColCount := 13;
+  If ShowTimeAverageColumns then DrawGrid.ColCount:=15
+  else DrawGrid.ColCount:=12;
   DrawGrid.ColWidths[0] := 50; // Block
   DrawGrid.ColWidths[1] := 110; // Time
   DrawGrid.ColWidths[2] := 30; // Ops
@@ -963,7 +1006,11 @@ begin
   DrawGrid.ColWidths[9] := 190; // PoW
   DrawGrid.ColWidths[10] := 190; // SafeBox Hash
   DrawGrid.ColWidths[11] := 50; // Protocol
-  DrawGrid.ColWidths[12] := 120; // Accumulated work
+  If ShowTimeAverageColumns then begin
+    DrawGrid.ColWidths[12] := 95; // Accumulated work
+    DrawGrid.ColWidths[13] := 55; // Deviation
+    DrawGrid.ColWidths[14] := 340; // Time average
+  end;
   FDrawGrid.DefaultRowHeight := 18;
   FDrawGrid.Invalidate;
   DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
@@ -985,10 +1032,11 @@ begin
   end;
 end;
 
-procedure TBlockChainGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer;
+procedure TBlockChainGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Longint;
   Rect: TRect; State: TGridDrawState);
 Var s : String;
   bcd : TBlockChainData;
+  deviation : Real;
 begin
   {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
@@ -1009,6 +1057,8 @@ begin
       10 : s := 'SafeBox Hash';
       11 : s := 'Protocol';
       12 : s := 'Acc.Work';
+      13 : s := 'Deviation';
+      14 : s := 'Time average';
     else s:= '';
     end;
     Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter]);
@@ -1028,13 +1078,25 @@ begin
         s := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime((bcd.Timestamp))));
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfleft,tfVerticalCenter,tfSingleLine]);
       end else if ACol=2 then begin
-        s := IntToStr(bcd.OperationsCount);
-        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter]);
+        if bcd.OperationsCount>=0 then begin
+          s := IntToStr(bcd.OperationsCount);
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter]);
+        end else begin
+          DrawGrid.Canvas.Font.Color := clGrayText;
+          s := '(no data)';
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);
+        end;
       end else if ACol=3 then begin
-        s := TAccountComp.FormatMoney(bcd.Volume);
-        if FBlockChainDataArray[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
-        else DrawGrid.Canvas.Font.Color := clGrayText;
-        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
+        if bcd.Volume>=0 then begin
+          s := TAccountComp.FormatMoney(bcd.Volume);
+          if FBlockChainDataArray[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
+          else DrawGrid.Canvas.Font.Color := clGrayText;
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
+        end else begin
+          DrawGrid.Canvas.Font.Color := clGrayText;
+          s := '(no data)';
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);
+        end;
       end else if ACol=4 then begin
         s := TAccountComp.FormatMoney(bcd.Reward);
         if FBlockChainDataArray[ARow-1].Reward>0 then DrawGrid.Canvas.Font.Color := ClGreen
@@ -1074,6 +1136,14 @@ begin
           s := '(no data)';
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);
         end;
+      end else if ACol=13 then begin
+        deviation := ((CT_NewLineSecondsAvg - bcd.TimeAverage100) / CT_NewLineSecondsAvg)*100;
+        s := Format('%.2f',[deviation])+' %';
+        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=14 then begin
+        s := Format('200:%.1f 150:%.1f 100:%.1f 75:%.1f 50:%.1f 25:%.1f 10:%.1f',[bcd.TimeAverage200,
+           bcd.TimeAverage150,bcd.TimeAverage100,bcd.TimeAverage75,bcd.TimeAverage50,bcd.TimeAverage25,bcd.TimeAverage10]);
+        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
       end;
     end;
   end;
@@ -1132,6 +1202,13 @@ begin
   UpdateBlockChainGrid;
 end;
 
+procedure TBlockChainGrid.SetShowTimeAverageColumns(AValue: Boolean);
+begin
+  if FShowTimeAverageColumns=AValue then Exit;
+  FShowTimeAverageColumns:=AValue;
+  InitGrid;
+end;
+
 procedure TBlockChainGrid.SetMaxBlocks(const Value: Integer);
 begin
   if FMaxBlocks=Value then exit;
@@ -1153,6 +1230,7 @@ Var nstart,nend : Cardinal;
   opc : TPCOperationsComp;
   bcd : TBlockChainData;
   i : Integer;
+  opb : TOperationBlock;
 begin
   if (FBlockStart>FBlockEnd) And (FBlockStart>=0) then FBlockEnd := -1;
   if (FBlockEnd>=0) And (FBlockEnd<FBlockStart) then FBlockStart:=-1;
@@ -1185,22 +1263,30 @@ begin
       while (nstart<=nend) do begin
         i := length(FBlockChainDataArray) - (nend-nstart+1);
         bcd := CT_TBlockChainData_NUL;
+        opb := Node.Bank.SafeBox.Block(nend).blockchainInfo;
+        bcd.Block:=opb.block;
+        bcd.Timestamp := opb.timestamp;
+        bcd.BlockProtocolVersion := opb.protocol_version;
+        bcd.BlockProtocolAvailable := opb.protocol_available;
+        bcd.Reward := opb.reward;
+        bcd.Fee := opb.fee;
+        bcd.Target := opb.compact_target;
+        bcd.HashRateKhs := Node.Bank.SafeBox.CalcBlockHashRateInKhs(bcd.Block,HashRateAverageBlocksCount);
+        bcd.MinerPayload := opb.block_payload;
+        bcd.PoW := opb.proof_of_work;
+        bcd.SafeBoxHash := opb.initial_safe_box_hash;
+        bcd.AccumulatedWork := Node.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
         if (Node.Bank.LoadOperations(opc,nend)) then begin
-          bcd.Block := opc.OperationBlock.block;
-          bcd.Timestamp := opc.OperationBlock.timestamp;
-          bcd.BlockProtocolVersion := opc.OperationBlock.protocol_version;
-          bcd.BlockProtocolAvailable := opc.OperationBlock.protocol_available;
           bcd.OperationsCount := opc.Count;
           bcd.Volume := opc.OperationsHashTree.TotalAmount + opc.OperationsHashTree.TotalFee;
-          bcd.Reward := opc.OperationBlock.reward;
-          bcd.Fee := opc.OperationBlock.fee;
-          bcd.Target := opc.OperationBlock.compact_target;
-          bcd.HashRateKhs := Node.Bank.SafeBox.CalcBlockHashRateInKhs(bcd.Block,HashRateAverageBlocksCount);
-          bcd.MinerPayload := opc.OperationBlock.block_payload;
-          bcd.PoW := opc.OperationBlock.proof_of_work;
-          bcd.SafeBoxHash := opc.OperationBlock.initial_safe_box_hash;
-          bcd.AccumulatedWork := Node.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
         end;
+        bcd.TimeAverage200:=Node.Bank.GetTargetSecondsAverage(bcd.Block,200);
+        bcd.TimeAverage150:=Node.Bank.GetTargetSecondsAverage(bcd.Block,150);
+        bcd.TimeAverage100:=Node.Bank.GetTargetSecondsAverage(bcd.Block,100);
+        bcd.TimeAverage75:=Node.Bank.GetTargetSecondsAverage(bcd.Block,75);
+        bcd.TimeAverage50:=Node.Bank.GetTargetSecondsAverage(bcd.Block,50);
+        bcd.TimeAverage25:=Node.Bank.GetTargetSecondsAverage(bcd.Block,25);
+        bcd.TimeAverage10:=Node.Bank.GetTargetSecondsAverage(bcd.Block,10);
         FBlockChainDataArray[i] := bcd;
         if (nend>0) then dec(nend) else break;
       end;

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