Browse Source

Source code Build 1.0.1

Source code of Pascal Coin Build 1.0.1 for Delphi 2010
PascalCoin 9 years ago
parent
commit
d4c4f06e19
42 changed files with 19189 additions and 23 deletions
  1. BIN
      Instaler/PascalCoinWalletB1.0.1.0.exe
  2. 0 21
      LICENSE
  3. 11 0
      LICENSE.txt
  4. 40 0
      PascalCoinWallet.dpr
  5. BIN
      PascalCoinWallet.res
  6. 49 2
      README.md
  7. 49 0
      README.txt
  8. 249 0
      Units/Forms/UFRMAbout.dfm
  9. 55 0
      Units/Forms/UFRMAbout.pas
  10. 86 0
      Units/Forms/UFRMNewPrivateKeyType.dfm
  11. 93 0
      Units/Forms/UFRMNewPrivateKeyType.pas
  12. 552 0
      Units/Forms/UFRMOperation.dfm
  13. 634 0
      Units/Forms/UFRMOperation.pas
  14. 241 0
      Units/Forms/UFRMPascalCoinWalletConfig.dfm
  15. 161 0
      Units/Forms/UFRMPascalCoinWalletConfig.pas
  16. 265 0
      Units/Forms/UFRMPayloadDecoder.dfm
  17. 237 0
      Units/Forms/UFRMPayloadDecoder.pas
  18. 1316 0
      Units/Forms/UFRMWallet.dfm
  19. 1321 0
      Units/Forms/UFRMWallet.pas
  20. 736 0
      Units/Forms/UFRMWalletKeys.dfm
  21. 481 0
      Units/Forms/UFRMWalletKeys.pas
  22. 1318 0
      Units/PascalCoin/UAccounts.pas
  23. 1739 0
      Units/PascalCoin/UBlockChain.pas
  24. 78 0
      Units/PascalCoin/UConst.pas
  25. 726 0
      Units/PascalCoin/UCrypto.pas
  26. 1006 0
      Units/PascalCoin/UDBStorage.pas
  27. 456 0
      Units/PascalCoin/UECIES.pas
  28. 286 0
      Units/PascalCoin/UFileStorage.pas
  29. 212 0
      Units/PascalCoin/ULog.pas
  30. 202 0
      Units/PascalCoin/UMiner.pas
  31. 2413 0
      Units/PascalCoin/UNetProtocol.pas
  32. 683 0
      Units/PascalCoin/UNode.pas
  33. 644 0
      Units/PascalCoin/UOpTransaction.pas
  34. 156 0
      Units/PascalCoin/UThread.pas
  35. 86 0
      Units/PascalCoin/UTime.pas
  36. 319 0
      Units/PascalCoin/UWalletKeys.pas
  37. 215 0
      Units/Utils/UAES.pas
  38. 480 0
      Units/Utils/UAppParams.pas
  39. 155 0
      Units/Utils/UFolderHelper.pas
  40. 1439 0
      Units/Utils/UGridUtils.pas
  41. BIN
      libeay32.dll
  42. BIN
      pascalcoin.mdb

BIN
Instaler/PascalCoinWalletB1.0.1.0.exe


+ 0 - 21
LICENSE

@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 PascalCoin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

+ 11 - 0
LICENSE.txt

@@ -0,0 +1,11 @@
+Pascal Coin - P2P Cryptocurrency without need of historical operations.
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Albert Molina
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 40 - 0
PascalCoinWallet.dpr

@@ -0,0 +1,40 @@
+program PascalCoinWallet;
+
+uses
+  Forms,
+  UBlockChain in 'Units\PascalCoin\UBlockChain.pas',
+  UCrypto in 'Units\PascalCoin\UCrypto.pas',
+  UTime in 'Units\PascalCoin\UTime.pas',
+  UWalletKeys in 'Units\PascalCoin\UWalletKeys.pas',
+  UMiner in 'Units\PascalCoin\UMiner.pas',
+  UOpTransaction in 'Units\PascalCoin\UOpTransaction.pas',
+  UNetProtocol in 'Units\PascalCoin\UNetProtocol.pas',
+  UAccounts in 'Units\PascalCoin\UAccounts.pas',
+  UConst in 'Units\PascalCoin\UConst.pas',
+  UThread in 'Units\PascalCoin\UThread.pas',
+  ULog in 'Units\PascalCoin\ULog.pas',
+  UNode in 'Units\PascalCoin\UNode.pas',
+  UFileStorage in 'Units\PascalCoin\UFileStorage.pas',
+  UECIES in 'Units\PascalCoin\UECIES.pas',
+  UDBStorage in 'Units\PascalCoin\UDBStorage.pas',
+  UFRMWallet in 'Units\Forms\UFRMWallet.pas' {FRMWallet},
+  UFolderHelper in 'Units\Utils\UFolderHelper.pas',
+  UAppParams in 'Units\Utils\UAppParams.pas',
+  UGridUtils in 'Units\Utils\UGridUtils.pas',
+  UFRMPascalCoinWalletConfig in 'Units\Forms\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
+  UFRMAbout in 'Units\Forms\UFRMAbout.pas' {FRMAbout},
+  UFRMOperation in 'Units\Forms\UFRMOperation.pas' {FRMOperation},
+  UFRMWalletKeys in 'Units\Forms\UFRMWalletKeys.pas' {FRMWalletKeys},
+  UFRMNewPrivateKeyType in 'Units\Forms\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
+  UAES in 'Units\Utils\UAES.pas',
+  UFRMPayloadDecoder in 'Units\Forms\UFRMPayloadDecoder.pas' {FRMPayloadDecoder};
+
+{$R *.res}
+
+begin
+  Application.Initialize;
+  Application.MainFormOnTaskbar := True;
+  Application.Title := 'Pascal Coin Wallet, Miner & Explorer';
+  Application.CreateForm(TFRMWallet, FRMWallet);
+  Application.Run;
+end.

BIN
PascalCoinWallet.res


+ 49 - 2
README.md

@@ -1,2 +1,49 @@
-# PascalCoin
-P2P cryptographic currency that works like a Bank
+Pascal Coin: P2P Cryptocurrency without need of historical operations.
+
+Copyright (c) 2016 Albert Molina
+
+THIS IS EXPERIMENTAL SOFTWARE. Use it for educational purposes only.
+
+This software is a Node of the Pascal Coin P2P Cryptocurrency.
+It 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.
+
+This product includes software developed by the OpenSSL Project and Denis
+Grinyuk (https://github.com/Arvur/OpenSSL-Delphi), and some
+cryptographic functions inspirated in code written by Ladar Levison and 
+Marco Ferrante.
+Original source code is written in Pascal Language and is available at 
+https://github.com/PascalCoin/PascalCoin
+
+
+HOW TO COMPILE:
+
+Version 1.0 of Pascal Coin Wallet, Miner & Explorer is compiled with
+
+- Included source code available at https://github.com/PascalCoin/PascalCoin
+- Delphi 2010 (Future versions will compile with Lazarus...)
+- Source code of OpenSSL-Delphi that can be obtained from: https://github.com/Arvur/OpenSSL-Delphi - Put it on any directory and make it available to compile.
+
+
+To run:
+- libeay32.dll - Can be downloaded from https://indy.fulgan.com/SSL/
+
+
+Enjoy Pascal Coin!
+
+If you like it, consider a donation using BitCoin:
+16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+History:
+
+Build 1.0.1.0 - 2016-08-12
+--------------------------
+- Included an option to Import/Export Wallet keys file
+- Some miner modifications
+
+
+Build 1.0.0.0 - 2016-08-11
+--------------------------
+- First stable version.

+ 49 - 0
README.txt

@@ -0,0 +1,49 @@
+Pascal Coin: P2P Cryptocurrency without need of historical operations.
+
+Copyright (c) 2016 Albert Molina
+
+THIS IS EXPERIMENTAL SOFTWARE. Use it for educational purposes only.
+
+This software is a Node of the Pascal Coin P2P Cryptocurrency.
+It 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.
+
+This product includes software developed by the OpenSSL Project and Denis
+Grinyuk (https://github.com/Arvur/OpenSSL-Delphi), and some
+cryptographic functions inspirated in code written by Ladar Levison and 
+Marco Ferrante.
+Original source code is written in Pascal Language and is available at 
+https://github.com/PascalCoin/PascalCoin
+
+
+HOW TO COMPILE:
+
+Version 1.0 of Pascal Coin Wallet, Miner & Explorer is compiled with
+
+- Included source code available at https://github.com/PascalCoin/PascalCoin
+- Delphi 2010 (Future versions will compile with Lazarus...)
+- Source code of OpenSSL-Delphi that can be obtained from: https://github.com/Arvur/OpenSSL-Delphi - Put it on any directory and make it available to compile.
+
+
+To run:
+- libeay32.dll - Can be downloaded from https://indy.fulgan.com/SSL/
+
+
+Enjoy Pascal Coin!
+
+If you like it, consider a donation using BitCoin:
+16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+History:
+
+Build 1.0.1.0 - 2016-08-12
+--------------------------
+- Included an option to Import/Export Wallet keys file
+- Some miner modifications
+
+
+Build 1.0.0.0 - 2016-08-11
+--------------------------
+- First stable version.

+ 249 - 0
Units/Forms/UFRMAbout.dfm

@@ -0,0 +1,249 @@
+object FRMAbout: TFRMAbout
+  Left = 0
+  Top = 0
+  ActiveControl = bbClose
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsDialog
+  Caption = 'About...'
+  ClientHeight = 341
+  ClientWidth = 522
+  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 Image1: TImage
+    Left = 15
+    Top = 15
+    Width = 62
+    Height = 62
+    AutoSize = True
+    Picture.Data = {
+      0954506E67496D61676589504E470D0A1A0A0000000D494844520000003E0000
+      003E080600000073C1A8440000000473424954080808087C0864880000000970
+      48597300000B1200000B1201D2DD7EFC0000001C74455874536F667477617265
+      0041646F62652046697265776F726B7320435336E8BCB28C0000001674455874
+      4372656174696F6E2054696D650030362F30372F313610A27E9700000EDA4944
+      415478DACD9B095813D716804F4280A860E1A9A0A295A556AD0B88F6A9CF85A4
+      6E6D5DC08A05956A5050BFAA155EAD56AB125CD0BA22D65A5C58DCAAA012A5D5
+      F75C4AA2A2A8AD605D9E5A41A08A80A2AC822CC93B77320943329384B53DDF77
+      BE994C6692FBDF73EFB9E79E7B87A752A9A0B984C7E3F5C3C367A843511D506D
+      512D51F9B4AA68AD462D43CD45CD404D413D8165BBDF6C656B6A7084F5C2C322
+      54F7364241CEE01EF679A2BE9DCC5C9DDA594FF8673757AEE78ACA2A8B2FDE7D
+      F6F876E6CB925F7ECFA9C173C7AA6A652BFCEA1AEA0E2CE7B9BF1D38C2BE8B87
+      25A8939CECAD1FCE1ADDA37AE1F83EFDDE6A63D1B631BF9B915BFCF4C8C5F4C7
+      61F1A9EDCA2AAA49251C42DD8D65CEFE4BC111B8271E969BF179C3BD873A6786
+      CD78BFBB73C7B60E8DAE498E4AF05E7F3E2335E3C5DBF8F1206A2496FDCF1605
+      4760D257BF429DE733DCE556E4FCE1EE8DB56E032AC0053F8662F977B7083842
+      937E7AB47D5BE1EB6B5BBCEC9ACBC2C6E4DA83FC8713D7FCB72CBFA8FC117E9C
+      8F1CCF9B0D1CA1A760B35E279D36206F858FFBB0A68649CB2880C2B237E0686F
+      0D8E76D6D4B5CCFC12C8CC2B019B3696E0E6DC4EEF199F6FCFCBE32E6774C1D3
+      79C872A149C11158888725D6ADCCA79C5B33CE62500FBB778D3C411E621C19A2
+      52A26A46B1BA225A96088A3BCF2064EA00C0CAA5AE490FFF06A13FFE061E7D3A
+      817CFD04D67F4BBC9E756BD2BAB376354AD56AE4F9A129C137E1D034EE698C9F
+      03675F26803C335AC9399F055CA586D6C0AB6AE87325F5AD8D6F0C19D620E19B
+      31E035D891B332D88434FD512B7EAE29ADA88A3005DE203802932164AB95D07C
+      C49398E95D58A109209FC00AF0C8A7C1F9B4D2566705A75559435540614919D8
+      FA445377A56E9FAC6DD68EB30F43567E294407894032D2704323B14057FF43D9
+      25E555C7F16318B25536147C395ADA8FD3D27C735401AD66740530C0752DAE69
+      E25AF01A2DB83C3513C44B8FAB6F4B9C535B86096AA79D14361E447D3B1B3324
+      05DF4572E8095A7E23B2C5D61B9C38B2B6AD2D9665474D73D183A6AC8CD06602
+      063C0DCD8097AC8B87CCDC57750C5E7BA2A23FABCF9F3C2F86F49C42B06A650E
+      035C3A50D70AB1D9DF7A5C403D419A3A11D212886A9C1F9B90663F74C94901F6
+      F9395C0E8F151CA1BBA3F73E9BBCD1B352CF911168330B54731D8BD3AAB13836
+      7BDEE060A3166AA860080C419E7D399B3F717838DC91DA7145C652A3E0084DAC
+      7B7EC1F8DEAF77CC1DEAC10ECD04D785575B5C7EF31188E76D6B36708D74B3B3
+      02D937630D0D7585783A0D39CB8D814BEDDE6A3531EFE067FD75BE4058CB5A68
+      72A49A3B0B3C82C7245E01FF907DCD0EAE112E1F60EF772015839CEF90338A13
+      9C9E469E4DDFE35BAD179151C0960C8B6B2A80CDF26620DD9500A1BB8EB71838
+      FA21488B98ACD7F7497F1FBC5846203D90358F0B3C0E63EF0E47968C14D5799A
+      C0106801073C5F73ACB5B8482205C58DBB2D064EC4739023C8568CD1BBEEBEE8
+      C4258CED15C8BA520F1CA19DD1A15D2C383CD3BAAE17C7262EB0A4C12D6B2D2F
+      A0C1F9CC0AD0787933B01938158A4ACAD8AD63DD06DC7A3A82665C7F925700E9
+      7FE683556B4B18D0A32B5E57E2B542F4F22FEB0DFF78DF543DAB93898D4BE011
+      2C1CE5E8F274C1F7A1B59D59AD4D010B6BC105421DAB5BD4E9F38525E560EB3E
+      99B3701E83FA81FCE006E4AB06A8A982A0B0BDB0FDE019F014B9816C63205E7B
+      833EE22AF887C543D70E6D21E27331849FB8018ADB3946C1174DEC03E181FF62
+      B3FA65B4FA05E495EA821763DF2ED6EBDB4C68CD3905AFDB0A6AC1E5D7EF82D8
+      771167E142822420FDC28F06AF0491DFD7D82DEE41C83C2F90067C44814B2313
+      2134EA1C78B87603F9661F80EA0A08DA9504DB4FDD3608CE15D3D356E7236F67
+      2D38427FEA646F1D9CB177EAE03A7793E14BA08116D2A04206B04EF3A79B7C78
+      D409080E0DE72C5CF4D69520998C7D51594581DBF4FF04BBC56B48080F06AF11
+      7D2870D19C2DA0484D87108908A47E43287072DDD17F3F15C272097172854724
+      ACDF594D89FA5F5945F56C64BEAA01BFB2C66F608DDE54935890095EC7FA5C15
+      6001927FAF85D8B89F380B97747C3788B0B993665EF8AA006CDDBCA8EBA9F11B
+      C0AD7B478444C0F1CB202BF725447FFD094846F7C27B2BA8EBD20357A9D99A21
+      6186BC4C597BF4E6E595077FCD42663F124863C40125584B557AA1A906888216
+      D6B5BA80A302105CE43D1714577EE52E58FE1DCAD204449E7C5DDB2D5477E3B5
+      96E5B907A82B697B008ECF9DA86BE43BD9E5FB8053D00681D3CD9D4CBC3A10F0
+      81381139501A3FABA7DE9D4C6026AC990EB80E3CAF236732155CFBF48234B98C
+      067903E1BB0F60B78800D79E4E90766233755D9E9206E2C08D6A884B61D47D54
+      85A0122717BCE70AE7EF1B9AB713B198B437BBAA5AF90101DF32D2D561C0F9B5
+      E374C2535E2DA0B990012BD46F058C9690F9EC05380D18CBF9C79EE3C682ECC0
+      F76A186CBE412BBE85EDFB8E82E7A82120DBB184828B49B800FEABF642B78EB6
+      90796CA9169AA864D31988BDF080F3F76762EC1E8353582EC139BBE2C2ADA767
+      08F8953D0B47F003C6F41C54179CE9D85AB1589B5D65679361D2CC059C7F1CB2
+      EC4B902E594083A0139BE40F8A945408412F2FFDDC9BBA2EFDEE0884FE20038F
+      FEEF807C4780F65E72B499BC934A567089B1793BDDCF9F13F0CC532BC716EA25
+      FBB5E02CCDDCBC556D5F679EA34AB7EE85D04D3B39FF38E1C768F0FA48ACB5A0
+      4DF761E8D14B2161D72AF012BBAB2B636608287EBB0F21011F825442EE2D5777
+      8BF82B101CA90043C216C030859EB59911F0227406EC99953A0E8CADC9EBB708
+      AF598BE1E4995FB80B76E71A383AD851D0852F9F836D8F11D4F5D4C49DE8D11D
+      A8EB8EA3E743D6B302885E390D2463FB51D7D21E6441FFB9D106A1B94256A690
+      44858D6F4C05017F83E016FAB7F0580085463DBCDBE8E970EB2E771F54156523
+      4825D5BFE517AF80D85BED81557F9CD636675EDFA9D4B5A49DF341D4AF0BC814
+      6920592FC342BF3108656A96863761773101AF41703EEB1D7A7D9A0BBE7658E3
+      7519C4F9871E4307833CF1907A2843EF1DFE432C044BB7816B2F67483B194141
+      CBAFA68278F63AEAFEE8155321E6A71450A43D360A638AB519E095045C89E03C
+      D63B3471791DEBB2043334BCFCFA3D10FB2C30E9CF9B524832222DC21B6CDA58
+      98743F82D71806D74C5074C1758035814B8C2C09FC176F68516812A2CAC326B0
+      66608C815721B880FD0E1E7B88AA999D69A1D5E0D4301471E06F0D4D835711F0
+      72041772DE556726A681B7A803AC51D1CC55A0B87EDBF412344248B291F46943
+      439701F022029E8771BA907B85C48CB559D7998ED253529B21FED42CAB398558
+      3968625F83AB2A26803F25E0773080A936B45BA16EB3B6D09B869263615915D8
+      0E9BDB6CC0C4814946F6A052CAA63A3136A103186A7DE7F4BACFDEB75EFE697F
+      EED54F6D5AD992C5D202EA28BF9901E2391BB90BDEB9833A70A117121E653F83
+      A7F9AFA0BD8D15F4762663AF12725F14C383EC7C105A0860502F07ECBBEDB129
+      5B81A8B71DB839D9364905EE3B77FF5A40C4452A645D8593940FF42629BA4203
+      EAA79BD479B6F0B84B10BCF518E7E3D16BE78164E270EDF29168761886A50F20
+      24701C4803C6527373E9DE33101A755E9D75D9E24B073A95EA844513093D49F9
+      9980BB9B0BF8272A1302BA197D4A9B5165E6D6D5E0927571107BFA06E7A3497B
+      968068400FCAB2A054828D28088A4ACB21E1DB59E035AC179586122D8CC46025
+      1342660C05E9F44174A05369B458F511EB29D1F74A2BAABC0838F1E839E97B7C
+      5F9BB4BBA14E3A5993571780E88B282C7406E763AA1BDF6B170D0B8BCBC076D4
+      52EA7A6AF4227073B1A3ACEAE8B30DB2F28A20FACB31EA19564D15B0ADA33754
+      E838FD8D4AA5B2D3A49E4EAEF11BF80F937739305750CCD4B9749E780DE7EDAE
+      DD3B43DAFEC55A70F9CD3F40BC20525D218AD5EAA4232A6F6418752D698317F6
+      6BFB2685261216977AF99B0337F29179B206FCE33642C1E6D2F859BD4CFE15ED
+      AA8939643E2F0527BF48CE5B3D87BD07B2F57E5AF0F0B86408FEEE0CB8BAD843
+      DAEED9D452B11CE371F15771EACAE0481D35565C028FA464E416AF41E6D31A70
+      12B991E65E59AFCD3CF4A600594A164C5ACD9D5C0C99E981F36A0FD02C20643E
+      7B0599B985382C99ABA32EA57A6340DAA33CB5E3336186555FA1F36DA4EFF446
+      E6D7CCBCFAB2FECEED3FBEB9FD937A6FEA91FE7813420F73271713A493C06B68
+      77F507ED36107A6380767380B2C96199E2BBF182FCE8A5F464E45D41F132C049
+      EC978D562FABEF162EAFB567E1E4B54CCEEF1FC748C0D19E0E0C995B4140B311
+      A8798538B576D362F36B94AA919A5D91BA8B869BD0EA83EB6B75B72F8E6B772E
+      B0892A914474CD0FC825B4B5159AE5233670B262A748D9EC55657C4B57AD68F6
+      A9B089B1746F730BDDB7C98EE9BEC89ACB0A4EC3CFB07BAB5590DEC6000E91DF
+      CE01F1726EC7662CDDDBDC426F0CD8AEBB11880DBC351E623F1DE6DCFEE8D251
+      464B1C73E121F887CB39BFDF1630849A58FC15428FDBA5C8F891EE775C9B7FC8
+      8C80CCDA9E1B9CB541EDCE432E313501D8D44276420C5B7AAAB4BA463915191F
+      9A044EC30F31E3F36293377AAA0CF577CDCE432E797544D2A869644384B1D76D
+      19F29D62E533B2C1CFDB4A68BEFA49CC74CEAD9C9A6D986C6268C9B639A1BBFA
+      1FCA2A29AFDA8F1FB7215F4DBDC169F8D508EFCD064F36E0D9223897B4B44767
+      6CE9DC696C3FABA99B78E7B56D6D31F7ECEA8F5B339B3D014FCB78C1F91C73FB
+      754B4063F37E8ACD3B8E395E370A9C860FE4F37861B215639E1A73782D2DC491
+      8D0D395D82F0648D698FA1CDBB0D0127AB2D235137FB0C7779A9B749E82F9285
+      91C98A5DA7EFBD83E16820B29C31F5B986BC9AE18C879DD8EFDF3EBF769CA03E
+      115E530A89C8862C3E998FC109D9BEE5871C05F579BEC16F21D1EF97EDC2D8FE
+      D1B165A39C5AEADD14D297E7EEBC7413636F27FC48B29B0791A1B8DEE56FE4EB
+      5724D0218B658BB002EE61053837E7EB57CBF7DFF8E358728623366B92D58C68
+      F1D7AF582A80E48966A0CEC62EA05C36C5ADC077844BA35B01B1EE8E9FEEFC1E
+      75EE81E0715E0999D0EFA1819F35E6779B0C5CA712C812C77C54B185805F35BC
+      77A79CF7DEB685D16E0E36237A7772E20A84745FB14C799067575651DD11BF22
+      A9DBCD7FCB572C0D540249597F08EA976A891324413B19D8490C4BD25DE4CFC9
+      D04356FCC9AE3DE2A8D2519350FF839A83E56B96D4CCFF0184B915F49D084B6C
+      0000000049454E44AE426082}
+  end
+  object Label1: TLabel
+    Left = 90
+    Top = 15
+    Width = 382
+    Height = 25
+    Caption = 'Pascal Coin Wallet, Miner && Explorer'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -21
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object lblBuild: TLabel
+    Left = 8
+    Top = 280
+    Width = 30
+    Height = 13
+    Caption = 'Build:'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object lblProtocolVersion: TLabel
+    Left = 8
+    Top = 299
+    Width = 50
+    Height = 13
+    Caption = 'Protocol:'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object Memo1: TMemo
+    Left = 90
+    Top = 46
+    Width = 401
+    Height = 229
+    BevelInner = bvNone
+    BevelOuter = bvNone
+    BorderStyle = bsNone
+    Ctl3D = False
+    Lines.Strings = (
+      'Copyright (c) 2016 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 an' +
+        'd 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.'
+      ''
+      
+        'THIS IS EXPERIMENTAL SOFTWARE. Use it for educational purposes o' +
+        'nly.'
+      ''
+      
+        'This product includes software developed by the OpenSSL Project ' +
+        'and Denis '
+      
+        'Grinyuk (https://github.com/Arvur/OpenSSL-Delphi), and some cryp' +
+        'tographic '
+      
+        'functions inspirated in code written by Ladar Levison and Marco ' +
+        'Ferrante. Original '
+      'source code is written in Pascal Language and is available at '
+      'https://github.com/PascalCoin/PascalCoin')
+    ParentColor = True
+    ParentCtl3D = False
+    ReadOnly = True
+    TabOrder = 0
+  end
+  object bbClose: TBitBtn
+    Left = 380
+    Top = 281
+    Width = 111
+    Height = 31
+    Caption = 'Close'
+    DoubleBuffered = True
+    Kind = bkOK
+    ParentDoubleBuffered = False
+    TabOrder = 1
+  end
+end

+ 55 - 0
Units/Forms/UFRMAbout.pas

@@ -0,0 +1,55 @@
+unit UFRMAbout;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, pngimage, ExtCtrls, StdCtrls, Buttons;
+
+type
+  TFRMAbout = class(TForm)
+    Image1: TImage;
+    Label1: TLabel;
+    Memo1: TMemo;
+    bbClose: TBitBtn;
+    lblBuild: TLabel;
+    lblProtocolVersion: TLabel;
+    procedure FormCreate(Sender: TObject);
+  private
+    { Private declarations }
+  public
+    { Public declarations }
+  end;
+
+implementation
+
+uses UFolderHelper, UConst;
+
+{$R *.dfm}
+
+procedure TFRMAbout.FormCreate(Sender: TObject);
+Var fvi : TFileVersionInfo;
+begin
+  fvi := TFolderHelper.GetTFileVersionInfo(Application.ExeName);
+  lblBuild.Caption :=  'Build: '+fvi.FileVersion;
+  if CT_Protocol_Available>0 then begin
+    lblProtocolVersion.Caption := Format('Protocol: %d (Available: %d)',[CT_Protocol_Version,CT_Protocol_Available]);
+  end else begin
+    lblProtocolVersion.Caption := Format('Protocol: %d',[CT_Protocol_Version]);
+  end;
+end;
+
+end.

+ 86 - 0
Units/Forms/UFRMNewPrivateKeyType.dfm

@@ -0,0 +1,86 @@
+object FRMNewPrivateKeyType: TFRMNewPrivateKeyType
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsDialog
+  Caption = 'New Private Key'
+  ClientHeight = 229
+  ClientWidth = 311
+  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 Label1: TLabel
+    Left = 30
+    Top = 25
+    Width = 27
+    Height = 13
+    Caption = 'Name'
+  end
+  object ebName: TEdit
+    Left = 70
+    Top = 22
+    Width = 206
+    Height = 21
+    TabOrder = 0
+    Text = 'ebName'
+  end
+  object rgKeyType: TRadioGroup
+    Left = 30
+    Top = 60
+    Width = 246
+    Height = 105
+    Caption = ' Key Type: '
+    Items.Strings = (
+      'asdf')
+    TabOrder = 1
+  end
+  object bbOk: TBitBtn
+    Left = 112
+    Top = 181
+    Width = 75
+    Height = 25
+    Caption = 'OK'
+    Default = True
+    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 = 2
+    OnClick = bbOkClick
+  end
+  object bbCancel: TBitBtn
+    Left = 201
+    Top = 181
+    Width = 75
+    Height = 25
+    DoubleBuffered = True
+    Kind = bkCancel
+    ParentDoubleBuffered = False
+    TabOrder = 3
+  end
+end

+ 93 - 0
Units/Forms/UFRMNewPrivateKeyType.pas

@@ -0,0 +1,93 @@
+unit UFRMNewPrivateKeyType;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, Buttons, ExtCtrls, UWalletKeys,UCrypto;
+
+type
+  TFRMNewPrivateKeyType = class(TForm)
+    Label1: TLabel;
+    ebName: TEdit;
+    rgKeyType: TRadioGroup;
+    bbOk: TBitBtn;
+    bbCancel: TBitBtn;
+    procedure FormCreate(Sender: TObject);
+    procedure bbOkClick(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FWalletKeys: TWalletKeys;
+    FGeneratedPrivateKey: TECPrivateKey;
+    procedure SetWalletKeys(const Value: TWalletKeys);
+    { Private declarations }
+  public
+    { Public declarations }
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+    Property GeneratedPrivateKey : TECPrivateKey read FGeneratedPrivateKey write FGeneratedPrivateKey;
+  end;
+
+
+implementation
+
+uses
+  UAccounts ;
+
+{$R *.dfm}
+
+procedure TFRMNewPrivateKeyType.bbOkClick(Sender: TObject);
+begin
+  if Not Assigned(WalletKeys) then exit;
+  if rgKeyType.ItemIndex<0 then raise Exception.Create('Select a key type');
+
+  if Assigned(FGeneratedPrivateKey) then FGeneratedPrivateKey.Free;
+
+  FGeneratedPrivateKey := TECPrivateKey.Create;
+  FGeneratedPrivateKey.GenerateRandomPrivateKey( Integer(rgKeyType.Items.Objects[rgKeyType.ItemIndex]) );
+  WalletKeys.AddPrivateKey(ebName.Text,FGeneratedPrivateKey);
+  ModalResult := MrOk;
+end;
+
+procedure TFRMNewPrivateKeyType.FormCreate(Sender: TObject);
+Var l : TList;
+  i : Integer;
+begin
+  FGeneratedPrivateKey := Nil;
+  FWalletKeys := Nil;
+  ebName.Text := DateTimeToStr(now);
+  rgKeyType.Items.Clear;
+  l := TList.Create;
+  Try
+    TAccountComp.ValidsEC_OpenSSL_NID(l);
+    for i := 0 to l.Count - 1 do begin
+      rgKeyType.Items.AddObject(TAccountComp.GetECInfoTxt(integer(l[i])),l[i]);
+    end;
+  Finally
+    l.free;
+  End;
+end;
+
+procedure TFRMNewPrivateKeyType.FormDestroy(Sender: TObject);
+begin
+  FreeAndNil(FGeneratedPrivateKey);
+end;
+
+procedure TFRMNewPrivateKeyType.SetWalletKeys(const Value: TWalletKeys);
+begin
+  FWalletKeys := Value;
+end;
+
+end.

+ 552 - 0
Units/Forms/UFRMOperation.dfm

@@ -0,0 +1,552 @@
+object FRMOperation: TFRMOperation
+  Left = 60
+  Top = 0
+  ActiveControl = ebDestAccount
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsSingle
+  Caption = 'New Operation'
+  ClientHeight = 443
+  ClientWidth = 608
+  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 Label1: TLabel
+    Left = 20
+    Top = 20
+    Width = 43
+    Height = 13
+    Caption = 'Account:'
+  end
+  object lblAccountBalance: TLabel
+    Left = 225
+    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 Label2: TLabel
+    Left = 180
+    Top = 20
+    Width = 41
+    Height = 13
+    Caption = 'Balance:'
+  end
+  object bbExecute: TBitBtn
+    Left = 295
+    Top = 386
+    Width = 130
+    Height = 31
+    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 = 2
+    OnClick = actExecuteExecute
+  end
+  object bbCancel: TBitBtn
+    Left = 460
+    Top = 386
+    Width = 116
+    Height = 31
+    DoubleBuffered = True
+    Kind = bkCancel
+    ParentDoubleBuffered = False
+    TabOrder = 3
+  end
+  object ebSenderAccount: TEdit
+    Left = 69
+    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'
+    OnExit = ebSenderAccountExit
+    OnKeyPress = ebSenderAccountKeyPress
+  end
+  object PageControl: TPageControl
+    Left = 20
+    Top = 50
+    Width = 556
+    Height = 321
+    ActivePage = tsOperation
+    TabOrder = 1
+    object tsOperation: TTabSheet
+      TabVisible = False
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object gbOperation: TGroupBox
+        Left = 13
+        Top = 8
+        Width = 521
+        Height = 167
+        Caption = ' Operation: '
+        TabOrder = 0
+        object lblDestAccount: TLabel
+          Left = 96
+          Top = 42
+          Width = 68
+          Height = 13
+          Caption = 'Dest. Account'
+        end
+        object lblAmount: TLabel
+          Left = 255
+          Top = 42
+          Width = 37
+          Height = 13
+          Caption = 'Amount'
+        end
+        object lblNewPrivateKey: TLabel
+          Left = 59
+          Top = 85
+          Width = 83
+          Height = 13
+          Caption = 'New Private Key:'
+        end
+        object lblTransactionErrors: TLabel
+          Left = 170
+          Top = 21
+          Width = 331
+          Height = 13
+          Alignment = taRightJustify
+          AutoSize = False
+          Caption = 'Errors detected'
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clRed
+          Font.Height = -11
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+        end
+        object lblChangeKeyErrors: TLabel
+          Left = 170
+          Top = 63
+          Width = 331
+          Height = 13
+          Alignment = taRightJustify
+          AutoSize = False
+          Caption = 'Errors detected'
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clRed
+          Font.Height = -11
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+        end
+        object lblNewOwnerPublicKey: TLabel
+          Left = 59
+          Top = 138
+          Width = 105
+          Height = 13
+          Caption = 'New owner Public Key'
+        end
+        object lblNewOwnerErrors: TLabel
+          Left = 199
+          Top = 113
+          Width = 302
+          Height = 13
+          Alignment = taRightJustify
+          AutoSize = False
+          Caption = 'Errors detected'
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clRed
+          Font.Height = -11
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+        end
+        object lblFee: TLabel
+          Left = 407
+          Top = 42
+          Width = 18
+          Height = 13
+          Caption = 'Fee'
+        end
+        object rbTransaction: TRadioButton
+          Left = 15
+          Top = 19
+          Width = 220
+          Height = 17
+          Caption = 'Transaction (tx)'
+          TabOrder = 0
+          OnClick = rbTransactionClick
+        end
+        object rbChangeKey: TRadioButton
+          Left = 15
+          Top = 62
+          Width = 220
+          Height = 17
+          Caption = 'Change Private key'
+          TabOrder = 4
+          OnClick = rbTransactionClick
+        end
+        object ebDestAccount: TEdit
+          Left = 170
+          Top = 39
+          Width = 76
+          Height = 21
+          TabOrder = 1
+          Text = 'ebDestAccount'
+          OnChange = ebDestAccountChange
+          OnClick = rbTransactionClick
+          OnExit = ebDestAccountExit
+        end
+        object ebAmount: TEdit
+          Left = 298
+          Top = 39
+          Width = 86
+          Height = 21
+          TabOrder = 2
+          Text = 'Edit1'
+          OnChange = ebDestAccountChange
+          OnClick = rbTransactionClick
+          OnExit = ebFeeExit
+        end
+        object cbNewPrivateKey: TComboBox
+          Left = 170
+          Top = 82
+          Width = 294
+          Height = 21
+          Style = csDropDownList
+          Sorted = True
+          TabOrder = 5
+          OnChange = cbNewPrivateKeyChange
+          OnClick = rbTransactionClick
+        end
+        object rbTransferToANewOwner: TRadioButton
+          Left = 15
+          Top = 112
+          Width = 220
+          Height = 17
+          Caption = 'Transfer account to a new owner'
+          TabOrder = 7
+          OnClick = rbTransactionClick
+        end
+        object ebNewPublicKey: TEdit
+          Left = 170
+          Top = 135
+          Width = 331
+          Height = 21
+          TabOrder = 8
+          Text = 'ebDestAccount'
+          OnChange = ebNewPublicKeyChange
+          OnClick = rbTransactionClick
+        end
+        object ebFee: TEdit
+          Left = 431
+          Top = 39
+          Width = 70
+          Height = 21
+          TabOrder = 3
+          Text = 'ebFee'
+          OnChange = ebDestAccountChange
+          OnClick = rbTransactionClick
+          OnExit = ebFeeExit
+        end
+        object bbKeys: TBitBtn
+          Left = 470
+          Top = 81
+          Width = 31
+          Height = 22
+          DoubleBuffered = True
+          Glyph.Data = {
+            36050000424D3605000000000000360400002800000010000000100000000100
+            0800000000000001000000000000000000000001000000010000FF00FF008C6B
+            6C0087707000AE877C000D7FA9006F7C88006D7C94001F7ECE000E80AA001180
+            A7001081AB00048CB900078DBC000B8DBA000C8DBA00088EBC001285B0007882
+            95006092BD005EA8BE000A91C1000F9DCE002087DE0011A7D10030BCDC001F89
+            E00059A9DC0044BADD004ABFE00057AEF4004DB1F90049B2FF004EB7FF0057B1
+            F60050B6FE0022D7FC0024D7FF0024D8FD0039D7FB0035D8FD004BC6DC0046C6
+            E40048D5EE0075D3E90058E9FD006FE6FF0070E6FF0071F9FE007BFFFF008683
+            88008B8697008F989B00969594009C919000AD858000AD868500AB939500A49E
+            9900B1979400B5A09F008AA5AD00CAA08C00CDAC9300C2A69A00F3BE8000C6AE
+            A000CFB7A100D3BBA200F4C88E00F5CB9A00F5D09C00F8D09800DAC5B700E4CC
+            A900EFD2A900ECD1AC00F6DAAB00F5DEB500F5E1B600F9E1B100FEEAB900FFF2
+            BA00A1C6C8008DE6FA0081F8FE008CFAFD008DFCFE0097FCFD009BFBFD00B8ED
+            F600A7FFFF00AAFFFE00ADFFFE00B6F6FF00B1FCFD00B4FFFF00ECDDCC00E8DD
+            D600FFF7C600FCF5CD00FCF7D100FAF6D600FFFBD500FEFED600F7F2D900FEFF
+            D900FFFEDD00C6F5FF00C6FEFF00D2FFFF00FEF7E000FBFCE100FDFFE100FFFF
+            E400E3FEFF00F9F6F200FFFFF400F1FBFC00F5FFFE00FBFFFF00000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            0000000000000000000000000000000000000000000000000000000000000000
+            00000000000000000605000004080408080A000000000011191A000B2A23272D
+            531B080000001116201D0B552C23272E531C1509003207201D000F552C23272B
+            3A3F41030112202000000F552C252938606771684236200000000B5F5D6B3B61
+            74676A67513D000000000B59181735716A676A63474B360000000F282C23396A
+            6A6A6A4C404D360000000B552C2534656A654F455049360000000B552C251343
+            6247446E7336000000000B552C25263C3E4B4E483636000000000F55542F3057
+            523331020000000000000B77766D5F5C5C5C2F08000000000000001476726C5C
+            5A58100000000000000000000F0F0B0F0F0F0000000000000000}
+          ParentDoubleBuffered = False
+          TabOrder = 6
+          OnClick = bbKeysClick
+        end
+      end
+      object gbPayload: TGroupBox
+        Left = 13
+        Top = 181
+        Width = 521
+        Height = 119
+        Caption = ' Payload: '
+        TabOrder = 1
+        object lblEncryptPassword: TLabel
+          Left = 38
+          Top = 76
+          Width = 50
+          Height = 13
+          Caption = 'Password:'
+        end
+        object Label4: TLabel
+          Left = 255
+          Top = 15
+          Width = 67
+          Height = 13
+          Caption = 'Payload data:'
+        end
+        object lblEncryptionErrors: TLabel
+          Left = 255
+          Top = 96
+          Width = 246
+          Height = 13
+          AutoSize = False
+          Caption = 'Errors detected'
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clRed
+          Font.Height = -11
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+        end
+        object lblPayloadLength: TLabel
+          Left = 483
+          Top = 15
+          Width = 18
+          Height = 13
+          Alignment = taRightJustify
+          Caption = '000'
+        end
+        object rbEncryptedWithEC: TRadioButton
+          Left = 15
+          Top = 35
+          Width = 231
+          Height = 17
+          Caption = 'Encrypted with dest account public key'
+          Checked = True
+          TabOrder = 1
+          TabStop = True
+          OnClick = memoPayloadClick
+        end
+        object rbEncrptedWithPassword: TRadioButton
+          Left = 15
+          Top = 53
+          Width = 231
+          Height = 17
+          Caption = 'Encrypted with Password'
+          TabOrder = 2
+          OnClick = memoPayloadClick
+        end
+        object rbNotEncrypted: TRadioButton
+          Left = 15
+          Top = 93
+          Width = 231
+          Height = 17
+          Caption = 'Don'#39't encrypt (Public payload)'
+          TabOrder = 4
+          OnClick = memoPayloadClick
+        end
+        object ebEncryptPassword: TEdit
+          Left = 94
+          Top = 73
+          Width = 122
+          Height = 21
+          TabOrder = 3
+          Text = 'ebDestAccount'
+          OnChange = ebEncryptPasswordChange
+          OnClick = memoPayloadClick
+        end
+        object memoPayload: TMemo
+          Left = 255
+          Top = 37
+          Width = 246
+          Height = 57
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clBlack
+          Font.Height = -16
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+          TabOrder = 5
+          WantReturns = False
+          OnChange = memoPayloadClick
+          OnClick = memoPayloadClick
+        end
+        object rbEncryptedWithOldEC: TRadioButton
+          Left = 15
+          Top = 16
+          Width = 231
+          Height = 17
+          Caption = 'Encrypted with old public key'
+          TabOrder = 0
+          OnClick = memoPayloadClick
+        end
+      end
+    end
+    object tsGlobalError: TTabSheet
+      ImageIndex = 1
+      TabVisible = False
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object lblGlobalErrors: TLabel
+        Left = 40
+        Top = 50
+        Width = 456
+        Height = 156
+        Alignment = taCenter
+        AutoSize = False
+        Caption = '????'
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clRed
+        Font.Height = -21
+        Font.Name = 'Tahoma'
+        Font.Style = [fsBold]
+        ParentFont = False
+        WordWrap = True
+      end
+      object bbPassword: TBitBtn
+        Left = 191
+        Top = 243
+        Width = 159
+        Height = 38
+        Caption = 'Password'
+        DoubleBuffered = True
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clWindowText
+        Font.Height = -13
+        Font.Name = 'Tahoma'
+        Font.Style = [fsBold]
+        Glyph.Data = {
+          76060000424D7606000000000000360400002800000018000000180000000100
+          0800000000004002000000000000000000000001000000010000000000000101
+          0100020202000303030004040400050505000606060007070700080808000909
+          09000A0A0A000B0B0B000C0C0C000D0D0D000E0E0E000F0F0F00101010001111
+          1100121212001313130014141400151515001616160017171700181818001919
+          19001A1A1A001B1B1B001C1C1C001D1D1D001E1E1E001F1F1F00202020002121
+          2100222222002323230024242400252525002626260027272700282828002929
+          29002A2A2A002B2B2B002C2C2C002D2D2D002E2E2E002F2F2F00303030003131
+          3100323232003333330034343400353535003636360037373700383838003939
+          39003A3A3A003B3B3B003C3C3C003D3D3D003E3E3E003F3F3F00404040004141
+          4100424242004343430044444400454545004646460047474700484848004949
+          49004A4A4A004B4B4B004C4C4C004D4D4D004E4E4E004F4F4F00505050005151
+          5100525252005353530054545400555555005656560057575700585858005959
+          59005A5A5A005B5B5B005C5C5C005D5D5D005E5E5E00685968007C4F7C009441
+          9400B72CB700DD15DD00F506F500FD01FD00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+          FE00FE00FE00FE00FE00FE00FE00F008F900D517F000BD24E800A830E1007B4B
+          D0005065C1003774B7002B7DB100237FAC001F82AD001C83AD001785AE001687
+          AF001489B100128AB300108CB6000F8EB9000D91BB000C94BF000A97C3000A99
+          C5000A9AC7000B9BC8000B9CCA000C9ECC000C9FCD000DA0CE000DA1CE000EA2
+          CF0011A3CF0015A4CF0018A5CF001CA7D10021A9D1002AAAD00035AACD0035AD
+          D00035AFD30035B1D60037B5D8003AB8DB003EB9DC0040BBDD0045BEDE004CC1
+          E00055C6E3005BC8E40061CAE40066CBE3006BCCE30072D3E60078D8EB0080D8
+          EE0083D9F10085DCF40088DBF5008BDBF6008EDAF70091DAF70095DCF70097E0
+          F7009EE3F800A4E7F900A9E9F900AEE9F900B5EBF900B2EBF800919191919191
+          919191919191919191919191919191919191919191919191D5E7E8E7E3DED591
+          919191919191919191919191919191D6DDF7F8F8F7F6F4EEE6D5919191919191
+          919191919191D7DADEF8F8F6F7F6EDF1F8F1E69191919191919191919191DADE
+          DFFAFAF8F7F7E9EDF7F7F8EB91919191919191919191DDE5E3FCFCFAFAEEE0E9
+          F3F7F7F7DC919191919191919191DEE7E4FEFEFCFCEDC5E0EFF7F6F6DC919191
+          919191919191DEEAE7FEFEFEFDFBC4C4F5F7F6F6DC919191919191919191E3EC
+          E9FEFEFEFEFEE0C3F9F7F6F6DC919191919191919191E3E9E3F8F8F1F1F8FBFB
+          FCFAF7F6DC919191919191919191DCEAEAE0E1E9E5E3DEE4EBF2FAF9DC919191
+          919191919191DBF3EEC5C5EFF3EBE5DDD9D6E6F5DC91919191919191919191D5
+          E8EDF1FAF6F0EAE4DAD1D2DBDC919191919191919191919191DBE8F0F3F3F3E4
+          D0CFE4D8DC919191919191919191919191919191D5DEE4DED2CFD4D491919191
+          91919191919191919191919191919191D2CE9191919191919191919191919191
+          91C8C89191919191D2CE919191919191919191919191919191D9D09191919191
+          D2CE919191919191919191919191919191E6E39191919191D2CE919191919191
+          919191919191919191E0F3CE91919191D2CE9191919191919191919191919191
+          9191EFF6E1C9C8CDD9CC91919191919191919191919191919191C9EDF9F3F3EB
+          DC91919191919191919191919191919191919191CBE0E1CF9191919191919191
+          9191919191919191919191919191919191919191919191919191}
+        ParentDoubleBuffered = False
+        ParentFont = False
+        TabOrder = 0
+        OnClick = bbPasswordClick
+      end
+    end
+  end
+  object ActionList1: TActionList
+    Left = 135
+    Top = 325
+    object actExecute: TAction
+      Caption = 'Execute (F12)'
+      ShortCut = 123
+      OnExecute = actExecuteExecute
+    end
+  end
+end

+ 634 - 0
Units/Forms/UFRMOperation.pas

@@ -0,0 +1,634 @@
+unit UFRMOperation;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UNode, UWalletKeys, UCrypto, Buttons, UBlockChain,
+  UAccounts, ActnList, ComCtrls;
+
+type
+  TFRMOperation = class(TForm)
+    Label1: TLabel;
+    bbExecute: TBitBtn;
+    bbCancel: TBitBtn;
+    lblAccountBalance: TLabel;
+    Label2: TLabel;
+    ebSenderAccount: TEdit;
+    PageControl: TPageControl;
+    tsOperation: TTabSheet;
+    gbOperation: TGroupBox;
+    lblDestAccount: TLabel;
+    lblAmount: TLabel;
+    lblNewPrivateKey: TLabel;
+    lblTransactionErrors: TLabel;
+    lblChangeKeyErrors: TLabel;
+    lblNewOwnerPublicKey: TLabel;
+    lblNewOwnerErrors: TLabel;
+    lblFee: TLabel;
+    rbTransaction: TRadioButton;
+    rbChangeKey: TRadioButton;
+    ebDestAccount: TEdit;
+    ebAmount: TEdit;
+    cbNewPrivateKey: TComboBox;
+    rbTransferToANewOwner: TRadioButton;
+    ebNewPublicKey: TEdit;
+    ebFee: TEdit;
+    bbKeys: TBitBtn;
+    gbPayload: TGroupBox;
+    lblEncryptPassword: TLabel;
+    Label4: TLabel;
+    lblEncryptionErrors: TLabel;
+    lblPayloadLength: TLabel;
+    rbEncryptedWithEC: TRadioButton;
+    rbEncrptedWithPassword: TRadioButton;
+    rbNotEncrypted: TRadioButton;
+    ebEncryptPassword: TEdit;
+    memoPayload: TMemo;
+    rbEncryptedWithOldEC: TRadioButton;
+    ActionList1: TActionList;
+    actExecute: TAction;
+    tsGlobalError: TTabSheet;
+    lblGlobalErrors: TLabel;
+    bbPassword: TBitBtn;
+    procedure FormCreate(Sender: TObject);
+    procedure rbTransactionClick(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure memoPayloadClick(Sender: TObject);
+    procedure ebDestAccountChange(Sender: TObject);
+    procedure cbNewPrivateKeyChange(Sender: TObject);
+    procedure ebNewPublicKeyChange(Sender: TObject);
+    procedure ebEncryptPasswordChange(Sender: TObject);
+    procedure ebFeeExit(Sender: TObject);
+    procedure bbKeysClick(Sender: TObject);
+    procedure actExecuteExecute(Sender: TObject);
+    procedure ebSenderAccountExit(Sender: TObject);
+    procedure ebSenderAccountKeyPress(Sender: TObject; var Key: Char);
+    procedure bbPasswordClick(Sender: TObject);
+    procedure ebDestAccountExit(Sender: TObject);
+  private
+    FSenderAccount: Cardinal;
+    FNode : TNode;
+    FWalletKeys: TWalletKeys;
+    FPAccount : PAccount;
+    FOperation : TPCOperation;
+    FFee: Int64;
+    FEncodedPayload : TRawBytes;
+    FDisabled : Boolean;
+    //
+    FTxDestAccount : Cardinal;
+    FTxAmount : Int64;
+    FNewAccountPublicKey : TAccountKey;
+    procedure SetSenderAccount(const Value: Cardinal);
+    procedure SetWalletKeys(const Value: TWalletKeys);
+    { Private declarations }
+    Function UpdateOperationOptions(var errors : AnsiString) : Boolean;
+    Function UpdatePayload(var errors : AnsiString) : Boolean;
+    Function GetSenderAcccount: PAccount;
+    procedure SetFee(const Value: Int64);
+  public
+    { Public declarations }
+    Property SenderAccount : Cardinal read FSenderAccount write SetSenderAccount;
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+    Property Fee : Int64 read FFee write SetFee;
+  end;
+
+implementation
+
+uses
+  UECIES, UConst, UOpTransaction, UFRMNewPrivateKeyType, UAES, UFRMWalletKeys;
+
+{$R *.dfm}
+
+{ TFRMOperation }
+
+procedure TFRMOperation.actExecuteExecute(Sender: TObject);
+Var errors : AnsiString;
+  P : PAccount;
+  i : Integer;
+  wk : TWalletKey;
+  npk : TECPrivateKey;
+  FRM : TFRMNewPrivateKeyType;
+begin
+  P := GetSenderAcccount;
+  if Not Assigned(P) then raise Exception.Create('Invalid sender account');
+  if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
+  i := WalletKeys.IndexOfAccountKey(P^.accountkey);
+  if (i<0) then begin
+    Raise Exception.Create('Sender account private key not found in Wallet');
+  end;
+  wk := WalletKeys.Key[i];
+  If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
+  If Not UpdatePayload(errors) then raise Exception.Create(errors);
+  //
+  FreeAndNil(FOperation);
+  if rbTransaction.Checked then begin
+    FOperation := TOpTransaction.Create(SenderAccount,P^.n_operation+1,FTxDestAccount,wk.PrivateKey,FTxAmount,fee,FEncodedPayload);
+  end else if rbChangeKey.Checked then begin
+    i := Integer(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+    if i=-1 then begin
+      // New key
+      FRM := TFRMNewPrivateKeyType.Create(Self);
+      try
+        FRM.WalletKeys := WalletKeys;
+        if FRM.ShowModal=MrOk then begin
+          npk := FRM.GeneratedPrivateKey;
+          FNewAccountPublicKey := npk.PublicKey;
+        end else begin
+          raise Exception.Create('No new key generated');
+        end;
+      finally
+        FRM.Free;
+      end;
+      // Note: Regenerate Payload because we have created a new Private Key...
+      If Not UpdatePayload(errors) then raise Exception.Create(errors);
+    end else begin
+      FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+    end;
+    FOperation := TOpChangeKey.Create(SenderAccount,P^.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,fee,FEncodedPayload);
+  end else if rbTransferToANewOwner.Checked then begin
+    FOperation := TOpChangeKey.Create(SenderAccount,P^.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,fee,FEncodedPayload);
+  end else begin
+    raise Exception.Create('No operation selected');
+  end;
+  if Application.MessageBox(PChar('Execute this operation:'+#10+#10+FOperation.ToString+#10+#10+'Note: This operation will be transmitted to the network!'),
+     PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
+
+  // Execute operation
+  If FNode.AddOperation(nil,FOperation,errors) then begin
+    Application.MessageBox(PChar('Executed opreation:'+#10+FOperation.ToString),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+    ModalResult := MrOk;
+  end else begin
+    raise Exception.Create(errors);
+  end;
+end;
+
+procedure TFRMOperation.bbKeysClick(Sender: TObject);
+Var FRM : TFRMWalletKeys;
+begin
+  FRM := TFRMWalletKeys.Create(Self);
+  Try
+    FRM.WalletKeys := WalletKeys;
+    FRM.ShowModal;
+    rbChangeKey.Checked := true;
+    cbNewPrivateKey.SetFocus;
+    SetWalletKeys(WalletKeys);
+  Finally
+    FRM.Free;
+  End;
+end;
+
+procedure TFRMOperation.bbPasswordClick(Sender: TObject);
+Var s : String;
+  errors : AnsiString;
+begin
+  if FWalletKeys.IsValidPassword then begin
+  end else begin
+    s := '';
+    Repeat
+      if Not InputQuery('Wallet Password','Insert Wallet Password',s) then exit;
+      FWalletKeys.WalletPassword := s;
+    Until FWalletKeys.IsValidPassword;
+    UpdateOperationOptions(errors);
+  end;
+end;
+
+procedure TFRMOperation.cbNewPrivateKeyChange(Sender: TObject);
+begin
+  If FDisabled then exit;
+  rbChangeKey.Checked := true;
+  rbTransactionClick(Nil);
+end;
+
+procedure TFRMOperation.ebDestAccountChange(Sender: TObject);
+begin
+  if FDisabled then exit;
+  rbTransaction.Checked := true;
+  rbTransactionClick(Nil);
+end;
+
+procedure TFRMOperation.ebDestAccountExit(Sender: TObject);
+Var an : Cardinal;
+begin
+  If TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,an) then begin
+    ebDestAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
+  end else begin
+    ebDestAccount.Text := '';
+  end;
+end;
+
+procedure TFRMOperation.ebEncryptPasswordChange(Sender: TObject);
+begin
+  if FDisabled then exit;
+  rbEncrptedWithPassword.Checked := true;
+  memoPayloadClick(Nil);
+end;
+
+procedure TFRMOperation.ebFeeExit(Sender: TObject);
+Var l : boolean;
+begin
+  l := FDisabled;
+  FDisabled := true;
+  try
+    ebFee.Text := TAccountComp.FormatMoney(Fee);
+    ebAmount.Text := TAccountComp.FormatMoney(FTxAmount);
+  finally
+    FDisabled := l;
+  end;
+end;
+
+procedure TFRMOperation.ebNewPublicKeyChange(Sender: TObject);
+begin
+  if FDisabled then exit;
+  rbTransferToANewOwner.Checked := true;
+  rbTransactionClick(Nil);
+end;
+
+procedure TFRMOperation.ebSenderAccountExit(Sender: TObject);
+Var an : Cardinal;
+begin
+  If TAccountComp.AccountTxtNumberToAccountNumber(ebSenderAccount.Text,an) then begin
+    SenderAccount := an;
+    ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
+  end else begin
+    ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccount);
+  end;
+end;
+
+procedure TFRMOperation.ebSenderAccountKeyPress(Sender: TObject; var Key: Char);
+begin
+  if Key=#13 then ebSenderAccountExit(Nil);
+end;
+
+procedure TFRMOperation.FormCreate(Sender: TObject);
+begin
+  FDisabled := true;
+  FNode := TNode.Node;
+  FOperation := Nil;
+  FPAccount := Nil;
+  ebDestAccount.Text := '';
+  ebAmount.Text := TAccountComp.FormatMoney(0);
+  ebEncryptPassword.Text := '';
+  ebNewPublicKey.Text := '';
+  ebFee.Text := TAccountComp.FormatMoney(0);
+  memoPayload.Lines.Text := '';
+  lblTransactionErrors.Caption := '';
+  lblChangeKeyErrors.Caption := '';
+  lblEncryptionErrors.Caption := '';
+  lblNewOwnerErrors.Caption := '';
+  FTxDestAccount := 0;
+  FTxAmount := 0;
+  FSenderAccount := MaxInt;
+  FNewAccountPublicKey := CT_Account_NUL.accountkey;
+  FDisabled := false;
+  lblAccountBalance.Caption := '';
+end;
+
+procedure TFRMOperation.FormDestroy(Sender: TObject);
+begin
+  if Assigned(FPAccount) then Dispose(FPAccount);
+  if assigned(FOperation) then FOperation.Free;
+end;
+
+function TFRMOperation.GetSenderAcccount: PAccount;
+begin
+  if Not Assigned(FPAccount) then begin
+    if (Assigned(FNode)) And (FSenderAccount>=0) And (FSenderAccount<FNode.Bank.AccountsCount) then begin
+      New(FPAccount);
+      FPAccount^ := FNode.Operations.SafeBoxTransaction.Account(FSenderAccount);
+    end;
+  end;
+  Result := FPAccount;
+  if Assigned(FPAccount) then begin
+    lblAccountBalance.Caption := 'Balance: '+TAccountComp.FormatMoney(FPAccount.balance);
+  end else lblAccountBalance.Caption := '';
+end;
+
+procedure TFRMOperation.memoPayloadClick(Sender: TObject);
+Var errors : AnsiString;
+begin
+  UpdatePayload(errors);
+end;
+
+procedure TFRMOperation.rbTransactionClick(Sender: TObject);
+Var errors : AnsiString;
+begin
+  UpdateOperationOptions(errors);
+end;
+
+procedure TFRMOperation.SetFee(const Value: Int64);
+var wd : Boolean;
+begin
+  if FFee = Value then exit;
+  wd := FDisabled;
+  try
+    FDisabled := true;
+    FFee := Value;
+    ebFee.Text := TAccountComp.FormatMoney(value);
+  finally
+    FDisabled := wd;
+  end;
+end;
+
+procedure TFRMOperation.SetSenderAccount(const Value: Cardinal);
+begin
+  if FSenderAccount=Value then exit;
+
+  FSenderAccount := Value;
+  if Assigned(FPAccount) then begin
+    Dispose(FPAccount);
+    FPAccount := Nil;
+  end;
+  ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(Value);
+  //
+  rbTransactionClick(Nil);
+end;
+
+procedure TFRMOperation.SetWalletKeys(const Value: TWalletKeys);
+Var i : Integer;
+  wk : TWalletKey;
+  s : String;
+begin
+  FWalletKeys := Value;
+  cbNewPrivateKey.items.BeginUpdate;
+  Try
+    cbNewPrivateKey.Items.Clear;
+    cbNewPrivateKey.Items.AddObject('Generate a new Private Key',TObject(-1));
+    For i:=0 to FWalletKeys.Count-1 do begin
+      wk := FWalletKeys.Key[i];
+      if (wk.Name='') then begin
+        s := TCrypto.ToHexaString( TAccountComp.AccountKey2RawString(wk.AccountKey));
+      end else begin
+        s := wk.Name;
+      end;
+      if Not Assigned(wk.PrivateKey) then s := s + '(*)';
+      cbNewPrivateKey.Items.AddObject(s,TObject(i));
+    end;
+  Finally
+    cbNewPrivateKey.Items.EndUpdate;
+  End;
+  rbTransactionClick(Nil);
+  memoPayloadClick(Nil);
+end;
+
+function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
+Var P : PAccount;
+  i : Integer;
+  wk : TWalletKey;
+  e : AnsiString;
+begin
+  Result := false;
+  errors := '';
+  FreeAndNil(FOperation);
+  rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
+  lblDestAccount.Enabled := rbTransaction.Checked;
+  lblAmount.Enabled := rbTransaction.Checked;
+  lblFee.Enabled := rbTransaction.Checked;
+  lblNewPrivateKey.Enabled := rbChangeKey.Checked;
+  lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
+  try
+    Try
+      P := GetSenderAcccount;
+      bbPassword.Visible := false;
+      bbPassword.Enabled := false;
+      if Not Assigned(P) then begin
+        errors := 'Invalid sender account';
+        lblGlobalErrors.Caption := errors;
+        exit;
+      end;
+      if Not Assigned(WalletKeys) then begin
+        errors := 'No wallet keys';
+        lblGlobalErrors.Caption := errors;
+        exit;
+      end;
+      i := WalletKeys.IndexOfAccountKey(P^.accountkey);
+      if (i<0) then begin
+        errors := 'Private Key not found in Wallet... You cannot operate with this account';
+        lblGlobalErrors.Caption := errors;
+        exit;
+      end;
+      wk := WalletKeys.Key[i];
+      if not assigned(wk.PrivateKey) then begin
+        if wk.CryptedKey<>'' then begin
+          errors := 'Wallet is password protected. Need password';
+          bbPassword.Visible := true;
+          bbPassword.Enabled := true;
+        end else begin
+          errors := 'Private Key not found in Wallet, only public key. You cannot operate with this account';
+        end;
+        lblGlobalErrors.Caption := errors;
+        exit;
+      end;
+      lblGlobalErrors.Caption := '';
+    Finally
+      tsGlobalError.TabVisible := false;
+      tsOperation.TabVisible := false;
+      if lblGlobalErrors.Caption<>'' then begin
+        PageControl.ActivePage := tsGlobalError;
+      end else begin
+        PageControl.ActivePage := tsOperation;
+      end;
+    End;
+    if rbTransaction.Checked then begin
+      rbTransaction.Font.Style := [fsBold];
+      rbChangeKey.ParentFont := true;
+      rbTransferToANewOwner.ParentFont := true;
+      lblChangeKeyErrors.Caption := '';
+      lblNewOwnerErrors.Caption := '';
+      rbEncryptedWithOldEC.Checked := false;
+      rbEncryptedWithEC.Caption := 'Encrypted with dest account public key';
+      ebDestAccount.ParentFont := true;
+      ebAmount.ParentFont := true;
+      ebFee.ParentFont := true;
+      cbNewPrivateKey.Font.Color := clGrayText;
+      ebNewPublicKey.Font.Color := clGrayText;
+      if not (TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,FTxDestAccount)) then begin
+        errors := 'Invalid dest account ('+ebDestAccount.Text+')';
+        lblTransactionErrors.Caption := errors;
+        exit;
+      end;
+      if not TAccountComp.TxtToMoney(ebAmount.Text,FTxAmount) then begin
+        errors := 'Invalid amount ('+ebAmount.Text+')';
+        lblTransactionErrors.Caption := errors;
+        exit;
+      end;
+      if not TAccountComp.TxtToMoney(ebFee.Text,FFee) then begin
+        errors := 'Invalid fee ('+ebFee.Text+')';
+        lblTransactionErrors.Caption := errors;
+        exit;
+      end;
+      if (P^.balance<(FTxAmount+FFee)) then begin
+        errors := 'Insufficient founds';
+        lblTransactionErrors.Caption := errors;
+        exit;
+      end;
+
+      lblTransactionErrors.Caption := '';
+      result := true;
+    end else if rbChangeKey.Checked then begin
+      rbTransaction.ParentFont := true;
+      rbChangeKey.Font.Style := [fsBold];
+      rbTransferToANewOwner.ParentFont := true;
+      lblTransactionErrors.Caption := '';
+      lblNewOwnerErrors.Caption := '';
+      rbEncryptedWithEC.Caption := 'Encrypted with new public key';
+      ebDestAccount.Font.Color := clGrayText;
+      ebAmount.Font.Color := clGrayText;
+      ebFee.Font.Color := clGrayText;
+      cbNewPrivateKey.ParentFont := true;
+      ebNewPublicKey.Font.Color := clGrayText;
+      //
+      if cbNewPrivateKey.ItemIndex<0 then begin
+        errors := 'Must select a new Private key';
+        lblChangeKeyErrors.Caption := errors;
+        exit;
+
+      end;
+      lblChangeKeyErrors.Caption := '';
+      Result := true;
+    end else if rbTransferToANewOwner.Checked then begin
+      rbTransaction.ParentFont := true;
+      rbChangeKey.ParentFont := true;
+      rbTransferToANewOwner.Font.Style := [fsBold];
+      lblTransactionErrors.Caption := '';
+      lblChangeKeyErrors.Caption := '';
+      lblNewOwnerErrors.Caption := '';
+      ebDestAccount.Font.Color := clGrayText;
+      ebAmount.Font.Color := clGrayText;
+      ebFee.Font.Color := clGrayText;
+      cbNewPrivateKey.Font.Color := clGrayText;
+      ebNewPublicKey.ParentFont := true;
+      If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,FNewAccountPublicKey,errors) then begin
+//        errors := 'Invalid new owner public key';
+        lblNewOwnerErrors.Caption := errors;
+        exit;
+      end;
+      Result := true;
+    end else begin
+      rbTransaction.ParentFont := true;
+      rbChangeKey.ParentFont := true;
+      rbTransferToANewOwner.ParentFont := true;
+      ebDestAccount.Font.Color := clGrayText;
+      ebAmount.Font.Color := clGrayText;
+      ebFee.Font.Color := clGrayText;
+      cbNewPrivateKey.Font.Color := clGrayText;
+      lblTransactionErrors.Caption := '';
+      lblChangeKeyErrors.Caption := '';
+      lblNewOwnerErrors.Caption := '';
+      errors := 'Must select an operation';
+    end;
+  finally
+
+  end;
+  //
+  UpdatePayload(e);
+end;
+
+function TFRMOperation.UpdatePayload(var errors : AnsiString) : Boolean;
+Var payload_u : AnsiString;
+  payload_encrypted : TRawBytes;
+  account : TAccount;
+  dest_account_number : Cardinal;
+  i : Integer;
+  valid : Boolean;
+  wk : TWalletKey;
+begin
+  valid := false;
+  payload_encrypted := '';
+  FEncodedPayload := '';
+  errors := 'Unknown error';
+  payload_u := memoPayload.Lines.Text;
+  try
+    if (payload_u='') then begin
+      valid := true;
+      exit;
+    end;
+    if (rbEncryptedWithOldEC.Checked) then begin
+      errors := 'Error encrypting';
+      account := FNode.Node.Operations.SafeBoxTransaction.Account(SenderAccount);
+      payload_encrypted := ECIESEncrypt(account.accountkey,payload_u);
+      valid := payload_encrypted<>'';
+    end else if (rbEncryptedWithEC.Checked) then begin
+      errors := 'Error encrypting';
+      if rbTransaction.Checked then begin
+        // With dest public key
+        If Not TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,dest_account_number) then begin
+          errors := 'Invalid dest account number';
+          exit;
+        end;
+        account := FNode.Node.Operations.SafeBoxTransaction.Account(dest_account_number);
+        payload_encrypted := ECIESEncrypt(account.accountkey,payload_u);
+        valid := payload_encrypted<>'';
+      end else if rbChangeKey.Checked then begin
+        // With new key generated
+        if (cbNewPrivateKey.ItemIndex>=0) then begin
+          i := Integer(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+          if (i>=0) then FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+        end else begin
+          valid := false;
+          errors := 'Must select a private key';
+          exit;
+        end;
+        if FNewAccountPublicKey.EC_OpenSSL_NID<>CT_Account_NUL.accountkey.EC_OpenSSL_NID then begin
+          payload_encrypted := ECIESEncrypt(FNewAccountPublicKey,payload_u);
+          valid := payload_encrypted<>'';
+        end else begin
+          valid := false;
+          errors := 'Selected private key is not valid to encode';
+          exit;
+        end;
+      end else if rbTransferToANewOwner.Checked then begin
+        //
+        if FNewAccountPublicKey.EC_OpenSSL_NID<>CT_Account_NUL.accountkey.EC_OpenSSL_NID then begin
+          payload_encrypted := ECIESEncrypt(FNewAccountPublicKey,payload_u);
+          valid := payload_encrypted<>'';
+        end else begin
+          valid := false;
+          errors := 'New owner public key not valid';
+        end;
+      end else begin
+        errors := 'Must select operation type to encrypt payload';
+        valid := false;
+      end;
+    end else if (rbEncrptedWithPassword.Checked) then begin
+      payload_encrypted := TAESComp.EVP_Encrypt_AES256(payload_u,ebEncryptPassword.Text);
+      valid := payload_encrypted<>'';
+    end else if (rbNotEncrypted.Checked) then begin
+      payload_encrypted := payload_u;
+      valid := true;
+    end else begin
+      errors := 'Must select an encryption option for payload';
+    end;
+  finally
+    if valid then begin
+      if length(payload_encrypted)>CT_MaxPayloadSize then begin
+        valid := false;
+        errors := 'Payload size is bigger than '+inttostr(CT_MaxPayloadSize)+' ('+Inttostr(length(payload_encrypted))+')';
+      end;
+    end;
+    if valid then begin
+      lblEncryptionErrors.Caption := '';
+      lblPayloadLength.Caption := Format('(%db -> %db)',[length(payload_u),length(payload_encrypted)]);
+    end else begin
+      lblEncryptionErrors.Caption := errors;
+      lblPayloadLength.Caption := Format('(%db -> ?)',[length(payload_u)]);
+    end;
+    FEncodedPayload := payload_encrypted;
+    Result := valid;
+  end;
+end;
+
+end.

+ 241 - 0
Units/Forms/UFRMPascalCoinWalletConfig.dfm

@@ -0,0 +1,241 @@
+object FRMPascalCoinWalletConfig: TFRMPascalCoinWalletConfig
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsSingle
+  Caption = 'Options'
+  ClientHeight = 311
+  ClientWidth = 374
+  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 Label1: TLabel
+    Left = 30
+    Top = 201
+    Width = 120
+    Height = 13
+    Caption = 'Default fee for operation'
+  end
+  object Label2: TLabel
+    Left = 30
+    Top = 116
+    Width = 98
+    Height = 13
+    Caption = 'Internet Server Port'
+  end
+  object lblDefaultInternetServerPort: TLabel
+    Left = 248
+    Top = 116
+    Width = 70
+    Height = 13
+    Caption = '(Default XXXX)'
+  end
+  object Label3: TLabel
+    Left = 15
+    Top = 67
+    Width = 60
+    Height = 13
+    Caption = 'Miner Name:'
+  end
+  object Label4: TLabel
+    Left = 90
+    Top = 90
+    Width = 259
+    Height = 13
+    Caption = 'This name will be included in each new block you mine!'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clGray
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    Font.Style = []
+    ParentFont = False
+  end
+  object cbAutomaticMiningWhenConnectedToNodes: TCheckBox
+    Left = 15
+    Top = 150
+    Width = 261
+    Height = 17
+    Caption = 'Allow automatic mining when connected to nodes'
+    TabOrder = 4
+  end
+  object cbGenerateANewPrivateKeyForEachNewGeneratedBlock: TCheckBox
+    Left = 15
+    Top = 173
+    Width = 326
+    Height = 17
+    Caption = 'Generate a new Private key for each new generated block'
+    TabOrder = 5
+  end
+  object udDefaultFee: TUpDown
+    Left = 226
+    Top = 198
+    Width = 17
+    Height = 21
+    Max = 5000000
+    Increment = 5
+    TabOrder = 7
+    Thousands = False
+    OnChangingEx = udDefaultFeeChangingEx
+  end
+  object ebDefaultFee: TEdit
+    Left = 170
+    Top = 198
+    Width = 56
+    Height = 21
+    Alignment = taRightJustify
+    TabOrder = 6
+    Text = '0'
+  end
+  object cbSaveLogFiles: TCheckBox
+    Left = 15
+    Top = 225
+    Width = 97
+    Height = 17
+    Caption = 'Save log file'
+    TabOrder = 8
+  end
+  object cbShowLogs: TCheckBox
+    Left = 15
+    Top = 248
+    Width = 97
+    Height = 17
+    Caption = 'Show logs'
+    TabOrder = 9
+  end
+  object bbOk: TBitBtn
+    Left = 186
+    Top = 270
+    Width = 75
+    Height = 25
+    DoubleBuffered = True
+    Kind = bkOK
+    ParentDoubleBuffered = False
+    TabOrder = 10
+    OnClick = bbOkClick
+  end
+  object bbCancel: TBitBtn
+    Left = 276
+    Top = 270
+    Width = 75
+    Height = 25
+    DoubleBuffered = True
+    Kind = bkCancel
+    ParentDoubleBuffered = False
+    TabOrder = 11
+  end
+  object udInternetServerPort: TUpDown
+    Left = 226
+    Top = 113
+    Width = 16
+    Height = 21
+    Associate = ebInternetServerPort
+    Min = 1
+    Max = 40000
+    Position = 4004
+    TabOrder = 3
+    Thousands = False
+  end
+  object ebInternetServerPort: TEdit
+    Left = 170
+    Top = 113
+    Width = 56
+    Height = 21
+    Alignment = taRightJustify
+    TabOrder = 2
+    Text = '4004'
+  end
+  object bbUpdatePassword: TBitBtn
+    Left = 15
+    Top = 14
+    Width = 336
+    Height = 38
+    Caption = 'Wallet Password'
+    DoubleBuffered = True
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -13
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    Glyph.Data = {
+      76060000424D7606000000000000360400002800000018000000180000000100
+      0800000000004002000000000000000000000001000000010000000000000101
+      0100020202000303030004040400050505000606060007070700080808000909
+      09000A0A0A000B0B0B000C0C0C000D0D0D000E0E0E000F0F0F00101010001111
+      1100121212001313130014141400151515001616160017171700181818001919
+      19001A1A1A001B1B1B001C1C1C001D1D1D001E1E1E001F1F1F00202020002121
+      2100222222002323230024242400252525002626260027272700282828002929
+      29002A2A2A002B2B2B002C2C2C002D2D2D002E2E2E002F2F2F00303030003131
+      3100323232003333330034343400353535003636360037373700383838003939
+      39003A3A3A003B3B3B003C3C3C003D3D3D003E3E3E003F3F3F00404040004141
+      4100424242004343430044444400454545004646460047474700484848004949
+      49004A4A4A004B4B4B004C4C4C004D4D4D004E4E4E004F4F4F00505050005151
+      5100525252005353530054545400555555005656560057575700585858005959
+      59005A5A5A005B5B5B005C5C5C005D5D5D005E5E5E00685968007C4F7C009441
+      9400B72CB700DD15DD00F506F500FD01FD00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00F008F900D517F000BD24E800A830E1007B4B
+      D0005065C1003774B7002B7DB100237FAC001F82AD001C83AD001785AE001687
+      AF001489B100128AB300108CB6000F8EB9000D91BB000C94BF000A97C3000A99
+      C5000A9AC7000B9BC8000B9CCA000C9ECC000C9FCD000DA0CE000DA1CE000EA2
+      CF0011A3CF0015A4CF0018A5CF001CA7D10021A9D1002AAAD00035AACD0035AD
+      D00035AFD30035B1D60037B5D8003AB8DB003EB9DC0040BBDD0045BEDE004CC1
+      E00055C6E3005BC8E40061CAE40066CBE3006BCCE30072D3E60078D8EB0080D8
+      EE0083D9F10085DCF40088DBF5008BDBF6008EDAF70091DAF70095DCF70097E0
+      F7009EE3F800A4E7F900A9E9F900AEE9F900B5EBF900B2EBF800919191919191
+      919191919191919191919191919191919191919191919191D5E7E8E7E3DED591
+      919191919191919191919191919191D6DDF7F8F8F7F6F4EEE6D5919191919191
+      919191919191D7DADEF8F8F6F7F6EDF1F8F1E69191919191919191919191DADE
+      DFFAFAF8F7F7E9EDF7F7F8EB91919191919191919191DDE5E3FCFCFAFAEEE0E9
+      F3F7F7F7DC919191919191919191DEE7E4FEFEFCFCEDC5E0EFF7F6F6DC919191
+      919191919191DEEAE7FEFEFEFDFBC4C4F5F7F6F6DC919191919191919191E3EC
+      E9FEFEFEFEFEE0C3F9F7F6F6DC919191919191919191E3E9E3F8F8F1F1F8FBFB
+      FCFAF7F6DC919191919191919191DCEAEAE0E1E9E5E3DEE4EBF2FAF9DC919191
+      919191919191DBF3EEC5C5EFF3EBE5DDD9D6E6F5DC91919191919191919191D5
+      E8EDF1FAF6F0EAE4DAD1D2DBDC919191919191919191919191DBE8F0F3F3F3E4
+      D0CFE4D8DC919191919191919191919191919191D5DEE4DED2CFD4D491919191
+      91919191919191919191919191919191D2CE9191919191919191919191919191
+      91C8C89191919191D2CE919191919191919191919191919191D9D09191919191
+      D2CE919191919191919191919191919191E6E39191919191D2CE919191919191
+      919191919191919191E0F3CE91919191D2CE9191919191919191919191919191
+      9191EFF6E1C9C8CDD9CC91919191919191919191919191919191C9EDF9F3F3EB
+      DC91919191919191919191919191919191919191CBE0E1CF9191919191919191
+      9191919191919191919191919191919191919191919191919191}
+    ParentDoubleBuffered = False
+    ParentFont = False
+    TabOrder = 0
+    OnClick = bbUpdatePasswordClick
+  end
+  object ebMinerName: TEdit
+    Left = 90
+    Top = 64
+    Width = 261
+    Height = 21
+    TabOrder = 1
+    Text = 'ebMinerName'
+  end
+  object cbShowModalMessages: TCheckBox
+    Left = 170
+    Top = 225
+    Width = 171
+    Height = 17
+    Caption = 'Show Modal Messages'
+    TabOrder = 12
+  end
+end

+ 161 - 0
Units/Forms/UFRMPascalCoinWalletConfig.pas

@@ -0,0 +1,161 @@
+unit UFRMPascalCoinWalletConfig;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, UWalletKeys;
+
+type
+  TFRMPascalCoinWalletConfig = class(TForm)
+    cbAutomaticMiningWhenConnectedToNodes: TCheckBox;
+    cbGenerateANewPrivateKeyForEachNewGeneratedBlock: TCheckBox;
+    udDefaultFee: TUpDown;
+    ebDefaultFee: TEdit;
+    Label1: TLabel;
+    cbSaveLogFiles: TCheckBox;
+    cbShowLogs: TCheckBox;
+    bbOk: TBitBtn;
+    bbCancel: TBitBtn;
+    udInternetServerPort: TUpDown;
+    ebInternetServerPort: TEdit;
+    Label2: TLabel;
+    lblDefaultInternetServerPort: TLabel;
+    bbUpdatePassword: TBitBtn;
+    Label3: TLabel;
+    ebMinerName: TEdit;
+    Label4: TLabel;
+    cbShowModalMessages: TCheckBox;
+    procedure FormCreate(Sender: TObject);
+    procedure bbOkClick(Sender: TObject);
+    procedure udDefaultFeeChangingEx(Sender: TObject; var AllowChange: Boolean;
+      NewValue: SmallInt; Direction: TUpDownDirection);
+    procedure bbUpdatePasswordClick(Sender: TObject);
+  private
+    FAppParams: TAppParams;
+    FWalletKeys: TWalletKeys;
+    procedure SetAppParams(const Value: TAppParams);
+    procedure SetWalletKeys(const Value: TWalletKeys);
+    Procedure UpdateWalletConfig;
+    { Private declarations }
+  public
+    { Public declarations }
+    Property AppParams : TAppParams read FAppParams write SetAppParams;
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+  end;
+
+implementation
+
+uses UConst, UAccounts, UFRMWallet;
+
+{$R *.dfm}
+
+procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
+begin
+  AppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(udDefaultFee.Position );
+  AppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(udInternetServerPort.Position );
+  AppParams.ParamByName[CT_PARAM_NewPrivateKeyForEachGeneratedBlock].SetAsBoolean(cbGenerateANewPrivateKeyForEachNewGeneratedBlock.Checked );
+  AppParams.ParamByName[CT_PARAM_AutomaticMineWhenConnectedToNodes].SetAsBoolean(cbAutomaticMiningWhenConnectedToNodes.Checked );
+  AppParams.ParamByName[CT_PARAM_SaveLogFiles].SetAsBoolean(cbSaveLogFiles.Checked );
+  AppParams.ParamByName[CT_PARAM_ShowLogs].SetAsBoolean(cbShowLogs.Checked );
+  AppParams.ParamByName[CT_PARAM_MinerName].SetAsString(ebMinerName.Text);
+  AppParams.ParamByName[CT_PARAM_ShowModalMessages].SetAsBoolean(cbShowModalMessages.Checked);
+end;
+
+procedure TFRMPascalCoinWalletConfig.bbUpdatePasswordClick(Sender: TObject);
+Var s,s2 : String;
+begin
+  if Not Assigned(FWalletKeys) then exit;
+  if Not FWalletKeys.IsValidPassword then begin
+    s := '';
+    Repeat
+      if Not InputQuery('Wallet Password','Insert Wallet Password',s) then exit;
+      FWalletKeys.WalletPassword := s;
+      if Not FWalletKeys.IsValidPassword then Application.MessageBox(PChar('Invalid password'),PChar(Application.Title),MB_ICONERROR+MB_OK);
+    Until FWalletKeys.IsValidPassword;
+  end;
+  if FWalletKeys.IsValidPassword then begin
+    s := ''; s2 := '';
+    if Not InputQuery('Change password','Type new password',s) then exit;
+    if trim(s)<>s then raise Exception.Create('Password cannot start or end with a space character');
+    if Not InputQuery('Change password','Type new password again',s2) then exit;
+    if s<>s2 then raise Exception.Create('Two passwords are different!');
+
+    FWalletKeys.WalletPassword := s;
+    Application.MessageBox(PChar('Password changed!'+#10+#10+
+      'Please note that your new password is "'+s+'"'+#10+#10+
+      '(If you lose this password, you will lose your Wallet forever !)'),
+      PChar(Application.Title),MB_ICONWARNING+MB_OK);
+  end;
+  UpdateWalletConfig;
+end;
+
+procedure TFRMPascalCoinWalletConfig.FormCreate(Sender: TObject);
+begin
+  lblDefaultInternetServerPort.Caption := Format('(Default %d)',[CT_NetServer_Port]);
+  udInternetServerPort.Position := CT_NetServer_Port;
+  udDefaultFee.Position := 0;
+  ebDefaultFee.Text := TAccountComp.FormatMoney(0);
+  ebMinerName.Text := '';
+  bbUpdatePassword.Enabled := false;
+  UpdateWalletConfig;
+end;
+
+procedure TFRMPascalCoinWalletConfig.SetAppParams(const Value: TAppParams);
+begin
+  FAppParams := Value;
+  if Not Assigned(Value) then exit;
+  udInternetServerPort.Position := AppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+  udDefaultFee.Position := AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+  ebDefaultFee.Text := TAccountComp.FormatMoney(udDefaultFee.Position);
+  cbAutomaticMiningWhenConnectedToNodes.Checked := AppParams.ParamByName[CT_PARAM_AutomaticMineWhenConnectedToNodes].GetAsBoolean(true);
+  cbGenerateANewPrivateKeyForEachNewGeneratedBlock.Checked := AppParams.ParamByName[CT_PARAM_NewPrivateKeyForEachGeneratedBlock].GetAsBoolean(false);
+  cbSaveLogFiles.Checked := AppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false);
+  cbShowLogs.Checked := AppParams.ParamByName[CT_PARAM_ShowLogs].GetAsBoolean(false);
+  ebMinerName.Text := AppParams.ParamByName[CT_PARAM_MinerName].GetAsString('');
+  cbShowModalMessages.Checked := AppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false);
+end;
+
+procedure TFRMPascalCoinWalletConfig.SetWalletKeys(const Value: TWalletKeys);
+begin
+  FWalletKeys := Value;
+  UpdateWalletConfig;
+end;
+
+procedure TFRMPascalCoinWalletConfig.udDefaultFeeChangingEx(Sender: TObject;
+  var AllowChange: Boolean; NewValue: SmallInt; Direction: TUpDownDirection);
+begin
+  AllowChange := true;
+  ebDefaultFee.Text := TAccountComp.FormatMoney(NewValue);
+end;
+
+procedure TFRMPascalCoinWalletConfig.UpdateWalletConfig;
+begin
+  if Assigned(FWalletKeys) then begin
+    if FWalletKeys.IsValidPassword then begin
+      if FWalletKeys.WalletPassword='' then begin
+        bbUpdatePassword.Caption := 'Wallet without password, protect it!';
+      end else begin
+        bbUpdatePassword.Caption := 'Change Wallet password';
+      end;
+    end else begin
+        bbUpdatePassword.Caption := 'Wallet with password, change it!';
+    end;
+  end else bbUpdatePassword.Caption := '(Wallet password)';
+  bbUpdatePassword.Enabled := Assigned(FWAlletKeys);
+end;
+
+end.

+ 265 - 0
Units/Forms/UFRMPayloadDecoder.dfm

@@ -0,0 +1,265 @@
+object FRMPayloadDecoder: TFRMPayloadDecoder
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsSingle
+  Caption = 'Payload Decoder'
+  ClientHeight = 360
+  ClientWidth = 569
+  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 Label1: TLabel
+    Left = 20
+    Top = 20
+    Width = 28
+    Height = 13
+    Caption = 'Block:'
+  end
+  object lblBlock: TLabel
+    Left = 54
+    Top = 15
+    Width = 30
+    Height = 19
+    Caption = '000'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object lblDateTime: TLabel
+    Left = 169
+    Top = 15
+    Width = 30
+    Height = 19
+    Caption = '000'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object Label6: TLabel
+    Left = 111
+    Top = 20
+    Width = 52
+    Height = 13
+    Caption = 'Date Time:'
+  end
+  object Label2: TLabel
+    Left = 20
+    Top = 45
+    Width = 52
+    Height = 13
+    Caption = 'Operation:'
+  end
+  object lblOperationTxt: TLabel
+    Left = 90
+    Top = 40
+    Width = 457
+    Height = 39
+    AutoSize = False
+    Caption = '000'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+    WordWrap = True
+  end
+  object PageControl: TPageControl
+    Left = 20
+    Top = 90
+    Width = 521
+    Height = 206
+    ActivePage = tsDecoded
+    TabOrder = 0
+    OnChanging = PageControlChanging
+    object tsDecoded: TTabSheet
+      Caption = 'Payload'
+      object Label7: TLabel
+        Left = 15
+        Top = 94
+        Width = 87
+        Height = 13
+        Caption = 'Decoded Payload:'
+      end
+      object lblDecodedMethod: TLabel
+        Left = 457
+        Top = 94
+        Width = 44
+        Height = 13
+        Alignment = taRightJustify
+        Caption = '(Method)'
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clGray
+        Font.Height = -11
+        Font.Name = 'Tahoma'
+        Font.Style = []
+        ParentFont = False
+      end
+      object Label3: TLabel
+        Left = 15
+        Top = 9
+        Width = 155
+        Height = 13
+        Caption = 'Original Payload in Hexadecimal:'
+      end
+      object memoDecoded: TMemo
+        Left = 15
+        Top = 111
+        Width = 486
+        Height = 55
+        TabStop = False
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clBlack
+        Font.Height = -13
+        Font.Name = 'Tahoma'
+        Font.Style = [fsBold]
+        Lines.Strings = (
+          'memoDecoded')
+        ParentFont = False
+        ReadOnly = True
+        TabOrder = 0
+        OnKeyDown = memoDecodedKeyDown
+      end
+      object memoOriginalPayloadInHexa: TMemo
+        Left = 15
+        Top = 28
+        Width = 486
+        Height = 55
+        TabStop = False
+        Color = clBtnFace
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clBlack
+        Font.Height = -13
+        Font.Name = 'Tahoma'
+        Font.Style = [fsBold]
+        Lines.Strings = (
+          'memoDecoded')
+        ParentFont = False
+        ReadOnly = True
+        TabOrder = 1
+        OnKeyDown = memoDecodedKeyDown
+      end
+    end
+    object tsDecodeMethods: TTabSheet
+      Caption = 'Decode methods'
+      ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object lblPasswordsInfo: TLabel
+        Left = 235
+        Top = 162
+        Width = 81
+        Height = 13
+        Caption = 'lblPasswordsInfo'
+      end
+      object cbMethodPublicPayload: TCheckBox
+        Left = 16
+        Top = 15
+        Width = 225
+        Height = 17
+        Caption = 'Not encrypted (Public payload)'
+        TabOrder = 0
+        OnClick = cbMethodPublicPayloadClick
+      end
+      object cbUsingPrivateKeys: TCheckBox
+        Left = 16
+        Top = 38
+        Width = 225
+        Height = 17
+        Caption = 'Using Private keys of my Wallet'
+        TabOrder = 1
+        OnClick = cbMethodPublicPayloadClick
+      end
+      object cbUsingPasswords: TCheckBox
+        Left = 16
+        Top = 61
+        Width = 225
+        Height = 17
+        Caption = 'Using Passwords of the list'
+        TabOrder = 2
+        OnClick = cbMethodPublicPayloadClick
+      end
+      object memoPasswords: TMemo
+        Left = 235
+        Top = 10
+        Width = 261
+        Height = 146
+        ScrollBars = ssBoth
+        TabOrder = 3
+        OnChange = cbMethodPublicPayloadClick
+      end
+      object bbSaveMethods: TBitBtn
+        Left = 60
+        Top = 110
+        Width = 75
+        Height = 25
+        Caption = 'Save'
+        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 = 4
+        OnClick = bbSaveMethodsClick
+      end
+    end
+  end
+  object BitBtn1: TBitBtn
+    Left = 462
+    Top = 310
+    Width = 79
+    Height = 25
+    Cancel = True
+    Caption = 'Close'
+    DoubleBuffered = True
+    Glyph.Data = {
+      76010000424D7601000000000000760000002800000020000000100000000100
+      04000000000000010000120B0000120B00001000000000000000000000000000
+      800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
+      FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00555555555555
+      555555555555555555555555555555555555555555FF55555555555559055555
+      55555555577FF5555555555599905555555555557777F5555555555599905555
+      555555557777FF5555555559999905555555555777777F555555559999990555
+      5555557777777FF5555557990599905555555777757777F55555790555599055
+      55557775555777FF5555555555599905555555555557777F5555555555559905
+      555555555555777FF5555555555559905555555555555777FF55555555555579
+      05555555555555777FF5555555555557905555555555555777FF555555555555
+      5990555555555555577755555555555555555555555555555555}
+    ModalResult = 2
+    NumGlyphs = 2
+    ParentDoubleBuffered = False
+    TabOrder = 1
+  end
+end

+ 237 - 0
Units/Forms/UFRMPayloadDecoder.pas

@@ -0,0 +1,237 @@
+unit UFRMPayloadDecoder;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UBlockChain, UCrypto, UWalletKeys, Buttons, ComCtrls,
+  UAppParams;
+
+type
+  TFRMPayloadDecoder = class(TForm)
+    Label1: TLabel;
+    lblBlock: TLabel;
+    lblDateTime: TLabel;
+    Label6: TLabel;
+    Label2: TLabel;
+    lblOperationTxt: TLabel;
+    PageControl: TPageControl;
+    tsDecoded: TTabSheet;
+    tsDecodeMethods: TTabSheet;
+    cbMethodPublicPayload: TCheckBox;
+    cbUsingPrivateKeys: TCheckBox;
+    cbUsingPasswords: TCheckBox;
+    memoPasswords: TMemo;
+    Label7: TLabel;
+    lblDecodedMethod: TLabel;
+    Label3: TLabel;
+    bbSaveMethods: TBitBtn;
+    BitBtn1: TBitBtn;
+    memoDecoded: TMemo;
+    memoOriginalPayloadInHexa: TMemo;
+    lblPasswordsInfo: TLabel;
+    procedure FormCreate(Sender: TObject);
+    procedure PageControlChanging(Sender: TObject; var AllowChange: Boolean);
+    procedure cbMethodPublicPayloadClick(Sender: TObject);
+    procedure bbSaveMethodsClick(Sender: TObject);
+    procedure memoDecodedKeyDown(Sender: TObject; var Key: Word;
+      Shift: TShiftState);
+  private
+    FPayloadData : TRawBytes;
+    FWalletKeys : TWalletKeys;
+    FOldECPrivateKey : TECPrivateKey;
+    FAccountECPrivateKey : TECPrivateKey;
+    FSavedDecodeMethods : boolean;
+    FAppParams : TAppParams;
+    { Private declarations }
+    Procedure TryToDecode;
+    Procedure SaveMethods;
+  public
+    { Public declarations }
+    Procedure Init(block, timestamp : Cardinal; const OperationText : AnsiString; Const PayloadData : TRawBytes; WalletKeys : TWalletKeys; AppParams : TAppParams);
+  end;
+
+implementation
+
+{$R *.dfm}
+
+Uses UNode, UTime, UECIES, UAES, UAccounts;
+
+{ TFRMPayloadDecoder }
+
+procedure TFRMPayloadDecoder.bbSaveMethodsClick(Sender: TObject);
+begin
+  SaveMethods;
+  PageControl.ActivePage := tsDecoded;
+  TryToDecode;
+end;
+
+procedure TFRMPayloadDecoder.cbMethodPublicPayloadClick(Sender: TObject);
+begin
+  FSavedDecodeMethods := false;
+  lblPasswordsInfo.Caption := Format('Possible passwords: %d',[memoPasswords.Lines.Count]);
+end;
+
+procedure TFRMPayloadDecoder.FormCreate(Sender: TObject);
+begin
+  FWalletKeys := Nil;
+  FAppParams := Nil;
+  memoDecoded.Lines.Clear;
+  memoOriginalPayloadInHexa.Lines.Clear;
+  lblPasswordsInfo.Caption := '';
+end;
+
+procedure TFRMPayloadDecoder.Init(block, timestamp : Cardinal; const OperationText : AnsiString; Const PayloadData : TRawBytes; WalletKeys : TWalletKeys; AppParams : TAppParams);
+begin
+  FWalletKeys := WalletKeys;
+  FAppParams := AppParams;
+  lblBlock.Caption := inttostr(block);
+  if timestamp>10000 then begin
+    lblDateTime.Caption := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime(timestamp)));
+    lblDateTime.Font.Color := clBlack;
+  end else begin
+    lblDateTime.Caption := '(Pending block)';
+    lblDateTime.Font.Color := clRed;
+  end;
+  lblOperationTxt.Caption := OperationText;
+  FPayloadData := PayloadData;
+  memoOriginalPayloadInHexa.Lines.Text := TCrypto.ToHexaString(FPayloadData);
+  if Assigned(FWalletKeys) then begin
+    cbMethodPublicPayload.Checked := FAppParams.ParamByName['PayloadDecoder.notencrypted'].GetAsBoolean(true);
+    cbUsingPrivateKeys.Checked := FAppParams.ParamByName['PayloadDecoder.usingprivatekeys'].GetAsBoolean(true);
+    cbUsingPasswords.Checked := FAppParams.ParamByName['PayloadDecoder.usingpasswords'].GetAsBoolean(true);
+    memoPasswords.Lines.Text := FAppParams.ParamByName['PayloadDecoder.passwords'].GetAsString('');
+  end else begin
+    cbMethodPublicPayload.Checked := true;
+    cbUsingPrivateKeys.Checked := true;
+    cbUsingPasswords.Checked := true;
+    memoPasswords.Lines.Text := '';
+  end;
+  FSavedDecodeMethods := true;
+  PageControl.ActivePage := tsDecoded;
+  TryToDecode;
+end;
+
+procedure TFRMPayloadDecoder.memoDecodedKeyDown(Sender: TObject; var Key: Word;
+  Shift: TShiftState);
+begin
+  if key=VK_ESCAPE then Close;
+
+end;
+
+procedure TFRMPayloadDecoder.PageControlChanging(Sender: TObject; var AllowChange: Boolean);
+begin
+  //
+  if PageControl.ActivePage=tsDecodeMethods then begin
+    If not FSavedDecodeMethods then begin
+      case Application.MessageBox(PChar('Save new decode methods?'),PChar(Application.Title),MB_YESNOCANCEL+MB_ICONQUESTION) of
+        IDYES : Begin
+          SaveMethods;
+        End;
+        IDCANCEL : begin
+          AllowChange := false;
+        end;
+      end;
+
+    end;
+  end else begin
+    FSavedDecodeMethods := true;
+  end;
+end;
+
+procedure TFRMPayloadDecoder.SaveMethods;
+begin
+  FAppParams.ParamByName['PayloadDecoder.notencrypted'].SetAsBoolean(cbMethodPublicPayload.Checked);
+  FAppParams.ParamByName['PayloadDecoder.usingprivatekeys'].SetAsBoolean(cbUsingPrivateKeys.Checked);
+  FAppParams.ParamByName['PayloadDecoder.usingpasswords'].SetAsBoolean(cbUsingPasswords.Checked);
+  FAppParams.ParamByName['PayloadDecoder.passwords'].SetAsString(memoPasswords.Lines.Text);
+  FSavedDecodeMethods := true;
+end;
+
+procedure TFRMPayloadDecoder.TryToDecode;
+  Function UseWallet(Const raw : TRawBytes; var Decrypted : AnsiString; var WalletKey : TWalletKey) : Boolean;
+  Var i : Integer;
+  begin
+    Result := false;
+    if Not assigned(FWalletKeys) then exit;
+
+    for i := 0 to FWalletKeys.Count - 1 do begin
+      WalletKey := FWalletKeys.Key[i];
+      If Assigned(WalletKey.PrivateKey) then begin
+        If ECIESDecrypt(WalletKey.PrivateKey.EC_OpenSSL_NID,WalletKey.PrivateKey.PrivateKey,false,raw,Decrypted) then begin
+          Result := true;
+          exit;
+        end;
+      end;
+    end;
+
+  end;
+
+  Function  UsePassword(const raw : TRawBytes; var Decrypted,PasswordUsed : AnsiString) : Boolean;
+  Var i : Integer;
+  Begin
+    Result := false;
+    for i := 0 to memoPasswords.Lines.Count - 1 do begin
+      if (TAESComp.EVP_Decrypt_AES256(raw,memoPasswords.Lines[i],Decrypted)) then begin
+        if (TCrypto.IsHumanReadable(Decrypted)) then begin
+          Result := true;
+          PasswordUsed := memoPasswords.Lines[i];
+          exit;
+        end;
+      end;
+    end;
+  End;
+
+
+Var raw : TRawBytes;
+  WalletKey : TWalletKey;
+  Decrypted,PasswordUsed : AnsiString;
+  ok : boolean;
+begin
+  ok := true;
+    raw := FPayloadData;
+    if raw<>'' then begin
+      // First try to a human readable...
+      if (cbMethodPublicPayload.Checked) and (TCrypto.IsHumanReadable(raw)) then begin
+        memoDecoded.Lines.Text := raw;
+        lblDecodedMethod.Caption := 'Not encrypted payload';
+      end else if (cbUsingPrivateKeys.Checked) And (UseWallet(raw,Decrypted,WalletKey)) then begin
+        memoDecoded.Lines.Text := Decrypted;
+        lblDecodedMethod.Caption := 'Encrypted with EC '+TAccountComp.GetECInfoTxt(WalletKey.PrivateKey.EC_OpenSSL_NID);
+      end else if (cbUsingPasswords.Checked) And (UsePassword(raw,Decrypted,PasswordUsed)) then begin
+        memoDecoded.Lines.Text := Decrypted;
+        lblDecodedMethod.Caption := 'Encrypted with Pwd:"'+PasswordUsed+'"';
+      end else begin
+        memoDecoded.Lines.Text := 'CANNOT DECRYPT';
+        lblDecodedMethod.Caption := '';
+        ok := false;
+      end;
+      if ok then begin
+        memoDecoded.Font.Color := clBlack;
+        memoDecoded.Color := clWhite;
+      end else begin
+        memoDecoded.Font.Color := clRed;
+        memoDecoded.Color := clBtnFace;
+      end;
+    end else begin
+      memoDecoded.Lines.Text := '(No payload)';
+      memoDecoded.Font.Color := clDkGray;
+      memoDecoded.Color := clLtGray;
+      lblDecodedMethod.Caption := '';
+    end;
+end;
+
+end.

+ 1316 - 0
Units/Forms/UFRMWallet.dfm

@@ -0,0 +1,1316 @@
+object FRMWallet: TFRMWallet
+  Left = 360
+  Top = 328
+  Caption = 'Pascal Coin Wallet, Miner & Explorer'
+  ClientHeight = 558
+  ClientWidth = 899
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  Menu = MainMenu
+  OldCreateOrder = False
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  PixelsPerInch = 96
+  TextHeight = 13
+  object pnlTop: TPanel
+    Left = 0
+    Top = 0
+    Width = 899
+    Height = 91
+    Align = alTop
+    BevelOuter = bvNone
+    TabOrder = 0
+    object Image1: TImage
+      Left = 15
+      Top = 15
+      Width = 62
+      Height = 62
+      AutoSize = True
+      Picture.Data = {
+        0954506E67496D61676589504E470D0A1A0A0000000D494844520000003E0000
+        003E080600000073C1A8440000000473424954080808087C0864880000000970
+        48597300000B1200000B1201D2DD7EFC0000001C74455874536F667477617265
+        0041646F62652046697265776F726B7320435336E8BCB28C0000001674455874
+        4372656174696F6E2054696D650030362F30372F313610A27E9700000EDA4944
+        415478DACD9B095813D716804F4280A860E1A9A0A295A556AD0B88F6A9CF85A4
+        6E6D5DC08A05956A5050BFAA155EAD56AB125CD0BA22D65A5C58DCAAA012A5D5
+        F75C4AA2A2A8AD605D9E5A41A08A80A2AC822CC93B77320943329384B53DDF77
+        BE994C6692FBDF73EFB9E79E7B87A752A9A0B984C7E3F5C3C367A843511D506D
+        512D51F9B4AA68AD462D43CD45CD404D413D8165BBDF6C656B6A7084F5C2C322
+        54F7364241CEE01EF679A2BE9DCC5C9DDA594FF8673757AEE78ACA2A8B2FDE7D
+        F6F876E6CB925F7ECFA9C173C7AA6A652BFCEA1AEA0E2CE7B9BF1D38C2BE8B87
+        25A8939CECAD1FCE1ADDA37AE1F83EFDDE6A63D1B631BF9B915BFCF4C8C5F4C7
+        61F1A9EDCA2AAA49251C42DD8D65CEFE4BC111B8271E969BF179C3BD873A6786
+        CD78BFBB73C7B60E8DAE498E4AF05E7F3E2335E3C5DBF8F1206A2496FDCF1605
+        4760D257BF429DE733DCE556E4FCE1EE8DB56E032AC0053F8662F977B7083842
+        937E7AB47D5BE1EB6B5BBCEC9ACBC2C6E4DA83FC8713D7FCB72CBFA8FC117E9C
+        8F1CCF9B0D1CA1A760B35E279D36206F858FFBB0A68649CB2880C2B237E0686F
+        0D8E76D6D4B5CCFC12C8CC2B019B3696E0E6DC4EEF199F6FCFCBE32E6774C1D3
+        79C872A149C11158888725D6ADCCA79C5B33CE62500FBB778D3C411E621C19A2
+        52A26A46B1BA225A96088A3BCF2064EA00C0CAA5AE490FFF06A13FFE061E7D3A
+        817CFD04D67F4BBC9E756BD2BAB376354AD56AE4F9A129C137E1D034EE698C9F
+        03675F26803C335AC9399F055CA586D6C0AB6AE87325F5AD8D6F0C19D620E19B
+        31E035D891B332D88434FD512B7EAE29ADA88A3005DE203802932164AB95D07C
+        C49398E95D58A109209FC00AF0C8A7C1F9B4D2566705A75559435540614919D8
+        FA445377A56E9FAC6DD68EB30F43567E294407894032D2704323B14057FF43D9
+        25E555C7F16318B25536147C395ADA8FD3D27C735401AD66740530C0752DAE69
+        E25AF01A2DB83C3513C44B8FAB6F4B9C535B86096AA79D14361E447D3B1B3324
+        05DF4572E8095A7E23B2C5D61B9C38B2B6AD2D9665474D73D183A6AC8CD06602
+        063C0DCD8097AC8B87CCDC57750C5E7BA2A23FABCF9F3C2F86F49C42B06A650E
+        035C3A50D70AB1D9DF7A5C403D419A3A11D212886A9C1F9B90663F74C94901F6
+        F9395C0E8F151CA1BBA3F73E9BBCD1B352CF911168330B54731D8BD3AAB13836
+        7BDEE060A3166AA860080C419E7D399B3F717838DC91DA7145C652A3E0084DAC
+        7B7EC1F8DEAF77CC1DEAC10ECD04D785575B5C7EF31188E76D6B36708D74B3B3
+        02D937630D0D7585783A0D39CB8D814BEDDE6A3531EFE067FD75BE4058CB5A68
+        72A49A3B0B3C82C7245E01FF907DCD0EAE112E1F60EF772015839CEF90338A13
+        9C9E469E4DDFE35BAD179151C0960C8B6B2A80CDF26620DD9500A1BB8EB71838
+        FA21488B98ACD7F7497F1FBC5846203D90358F0B3C0E63EF0E47968C14D5799A
+        C0106801073C5F73ACB5B8482205C58DBB2D064EC4739023C8568CD1BBEEBEE8
+        C4258CED15C8BA520F1CA19DD1A15D2C383CD3BAAE17C7262EB0A4C12D6B2D2F
+        A0C1F9CC0AD0787933B01938158A4ACAD8AD63DD06DC7A3A82665C7F925700E9
+        7FE683556B4B18D0A32B5E57E2B542F4F22FEB0DFF78DF543DAB93898D4BE011
+        2C1CE5E8F274C1F7A1B59D59AD4D010B6BC105421DAB5BD4E9F38525E560EB3E
+        99B3701E83FA81FCE006E4AB06A8A982A0B0BDB0FDE019F014B9816C63205E7B
+        833EE22AF887C543D70E6D21E27331849FB8018ADB3946C1174DEC03E181FF62
+        B3FA65B4FA05E495EA821763DF2ED6EBDB4C68CD3905AFDB0A6AC1E5D7EF82D8
+        771167E142822420FDC28F06AF0491DFD7D82DEE41C83C2F90067C44814B2313
+        2134EA1C78B87603F9661F80EA0A08DA9504DB4FDD3608CE15D3D356E7236F67
+        2D38427FEA646F1D9CB177EAE03A7793E14BA08116D2A04206B04EF3A79B7C78
+        D409080E0DE72C5CF4D69520998C7D51594581DBF4FF04BBC56B48080F06AF11
+        7D2870D19C2DA0484D87108908A47E43287072DDD17F3F15C272097172854724
+        ACDF594D89FA5F5945F56C64BEAA01BFB2C66F608DDE54935890095EC7FA5C15
+        6001927FAF85D8B89F380B97747C3788B0B993665EF8AA006CDDBCA8EBA9F11B
+        C0AD7B478444C0F1CB202BF725447FFD094846F7C27B2BA8EBD20357A9D99A21
+        6186BC4C597BF4E6E595077FCD42663F124863C40125584B557AA1A906888216
+        D6B5BA80A302105CE43D1714577EE52E58FE1DCAD204449E7C5DDB2D5477E3B5
+        96E5B907A82B697B008ECF9DA86BE43BD9E5FB8053D00681D3CD9D4CBC3A10F0
+        81381139501A3FABA7DE9D4C6026AC990EB80E3CAF236732155CFBF48234B98C
+        067903E1BB0F60B78800D79E4E90766233755D9E9206E2C08D6A884B61D47D54
+        85A0122717BCE70AE7EF1B9AB713B198B437BBAA5AF90101DF32D2D561C0F9B5
+        E374C2535E2DA0B990012BD46F058C9690F9EC05380D18CBF9C79EE3C682ECC0
+        F76A186CBE412BBE85EDFB8E82E7A82120DBB184828B49B800FEABF642B78EB6
+        90796CA9169AA864D31988BDF080F3F76762EC1E8353582EC139BBE2C2ADA767
+        08F8953D0B47F003C6F41C54179CE9D85AB1589B5D65679361D2CC059C7F1CB2
+        EC4B902E594083A0139BE40F8A945408412F2FFDDC9BBA2EFDEE0884FE20038F
+        FEEF807C4780F65E72B499BC934A567089B1793BDDCF9F13F0CC532BC716EA25
+        FBB5E02CCDDCBC556D5F679EA34AB7EE85D04D3B39FF38E1C768F0FA48ACB5A0
+        4DF761E8D14B2161D72AF012BBAB2B636608287EBB0F21011F825442EE2D5777
+        8BF82B101CA90043C216C030859EB59911F0227406EC99953A0E8CADC9EBB708
+        AF598BE1E4995FB80B76E71A383AD851D0852F9F836D8F11D4F5D4C49DE8D11D
+        A8EB8EA3E743D6B302885E390D2463FB51D7D21E6441FFB9D106A1B94256A690
+        44858D6F4C05017F83E016FAB7F0580085463DBCDBE8E970EB2E771F54156523
+        4825D5BFE517AF80D85BED81557F9CD636675EDFA9D4B5A49DF341D4AF0BC814
+        6920592FC342BF3108656A96863761773101AF41703EEB1D7A7D9A0BBE7658E3
+        7519C4F9871E4307833CF1907A2843EF1DFE432C044BB7816B2F67483B194141
+        CBAFA68278F63AEAFEE8155321E6A71450A43D360A638AB519E095045C89E03C
+        D63B3471791DEBB2043334BCFCFA3D10FB2C30E9CF9B524832222DC21B6CDA58
+        98743F82D71806D74C5074C1758035814B8C2C09FC176F68516812A2CAC326B0
+        66608C815721B880FD0E1E7B88AA999D69A1D5E0D4301471E06F0D4D835711F0
+        72041772DE556726A681B7A803AC51D1CC55A0B87EDBF412344248B291F46943
+        439701F022029E8771BA907B85C48CB559D7998ED253529B21FED42CAB398558
+        3968625F83AB2A26803F25E0773080A936B45BA16EB3B6D09B869263615915D8
+        0E9BDB6CC0C4814946F6A052CAA63A3136A103186A7DE7F4BACFDEB75EFE697F
+        EED54F6D5AD992C5D202EA28BF9901E2391BB90BDEB9833A70A117121E653F83
+        A7F9AFA0BD8D15F4762663AF12725F14C383EC7C105A0860502F07ECBBEDB129
+        5B81A8B71DB839D9364905EE3B77FF5A40C4452A645D8593940FF42629BA4203
+        EAA79BD479B6F0B84B10BCF518E7E3D16BE78164E270EDF29168761886A50F20
+        24701C4803C6527373E9DE33101A755E9D75D9E24B073A95EA844513093D49F9
+        9980BB9B0BF8272A1302BA197D4A9B5165E6D6D5E0927571107BFA06E7A3497B
+        968068400FCAB2A054828D28088A4ACB21E1DB59E035AC179586122D8CC46025
+        1342660C05E9F44174A05369B458F511EB29D1F74A2BAABC0838F1E839E97B7C
+        5F9BB4BBA14E3A5993571780E88B282C7406E763AA1BDF6B170D0B8BCBC076D4
+        52EA7A6AF4227073B1A3ACEAE8B30DB2F28A20FACB31EA19564D15B0ADA33754
+        E838FD8D4AA5B2D3A49E4EAEF11BF80F937739305750CCD4B9749E780DE7EDAE
+        DD3B43DAFEC55A70F9CD3F40BC20525D218AD5EAA4232A6F6418752D698317F6
+        6BFB2685261216977AF99B0337F29179B206FCE33642C1E6D2F859BD4CFE15ED
+        AA8939643E2F0527BF48CE5B3D87BD07B2F57E5AF0F0B86408FEEE0CB8BAD843
+        DAEED9D452B11CE371F15771EACAE0481D35565C028FA464E416AF41E6D31A70
+        12B991E65E59AFCD3CF4A600594A164C5ACD9D5C0C99E981F36A0FD02C20643E
+        7B0599B985382C99ABA32EA57A6340DAA33CB5E3336186555FA1F36DA4EFF446
+        E6D7CCBCFAB2FECEED3FBEB9FD937A6FEA91FE7813420F73271713A493C06B68
+        77F507ED36107A6380767380B2C96199E2BBF182FCE8A5F464E45D41F132C049
+        EC978D562FABEF162EAFB567E1E4B54CCEEF1FC748C0D19E0E0C995B4140B311
+        A8798538B576D362F36B94AA919A5D91BA8B869BD0EA83EB6B75B72F8E6B772E
+        B0892A914474CD0FC825B4B5159AE5233670B262A748D9EC55657C4B57AD68F6
+        A9B089B1746F730BDDB7C98EE9BEC89ACB0A4EC3CFB07BAB5590DEC6000E91DF
+        CE01F1726EC7662CDDDBDC426F0CD8AEBB11880DBC351E623F1DE6DCFEE8D251
+        464B1C73E121F887CB39BFDF1630849A58FC15428FDBA5C8F891EE775C9B7FC8
+        8C80CCDA9E1B9CB541EDCE432E313501D8D44276420C5B7AAAB4BA463915191F
+        9A044EC30F31E3F36293377AAA0CF577CDCE432E797544D2A869644384B1D76D
+        19F29D62E533B2C1CFDB4A68BEFA49CC74CEAD9C9A6D986C6268C9B639A1BBFA
+        1FCA2A29AFDA8F1FB7215F4DBDC169F8D508EFCD064F36E0D9223897B4B44767
+        6CE9DC696C3FABA99B78E7B56D6D31F7ECEA8F5B339B3D014FCB78C1F91C73FB
+        754B4063F37E8ACD3B8E395E370A9C860FE4F37861B215639E1A73782D2DC491
+        8D0D395D82F0648D698FA1CDBB0D0127AB2D235137FB0C7779A9B749E82F9285
+        91C98A5DA7EFBD83E16820B29C31F5B986BC9AE18C879DD8EFDF3EBF769CA03E
+        115E530A89C8862C3E998FC109D9BEE5871C05F579BEC16F21D1EF97EDC2D8FE
+        D1B165A39C5AEADD14D297E7EEBC7413636F27FC48B29B0791A1B8DEE56FE4EB
+        5724D0218B658BB002EE61053837E7EB57CBF7DFF8E358728623366B92D58C68
+        F1D7AF582A80E48966A0CEC62EA05C36C5ADC077844BA35B01B1EE8E9FEEFC1E
+        75EE81E0715E0999D0EFA1819F35E6779B0C5CA712C812C77C54B185805F35BC
+        77A79CF7DEB685D16E0E36237A7772E20A84745FB14C799067575651DD11BF22
+        A9DBCD7FCB572C0D540249597F08EA976A891324413B19D8490C4BD25DE4CFC9
+        D04356FCC9AE3DE2A8D2519350FF839A83E56B96D4CCFF0184B915F49D084B6C
+        0000000049454E44AE426082}
+    end
+    object lblCurrentBlockCaption: TLabel
+      Left = 90
+      Top = 11
+      Width = 60
+      Height = 13
+      Caption = 'Total Blocks:'
+    end
+    object lblCurrentBlock: TLabel
+      Left = 156
+      Top = 11
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object lblCurrentBlockTimeCaption: TLabel
+      Left = 90
+      Top = 26
+      Width = 89
+      Height = 13
+      Caption = 'Current Block age:'
+    end
+    object lblCurrentBlockTime: TLabel
+      Left = 188
+      Top = 26
+      Width = 81
+      Height = 13
+      Caption = '000 seconds ago'
+    end
+    object lblOperationsPendingCaption: TLabel
+      Left = 90
+      Top = 41
+      Width = 98
+      Height = 13
+      Caption = 'Pending Operations:'
+    end
+    object lblOperationsPending: TLabel
+      Left = 194
+      Top = 41
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object lblMiningStatusCaption: TLabel
+      Left = 90
+      Top = 56
+      Width = 67
+      Height = 13
+      Caption = 'Mining status:'
+    end
+    object lblMiningStatus: TLabel
+      Left = 163
+      Top = 56
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object lblCurrentDifficultyCaption: TLabel
+      Left = 429
+      Top = 11
+      Width = 76
+      Height = 13
+      Caption = 'Current Target:'
+    end
+    object lblCurrentDifficulty: TLabel
+      Left = 511
+      Top = 11
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object lblTimeAverage: TLabel
+      Left = 360
+      Top = 26
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object Label4: TLabel
+      Left = 285
+      Top = 26
+      Width = 69
+      Height = 13
+      Caption = 'Time average:'
+    end
+    object Label8: TLabel
+      Left = 90
+      Top = 70
+      Width = 63
+      Height = 13
+      Caption = 'Node Status:'
+    end
+    object lblNodeStatus: TLabel
+      Left = 163
+      Top = 70
+      Width = 15
+      Height = 13
+      Caption = '???'
+    end
+    object Label5: TLabel
+      Left = 285
+      Top = 11
+      Width = 48
+      Height = 13
+      Caption = 'Accounts:'
+    end
+    object lblCurrentAccounts: TLabel
+      Left = 337
+      Top = 11
+      Width = 18
+      Height = 13
+      Caption = '000'
+    end
+    object lblTimeAverageAux: TLabel
+      Left = 360
+      Top = 41
+      Width = 18
+      Height = 13
+      Caption = '000'
+      Font.Charset = DEFAULT_CHARSET
+      Font.Color = clGray
+      Font.Height = -11
+      Font.Name = 'Tahoma'
+      Font.Style = []
+      ParentFont = False
+    end
+    object cbAllowMining: TCheckBox
+      Left = 620
+      Top = 10
+      Width = 97
+      Height = 17
+      Caption = 'Allow Mining'
+      TabOrder = 0
+      OnClick = cbAllowMiningClick
+    end
+  end
+  object StatusBar: TStatusBar
+    Left = 0
+    Top = 539
+    Width = 899
+    Height = 19
+    Panels = <
+      item
+        Alignment = taCenter
+        Text = 'Server Active'
+        Width = 130
+      end
+      item
+        Text = 'Connection status'
+        Width = 430
+      end
+      item
+        Text = 'Blocks'
+        Width = 50
+      end>
+  end
+  object PageControl: TPageControl
+    Left = 0
+    Top = 91
+    Width = 899
+    Height = 448
+    ActivePage = tsMessages
+    Align = alClient
+    TabOrder = 2
+    OnChange = PageControlChange
+    object tsAccountsExplorer: TTabSheet
+      Caption = 'Accounts Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object Splitter1: TSplitter
+        Left = 380
+        Top = 41
+        Height = 379
+        ExplicitLeft = 390
+        ExplicitTop = 140
+        ExplicitHeight = 100
+      end
+      object pnlMyAccountsTop: TPanel
+        Left = 0
+        Top = 0
+        Width = 891
+        Height = 41
+        Align = alTop
+        TabOrder = 0
+        object cbMyPrivateKeys: TComboBox
+          Left = 260
+          Top = 7
+          Width = 411
+          Height = 21
+          Style = csDropDownList
+          TabOrder = 0
+          OnChange = cbMyPrivateKeysChange
+        end
+        object cbExploreMyAccounts: TCheckBox
+          Left = 11
+          Top = 10
+          Width = 235
+          Height = 17
+          Caption = 'Explore accounts with one of my Wallet Keys'
+          TabOrder = 1
+          OnClick = cbExploreMyAccountsClick
+        end
+      end
+      object dgAccounts: TDrawGrid
+        Left = 0
+        Top = 41
+        Width = 380
+        Height = 379
+        Align = alLeft
+        TabOrder = 1
+        OnClick = dgAccountsClick
+        OnColumnMoved = dgAccountsColumnMoved
+        OnFixedCellClick = dgAccountsFixedCellClick
+        ColWidths = (
+          64
+          64
+          64
+          64
+          64)
+      end
+      object dgAccountOperations: TDrawGrid
+        Left = 383
+        Top = 41
+        Width = 508
+        Height = 379
+        Align = alClient
+        TabOrder = 2
+        OnDblClick = MiDecodePayloadClick
+      end
+    end
+    object tsPendingOperations: TTabSheet
+      Caption = 'Pending Operations'
+      ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object dgPendingOperations: TDrawGrid
+        Left = 0
+        Top = 86
+        Width = 891
+        Height = 334
+        Align = alClient
+        TabOrder = 0
+        OnDblClick = MiDecodePayloadClick
+      end
+      object pnlPendingOperations: TPanel
+        Left = 0
+        Top = 0
+        Width = 891
+        Height = 86
+        Align = alTop
+        BevelOuter = bvNone
+        BorderWidth = 10
+        TabOrder = 1
+        object Label10: TLabel
+          Left = 10
+          Top = 10
+          Width = 871
+          Height = 66
+          Align = alClient
+          AutoSize = False
+          Caption = 
+            'Here you can see Operations transmited/received from other nodes' +
+            ' that will be included in next block. There is no guarantee that' +
+            ' other nodes will include them when mining, so it'#39's important th' +
+            'at you mine too to help include Operations to the main BlockChai' +
+            'n'
+          Font.Charset = DEFAULT_CHARSET
+          Font.Color = clWindowText
+          Font.Height = -16
+          Font.Name = 'Tahoma'
+          Font.Style = []
+          ParentFont = False
+          WordWrap = True
+          ExplicitLeft = 36
+          ExplicitWidth = 765
+          ExplicitHeight = 41
+        end
+      end
+    end
+    object tsBlockChain: TTabSheet
+      Caption = 'BlockChain Explorer'
+      ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object Panel2: TPanel
+        Left = 0
+        Top = 0
+        Width = 891
+        Height = 41
+        Align = alTop
+        TabOrder = 0
+        object Label9: TLabel
+          Left = 306
+          Top = 10
+          Width = 102
+          Height = 13
+          Caption = 'Filter by blocks range'
+        end
+        object ebBlockChainBlockStart: TEdit
+          Left = 414
+          Top = 7
+          Width = 57
+          Height = 21
+          TabOrder = 0
+          OnExit = ebBlockChainBlockStartExit
+          OnKeyPress = ebBlockChainBlockStartKeyPress
+        end
+        object ebBlockChainBlockEnd: TEdit
+          Left = 474
+          Top = 7
+          Width = 57
+          Height = 21
+          TabOrder = 1
+          OnExit = ebBlockChainBlockStartExit
+          OnKeyPress = ebBlockChainBlockStartKeyPress
+        end
+        object dtpBlockChainDateStart: TDateTimePicker
+          Left = 654
+          Top = 7
+          Width = 101
+          Height = 21
+          Date = 42556.766794687500000000
+          Time = 42556.766794687500000000
+          TabOrder = 2
+          OnChange = ebBlockChainBlockStartExit
+          OnExit = ebBlockChainBlockStartExit
+        end
+        object cbBlockChainFilterByDate: TCheckBox
+          Left = 557
+          Top = 9
+          Width = 91
+          Height = 17
+          Caption = 'Filter by date'
+          TabOrder = 3
+          OnClick = cbBlockChainFilterByDateClick
+        end
+        object dtpBlockChainDateEnd: TDateTimePicker
+          Left = 761
+          Top = 7
+          Width = 101
+          Height = 21
+          Date = 42556.766794687500000000
+          Time = 42556.766794687500000000
+          TabOrder = 4
+          OnChange = ebBlockChainBlockStartExit
+          OnExit = ebBlockChainBlockStartExit
+        end
+      end
+      object dbGridBlockChain: TDBGrid
+        Left = 0
+        Top = 41
+        Width = 891
+        Height = 379
+        Align = alClient
+        Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack]
+        ReadOnly = True
+        TabOrder = 1
+        TitleFont.Charset = DEFAULT_CHARSET
+        TitleFont.Color = clWindowText
+        TitleFont.Height = -11
+        TitleFont.Name = 'Tahoma'
+        TitleFont.Style = []
+      end
+    end
+    object tsOperations: TTabSheet
+      Caption = 'Operations Explorer'
+      ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object Panel1: TPanel
+        Left = 0
+        Top = 0
+        Width = 891
+        Height = 41
+        Align = alTop
+        TabOrder = 0
+        object Label1: TLabel
+          Left = 11
+          Top = 10
+          Width = 80
+          Height = 13
+          Caption = 'Filter by account'
+        end
+        object Label2: TLabel
+          Left = 306
+          Top = 10
+          Width = 102
+          Height = 13
+          Caption = 'Filter by blocks range'
+        end
+        object ebFilterOperationsAccount: TEdit
+          Left = 108
+          Top = 7
+          Width = 173
+          Height = 21
+          TabOrder = 0
+          OnExit = ebFilterOperationsAccountExit
+          OnKeyPress = ebFilterOperationsAccountKeyPress
+        end
+        object ebFilterOperationsStartBlock: TEdit
+          Left = 414
+          Top = 7
+          Width = 57
+          Height = 21
+          TabOrder = 1
+          OnExit = ebFilterOperationsAccountExit
+          OnKeyPress = ebFilterOperationsAccountKeyPress
+        end
+        object ebFilterOperationsEndBlock: TEdit
+          Left = 474
+          Top = 7
+          Width = 57
+          Height = 21
+          TabOrder = 2
+          OnExit = ebFilterOperationsAccountExit
+          OnKeyPress = ebFilterOperationsAccountKeyPress
+        end
+        object dtpFilterOperationsDateStart: TDateTimePicker
+          Left = 654
+          Top = 7
+          Width = 101
+          Height = 21
+          Date = 42556.766794687500000000
+          Time = 42556.766794687500000000
+          TabOrder = 3
+          OnChange = ebFilterOperationsAccountExit
+          OnExit = ebFilterOperationsAccountExit
+        end
+        object cbFilterOperationsByDate: TCheckBox
+          Left = 557
+          Top = 9
+          Width = 91
+          Height = 17
+          Caption = 'Filter by date'
+          TabOrder = 4
+          OnClick = cbFilterOperationsByDateClick
+        end
+        object dtpFilterOperationsDateEnd: TDateTimePicker
+          Left = 761
+          Top = 7
+          Width = 101
+          Height = 21
+          Date = 42556.766794687500000000
+          Time = 42556.766794687500000000
+          TabOrder = 5
+          OnChange = ebFilterOperationsAccountExit
+          OnExit = ebFilterOperationsAccountExit
+        end
+      end
+      object dbgridOperations: TDBGrid
+        Left = 0
+        Top = 41
+        Width = 891
+        Height = 379
+        Align = alClient
+        Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack]
+        ReadOnly = True
+        TabOrder = 1
+        TitleFont.Charset = DEFAULT_CHARSET
+        TitleFont.Color = clWindowText
+        TitleFont.Height = -11
+        TitleFont.Name = 'Tahoma'
+        TitleFont.Style = []
+        OnDblClick = MiDecodePayloadClick
+      end
+    end
+    object tsLogs: TTabSheet
+      Caption = 'Logs'
+      ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      object pnlTopLogs: TPanel
+        Left = 0
+        Top = 0
+        Width = 891
+        Height = 41
+        Align = alTop
+        TabOrder = 0
+        object cbShowDebugLogs: TCheckBox
+          Left = 15
+          Top = 10
+          Width = 97
+          Height = 17
+          Caption = 'Show Debug logs'
+          TabOrder = 0
+        end
+      end
+      object memoLogs: TMemo
+        Left = 0
+        Top = 41
+        Width = 891
+        Height = 379
+        Align = alClient
+        ScrollBars = ssBoth
+        TabOrder = 1
+      end
+    end
+    object tsNodeStats: TTabSheet
+      Caption = 'Node Stats'
+      ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
+      DesignSize = (
+        891
+        420)
+      object Label3: TLabel
+        Left = 15
+        Top = 15
+        Width = 177
+        Height = 13
+        Caption = 'Active Net Connections of this Node:'
+      end
+      object Label6: TLabel
+        Left = 15
+        Top = 236
+        Width = 169
+        Height = 13
+        Caption = 'Available or possible Node Servers:'
+      end
+      object Label7: TLabel
+        Left = 15
+        Top = 132
+        Width = 86
+        Height = 13
+        Caption = 'BlackList of Nodes'
+      end
+      object memoNetConnections: TMemo
+        Left = 15
+        Top = 34
+        Width = 571
+        Height = 92
+        ReadOnly = True
+        ScrollBars = ssVertical
+        TabOrder = 0
+      end
+      object memoNetServers: TMemo
+        Left = 15
+        Top = 255
+        Width = 860
+        Height = 146
+        Anchors = [akLeft, akTop, akRight, akBottom]
+        ReadOnly = True
+        ScrollBars = ssVertical
+        TabOrder = 1
+      end
+      object memoNetBlackLists: TMemo
+        Left = 15
+        Top = 151
+        Width = 860
+        Height = 79
+        Anchors = [akLeft, akTop, akRight]
+        ReadOnly = True
+        ScrollBars = ssVertical
+        TabOrder = 2
+      end
+    end
+    object tsMessages: TTabSheet
+      Caption = 'Messages'
+      ImageIndex = 6
+      DesignSize = (
+        891
+        420)
+      object Label11: TLabel
+        Left = 15
+        Top = 151
+        Width = 51
+        Height = 13
+        Caption = 'Messages:'
+      end
+      object Label12: TLabel
+        Left = 315
+        Top = 11
+        Width = 85
+        Height = 13
+        Caption = 'Message to send:'
+      end
+      object Label13: TLabel
+        Left = 15
+        Top = 11
+        Width = 107
+        Height = 13
+        Caption = 'Available connections:'
+      end
+      object Label14: TLabel
+        Left = 410
+        Top = 11
+        Width = 361
+        Height = 13
+        Caption = 
+          '(Messages will be encrypted, so only dest connection will be abl' +
+          'e to read it)'
+        Font.Charset = DEFAULT_CHARSET
+        Font.Color = clGrayText
+        Font.Height = -11
+        Font.Name = 'Tahoma'
+        Font.Style = []
+        ParentFont = False
+      end
+      object lbNetConnections: TListBox
+        Left = 15
+        Top = 30
+        Width = 275
+        Height = 96
+        ItemHeight = 13
+        MultiSelect = True
+        TabOrder = 0
+      end
+      object bbSendAMessage: TButton
+        Left = 315
+        Top = 101
+        Width = 456
+        Height = 25
+        Caption = 'Send a Message'
+        TabOrder = 1
+        OnClick = bbSendAMessageClick
+      end
+      object memoMessages: TMemo
+        Left = 15
+        Top = 170
+        Width = 860
+        Height = 231
+        Anchors = [akLeft, akTop, akRight, akBottom]
+        ReadOnly = True
+        ScrollBars = ssBoth
+        TabOrder = 2
+      end
+      object memoMessageToSend: TMemo
+        Left = 315
+        Top = 30
+        Width = 456
+        Height = 61
+        Lines.Strings = (
+          'memoMessageToSend')
+        TabOrder = 3
+        WantReturns = False
+        WordWrap = False
+      end
+    end
+  end
+  object TrayIcon: TTrayIcon
+    Icon.Data = {
+      0000010001001010000001002000680400001600000028000000100000002000
+      0000010020000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000053000000BE000000E8000000F70000
+      00F7000000E9000000B20000004A000000000000000000000000000000000000
+      00000000000000000015000000B6070D16CE3E6C9BC860A8F0F166B3FFFF66B3
+      FFFF66B3FFFF3E6C9BC8070F16CC000000BA0000002600000000000000000000
+      000000000028000000CC2A4A6BC05391CFFF4C86BFFF599DDFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF2A4D6BC0000000C90000001D000000000000
+      0000000000B62A4D6BC066B3FFFF33597FFF000000FF33597FFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF2A4D6BC0000000B1000000000000
+      0050070F16CC66B3FFFF66B3FFFF40709FFF000000FF22394FFF6FB7FFFF68B4
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF070F16CC000000470000
+      00C13E6C9BC866B3FFFF66B3FFFF5D96CFFF000000FF11181FFF5A7D9FFF5A81
+      A8FF4A759FFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF3E6C9BC8000000BA0000
+      00ED60A8F0F166B3FFFF6FB7FFFF8BC5FFFF000000FF000000FF43515FFF2631
+      3BFF161F27FF5180AFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF000000EF0000
+      00FD66B3FFFF66B3FFFF7DBFFFFF9DCEFFFF22292FFF000000FF000000FF0000
+      00FF000000FF000000FF0C161FFF5391CFFF66B3FFFF66B3FFFF000000FD0000
+      00FD66B3FFFF66B3FFFF84C2FFFFA6D3FFFF3E474FFF000000FF686C6FFF262B
+      2FFF1A2127FF1A242FFF000000FF192C3FFF66B3FFFF66B3FFFF000000FB0000
+      00F666B3FFFF66B3FFFF82C1FFFFA3D1FFFF61707FFF000000FF62686FFF3238
+      3FFF4A5D6FFF6895C2FF000000FF000000FF5391CFFF60A8F0F1000000ED0000
+      00CA3E6C9BC866B3FFFF76BBFFFF94CAFFFF82A0BFFF000000FF30373FFF4251
+      5FFF4B647DFF406283FF000000FF000000FF4C86BFFF3E6C9BC8000000BF0000
+      005B070D12CE66B3FFFF66B3FFFF7DBFFFFF80B0DFFF000000FF000000FF0000
+      00FF000000FF000000FF000000FF0C161FFF66B3FFFF070F16CC000000500000
+      0000000000CF2A4A6BC066B3FFFF66B3FFFF73BAFFFF1F2F3FFF1F2F3FFF1622
+      2FFF0C141DFF080E14FF2C4E6FFF599DDFFF2A4D6BC0000000B6000000000000
+      000000000030000000CF2A4D6BC066B3FFFF66B3FFFF66B3FFFF66B3FFFF599D
+      DFFF39648FFF2C4E6FFF66B3FFFF2A4D6BC0000000CF00000022000000000000
+      0000000000000000002B000000C4070D16CD3E6C9BC866B3FFFF66B3FFFF66B3
+      FFFF60A8F0F13E6C9BC8070D16CD000000C00000002600000000000000000000
+      00000000000000000000000000000000005D000000CE000000F4000000FE0000
+      00FE000000F6000000C60000005600000000000000000000000000000000FFFF
+      0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF
+      0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000}
+    Icons = ImageListIcons
+    Visible = True
+    OnDblClick = TrayIconDblClick
+    Left = 45
+    Top = 20
+  end
+  object TimerUpdateStatus: TTimer
+    OnTimer = TimerUpdateStatusTimer
+    Left = 25
+    Top = 45
+  end
+  object MainMenu: TMainMenu
+    Left = 165
+    Top = 160
+    object miProject: TMenuItem
+      Caption = 'Project'
+      object miNewOperation: TMenuItem
+        Caption = 'New Operation'
+        ShortCut = 120
+        OnClick = miNewOperationClick
+      end
+      object MiDecodePayload: TMenuItem
+        Caption = 'Decode Payload'
+        ShortCut = 113
+        OnClick = MiDecodePayloadClick
+      end
+      object miPrivatekeys: TMenuItem
+        Caption = 'Private keys'
+        ShortCut = 16464
+        OnClick = miPrivatekeysClick
+      end
+      object miN1: TMenuItem
+        Caption = '-'
+      end
+      object miOptions: TMenuItem
+        Caption = 'Options'
+        ShortCut = 16463
+        OnClick = miOptionsClick
+      end
+      object N1: TMenuItem
+        Caption = '-'
+      end
+      object MiClose: TMenuItem
+        Caption = 'Close'
+        OnClick = MiCloseClick
+      end
+    end
+    object miAbout: TMenuItem
+      Caption = 'About'
+      object miAboutPascalCoin: TMenuItem
+        Caption = 'About Pascal Coin...'
+        OnClick = miAboutPascalCoinClick
+      end
+    end
+  end
+  object ImageListIcons: TImageList
+    Height = 48
+    Left = 90
+    Top = 155
+    Bitmap = {
+      494C010102000800600010003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      0000000000003600000028000000400000003000000001002000000000000030
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00002A2B2ED766B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF5E768FC00000
+      0032000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000006D6D
+      6D9266B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF292A
+      2DD8000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000006D6D
+      6D9266B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF292A
+      2DD8000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000000000007866B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF2C3032D50000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000002B2F31D666B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF68ACF0F10000000B00000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000002B2F31D666B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF68ACF0F10000000B00000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000007F7F7F8066B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF2B2F30D600000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000141415EB66B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF68ACF0F100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000141415EB66B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF68ACF0F100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000066B3FFFF66B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF68686897000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000003566B3FFFF66B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF202222E0000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000003566B3FFFF66B3FFFF66B3
+      FFFF000000FF000000FF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF202222E0000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000202222E066B3FFFF66B3FFFF66B3
+      FFFF000000FF000000FF79BCFFFF73BAFFFF68B4FFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000586D7FBF66B3FFFF66B3FFFF67B4
+      FFFF000000FF000000FF83C1FFFF3E5F7FFF5C96CFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000586D7FBF66B3FFFF66B3FFFF67B4
+      FFFF000000FF000000FF83C1FFFF3E5F7FFF5C96CFFF66B3FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF66B3FFFF77BC
+      FFFF19242FFF000000FF7198BFFF12181FFF83C2FFFF70B8FFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF0000002E0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF68B4FFFF7FC0
+      FFFF2D3E4FFF000000FF65829FFF26333FFF8CC6FFFF70B0EFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF0000007F0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF68B4FFFF7FC0
+      FFFF2D3E4FFF000000FF65829FFF26333FFF8CC6FFFF70B0EFFF66B3FFFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF0000007F0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF74BAFFFF8EC7
+      FFFF7A9DBFFF000000FF000000FF000000FF000000FF000000FF0D161FFF66B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF232323DC0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF79BDFFFF94CA
+      FFFF96BADFFF000000FF000000FF000000FF000000FF000000FF000000FF3964
+      8FFF66B3FFFF66B3FFFF66B3FFFF111111EE0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF79BDFFFF94CA
+      FFFF96BADFFF000000FF000000FF000000FF000000FF000000FF000000FF3964
+      8FFF66B3FFFF66B3FFFF66B3FFFF111111EE0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF66B3FFFF82C1FFFF9ECF
+      FFFFB9DCFFFF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF60A8EFFF66B3FFFF66B3FFFF050505FA0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF67B3FFFF85C2FFFFA3D1
+      FFFFBFDFFFFF000000FF000000FF33393FFF2A323BFF000000FF000000FF0000
+      00FF20374FFF66B3FFFF66B3FFFF020202FD0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF67B3FFFF85C2FFFFA3D1
+      FFFFBFDFFFFF000000FF000000FF33393FFF2A323BFF000000FF000000FF0000
+      00FF20374FFF66B3FFFF66B3FFFF020202FD0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF69B4FFFF88C4FFFFA7D3
+      FFFFC6E3FFFF000000FF000000FFD9ECFFFF697C8FFF000000FF659ACFFF0000
+      00FF000000FF66B3FFFF66B3FFFF0D0D0DF20000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF6AB5FFFF89C4FFFFA8D4
+      FFFFC7E3FFFF000000FF000000FFDAEDFFFF51606FFF000000FF7DBFFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF232323DC0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF6AB5FFFF89C4FFFFA8D4
+      FFFFC7E3FFFF000000FF000000FFDAEDFFFF51606FFF000000FF7DBFFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF232323DC0000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF68B4FFFF87C3FFFFA6D3
+      FFFFC3E1FFFF000000FF000000FFD5EAFFFF0B0D0FFF13191FFF7CBEFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000780000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF67B3FFFF85C2FFFFA3D1
+      FFFFBFDFFFFF000000FF000000FFCFE7FFFF000000FF25323FFF7ABDFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000280000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000066B3FFFF67B3FFFF85C2FFFFA3D1
+      FFFFBFDFFFFF000000FF000000FFCFE7FFFF000000FF25323FFF7ABDFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000280000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000005E768FC066B3FFFF7EBFFFFF9ACD
+      FFFFB3D9FFFF000000FF000000FFC0E0FFFF000000FF597D9FFF73BAFFFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000212223DF66B3FFFF79BDFFFF94CA
+      FFFFACD6FFFF000000FF000000FFB7DBFFFF000000FF6894BFFF375B7FFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000212223DF66B3FFFF79BDFFFF94CA
+      FFFFACD6FFFF000000FF000000FFB7DBFFFF000000FF6894BFFF375B7FFF0000
+      00FF000000FF66B3FFFF66B3FFFF000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000003F66B3FFFF6EB7FFFF87C3
+      FFFF9BCDFFFF34424FFF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF66B3FFFF2B2C2FD6000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000066B3FFFF68B4FFFF7FC0
+      FFFF92C9FFFF4F677FFF000000FF000000FF000000FF000000FF000000FF0000
+      00FF13212FFF66B3FFFF6E6E6E91000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000066B3FFFF68B4FFFF7FC0
+      FFFF92C9FFFF4F677FFF000000FF000000FF000000FF000000FF000000FF0000
+      00FF13212FFF66B3FFFF6E6E6E91000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000002B2F30D666B3FFFF70B8
+      FFFF80C0FFFF7AACDFFF000000FF000000FF000000FF000000FF000000FF2C4E
+      6FFF66B3FFFF68ACF0F100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000007474748B66B3FFFF67B4
+      FFFF77BBFFFF80C0FFFF83C1FFFF7DBFFFFF558BBFFF66B3FFFF2C4E6FFF66B3
+      FFFF66B3FFFF323438D100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000007474748B66B3FFFF67B4
+      FFFF77BBFFFF80C0FFFF83C1FFFF7DBFFFFF558BBFFF66B3FFFF2C4E6FFF66B3
+      FFFF66B3FFFF323438D100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000002B2F30D666B3
+      FFFF66B3FFFF6CB6FFFF6EB7FFFF6AB5FFFF66B3FFFF66B3FFFF060B0FFF66B3
+      FFFF68ACF0F10000000A00000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000007878788766B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF33597FFF66B3
+      FFFF1D1E1FE20000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000007878788766B3
+      FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF33597FFF66B3
+      FFFF1D1E1FE20000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000005252
+      52AD66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF2526
+      27DB000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000232425DD66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF586D7FBF0000
+      0035000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000232425DD66B3FFFF66B3FFFF66B3FFFF66B3FFFF66B3FFFF586D7FBF0000
+      0035000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000003D161616E9010101FE0C0C0CF37171718E000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000424D3E000000000000003E000000
+      2800000040000000300000000100010000000000800100000000000000000000
+      000000000000000000000000FFFFFF00FC7F000000000000FC7F000000000000
+      F01F000000000000E00F000000000000E00F000000000000E007000000000000
+      C007000000000000C00700000000000080030000000000008003000000000000
+      8003000000000000800100000000000080010000000000008001000000000000
+      0001000000000000000100000000000000010000000000000001000000000000
+      0001000000000000000100000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000001000000000000
+      0001000000000000000100000000000000010000000000000001000000000000
+      0001000000000000800100000000000080010000000000008001000000000000
+      800300000000000080030000000000008003000000000000C007000000000000
+      C007000000000000C007000000000000E00F000000000000F01F000000000000
+      F01F000000000000FC3F00000000000000000000000000000000000000000000
+      000000000000}
+  end
+  object ApplicationEvents: TApplicationEvents
+    OnMinimize = ApplicationEventsMinimize
+    Left = 145
+    Top = 225
+  end
+end

+ 1321 - 0
Units/Forms/UFRMWallet.pas

@@ -0,0 +1,1321 @@
+unit UFRMWallet;
+
+{ 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
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, pngimage, ExtCtrls, ComCtrls, UWalletKeys, ShlObj, ADOInt, StdCtrls,
+  ULog, DB, ADODB, Grids, DBGrids, DBCGrids, UAppParams,
+  UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
+  AppEvnts, UNetProtocol, UCrypto;
+
+Const
+  CT_PARAM_GridAccountsStream = 'GridAccountsStream';
+  CT_PARAM_GridAccountsPos = 'GridAccountsPos';
+
+  CT_PARAM_DefaultFee = 'DefaultFee';
+  CT_PARAM_InternetServerPort = 'InternetServerPort';
+  CT_PARAM_AutomaticMineWhenConnectedToNodes = 'AutomaticMineWhenConnectedToNodes';
+  CT_PARAM_NewPrivateKeyForEachGeneratedBlock = 'NewPrivateKeyForEachGeneratedBlock';
+  CT_PARAM_SaveLogFiles = 'SaveLogFiles';
+  CT_PARAM_ShowLogs = 'ShowLogs';
+  CT_PARAM_MinerName = 'MinerName';
+  CT_PARAM_FirstTime = 'FirstTime';
+  CT_PARAM_ShowModalMessages = 'ShowModalMessages';
+
+type
+  TStringListAux = Class(TStringList)
+
+  End;
+
+  TFRMWallet = class(TForm)
+    pnlTop: TPanel;
+    Image1: TImage;
+    StatusBar: TStatusBar;
+    PageControl: TPageControl;
+    tsAccountsExplorer: TTabSheet;
+    tsOperations: TTabSheet;
+    TrayIcon: TTrayIcon;
+    TimerUpdateStatus: TTimer;
+    tsLogs: TTabSheet;
+    pnlTopLogs: TPanel;
+    cbShowDebugLogs: TCheckBox;
+    memoLogs: TMemo;
+    pnlMyAccountsTop: TPanel;
+    dgAccounts: TDrawGrid;
+    cbMyPrivateKeys: TComboBox;
+    dgAccountOperations: TDrawGrid;
+    Splitter1: TSplitter;
+    MainMenu: TMainMenu;
+    miProject: TMenuItem;
+    miOptions: TMenuItem;
+    miPrivatekeys: TMenuItem;
+    miN1: TMenuItem;
+    miAbout: TMenuItem;
+    miAboutPascalCoin: TMenuItem;
+    miNewOperation: TMenuItem;
+    Panel1: TPanel;
+    Label1: TLabel;
+    dbgridOperations: TDBGrid;
+    cbAllowMining: TCheckBox;
+    ebFilterOperationsAccount: TEdit;
+    Label2: TLabel;
+    ebFilterOperationsStartBlock: TEdit;
+    ebFilterOperationsEndBlock: TEdit;
+    dtpFilterOperationsDateStart: TDateTimePicker;
+    cbFilterOperationsByDate: TCheckBox;
+    dtpFilterOperationsDateEnd: TDateTimePicker;
+    tsNodeStats: TTabSheet;
+    memoNetConnections: TMemo;
+    memoNetServers: TMemo;
+    memoNetBlackLists: TMemo;
+    Label3: TLabel;
+    Label6: TLabel;
+    Label7: TLabel;
+    lblCurrentBlockCaption: TLabel;
+    lblCurrentBlock: TLabel;
+    lblCurrentBlockTimeCaption: TLabel;
+    lblCurrentBlockTime: TLabel;
+    lblOperationsPendingCaption: TLabel;
+    lblOperationsPending: TLabel;
+    lblMiningStatusCaption: TLabel;
+    lblMiningStatus: TLabel;
+    lblCurrentDifficultyCaption: TLabel;
+    lblCurrentDifficulty: TLabel;
+    lblTimeAverage: TLabel;
+    Label4: TLabel;
+    tsBlockChain: TTabSheet;
+    Panel2: TPanel;
+    Label9: TLabel;
+    ebBlockChainBlockStart: TEdit;
+    ebBlockChainBlockEnd: TEdit;
+    dtpBlockChainDateStart: TDateTimePicker;
+    cbBlockChainFilterByDate: TCheckBox;
+    dtpBlockChainDateEnd: TDateTimePicker;
+    dbGridBlockChain: TDBGrid;
+    Label8: TLabel;
+    lblNodeStatus: TLabel;
+    tsPendingOperations: TTabSheet;
+    dgPendingOperations: TDrawGrid;
+    pnlPendingOperations: TPanel;
+    Label10: TLabel;
+    cbExploreMyAccounts: TCheckBox;
+    N1: TMenuItem;
+    MiClose: TMenuItem;
+    MiDecodePayload: TMenuItem;
+    ImageListIcons: TImageList;
+    ApplicationEvents: TApplicationEvents;
+    Label5: TLabel;
+    lblCurrentAccounts: TLabel;
+    lblTimeAverageAux: TLabel;
+    tsMessages: TTabSheet;
+    lbNetConnections: TListBox;
+    bbSendAMessage: TButton;
+    Label11: TLabel;
+    memoMessages: TMemo;
+    memoMessageToSend: TMemo;
+    Label12: TLabel;
+    Label13: TLabel;
+    Label14: TLabel;
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure TimerUpdateStatusTimer(Sender: TObject);
+    procedure cbMyPrivateKeysChange(Sender: TObject);
+    procedure dgAccountsClick(Sender: TObject);
+    procedure miOptionsClick(Sender: TObject);
+    procedure miAboutPascalCoinClick(Sender: TObject);
+    procedure miNewOperationClick(Sender: TObject);
+    procedure miPrivatekeysClick(Sender: TObject);
+    procedure dgAccountsColumnMoved(Sender: TObject; FromIndex,
+      ToIndex: Integer);
+    procedure dgAccountsFixedCellClick(Sender: TObject; ACol, ARow: Integer);
+    procedure PageControlChange(Sender: TObject);
+    procedure ebFilterOperationsAccountExit(Sender: TObject);
+    procedure ebFilterOperationsAccountKeyPress(Sender: TObject; var Key: Char);
+    procedure cbFilterOperationsByDateClick(Sender: TObject);
+    procedure ebBlockChainBlockStartExit(Sender: TObject);
+    procedure ebBlockChainBlockStartKeyPress(Sender: TObject; var Key: Char);
+    procedure cbBlockChainFilterByDateClick(Sender: TObject);
+    procedure cbAllowMiningClick(Sender: TObject);
+    procedure cbExploreMyAccountsClick(Sender: TObject);
+    procedure MiCloseClick(Sender: TObject);
+    procedure MiDecodePayloadClick(Sender: TObject);
+    procedure TrayIconDblClick(Sender: TObject);
+    procedure ApplicationEventsMinimize(Sender: TObject);
+    procedure bbSendAMessageClick(Sender: TObject);
+  private
+  protected
+    { Private declarations }
+    FNode : TNode;
+    FIsActivated : Boolean;
+    FWalletKeys : TWalletKeys;
+    FLog : TLog;
+    FAppParams : TAppParams;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FAccountsGrid : TAccountsGrid;
+    FOperationsGrid : TOperationsGrid;
+    FPendingOperationsGrid : TOperationsGrid;
+    FOrderedAccountsKeyList : TOrderedAccountKeysList;
+    FOperationsDBGrid : TOperationsDBGrid;
+    FBlockChainDBGrid : TBlockChainDBGrid;
+
+    FMemoNetConnections : TStringListAux;
+    FMemoBlackListNodes : TStringListAux;
+    FMemoAvailableNodeServers : TStringListAux;
+
+    FUpdating : Boolean;
+    Procedure CheckMining;
+    Procedure OnNewAccount(Sender : TObject);
+    Procedure OnReceivedHelloResponse(Sender : TObject);
+    Procedure OnNetStatisticsChanged(Sender : TObject);
+    procedure OnNewLog(logtype : TLogType; Time : TDateTime; ThreadID : Cardinal; Const sender, logtext : AnsiString);
+    procedure OnMinerNewBlockFound(sender : TMinerThread; Operations : TPCOperationsComp);
+    procedure OnWalletChanged(Sender : TObject);
+    procedure OnNetConnectionsUpdated(Sender : TObject);
+    procedure OnNetNodeServersUpdated(Sender : TObject);
+    procedure OnNetBlackListUpdated(Sender : TObject);
+    Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
+    Procedure UpdateConnectionStatus;
+    Procedure UpdateAccounts;
+    Procedure UpdateBlockChainState;
+    Procedure UpdatePrivateKeys;
+    Procedure UpdateOperations;
+    Procedure LoadAppParams;
+    Procedure SaveAppParams;
+    Procedure UpdateConfigChanged;
+    Procedure UpdateNodeStatus;
+    Procedure UpdateAvailableConnections;
+    procedure Activate; override;
+    Function ForceMining : Boolean; virtual;
+  public
+    { Public declarations }
+    Property WalletKeys : TWalletKeys read FWalletKeys;
+  end;
+
+var
+  FRMWallet: TFRMWallet;
+
+implementation
+
+{$R *.dfm}
+
+Uses UFolderHelper, ssl_lib, UConst, UTime,
+  UDBStorage, UThread, UOpTransaction, UECIES, UFRMPascalCoinWalletConfig,
+  UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder;
+
+Type
+  TThreadActivate = Class(TPCThread)
+  protected
+    procedure BCExecute; override;
+  End;
+
+{ TThreadActivate }
+
+procedure TThreadActivate.BCExecute;
+begin
+  // Read Operations saved from disk
+  TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+  // Activating server
+  TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+  TNode.Node.NetServer.Active := true;
+  FRMWallet.UpdateAccounts;
+end;
+
+{ TFRMWallet }
+
+procedure TFRMWallet.Activate;
+begin
+  inherited;
+  if FIsActivated then exit;
+  FIsActivated := true;
+  try
+    // Check OpenSSL dll
+    if Not LoadSSLCrypt then raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall this Application');
+    TCrypto.InitCrypto;
+    // Read Wallet
+    Try
+      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+'\WalletKeys.dat';
+    Except
+      On E:Exception do begin
+        E.Message := 'Cannot open your Wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
+        Raise;
+      end;
+    End;
+    // Creating Node:
+    FNode := TNode.Node;
+    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    // Check Database
+    FNode.Bank.StorageClass := TDBStorage;
+    TDBStorage(FNode.Bank.Storage).AccessFileName := TFolderHelper.GetPascalCoinDataFolder+'\pascalcoin.mdb';
+    // Init Grid
+    FAccountsGrid.Node := FNode;
+    FWalletKeys.OnChanged := OnWalletChanged;
+    FOperationsGrid.Node := FNode;
+    FPendingOperationsGrid.Node := FNode;
+    // Reading database
+    TThreadActivate.Create(false).FreeOnTerminate := true;
+    FNodeNotifyEvents.Node := FNode;
+    FOperationsDBGrid.Node := FNode;
+    FOperationsDBGrid.AdoConnection := TDBStorage(FNode.Bank.Storage).ADOConnection;
+    FOperationsDBGrid.DBGrid := dbgridOperations;
+    FBlockChainDBGrid.Node := FNode;
+    FBlockChainDBGrid.AdoConnection := TDBStorage(FNode.Bank.Storage).ADOConnection;
+    FBlockChainDBGrid.DBGrid := dbGridBlockChain;
+    // Init TNode
+    TNetData.NetData.OnReceivedHelloResponse := OnReceivedHelloResponse;
+    TNetData.NetData.OnStatisticsChanged := OnNetStatisticsChanged;
+    TNetData.NetData.OnNetConnectionsUpdated := onNetConnectionsUpdated;
+    TNetData.NetData.OnNodeServersUpdated := OnNetNodeServersUpdated;
+    TNetData.NetData.OnBlackListUpdated := OnNetBlackListUpdated;
+    //
+    TimerUpdateStatus.Enabled := true;
+    UpdateConfigChanged;
+  Except
+    On E:Exception do begin
+      E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
+      Application.MessageBox(PChar(E.Message),PChar(Application.Title),MB_ICONERROR+MB_OK);
+      Halt;
+    end;
+  end;
+  UpdatePrivateKeys;
+  UpdateAccounts;
+  if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
+    FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
+    miAboutPascalCoinClick(Nil);
+  end;
+
+end;
+
+procedure TFRMWallet.ApplicationEventsMinimize(Sender: TObject);
+begin
+  Hide();
+  WindowState := wsMinimized;
+  { Show the animated tray icon and also a hint balloon. }
+  TrayIcon.Visible := True;
+  TrayIcon.ShowBalloonHint;
+end;
+
+procedure TFRMWallet.bbSendAMessageClick(Sender: TObject);
+Var m : String;
+  them, errors : AnsiString;
+  i : Integer;
+  nc : TNetConnection;
+begin
+  if (lbNetConnections.SelCount<=0) Or (lbNetConnections.ItemIndex<0) then raise Exception.Create('Select at least 1 connection');
+  if lbNetConnections.SelCount<=0 then i := 1
+  else i := lbNetConnections.SelCount;
+
+  m := memoMessageToSend.Lines.Text;
+  if trim(m)='' then raise Exception.Create('No message');
+
+  if Application.MessageBox(PChaR('Send this message to '+inttostr(i)+' nodes?'+#10+
+    'ALERT: Sending unauthorized messages will be considered spam and you will be banned'+#10+
+    #10+
+    'Message: '+#10+
+    m),PChar(Application.Title),MB_ICONQUESTION+MB_YESNO+MB_DEFBUTTON1)<>IdYes then exit;
+  them := m;
+  if i>1 then begin
+    for i := 0 to lbNetConnections.Items.Count - 1 do begin
+      if lbNetConnections.Selected[i] then begin
+        nc := TNetConnection(lbNetconnections.Items.Objects[i]);
+        if TNetData.NetData.ConnectionExists(nc) then begin
+          FNode.SendNodeMessage(nc,m,errors);
+          memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.Client.RemoteHost+':'+nc.Client.RemotePort+' > '+m);
+        end;
+      end;
+    end;
+  end else begin
+    nc := TNetConnection(lbNetconnections.Items.Objects[lbNetconnections.ItemIndex]);
+    if TNetData.NetData.ConnectionExists(nc) then begin
+      FNode.SendNodeMessage(nc,m,errors);
+      memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.Client.RemoteHost+':'+nc.Client.RemotePort+' > '+m);
+    end;
+  end;
+
+  Application.MessageBox(PChaR('Message sent to '+inttostr(i)+' nodes'+#10+
+    'Message: '+#10+m),PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+end;
+
+procedure TFRMWallet.cbAllowMiningClick(Sender: TObject);
+begin
+  if Not Assigned(FNode) then exit;
+  if cbAllowMining.Checked then begin
+    if (TNetData.NetData.NetStatistics.ClientsConnections<=0) then begin
+      Application.MessageBox(PChar(Format('In order to Mine is necessary that you open your external port %d from the Internet to allow other nodes of Pascal Coin to connect to you.'+#10+
+        #10+
+        'To do this you must configure your Router/Firewall and enable NAT to your local machine at port: %d'+#10+#10+
+        'After allowing incoming connections... you must wait until other nodes connects to you to mine'+#10+
+        'Please... be patient'+#10+#10+
+        'Help mining Pascal Coin and win Pascal Coins!',[FNode.NetServer.Port,FNode.NetServer.Port])),
+        PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+    end;
+  end;
+end;
+
+procedure TFRMWallet.cbBlockChainFilterByDateClick(Sender: TObject);
+begin
+  dtpBlockChainDateStart.Enabled := cbBlockChainFilterByDate.Checked;
+  dtpBlockChainDateEnd.Enabled := cbBlockChainFilterByDate.Checked;
+  ebBlockChainBlockStartExit(Nil);
+end;
+
+procedure TFRMWallet.cbExploreMyAccountsClick(Sender: TObject);
+begin
+  cbMyPrivateKeys.Enabled := cbExploreMyAccounts.Checked;
+  if cbExploreMyAccounts.Checked then UpdateAccounts;
+  FAccountsGrid.ShowAllAccounts := Not cbExploreMyAccounts.Checked;
+  UpdateOperations;
+end;
+
+procedure TFRMWallet.cbFilterOperationsByDateClick(Sender: TObject);
+begin
+  dtpFilterOperationsDateStart.Enabled := cbFilterOperationsByDate.Checked;
+  dtpFilterOperationsDateEnd.Enabled := cbFilterOperationsByDate.Checked;
+  ebFilterOperationsAccountExit(Nil);
+end;
+
+procedure TFRMWallet.cbMyPrivateKeysChange(Sender: TObject);
+begin
+  UpdateAccounts;
+end;
+
+procedure TFRMWallet.CheckMining;
+  Procedure Stop;
+  var i : Integer;
+    mtl : TList;
+  begin
+    if ForceMining then exit;
+    // Stop mining
+    mtl := FNode.MinerThreads.LockList;
+    try
+      for i:=mtl.Count-1 downto 0 do begin
+        TMinerThread(mtl[i]).Paused := true;
+      end;
+    finally
+      FNode.MinerThreads.UnlockList;
+    end;
+  end;
+Var i, n : Integer;
+  MT : TMinerThread;
+  wk : TWalletKey;
+  EC : TECPrivateKey;
+  mtl : TList;
+begin
+  if Not Assigned(FNode) then exit;
+  if (ForceMining) Or
+    ((TNetData.NetData.NetStatistics.TotalClientsConnections>0) And (TNetData.NetData.NetStatistics.ActiveConnections>0) And
+    (TNetData.NetData.MaxRemoteOperationBlock.block<=FNode.Operations.OperationBlock.block)) then begin
+    if (cbAllowMining.checked) Or (ForceMining) then begin
+      n := 0;
+      mtl := FNode.MinerThreads.LockList;
+      try
+        for i:=mtl.Count-1 downto 0 do begin
+          if TMinerThread(mtl[i]).Paused then TMinerThread(mtl[i]).Paused := false;
+          inc(n);
+        end;
+        if n=0 then begin
+          wk := CT_TWalletKey_NUL;
+          If FWalletKeys.Count>0 then begin
+            wk := FWalletKeys.Key[Random(FWalletKeys.Count)];
+          end;
+          if wk.CryptedKey='' then begin
+            EC := TECPrivateKey.Create;
+            EC.GenerateRandomPrivateKey(CT_Default_EC_OpenSSL_NID);
+            wk := FWalletKeys.Key[FWalletKeys.AddPrivateKey('New key '+DateTimeToStr(Now),EC)];
+          end;
+          if mtl.Count<=0 then MT := FNode.AddMiner(wk.AccountKey)
+          else MT := TMinerThread(mtl[0]);
+          MT.OnNewAccountFound := OnMinerNewBlockFound;
+          MT.Paused := false;
+        end;
+      Finally
+        FNode.MinerThreads.UnlockList;
+      End;
+    end else begin
+      Stop;
+    end;
+  end else Stop;
+end;
+
+procedure TFRMWallet.dgAccountsClick(Sender: TObject);
+begin
+  UpdateOperations;
+end;
+
+procedure TFRMWallet.dgAccountsColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer);
+begin
+  SaveAppParams;
+end;
+
+procedure TFRMWallet.dgAccountsFixedCellClick(Sender: TObject; ACol,
+  ARow: Integer);
+begin
+  SaveAppParams;
+end;
+
+procedure TFRMWallet.ebBlockChainBlockStartExit(Sender: TObject);
+var i64 : Int64;
+begin
+  If FUpdating then exit;
+  FUpdating := True;
+  Try
+    FBlockChainDBGrid.Disable;
+    try
+      i64 := StrToInt64Def(ebBlockChainBlockStart.Text,-1);
+      FBlockChainDBGrid.BlockStart := i64;
+      if i64>=0 then ebBlockChainBlockStart.Text := Inttostr(i64) else ebBlockChainBlockStart.Text := '';
+      i64 := StrToInt64Def(ebBlockChainBlockEnd.Text,-1);
+      FBlockChainDBGrid.BlockEnd := i64;
+      if i64>=0 then ebBlockChainBlockEnd.Text := Inttostr(i64) else ebBlockChainBlockEnd.Text := '';
+      if cbBlockChainFilterByDate.Checked then begin
+        if dtpBlockChainDateStart.Date<encodedate(2016,01,01) then dtpBlockChainDateStart.Date := encodedate(2016,01,01);
+        if dtpBlockChainDateStart.Date>dtpBlockChainDateEnd.Date then dtpBlockChainDateEnd.Date := dtpBlockChainDateStart.Date;
+        FBlockChainDBGrid.SetDates(dtpBlockChainDateStart.Date,dtpBlockChainDateEnd.Date);
+        dtpBlockChainDateStart.Date := FBlockChainDBGrid.DateStart;
+        dtpBlockChainDateEnd.Date := FBlockChainDBGrid.DateEnd;
+      end else begin
+        FBlockChainDBGrid.SetDates(0,0);
+      end;
+    finally
+      FBlockChainDBGrid.Enable;
+    end;
+  Finally
+    FUpdating := false;
+  End;
+end;
+
+procedure TFRMWallet.ebBlockChainBlockStartKeyPress(Sender: TObject;
+  var Key: Char);
+begin
+  if key=#13 then  ebBlockChainBlockStartExit(Nil);
+end;
+
+procedure TFRMWallet.ebFilterOperationsAccountExit(Sender: TObject);
+Var acc : Cardinal;
+  i64 : Int64;
+begin
+  If FUpdating then exit;
+  FUpdating := True;
+  Try
+    FOperationsDBGrid.Disable;
+    try
+      if TAccountComp.AccountTxtNumberToAccountNumber(ebFilterOperationsAccount.Text,acc) then begin
+        FOperationsDBGrid.AccountNumber := acc;
+        ebFilterOperationsAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(acc);
+      end else begin
+        FOperationsDBGrid.AccountNumber := -1;
+        ebFilterOperationsAccount.Text := '';
+      end;
+      i64 := StrToInt64Def(ebFilterOperationsStartBlock.Text,-1);
+      FOperationsDBGrid.BlockStart := i64;
+      if i64>=0 then ebFilterOperationsStartBlock.Text := Inttostr(i64) else ebFilterOperationsStartBlock.Text := '';
+      i64 := StrToInt64Def(ebFilterOperationsEndBlock.Text,-1);
+      FOperationsDBGrid.BlockEnd := i64;
+      if i64>=0 then ebFilterOperationsEndBlock.Text := Inttostr(i64) else ebFilterOperationsEndBlock.Text := '';
+      if cbFilterOperationsByDate.Checked then begin
+        if dtpFilterOperationsDateStart.Date<encodedate(2016,01,01) then dtpFilterOperationsDateStart.Date := encodedate(2016,01,01);
+        if dtpFilterOperationsDateStart.Date>dtpFilterOperationsDateEnd.Date then dtpFilterOperationsDateEnd.Date := dtpFilterOperationsDateStart.Date;
+        FOperationsDBGrid.SetDates(dtpFilterOperationsDateStart.Date,dtpFilterOperationsDateEnd.Date);
+        dtpFilterOperationsDateStart.Date := FOperationsDBGrid.DateStart;
+        dtpFilterOperationsDateEnd.Date := FOperationsDBGrid.DateEnd;
+      end else begin
+        FOperationsDBGrid.SetDates(0,0);
+      end;
+      //
+    finally
+      FOperationsDBGrid.Enable;
+    end;
+  Finally
+    FUpdating := false;
+  End;
+end;
+
+procedure TFRMWallet.ebFilterOperationsAccountKeyPress(Sender: TObject;
+  var Key: Char);
+begin
+  if key=#13 then  ebFilterOperationsAccountExit(Nil);
+end;
+
+function TFRMWallet.ForceMining: Boolean;
+begin
+  Result := false;
+end;
+
+procedure TFRMWallet.FormCreate(Sender: TObject);
+Var i : Integer;
+begin
+  FMemoNetConnections := TStringListAux.Create;
+  FMemoBlackListNodes := TStringListAux.Create;
+  FMemoAvailableNodeServers := TStringListAux.Create;
+  memoNetConnections.Lines.Clear;
+  memoNetServers.Lines.Clear;
+  memoNetBlackLists.Lines.Clear;
+  memoMessages.Lines.Clear;
+  memoMessageToSend.Lines.Clear;
+  FNode := Nil;
+  FUpdating := false;
+  FOrderedAccountsKeyList := Nil;
+  TimerUpdateStatus.Enabled := false;
+  FIsActivated := false;
+  FWalletKeys := TWalletKeys.Create(Self);
+  for i := 0 to StatusBar.Panels.Count - 1 do begin
+    StatusBar.Panels[i].Text := '';
+  end;
+  FLog := TLog.Create(Self);
+  FLog.OnNewLog := OnNewLog;
+  FLog.SaveTypes := [];
+  If Not ForceDirectories(TFolderHelper.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TFolderHelper.GetPascalCoinDataFolder);
+  FAppParams := TAppParams.Create(self);
+  FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+'\AppParams.prm';
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
+  FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
+  FAccountsGrid := TAccountsGrid.Create(Self);
+  FAccountsGrid.DrawGrid := dgAccounts;
+  FOperationsGrid := TOperationsGrid.Create(Self);
+  FOperationsGrid.DrawGrid := dgAccountOperations;
+  FPendingOperationsGrid := TOperationsGrid.Create(Self);
+  FPendingOperationsGrid.DrawGrid := dgPendingOperations;
+  FPendingOperationsGrid.AccountNumber := -1; // all
+  FWalletKeys.OnChanged := OnWalletChanged;
+  FOperationsDBGrid := TOperationsDBGrid.Create(Self);
+  FBlockChainDBGrid := TBlockChainDBGrid.Create(Self);
+  LoadAppParams;
+  UpdatePrivateKeys;
+  UpdateBlockChainState;
+  UpdateConnectionStatus;
+  ebFilterOperationsAccount.Text := '';
+  PageControl.ActivePage := tsAccountsExplorer;
+  dtpFilterOperationsDateStart.Date := Now;
+  dtpFilterOperationsDateEnd.Date := Now;
+  ebFilterOperationsAccount.Text := '';
+  ebFilterOperationsStartBlock.Text := '';
+  ebFilterOperationsEndBlock.Text := '';
+  cbFilterOperationsByDate.Checked := false;
+  cbFilterOperationsByDateClick(nil);
+  cbExploreMyAccountsClick(nil);
+
+  TrayIcon.Visible := true;
+  TrayIcon.Hint := Self.Caption;
+  TrayIcon.BalloonTitle := 'Restoring the window.';
+  TrayIcon.BalloonHint :=
+    'Double click the system tray icon to restore Pascal Coin';
+  TrayIcon.BalloonFlags := bfInfo;
+end;
+
+procedure TFRMWallet.FormDestroy(Sender: TObject);
+Var i : Integer;
+  MT : TMinerThread;
+  step : String;
+begin
+  TLog.NewLog(ltinfo,Classname,'Destroying form - START');
+  Try
+  step := 'Saving params';
+  SaveAppParams;
+  //
+  step := 'Assigning nil events';
+  FLog.OnNewLog :=Nil;
+  FNodeNotifyEvents.Node := Nil;
+  FOperationsGrid.Node := Nil;
+  FPendingOperationsGrid.Node := Nil;
+  FAccountsGrid.Node := Nil;
+  TNetData.NetData.OnReceivedHelloResponse := Nil;
+  TNetData.NetData.OnStatisticsChanged := Nil;
+  TNetData.NetData.OnNetConnectionsUpdated := Nil;
+  TNetData.NetData.OnNodeServersUpdated := Nil;
+  TNetData.NetData.OnBlackListUpdated := Nil;
+  //
+
+  Repeat
+    i := TPCThread.ThreadClassFound(TMinerThread,nil);
+    if i>=0 then begin
+      step := 'Terminating Miner thread '+inttostr(i);
+      MT := TMinerThread( TPCThread.GetThread(i) );
+      MT.Paused := false;
+      MT.Terminate;
+      MT.WaitFor;
+    end;
+  Until i<0;
+
+  step := 'Destroying NodeNotifyEvents';
+  FNodeNotifyEvents.Free;
+  //
+  step := 'Assigning Nil to TNetData';
+  TNetData.NetData.OnReceivedHelloResponse := Nil;
+  TNetData.NetData.OnStatisticsChanged := Nil;
+
+  step := 'Destroying grids operators';
+  FOperationsDBGrid.Free;
+  FBlockChainDBGrid.Free;
+
+  step := 'Ordered Accounts Key list';
+  FreeAndNil(FOrderedAccountsKeyList);
+
+  step := 'Desactivating Node';
+  TNode.Node.NetServer.Active := false;
+  FNode := Nil;
+
+
+  step := 'Processing messages 1';
+  Application.ProcessMessages;
+
+  step := 'Destroying Node';
+  TNode.Node.Free;
+
+  step := 'Destroying Wallet';
+  FWalletKeys.Free;
+  step := 'Processing messages 2';
+  Application.ProcessMessages;
+  step := 'Destroying stringslist';
+  FMemoNetConnections.Free;
+  FMemoBlackListNodes.Free;
+  FMemoAvailableNodeServers.Free;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error destroying Form step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
+    end;
+  End;
+  TLog.NewLog(ltinfo,Classname,'Destroying form - END');
+end;
+
+procedure TFRMWallet.LoadAppParams;
+Var ms : TMemoryStream;
+  s : AnsiString;
+  fvi : TFileVersionInfo;
+begin
+  ms := TMemoryStream.Create;
+  Try
+    s := FAppParams.ParamByName[CT_PARAM_GridAccountsStream].GetAsString('');
+    ms.WriteBuffer(s[1],length(s));
+    ms.Position := 0;
+    FAccountsGrid.LoadFromStream(ms);
+  Finally
+    ms.Free;
+  End;
+  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
+    // New configuration... assigning a new random value
+    fvi := TFolderHelper.GetTFileVersionInfo(Application.ExeName);
+    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+
+      fvi.InternalName+' Build:'+fvi.FileVersion);
+  end;
+  UpdateConfigChanged;
+end;
+
+procedure TFRMWallet.miAboutPascalCoinClick(Sender: TObject);
+begin
+  With TFRMAbout.Create(Self) do
+  try
+    showmodal;
+  finally
+    free;
+  end;
+end;
+
+procedure TFRMWallet.MiCloseClick(Sender: TObject);
+begin
+  Close;
+end;
+
+procedure TFRMWallet.MiDecodePayloadClick(Sender: TObject);
+begin
+  if PageControl.ActivePage=tsOperations then begin
+    FOperationsDBGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+  end else if PageControl.ActivePage=tsPendingOperations then begin
+    FPendingOperationsGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+  end else if PageControl.ActivePage=tsAccountsExplorer then begin
+    FOperationsGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+  end;
+end;
+
+procedure TFRMWallet.miNewOperationClick(Sender: TObject);
+begin
+  With TFRMOperation.Create(Self) do
+  Try
+    SenderAccount := FAccountsGrid.AccountNumber(dgAccounts.Row);
+    Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    WalletKeys := FWalletKeys;
+    ShowModal;
+  Finally
+    Free;
+  End;
+end;
+
+procedure TFRMWallet.miOptionsClick(Sender: TObject);
+begin
+  With TFRMPascalCoinWalletConfig.Create(Self) do
+  try
+    AppParams := Self.FAppParams;
+    WalletKeys := Self.FWalletKeys;
+    if ShowModal=MrOk then begin
+      SaveAppParams;
+      UpdateConfigChanged;
+    end;
+  finally
+    free;
+  end;
+end;
+
+procedure TFRMWallet.miPrivatekeysClick(Sender: TObject);
+Var FRM : TFRMWalletKeys;
+begin
+  FRM := TFRMWalletKeys.Create(Self);
+  Try
+    FRM.WalletKeys := FWalletKeys;
+    FRM.ShowModal;
+    UpdatePrivateKeys;
+  Finally
+    FRM.Free;
+  End;
+end;
+
+procedure TFRMWallet.OnMinerNewBlockFound(sender: TMinerThread; Operations: TPCOperationsComp);
+Var PK : TECPrivateKey;
+  i : Integer;
+begin
+  if FAppParams.ParamByName[CT_PARAM_NewPrivateKeyForEachGeneratedBlock].GetAsBoolean(false) then begin
+    PK := TECPrivateKey.Create;
+    try
+      PK.GenerateRandomPrivateKey(CT_Default_EC_OpenSSL_NID);
+      FWalletKeys.AddPrivateKey('New for miner '+DateTimeToStr(Now), PK);
+      sender.AccountKey := PK.PublicKey;
+    finally
+      PK.Free;
+    end;
+  end else begin
+    If (FWalletKeys.Count>1) then begin
+      i := Random(FWalletKeys.Count);
+      if Assigned(FWalletKeys.Key[i].PrivateKey) then sender.AccountKey := FWalletKeys.Key[i].AccountKey;
+    end;
+  end;
+end;
+
+procedure TFRMWallet.OnNetBlackListUpdated(Sender: TObject);
+Const CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
+Var i,j : integer;
+ P : PNodeServerAddress;
+ l : TList;
+ strings : TStrings;
+begin
+  l := TNetData.NetData.BlackList.LockList;
+  try
+    strings := FMemoBlackListNodes;
+    strings.BeginUpdate;
+    Try
+      strings.Clear;
+      strings.Add('BlackList Updated: '+DateTimeToStr(now)+' by TID:'+IntToHex(TThread.CurrentThread.ThreadID,8));
+      j := 0;
+      for i := 0 to l.Count - 1 do begin
+        P := l[i];
+        if Not P^.its_myself then begin
+          inc(j);
+          strings.Add(Format('Blacklist IP:%s:%d LastConnection:%s Reason: %s',
+            [
+             P^.ip,P^.port,
+             DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection))),P^.BlackListText]));
+        end;
+      end;
+      Strings.Add(Format('Total Blacklisted IPs: %d (Total %d)',[j,l.Count]));
+    Finally
+      strings.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.BlackList.UnlockList;
+  end;
+end;
+
+procedure TFRMWallet.OnNetConnectionsUpdated(Sender: TObject);
+Const CT_BooleanToString : Array[Boolean] of String = ('False','True');
+Var i : integer;
+ NC : TNetConnection;
+ l : TList;
+ strings, sNSC, sRS, sDisc : TStrings;
+begin
+  l := TNetData.NetData.NetConnections.LockList;
+  try
+    strings := FMemoNetConnections;
+    sNSC := TStringList.Create;
+    sRS := TStringList.Create;
+    sDisc := TStringList.Create;
+    strings.BeginUpdate;
+    Try
+      for i := 0 to l.Count - 1 do begin
+        NC := l[i];
+        if NC.Connected then begin
+          if NC is TNetServerClient then begin
+            sNSC.Add(Format('Client: IP:%s:%s Sent/Received:%d/%d Bytes',
+              [NC.Client.RemoteHost,NC.Client.RemotePort,NC.Client.BytesSent,NC.Client.BytesReceived]));
+          end else begin
+            if NC.IsMyselfServer then sNSC.Add(Format('MySelf IP:%s:%s Sent/Received:%d/%d Bytes',
+              [NC.Client.RemoteHost,NC.Client.RemotePort,NC.Client.BytesSent,NC.Client.BytesReceived]))
+            else begin
+              sRS.Add(Format('Remote Server: IP:%s:%s Sent/Received:%d/%d Bytes',
+              [NC.Client.RemoteHost,NC.Client.RemotePort,NC.Client.BytesSent,NC.Client.BytesReceived]));
+            end;
+          end;
+        end else begin
+          if NC is TNetServerClient then begin
+            sDisc.Add(Format('Disconnected client: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]));
+          end else if NC.IsMyselfServer then begin
+            sDisc.Add(Format('Disconnected MySelf IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]));
+          end else begin
+            sDisc.Add(Format('Disconnected Remote Server: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort,CT_BooleanToString[NC.Connected]]));
+          end;
+        end;
+      end;
+      strings.Clear;
+      strings.Add(Format('Connections Updated %s Clients:%d Servers:%d',[DateTimeToStr(now),sNSC.Count,sRS.Count]));
+      strings.AddStrings(sNSC);
+      strings.AddStrings(sRS);
+      if sDisc.Count>0 then begin
+        strings.Add('');
+        strings.Add('Disconnected connections: '+Inttostr(sDisc.Count));
+        strings.AddStrings(sDisc);
+      end;
+    Finally
+      strings.EndUpdate;
+      sNSC.Free;
+      sRS.Free;
+      sDisc.Free;
+    End;
+    CheckMining;
+  finally
+    TNetData.NetData.NetConnections.UnlockList;
+  end;
+end;
+
+procedure TFRMWallet.OnNetNodeServersUpdated(Sender: TObject);
+Var i : integer;
+ P : PNodeServerAddress;
+ l : TList;
+ strings : TStrings;
+ s : String;
+begin
+  l := TNetData.NetData.NodeServers.LockList;
+  try
+    strings := FMemoAvailableNodeServers;
+    strings.BeginUpdate;
+    Try
+      strings.Clear;
+      strings.Add('NodeServers Updated: '+DateTimeToStr(now));
+      for i := 0 to l.Count - 1 do begin
+        P := l[i];
+        s := Format('Server IP:%s:%d',[P^.ip,P^.port]);
+        if Assigned(P.netConnection) then begin
+          s := s+ ' ** ACTIVE **';
+        end;
+        if P.its_myself then begin
+          s := s+' ** DUPLICATE **';
+        end;
+        if P.last_connection>0 then begin
+          s := s + ' Last Connection: '+DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection)));
+        end;
+        if (Not Assigned(P.netConnection)) AND (P.last_attempt_to_connect>0) then begin
+          s := s + ' Attempting to connect: '+DateTimeToStr(P^.last_attempt_to_connect)+' (Attempts: '+inttostr(P^.total_failed_attemps_to_connect)+')';
+        end;
+        strings.Add(s);
+      end;
+    Finally
+      strings.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.NodeServers.UnlockList;
+  end;
+end;
+
+procedure TFRMWallet.OnNetStatisticsChanged(Sender: TObject);
+Var NS : TNetStatistics;
+begin
+  CheckMining;
+  if Assigned(FNode) then begin
+    If FNode.NetServer.Active then begin
+      StatusBar.Panels[0].Text := 'Active (Port '+Inttostr(FNode.NetServer.Port)+')';
+    end else StatusBar.Panels[0].Text := 'Server stopped';
+    NS := TNetData.NetData.NetStatistics;
+    StatusBar.Panels[1].Text := Format('Connections:%d (%d) Clients:%d Servers:%d - Rcvd:%d Bytes Send:%d Bytes',
+      [NS.ActiveConnections,NS.TotalConnections,NS.ClientsConnections,NS.ServersConnections,NS.BytesReceived,NS.BytesSend]);
+  end else begin
+    StatusBar.Panels[0].Text := '';
+    StatusBar.Panels[1].Text := '';
+  end;
+end;
+
+procedure TFRMWallet.OnNewAccount(Sender: TObject);
+begin
+  UpdateAccounts;
+  UpdateBlockChainState;
+end;
+
+procedure TFRMWallet.OnNewLog(logtype: TLogType; Time : TDateTime; ThreadID : Cardinal; const sender,logtext: AnsiString);
+Var s : AnsiString;
+begin
+  if (logtype=ltdebug) And (Not cbShowDebugLogs.Checked) then exit;
+  if ThreadID=MainThreadID then s := ' MAIN:' else s:=' TID:';
+  if MemoLogs.Lines.Count>300 then begin
+    // Limit max lines in logs...
+    memoLogs.Lines.BeginUpdate;
+    try
+      while memoLogs.Lines.Count>250 do memoLogs.Lines.Delete(0);
+    finally
+      memoLogs.Lines.EndUpdate;
+    end;
+  end;
+  memoLogs.Lines.Add(formatDateTime('dd/mm/yyyy hh:nn:ss.zzz',Time)+s+IntToHex(ThreadID,8)+' ['+CT_LogType[Logtype]+'] <'+sender+'> '+logtext);
+  //
+end;
+
+procedure TFRMWallet.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+Var s : String;
+begin
+  s := DateTimeToStr(now)+' Received from '+NetConnection.Client.RemoteHost+':'+NetConnection.Client.RemotePort+' < ';
+  s := s + MessageData + ' (Hexadecimal: '+TCrypto.ToHexaString(MessageData)+') Length '+inttostr(Length(MessageData))+' bytes';
+  memoMessages.Lines.Add(s);
+  if FAppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
+    s := DateTimeToStr(now)+' Message from '+NetConnection.Client.RemoteHost+':'+NetConnection.Client.RemotePort+#10+
+       'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
+    if TCrypto.IsHumanReadable(MessageData) then begin
+       s := s + MessageData;
+    end else begin
+       s := s +'Value in hexadecimal:'+#10+
+            TCrypto.ToHexaString(MessageData);
+    end;
+    Application.MessageBox(PChar(s),PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+  end;
+end;
+
+procedure TFRMWallet.OnReceivedHelloResponse(Sender: TObject);
+begin
+  CheckMining;
+end;
+
+procedure TFRMWallet.OnWalletChanged(Sender: TObject);
+begin
+  UpdatePrivateKeys;
+end;
+
+procedure TFRMWallet.PageControlChange(Sender: TObject);
+begin
+  MiDecodePayload.Enabled := false;
+  if PageControl.ActivePage=tsOperations then begin
+    FOperationsDBGrid.Node := FNode;
+    MiDecodePayload.Enabled := true;
+  end else FOperationsDBGrid.Node := Nil;
+  if PageControl.ActivePage=tsBlockChain then FBlockChainDBGrid.Node := FNode
+  else FBlockChainDBGrid.Node := Nil;
+  if PageControl.ActivePage=tsPendingOperations then begin
+    FPendingOperationsGrid.Node := FNode;
+    MiDecodePayload.Enabled := true;
+  end else FPendingOperationsGrid.Node := Nil;
+  if PageControl.ActivePage=tsAccountsExplorer then begin
+    FAccountsGrid.Node := FNode;
+    MiDecodePayload.Enabled := true;
+  end else FAccountsGrid.Node := Nil;
+  if PageControl.ActivePage=tsMessages then begin
+    UpdateAvailableConnections;
+  end;
+end;
+
+procedure TFRMWallet.SaveAppParams;
+Var ms : TMemoryStream;
+  s : AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  Try
+    FAccountsGrid.SaveToStream(ms);
+    ms.Position := 0;
+    setlength(s,ms.Size);
+    ms.ReadBuffer(s[1],ms.Size);
+    FAppParams.ParamByName[CT_PARAM_GridAccountsStream].SetAsString(s);
+  Finally
+    ms.Free;
+  End;
+end;
+
+procedure TFRMWallet.TimerUpdateStatusTimer(Sender: TObject);
+begin
+  UpdateConnectionStatus;
+  UpdateBlockChainState;
+  UpdateNodeStatus;
+  If (FMemoNetConnections.UpdateCount=0) And (FMemoNetConnections.Count>0) then begin
+    memoNetConnections.Lines.Assign(FMemoNetConnections);
+    FMemoNetConnections.Clear;
+  end;
+  If (FMemoAvailableNodeServers.UpdateCount=0) And (FMemoAvailableNodeServers.Count>0) then begin
+    memoNetServers.Lines.Assign(FMemoAvailableNodeServers);
+    FMemoAvailableNodeServers.Clear;
+  end;
+  If (FMemoBlackListNodes.UpdateCount=0) And (FMemoBlackListNodes.Count>0) then begin
+    memoNetBlackLists.Lines.Assign(FMemoBlackListNodes);
+    FMemoBlackListNodes.Clear;
+  end;
+end;
+
+procedure TFRMWallet.TrayIconDblClick(Sender: TObject);
+begin
+  TrayIcon.Visible := False;
+  Show();
+  WindowState := wsNormal;
+  Application.BringToFront();
+end;
+
+procedure TFRMWallet.UpdateAccounts;
+Var accl : TOrderedCardinalList;
+  l : TList;
+  i,j,k : Integer;
+begin
+  If Not Assigned(FOrderedAccountsKeyList) Then exit;
+  accl := FAccountsGrid.LockAccountsList;
+  Try
+    accl.Clear;
+    if cbMyPrivateKeys.ItemIndex<0 then exit;
+    if cbMyPrivateKeys.ItemIndex=0 then begin
+      // All keys in the wallet
+      for i := 0 to FWalletKeys.Count - 1 do begin
+        j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+        if (j>=0) then begin
+          l := FOrderedAccountsKeyList.AccountKeyList[j];
+          for k := 0 to l.Count - 1 do begin
+            accl.Add(Cardinal(l[k]));
+          end;
+        end;
+      end;
+    end else begin
+      i := cbMyPrivateKeys.ItemIndex-1;
+      j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+      if (j>=0) then begin
+        l := FOrderedAccountsKeyList.AccountKeyList[j];
+        for k := 0 to l.Count - 1 do begin
+          accl.Add(Cardinal(l[k]));
+        end;
+      end;
+    end;
+  Finally
+    FAccountsGrid.UnlockAccountsList;
+  End;
+  UpdateOperations;
+end;
+
+procedure TFRMWallet.UpdateAvailableConnections;
+Var i : integer;
+ NC : TNetConnection;
+ l : TList;
+begin
+  l := TNetData.NetData.NetConnections.LockList;
+  try
+    lbNetConnections.Items.BeginUpdate;
+    Try
+      lbNetConnections.Items.Clear;
+      for i := 0 to l.Count - 1 do begin
+        NC := l[i];
+        if NC.Connected then begin
+          if NC is TNetServerClient then begin
+            if Not NC.IsMyselfServer then begin
+              lbNetConnections.Items.AddObject(Format('Client: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]),NC);
+            end;
+          end else begin
+            if Not NC.IsMyselfServer then begin
+              lbNetConnections.Items.AddObject(Format('Server: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]),NC);
+            end;
+          end;
+        end;
+      end;
+    Finally
+      lbNetConnections.Items.EndUpdate;
+    End;
+  finally
+    TNetData.NetData.NetConnections.UnlockList;
+  end;
+end;
+
+procedure TFRMWallet.UpdateBlockChainState;
+Var isMining : boolean;
+  hr : Int64;
+  pc : Int64;
+  mc : Integer;
+  s : String;
+  mtl : TList;
+  f, favg : real;
+begin
+  UpdateNodeStatus;
+  if Assigned(FNode) then begin
+    if FNode.Bank.BlocksCount>0 then begin
+      lblCurrentBlock.Caption :=  Inttostr(FNode.Bank.BlocksCount)+' (0..'+Inttostr(FNode.Bank.BlocksCount-1)+')'; ;
+    end else lblCurrentBlock.Caption :=  '(none)';
+    lblCurrentAccounts.Caption := Inttostr(FNode.Bank.AccountsCount);
+    lblCurrentBlockTime.Caption := UnixTimeToLocalElapsedTime(FNode.Bank.LastOperationBlock.timestamp);
+    lblOperationsPending.Caption := Inttostr(FNode.Operations.Count);
+    lblCurrentDifficulty.Caption := InttoHex(FNode.Operations.OperationBlock.compact_target,8);
+    favg := FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
+    f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
+    lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
+    if favg>=CT_NewLineSecondsAvg then begin
+      lblTimeAverage.Font.Color := clNavy;
+    end else begin
+      lblTimeAverage.Font.Color := clOlive;
+    end;
+    lblTimeAverageAux.Caption := Format('Last %d: %s sec. - Last %d: %s sec. - Last %d: %s sec.',[
+        CT_CalcNewTargetBlocksAverage * 2 ,FormatFloat('0.0',FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage * 2)),
+        CT_CalcNewTargetBlocksAverage DIV 2,FormatFloat('0.0',FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 2)),
+        CT_CalcNewTargetBlocksAverage DIV 4,FormatFloat('0.0',FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 4))]);
+    mtl := FNode.MinerThreads.LockList;
+    try
+      mc := mtl.Count;
+      If mc>0 then begin
+        isMining := Not TMinerThread(mtl[0]).Paused;
+        hr := TMinerThread(mtl[0]).HashRate;
+        pc := TMinerThread(mtl[0]).PlayCount;
+      end else isMining :=false;
+    finally
+      FNode.MinerThreads.UnlockList;
+    end;
+  end else begin
+    isMining := false;
+    lblCurrentBlock.Caption := '';
+    lblCurrentAccounts.Caption := '';
+    lblCurrentBlockTime.Caption := '';
+    lblOperationsPending.Caption := '';
+    lblCurrentDifficulty.Caption := '';
+    lblTimeAverage.Caption := '';
+    lblTimeAverageAux.Caption := '';
+  end;
+  if isMining then begin
+    if mc>1 then s := inttostr(mc)+' Miners at '
+    else s := 'Mining at ';
+    lblMiningStatus.Caption := s +FormatFloat('0.0',hr / 1024)+' Kh/s Rounds: '+FormatFloat('0.0',pc / 1000000)+' G';
+    lblMiningStatus.Font.Color := clNavy
+  end else begin
+    lblMiningStatus.Caption := 'Not mining';
+    lblMiningStatus.Font.Color := clRed;
+  end;
+
+end;
+
+procedure TFRMWallet.UpdateConfigChanged;
+Var wa : Boolean;
+begin
+  tsLogs.TabVisible := FAppParams.ParamByName[CT_PARAM_ShowLogs].GetAsBoolean(false);
+  if (Not tsLogs.TabVisible) then begin
+    FLog.OnNewLog := Nil;
+    if PageControl.ActivePage = tsLogs then PageControl.ActivePage := tsAccountsExplorer;
+  end else FLog.OnNewLog := OnNewLog;
+  if FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false) then begin
+    FLog.SaveTypes := CT_TLogTypes_DEFAULT;
+    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+'\PascalCointWallet.log';
+  end else begin
+    FLog.SaveTypes := [];
+    FLog.FileName := '';
+  end;
+  if Assigned(FNode) then begin
+    wa := FNode.NetServer.Active;
+    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    FNode.NetServer.Active := wa;
+    FNode.Operations.BlockPayload := FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('');
+  end;
+  cbAllowMining.Checked :=  (FAppParams.ParamByName[CT_PARAM_AutomaticMineWhenConnectedToNodes].GetAsBoolean(true));
+end;
+
+procedure TFRMWallet.UpdateConnectionStatus;
+Var
+  NS : TNetStatistics;
+  errors : AnsiString;
+
+Var i : integer;
+ NC : TNetConnection;
+ l : TList;
+begin
+  UpdateNodeStatus;
+  OnNetStatisticsChanged(Nil);
+  if Assigned(FNode) then begin
+    if FNode.IsBlockChainValid(errors) then begin
+      StatusBar.Panels[2].Text := Format('Last Account time:%s',
+       [FormatDateTime('dd/mm/yyyy hh:nn:ss',UnivDateTime2LocalDateTime(UnixToUnivDateTime( FNode.Bank.LastOperationBlock.timestamp )))]);
+    end else begin
+      StatusBar.Panels[2].Text := 'NO BLOCKCHAIN: '+errors;
+    end;
+  end else begin
+    StatusBar.Panels[2].Text := '';
+  end;
+end;
+
+procedure TFRMWallet.UpdateNodeStatus;
+Var status : AnsiString;
+begin
+  If Not Assigned(FNode) then begin
+    lblNodeStatus.Font.Color := clRed;
+    lblNodeStatus.Caption := 'Initializing...';
+  end else begin
+    If FNode.IsReady(status) then begin
+      if TNetData.NetData.NetStatistics.ActiveConnections>0 then begin
+        lblNodeStatus.Font.Color := clGreen;
+        if TNetData.NetData.IsDiscoveringServers then begin
+          lblNodeStatus.Caption := 'Discovering servers';
+        end else if TNetData.NetData.IsGettingNewBlockChainFromClient then begin
+          lblNodeStatus.Caption := 'Obtaining new BlockChain';
+        end else begin
+          lblNodeStatus.Caption := 'Running';
+        end;
+      end else begin
+        lblNodeStatus.Font.Color := clRed;
+        lblNodeStatus.Caption := 'Alone in the world...';
+      end;
+    end else begin
+      lblNodeStatus.Font.Color := clRed;
+      lblNodeStatus.Caption := status;
+    end;
+  end;
+end;
+
+procedure TFRMWallet.UpdateOperations;
+Var accn : Int64;
+begin
+  accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  FOperationsGrid.AccountNumber := accn;
+end;
+
+procedure TFRMWallet.UpdatePrivateKeys;
+Var i,last_i : Integer;
+  wk : TWalletKey;
+  s : AnsiString;
+begin
+  If (Not Assigned(FOrderedAccountsKeyList)) And (Assigned(FNode)) Then begin
+    FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(FNode.Bank.SafeBox,false);
+  end;
+  last_i := cbMyPrivateKeys.ItemIndex;
+  cbMyPrivateKeys.items.BeginUpdate;
+  Try
+    cbMyPrivateKeys.Items.Clear;
+    cbMyPrivateKeys.Items.AddObject('(All my private keys)',TObject(-1));
+    For i:=0 to FWalletKeys.Count-1 do begin
+      wk := FWalletKeys.Key[i];
+      if assigned(FOrderedAccountsKeyList) then begin
+        FOrderedAccountsKeyList.AddAccountKey(wk.AccountKey);
+      end;
+      if (wk.Name='') then begin
+        s := 'Sha256='+TCrypto.ToHexaString( TCrypto.DoSha256( TAccountComp.AccountKey2RawString(wk.AccountKey) ) );
+      end else begin
+        s := wk.Name;
+      end;
+      if Not Assigned(wk.PrivateKey) then s := s + '(*)';
+      cbMyPrivateKeys.Items.AddObject(s,TObject(i));
+    end;
+  Finally
+    cbMyPrivateKeys.Items.EndUpdate;
+  End;
+  if last_i<0 then last_i := 0;
+  if cbMyPrivateKeys.Items.Count>last_i then cbMyPrivateKeys.ItemIndex := last_i
+  else if cbMyPrivateKeys.Items.Count>=0 then cbMyPrivateKeys.ItemIndex := 0;
+end;
+
+end.

+ 736 - 0
Units/Forms/UFRMWalletKeys.dfm

@@ -0,0 +1,736 @@
+object FRMWalletKeys: TFRMWalletKeys
+  Left = 0
+  Top = 0
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsSingle
+  Caption = 'Wallet keys'
+  ClientHeight = 478
+  ClientWidth = 569
+  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 lblEncryptionTypeCaption: TLabel
+    Left = 30
+    Top = 298
+    Width = 80
+    Height = 13
+    Caption = 'Encryption type:'
+  end
+  object lblEncryptionType: TLabel
+    Left = 125
+    Top = 298
+    Width = 54
+    Height = 13
+    Caption = '000000000'
+  end
+  object lblKeyNameCaption: TLabel
+    Left = 30
+    Top = 317
+    Width = 51
+    Height = 13
+    Caption = 'Key name:'
+  end
+  object lblKeyName: TLabel
+    Left = 125
+    Top = 317
+    Width = 329
+    Height = 13
+    AutoSize = False
+    Caption = '000000000'
+  end
+  object lblPrivateKeyCaption: TLabel
+    Left = 30
+    Top = 336
+    Width = 58
+    Height = 13
+    Caption = 'Private key:'
+  end
+  object lblPrivateKeyCaption2: TLabel
+    Left = 30
+    Top = 355
+    Width = 80
+    Height = 13
+    Caption = '(In hexa format)'
+  end
+  object lblKeysEncrypted: TLabel
+    Left = 30
+    Top = 15
+    Width = 346
+    Height = 39
+    AutoSize = False
+    Caption = 'lblKeysEncrypted'
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+    WordWrap = True
+  end
+  object lbWalletKeys: TListBox
+    Left = 30
+    Top = 60
+    Width = 346
+    Height = 227
+    ItemHeight = 13
+    TabOrder = 0
+    OnClick = lbWalletKeysClick
+  end
+  object bbExportPrivateKey: TBitBtn
+    Left = 382
+    Top = 136
+    Width = 159
+    Height = 36
+    Caption = 'Export Private key'
+    DoubleBuffered = True
+    Glyph.Data = {
+      76060000424D7606000000000000360400002800000018000000180000000100
+      0800000000004002000000000000000000000001000000010000FF00FF005251
+      5100565758005A5958005B5D5D005D5E5E006361610066666600696969006C6B
+      6B0073706F0072707000747271000E80AA001788AF000B86B2000C85B000098A
+      B700078CBA00198EB7000E91BE001E94BC002898BA003E99BB000493C4000997
+      C7000A9ECF00229BC20035A0C30030AACA0032AACE003DAACC003FB0CB0036A9
+      D0003BB1D9003DBDDA0039B8DE0029BBE0004DBBD90068B0CA0019C7F1001BD4
+      FE002FC6E7003ECDE9002CD3F9002ED8FE003CE0FE0054C0D40059C4D70059C2
+      DD006FC2D6007CC0D50078C1D80049DDFE0054DFFE0061C6E5006CD4EE0064D9
+      E90076DFE9007DDFE90055E0FF0058E0FE006BE3F40063E6FE0060ECFE0075E2
+      FA0077E6FF007BE8FF0071F2FE0088828000928E8C0096908F00969291009794
+      94009E969400A09C9B00A19F9E00ACA09E00A6A2A100A6A4A400ADA5A300AAA5
+      A400ABA8A800ADAAA900B0A5A300B2A7A500B2ABAB00B4ABA900BBB1AE00BEB1
+      AF00BAB3B200BAB4B200BEB6B400C4B8B600C1BCBB0083DFE9008ADAEB0094DF
+      E90089DDF100A4DFEA009AE2F40095EBFD0095EDFE009EEDFF0084F3F90080F5
+      FC008FF6FB0083F8FE0093F2FE0097F9FE0094FDFE0099FFFE009EFFFE00AAEF
+      FD00B4EEF300A4FFFE00A8F9FE00ADFBFE00AAFFFE00B6F1FF00BCF2F800B3F9
+      FF00B3FFFE00B9FFFE00C7C2C100C7C6C600CCC4C200CAC7C600CFCCCC00D1C7
+      C500D4CBC900D1CDCD00D5CFCE00D8D0CE00D7D1D000D9D5D400DCD7D600DFD8
+      D600E3DEDD00C3DFEA00CCF6FF00C9FFFE00CFFFFF00D1F7FF00DBF8FE00DFFF
+      FF00E7E3E200E9E5E500ECE9E900EFEEED00F1EBEA00F1EFEE00E5FFFF00F3F1
+      F100F6F2F000F8F5F300F8F5F400F0F7FA00F5FFFF00FBF8F800FAFFFF00FFFE
+      FE00000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0007070707000000000000000000000000000000000000000746967E07000000
+      0000000000000000000000000000000D085E489A5E080D0D0000000000000000
+      00000000000D0D2626075E48A1071F130D0D000000000000000000000D163940
+      3F35075E4C93070707100D000000000000000000123A6B402E353D075E519580
+      4E0707070707000000000000123A6B402E293D42075E4EA19289858281550700
+      00000000123A6B402E292D4266075E4C9689865A475D4D0700000000123A6B40
+      2E292D3C6671077D928A7D0A0A58580700000000123A6B402E292D3C4371079A
+      958007035151550700000000123A6D6C6667778C8F90079A860701574C024A07
+      0000000012727860311E1B151C3407A17E0B534C000445070000000011322023
+      2A282C36426407539A8882040404570700000000103169402E292D3643716207
+      5393825A515A5A0700000000123A6B402E292D3C436662370707070708070700
+      00000000123A6B402E292D3C43663837222117000000000000000000123A6B40
+      2E292D3C436638231A180D000000000000000000123A6B402E292D3C43663822
+      1A180D000000000000000000123A6B402E292D3C436638231A180D0000000000
+      00000000123B757975756F6F6F6F6A3E25190D000000000000000000128BA190
+      8E79736F6F6F6F6D442B0D00000000000000000012279D9D908E796F6F6F6F6F
+      691E0D000000000000000000001214333363615F3B3A2F2F0E0D000000000000
+      0000000000000014121212121212121200000000000000000000}
+    ParentDoubleBuffered = False
+    TabOrder = 4
+    OnClick = bbExportPrivateKeyClick
+  end
+  object memoPrivateKey: TMemo
+    Left = 125
+    Top = 336
+    Width = 416
+    Height = 80
+    TabStop = False
+    BevelInner = bvNone
+    BevelOuter = bvNone
+    Color = clBtnFace
+    Ctl3D = False
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = []
+    Lines.Strings = (
+      'memoPrivateKey')
+    ParentCtl3D = False
+    ParentFont = False
+    ReadOnly = True
+    TabOrder = 9
+  end
+  object bbChangeName: TBitBtn
+    Left = 460
+    Top = 305
+    Width = 81
+    Height = 25
+    Caption = 'Change Name'
+    DoubleBuffered = True
+    ParentDoubleBuffered = False
+    TabOrder = 8
+    OnClick = bbChangeNameClick
+  end
+  object bbImportPrivateKey: TBitBtn
+    Left = 382
+    Top = 98
+    Width = 159
+    Height = 36
+    Caption = 'Import Private key'
+    DoubleBuffered = True
+    Glyph.Data = {
+      76060000424D7606000000000000360400002800000018000000180000000100
+      0800000000004002000000000000000000000001000000010000FF00FF000571
+      0A0008750D0024B53B0029B942002ABA44002EBD4A002FBF4C0033C1500034C2
+      520035C3540037C5570039C659003BC85C003CC95E003FCC630040CC650045D0
+      6B0046D16C0049D472004BD675004ED8790050D97B0051DA7E000E80AA001788
+      AF000B86B200098AB700078CBA00198EB700118EB9000E91BE001E94BC002898
+      BA0055DD810056DF850058E087000493C4000997C7000C9BCB000A9DCE00229B
+      C20015AFD90026A0C80021AACF0035A0C30030AACA0032AACE003DAACC003FB0
+      CB0031AED7003DBDDA0039B8DE0029BBE00056B1CE0052BDDB0068B0CA006BBD
+      D70019C7F1001BD4FE002FC6E70026C2E9003ECDE9002CD3F9002ED8FE003CE0
+      FE0054C0D40059C4D70059C2DD006FC2D60062C3DE007CC0D5007AC6DF0043C8
+      E90054DFFE0060CEEA0079CFE9006CD4EE0064D9E90076DFE9007DDFE90055E0
+      FF006BE3F40060ECFE0075E2FA007BE8FF0071F2FE008FD0E60083DFE9008ADA
+      EB0094DFE90081DBF100A4DFEA0095EBFD0095EEFE009EEDFF0084F3F90080F5
+      FC008FF6FB0083F8FE0093F2FE0097F9FE0094FDFE0099FFFE009EFFFE00AFE8
+      F600B4EEF300A4FFFE00A8F9FE00ADFBFE00AAFFFE00B6F1FF00BCF2F800B3F9
+      FF00B3FFFE00B9FFFE00C3DFEA00CCF6FF00C9FFFE00CFFFFF00D1F6FE00DFFF
+      FF00E5FFFF00F0F7FA00F5FFFF00FAFFFF000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000202020200000000000000000000000000000000000000
+      0002040302000000000000000000001818181818181818180002060402000000
+      000000000018182C2C2A3D494B46301D18020906020000000000000018214E53
+      413B4051555D4D3427020D0902000000000000001C4F6353413B405102020202
+      02020F0D02020202020200001C4F6353413B405102242317151311100D090906
+      040200001C4F6353413B40510224242323151311100F0D09060200001C4F6353
+      413B4051020101010102171302010101020100001C4F6353413B4051555D4D34
+      2702231702000000000000001C4F65645D5F6F757878694C3202242302000000
+      000000001C6A7059442F29202D3639485702242402000000000000001B453134
+      3C3A3F4A545B372B1E02242402000000000000001A436153413B4051555D4D34
+      2701020202000000000000001C4F6353413B4051555D4D342725180000000000
+      000000001C4F6353413B4051555D4D342725180000000000000000001C4F6353
+      413B4051555D4D342725180000000000000000001C4F6353413B4051555D4D34
+      2825180000000000000000001C4F6353413B4051555D4D342725180000000000
+      000000001C506D716D6D6767676762523527180000000000000000001C747D7A
+      77716B6767676765563E180000000000000000001C387B7C7977716767676767
+      612F18000000000000000000001C1F47475C5A58504F42421918000000000000
+      000000000000001C1C1C1C1C1C1C1C1C00000000000000000000}
+    ParentDoubleBuffered = False
+    TabOrder = 3
+    OnClick = bbImportPrivateKeyClick
+  end
+  object bbExportPublicKey: TBitBtn
+    Left = 382
+    Top = 212
+    Width = 159
+    Height = 36
+    Caption = 'Export Public key'
+    DoubleBuffered = True
+    Glyph.Data = {
+      F6060000424DF606000000000000360000002800000018000000180000000100
+      180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C8518FF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+      9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C8518
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+      FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+      00FF0C85181399220C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF0C85180C85180C85180C85
+      180C85180C85180C85180C85181DAC31139A220C8518FF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE0C8518
+      57E38851DD7E4AD77443D0693BC95E34C1522BBA4725B33C1EAE33149B230C85
+      18FF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+      DEFE77DEFE0C85185EE89059E48953DE804CD87645D16C3DCA6035C2542DBB49
+      26B53F1FAF35149B250C8518FF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+      F38FE8FF7CE2FE7CE3FE7CE3FE0C851861EB955FE9925AE58B54DF824DD97846
+      D26D3ECB6237C4562FBD4C27B64021B037159B250C8518FF00FFFF00FF019ACF
+      60CAEF1FA8D85EC1E1C2E6ED8ACEE08FCFE18ECFE10C851861EB9561EB955FEA
+      935CE58D56E0844FDB7A48D47040CD6538C65931BF4D1DA3320C8518FF00FFFF
+      00FFFF00FF019ACF65CFF53EB7E52CA9D4C5EFF8ACF3FEA5F2FFA5F2FF0C8518
+      61EB9561EB9561EB9561EB945CE68E57E18650DC7C49D57242CE6727AD410C85
+      18FF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F32A9CC673CBE7D6FEFDB1
+      FBFDB2FBFD0C85180C85180C85180C85180C85180C85180C85180C851852DD7F
+      32B6500C851898FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB748E
+      A224A8D5B9E7F3D5F5F9D5F6F9D6F6FADCFAFBCDFDFCB9FCFCAFFAFCB0FAFCB1
+      FAFC0C85183ABE5C0C85189FFCFFA4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+      8BDBFF5FCDFFB7898973C3DD18A2D218A2D216A2D215A1D21AA4D391D7EBEBFE
+      FDDBFDFCC5FBFBC2FBFB0C85180C851883E4F3B6FDFFBAFFFFB5FCFD019ACFFF
+      00FFFF00FF019ACF99E2FF67D3FFB88989FEF5ECFDF3EBF0EFEAE5EBE8D6E5E6
+      A4D2E025A6D34DB9DDE5F8FBF5FDFCEBFCFB0C8518C4FBFF9CE4F2DAFEFFD9FE
+      FFE3FFFFADE9F5019ACFFF00FF019ACF9FE9FF70DCFFB88989FEF3E9FFF2E6FE
+      F3E9FEF3E9FEF3E9FEF3E9D4E4E439ADD422A5D49DD8ECF1F9FBEEEFEFE9FDFF
+      CEEEF7F8FFFFF7FFFFFEFFFFE9F9FD019ACFFF00FF019ACFA7EFFF76E5FFB889
+      89FFF2E5FFF0E2FFF2E5FFF2E5FFF2E5FFF2E5FFF2E5EAEBE38EC9DA44B0D501
+      9ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACFFF00FF019ACF
+      ABF6FF7EEDFFB88989FFF0E2FFEFDFFFF0E2FFF0E2FFF0E2FFF0E2FFF0E2FEEE
+      E0FBECDEFAEBDEF6E6D9B8898993F7FF019ACFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACFC7FFFF82F5FFB88989FFEEDFFFECDBFFEEDFFFEEDFFFEEDF
+      FFEEDFF9E8D9DECCC1D9CABDCFBDB4C8B3ACB88989B5FFFF019ACFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFFB88989FFECDBFFEBD8FF
+      ECDBFFECDBFFECDBFFECDBF5E2D2C4ABA7C2A8A5BBA39FC2AFA9B88989019ACF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFB889
+      89FFEBD8FFEAD5FFEBD8FFEBD8FFEBD8FFEBD8FFEBD8D9C8C5FEFEFDFEF6EFDE
+      C9C0B88989FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FF019ACFB88989FFE9D5FFE8D3FFE9D5FFE9D5FFE9D5FFE9D5FFE9D5C6AD
+      A9FEF8F2E8D4CACD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFB88989FFE7D1FFE7D0FFE7D1FFE7D1FFE7D1
+      FFE7D1E7CEBFD3BFB9E8D5CCCD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB88989FFE6CFFFE6CFFF
+      E6CFFFE6CFFFE6CFFFE6CFD5BBB2E0CCC5CD9999FF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB889
+      89B88989B88989B88989B88989B88989B88989B88989B88989FF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+    ParentDoubleBuffered = False
+    TabOrder = 6
+    OnClick = bbExportPublicKeyClick
+  end
+  object bbImportPublicKey: TBitBtn
+    Left = 382
+    Top = 174
+    Width = 159
+    Height = 36
+    Caption = 'Import Public key'
+    DoubleBuffered = True
+    Glyph.Data = {
+      F6060000424DF606000000000000360000002800000018000000180000000100
+      180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+      9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+      FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF74DBFF7ADEFF79DDFF73D9
+      FF5CCCF522ACDD019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE71D9FE
+      71D9FE71D9FE73DAFE76DCFF7BDFFF7ADEFF78DCFF77DCFF019ACFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+      DEFE77DEFE77DEFE77DEFE77DEFE77DEFE76DEFE76DEFE76DEFE78DFFF7CE1FF
+      65D2F8019ACFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+      F38FE8FF7CE2FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7C
+      E3FE7DE4FE7DE3FE5ED1F3019ACFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      60CAEF1FA8D85EC1E2BBF4FE7DE7FE82E8FE81E8FE81E8FE81E8FE81E8FE81E8
+      FE81E8FE81E8FE81E8FE82E8FE84E9FE5ED3F18DEEFF019ACFFF00FFFF00FFFF
+      00FFFF00FF019ACF65CFF53EB7E529ACD8BFEEF88DEFFF85EDFF85EDFF85EDFF
+      85EDFF86EDFF86EDFF86EDFF86EDFF86EDFF87EDFF89EEFF65D9F396F5FF019A
+      CFFF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F31FA7D773CBE7C5FCFF93
+      F7FF93F7FF92F6FF8DF4FF89F3FF89F2FF8BF2FF8BF2FF8BF2FF8BF2FF8EF3FF
+      6ADEF395F8FF98FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB3EB8
+      E920A7D5B6E6F3D0F4FAD1F5FAD2F6FAD5F9FCB9FBFE9BF8FF8FF6FF91F6FF92
+      F6FF93F7FF6BD0B70C85188BEAE0A4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+      8BDBFF5FCDFF64CDFE2CAFE30D9FD30FA0D310A0D310A0D317A3D38ED7ECE2FD
+      FEC3FAFFA5F8FFA3F8FF84DDCF0C851838B5570C8518ABF3EBB5FCFD019ACFFF
+      00FFFF00FF019ACF99E2FF67D3FF6DD4FE6CD4FE69D1FE64CEFB61CDF95BC9F5
+      48BEEB17A3D54BB8DDDFF7FBE8FCFFB1E7DD0C85184ACE7361EB9541C1640C85
+      18D6F6F0ADE9F5019ACFFF00FF019ACF9FE9FF70DCFF76DDFE76DDFE76DDFE75
+      DCFE74DCFE73DCFE73DBFE61CEF61CA8D91CA5D58CCED70C851842C5665BE68C
+      59E1895DE78F3EBD600C8518DBF1EF019ACFFF00FF019ACFA7EFFF76E5FF7CE5
+      FF7CE5FF7CE5FF7CE5FF7DE5FF7DE5FF7DE5FF7DE3FF72DDFB40B8D20C85182D
+      AD474AD47250D97B55DE8359E1885AE38B33AF510C85180197C3FF00FF019ACF
+      ABF6FF7EEDFF85ECFF85ECFF85ECFF85ECFF84ECFF80ECFF7CECFF7DECFF7EEC
+      FF0C85180C85180C85180C851840C7634FDA7A55DF830C85180C85180C85180C
+      8518FF00FF019ACFC7FFFF82F5FF8FF5FF8FF5FF8FF5FF8EF5FF8DF4FFA0FDFF
+      B7FFFFAFFFFFAEFFFFA6F9F4A5FBF8A3FCFA4CB07732B74F48D6704AD3720C85
+      18FF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFF8AFCFF90FCFF90FCFF90
+      FCFF99FDFF86E8F5019ACF019ACF019ACF019ACF019ACF019ACF0486642CB347
+      41D16636BC540C8518FF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFBCFF
+      FFBCFFFFBCFFFFC0FFFF9DF5FB019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FF0C85182BB74538C9580C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FF019ACF019ACF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FF0C851828BB4126B13E0C8518FF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FF0C851814A4241CAE310C8518FF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C85180C96170D991A0C8518
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C85180C85180C85180C
+      8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+    ParentDoubleBuffered = False
+    TabOrder = 5
+    OnClick = bbImportPublicKeyClick
+  end
+  object bbGenerateNewKey: TBitBtn
+    Left = 382
+    Top = 60
+    Width = 159
+    Height = 36
+    Caption = 'Generate a new Key'
+    DoubleBuffered = True
+    Glyph.Data = {
+      76060000424D7606000000000000360400002800000018000000180000000100
+      0800000000004002000000000000000000000001000000010000FF00FF007D33
+      2F00993300000C35EB000335FB001342FB00224EFB00325BFC005274FC000E80
+      AA001788AF000B86B200098AB700078CBA00078EBD00118EB9000E91BE001E94
+      BC002898BA000493C4000997C7000A9DCE00229BC20015AFD90026A0C80021AA
+      CF0035A0C30030AACA0032AACE003FB0CB003DBDDA0039B8DE0029BBE00046A6
+      C90056B1CE0052BDDB0068B0CA006BBDD700718DFC0019C7F1001BD4FE002FC6
+      E70026C2E9003ECDE9002CD3F9002ED8FE003CE0FE0054C0D40059C4D70059C2
+      DD006FC2D6007CC0D50076C4DF007AC6DF0043C8E90054DFFE0060CEEA006CD4
+      EE0064D9E90076DFE9007DDFE90055E0FF006BE3F40060ECFE0075E2FA007BE8
+      FF0071F2FE008099FC00A0B2FD00AFBFFD008FD0E60083DFE9008ADAEB0094DF
+      E90081DBF100A4DFEA0095EBFD0095EEFE009EEDFF0084F3F90080F5FC008FF6
+      FB0083F8FE0093F2FE0097F9FE0094FDFE0099FFFE009EFFFE00B4EEF300A4FF
+      FE00A8F9FE00ADFBFE00AAFFFE00B6F1FF00BCF2F800B3F9FF00B3FFFE00B9FF
+      FE00C3DFEA00CFD8FD00DFE5FE00CCF6FF00C9FFFE00CFFFFF00D1F7FF00DFFF
+      FF00EEF1FE00E5FFFF00F0F7FA00F5FFFF00FAFFFF00FEFEFE00000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000020202020202020202020200000000000000000000000000026F6F6F6F
+      64076A6F6F0200000000000000090909090909026F6F6F450504446F6F020000
+      00000009091919172A3638026F6F26040404066F6F020000000009123A3F2E28
+      2D3D41026A070404260504456F02000000000D3B523F2E282D3D410206040545
+      6F4304066F02000000000D3B523F2E282D3D41024407636F6F6A050443020000
+      00000D3B523F2E282D3D41026F6F6F6F6F6F44040401000000000D3B523F2E28
+      2D3D41026F6F6F6F6F6F6F080403000000000D3B523F2E282D3D41026F6F6F6F
+      6F6F6F6A0704040000000D3B54534D4E5D656802020202020202020201030404
+      00000D585E48311C16111A2225354634090000000000040404000C321D1E2927
+      2C37404A23180F21090000000000000404040B30503F2E282D3D414D391E150D
+      090000000000000000000D3B523F2E282D3D414D391E15130900000000000000
+      00000D3B523F2E282D3D414D391F1513090000000000000000000D3B523F2E28
+      2D3D414D391F1513090000000000000000000D3B523F2E282D3D414D391E1513
+      090000000000000000000D3B523F2E282D3D414D391F15130900000000000000
+      00000D3C5B5F5B5B56565656513E2014090000000000000000000D626F69675F
+      595656565654422B090000000000000000000D246C6D69675F5656565656501C
+      09000000000000000000000D1033334B49473C3B2F2F0A090000000000000000
+      00000000000D0D0D0D0D0D0D0D0D000000000000000000000000}
+    ParentDoubleBuffered = False
+    TabOrder = 2
+    OnClick = bbGenerateNewKeyClick
+  end
+  object bbDelete: TBitBtn
+    Left = 382
+    Top = 251
+    Width = 159
+    Height = 36
+    Caption = 'Delete'
+    DoubleBuffered = True
+    Glyph.Data = {
+      F6060000424DF606000000000000360000002800000018000000180000000100
+      180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF00009A00009A00039D020DA4020D
+      A400039D00009A00009AFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF00009A020DA4041FB3072FC0
+      0732C20732C20732C20732C2072FC0041FB3020DA400009AFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF00009A01079F0526B807
+      32C20732C20732C20732C20732C20732C20732C20732C20732C20732C20526B8
+      01079F00009AFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF00009A0210
+      A7062FC50732C50732C20732C20732C20732C20732C20732C20732C20732C207
+      32C20732C20732C2072FC00210A700009AFF00FFFF00FFFF00FFFF00FFFF00FF
+      00009A0210A80633D00633CF0632CA0732C60732C20732C20732C20732C20732
+      C20732C20732C20732C20732C20732C20732C20732C20210A700009AFF00FFFF
+      00FFFF00FFFF00FF01079F0530D70533DA0633D50633D00632CB0732C70732C3
+      0732C20732C20732C20732C20732C20732C20732C20732C20732C20732C2072F
+      C001079FFF00FFFF00FFFF00FF00009A0427CC0534E40533DF0533DB022FD512
+      3DD44868DA042FC70430C30732C20732C20631C2002BC03E5FD01F46C8012CC0
+      0732C20732C20732C20526B800009AFF00FFFF00FF010DA90434EC0434EA0534
+      E50533E1113ADDB3C1F3FEFEFF7F95E50029C7042FC40631C20026BE5F7BD7F9
+      FAFDCED6F32146C80732C20732C20732C20732C2020DA4FF00FF00009A0321CB
+      0335F50434F00434EB0534E6224AE5EFF2FDFFFFFFFFFFFF7E95E60029C80024
+      C25F7AD7F9FAFEFFFFFFFFFFFF4464D10732C20732C20732C20732C2041FB300
+      009A00009A0431E90335FA0335F60434F10434EC002DE64164EAEBEEFCFFFFFF
+      FFFFFF718AE35875DCF9FAFEFFFFFFFCFCFE6781D9022CC00732C20732C20732
+      C20732C2072FC000009A00039E0737F30839FB0537FB0335F70434F20434ED00
+      28E64063EAEAEEFDFFFFFFFDFDFFFCFCFFFFFFFFFBFBFE6781DA0027BE0531C2
+      0732C20732C20732C20732C20732C200039D010DAC1241FB1E4AFB0C3CFB0335
+      FB0335F80435F30434EE0027E7385EEBE9EDFDFFFFFFFFFFFFF8F9FE5F7CDF00
+      25C50631C30732C20732C20732C20732C20732C20732C2020DA4020EAD224EFB
+      456AFC204CFB0335FB0335FB0335F90333F50029EF5272F1ECF0FDFFFFFFFFFF
+      FFF8F9FE718BE5002ACD0430CA0732C50732C20732C20732C20732C20732C202
+      0DA400039F2852F86785FD3E65FC0738FB0335FB0234FB002AFA5D7CF9F9FAFF
+      FFFFFFF4F6FEEEF1FDFFFFFFFFFFFF7E96E8002ACE0430CB0732C60732C30732
+      C20732C20732C200039D00009A2048F37792FD6B88FD1946FB0335FB0130FB5D
+      7DFDF9FAFFFFFFFFFCFCFF5978F3395DEDEBEEFDFFFFFFFFFFFF7F96E90631D1
+      0632CC0732C80732C30732C2072FC000009A00009A112ED76B88FD9AAEFD466B
+      FC093AFB214CFBFAFBFFFFFFFFFCFCFF6482FB002AF20027EC4063EEEBEEFDFF
+      FFFFFFFFFF4466E00633D20633CD0732C90732C4041FB300009AFF00FF0511B2
+      456AFCA8B9FE8FA5FD2E58FC0835FB92A7FDF1F4FF6583FD002AFB0234F90435
+      F40028ED4064EFE6EBFCABBAF5113ADE0633D80633D30633CF0632C9020DA4FF
+      00FFFF00FF00009A1B3DE57A94FDBFCCFE89A1FD2A54FC0637FB2652FC0030FB
+      0234FB0335FB0335FA0335F6002DF01F4AEE0A39E70130E20533DE0533D90633
+      D40526BF00009AFF00FFFF00FFFF00FF0108A72F56F792A8FDCDD7FE97ACFD45
+      6AFC1745FB0637FB0335FB0335FB0335FB0335FA0335F70434F20535ED0535E8
+      0534E40533DF0530D301079FFF00FFFF00FFFF00FFFF00FF00009A0717BA3A61
+      FC95AAFDCFD8FEB6C4FE7A94FD456AFC2752FC1745FB103FFB103FFB1543FB1B
+      48F91A46F40D3BEF0434E90533DF0210A900009AFF00FFFF00FFFF00FFFF00FF
+      FF00FF00009A0717BA3158F7839CFDC0CCFECDD7FEB8C6FE99ADFD7E97FD6C89
+      FD6583FD607FFC5073FC2E58FA0F3DF40431E60210AB00009AFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FF00009A0209A81F40E65073FC819AFDA1B4FE
+      AEBEFEA9BAFE9AAEFD839CFD6281FD3860FC1644F90529D90107A200009AFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF00009A07
+      13B41733DA2C53F73A61FB4066FC3961FC2A54F81741F00826D2010DAD00009A
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FF00009A00009A0105A0020EB1010DB00003A000009A00
+      009AFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+    ParentDoubleBuffered = False
+    TabOrder = 7
+    OnClick = bbDeleteClick
+  end
+  object bbUpdatePassword: TBitBtn
+    Left = 382
+    Top = 8
+    Width = 159
+    Height = 38
+    Caption = 'Password'
+    DoubleBuffered = True
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -13
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    Glyph.Data = {
+      76060000424D7606000000000000360400002800000018000000180000000100
+      0800000000004002000000000000000000000001000000010000000000000101
+      0100020202000303030004040400050505000606060007070700080808000909
+      09000A0A0A000B0B0B000C0C0C000D0D0D000E0E0E000F0F0F00101010001111
+      1100121212001313130014141400151515001616160017171700181818001919
+      19001A1A1A001B1B1B001C1C1C001D1D1D001E1E1E001F1F1F00202020002121
+      2100222222002323230024242400252525002626260027272700282828002929
+      29002A2A2A002B2B2B002C2C2C002D2D2D002E2E2E002F2F2F00303030003131
+      3100323232003333330034343400353535003636360037373700383838003939
+      39003A3A3A003B3B3B003C3C3C003D3D3D003E3E3E003F3F3F00404040004141
+      4100424242004343430044444400454545004646460047474700484848004949
+      49004A4A4A004B4B4B004C4C4C004D4D4D004E4E4E004F4F4F00505050005151
+      5100525252005353530054545400555555005656560057575700585858005959
+      59005A5A5A005B5B5B005C5C5C005D5D5D005E5E5E00685968007C4F7C009441
+      9400B72CB700DD15DD00F506F500FD01FD00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00
+      FE00FE00FE00FE00FE00FE00FE00F008F900D517F000BD24E800A830E1007B4B
+      D0005065C1003774B7002B7DB100237FAC001F82AD001C83AD001785AE001687
+      AF001489B100128AB300108CB6000F8EB9000D91BB000C94BF000A97C3000A99
+      C5000A9AC7000B9BC8000B9CCA000C9ECC000C9FCD000DA0CE000DA1CE000EA2
+      CF0011A3CF0015A4CF0018A5CF001CA7D10021A9D1002AAAD00035AACD0035AD
+      D00035AFD30035B1D60037B5D8003AB8DB003EB9DC0040BBDD0045BEDE004CC1
+      E00055C6E3005BC8E40061CAE40066CBE3006BCCE30072D3E60078D8EB0080D8
+      EE0083D9F10085DCF40088DBF5008BDBF6008EDAF70091DAF70095DCF70097E0
+      F7009EE3F800A4E7F900A9E9F900AEE9F900B5EBF900B2EBF800919191919191
+      919191919191919191919191919191919191919191919191D5E7E8E7E3DED591
+      919191919191919191919191919191D6DDF7F8F8F7F6F4EEE6D5919191919191
+      919191919191D7DADEF8F8F6F7F6EDF1F8F1E69191919191919191919191DADE
+      DFFAFAF8F7F7E9EDF7F7F8EB91919191919191919191DDE5E3FCFCFAFAEEE0E9
+      F3F7F7F7DC919191919191919191DEE7E4FEFEFCFCEDC5E0EFF7F6F6DC919191
+      919191919191DEEAE7FEFEFEFDFBC4C4F5F7F6F6DC919191919191919191E3EC
+      E9FEFEFEFEFEE0C3F9F7F6F6DC919191919191919191E3E9E3F8F8F1F1F8FBFB
+      FCFAF7F6DC919191919191919191DCEAEAE0E1E9E5E3DEE4EBF2FAF9DC919191
+      919191919191DBF3EEC5C5EFF3EBE5DDD9D6E6F5DC91919191919191919191D5
+      E8EDF1FAF6F0EAE4DAD1D2DBDC919191919191919191919191DBE8F0F3F3F3E4
+      D0CFE4D8DC919191919191919191919191919191D5DEE4DED2CFD4D491919191
+      91919191919191919191919191919191D2CE9191919191919191919191919191
+      91C8C89191919191D2CE919191919191919191919191919191D9D09191919191
+      D2CE919191919191919191919191919191E6E39191919191D2CE919191919191
+      919191919191919191E0F3CE91919191D2CE9191919191919191919191919191
+      9191EFF6E1C9C8CDD9CC91919191919191919191919191919191C9EDF9F3F3EB
+      DC91919191919191919191919191919191919191CBE0E1CF9191919191919191
+      9191919191919191919191919191919191919191919191919191}
+    ParentDoubleBuffered = False
+    ParentFont = False
+    TabOrder = 1
+    OnClick = bbUpdatePasswordClick
+  end
+  object bbExportAllWalletKeys: TBitBtn
+    Left = 30
+    Top = 425
+    Width = 201
+    Height = 36
+    Caption = 'Export all Wallet Keys to a file'
+    DoubleBuffered = True
+    Glyph.Data = {
+      F6060000424DF606000000000000360000002800000018000000180000000100
+      180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C8518FF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+      9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C8518
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+      FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+      00FF0C85181399220C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF0C85180C85180C85180C85
+      180C85180C85180C85180C85181DAC31139A220C8518FF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE0C8518
+      57E38851DD7E4AD77443D0693BC95E34C1522BBA4725B33C1EAE33149B230C85
+      18FF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+      DEFE77DEFE0C85185EE89059E48953DE804CD87645D16C3DCA6035C2542DBB49
+      26B53F1FAF35149B250C8518FF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+      F38FE8FF7CE2FE7CE3FE7CE3FE0C851861EB955FE9925AE58B54DF824DD97846
+      D26D3ECB6237C4562FBD4C27B64021B037159B250C8518FF00FFFF00FF019ACF
+      60CAEF1FA8D85EC1E1C2E6ED8ACEE08FCFE18ECFE10C851861EB9561EB955FEA
+      935CE58D56E0844FDB7A48D47040CD6538C65931BF4D1DA3320C8518FF00FFFF
+      00FFFF00FF019ACF65CFF53EB7E52CA9D4C5EFF8ACF3FEA5F2FFA5F2FF0C8518
+      61EB9561EB9561EB9561EB945CE68E57E18650DC7C49D57242CE6727AD410C85
+      18FF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F32A9CC673CBE7D6FEFDB1
+      FBFDB2FBFD0C85180C85180C85180C85180C85180C85180C85180C851852DD7F
+      32B6500C851898FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB748E
+      A224A8D5B9E7F3D5F5F9D5F6F9D6F6FADCFAFBCDFDFCB9FCFCAFFAFCB0FAFCB1
+      FAFC0C85183ABE5C0C85189FFCFFA4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+      8BDBFF5FCDFFB7898973C3DD18A2D218A2D216A2D215A1D21AA4D391D7EBEBFE
+      FDDBFDFCC5FBFBC2FBFB0C85180C851883E4F3B6FDFFBAFFFFB5FCFD019ACFFF
+      00FFFF00FF019ACF99E2FF67D3FFB88989FEF5ECFDF3EBF0EFEAE5EBE8D6E5E6
+      A4D2E025A6D34DB9DDE5F8FBF5FDFCEBFCFB0C8518C4FBFF9CE4F2DAFEFFD9FE
+      FFE3FFFFADE9F5019ACFFF00FF019ACF9FE9FF70DCFFB88989FEF3E9FFF2E6FE
+      F3E9FEF3E9FEF3E9FEF3E9D4E4E439ADD422A5D49DD8ECF1F9FBEEEFEFE9FDFF
+      CEEEF7F8FFFFF7FFFFFEFFFFE9F9FD019ACFFF00FF019ACFA7EFFF76E5FFB889
+      89FFF2E5FFF0E2FFF2E5FFF2E5FFF2E5FFF2E5FFF2E5EAEBE38EC9DA44B0D501
+      9ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACFFF00FF019ACF
+      ABF6FF7EEDFFB88989FFF0E2FFEFDFFFF0E2FFF0E2FFF0E2FFF0E2FFF0E2FEEE
+      E0FBECDEFAEBDEF6E6D9B8898993F7FF019ACFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACFC7FFFF82F5FFB88989FFEEDFFFECDBFFEEDFFFEEDFFFEEDF
+      FFEEDFF9E8D9DECCC1D9CABDCFBDB4C8B3ACB88989B5FFFF019ACFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFFB88989FFECDBFFEBD8FF
+      ECDBFFECDBFFECDBFFECDBF5E2D2C4ABA7C2A8A5BBA39FC2AFA9B88989019ACF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFB889
+      89FFEBD8FFEAD5FFEBD8FFEBD8FFEBD8FFEBD8FFEBD8D9C8C5FEFEFDFEF6EFDE
+      C9C0B88989FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FF019ACFB88989FFE9D5FFE8D3FFE9D5FFE9D5FFE9D5FFE9D5FFE9D5C6AD
+      A9FEF8F2E8D4CACD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFB88989FFE7D1FFE7D0FFE7D1FFE7D1FFE7D1
+      FFE7D1E7CEBFD3BFB9E8D5CCCD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB88989FFE6CFFFE6CFFF
+      E6CFFFE6CFFFE6CFFFE6CFD5BBB2E0CCC5CD9999FF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB889
+      89B88989B88989B88989B88989B88989B88989B88989B88989FF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+    ParentDoubleBuffered = False
+    TabOrder = 10
+    OnClick = bbExportAllWalletKeysClick
+  end
+  object bbImportKeysFile: TBitBtn
+    Left = 247
+    Top = 425
+    Width = 174
+    Height = 36
+    Caption = 'Import a Wallet Keys File'
+    DoubleBuffered = True
+    Glyph.Data = {
+      F6060000424DF606000000000000360000002800000018000000180000000100
+      180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+      9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+      FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF74DBFF7ADEFF79DDFF73D9
+      FF5CCCF522ACDD019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE71D9FE
+      71D9FE71D9FE73DAFE76DCFF7BDFFF7ADEFF78DCFF77DCFF019ACFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+      DEFE77DEFE77DEFE77DEFE77DEFE77DEFE76DEFE76DEFE76DEFE78DFFF7CE1FF
+      65D2F8019ACFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+      F38FE8FF7CE2FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7CE3FE7C
+      E3FE7DE4FE7DE3FE5ED1F3019ACFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+      60CAEF1FA8D85EC1E2BBF4FE7DE7FE82E8FE81E8FE81E8FE81E8FE81E8FE81E8
+      FE81E8FE81E8FE81E8FE82E8FE84E9FE5ED3F18DEEFF019ACFFF00FFFF00FFFF
+      00FFFF00FF019ACF65CFF53EB7E529ACD8BFEEF88DEFFF85EDFF85EDFF85EDFF
+      85EDFF86EDFF86EDFF86EDFF86EDFF86EDFF87EDFF89EEFF65D9F396F5FF019A
+      CFFF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F31FA7D773CBE7C5FCFF93
+      F7FF93F7FF92F6FF8DF4FF89F3FF89F2FF8BF2FF8BF2FF8BF2FF8BF2FF8EF3FF
+      6ADEF395F8FF98FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB3EB8
+      E920A7D5B6E6F3D0F4FAD1F5FAD2F6FAD5F9FCB9FBFE9BF8FF8FF6FF91F6FF92
+      F6FF93F7FF6BD0B70C85188BEAE0A4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+      8BDBFF5FCDFF64CDFE2CAFE30D9FD30FA0D310A0D310A0D317A3D38ED7ECE2FD
+      FEC3FAFFA5F8FFA3F8FF84DDCF0C851838B5570C8518ABF3EBB5FCFD019ACFFF
+      00FFFF00FF019ACF99E2FF67D3FF6DD4FE6CD4FE69D1FE64CEFB61CDF95BC9F5
+      48BEEB17A3D54BB8DDDFF7FBE8FCFFB1E7DD0C85184ACE7361EB9541C1640C85
+      18D6F6F0ADE9F5019ACFFF00FF019ACF9FE9FF70DCFF76DDFE76DDFE76DDFE75
+      DCFE74DCFE73DCFE73DBFE61CEF61CA8D91CA5D58CCED70C851842C5665BE68C
+      59E1895DE78F3EBD600C8518DBF1EF019ACFFF00FF019ACFA7EFFF76E5FF7CE5
+      FF7CE5FF7CE5FF7CE5FF7DE5FF7DE5FF7DE5FF7DE3FF72DDFB40B8D20C85182D
+      AD474AD47250D97B55DE8359E1885AE38B33AF510C85180197C3FF00FF019ACF
+      ABF6FF7EEDFF85ECFF85ECFF85ECFF85ECFF84ECFF80ECFF7CECFF7DECFF7EEC
+      FF0C85180C85180C85180C851840C7634FDA7A55DF830C85180C85180C85180C
+      8518FF00FF019ACFC7FFFF82F5FF8FF5FF8FF5FF8FF5FF8EF5FF8DF4FFA0FDFF
+      B7FFFFAFFFFFAEFFFFA6F9F4A5FBF8A3FCFA4CB07732B74F48D6704AD3720C85
+      18FF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFF8AFCFF90FCFF90FCFF90
+      FCFF99FDFF86E8F5019ACF019ACF019ACF019ACF019ACF019ACF0486642CB347
+      41D16636BC540C8518FF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFBCFF
+      FFBCFFFFBCFFFFC0FFFF9DF5FB019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FF0C85182BB74538C9580C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FF019ACF019ACF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FF0C851828BB4126B13E0C8518FF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+      FF00FFFF00FFFF00FFFF00FFFF00FF0C851814A4241CAE310C8518FF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+      00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C85180C96170D991A0C8518
+      FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+      FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C85180C85180C85180C
+      8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+    ParentDoubleBuffered = False
+    TabOrder = 11
+    OnClick = bbImportKeysFileClick
+  end
+  object SaveDialog: TSaveDialog
+    DefaultExt = 'dat'
+    Filter = 'Wallet keys file|*.dat|All files|*.*'
+    FilterIndex = 0
+    Left = 150
+    Top = 150
+  end
+  object OpenDialog: TOpenDialog
+    DefaultExt = 'dat'
+    Filter = 'Wallet keys file (*.dat)|*.dat|All files (*.*)|*.*'
+    FilterIndex = 0
+    Left = 205
+    Top = 155
+  end
+end

+ 481 - 0
Units/Forms/UFRMWalletKeys.pas

@@ -0,0 +1,481 @@
+unit UFRMWalletKeys;
+
+{ 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
+  Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UWalletKeys, Buttons, clipbrd;
+
+type
+  TFRMWalletKeys = class(TForm)
+    lbWalletKeys: TListBox;
+    bbExportPrivateKey: TBitBtn;
+    lblEncryptionTypeCaption: TLabel;
+    lblEncryptionType: TLabel;
+    lblKeyNameCaption: TLabel;
+    lblKeyName: TLabel;
+    lblPrivateKeyCaption: TLabel;
+    memoPrivateKey: TMemo;
+    bbChangeName: TBitBtn;
+    bbImportPrivateKey: TBitBtn;
+    bbExportPublicKey: TBitBtn;
+    bbImportPublicKey: TBitBtn;
+    bbGenerateNewKey: TBitBtn;
+    lblPrivateKeyCaption2: TLabel;
+    bbDelete: TBitBtn;
+    lblKeysEncrypted: TLabel;
+    bbUpdatePassword: TBitBtn;
+    bbExportAllWalletKeys: TBitBtn;
+    SaveDialog: TSaveDialog;
+    bbImportKeysFile: TBitBtn;
+    OpenDialog: TOpenDialog;
+    procedure FormCreate(Sender: TObject);
+    procedure bbChangeNameClick(Sender: TObject);
+    procedure bbExportPrivateKeyClick(Sender: TObject);
+    procedure bbImportPrivateKeyClick(Sender: TObject);
+    procedure lbWalletKeysClick(Sender: TObject);
+    procedure bbGenerateNewKeyClick(Sender: TObject);
+    procedure bbExportPublicKeyClick(Sender: TObject);
+    procedure bbDeleteClick(Sender: TObject);
+    procedure bbImportPublicKeyClick(Sender: TObject);
+    procedure bbUpdatePasswordClick(Sender: TObject);
+    procedure bbExportAllWalletKeysClick(Sender: TObject);
+    procedure bbImportKeysFileClick(Sender: TObject);
+  private
+    FWalletKeys: TWalletKeys;
+    procedure SetWalletKeys(const Value: TWalletKeys);
+    { Private declarations }
+    Procedure UpdateWalletKeys;
+    Procedure UpdateSelectedWalletKey;
+    Function GetSelectedWalletKey(var WalletKey : TWalletKey) : Boolean;
+    Function GetSelectedWalletKeyAndIndex(var WalletKey : TWalletKey; var index : Integer) : Boolean;
+    Procedure CheckIsWalletKeyValidPassword;
+  public
+    { Public declarations }
+    Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
+  end;
+
+implementation
+
+uses
+  UCrypto, UAccounts, Windows, UFRMNewPrivateKeyType, UAES;
+
+{$R *.dfm}
+
+{ TFRMWalletKeys }
+
+procedure TFRMWalletKeys.bbChangeNameClick(Sender: TObject);
+Var wk : TWalletKey;
+  s : String;
+  index : integer;
+begin
+  if not GetSelectedWalletKeyAndIndex(wk,index) then exit;
+  s := wk.Name;
+  if InputQuery('Change name','Input new key name:',s) then begin
+    WalletKeys.SetName(index,s);
+    UpdateWalletKeys;
+  end;
+end;
+
+procedure TFRMWalletKeys.bbDeleteClick(Sender: TObject);
+Var wk : TWalletKey;
+  index : Integer;
+  s : String;
+begin
+  if Not GetSelectedWalletKeyAndIndex(wk,index) then exit;
+  s := 'Are you sure to delete selected private key?'+#10+wk.Name+#10+#10+
+    'Please note that this will remove FOREVER access to accounts using this key...';
+  if Application.MessageBox(Pchar(s),PChar('Delete key'),
+    MB_YESNO+MB_DEFBUTTON2+MB_ICONWARNING)<>Idyes then exit;
+  if Application.MessageBox(PChar('Sure? You want to delete?'),PChar('Delete key'),
+    MB_YESNO+MB_DEFBUTTON2+MB_ICONWARNING)<>Idyes then exit;
+  WalletKeys.Delete(index);
+  UpdateWalletKeys;
+end;
+
+procedure TFRMWalletKeys.bbExportAllWalletKeysClick(Sender: TObject);
+Var efn : String;
+  fs : TFileStream;
+begin
+  if WalletKeys.Count<=0 then raise Exception.Create('Your wallet is empty. No keys!');
+  if ((WalletKeys.IsValidPassword) And (WalletKeys.WalletPassword='')) then begin
+    if Application.MessageBox(PChar('Your wallet has NO PASSWORD'+#10+#10+
+      'I think that prior to export it you must consider to protect it!'+#10+#10+
+      'Continue without password protection?'),PChar(Application.Title),MB_YESNO+MB_ICONWARNING+MB_DEFBUTTON2)<>IdYes then exit;
+  end;
+
+  if Not SaveDialog.Execute then exit;
+  efn := SaveDialog.FileName;
+  if FileExists(efn) then begin
+    if Application.MessageBox(PChar('This file exists:'+#10+efn+#10#10+'Overwrite?'),PChar(Application.Title),MB_YESNO+MB_ICONQUESTION+MB_DEFBUTTON2)<>IdYes then exit;
+  end;
+  fs := TFileStream.Create(efn,fmCreate);
+  try
+    fs.Size := 0;
+    WalletKeys.SaveToStream(fs);
+  finally
+    fs.Free;
+  end;
+  Application.MessageBox(PChar('Wallet key exported to a file:'+#10+efn),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+end;
+
+procedure TFRMWalletKeys.bbExportPrivateKeyClick(Sender: TObject);
+Var wk : TWalletKey;
+  pwd1,pwd2, enc : String;
+begin
+  CheckIsWalletKeyValidPassword;
+  if Not GetSelectedWalletKey(wk) then exit;
+  if Assigned(wk.PrivateKey) then begin
+    if InputQuery('Export private key','Insert a password to export',pwd1) then begin
+      if InputQuery('Export private key','Repeat the password to export',pwd2) then begin
+        if pwd1<>pwd2 then raise Exception.Create('Passwords does not match!');
+        enc := TCrypto.ToHexaString( TAESComp.EVP_Encrypt_AES256( wk.PrivateKey.ExportToRaw,pwd1) );
+        Clipboard.AsText := enc;
+        Application.MessageBox(PChar('The password has been encrypted with your password and copied to the clipboard.'+
+          #10+#10+
+          'Password: "'+pwd1+'"'+#10+
+          #10+
+          'Encrypted key value (Copied to the clipboard):'+#10+
+          enc+
+          #10+#10+'Length='+Inttostr(length(enc))+' bytes'),
+          PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+      end else raise Exception.Create('Cancelled operation');
+    end;
+  end;
+end;
+
+procedure TFRMWalletKeys.bbExportPublicKeyClick(Sender: TObject);
+Var wk : TWalletKey;
+  s : String;
+begin
+  CheckIsWalletKeyValidPassword;
+  if Not GetSelectedWalletKey(wk) then exit;
+  s := TAccountComp.AccountPublicKeyExport(wk.AccountKey);
+  Clipboard.AsText := s;
+  Application.MessageBox(PChar('The public key has been copied to the clipboard'+#10+#10+
+    'Public key value:'+#10+
+    s+#10+#10+
+    'Length='+Inttostr(length(s))+' bytes'),
+          PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+end;
+
+procedure TFRMWalletKeys.bbGenerateNewKeyClick(Sender: TObject);
+Var FRM : TFRMNewPrivateKeyType;
+begin
+  CheckIsWalletKeyValidPassword;
+  FRM := TFRMNewPrivateKeyType.Create(Self);
+  try
+    FRM.WalletKeys := WalletKeys;
+    FRM.ShowModal;
+    UpdateWalletKeys;
+  finally
+    FRM.Free;
+  end;
+end;
+
+procedure TFRMWalletKeys.bbImportKeysFileClick(Sender: TObject);
+Var wki : TWalletKeys;
+  ifn,pwd : String;
+  i : Integer;
+  cPrivatekeys, cPublicKeys : Integer;
+begin
+  if Not OpenDialog.Execute then exit;
+  ifn := OpenDialog.FileName;
+  if Not FileExists(ifn) then raise Exception.Create('Cannot find file '+ifn);
+
+  wki := TWalletKeys.Create(Self);
+  try
+    wki.WalletFileName := ifn;
+    if wki.Count<=0 then raise Exception.Create('Wallet file has no valid data');
+    pwd := '';
+    While (Not wki.IsValidPassword) do begin
+      if Not InputQuery('Import','Insert the wallet file Password:',pwd) then exit;
+      wki.WalletPassword := pwd;
+      if not wki.IsValidPassword then begin
+        If Application.MessageBox(PChar('Password entered is not valid, retry?'),PChar(Application.Title),MB_ICONERROR+MB_YESNO)<>Idyes then exit;
+      end;
+    end;
+    cPrivatekeys := 0;
+    cPublicKeys := 0;
+    if wki.IsValidPassword then begin
+      for i := 0 to wki.Count - 1 do begin
+        if WalletKeys.IndexOfAccountKey(wki.Key[i].AccountKey)<0 then begin
+          If Assigned(wki.Key[i].PrivateKey) then begin
+            WalletKeys.AddPrivateKey(wki.Key[i].Name,wki.Key[i].PrivateKey);
+            inc(cPrivatekeys);
+          end else begin
+            WalletKeys.AddPublicKey(wki.Key[i].Name,wki.Key[i].AccountKey);
+            inc(cPublicKeys);
+          end;
+        end;
+      end;
+    end;
+    if (cPrivatekeys>0) Or (cPublicKeys>0) then begin
+      Application.MessageBox(PChar('Wallet file imported successfully'+#10+#10+
+        'File:'+ifn+#10+#10+
+        'Total Keys in wallet: '+inttostr(wki.Count)+#10+
+        'Imported Private keys: '+IntToStr(cPrivatekeys)+#10+
+        'Imported public keys only: '+IntToStr(cPublicKeys)+#10+
+        'Duplicated (not imported): '+InttoStr(wki.Count - cPrivatekeys - cPublicKeys)),
+        PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+    end else begin
+      Application.MessageBox(PChar('Wallet file keys were allready in your Wallet. Nothing imported'+#10+#10+
+        'File:'+ifn+#10+#10+
+        'Total Keys in wallet: '+inttostr(wki.Count)),
+        PChar(Application.Title),MB_OK+MB_ICONWARNING);
+    end;
+  finally
+    wki.Free;
+  end;
+end;
+
+procedure TFRMWalletKeys.bbImportPrivateKeyClick(Sender: TObject);
+var s : String;
+ desenc, enc : AnsiString;
+ 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','Insert 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 after decryption!'),'',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 allready 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);
+              end;
+            Finally
+              EC.Free;
+            End;
+          end else begin
+            if Application.MessageBox(PChar('Invalid password!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then exit;
+            desenc := '';
+          end;
+        end else begin
+          if Application.MessageBox(PChar('Invalid password or corrupted data!'),'',MB_RETRYCANCEL+MB_ICONERROR)<>IDRETRY then exit;
+        end;
+      end else exit;
+    Until (desenc<>'');
+    UpdateWalletKeys;
+
+  end;
+end;
+
+procedure TFRMWalletKeys.bbImportPublicKeyClick(Sender: TObject);
+var s : String;
+ raw : AnsiString;
+ EC : TECPrivateKey;
+ account : TAccountKey;
+ errors : AnsiString;
+begin
+  CheckIsWalletKeyValidPassword;
+  if Not Assigned(WalletKeys) then exit;
+  if Not InputQuery('Import publick key','Insert the public key in hexa format or imported format',s) then exit;
+  If not TAccountComp.AccountPublicKeyImport(s,account,errors) then begin
+    raw := TCrypto.HexaToRaw(s);
+    if trim(raw)='' then raise Exception.Create('Invalid public key value (Not hexa or not an imported format)'+#10+errors);
+    account := TAccountComp.RawString2Accountkey(raw);
+  end;
+  If not TAccountComp.IsValidAccountKey(account,errors) then raise Exception.Create('This data is not a valid public key'+#10+errors);
+  if WalletKeys.IndexOfAccountKey(account)>=0 then raise exception.Create('This key exists on your wallet');
+  s := 'Imported public key '+DateTimeToStr(now);
+  if InputQuery('Set a name','Name for this private key:',s) then begin
+    WalletKeys.AddPublicKey(s,account);
+    UpdateWalletKeys;
+    Application.MessageBox(PChar('Imported public key'),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+  end;
+end;
+
+procedure TFRMWalletKeys.bbUpdatePasswordClick(Sender: TObject);
+Var s,s2 : String;
+begin
+  if FWalletKeys.IsValidPassword then begin
+    s := ''; s2 := '';
+    if Not InputQuery('Change password','Type new password',s) then exit;
+    if trim(s)<>s then raise Exception.Create('Password cannot start or end with a space character');
+    if Not InputQuery('Change password','Type new password again',s2) then exit;
+    if s<>s2 then raise Exception.Create('Two passwords are different!');
+
+    FWalletKeys.WalletPassword := s;
+    Application.MessageBox(PChar('Password changed!'+#10+#10+
+      'Please note that your new password is "'+s+'"'+#10+#10+
+      '(If you lose this password, you will lose your Wallet forever !)'),
+      PChar(Application.Title),MB_ICONWARNING+MB_OK);
+    UpdateWalletKeys;
+  end else begin
+    s := '';
+    Repeat
+      if Not InputQuery('Wallet Password','Insert Wallet Password',s) then exit;
+      FWalletKeys.WalletPassword := s;
+      if Not FWalletKeys.IsValidPassword then Application.MessageBox(PChar('Invalid password'),PChar(Application.Title),MB_ICONERROR+MB_OK);
+    Until FWalletKeys.IsValidPassword;
+    UpdateWalletKeys;
+  end;
+end;
+
+procedure TFRMWalletKeys.CheckIsWalletKeyValidPassword;
+begin
+  if Not Assigned(FWalletKeys) then exit;
+
+  if Not FWalletKeys.IsValidPassword then begin
+    Application.MessageBox(PChar('Wallet key is encrypted!'+#10+#10+'You must insert password to continue...'),
+      PCHar(Application.Title),MB_OK+MB_ICONWARNING);
+    bbUpdatePasswordClick(Nil);
+    if Not FWalletKeys.IsValidPassword then raise Exception.Create('Cannot continue without valid password');
+  end;
+end;
+
+procedure TFRMWalletKeys.FormCreate(Sender: TObject);
+begin
+  lbWalletKeys.Sorted := true;
+  FWalletKeys := Nil;
+  UpdateWalletKeys;
+end;
+
+function TFRMWalletKeys.GetSelectedWalletKey(var WalletKey: TWalletKey): Boolean;
+Var i : Integer;
+begin
+  Result := GetSelectedWalletKeyAndIndex(WalletKey,i);
+end;
+
+function TFRMWalletKeys.GetSelectedWalletKeyAndIndex(var WalletKey: TWalletKey; var index: Integer): Boolean;
+begin
+  index := -1;
+  Result := false;
+  if Not Assigned(WalletKeys) then exit;
+  if lbWalletKeys.ItemIndex<0 then exit;
+  index := Integer(lbWalletKeys.Items.Objects[ lbWalletKeys.ItemIndex ]);
+  if (index>=0) And (index<WalletKeys.Count) then begin
+    WalletKey := WalletKeys.Key[index];
+    Result := true;
+  end;
+end;
+
+procedure TFRMWalletKeys.lbWalletKeysClick(Sender: TObject);
+begin
+  UpdateSelectedWalletKey;
+end;
+
+procedure TFRMWalletKeys.SetWalletKeys(const Value: TWalletKeys);
+begin
+  FWalletKeys := Value;
+  UpdateWalletKeys;
+end;
+
+procedure TFRMWalletKeys.UpdateSelectedWalletKey;
+Var
+  wk : TWalletKey;
+  ok : Boolean;
+begin
+  ok := false;
+  wk := CT_TWalletKey_NUL;
+  try
+    if Not GetSelectedWalletKey(wk) then exit;
+    ok := true;
+    lblEncryptionType.Caption := TAccountComp.GetECInfoTxt( wk.AccountKey.EC_OpenSSL_NID );
+    if wk.Name='' then lblKeyName.Caption := '(No name)'
+    else lblKeyName.Caption := wk.Name;
+    if Assigned(wk.PrivateKey) then begin
+      memoPrivateKey.Lines.Text :=  TCrypto.PrivateKey2Hexa(wk.PrivateKey.PrivateKey);
+      memoPrivateKey.Font.Color := clBlack;
+    end else begin
+      memoPrivateKey.Lines.Text := '(No private key)';
+      memoPrivateKey.Font.Color := clRed;
+    end;
+  finally
+    lblEncryptionTypeCaption.Enabled := ok;
+    lblEncryptionType.Enabled := ok;
+    lblKeyNameCaption.Enabled := ok;
+    lblKeyName.Enabled := (ok) And (wk.Name<>'');
+    lblPrivateKeyCaption.Enabled := ok;
+    memoPrivateKey.Enabled := ok;
+    bbExportPrivateKey.Enabled := ok;
+    bbExportPublicKey.Enabled := ok;
+    bbChangeName.Enabled := ok;
+    lblPrivateKeyCaption2.Enabled := ok;
+    bbDelete.Enabled := ok;
+    if not ok then begin
+      lblEncryptionType.Caption := '';
+      lblKeyName.Caption := '';
+      memoPrivateKey.Lines.Clear;
+    end;
+  end;
+
+end;
+
+procedure TFRMWalletKeys.UpdateWalletKeys;
+Var lasti,i : Integer;
+  wk : TWalletKey;
+  s : AnsiString;
+begin
+  GetSelectedWalletKeyAndIndex(wk,lasti);
+  lbWalletKeys.Items.BeginUpdate;
+  Try
+    lbWalletKeys.Items.Clear;
+    lblKeysEncrypted.Caption := '';
+    if not assigned(FWalletKeys) then exit;
+    If FWalletKeys.IsValidPassword then begin
+      if FWalletKeys.WalletPassword='' then lblKeysEncrypted.Caption := 'Wallet without password'
+      else lblKeysEncrypted.Caption := 'Wallet is password protected';
+      lblKeysEncrypted.font.Color := clGreen;
+      bbUpdatePassword.Caption := 'Change password';
+    end else begin
+      lblKeysEncrypted.Caption := 'Wallet is encrypted... need password!';
+      lblKeysEncrypted.font.Color := clRed;
+      bbUpdatePassword.Caption := 'Input password';
+    end;
+    for i := 0 to WalletKeys.Count - 1 do begin
+      wk := WalletKeys.Key[i];
+      if (wk.Name='') then begin
+        s := 'Sha256='+TCrypto.ToHexaString( TCrypto.DoSha256( TAccountComp.AccountKey2RawString(wk.AccountKey) ) );
+      end else begin
+        s := wk.Name;
+      end;
+      if Not Assigned(wk.PrivateKey) then begin
+        if wk.CryptedKey<>'' then s:=s+' (Encrypted, need password)';
+        s:=s+' (* without key)';
+      end;
+      lbWalletKeys.Items.AddObject(s,TObject(i));
+    end;
+    i := lbWalletKeys.Items.IndexOfObject(TObject(lasti));
+    lbWalletKeys.ItemIndex := i;
+  Finally
+    lbWalletKeys.Items.EndUpdate;
+  End;
+  UpdateSelectedWalletKey;
+end;
+
+end.

+ 1318 - 0
Units/PascalCoin/UAccounts.pas

@@ -0,0 +1,1318 @@
+unit UAccounts;
+
+{ 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, UConst, Windows, UCrypto;
+
+Type
+  TAccountKey = TECDSA_Public;
+  PAccountKey = ^TAccountKey;
+
+  TAccountComp = Class
+  private
+  public
+    Class Function IsValidAccountKey(account: TAccountKey; var errors : AnsiString): Boolean;
+    Class Function GetECInfoTxt(Const EC_OpenSSL_NID: Word) : AnsiString;
+    Class Procedure ValidsEC_OpenSSL_NID(list : TList);
+    Class Function AccountKey2RawString(account: TAccountKey): AnsiString;
+    Class Function RawString2Accountkey(rawaccstr: AnsiString): TAccountKey;
+    Class Function PrivateToAccountkey(key: TECPrivateKey): TAccountKey;
+    Class Function IsAccountBlockedByProtocol(account_number, blocks_count : Cardinal) : Boolean;
+    Class Function Equal(account1,account2 : TAccountKey) : Boolean;
+    Class Function AccountNumberToAccountTxtNumber(account_number : Cardinal) : AnsiString;
+    Class Function AccountTxtNumberToAccountNumber(Const account_txt_number : AnsiString; var account_number : Cardinal) : Boolean;
+    Class Function FormatMoney(Money : Int64) : AnsiString;
+    Class Function TxtToMoney(Const moneytxt : AnsiString; var money : Int64) : Boolean;
+    Class Function AccountKeyToExport(Const account : TAccountKey) : AnsiString;
+    Class Function AccountKeyFromImport(Const HumanReadable : AnsiString; var account : TAccountKey; var errors : AnsiString) : Boolean;
+    Class Function AccountPublicKeyExport(Const account : TAccountKey) : AnsiString;
+    Class Function AccountPublicKeyImport(Const HumanReadable : AnsiString; var account : TAccountKey; var errors : AnsiString) : Boolean;
+  End;
+
+  TAccount = Record
+    account: Cardinal;        // FIXED value. Account number
+    accountkey: TAccountKey;  // Public EC
+    balance: UInt64;          // Balance, allways >= 0
+    updated_block: Cardinal;  // Number of block where was updated
+    n_operation: Cardinal;    // count number of owner operations (when receive, this is not updated)
+  End;
+  PAccount = ^TAccount;
+
+  TBlockAccount = Record
+    blockaccount : Cardinal;  // FIXED. Number in the BlockChain
+    accounts : Array[0..CT_AccountsPerBlock-1] of TAccount;
+    timestamp: Cardinal;      // FIXED: Same value that stored in BlockChain. Included here because I need it to calculate new target value
+    block_hash: AnsiString;   // Calculated on every block change (on create and on accounts updated)
+  end;
+  PBlockAccount = ^TBlockAccount;
+
+  TPCSafeBox = Class;
+
+  // This is a class to quickly find accountkeys and their respective account number/s
+  TOrderedAccountKeysList = Class
+  Private
+    FAutoAddAll : Boolean;
+    FAccountList : TPCSafeBox;
+    FOrderedAccountKeysList : TList; // An ordered list of pointers to quickly find account keys in account list
+    Function Find(Const AccountKey: TAccountKey; var Index: Integer): Boolean;
+    function GetAccountKeyList(index: Integer): TList;
+    function GetAccountKey(index: Integer): TAccountKey;
+  protected
+    Procedure Clear(RemoveAccountList : Boolean);
+  public
+    Constructor Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
+    Destructor Destroy; override;
+    Procedure AddAccountKey(Const AccountKey : TAccountKey);
+    Procedure RemoveAccountKey(Const AccountKey : TAccountKey);
+    Procedure AddAccounts(Const AccountKey : TAccountKey; accounts : Array of Cardinal);
+    Procedure RemoveAccounts(Const AccountKey : TAccountKey; accounts : Array of Cardinal);
+    Function IndexOfAccountKey(Const AccountKey : TAccountKey) : Integer;
+    Property AccountKeyList[index : Integer] : TList read GetAccountKeyList;
+    Property AccountKey[index : Integer] : TAccountKey read GetAccountKey;
+    Function Count : Integer;
+  End;
+
+
+  // SafeBox is a box that only can be updated using SafeBoxTransaction, and this
+  // happens only when a new BlockChain is included. After this, a new "SafeBoxHash"
+  // is created, so each SafeBox has a unique SafeBoxHash
+
+  TPCSafeBox = Class
+  private
+    FBlockAccountsList : TList;
+    FListOfOrderedAccountKeysList : TList;
+    FBufferBlocksHash: AnsiString;
+    FTotalBalance: Int64;
+    FTotalFee: Int64;
+    FSafeBoxHash : TRawBytes;
+    FLock: TRTLCriticalSection; // Thread safe
+    FIsLocked : Boolean;
+    Procedure SetAccount(account_number : Cardinal; newAccountkey: TAccountKey; newBalance: UInt64; newN_operation: Cardinal);
+    Procedure AccountKeyListAddAccounts(Const AccountKey : TAccountKey; accounts : Array of Cardinal);
+    Procedure AccountKeyListRemoveAccount(Const AccountKey : TAccountKey; accounts : Array of Cardinal);
+  protected
+    Function AddNew(Const accountkey: TAccountKey; reward: UInt64; timestamp: Cardinal; compact_target: Cardinal; Const proof_of_work: AnsiString) : TBlockAccount;
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    function AccountsCount: Integer;
+    Function BlocksCount : Integer;
+    Procedure CopyFrom(accounts : TPCSafeBox);
+    Class Function CalcBlockHash(const block : TBlockAccount):AnsiString;
+    Class Function BlockAccountToText(Const block : TBlockAccount):AnsiString;
+    Function LoadFromStream(Stream : TStream; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;
+    Procedure SaveToStream(Stream : TStream);
+    Procedure Clear;
+    Function Account(account_number : Cardinal) : TAccount;
+    Function Block(block_number : Cardinal) : TBlockAccount;
+    Function CalcSafeBoxHash : TRawBytes;
+    Property TotalBalance : Int64 read FTotalBalance;
+    Procedure StartThreadSafe;
+    Procedure EndThreadSave;
+    Property IsLocked : Boolean read FIsLocked;
+  End;
+
+
+  TOrderedAccountList = Class
+  private
+    FList : TList;
+    Function Find(const account_number: Cardinal; var Index: Integer): Boolean;
+  public
+    Constructor Create;
+    Destructor Destroy; Override;
+    Procedure Clear;
+    Function Add(Const account : TAccount) : Integer;
+    Function Count : Integer;
+    Function Get(index : Integer) : TAccount;
+  End;
+
+
+  TPCSafeBoxTransaction = Class
+  private
+    FOrderedList : TOrderedAccountList;
+    FFreezedAccounts : TPCSafeBox;
+    FTotalBalance: Int64;
+    FTotalFee: Int64;
+    FOldSafeBoxHash : TRawBytes;
+    Function GetInternalAccount(account_number : Cardinal) : PAccount;
+  protected
+  public
+    Constructor Create(SafeBox : TPCSafeBox);
+    Destructor Destroy; override;
+    Function TransferAmount(sender,target : Cardinal; n_operation : Cardinal; amount, fee : UInt64; var errors : AnsiString) : Boolean;
+    Function UpdateAccountkey(account_number, n_operation: Cardinal; accountkey: TAccountKey; fee: UInt64; var errors : AnsiString) : Boolean;
+    Function Commit(accountkey: TAccountKey; reward: UInt64; timestamp: Cardinal; compact_target: Cardinal; proof_of_work: AnsiString; var errors : AnsiString) : Boolean;
+    Function Account(account_number : Cardinal) : TAccount;
+    Procedure Rollback;
+    Function CheckIntegrity : Boolean;
+    Property FreezedSafeBox : TPCSafeBox read FFreezedAccounts;
+    Property TotalFee : Int64 read FTotalFee;
+    Property TotalBalance : Int64 read FTotalBalance;
+    Procedure CopyFrom(transaction : TPCSafeBoxTransaction);
+    Procedure CleanTransaction;
+    Function ModifiedCount : Integer;
+    Function Modified(index : Integer) : TAccount;
+  End;
+
+  TStreamOp = Class
+  public
+    class Function WriteAnsiString(Stream: TStream; value: AnsiString): Integer;
+    class Function ReadAnsiString(Stream: TStream; var value: AnsiString): Integer;
+  End;
+
+Const
+  CT_Account_NUL : TAccount = (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0);
+  CT_BlockAccount_NUL : TBlockAccount = (
+    blockaccount:0;
+    accounts:(
+    (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0),
+    (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0),
+    (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0),
+    (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0),
+    (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0)
+    );
+    timestamp:0;
+    block_hash:'');
+
+implementation
+
+uses
+  SysUtils, ULog, ssl_const, ssl_err, UThread;
+
+{ TStreamOp }
+
+class function TStreamOp.ReadAnsiString(Stream: TStream; var value: AnsiString): Integer;
+Var
+  l: Word;
+begin
+  value := '';
+  Result := -1;
+  if Stream.Size - Stream.Position < 2 then
+    exit;
+  Stream.Read(l, 2);
+  if Stream.Size - Stream.Position < l then begin
+    Stream.Position := Stream.Position - 2; // Go back!
+    exit;
+  end;
+  SetLength(value, l);
+  Stream.ReadBuffer(value[1], l);
+  Result := l;
+end;
+
+class function TStreamOp.WriteAnsiString(Stream: TStream; value: AnsiString): Integer;
+Var
+  l: Word;
+begin
+  if (Length(value)>(256*256)) then begin
+    TLog.NewLog(lterror,Classname,'Invalid stream size! '+Inttostr(Length(value)));
+    raise Exception.Create('Invalid stream size! '+Inttostr(Length(value)));
+  end;
+
+  l := Length(value);
+  Stream.Write(l, 2);
+  if (l > 0) then
+    Stream.WriteBuffer(value[1], Length(value));
+  Result := l;
+end;
+
+{ TAccountComp }
+Const CT_Base58 : AnsiString = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
+
+class function TAccountComp.AccountKeyToExport(const account: TAccountKey): AnsiString;
+Var raw : TRawBytes;
+  BN, BNMod, BNDiv : TBigNum;
+  i : Integer;
+begin
+  Result := '';
+  raw := AccountKey2RawString(account);
+  BN := TBigNum.Create;
+  BNMod := TBigNum.Create;
+  BNDiv := TBigNum.Create(Length(CT_Base58));
+  try
+    BN.HexaValue := '01'+TCrypto.ToHexaString( raw )+TCrypto.ToHexaString(Copy(TCrypto.DoSha256(raw),1,4));
+    while (Not BN.IsZero) do begin
+      BN.Divide(BNDiv,BNMod);
+      If (BNMod.Value>=0) And (BNMod.Value<length(CT_Base58)) then Result := CT_Base58[Byte(BNMod.Value)+1] + Result
+      else raise Exception.Create('Error converting to Base 58');
+    end;
+  finally
+    BN.Free;
+    BNMod.Free;
+    BNDiv.Free;
+  end;
+end;
+
+
+class function TAccountComp.AccountKey2RawString(account: TAccountKey): AnsiString;
+Var s : TMemoryStream;
+begin
+  s := TMemoryStream.Create;
+  try
+    s.Write(account.EC_OpenSSL_NID, SizeOf(account.EC_OpenSSL_NID));
+    TStreamOp.WriteAnsiString(s,account.x);
+    TStreamOp.WriteAnsiString(s,account.y);
+    SetLength(Result,s.Size);
+    s.Position := 0;
+    s.Read(Result[1],s.Size);
+  finally
+    s.Free;
+  end;
+end;
+
+class function TAccountComp.AccountKeyFromImport(const HumanReadable: AnsiString; var account: TAccountKey; var errors : AnsiString): Boolean;
+Var raw : TRawBytes;
+  BN, BNAux, BNBase : TBigNum;
+  i,j : Integer;
+  s1,s2 : AnsiString;
+  i64 : Int64;
+  b : Byte;
+begin
+  result := false;
+  errors := 'Invalid length';
+  account := CT_Account_NUL.accountkey;
+  if length(HumanReadable)<20 then exit;
+  BN := TBigNum.Create(0);
+  BNAux := TBigNum.Create;
+  BNBase := TBigNum.Create(1);
+  try
+    for i := length(HumanReadable) downto 1 do begin
+      j := pos(HumanReadable[i],CT_Base58);
+      if j=0 then begin
+        errors := 'Invalid char "'+HumanReadable[i]+'" at pos '+inttostr(i)+'/'+inttostr(length(HumanReadable));
+        exit;
+      end;
+      BNAux.Value := j-1;
+      BNAux.Multiply(BNBase);
+      BN.Add(BNAux);
+      BNBase.Multiply(length(CT_Base58));
+    end;
+    // Last 8 hexa chars are the checksum of others
+    s1 := Copy(BN.HexaValue,3,length(BN.HexaValue));
+    s2 := copy(s1,length(s1)-7,8);
+    s1 := copy(s1,1,length(s1)-8);
+    raw := TCrypto.HexaToRaw(s1);
+    s1 := TCrypto.ToHexaString( TCrypto.DoSha256(raw) );
+    if copy(s1,1,8)<>s2 then begin
+      // Invalid checksum
+      errors := 'Invalid checksum';
+      exit;
+    end;
+    try
+      account := TAccountComp.RawString2Accountkey(raw);
+      Result := true;
+      errors := '';
+    except
+      // Nothing to do... invalid
+      errors := 'Error on conversion from Raw to Account key';
+    end;
+  Finally
+    BN.Free;
+    BNBase.Free;
+    BNAux.Free;
+  end;
+end;
+
+class function TAccountComp.AccountNumberToAccountTxtNumber(account_number: Cardinal): AnsiString;
+Var an : int64;
+begin
+  an := account_number;
+  an := ((((((an * 3) MOD 97) * 7) MOD 101) * 5) MOD 89)+10;
+  Result := IntToStr(account_number)+'-'+Inttostr(an);
+end;
+
+class function TAccountComp.AccountPublicKeyExport(const account: TAccountKey): AnsiString;
+Var raw : TRawBytes;
+  BN, BNMod, BNDiv : TBigNum;
+  i : Integer;
+begin
+  Result := '';
+  raw := AccountKey2RawString(account);
+  BN := TBigNum.Create;
+  BNMod := TBigNum.Create;
+  BNDiv := TBigNum.Create(Length(CT_Base58));
+  try
+    BN.HexaValue := '01'+TCrypto.ToHexaString( raw )+TCrypto.ToHexaString(Copy(TCrypto.DoSha256(raw),1,4));
+    while (Not BN.IsZero) do begin
+      BN.Divide(BNDiv,BNMod);
+      If (BNMod.Value>=0) And (BNMod.Value<length(CT_Base58)) then Result := CT_Base58[Byte(BNMod.Value)+1] + Result
+      else raise Exception.Create('Error converting to Base 58');
+    end;
+  finally
+    BN.Free;
+    BNMod.Free;
+    BNDiv.Free;
+  end;
+end;
+
+class function TAccountComp.AccountPublicKeyImport(
+  const HumanReadable: AnsiString; var account: TAccountKey;
+  var errors: AnsiString): Boolean;
+Var raw : TRawBytes;
+  BN, BNAux, BNBase : TBigNum;
+  i,j : Integer;
+  s1,s2 : AnsiString;
+  i64 : Int64;
+  b : Byte;
+begin
+  result := false;
+  errors := 'Invalid length';
+  account := CT_Account_NUL.accountkey;
+  if length(HumanReadable)<20 then exit;
+  BN := TBigNum.Create(0);
+  BNAux := TBigNum.Create;
+  BNBase := TBigNum.Create(1);
+  try
+    for i := length(HumanReadable) downto 1 do begin
+      j := pos(HumanReadable[i],CT_Base58);
+      if j=0 then begin
+        errors := 'Invalid char "'+HumanReadable[i]+'" at pos '+inttostr(i)+'/'+inttostr(length(HumanReadable));
+        exit;
+      end;
+      BNAux.Value := j-1;
+      BNAux.Multiply(BNBase);
+      BN.Add(BNAux);
+      BNBase.Multiply(length(CT_Base58));
+    end;
+    // Last 8 hexa chars are the checksum of others
+    s1 := Copy(BN.HexaValue,3,length(BN.HexaValue));
+    s2 := copy(s1,length(s1)-7,8);
+    s1 := copy(s1,1,length(s1)-8);
+    raw := TCrypto.HexaToRaw(s1);
+    s1 := TCrypto.ToHexaString( TCrypto.DoSha256(raw) );
+    if copy(s1,1,8)<>s2 then begin
+      // Invalid checksum
+      errors := 'Invalid checksum';
+      exit;
+    end;
+    try
+      account := TAccountComp.RawString2Accountkey(raw);
+      Result := true;
+      errors := '';
+    except
+      // Nothing to do... invalid
+      errors := 'Error on conversion from Raw to Account key';
+    end;
+  Finally
+    BN.Free;
+    BNBase.Free;
+    BNAux.Free;
+  end;
+end;
+
+class function TAccountComp.AccountTxtNumberToAccountNumber(const account_txt_number: AnsiString; var account_number: Cardinal): Boolean;
+Var i : Integer;
+  char1 : AnsiChar;
+  char2 : AnsiChar;
+  n : Int64;
+begin
+  Result := false;
+  if length(trim(account_txt_number))=0 then exit;
+  n := 0;
+  for i := 1 to length(account_txt_number) do begin
+    if account_txt_number[i] in ['0'..'9'] then begin
+      n := (n * 10) + ord( account_txt_number[i] ) - ord('0');
+    end else break;
+  end;
+  account_number := n;
+  if i>length(account_txt_number) then begin
+    result := true;
+    exit;
+  end;
+  if (account_txt_number[i] in ['-','.',' ']) then inc(i);
+  if length(account_txt_number)-1<>i then exit;
+  n := StrToIntDef(copy(account_txt_number,i,length(account_txt_number)),0);
+  Result := n = (((((((account_number * 3) MOD 97) * 7) MOD 101) * 5) MOD 89)+10);
+end;
+
+class function TAccountComp.Equal(account1, account2: TAccountKey): Boolean;
+begin
+  Result := (account1.EC_OpenSSL_NID=account2.EC_OpenSSL_NID) And
+    (account1.x=account2.x) And (account1.y=account2.y);
+end;
+
+class function TAccountComp.FormatMoney(Money: Int64): AnsiString;
+begin
+  Result := FormatFloat('0.0000',(Money/10000));
+end;
+
+class function TAccountComp.GetECInfoTxt(const EC_OpenSSL_NID: Word): AnsiString;
+begin
+  case EC_OpenSSL_NID of
+    NID_secp256k1 : begin
+      Result := 'secp256k1';
+    end;
+    NID_secp384r1 : begin
+      Result := 'secp384r1';
+    end;
+    NID_sect283k1 : Begin
+      Result := 'secp283k1';
+    End;
+    NID_secp521r1 : begin
+      Result := 'secp521r1';
+    end
+  else Result := '(Unknown ID:'+inttostr(EC_OpenSSL_NID)+')';
+  end;
+end;
+
+class function TAccountComp.IsAccountBlockedByProtocol(account_number, blocks_count: Cardinal): Boolean;
+begin
+  if blocks_count<CT_WaitNewBlocksBeforeTransaction then result := true
+  else begin
+    Result := ((blocks_count-CT_WaitNewBlocksBeforeTransaction) * CT_AccountsPerBlock) <= account_number;
+  end;
+end;
+
+class function TAccountComp.IsValidAccountKey(account: TAccountKey; var errors : AnsiString): Boolean;
+Var _a, _b: AnsiString;
+begin
+  errors := '';
+  case account.EC_OpenSSL_NID of
+    NID_secp256k1,NID_secp384r1,NID_sect283k1,NID_secp521r1 : begin
+      Result := TECPrivateKey.IsValidPublicKey(account);
+      if Not Result then begin
+        errors := Format('Invalid AccountKey type:%d - Length x:%d y:%d Error:%s',[account.EC_OpenSSL_NID,length(account.x),length(account.y),  ERR_error_string(ERR_get_error(),nil)]);
+      end;
+    end;
+  else
+    errors := Format('Invalid AccountKey type:%d (Unknown type) - Length x:%d y:%d',[account.EC_OpenSSL_NID,length(account.x),length(account.y)]);
+    Result := False;
+  end;
+  if (errors='') And (Not Result) then errors := ERR_error_string(ERR_get_error(),nil);
+end;
+
+class function TAccountComp.PrivateToAccountkey(key: TECPrivateKey): TAccountKey;
+begin
+  Result := key.PublicKey;
+end;
+
+class function TAccountComp.RawString2Accountkey(rawaccstr: AnsiString): TAccountKey;
+Var s : TMemoryStream;
+begin
+  Result := CT_TECDSA_Public_Nul;
+  s := TMemoryStream.Create;
+  try
+    s.WriteBuffer(rawaccstr[1],length(rawaccstr));
+    s.Position := 0;
+    s.Read(Result.EC_OpenSSL_NID,SizeOf(Result.EC_OpenSSL_NID));
+    TStreamOp.ReadAnsiString(s,Result.x);
+    TStreamOp.ReadAnsiString(s,Result.y);
+  finally
+    s.Free;
+  end;
+end;
+
+class function TAccountComp.TxtToMoney(Const moneytxt : AnsiString; var money : Int64) : Boolean;
+Var s : AnsiString;
+begin
+  money := 0;
+  if Trim(moneytxt)='' then begin
+    Result := true;
+    exit;
+  end;
+  try
+    s := StringReplace(moneytxt,ThousandSeparator,DecimalSeparator,[rfReplaceAll]);
+    money := Round( StrToFloat(s)*10000 );
+    Result := true;
+  Except
+    result := false;
+  end;
+end;
+
+class procedure TAccountComp.ValidsEC_OpenSSL_NID(list: TList);
+begin
+  list.Clear;
+  list.Add(TObject(NID_secp256k1)); // = 714
+  list.Add(TObject(NID_secp384r1)); // = 715
+  list.Add(TObject(NID_sect283k1)); // = 729
+  list.Add(TObject(NID_secp521r1)); // = 716
+end;
+
+{ TPCSafeBox }
+
+function TPCSafeBox.Account(account_number: Cardinal): TAccount;
+var b : Cardinal;
+begin
+  b := account_number DIV CT_AccountsPerBlock;
+  if (b<0) Or (b>=FBlockAccountsList.Count) then raise Exception.Create('Invalid account: '+IntToStr(account_number));
+  Result := PBlockAccount(FBlockAccountsList.Items[b])^.accounts[account_number MOD CT_AccountsPerBlock];
+end;
+
+function TPCSafeBox.AddNew(Const accountkey: TAccountKey; reward: UInt64; timestamp: Cardinal; compact_target: Cardinal; Const proof_of_work: AnsiString) : TBlockAccount;
+var i, base_addr : Integer;
+  P : PBlockAccount;
+  accs : Array of cardinal;
+begin
+  base_addr := BlocksCount * CT_AccountsPerBlock;
+  Result := CT_BlockAccount_NUL;
+  Result.blockaccount := BlocksCount;
+  setlength(accs,length(Result.accounts));
+  for i := Low(Result.accounts) to High(Result.accounts) do begin
+    Result.accounts[i] := CT_Account_NUL;
+    Result.accounts[i].account := base_addr + i;
+    Result.accounts[i].accountkey := accountkey;
+    Result.accounts[i].updated_block := BlocksCount;
+    Result.accounts[i].n_operation := 0;
+    if i=0 then begin
+      // Only first account wins the reward + fee
+      Result.accounts[i].balance := reward + FTotalFee;
+    end else begin
+    end;
+    accs[i] := base_addr + i;
+  end;
+  Result.timestamp := timestamp;
+  Result.block_hash := CalcBlockHash(Result);
+  New(P);
+  P^ := Result;
+  FBlockAccountsList.Add(P);
+  FBufferBlocksHash := FBufferBlocksHash+Result.block_hash;
+  Inc(FTotalBalance,reward + FTotalFee);
+  Dec(FTotalFee,FTotalFee);
+  AccountKeyListAddAccounts(accountkey,accs);
+  // Calculating new value of safebox
+  FSafeBoxHash := CalcSafeBoxHash;
+end;
+
+procedure TPCSafeBox.AccountKeyListAddAccounts(const AccountKey: TAccountKey; accounts: array of Cardinal);
+Var i : Integer;
+begin
+  for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
+    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).AddAccounts(AccountKey,accounts);
+  end;
+end;
+
+procedure TPCSafeBox.AccountKeyListRemoveAccount(const AccountKey: TAccountKey; accounts: array of Cardinal);
+Var i : Integer;
+begin
+  for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
+    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).RemoveAccounts(AccountKey,accounts);
+  end;
+end;
+
+function TPCSafeBox.AccountsCount: Integer;
+begin
+  Result := BlocksCount * CT_AccountsPerBlock;
+end;
+
+function TPCSafeBox.Block(block_number: Cardinal): TBlockAccount;
+begin
+  if (block_number<0) Or (block_number>=FBlockAccountsList.Count) then raise Exception.Create('Invalid block number: '+inttostr(block_number));
+  Result := PBlockAccount(FBlockAccountsList.Items[block_number])^;
+end;
+
+class function TPCSafeBox.BlockAccountToText(const block: TBlockAccount): AnsiString;
+begin
+  Result := Format('Block:%d Timestamp:%d BlockHash:%s',
+    [block.blockaccount, block.timestamp,
+     TCrypto.ToHexaString(block.block_hash)]);
+end;
+
+function TPCSafeBox.BlocksCount: Integer;
+begin
+  Result := FBlockAccountsList.Count;
+end;
+
+class function TPCSafeBox.CalcBlockHash(const block : TBlockAccount): AnsiString;
+Var s: AnsiString;
+  ms : TMemoryStream;
+  i : Integer;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(block.blockaccount,4); // Little endian
+    for i := Low(block.accounts) to High(block.accounts) do begin
+      ms.Write(block.accounts[i].account,4);  // Little endian
+      s := TAccountComp.AccountKey2RawString(block.accounts[i].accountkey);
+      ms.WriteBuffer(s[1],length(s)); // Raw bytes
+      ms.Write(block.accounts[i].balance,SizeOf(Uint64));  // Little endian
+      ms.Write(block.accounts[i].updated_block,4);  // Little endian
+      ms.Write(block.accounts[i].n_operation,4); // Little endian
+    end;
+    ms.Write(block.timestamp,4); // Little endian
+    Result := TCrypto.DoSha256(ms.Memory,ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+function TPCSafeBox.CalcSafeBoxHash: TRawBytes;
+begin
+  // If No buffer to hash is because it's firts block... so use Genesis: CT_Genesis_Magic_String_For_Old_Block_Hash
+  if (FBufferBlocksHash='') then Result := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash)
+  else Result := TCrypto.DoSha256(FBufferBlocksHash);
+end;
+
+procedure TPCSafeBox.Clear;
+Var i : Integer;
+  P : PBlockAccount;
+begin
+  for i := 0 to FBlockAccountsList.Count - 1 do begin
+    P := FBlockAccountsList.Items[i];
+    Dispose(P);
+  end;
+  FBlockAccountsList.Clear;
+  For i:=0 to FListOfOrderedAccountKeysList.count-1 do begin
+    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).Clear(False);
+  end;
+  FBufferBlocksHash := '';
+  FTotalBalance := 0;
+  FTotalFee := 0;
+  FSafeBoxHash := CalcSafeBoxHash;
+end;
+
+procedure TPCSafeBox.CopyFrom(accounts: TPCSafeBox);
+Var i,j : Cardinal;
+  P : PBlockAccount;
+  BA : TBlockAccount;
+begin
+  if accounts=Self then exit;
+  Clear;
+  if accounts.BlocksCount>0 then begin
+    for i := 0 to accounts.BlocksCount - 1 do begin
+      BA := accounts.Block(i);
+      New(P);
+      P^ := BA;
+      FBlockAccountsList.Add(P);
+      for j := Low(BA.accounts) to High(BA.accounts) do begin
+        AccountKeyListAddAccounts(BA.accounts[j].accountkey,[BA.accounts[j].account]);
+      end;
+    end;
+  end;
+
+  FTotalBalance := accounts.TotalBalance;
+  FTotalFee := accounts.FTotalFee;
+  FBufferBlocksHash := accounts.FBufferBlocksHash;
+  FSafeBoxHash := accounts.FSafeBoxHash;
+end;
+
+constructor TPCSafeBox.Create;
+begin
+  InitializeCriticalSection(FLock);
+  FIsLocked := false;
+  FBlockAccountsList := TList.Create;
+  FListOfOrderedAccountKeysList := TList.Create;
+  Clear;
+end;
+
+destructor TPCSafeBox.Destroy;
+Var i : Integer;
+begin
+  Clear;
+  for i := 0 to FListOfOrderedAccountKeysList.Count - 1 do begin
+    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).FAccountList := Nil;
+  end;
+  FBlockAccountsList.Free;
+  FListOfOrderedAccountKeysList.Free;
+  DeleteCriticalSection(Flock);
+  inherited;
+end;
+
+procedure TPCSafeBox.EndThreadSave;
+begin
+  if Not FIsLocked then raise Exception.Create('Is not locked');
+  FIsLocked := False;
+  LeaveCriticalSection(FLock);
+end;
+
+function TPCSafeBox.LoadFromStream(Stream : TStream; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;
+Var w : Word;
+  blockscount,iblock,iacc : Cardinal;
+  s : AnsiString;
+  block : TBlockAccount;
+  P : PBlockAccount;
+  j : Integer;
+begin
+  Clear;
+  Result := false;
+  Try
+    errors := 'Invalid stream';
+    TStreamOp.ReadAnsiString(Stream,s);
+    if (s<>CT_MagicIdentificator) then exit;
+    errors := 'Invalid version or corrupted stream';
+    if Stream.Size<8 then exit;
+    Stream.Read(w,2);
+    if w<>CT_Protocol_Version then exit;
+    Stream.Read(w,2); // protocol version available, nothing to do with it
+    Stream.Read(blockscount,4);
+    if blockscount>(CT_NewLineSecondsAvg*2000000) then exit; // Protection for corrupted data...
+    errors := 'Corrupted stream';
+    for iblock := 0 to blockscount-1 do begin
+      errors := 'Corrupted stream reading block '+inttostr(iblock+1)+'/'+inttostr(blockscount);
+      block := CT_BlockAccount_NUL;
+      if Stream.Read(block.blockaccount,4)<4 then exit;
+      if (block.blockaccount<>iblock) then exit; // Invalid value
+      for iacc := Low(block.accounts) to High(block.accounts) do begin
+        errors := 'Corrupted stream reading account '+inttostr(iacc+1)+'/'+inttostr(length(block.accounts))+' of block '+inttostr(iblock+1)+'/'+inttostr(blockscount);
+        if Stream.Read(block.accounts[iacc].account,4)<4 then exit;
+        if TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+        block.accounts[iacc].accountkey := TAccountComp.RawString2Accountkey(s);
+        if Stream.Read(block.accounts[iacc].balance,SizeOf(UInt64))<SizeOf(UInt64) then exit;
+        if Stream.Read(block.accounts[iacc].updated_block,4)<4 then exit;
+        if Stream.Read(block.accounts[iacc].n_operation,4)<4 then exit;
+        // check valid
+        if not TAccountComp.IsValidAccountKey(block.accounts[iacc].accountkey,s) then begin
+          errors := errors + ' > '+s;
+          exit;
+        end;
+        inc(FTotalBalance,block.accounts[iacc].balance);
+      end;
+      errors := 'Corrupted stream reading block hash '+inttostr(iblock+1)+'/'+inttostr(blockscount);
+      if Stream.Read(block.timestamp,4)<4 then exit;
+      if TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+      block.block_hash := s;
+      // Check is valid:
+      if CalcBlockHash(block)<>block.block_hash then exit;
+      // Add
+      New(P);
+      P^ := block;
+      FBlockAccountsList.Add(P);
+      for j := low(block.accounts) to High(block.accounts) do begin
+        AccountKeyListAddAccounts(block.accounts[j].accountkey,[block.accounts[j].account]);
+      end;
+      FBufferBlocksHash := FBufferBlocksHash+block.block_hash;
+      LastReadBlock := block;
+    end;
+    Result := true;
+  Finally
+    if Not Result then Clear;
+  End;
+end;
+
+procedure TPCSafeBox.SaveToStream(Stream: TStream);
+Var
+  c,iblock,iacc : Cardinal;
+  b : TBlockAccount;
+begin
+  TStreamOp.WriteAnsiString(Stream,CT_MagicIdentificator);
+  Stream.Write(CT_Protocol_Version,SizeOf(CT_Protocol_Version));
+  Stream.Write(CT_Protocol_Available,SizeOf(CT_Protocol_Available));
+  c := BlocksCount;
+  Stream.Write(c,Sizeof(c));
+  for iblock := 0 to c-1 do begin
+    b := Block(iblock);
+    Stream.Write(b.blockaccount,SizeOf(b.blockaccount)); // Little endian
+    for iacc := Low(b.accounts) to High(b.accounts) do begin
+      Stream.Write(b.accounts[iacc].account,Sizeof(b.accounts[iacc].account));
+      TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(b.accounts[iacc].accountkey));
+      Stream.Write(b.accounts[iacc].balance,Sizeof(b.accounts[iacc].balance));
+      Stream.Write(b.accounts[iacc].updated_block,Sizeof(b.accounts[iacc].updated_block));
+      Stream.Write(b.accounts[iacc].n_operation,Sizeof(b.accounts[iacc].n_operation));
+    end;
+    Stream.Write(b.timestamp,Sizeof(b.timestamp));
+    TStreamOp.WriteAnsiString(Stream,b.block_hash);
+  end;
+end;
+
+procedure TPCSafeBox.SetAccount(account_number : Cardinal; newAccountkey: TAccountKey; newBalance: UInt64; newN_operation: Cardinal);
+Var iBlock : Cardinal;
+  i,j,iAccount : Integer;
+  lastbalance : UInt64;
+  P : PBlockAccount;
+begin
+  iBlock := account_number DIV CT_AccountsPerBlock;
+  iAccount := account_number MOD CT_AccountsPerBlock;
+  P := FBlockAccountsList.Items[iBlock];
+  if (NOT TAccountComp.Equal(P^.accounts[iAccount].accountkey,newAccountkey)) then begin
+    AccountKeyListRemoveAccount(P^.accounts[iAccount].accountkey,[account_number]);
+    AccountKeyListAddAccounts(newAccountkey,[account_number]);
+  end;
+
+  P^.accounts[iAccount].accountkey := newAccountkey;
+  lastbalance := P^.accounts[iAccount].balance;
+  P^.accounts[iAccount].balance := newBalance;
+  P^.accounts[iAccount].updated_block := BlocksCount;
+  P^.accounts[iAccount].n_operation := newN_operation;
+  P^.block_hash := CalcBlockHash(P^);
+  j := (length(P^.block_hash)*(iBlock));
+  for i := 1 to length(P^.block_hash) do begin
+    FBufferBlocksHash[i+j] := P^.block_hash[i];
+  end;
+
+  FTotalBalance := FTotalBalance - (Int64(lastbalance)-Int64(newBalance));
+  FTotalFee := FTotalFee + (Int64(lastbalance)-Int64(newBalance));
+end;
+
+procedure TPCSafeBox.StartThreadSafe;
+begin
+  if FIsLocked then Begin
+    TLog.NewLog(lterror,Classname,'IS LOCKED !!!');
+    raise Exception.Create('IS LOCKED !!!');
+  end;
+  TPCThread.ProtectEnterCriticalSection(Self,FLock);
+  FIsLocked := true;
+end;
+
+{ TPCSafeBoxTransaction }
+
+function TPCSafeBoxTransaction.Account(account_number: Cardinal): TAccount;
+Var i :Integer;
+begin
+  if FOrderedList.Find(account_number,i) then Result := PAccount(FOrderedList.FList[i])^
+  else begin
+    Result := FreezedSafeBox.Account(account_number);
+  end;
+end;
+
+function TPCSafeBoxTransaction.CheckIntegrity: Boolean;
+begin
+  Result := FOldSafeBoxHash = FFreezedAccounts.FSafeBoxHash;
+end;
+
+procedure TPCSafeBoxTransaction.CleanTransaction;
+begin
+  FOrderedList.Clear;
+  FOldSafeBoxHash := FFreezedAccounts.FSafeBoxHash;
+  FTotalBalance := FFreezedAccounts.FTotalBalance;
+  FTotalFee := 0;
+end;
+
+function TPCSafeBoxTransaction.Commit(accountkey: TAccountKey; reward: UInt64; timestamp: Cardinal; compact_target: Cardinal; proof_of_work: AnsiString; var errors : AnsiString) : Boolean;
+Var i,j : Integer;
+  B : TBlockAccount;
+  Pa : PAccount;
+begin
+  Result := false;
+  errors := '';
+  FFreezedAccounts.StartThreadSafe;
+  try
+    if not CheckIntegrity then begin
+      errors := 'Invalid integrity in accounts transaction on commit';
+      exit;
+    end;
+    for i := 0 to FOrderedList.FList.Count - 1 do begin
+      Pa := PAccount(FOrderedList.FList[i]);
+      FFreezedAccounts.SetAccount(Pa^.account,
+            Pa^.accountkey,
+            Pa^.balance,
+            Pa^.n_operation);
+    end;
+    //
+    if (FFreezedAccounts.TotalBalance<>FTotalBalance) then begin
+      TLog.NewLog(lterror,ClassName,Format('Invalid integrity balance! StrongBox:%d Transaction:%d',[FFreezedAccounts.TotalBalance,FTotalBalance]));
+    end;
+    if (FFreezedAccounts.FTotalFee<>FTotalFee) then begin
+      TLog.NewLog(lterror,ClassName,Format('Invalid integrity fee! StrongBox:%d Transaction:%d',[FFreezedAccounts.FTotalFee,FTotalFee]));
+    end;
+    B := FFreezedAccounts.AddNew(accountkey,reward,timestamp,compact_target,proof_of_work);
+    if (B.accounts[0].balance<>(reward + FTotalFee)) then begin
+      TLog.NewLog(lterror,ClassName,Format('Invalid integrity reward! Account:%d Balance:%d  Reward:%d Fee:%d (Reward+Fee:%d)',
+        [B.accounts[0].account,B.accounts[0].balance,reward,FTotalFee,reward+FTotalFee]));
+    end;
+    CleanTransaction;
+    Result := true;
+  finally
+    FFreezedAccounts.EndThreadSave;
+  end;
+end;
+
+procedure TPCSafeBoxTransaction.CopyFrom(transaction : TPCSafeBoxTransaction);
+Var i : Integer;
+  P : PAccount;
+begin
+  if transaction=Self then exit;
+  if transaction.FFreezedAccounts<>FFreezedAccounts then raise Exception.Create('Invalid Freezed accounts to copy');
+  CleanTransaction;
+  for i := 0 to transaction.FOrderedList.FList.Count - 1 do begin
+    P := PAccount(transaction.FOrderedList.FList[i]);
+    FOrderedList.Add(P^);
+  end;
+  FOldSafeBoxHash := transaction.FOldSafeBoxHash;
+  FTotalBalance := transaction.FTotalBalance;
+  FTotalFee := transaction.FTotalFee;
+end;
+
+constructor TPCSafeBoxTransaction.Create(SafeBox : TPCSafeBox);
+begin
+  FOrderedList := TOrderedAccountList.Create;
+  FFreezedAccounts := SafeBox;
+  FOldSafeBoxHash := SafeBox.FSafeBoxHash;
+  FTotalBalance := FFreezedAccounts.FTotalBalance;
+  FTotalFee := 0;
+end;
+
+destructor TPCSafeBoxTransaction.Destroy;
+begin
+  CleanTransaction;
+  FOrderedList.Free;
+  inherited;
+end;
+
+function TPCSafeBoxTransaction.GetInternalAccount(account_number: Cardinal): PAccount;
+Var i :Integer;
+  P : PAccount;
+begin
+  if FOrderedList.Find(account_number,i) then Result := PAccount(FOrderedList.FList[i])
+  else begin
+    i := FOrderedList.Add( FreezedSafeBox.Account(account_number) );
+    Result := PAccount(FOrderedList.FList[i]);
+  end;
+end;
+
+function TPCSafeBoxTransaction.Modified(index: Integer): TAccount;
+begin
+  Result := FOrderedList.Get(index);
+end;
+
+function TPCSafeBoxTransaction.ModifiedCount: Integer;
+begin
+  Result := FOrderedList.Count;
+end;
+
+procedure TPCSafeBoxTransaction.Rollback;
+begin
+  CleanTransaction;
+end;
+
+function TPCSafeBoxTransaction.TransferAmount(sender, target, n_operation : Cardinal; amount, fee: UInt64; var errors: AnsiString): Boolean;
+Var
+  intSender, intTarget : Integer;
+  PaccSender, PaccTarget : PAccount;
+begin
+  Result := false;
+  errors := '';
+  if not CheckIntegrity then begin
+    errors := 'Invalid integrity in accounts transaction';
+    exit;
+  end;
+  if (sender<0) Or (sender>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Or
+     (target<0) Or (target>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) then begin
+     errors := 'Invalid sender or target on transfer';
+     exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(sender,FFreezedAccounts.BlocksCount) then begin
+    errors := 'Sender account is blocked for protocol';
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(target,FFreezedAccounts.BlocksCount) then begin
+    errors := 'Target account is blocked for protocol';
+    Exit;
+  end;
+  PaccSender := GetInternalAccount(sender);
+  PaccTarget := GetInternalAccount(target);
+  if (PaccSender^.n_operation+1<>n_operation) then begin
+    errors := 'Incorrect n_operation';
+    Exit;
+  end;
+  if (PaccSender^.balance < (amount+fee)) then begin
+    errors := 'Insuficient founds';
+    Exit;
+  end;
+  if ((PaccTarget^.balance + amount)>CT_MaxWalletAmount) then begin
+    errors := 'Max account balance';
+    Exit;
+  end;
+  if (fee>CT_MaxTransactionFee) then begin
+    errors := 'Max fee';
+    Exit;
+  end;
+  PaccSender^.updated_block := FFreezedAccounts.BlocksCount;
+  PaccTarget^.updated_block := FFreezedAccounts.BlocksCount;
+  PaccSender^.n_operation := n_operation;
+  PaccSender^.balance := PaccSender^.balance - (amount + fee);
+  PaccTarget^.balance := PaccTarget^.balance + (amount);
+
+  Dec(FTotalBalance,fee);
+  inc(FTotalFee,fee);
+  Result := true;
+end;
+
+function TPCSafeBoxTransaction.UpdateAccountkey(account_number, n_operation: Cardinal; accountkey: TAccountKey; fee: UInt64; var errors: AnsiString): Boolean;
+Var intAccount : Integer;
+  P : PAccount;
+begin
+  Result := false;
+  errors := '';
+  if (account_number<0) Or (account_number>=(FFreezedAccounts.BlocksCount*CT_AccountsPerBlock)) Then begin
+     errors := 'Invalid account';
+     exit;
+  end;
+  if (TAccountComp.IsAccountBlockedByProtocol(account_number,FFreezedAccounts.BlocksCount)) then begin
+    errors := 'account is blocked for protocol';
+    Exit;
+  end;
+  P := GetInternalAccount(account_number);
+  if (P^.n_operation+1<>n_operation) then begin
+    errors := 'Incorrect n_operation';
+    Exit;
+  end;
+  if (P^.balance < fee) then begin
+    errors := 'Insuficient founds';
+    Exit;
+  end;
+  P^.updated_block := FFreezedAccounts.BlocksCount;
+  P^.n_operation := n_operation;
+  P^.accountkey := accountkey;
+  Dec(P^.balance,fee);
+  Dec(FTotalBalance,fee);
+  Inc(FTotalFee,fee);
+  Result := true;
+end;
+
+{ TOrderedAccountList }
+
+Function TOrderedAccountList.Add(const account: TAccount) : Integer;
+Var P : PAccount;
+begin
+  if Find(account.account,Result) then begin
+    PAccount(FList[Result])^ := account;
+  end else begin
+    New(P);
+    P^:=account;
+    FList.Insert(Result,P);
+  end;
+end;
+
+procedure TOrderedAccountList.Clear;
+Var i : Integer;
+  P : PAccount;
+begin
+  for I := 0 to FList.Count - 1 do begin
+    P := FList[i];
+    Dispose(P);
+  end;
+  FList.Clear;
+end;
+
+function TOrderedAccountList.Count: Integer;
+begin
+  Result := FList.Count;
+end;
+
+constructor TOrderedAccountList.Create;
+begin
+  FList := TList.Create;
+end;
+
+destructor TOrderedAccountList.Destroy;
+begin
+  Clear;
+  FList.Free;
+  inherited;
+end;
+
+function TOrderedAccountList.Find(const account_number: Cardinal; var Index: Integer): Boolean;
+var L, H, I: Integer;
+  C : Int64;
+begin
+  Result := False;
+  L := 0;
+  H := FList.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := Int64(PAccount(FList[I]).account) - Int64(account_number);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function TOrderedAccountList.Get(index: Integer): TAccount;
+begin
+  Result := PAccount(FList.Items[index])^;
+end;
+
+{ TOrderedAccountKeysList }
+Type
+  TOrderedAccountKeyList = Record
+    rawaccountkey : TRawBytes;
+    accounts : TList;
+  end;
+  POrderedAccountKeyList = ^TOrderedAccountKeyList;
+
+function SortOrdered(Item1, Item2: Pointer): Integer;
+begin
+   Result := Integer(Item1) - Integer(Item2);
+end;
+
+procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
+Var P : POrderedAccountKeyList;
+  i,j : Integer;
+begin
+  if Not Find(AccountKey,i) then begin
+    New(P);
+    P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+    P^.accounts := TList.Create;
+    FOrderedAccountKeysList.Insert(i,P);
+    // Search this key in the AccountsList and add all...
+    j := 0;
+    if Assigned(FAccountList) then begin
+      For i:=0 to FAccountList.AccountsCount-1 do begin
+        If TAccountComp.Equal(FAccountList.Account(i).accountkey,AccountKey) then begin
+          // Note: P^.accounts will be ascending ordered due to "for i:=0 to ..."
+          P^.accounts.Add(TObject(i));
+        end;
+      end;
+      TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d of %d) %s',[j,FAccountList.AccountsCount,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
+    end else begin
+      TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
+    end;
+  end;
+end;
+
+procedure TOrderedAccountKeysList.AddAccounts(const AccountKey: TAccountKey; accounts: array of Cardinal);
+Var P : POrderedAccountKeyList;
+  i : Integer;
+begin
+  if Find(AccountKey,i) then begin
+    P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
+  end else if (FAutoAddAll) then begin
+    New(P);
+    P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+    P^.accounts := TList.Create;
+    FOrderedAccountKeysList.Insert(i,P);
+  end else exit;
+  for i := Low(accounts) to High(accounts) do begin
+    If P^.accounts.IndexOf(TObject(accounts[i]))<0 then begin
+      // Add ordered
+      P^.accounts.Add(TObject(accounts[i]));
+    end;
+    P^.accounts.SortList(SortOrdered);
+  end;
+end;
+
+procedure TOrderedAccountKeysList.Clear(RemoveAccountList : Boolean);
+Var P : POrderedAccountKeyList;
+  i : Integer;
+begin
+  for i := 0 to FOrderedAccountKeysList.Count - 1 do begin
+    P := FOrderedAccountKeysList[i];
+    if RemoveAccountList then begin
+      P^.accounts.Free;
+      Dispose(P);
+    end else begin
+      P^.accounts.Clear;
+    end;
+  end;
+  if RemoveAccountList then begin
+    FOrderedAccountKeysList.Clear;
+  end;
+end;
+
+function TOrderedAccountKeysList.Count: Integer;
+begin
+  Result := FOrderedAccountKeysList.Count;
+end;
+
+constructor TOrderedAccountKeysList.Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
+Var i : Integer;
+begin
+  TLog.NewLog(ltdebug,Classname,'Creating an Ordered Account Keys List adding all:'+CT_TRUE_FALSE[AutoAddAll]);
+  FAutoAddAll := AutoAddAll;
+  FAccountList := AccountList;
+  FOrderedAccountKeysList := TList.Create;
+  if Assigned(AccountList) then begin
+    AccountList.FListOfOrderedAccountKeysList.Add(Self);
+    if AutoAddAll then begin
+      for i := 0 to AccountList.AccountsCount - 1 do begin
+        AddAccountKey(AccountList.Account(i).accountkey);
+      end;
+    end;
+  end;
+end;
+
+destructor TOrderedAccountKeysList.Destroy;
+begin
+  TLog.NewLog(ltdebug,Classname,'Destroying an Ordered Account Keys List adding all:'+CT_TRUE_FALSE[FAutoAddAll]);
+  if Assigned(FAccountList) then begin
+    FAccountList.FListOfOrderedAccountKeysList.Remove(Self);
+  end;
+  Clear(true);
+  FOrderedAccountKeysList.Free;
+  inherited;
+end;
+
+function TOrderedAccountKeysList.Find(const AccountKey: TAccountKey; var Index: Integer): Boolean;
+var L, H, I, C: Integer;
+  rak : TRawBytes;
+begin
+  Result := False;
+  rak := TAccountComp.AccountKey2RawString(AccountKey);
+  L := 0;
+  H := FOrderedAccountKeysList.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := CompareStr( POrderedAccountKeyList(FOrderedAccountKeysList[I]).rawaccountkey, rak );
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
+Var raw : TRawBytes;
+begin
+  raw := POrderedAccountKeyList(FOrderedAccountKeysList[index]).rawaccountkey;
+  Result := TAccountComp.RawString2Accountkey(raw);
+end;
+
+function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TList;
+begin
+  Result := POrderedAccountKeyList(FOrderedAccountKeysList[index]).accounts;
+end;
+
+function TOrderedAccountKeysList.IndexOfAccountKey(const AccountKey: TAccountKey): Integer;
+begin
+  If Not Find(AccountKey,Result) then Result := -1;
+end;
+
+procedure TOrderedAccountKeysList.RemoveAccounts(const AccountKey: TAccountKey; accounts: array of Cardinal);
+Var P : POrderedAccountKeyList;
+  i,j : Integer;
+begin
+  if Not Find(AccountKey,i) then exit; // Nothing to do
+  P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
+  for j := Low(accounts) to High(accounts) do begin
+    P^.accounts.Remove(TObject(accounts[j]));
+  end;
+  if (P^.accounts.Count=0) And (FAutoAddAll) then begin
+    // Remove from list
+    FOrderedAccountKeysList.Delete(i);
+    // Free it
+    P^.accounts.free;
+    Dispose(P);
+  end;
+end;
+
+procedure TOrderedAccountKeysList.RemoveAccountKey(const AccountKey: TAccountKey);
+Var P : POrderedAccountKeyList;
+  i,j : Integer;
+begin
+  if Not Find(AccountKey,i) then exit; // Nothing to do
+  P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
+  // Remove from list
+  FOrderedAccountKeysList.Delete(i);
+  // Free it
+  P^.accounts.free;
+  Dispose(P);
+end;
+
+end.

+ 1739 - 0
Units/PascalCoin/UBlockChain.pas

@@ -0,0 +1,1739 @@
+unit UBlockChain;
+
+{ 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, UCrypto, UAccounts, Windows, ULog;
+
+
+Type
+  TOperationBlock = Record
+    block: Cardinal;
+    account_key: TAccountKey;
+    reward: UInt64;
+    fee: UInt64;
+    protocol_version: Word;     // Protocol version
+    protocol_available: Word;   // Used to upgrade protocol
+    timestamp: Cardinal;        // Timestamp creation
+    compact_target: Cardinal;   // Target in compact form
+    nonce: Cardinal;            // Random value to generate a new P-o-W
+    block_payload : TRawBytes;  // RAW Payload that a miner can include to a blockchain
+    initial_safe_box_hash: TRawBytes; // RAW Safe Box Hash value (32 bytes, it's a Sha256)
+    operations_hash: TRawBytes; // RAW sha256 (32 bytes) of Operations
+    proof_of_work: TRawBytes;   // RAW Double Sha256
+  end;
+
+  {
+
+    Bank BlockChain:
+
+    Safe Box content: (See Unit "UAccounts.pas" to see pascal code)
+    +--------------+--------------------------------------------------+------------+------------+
+    + BlockAccount + Each BlockAccount has N "Account"                +  Timestamp + Block Hash +
+    +              +--------------------------------------------------+            +            +
+    +              + Addr B0 + Public key +  Balance + updated + n_op +            +            +
+    +              + Addr B1 + Public key +  Balance + updated + n_op +            +            +
+    +              + ......                                           +            +            +
+    +              + Addr B4 + Public key +  Balance + updated + n_op +            +            +
+    +--------------+---------+----------------------------------------+------------+------------+
+    +            0 +       0 + pk_aaaaaaa + 100.0000 +       0 +    0 + 1461701856 +   Sha256() +
+    +              +       1 + pk_aaaaaaa +   0.0000 +       0 +    0 +            + = h1111111 +
+    +              +       2 + pk_aaaaaaa +   0.0000 +       0 +    0 +            +            +
+    +              +       3 + pk_aaaaaaa +   0.0000 +       0 +    0 +            +            +
+    +              +       4 + pk_aaaaaaa +   0.0000 +       0 +    0 +            +            +
+    +--------------+---------+----------------------------------------+------------+------------+
+    +            1 +       5 + pk_bbbbbbb + 100.0000 +       0 +    0 + 1461702960 +   Sha256() +
+    +              +       6 + pk_bbbbbbb +   0.0000 +       0 +    0 +            + = h2222222 +
+    +              +       7 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +              +       8 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +              +       9 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +--------------+---------+----------------------------------------+------------+------------+
+    +     ................                                                                      +
+    +--------------+---------+----------------------------------------+------------+------------+
+    +            5 +      25 + pk_bbbbbbb + 100.0000 +       0 +    0 + 1461713484 +   Sha256() +
+    +              +      26 + pk_bbbbbbb +   0.0000 +       0 +    0 +            + = h3333333 +
+    +              +      27 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +              +      28 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +              +      29 + pk_bbbbbbb +   0.0000 +       0 +    0 +            +            +
+    +--------------+---------+----------------------------------------+------------+------------+
+    +  Safe Box Hash  : Sha256(h1111111 + h2222222 + ... + h3333333) = sbh_A1                   +
+    +-------------------------------------------------------------------------------------------+
+
+    BlockChain:
+
+    To generate a BlockChain (block X) we need the previous "Safe Box Hash"
+    (the Safe Box Hash number X-1, generated when BlockChain X-1 was generated)
+    Each BlockChain block generates a new "Safe Box" with a new "Safe Box Hash"
+
+    With this method, Safe Box is unique after a BlockChain, so we can assume
+    that a hard coded Safe Box X is the same that to load all previous BlockChain
+    from 0 to X. Conclusion: It's not necessary historical operations (block chains)
+    to work with Pascal Coin
+
+    Some BlockChain fields:
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+    + Block + Account key     +  reward  + fee  + protocols + timestamp  + target + nonce + Miner Payload + safe box hash + operations hash + Proof of Work + Operations stream     +
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+    +     0 + (hard coded)    + 100.0000 +    0 +   1 +   0 + 1461701856 + trgt_1 +  ...  + (Hard coded)  +  (Hard coded) + Sha256(Operat.) + 000000C3F5... + Operations of block 0 +
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+    +     1 + hhhhhhhhhhhhhhh + 100.0000 +    0 +   1 +   0 + 1461701987 + trgt_1 +  ...  +      ...      + SFH block 0   + Sha256(Operat.) + 000000A987... + Operations of block 1 +
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+    +     2 + iiiiiiiiiiiiiii + 100.0000 + 0.43 +   1 +   0 + 1461702460 + trgt_1 +  ...  +      ...      + SFH block 1   + Sha256(Operat.) + 0000003A1C... + Operations of block 2 +
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+    +       .....                                                                                                                                                   +
+    +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
+
+    Considerations:
+    - Account Key: Is a public key that will have all new generated Accounts of the Safe Box
+    - Protocols are 2 values: First indicate protocol of this block, second future candidate protocol that is allowed by miner who made this. (For protocol upgrades)
+    - Safe Box Has: Each Block of the Bloch Chain is made in base of a previous Safe Box. This value hard codes consistency
+    - Operations Stream includes all the operations that will be made to the Safe Box after this block is generated. A hash value of Operations stream is "Operations Hash"
+
+    Operations:
+
+    Each Block of the Block Chain has its owns operations that will be used to change Safe Box after block is completed and included in BlockChain
+
+    Operations of actual Protocol (version 1) can be one of this:
+    - Transaction from 1 account to 1 account
+    - Change AccountKey of an account
+    - Recover balance from an unused account (lost keys)
+
+    Each Operation has a Hash value that is used to generate "Operations Hash". Operations Hash is a Sha256 of all the Operations included
+    inside it hashed like a Merkle Tree.
+
+    In unit "UOpTransaction.pas" you can see how each Operation Works.
+
+}
+
+Type
+
+  TPCBank = Class;
+  TPCBankNotify = Class;
+  TPCOperation = Class;
+  TPCOperationClass = Class of TPCOperation;
+
+  TPCOperation = Class
+  Private
+    Ftag: integer;
+    FAuxBalance: Int64;
+  public
+    function GetOperationBufferToHash: TRawBytes; virtual; abstract;
+    function DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors: AnsiString): Boolean; virtual; abstract;
+    function SaveToStream(Stream: TStream): Boolean; virtual; abstract;
+    function LoadFromStream(Stream: TStream): Boolean; virtual; abstract;
+    procedure AffectedAccounts(list : TList); virtual; abstract;
+    class function OpType: Byte; virtual; abstract;
+    function OperationAmount : Int64; virtual; abstract;
+    function OperationFee: UInt64; virtual; abstract;
+    function OperationPayload : TRawBytes; virtual; abstract;
+    function SenderAccount : Cardinal; virtual; abstract;
+    Property tag : integer read Ftag Write Ftag;
+    Property AuxBalance : Int64 read FAuxBalance Write FAuxBalance;
+    Class Function IsReadablePayload(const Payload : TRawBytes) : Boolean;
+  End;
+
+  TOperationsHashTree = Class
+  private
+    FHashTreeOperations : TThreadList;
+    FHashTree: TRawBytes;
+    Procedure InternalAddOperationToHashTree(list : TList; op : TPCOperation);
+  public
+    Constructor Create;
+    Destructor Destroy; Override;
+    Procedure AddOperationToHashTree(op : TPCOperation);
+    Procedure ClearHastThree;
+    Property HashTree : TRawBytes read FHashTree;
+    Function OperationsCount : Integer;
+    Function GetOperation(index : Integer) : TPCOperation;
+    Function GetOperationsAffectingAccount(account_number : Cardinal; List : TList) : Integer;
+    Procedure CopyFromHashTree(Sender : TOperationsHashTree);
+  End;
+
+  TPCOperationsComp = Class(TComponent)
+  private
+    FBank: TPCBank;
+    FSafeBoxTransaction : TPCSafeBoxTransaction;
+    FOperationBlock: TOperationBlock;
+    FOperationsHashTree : TOperationsHashTree;
+    FDigest_Basic : TRawBytes;
+    FDigest_Operations : TRawBytes;
+    FIsOnlyOperationBlock: Boolean;
+    function GetOperation(index: Integer): TPCOperation;
+    procedure SetBank(const value: TPCBank);
+    procedure SetnOnce(const value: Cardinal);
+    procedure Settimestamp(const value: Cardinal);
+    function GetnOnce: Cardinal;
+    function Gettimestamp: Cardinal;
+    procedure SetAccountKey(const value: TAccountKey);
+    function GetAccountKey: TAccountKey;
+    Procedure Calc_Digest_Basic;
+    Procedure Calc_Digest_Operations;
+    Function CalcProofOfWork(fullcalculation : Boolean): TRawBytes;
+    function GetBlockPayload: TRawBytes;
+    procedure SetBlockPayload(const Value: TRawBytes);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  public
+    Constructor Create(AOwner: TComponent); Override;
+    Destructor Destroy; Override;
+    Procedure CopyFromExceptAddressKey(Operations : TPCOperationsComp);
+    Function CopyFromAndValidate(Operations : TPCOperationsComp; var errors : AnsiString) : Boolean;
+    Function AddOperation(Execute : Boolean; op: TPCOperation; var errors: AnsiString): Boolean;
+    Function AddOperations(operations: TOperationsHashTree; var errors: AnsiString): Integer;
+    Property Operation[index: Integer]: TPCOperation read GetOperation;
+    Property bank: TPCBank read FBank write SetBank;
+    Procedure Clear(DeleteOperations : Boolean);
+    Function Count: Integer;
+    Property OperationBlock: TOperationBlock read FOperationBlock;
+    Class Function OperationBlockToText(OperationBlock: TOperationBlock) : AnsiString;
+    Property AccountKey: TAccountKey read GetAccountKey write SetAccountKey;
+    Property nonce: Cardinal read GetnOnce write SetnOnce;
+    Property timestamp: Cardinal read Gettimestamp write Settimestamp;
+    Property BlockPayload : TRawBytes read GetBlockPayload write SetBlockPayload;
+    Function IncrementNOnce: Boolean;
+    procedure UpdateTimestamp;
+    function SaveToStream(save_header, save_only_OperationBlock : Boolean; Stream: TStream): Boolean;
+    function LoadFromStream(ExecuteOperations : Boolean; load_header : Boolean; Stream: TStream; var errors: AnsiString): Boolean;
+    //
+    Function ValidateOperationBlock(var errors : AnsiString) : Boolean;
+    Property IsOnlyOperationBlock : Boolean read FIsOnlyOperationBlock;
+    //
+    Procedure SanitizeOperations;
+
+    Class Function RegisterOperationClass(OpClass: TPCOperationClass): Boolean;
+    Class Function IndexOfOperationClass(OpClass: TPCOperationClass): Integer;
+    Class Function IndexOfOperationClassByOpType(OpType: Cardinal): Integer;
+    Class Function GetOperationClassByOpType(OpType: Cardinal): TPCOperationClass;
+    Class Function GetFirstBlock : TOperationBlock;
+    //
+    Property SafeBoxTransaction : TPCSafeBoxTransaction read FSafeBoxTransaction;
+    Property OperationsHashTree : TOperationsHashTree read FOperationsHashTree;
+  End;
+
+  TPCBankLog = procedure(sender: TPCBank; Operations: TPCOperationsComp; Logtype: TLogType ; Logtxt: AnsiString) of object;
+
+  TPCBankNotify = Class(TComponent)
+  private
+    FOnNewBlock: TNotifyEvent;
+    FBank: TPCBank;
+    procedure SetBank(const Value: TPCBank);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+    Procedure NotifyNewBlock;
+  public
+    Constructor Create(AOwner: TComponent); Override;
+    Destructor Destroy; Override;
+    Property Bank : TPCBank read FBank write SetBank;
+    Property OnNewBlock : TNotifyEvent read FOnNewBlock write FOnNewBlock;
+  End;
+
+  TOrphan = AnsiString;
+
+  TStorage = Class(TComponent)
+  private
+    FOrphan: TOrphan;
+    FBank : TPCBank;
+    procedure SetBank(const Value: TPCBank);
+  protected
+    procedure SetOrphan(const Value: TOrphan); virtual;
+    Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
+    Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
+    Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan) : Boolean; virtual; abstract;
+    Function DoSaveBank : Boolean; virtual; abstract;
+    Function DoRestoreBank(max_block : Int64) : Boolean; virtual; abstract;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); virtual; abstract;
+    Function BlockExists(Block : Cardinal) : Boolean; virtual; abstract;
+  public
+    Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
+    Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
+    Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan) : Boolean;
+    Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan);
+    Function SaveBank : Boolean;
+    Function RestoreBank(max_block : Int64) : Boolean;
+    Constructor Create(AOwner : TComponent); Override;
+    Property Orphan : TOrphan read FOrphan write SetOrphan;
+    Property Bank : TPCBank read FBank write SetBank;
+    Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
+  End;
+
+  TStorageClass = Class of TStorage;
+
+  TPCBank = Class(TComponent)
+  private
+    FStorage : TStorage;
+    FSafeBox: TPCSafeBox;
+    FLastOperationBlock: TOperationBlock;
+    FInitialSafeBoxHash: TRawBytes;
+    FActualTargetHash: TRawBytes;
+    FIsRestoringFromFile: Boolean;
+    FOnLog: TPCBankLog;
+    FBankLock: TRTLCriticalSection;
+    FNotifyList : TList;
+    FStorageClass: TStorageClass;
+    function GetStorage: TStorage;
+    procedure SetStorageClass(const Value: TStorageClass);
+  protected
+  public
+    Constructor Create(AOwner: TComponent); Override;
+    Destructor Destroy; Override;
+    Function BlocksCount: Cardinal;
+    Function AccountsCount : Cardinal;
+    procedure AssignTo(Dest: TPersistent); Override;
+    Class Function GetRewardForNewLine(line_index: Cardinal): UInt64;
+    Class Function TargetToCompact(target: TRawBytes): Cardinal;
+    Class Function TargetFromCompact(encoded: Cardinal): TRawBytes;
+    Class Function GetNewTarget(vteorical, vreal: Cardinal; Const actualTarget: TRawBytes): TRawBytes;
+    Function GetActualCompactTargetHash: Cardinal;
+    function GetActualTargetHash: AnsiString;
+    function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
+    function LoadFromStream(Stream : TStream; var errors : AnsiString) : Boolean;
+    Procedure SaveToStream(Stream : TStream);
+    Procedure Clear;
+    Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
+    Property SafeBox : TPCSafeBox read FSafeBox;
+    Function AddNewBlockChainBlock(Operations: TPCOperationsComp; var newBlock: TBlockAccount; var errors: AnsiString): Boolean;
+    Procedure DiskRestoreFromOperations(max_block : Int64);
+    Procedure NewLog(Operations: TPCOperationsComp; Logtype: TLogType; Logtxt: AnsiString);
+    Property OnLog: TPCBankLog read FOnLog write FOnLog;
+    Property LastOperationBlock : TOperationBlock read FLastOperationBlock;
+    Property Storage : TStorage read GetStorage;
+    Property StorageClass : TStorageClass read FStorageClass write SetStorageClass;
+    Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
+  End;
+
+Const
+  CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:'';y:'');reward:0;fee:0;protocol_version:0;
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:'';initial_safe_box_hash:'';operations_hash:'';proof_of_work:'');
+
+implementation
+
+uses
+  Messages, SysUtils, Variants, Graphics, Controls, Forms,
+  Dialogs, StdCtrls,
+  UTime, UConst, UThread;
+
+{ TPCBank }
+
+function TPCBank.AccountsCount: Cardinal;
+begin
+  Result := FSafeBox.AccountsCount;
+end;
+
+function TPCBank.AddNewBlockChainBlock(Operations: TPCOperationsComp; var newBlock: TBlockAccount; var errors: AnsiString): Boolean;
+Var
+  buffer, pow: AnsiString;
+  i : Integer;
+begin
+  TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
+  Try
+    Result := False;
+    errors := '';
+    Try
+      If Not Operations.ValidateOperationBlock(errors) then begin
+        exit;
+      end;
+      // Check valid data
+      if (BlocksCount <> Operations.OperationBlock.block) then begin
+        errors := 'block ('+inttostr(Operations.OperationBlock.block)+') is not new position ('+inttostr(BlocksCount)+')';
+        exit;
+      end;
+      if (SafeBox.TotalBalance<>(Operations.FSafeBoxTransaction.TotalBalance+Operations.FSafeBoxTransaction.TotalFee)) then begin
+        errors := Format('Invalid integrity balance at SafeBox. Actual Balance:%d  New Balance:(%d + fee %d = %d)',
+          [SafeBox.TotalBalance,
+            Operations.FSafeBoxTransaction.TotalBalance,
+            Operations.FSafeBoxTransaction.TotalFee,
+            Operations.FSafeBoxTransaction.TotalBalance+Operations.FSafeBoxTransaction.TotalFee]);
+        exit;
+      end;
+      if (Operations.OperationBlock.block > 0) then begin
+        if ((Operations.OperationBlock.timestamp) < (FLastOperationBlock.timestamp)) then begin
+          errors := 'Invalid timestamp (New timestamp:'+inttostr(Operations.OperationBlock.timestamp)+' last timestamp ('+Inttostr(SafeBox.BlocksCount-1)+'):'+Inttostr(FLastOperationBlock.timestamp)+')';
+          exit;
+        end;
+        if (Operations.OperationBlock.timestamp > (UnivDateTimeToUnix(DateTime2UnivDateTime(now))+CT_MaxSecondsDifferenceOfNetworkNodes)) then begin
+          errors := 'Invalid timestamp (Future time '+Inttostr(Operations.OperationBlock.timestamp)+'-'+inttostr(UnivDateTimeToUnix(DateTime2UnivDateTime(now)))+'='+
+             inttostr(Operations.OperationBlock.timestamp-UnivDateTimeToUnix(DateTime2UnivDateTime(now)))+' > '+inttostr(CT_MaxSecondsDifferenceOfNetworkNodes)+')';
+          exit;
+        end;
+      end else begin
+        // Check if valid Zero block
+        if Not (AnsiSameText(TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work),CT_Zero_Block_Proof_of_work_in_Hexa)) then begin
+          errors := 'Zero block not valid, Proof of Work invalid: '+TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work)+'<>'+CT_Zero_Block_Proof_of_work_in_Hexa;
+          exit;
+        end;
+      end;
+      if (Operations.OperationBlock.compact_target <> GetActualCompactTargetHash) then begin
+        errors := 'Invalid target found:'+IntToHex(Operations.OperationBlock.compact_target,8)+' actual:'+IntToHex(GetActualCompactTargetHash,8);
+        exit;
+      end;
+      if (Operations.OperationBlock.proof_of_work > GetActualTargetHash) then begin
+        errors := 'Proof of work is higher than target';
+        exit;
+      end;
+      if (Operations.OperationBlock.initial_safe_box_hash <> SafeBox.CalcSafeBoxHash) then begin
+        errors := 'BlockChain Safe box hash invalid: '+TCrypto.ToHexaString(Operations.OperationBlock.initial_safe_box_hash)+' var: '+
+          TCrypto.ToHexaString(FInitialSafeBoxHash)+
+          ' Calculated:'+TCrypto.ToHexaString(SafeBox.CalcSafeBoxHash);
+        exit;
+      end;
+
+      // Ok, include!
+      // WINNER !!!
+      // Congrats!
+
+      if Not Operations.SafeBoxTransaction.Commit(Operations.OperationBlock.account_key,
+        Operations.OperationBlock.reward,
+        Operations.OperationBlock.timestamp,Operations.OperationBlock.compact_target,
+        Operations.OperationBlock.proof_of_work,errors) then begin
+        exit;
+      end;
+      newBlock := SafeBox.Block(SafeBox.BlocksCount-1);
+
+      // Initialize values
+      FActualTargetHash := GetActualTargetHash;
+      FInitialSafeBoxHash := SafeBox.CalcSafeBoxHash;
+      FLastOperationBlock := Operations.OperationBlock;
+      // log it!
+      NewLog(Operations, ltupdate,
+        Format('New block height:%d nOnce:%d timestamp:%d Operations:%d Fee:%d SafeBoxBalance:%d=%d PoW:%s Operations previous Safe Box hash:%s Future old Safe Box hash for next block:%s',
+          [ Operations.OperationBlock.block,Operations.OperationBlock.nonce,Operations.OperationBlock.timestamp,
+            Operations.Count,
+            Operations.OperationBlock.fee,
+            SafeBox.TotalBalance,
+            Operations.SafeBoxTransaction.TotalBalance,
+            TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work),
+            TCrypto.ToHexaString(Operations.OperationBlock.initial_safe_box_hash),
+            TCrypto.ToHexaString(FInitialSafeBoxHash)]));
+      // Save Operations to disk
+      if Not FIsRestoringFromFile then begin
+        Storage.SaveBlockChainBlock(Operations);
+      end;
+      Operations.Clear(true);
+      Result := true;
+    Finally
+      if Not Result then
+        NewLog(Operations, lterror, 'Invalid new block '+inttostr(Operations.OperationBlock.block)+': ' + errors);
+    End;
+  Finally
+    LeaveCriticalSection(FBankLock);
+  End;
+  if Result then begin
+    for i := 0 to FNotifyList.Count - 1 do begin
+      TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
+    end;
+  end;
+end;
+
+procedure TPCBank.AssignTo(Dest: TPersistent);
+var d : TPCBank;
+begin
+  if (Not (Dest is TPCBank)) then begin
+    inherited;
+    exit;
+  end;
+  if (Self=Dest) then exit;
+
+  d := TPCBank(Dest);
+  d.SafeBox.CopyFrom(SafeBox);
+  d.FInitialSafeBoxHash := FInitialSafeBoxHash;
+  d.FLastOperationBlock := FLastOperationBlock;
+  d.FActualTargetHash := FActualTargetHash;
+  d.FIsRestoringFromFile := FIsRestoringFromFile;
+end;
+
+function TPCBank.BlocksCount: Cardinal;
+begin
+  Result := SafeBox.BlocksCount;
+end;
+
+procedure TPCBank.Clear;
+begin
+  SafeBox.Clear;
+  FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
+  FLastOperationBlock.initial_safe_box_hash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash); // Genesis hash
+  FInitialSafeBoxHash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash); // Genesis hash
+  FActualTargetHash := TargetFromCompact(CT_MinCompactTarget);
+  NewLog(Nil, ltupdate, 'Clear Bank');
+end;
+
+constructor TPCBank.Create(AOwner: TComponent);
+begin
+  inherited;
+  FStorage := Nil;
+  FStorageClass := Nil;
+  InitializeCriticalSection(FBankLock);
+  FIsRestoringFromFile := False;
+  FOnLog := Nil;
+  FSafeBox := TPCSafeBox.Create;
+  FNotifyList := TList.Create;
+  Clear;
+end;
+
+destructor TPCBank.Destroy;
+var step : String;
+begin
+  Try
+    step := 'Deleting critical section';
+    DeleteCriticalSection(FBankLock);
+    step := 'Clear';
+    Clear;
+    step := 'Destroying SafeBox';
+    FreeAndNil(FSafeBox);
+    step := 'Destroying NotifyList';
+    FreeAndNil(FNotifyList);
+    step := 'Destroying Storage';
+    FreeAndNil(FStorage);
+    step := 'inherited';
+    inherited;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error destroying Bank step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
+      Raise;
+    end;
+  End;
+end;
+
+procedure TPCBank.DiskRestoreFromOperations(max_block : Int64);
+Var
+  errors: AnsiString;
+  newBlock: TBlockAccount;
+  Operations: TPCOperationsComp;
+begin
+  if FIsRestoringFromFile then begin
+    TLog.NewLog(lterror,Classname,'Is Restoring!!!');
+    raise Exception.Create('Is restoring!');
+  end;
+  TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
+  try
+    FIsRestoringFromFile := true;
+    try
+      Clear;
+      Storage.RestoreBank(max_block);
+      NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' +Storage.Orphan);
+      Operations := TPCOperationsComp.Create(Self);
+      try
+        while ((BlocksCount<=max_block)) do begin
+          if Storage.BlockExists(BlocksCount) then begin
+            if Storage.LoadBlockChainBlock(Operations,BlocksCount) then begin
+                  if Not AddNewBlockChainBlock(Operations,newBlock,errors) then begin
+                    NewLog(Operations, lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
+                    Storage.DeleteBlockChainBlocks(BlocksCount,Storage.Orphan);
+                    break;
+                  end else Storage.SaveBank;
+            end else break;
+          end else break;
+        end;
+      finally
+        Operations.Free;
+      end;
+      NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan);
+    finally
+      FIsRestoringFromFile := False;
+    end;
+  finally
+    LeaveCriticalSection(FBankLock);
+  end;
+end;
+
+function TPCBank.GetActualCompactTargetHash: Cardinal;
+begin
+  Result := TargetToCompact(GetActualTargetHash);
+end;
+
+function TPCBank.GetActualTargetHash: AnsiString;
+  { Target is calculated in each block with avg obtained in previous
+    CT_CalcNewDifficulty blocks.
+    If Block is lower than CT_CalcNewDifficulty then is calculated
+    with all previous blocks.
+  }
+Var ts1, ts2, tsTeorical, tsReal: Int64;
+  CalcBack : Integer;
+begin
+  if (BlocksCount <= 1) then begin
+    // Important: CT_MinCompactTarget is applied for blocks 0 until ((CT_CalcNewDifficulty*2)-1)
+    FActualTargetHash := TargetFromCompact(CT_MinCompactTarget);
+  end else begin
+    if BlocksCount > CT_CalcNewTargetBlocksAverage then CalcBack := CT_CalcNewTargetBlocksAverage
+    else CalcBack := BlocksCount-1;
+    // Calc new target!
+    ts1 := SafeBox.Block(BlocksCount-1).timestamp;
+    ts2 := SafeBox.Block(BlocksCount-CalcBack-1).timestamp;
+    tsTeorical := (CalcBack * CT_NewLineSecondsAvg);
+    tsReal := (ts1 - ts2);
+    FActualTargetHash := GetNewTarget(tsTeorical, tsReal,TargetFromCompact(FLastOperationBlock.compact_target));
+  end;
+  Result := FActualTargetHash;
+
+  exit;
+
+  if (BlocksCount <= CT_CalcNewTargetBlocksAverage) then begin
+    // Important: CT_MinCompactTarget is applied for blocks 0 until ((CT_CalcNewDifficulty*2)-1)
+    FActualTargetHash := TargetFromCompact(CT_MinCompactTarget);
+  end else begin
+    if (BlocksCount MOD CT_CalcNewTargetBlocksAverage) = 0 then begin
+      // Calc new target!
+      ts1 := SafeBox.Block(BlocksCount-1).timestamp;
+      ts2 := SafeBox.Block(BlocksCount-CT_CalcNewTargetBlocksAverage-1).timestamp;
+      tsTeorical := (CT_CalcNewTargetBlocksAverage * CT_NewLineSecondsAvg);
+      tsReal := (ts1 - ts2);
+      FActualTargetHash := GetNewTarget(tsTeorical, tsReal,TargetFromCompact(FLastOperationBlock.compact_target));
+    end;
+  end;
+  Result := FActualTargetHash;
+end;
+
+function TPCBank.GetActualTargetSecondsAverage(BackBlocks: Cardinal): Real;
+Var ts1, ts2: Int64;
+begin
+  if BlocksCount>BackBlocks then begin
+    ts1 := SafeBox.Block(BlocksCount-1).timestamp;
+    ts2 := SafeBox.Block(BlocksCount-BackBlocks-1).timestamp;
+  end else if (BlocksCount>1) then begin
+    ts1 := SafeBox.Block(BlocksCount-1).timestamp;
+    ts2 := SafeBox.Block(0).timestamp;
+    BackBlocks := BlocksCount-1;
+  end else begin
+    Result := 0;
+    exit;
+  end;
+  Result := (ts1 - ts2) / BackBlocks;
+end;
+
+class function TPCBank.GetNewTarget(vteorical, vreal: Cardinal; Const actualTarget: TRawBytes): TRawBytes;
+Var
+  bnact, bnaux, bnmindiff, bnremainder, bn: TBigNum;
+  ts1, ts2: Cardinal;
+  tsTeorical, tsReal, factor1000, factor1000Min, factor1000Max: Int64;
+begin
+  { Given a teorical time in seconds (vteorical>0) and a real time in seconds (vreal>0)
+    and an actual target, calculates a new target
+    by % of difference of teorical vs real.
+
+    Increment/decrement is adjusted to +-200% in a full CT_CalcNewTargetBlocksAverage round
+    ...so each new target is a maximum +-(100% DIV (CT_CalcNewTargetBlocksAverage DIV 2)) of
+    previous target. This makes target more stable.
+    }
+  tsTeorical := vteorical;
+  tsReal := vreal;
+  factor1000 := (((tsTeorical - tsReal) * 1000) DIV (tsTeorical)) * (-1);
+
+  { Important: Note that a -500 is the same that divide by 2 (-100%), and
+    1000 is the same that multiply by 2 (+100%), so we limit increase
+    in a limit [-500..+1000] for a complete (CT_CalcNewTargetBlocksAverage DIV 2) round }
+  if CT_CalcNewTargetBlocksAverage>1 then begin
+    factor1000Min := (-500) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
+    factor1000Max := (1000) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
+  end else begin
+    factor1000Min := (-500);
+    factor1000Max := (1000);
+  end;
+
+  if factor1000 < factor1000Min then factor1000 := factor1000Min
+  else if factor1000 > factor1000Max then factor1000 := factor1000Max
+  else if factor1000=0 then begin
+    Result := actualTarget;
+    exit;
+  end;
+
+  // Calc new target by increasing factor (-500 <= x <= 1000)
+  bn := TBigNum.Create(factor1000);
+  bnact := TBigNum.Create(0);
+  try
+    bnact.RawValue := actualTarget;
+    bnaux := bnact.Copy;
+    try
+      bnact.Multiply(factor1000).Divide(1000).Add(bnaux);
+    finally
+      bnaux.Free;
+    end;
+    // Adjust to TargetCompact limitations:
+    Result := TargetFromCompact(TargetToCompact(bnact.RawValue));
+  finally
+    bn.Free;
+    bnact.Free;
+  end;
+end;
+
+class function TPCBank.GetRewardForNewLine(line_index: Cardinal): UInt64;
+Var n, i : Cardinal;
+begin
+  n := (line_index + 1) DIV CT_NewLineRewardDecrease;
+  Result := CT_FirstReward;
+  for i := 1 to n do begin
+    Result := Result DIV 2;
+  end;
+  if (Result < CT_MinReward) then
+    Result := CT_MinReward;
+end;
+
+function TPCBank.GetStorage: TStorage;
+begin
+  if Not Assigned(FStorage) then begin
+    if Not Assigned(FStorageClass) then raise Exception.Create('StorageClass not defined');
+    FStorage := FStorageClass.Create(Self);
+    FStorage.Bank := Self;
+  end;
+  Result := FStorage;
+end;
+
+function TPCBank.IsReady(var CurrentProcess: AnsiString): Boolean;
+begin
+  Result := false;
+  CurrentProcess := '';
+  if FIsRestoringFromFile then CurrentProcess := 'Is restoring from file...'
+  else Result := true;
+end;
+
+function TPCBank.LoadFromStream(Stream: TStream; var errors : AnsiString) : Boolean;
+Var LastReadBlock : TBlockAccount;
+  op : TPCOperationsComp;
+  i : Integer;
+begin
+  Clear;
+  Result := SafeBox.LoadFromStream(Stream,LastReadBlock,errors);
+  if Result then begin
+    TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
+    try
+      op := TPCOperationsComp.Create(Self);
+      try
+        if Not LoadOperations(op,BlocksCount-1) then begin
+          errors := 'Cannot read operations of block '+inttostr(BlocksCount-1);
+          Result := false;
+          Clear;
+          exit;
+        end;
+        FLastOperationBlock := op.OperationBlock;
+      finally
+        op.Free;
+      end;
+      FInitialSafeBoxHash := SafeBox.CalcSafeBoxHash;
+      if (BlocksCount>0) then FActualTargetHash := TargetFromCompact( FLastOperationBlock.compact_target );
+      // Initialize new target hash:
+      FActualTargetHash := GetActualTargetHash;
+    finally
+      LeaveCriticalSection(FBankLock);
+    end;
+    for i := 0 to FNotifyList.Count - 1 do begin
+      TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
+    end;
+  end;
+end;
+
+
+function TPCBank.LoadOperations(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
+begin
+  TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
+  try
+    Result := Storage.LoadBlockChainBlock(Operations,Block);
+  finally
+    LeaveCriticalSection(FBankLock);
+  end;
+end;
+
+procedure TPCBank.NewLog(Operations: TPCOperationsComp; Logtype: TLogType; Logtxt: AnsiString);
+var s : AnsiString;
+begin
+  if Assigned(Operations) then s := Operations.ClassName
+  else s := Classname;
+  TLog.NewLog(Logtype,s,Logtxt);
+  if Assigned(FOnLog) then
+    FOnLog(Self, Operations, Logtype, Logtxt);
+end;
+
+procedure TPCBank.SaveToStream(Stream: TStream);
+begin
+  SafeBox.SaveToStream(Stream);
+end;
+
+procedure TPCBank.SetStorageClass(const Value: TStorageClass);
+begin
+  if FStorageClass=Value then exit;
+  FStorageClass := Value;
+  if Assigned(FStorage) then FreeAndNil(FStorage);
+end;
+
+class function TPCBank.TargetFromCompact(encoded: Cardinal): TRawBytes;
+Var
+  nbits, high, offset, i: Cardinal;
+  bn: TBigNum;
+  s1,s2 : String;
+begin
+  {
+    Compact Target is a 4 byte value that tells how many "0" must have the hash at left if presented in binay format.
+    First byte indicates haw many "0 bits" are on left, so can be from 0x00 to 0xE7
+    (Because 24 bits are reserved for 3 bytes, and 1 bit is implicit, max: 256-24-1=231=0xE7)
+    Next 3 bytes indicates next value in XOR, stored in RAW format
+
+    Example: If we want a hash lower than 0x0000 0000 0000 65A0 A2F4 +29 bytes
+    Binary "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 1111 0100"
+    That is 49 zeros on left before first 1. So first byte is 49 decimal = 0x31
+    After we have "110 0101 1010 0000 1010 0010 1111 0100 1111 0100" but we only can accept first 3 bytes,
+    also note that first "1" is implicit, so value is transformed in
+    binary as "10 0101 1010 0000 1010 0010 11" that is 0x96828B
+    But note that we must XOR this value, so result offset is: 0x697D74
+    Compacted value is: 0x31697D74
+
+    When translate compact target back to target: ( 0x31697D74 )
+    0x31 = 49 bits at "0", then 1 bit at "1" followed by XOR 0x697D74 = 0x96828B
+    49 "0" bits "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0"
+    0x96828B "1001 0110 1000 0010 1000 1011"
+    Hash target = "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 11.. ...."
+    Fill last "." with "1"
+    Hash target = "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 1111 1111"
+    Hash target = 0x00 00 00 00 00 00 65 A0 A2 FF + 29 bytes
+    Note that is not exactly the same than expected due to compacted format
+    }
+  nbits := encoded shr 24;
+  i := CT_MinCompactTarget shr 24;
+  if nbits < i then
+    nbits := i; // min nbits
+  if nbits > 231 then
+    nbits := 231; // max nbits
+
+  offset := (encoded shl 8) shr 8;
+  // Make a XOR at offset and put a "1" on the left
+  offset := ((offset XOR $00FFFFFF) OR ($01000000));
+
+  bn := TBigNum.Create(offset);
+  bn.LShift(256 - nbits - 25);
+  Result := bn.RawValue;
+  Result := StringOfChar(#0, 32 - Length(Result)) + Result;
+  if length(Result)<>32 then begin
+    raise Exception.Create('TargetFromCompact result length<>32 '+inttostr(Length(Result)));
+  end;
+  bn.Free;
+end;
+
+class function TPCBank.TargetToCompact(target: TRawBytes): Cardinal;
+Var
+  bn, bn2: TBigNum;
+  i: Int64;
+  nbits: Cardinal;
+  c: AnsiChar;
+begin
+  { See instructions in explanation of TargetFromCompact }
+  Result := 0;
+  if length(target)>32 then begin
+    raise Exception.Create('Invalid target to compact: '+TCrypto.ToHexaString(target)+' ('+inttostr(length(target))+')');
+  end;
+  target := StringOfChar(#0, 32 - Length(target)) + target;
+  bn := TBigNum.Create(0);
+  bn2 := TBigNum.Create('8000000000000000000000000000000000000000000000000000000000000000'); // First bit 1 followed by 0
+  try
+    bn.RawValue := target;
+    nbits := 0;
+    while (bn.CompareTo(bn2) < 0) And (nbits < 231) do
+    begin
+      bn2.RShift(1);
+      inc(nbits);
+    end;
+    i := CT_MinCompactTarget shr 24;
+    if (nbits < i) then
+    begin
+      Result := CT_MinCompactTarget;
+      exit;
+    end;
+    bn.RShift((256 - 25) - nbits);
+    Result := (nbits shl 24) + ((bn.value AND $00FFFFFF) XOR $00FFFFFF);
+  finally
+    bn.Free;
+    bn2.Free;
+  end;
+end;
+
+{ TPCOperationsComp }
+
+var
+  _OperationsClass: Array of TPCOperationClass;
+
+Function TPCOperationsComp.AddOperation(Execute : Boolean; op: TPCOperation; var errors: AnsiString): Boolean;
+Begin
+  errors := '';
+  Result := False;
+  if Execute then begin
+    if (FBank = Nil) then begin
+      errors := 'No Bank';
+      exit;
+    end;
+    if (FBank.BlocksCount<>OperationBlock.block) then begin
+      errors := 'Bank blockcount<>OperationBlock.Block';
+      exit;
+    end;
+    // Only process when in current address, prevent do it when reading operations from file
+    Result := op.DoOperation(SafeBoxTransaction, errors);
+  end else Result := true;
+  if Result then begin
+    FOperationsHashTree.AddOperationToHashTree(op);
+    FOperationBlock.fee := FOperationBlock.fee + op.OperationFee;
+    FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
+    Calc_Digest_Operations;
+  end;
+End;
+
+
+function TPCOperationsComp.AddOperations(operations: TOperationsHashTree; var errors: AnsiString): Integer;
+Var i : Integer;
+  e : AnsiString;
+begin
+  Result := 0;
+  errors := '';
+  if operations=FOperationsHashTree then exit;
+  for i := 0 to operations.OperationsCount - 1 do begin
+    if not AddOperation(true,operations.GetOperation(i),e) then begin
+      if (errors<>'') then errors := errors+' ';
+      errors := errors + 'Op'+inttostr(i+1)+'/'+inttostr(operations.OperationsCount)+':'+e;
+    end else inc(Result);
+  end;
+end;
+
+function TPCOperationsComp.CalcProofOfWork(fullcalculation : Boolean): TRawBytes;
+Var ms : TMemoryStream;
+begin
+  if fullcalculation then begin
+    Calc_Digest_Basic;
+    Calc_Digest_Operations;
+  end;
+  ms := TMemoryStream.Create;
+  try
+    ms.WriteBuffer(FDigest_Basic[1],length(FDigest_Basic));
+    ms.WriteBuffer(FDigest_Operations[1],length(FDigest_Operations));
+    ms.Write(FOperationBlock.timestamp,4);
+    ms.Write(FOperationBlock.nonce,4);
+    Result := TCrypto.DoDoubleSha256(ms.Memory,ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+procedure TPCOperationsComp.Calc_Digest_Basic;
+var ms : TMemoryStream;
+  s : AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FOperationBlock.block,Sizeof(FOperationBlock.block)); // Little endian
+    s := TAccountComp.AccountKey2RawString(FOperationBlock.account_key);
+    ms.WriteBuffer(s[1],length(s));
+    ms.Write(FOperationBlock.reward,Sizeof(FOperationBlock.reward)); // Little endian
+    ms.Write(FOperationBlock.protocol_version,Sizeof(FOperationBlock.protocol_version)); // Little endian
+    ms.Write(FOperationBlock.protocol_available,Sizeof(FOperationBlock.protocol_available)); // Little endian
+    ms.Write(FOperationBlock.compact_target,Sizeof(FOperationBlock.compact_target)); // Little endian
+    ms.WriteBuffer(FOperationBlock.block_payload[1],length(FOperationBlock.block_payload));
+    ms.WriteBuffer(FOperationBlock.initial_safe_box_hash[1],length(FOperationBlock.initial_safe_box_hash));
+    SetLength(FDigest_Basic,ms.Size);
+    ms.Position :=0;
+    ms.ReadBuffer(FDigest_Basic[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+procedure TPCOperationsComp.Calc_Digest_Operations;
+var ms : TMemoryStream;
+  i: Cardinal;
+  buff: AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.WriteBuffer(FOperationsHashTree.HashTree[1],length(FOperationsHashTree.HashTree));
+    ms.Write(FOperationBlock.fee,4);
+    SetLength(FDigest_Operations,ms.Size);
+    ms.Position := 0;
+    ms.ReadBuffer(FDigest_Operations[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+procedure TPCOperationsComp.Clear(DeleteOperations : Boolean);
+begin
+  Try
+    if DeleteOperations then begin
+      FOperationsHashTree.ClearHastThree;
+      if Assigned(FSafeBoxTransaction) then
+        FSafeBoxTransaction.CleanTransaction;
+    end;
+    FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    if Assigned(FBank) then begin
+      FOperationBlock.block := bank.BlocksCount;
+      FOperationBlock.reward := TPCBank.GetRewardForNewLine(bank.BlocksCount);
+      FOperationBlock.compact_target := bank.GetActualCompactTargetHash;
+      FOperationBlock.initial_safe_box_hash := bank.FInitialSafeBoxHash;
+      If Bank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
+        FOperationBlock.timestamp := Bank.LastOperationBlock.timestamp;
+    end else begin
+      FOperationBlock.block := 0;
+      FOperationBlock.reward := TPCBank.GetRewardForNewLine(0);
+      FOperationBlock.compact_target := CT_MinCompactTarget;
+      FOperationBlock.initial_safe_box_hash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash); // Nothing for first line
+    end;
+    FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
+    FOperationBlock.fee := 0;
+    FOperationBlock.nonce := 0;
+
+    FOperationBlock.proof_of_work := '';
+    FOperationBlock.protocol_version := CT_Protocol_Version;
+    FOperationBlock.protocol_available := CT_Protocol_Available;
+    FIsOnlyOperationBlock := false;
+  Finally
+    FOperationBlock.proof_of_work := CalcProofOfWork(true);
+  End;
+end;
+
+function TPCOperationsComp.CopyFromAndValidate(Operations: TPCOperationsComp; var errors: AnsiString): Boolean;
+Var i : Integer;
+  e : AnsiString;
+  op : TPCOperation;
+begin
+  errors := '';
+  if Self=Operations then begin
+    result := true;
+    exit;
+  end else Result := false;
+  Clear(true);
+  if Operations.IsOnlyOperationBlock then begin
+    errors := 'Operations is only an operation block';
+    exit;
+  end;
+  FOperationBlock := Operations.OperationBlock;
+  for i := 0 to Operations.OperationsHashTree.OperationsCount-1 do begin
+    op := Operations.OperationsHashTree.GetOperation(i);
+    if Not op.DoOperation(SafeBoxTransaction,e) then begin
+      errors := 'Error executing operation '+inttostr(i+1)+'/'+Inttostr(Operations.OperationsHashTree.OperationsCount)+':'+e;
+      exit;
+    end;
+    FOperationsHashTree.AddOperationToHashTree(op);
+  end;
+  Result := ValidateOperationBlock(errors);
+end;
+
+procedure TPCOperationsComp.CopyFromExceptAddressKey(Operations: TPCOperationsComp);
+var lastopb : TOperationBlock;
+begin
+  if Self=Operations then exit;
+  lastopb := FOperationBlock;
+  FOperationBlock := Operations.FOperationBlock;
+  FOperationBlock.account_key := lastopb.account_key; // Except AddressKey
+  FOperationBlock.compact_target := Bank.GetActualCompactTargetHash;
+  FIsOnlyOperationBlock := Operations.FIsOnlyOperationBlock;
+  FOperationsHashTree.CopyFromHashTree(Operations.FOperationsHashTree);
+  FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
+  FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
+  // Recalc all
+  FOperationBlock.proof_of_work := CalcProofOfWork(true);
+end;
+
+function TPCOperationsComp.Count: Integer;
+begin
+  Result := FOperationsHashTree.OperationsCount;
+end;
+
+constructor TPCOperationsComp.Create(AOwner: TComponent);
+begin
+  inherited;
+  FOperationsHashTree := TOperationsHashTree.Create;
+  FBank := Nil;
+  FOperationBlock := GetFirstBlock;
+  FSafeBoxTransaction := Nil;
+  if Assigned(AOwner) And (AOwner is TPCBank) then begin
+    Bank := TPCBank(AOwner);
+  end else Clear(true);
+end;
+
+destructor TPCOperationsComp.Destroy;
+begin
+  Clear(true);
+  FOperationsHashTree.Free;
+  FSafeBoxTransaction.Free;
+  inherited;
+end;
+
+function TPCOperationsComp.GetAccountKey: TAccountKey;
+begin
+  Result := FOperationBlock.account_key;
+end;
+
+function TPCOperationsComp.GetBlockPayload: TRawBytes;
+begin
+  Result := FOperationBlock.block_payload;
+end;
+
+class function TPCOperationsComp.GetFirstBlock: TOperationBlock;
+begin
+  Result := CT_OperationBlock_NUL;
+end;
+
+function TPCOperationsComp.GetnOnce: Cardinal;
+begin
+  Result := FOperationBlock.nonce;
+end;
+
+function TPCOperationsComp.GetOperation(index: Integer): TPCOperation;
+begin
+  Result := FOperationsHashTree.GetOperation(index);
+end;
+
+class function TPCOperationsComp.GetOperationClassByOpType(OpType: Cardinal): TPCOperationClass;
+Var i : Integer;
+begin
+  i := IndexOfOperationClassByOpType(OpType);
+  if i<0 then result := Nil
+  else Result := TPCOperationClass( _OperationsClass[i] );
+end;
+
+function TPCOperationsComp.Gettimestamp: Cardinal;
+begin
+  Result := FOperationBlock.timestamp;
+end;
+
+function TPCOperationsComp.IncrementNOnce: Boolean;
+begin
+  nonce := nonce + 1;
+  Result := FOperationBlock.proof_of_work < FBank.FActualTargetHash;
+end;
+
+class function TPCOperationsComp.IndexOfOperationClass(OpClass: TPCOperationClass): Integer;
+begin
+  for Result := low(_OperationsClass) to high(_OperationsClass) do
+  begin
+    if (_OperationsClass[Result] = OpClass) then
+      exit;
+  end;
+  Result := -1;
+end;
+
+class function TPCOperationsComp.IndexOfOperationClassByOpType(OpType: Cardinal): Integer;
+begin
+  for Result := low(_OperationsClass) to high(_OperationsClass) do
+  begin
+    if (_OperationsClass[Result].OpType = OpType) then
+      exit;
+  end;
+  Result := -1;
+end;
+
+function TPCOperationsComp.LoadFromStream(ExecuteOperations : Boolean; load_header : Boolean; Stream: TStream; var errors: AnsiString): Boolean;
+Var
+  c, i, lastfee: Cardinal;
+  soob : Byte;
+  OpType: Cardinal;
+  bcop: TPCOperation;
+  pow,
+  m: AnsiString;
+  j: Integer;
+  OpClass: TPCOperationClass;
+  errors2 : AnsiString;
+begin
+  Clear(true);
+  Result := False;
+  if (load_header) then begin
+    // Header: Magic string + protocol info
+    if TStreamOp.ReadAnsiString(Stream, m) < 0 then begin
+      errors := 'Invalid header';
+      exit;
+    end;
+    if (Not AnsiSameStr(CT_MagicIdentificator, m)) then begin
+      errors := 'Invalid header name';
+      exit;
+    end;
+    errors := 'Invalid header structure';
+    if (Stream.Size - Stream.Position < 4) then exit;
+    Stream.Read(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
+    Stream.Read(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
+    if (FOperationBlock.protocol_version<>CT_Protocol_Version) then begin
+      errors := 'Invalid protocol block: '+inttostr(FOperationBlock.protocol_version);
+      exit;
+    end;
+  end;
+  //
+  errors := 'Invalid structure';
+  if (Stream.Size - Stream.Position < 5) then exit;
+  Stream.Read(soob,1);
+  if soob=0 then FIsOnlyOperationBlock:=false
+  else if soob=1 then FIsOnlyOperationBlock:=true
+  else exit; // Invalid value
+
+  Stream.Read(FOperationBlock.block, Sizeof(FOperationBlock.block));
+
+  if TStreamOp.ReadAnsiString(Stream, m) < 0 then exit;
+  FOperationBlock.account_key := TAccountComp.RawString2Accountkey(m); // String2Addresskey(m);
+  if Stream.Read(FOperationBlock.reward, Sizeof(FOperationBlock.reward)) < 0 then exit;
+  if Stream.Read(FOperationBlock.fee, Sizeof(FOperationBlock.fee)) < 0 then exit;
+  if Stream.Read(FOperationBlock.timestamp, Sizeof(FOperationBlock.timestamp)) < 0 then exit;
+  if Stream.Read(FOperationBlock.compact_target, Sizeof(FOperationBlock.compact_target)) < 0 then exit;
+  if Stream.Read(FOperationBlock.nonce, Sizeof(FOperationBlock.nonce)) < 0 then exit;
+  if TStreamOp.ReadAnsiString(Stream, FOperationBlock.block_payload) < 0 then exit;
+  if TStreamOp.ReadAnsiString(Stream, FOperationBlock.initial_safe_box_hash) < 0 then exit;
+  if TStreamOp.ReadAnsiString(Stream, FOperationBlock.operations_hash) < 0 then exit;
+  if TStreamOp.ReadAnsiString(Stream, FOperationBlock.proof_of_work) < 0 then exit;
+  pow := OperationBlock.proof_of_work;
+  If FIsOnlyOperationBlock then begin
+    Result := true;
+    exit;
+  end;
+  // Fee will be calculated for each operation. Set it to 0 and check later for integrity
+  lastfee := OperationBlock.fee;
+  FOperationBlock.fee := 0;
+  Stream.Read(c, 4);
+  // c = operations count
+  for i := 1 to c do begin
+    bcop := Nil;
+    try
+      errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
+      if Stream.Size - Stream.Position < 4 then exit;
+      Stream.Read(OpType, 4);
+      j := IndexOfOperationClassByOpType(OpType);
+      if j >= 0 then
+        OpClass := _OperationsClass[j]
+      else
+        OpClass := Nil;
+      if Not Assigned(OpClass) then begin
+        errors := errors + ' optype not valid:' + InttoHex(OpType, 4);
+        exit;
+      end;
+      errors := errors + ' Operation:'+OpClass.ClassName;
+      bcop := OpClass.Create;
+      if not bcop.LoadFromStream(Stream) then begin
+        bcop.Free;
+        exit;
+      end;
+      if Not AddOperation(ExecuteOperations,bcop, errors2) then begin
+        errors := errors + ' '+errors2+' '+bcop.ToString;
+        bcop.Free;
+        exit;
+      end;
+    Except
+      FreeAndNil(bcop);
+      raise ;
+    end;
+  end;
+  // Validation control:
+  if (lastfee<>OperationBlock.fee) then begin
+    errors := 'Corrupted operations fee old:'+inttostr(lastfee)+' new:'+inttostr(OperationBlock.fee);
+    for i := 0 to FOperationsHashTree.OperationsCount - 1 do begin
+      errors := errors + ' Op'+inttostr(i+1)+':'+FOperationsHashTree.GetOperation(i).ToString;
+    end;
+    Result := false;
+    exit;
+  end;
+  Result := true;
+end;
+
+procedure TPCOperationsComp.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if (Operation = opRemove) then
+  begin
+    if AComponent = FBank then begin
+      FBank := Nil;
+      FSafeBoxTransaction.Free;
+      FSafeBoxTransaction := Nil;
+    end;
+  end;
+end;
+
+class function TPCOperationsComp.OperationBlockToText(OperationBlock: TOperationBlock): AnsiString;
+begin
+  Result := Format('Block:%d Timestamp:%d Reward:%d Fee:%d PoW:%s',[operationBlock.block,
+    operationblock.timestamp,operationblock.reward,operationblock.fee, TCrypto.ToHexaString(operationblock.proof_of_work)]);
+end;
+
+class function TPCOperationsComp.RegisterOperationClass(OpClass: TPCOperationClass): Boolean;
+Var
+  i: Integer;
+begin
+  i := IndexOfOperationClass(OpClass);
+  if i >= 0 then
+    exit;
+  SetLength(_OperationsClass, Length(_OperationsClass) + 1);
+  _OperationsClass[ high(_OperationsClass)] := OpClass;
+end;
+
+procedure TPCOperationsComp.SanitizeOperations;
+  { This function check operationblock with bank and updates itself if necessary
+    Then checks if operations are ok, and deletes old ones.
+    Finally calculates new operation pow
+    It's used when a new account has beed found by other chanels (miners o nodes...)
+    }
+Var i,n,lastn : Integer;
+  op : TPCOperation;
+  errors : AnsiString;
+  aux : TOperationsHashTree;
+begin
+  Try
+    FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    if Assigned(FBank) then begin
+      FOperationBlock.block := bank.BlocksCount;
+      FOperationBlock.reward := TPCBank.GetRewardForNewLine(bank.BlocksCount);
+      FOperationBlock.compact_target := bank.GetActualCompactTargetHash;
+      FOperationBlock.initial_safe_box_hash := bank.FInitialSafeBoxHash;
+      If Bank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
+        FOperationBlock.timestamp := Bank.LastOperationBlock.timestamp;
+    end else begin
+      FOperationBlock.block := 0;
+      FOperationBlock.reward := TPCBank.GetRewardForNewLine(0);
+      FOperationBlock.compact_target := CT_MinCompactTarget;
+      FOperationBlock.initial_safe_box_hash := TCrypto.DoSha256(CT_Genesis_Magic_String_For_Old_Block_Hash);
+    end;
+    FOperationBlock.proof_of_work := '';
+    FOperationBlock.protocol_version := CT_Protocol_Version;
+    FOperationBlock.protocol_available := CT_Protocol_Available;
+    n := 0;
+    FOperationBlock.fee := 0;
+    //
+    SafeBoxTransaction.CleanTransaction;
+    //
+    aux := TOperationsHashTree.Create;
+    Try
+      lastn := FOperationsHashTree.OperationsCount;
+      for i:=0 to lastn-1 do begin
+        op := FOperationsHashTree.GetOperation(i);
+        if (op.DoOperation(SafeBoxTransaction,errors)) then begin
+          inc(n);
+          aux.AddOperationToHashTree(op);
+          inc(FOperationBlock.fee,op.OperationFee);
+          TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString);
+        end;
+      end;
+      FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
+    Finally
+      FOperationsHashTree.Free;
+      FOperationsHashTree := aux;
+    End;
+  Finally
+    FOperationBlock.proof_of_work := CalcProofOfWork(true);
+  End;
+  if (n>0) then begin
+    TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
+  end;
+end;
+
+function TPCOperationsComp.SaveToStream(save_header, save_only_OperationBlock : Boolean; Stream: TStream): Boolean;
+Var
+  c, opl, i, OpType: Cardinal;
+  soob : Byte;
+  bcop: TPCOperation;
+  bcops, errors: AnsiString;
+begin
+  if save_header then begin
+    // Header: Magic string + protocol info
+    TStreamOp.WriteAnsiString(Stream, CT_MagicIdentificator);
+    Stream.Write(CT_Protocol_Version, Sizeof(FOperationBlock.protocol_version));
+    Stream.Write(CT_Protocol_Available, Sizeof(FOperationBlock.protocol_available));
+  end;
+  //
+  if save_only_OperationBlock then soob := 1
+  else soob := 0;
+  Stream.Write(soob,1);
+  //
+  Stream.Write(FOperationBlock.block, Sizeof(FOperationBlock.block));
+  TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FOperationBlock.account_key));
+  Stream.Write(FOperationBlock.reward, Sizeof(FOperationBlock.reward));
+  Stream.Write(FOperationBlock.fee, Sizeof(FOperationBlock.fee));
+  Stream.Write(FOperationBlock.timestamp, Sizeof(FOperationBlock.timestamp));
+  Stream.Write(FOperationBlock.compact_target, Sizeof(FOperationBlock.compact_target));
+  Stream.Write(FOperationBlock.nonce, Sizeof(FOperationBlock.nonce));
+  TStreamOp.WriteAnsiString(Stream, FOperationBlock.block_payload);
+  TStreamOp.WriteAnsiString(Stream, FOperationBlock.initial_safe_box_hash);
+  TStreamOp.WriteAnsiString(Stream, FOperationBlock.operations_hash);
+  TStreamOp.WriteAnsiString(Stream, FOperationBlock.proof_of_work);
+  if (Not save_only_OperationBlock) then begin
+    c := Count;
+    Stream.Write(c, 4);
+    // c = operations count
+    for i := 1 to c do
+    begin
+      bcop := Operation[i - 1];
+      OpType := bcop.OpType;
+      Stream.write(OpType, 4);
+      bcop.SaveToStream(Stream);
+    end;
+  end;
+  Result := true;
+end;
+
+procedure TPCOperationsComp.SetAccountKey(const value: TAccountKey);
+begin
+  if TAccountComp.AccountKey2RawString(value)=TAccountComp.AccountKey2RawString(FOperationBlock.account_key) then exit;
+  FOperationBlock.account_key := value;
+  Calc_Digest_Basic;
+end;
+
+procedure TPCOperationsComp.SetBank(const value: TPCBank);
+begin
+  if FBank = value then exit;
+  if Assigned(FBank) then begin
+     FSafeBoxTransaction.Free;
+     FSafeBoxTransaction := Nil;
+  end;
+  FBank := value;
+  if Assigned(value) then begin
+    value.FreeNotification(Self);
+    FSafeBoxTransaction := TPCSafeBoxTransaction.Create(FBank.SafeBox);
+  end;
+  Clear(true);
+end;
+
+procedure TPCOperationsComp.SetBlockPayload(const Value: TRawBytes);
+begin
+  if Value=FOperationBlock.block_payload then exit;
+  if Length(Value)>CT_MaxPayloadSize then Exit;
+  FOperationBlock.block_payload := Value;
+  FOperationBlock.proof_of_work := CalcProofOfWork(true);
+end;
+
+procedure TPCOperationsComp.SetnOnce(const value: Cardinal);
+begin
+  FOperationBlock.nonce := value;
+  FOperationBlock.proof_of_work := CalcProofOfWork(false);
+end;
+
+procedure TPCOperationsComp.Settimestamp(const value: Cardinal);
+begin
+  if FOperationBlock.timestamp=Value then exit; // No change, nothing to do
+  FOperationBlock.timestamp := value;
+  FOperationBlock.proof_of_work := CalcProofOfWork(false);
+end;
+
+procedure TPCOperationsComp.UpdateTimestamp;
+Var ts : Cardinal;
+begin
+  ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+  if Assigned(bank) then begin
+    If Bank.FLastOperationBlock.timestamp>ts then ts := Bank.FLastOperationBlock.timestamp;
+  end;
+  timestamp := ts;
+end;
+
+function TPCOperationsComp.ValidateOperationBlock(var errors : AnsiString): Boolean;
+Var lastpow : AnsiString;
+  i : Integer;
+begin
+  Result := false;
+  lastpow := OperationBlock.proof_of_work;
+  // Execute SafeBoxTransaction operations:
+  SafeBoxTransaction.Rollback;
+  for i := 0 to Count - 1 do begin
+    If Not Operation[i].DoOperation(SafeBoxTransaction,errors) then begin
+      errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors;
+      exit;
+    end;
+  end;
+  FOperationBlock.proof_of_work := CalcProofOfWork(true);
+  if Not AnsiSameStr(OperationBlock.proof_of_work,lastpow) then begin
+    errors := 'Invalid Proof of work calculation';
+    exit;
+  end;
+  if FOperationsHashTree.HashTree<>OperationBlock.operations_hash then begin
+    errors := 'Invalid Operations Hash '+TCrypto.ToHexaString(OperationBlock.operations_hash)+'<>'+TCrypto.ToHexaString(FOperationsHashTree.HashTree);
+    exit;
+  end;
+
+  if Not TAccountComp.IsValidAccountKey(OperationBlock.account_key,errors) then begin
+    exit;
+  end;
+  if (OperationBlock.reward<>TPCBank.GetRewardForNewLine(OperationBlock.block)) then begin
+    errors := 'Invalid reward';
+    exit;
+  end;
+  if (SafeBoxTransaction.TotalFee<>OperationBlock.fee) then begin
+    errors := Format('Invalid fee integrity at SafeBoxTransaction. New Balance:(%d + fee %d = %d)  OperationBlock.fee:%d',
+      [
+        SafeBoxTransaction.TotalBalance,
+        SafeBoxTransaction.TotalFee,
+        SafeBoxTransaction.TotalBalance+SafeBoxTransaction.TotalFee,
+        OperationBlock.fee]);
+    exit;
+  end;
+  Result := true;
+end;
+
+
+{ TPCBankNotify }
+
+constructor TPCBankNotify.Create(AOwner: TComponent);
+begin
+  inherited;
+  FBank := Nil;
+end;
+
+destructor TPCBankNotify.Destroy;
+begin
+  Bank := Nil;
+  inherited;
+end;
+
+procedure TPCBankNotify.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if (operation=opremove) then if AComponent=FBank then FBank:=nil;
+end;
+
+procedure TPCBankNotify.NotifyNewBlock;
+begin
+  if Assigned(FOnNewBlock) Then FOnNewBlock(Bank);
+end;
+
+procedure TPCBankNotify.SetBank(const Value: TPCBank);
+begin
+  if Assigned(FBank) then begin
+    FBank.FNotifyList.Remove(Self);
+    FBank.RemoveFreeNotification(Self);
+  end;
+  FBank := Value;
+  if Assigned(FBank) then begin
+    FBank.FreeNotification(Self);
+    FBank.FNotifyList.Add(Self);
+  end;
+end;
+
+{ TOperationsHashTree }
+
+procedure TOperationsHashTree.AddOperationToHashTree(op: TPCOperation);
+Var l : TList;
+begin
+  l := FHashTreeOperations.LockList;
+  try
+    InternalAddOperationToHashTree(l,op);
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
+procedure TOperationsHashTree.ClearHastThree;
+var op : TPCOperation;
+  l : TList;
+  i : Integer;
+begin
+  l := FHashTreeOperations.LockList;
+  try
+    Try
+      for i := 0 to l.Count - 1 do begin
+        op := l[i];
+        op.Free;
+      end;
+    Finally
+      l.Clear;
+      FHashTree := TCrypto.DoSha256('');
+    End;
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
+procedure TOperationsHashTree.CopyFromHashTree(Sender: TOperationsHashTree);
+Var i : Integer;
+  lme, lsender : TList;
+  opsender,op : TPCOperation;
+  ms : TMemoryStream;
+begin
+  if (Sender = Self) then begin
+    exit;
+  end;
+
+  ClearHastThree;
+  lme := FHashTreeOperations.LockList;
+  lsender := Sender.FHashTreeOperations.LockList;
+  try
+    ms := TMemoryStream.Create;
+    Try
+      for i := 0 to lsender.Count - 1 do begin
+        opsender := lsender[i];
+        op := TPCOperation(opsender.NewInstance);
+        ms.Size:=0;
+        opsender.SaveToStream(ms);
+        ms.Position:=0;
+        op.LoadFromStream(ms);
+        InternalAddOperationToHashTree(lme,op);
+      end;
+    Finally
+      ms.Free;
+    End;
+  finally
+    FHashTreeOperations.UnlockList;
+    Sender.FHashTreeOperations.UnlockList;
+  end;
+end;
+
+constructor TOperationsHashTree.Create;
+begin
+  FHashTree := TCrypto.DoSha256('');
+  FHashTreeOperations := TThreadList.Create;
+end;
+
+destructor TOperationsHashTree.Destroy;
+begin
+  ClearHastThree;
+  FHashTreeOperations.Free;
+  inherited;
+end;
+
+function TOperationsHashTree.GetOperation(index: Integer): TPCOperation;
+Var l : TList;
+begin
+  l := FHashTreeOperations.LockList;
+  try
+    Result := TPCOperation(l[index]);
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
+function TOperationsHashTree.GetOperationsAffectingAccount(account_number: Cardinal; List: TList): Integer;
+  // This function retrieves operations from HashTree that affeccts to an account_number
+Var l,intl : TList;
+  i,j : Integer;
+begin
+  List.Clear;
+  l := FHashTreeOperations.LockList;
+  try
+    intl := TList.Create;
+    try
+      for i := 0 to l.Count - 1 do begin
+        intl.Clear;
+        TPCOperation(l[i]).AffectedAccounts(intl);
+        if intl.IndexOf(TObject(account_number))>=0 then List.Add(TObject(i));
+      end;
+    finally
+      intl.Free;
+    end;
+    Result := List.Count;
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
+procedure TOperationsHashTree.InternalAddOperationToHashTree(list: TList; op: TPCOperation);
+Var ms : TMemoryStream;
+  h : TRawBytes;
+  newOp : TPCOperation;
+begin
+  ms := TMemoryStream.Create;
+  try
+      newOp := TPCOperation( op.NewInstance );
+      op.SaveToStream(ms);
+      ms.Position := 0;
+      newOp.LoadFromStream(ms);
+      h := TCrypto.DoSha256(ms.Memory,ms.Size);
+      list.Add(newOp);
+  finally
+      ms.Free;
+  end;
+  // Include to hash tree
+  FHashTree := TCrypto.DoSha256(FHashTree+h);
+end;
+
+function TOperationsHashTree.OperationsCount: Integer;
+Var l : TList;
+begin
+  l := FHashTreeOperations.LockList;
+  try
+    Result := l.Count;
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
+{ TStorage }
+
+procedure TStorage.CopyConfiguration(const CopyFrom: TStorage);
+begin
+  Orphan := CopyFrom.Orphan;
+end;
+
+constructor TStorage.Create(AOwner: TComponent);
+begin
+  inherited;
+  FOrphan := '';
+end;
+
+procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
+begin
+  DoDeleteBlockChainBlocks(StartingDeleteBlock,Orphan);
+end;
+
+function TStorage.LoadBlockChainBlock(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
+begin
+   Result := DoLoadBlockChain(Operations,Block);
+end;
+
+function TStorage.MoveBlockChainBlocks(StartBlock: Cardinal; const DestOrphan: TOrphan): Boolean;
+begin
+  Result := DoMoveBlockChain(StartBlock,DestOrphan);
+end;
+
+function TStorage.RestoreBank(max_block: Int64): Boolean;
+begin
+  Result := DoRestoreBank(max_block);
+end;
+
+function TStorage.SaveBank: Boolean;
+begin
+  Result := true;
+  if (Bank.BlocksCount MOD CT_BankToDiskEveryNBlocks)<>0 then exit; // No bank!
+  Try
+    Result := DoSaveBank;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error saving Bank: '+E.Message);
+      Raise;
+    end;
+  End;
+end;
+
+function TStorage.SaveBlockChainblock(Operations: TPCOperationsComp): Boolean;
+begin
+  Try
+    Result := DoSaveBlockChain(Operations);
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error saving block chain: '+E.Message);
+      Raise;
+    end;
+  End;
+end;
+
+procedure TStorage.SetBank(const Value: TPCBank);
+begin
+  FBank := Value;
+end;
+
+procedure TStorage.SetOrphan(const Value: TOrphan);
+begin
+  FOrphan := Value;
+end;
+
+{ TPCOperation }
+
+class function TPCOperation.IsReadablePayload(const Payload: TRawBytes): Boolean;
+Var i : Integer;
+begin
+  Result := true;
+    for i := 1 to length(Payload) do begin
+      if (ord(Payload[i])<32) Or (ord(Payload[i])>=127) then begin
+        Result := false;
+        Exit;
+      end;
+    end;
+end;
+
+initialization
+  SetLength(_OperationsClass, 0);
+finalization
+
+end.

+ 78 - 0
Units/PascalCoin/UConst.pas

@@ -0,0 +1,78 @@
+unit UConst;
+
+{ 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 ssl_const;
+
+Const
+  CT_Genesis_Magic_String_For_Old_Block_Hash :
+    AnsiString =
+    '(c) Albert Molina - Genesis block at same time than BitCoin Block 424720 Hash 000000000000000001cc41ff7846264718ef0a15f97f532a98277bd5f6820b89';
+
+  CT_Zero_Block_Proof_of_work_in_Hexa =
+    '00000003A29C32E84A539ADE24397D41D30116A6FAFEC17B7D9CED68A4238C92';
+
+
+  CT_NetServer_Port = 4004;
+  CT_AccountsPerBlock = 5;
+
+  CT_NewLineSecondsAvg: Cardinal = 300; // 60*5=300 seconds -> 5 minutes avg
+    //   -> 1 day = 86400 seconds -> 1 year = 31536000 seconds (aprox)
+    //   Each year = 105120 new blocks (aprox)
+    //   -> *5 accounts per block = 525600 new accounts each year (aprox)
+
+  CT_FirstReward: UInt64 = 1000000; // 4 decimals... First reward = 100,0000
+  CT_MinReward: UInt64 = 10000; // 4 decimals... Min reward = 1,0000
+  CT_NewLineRewardDecrease: Cardinal = 420480; // Avg 4 year
+
+  CT_WaitNewBlocksBeforeTransaction = 100;
+
+  CT_RecoverFoundsWaitInactiveCount = 420480;  // After 4 years... if an account has no operations, money will be a reward for a miner!
+
+  CT_MaxTransactionAmount = 1000000000000;
+  CT_MaxTransactionFee = 100000000;
+  CT_MaxWalletAmount = 10000000000000;
+  //
+  CT_MinCompactTarget: Cardinal = $19000000; // First compact target of block 0
+
+  CT_CalcNewTargetBlocksAverage: Cardinal = 100;
+  CT_MaxBlock : Cardinal = $FFFFFFFF;
+
+  CT_MaxPayloadSize = 255; // Max payload size in bytes
+  CT_MaxSecondsDifferenceOfNetworkNodes = 180; // 3 minutes. If a Node has a +- value difference, will be blacklisted
+
+  CT_MaxServersConnected = 10;
+
+  CT_BankToDiskEveryNBlocks = 500;
+
+  CT_Default_EC_OpenSSL_NID = NID_secp256k1;
+
+  CT_Protocol_Version: Word = $0001; // Version 1
+  CT_Protocol_Available: Word = $0000;
+  CT_MagicIdentificator: AnsiString = 'PascalCoin'; //
+
+  // Value of Operations type in Protocol 1
+  CT_Op_Transaction = $01;
+  CT_Op_Changekey = $02;
+  CT_Op_Recover = $03;
+
+  CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.ddns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
+
+  CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
+
+implementation
+
+end.

+ 726 - 0
Units/PascalCoin/UCrypto.pas

@@ -0,0 +1,726 @@
+unit UCrypto;
+
+{ 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, ssl_err, ssl_const, ssl_bn, ssl_ec, ssl_types, ssl_ecdsa, ssl_sha, ssl_ripemd, ssl_util,
+  ssl_evp, ssl_ecdh, ssl_hmac;
+
+Type
+  ECryptoException = Class(Exception);
+
+  TRawBytes = AnsiString;
+  PRawBytes = ^TRawBytes;
+
+  TECDSA_SIG = record
+     r: TRawBytes;
+     s: TRawBytes;
+  end; { record }
+
+  TECDSA_Public = record
+     EC_OpenSSL_NID : Word;
+     x: TRawBytes;
+     y: TRawBytes;
+  end;
+  PECDSA_Public = ^TECDSA_Public;
+
+  TECPrivateKey = Class
+  private
+    FPrivateKey: PEC_KEY;
+    FEC_OpenSSL_NID : Word;
+    procedure SetPrivateKey(const Value: PEC_KEY);
+    function GetPublicKey: TECDSA_Public;
+    function GetPublicKeyPoint: PEC_POINT;
+  public
+    Constructor Create;
+    Procedure GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
+    Destructor Destroy;
+    Property PrivateKey : PEC_KEY read FPrivateKey;// write SetPrivateKey;
+    Property PublicKey : TECDSA_Public read GetPublicKey;
+    Property PublicKeyPoint : PEC_POINT read GetPublicKeyPoint;
+    Function SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; hexa : AnsiString) : Boolean;
+    Property EC_OpenSSL_NID : Word Read FEC_OpenSSL_NID;
+    class function IsValidPublicKey(PubKey : TECDSA_Public) : Boolean;
+    Function ExportToRaw : TRawBytes;
+    class Function ImportFromRaw(Const raw : TRawBytes) : TECPrivateKey; static;
+  End;
+
+  TCrypto = Class
+  private
+  public
+    Class function ToHexaString(const raw : TRawBytes) : AnsiString;
+    Class function HexaToRaw(const HexaString : AnsiString) : TRawBytes;
+    Class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
+    Class function DoSha256(const TheMessage : AnsiString) : TRawBytes; overload;
+    Class function DoDoubleSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
+    Class function DoDoubleSha256(const TheMessage : AnsiString) : TRawBytes; overload;
+    Class function DoRipeMD160(const TheMessage : AnsiString) : TRawBytes;
+    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;
+    Class function ECDSAVerify(PubKey : TECDSA_Public; const digest : AnsiString; Signature : TECDSA_SIG) : Boolean; overload;
+    Class procedure InitCrypto;
+    Class function IsHumanReadable(Const ReadableText : TRawBytes) : Boolean;
+  End;
+
+  TBigNum = Class
+  private
+    FBN : PBIGNUM;
+    procedure SetHexaValue(const Value: AnsiString);
+    function GetHexaValue: AnsiString;
+    procedure SetValue(const Value: Int64);
+    function GetValue: Int64;
+    function GetDecimalValue: AnsiString;
+    procedure SetDecimalValue(const Value: AnsiString);
+    function GetRawValue: TRawBytes;
+    procedure SetRawValue(const Value: TRawBytes);
+  public
+    Constructor Create; overload;
+    Constructor Create(initialValue : Int64); overload;
+    Constructor Create(hexaValue : AnsiString); overload;
+    Destructor Destroy; override;
+    Function Copy : TBigNum;
+    Function Add(BN : TBigNum) : TBigNum; overload;
+    Function Add(int : Int64) : TBigNum; overload;
+    Function Sub(BN : TBigNum) : TBigNum; overload;
+    Function Sub(int : Int64) : TBigNum; overload;
+    Function Multiply(BN : TBigNum) : TBigNum; overload;
+    Function Multiply(int : Int64) : TBigNum; overload;
+    Function LShift(nbits : Integer) : TBigNum;
+    Function RShift(nbits : Integer) : TBigNum;
+    Function CompareTo(BN : TBigNum) : Integer;
+    Function Divide(BN : TBigNum) : TBigNum; overload;
+    Function Divide(int : Int64) : TBigNum; overload;
+    Procedure Divide(dividend, remainder : TBigNum); overload;
+    Function ToInt64(var int : Int64) : TBigNum;
+    Function ToDecimal : AnsiString;
+    Property HexaValue : AnsiString read GetHexaValue write SetHexaValue;
+    Property RawValue : TRawBytes read GetRawValue write SetRawValue;
+    Property DecimalValue : AnsiString read GetDecimalValue write SetDecimalValue;
+    Property Value : Int64 read GetValue write SetValue;
+    Function IsZero : Boolean;
+    Class Function HexaToDecimal(hexa : AnsiString) : AnsiString;
+  End;
+
+Const
+  CT_TECDSA_Public_Nul : TECDSA_Public = (EC_OpenSSL_NID:0;x:'';y:'');
+
+implementation
+
+uses
+  ULog, UConst, Windows, UAccounts;
+
+Var _initialized : Boolean = false;
+
+Procedure _DoInit;
+Begin
+  if Not (_initialized) then begin
+    _initialized := true;
+    SSL_InitERR;
+    SSL_InitEC;
+    SSL_InitECDSA;
+    SSL_InitBN;
+    SSL_Initsha;
+    SSL_Initripemd;
+    // Used by UECIES & UAES
+    SSL_InitEVP;
+    SSL_InitSSLDH;
+    SSL_InitHMAC;
+  end;
+End;
+
+{ TECPrivateKey }
+
+constructor TECPrivateKey.Create;
+begin
+  FPrivateKey := Nil;
+  FEC_OpenSSL_NID := CT_Default_EC_OpenSSL_NID;
+end;
+
+destructor TECPrivateKey.Destroy;
+begin
+  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
+end;
+
+function TECPrivateKey.ExportToRaw: TRawBytes;
+Var ms : TStream;
+  aux : TRawBytes;
+begin
+  ms := TMemoryStream.Create;
+  Try
+    ms.Write(FEC_OpenSSL_NID,sizeof(FEC_OpenSSL_NID));
+    SetLength(aux,BN_num_bytes(FPrivateKey^.priv_key));
+    BN_bn2bin(FPrivateKey^.priv_key,@aux[1]);
+    TStreamOp.WriteAnsiString(ms,aux);
+    SetLength(Result,ms.Size);
+    ms.Position := 0;
+    ms.Read(Result[1],ms.Size);
+  Finally
+    ms.Free;
+  End;
+end;
+
+procedure TECPrivateKey.GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
+Var i : Integer;
+begin
+  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
+  FEC_OpenSSL_NID := EC_OpenSSL_NID;
+  FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
+  i := EC_KEY_generate_key(FPrivateKey);
+  if i<>1 then Raise ECryptoException.Create('Error generating new Random Private Key');
+end;
+
+function TECPrivateKey.GetPublicKey: TECDSA_Public;
+var ps : PAnsiChar;
+  BNx,BNy : PBIGNUM;
+  ctx : PBN_CTX;
+begin
+  Result.EC_OpenSSL_NID := FEC_OpenSSL_NID;
+  ctx := BN_CTX_new;
+  BNx := BN_new;
+  BNy := BN_new;
+  Try
+    EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(FPrivateKey),EC_KEY_get0_public_key(FPrivateKey),BNx,BNy,ctx);
+    SetLength(Result.x,BN_num_bytes(BNx));
+    BN_bn2bin(BNx,@Result.x[1]);
+    SetLength(Result.y,BN_num_bytes(BNy));
+    BN_bn2bin(BNy,@Result.y[1]);
+  Finally
+    BN_CTX_free(ctx);
+    BN_free(BNx);
+    BN_free(BNy);
+  End;
+end;
+
+function TECPrivateKey.GetPublicKeyPoint: PEC_POINT;
+begin
+  Result := EC_KEY_get0_public_key(FPrivateKey);
+end;
+
+class function TECPrivateKey.ImportFromRaw(const raw: TRawBytes): TECPrivateKey;
+Var ms : TStream;
+  aux : TRawBytes;
+  BNx : PBIGNUM;
+  ECID : Word;
+  PAC : PAnsiChar;
+begin
+  Result := Nil;
+  ms := TMemoryStream.Create;
+  Try
+    ms.WriteBuffer(raw[1],length(raw));
+    ms.Position := 0;
+    if ms.Read(ECID,sizeof(ECID))<>sizeof(ECID) then exit;
+    If TStreamOp.ReadAnsiString(ms,aux)<0 then exit;
+    BNx := BN_bin2bn(PAnsiChar(aux),length(aux),nil);
+    if assigned(BNx) then begin
+      try
+        PAC := BN_bn2hex(BNx);
+        try
+          Result := TECPrivateKey.Create;
+          Result.SetPrivateKeyFromHexa(ECID,PAC);
+        finally
+          OpenSSL_free(PAC);
+        end;
+      finally
+        BN_free(BNx);
+      end;
+    end;
+  Finally
+    ms.Free;
+  End;
+end;
+
+class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public): Boolean;
+Var BNx,BNy : PBIGNUM;
+  ECG : PEC_GROUP;
+  ctx : PBN_CTX;
+  pub_key : PEC_POINT;
+begin
+  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
+  try
+    BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);
+    try
+      ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
+      try
+        pub_key := EC_POINT_new(ECG);
+        try
+          ctx := BN_CTX_new;
+          try
+            Result := EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1;
+          finally
+            BN_CTX_free(ctx);
+          end;
+        finally
+          EC_POINT_free(pub_key);
+        end;
+      finally
+        EC_GROUP_free(ECG);
+      end;
+    finally
+      BN_free(BNy);
+    end;
+  finally
+    BN_free(BNx);
+  end;
+end;
+
+procedure TECPrivateKey.SetPrivateKey(const Value: PEC_KEY);
+begin
+  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
+  FPrivateKey := Value;
+end;
+
+function TECPrivateKey.SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; hexa : AnsiString) : Boolean;
+var bn : PBIGNUM;
+  ctx : PBN_CTX;
+  pub_key : PEC_POINT;
+begin
+  bn := BN_new;
+  try
+    if BN_hex2bn(@bn,PAnsiChar(hexa))=0 then Raise ECryptoException.Create('Invalid Hexadecimal value:'+hexa);
+
+    if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
+    FEC_OpenSSL_NID := EC_OpenSSL_NID;
+    FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
+
+    if EC_KEY_set_private_key(FPrivateKey,bn)<>1 then raise ECryptoException.Create('Invalid num to set as private key');
+    //
+    ctx := BN_CTX_new;
+    pub_key := EC_POINT_new(FPrivateKey.group);
+    try
+      if EC_POINT_mul(FPrivateKey.group,pub_key,bn,nil,nil,ctx)<>1 then raise ECryptoException.Create('Error obtaining public key');
+      EC_KEY_set_public_key(FPrivateKey,pub_key);
+    finally
+      BN_CTX_free(ctx);
+      EC_POINT_free(pub_key);
+    end;
+  finally
+    BN_free(bn);
+  end;
+end;
+
+{ TCrypto }
+
+class function TCrypto.DoDoubleSha256(const TheMessage: AnsiString): TRawBytes;
+begin
+  Result := DoSha256(DoSha256(TheMessage));
+end;
+
+class function TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
+Var PS,PS1 : PAnsiChar;
+  PC : PAnsiChar;
+begin
+  SetLength(Result,32);
+  PS := @Result[1];
+  GetMem(PS1,32);
+  SHA256(p,plength,PS1);
+  SHA256(PS1,32,PS);
+  FreeMem(PS1,32);
+end;
+
+class function TCrypto.DoRipeMD160(const TheMessage: AnsiString): TRawBytes;
+Var PS : PAnsiChar;
+  PC : PAnsiChar;
+  i : Integer;
+begin
+  GetMem(PS,33);
+  RIPEMD160(PAnsiChar(TheMessage),Length(TheMessage),PS);
+  PC := PS;
+  Result := '';
+  for I := 1 to 20 do begin
+    Result := Result + IntToHex(Integer(PC^),2);
+    inc(PC);
+  end;
+  FreeMem(PS,33);
+end;
+
+class function TCrypto.DoSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
+Var PS : PAnsiChar;
+  PC : PAnsiChar;
+begin
+  SetLength(Result,32);
+  PS := @Result[1];
+  SHA256(p,plength,PS);
+end;
+
+class function TCrypto.DoSha256(const TheMessage: AnsiString): TRawBytes;
+Var PS : PAnsiChar;
+begin
+  SetLength(Result,32);
+  PS := @Result[1];
+  SHA256(PAnsiChar(TheMessage),Length(TheMessage),PS);
+  exit;
+end;
+
+class function TCrypto.ECDSASign(Key: PEC_KEY; const digest: AnsiString): TECDSA_SIG;
+Var PECS : PECDSA_SIG;
+  p, pr,ps : PAnsiChar;
+  i : Integer;
+begin
+  PECS := ECDSA_do_sign(PAnsiChar(digest),length(digest),Key);
+  Try
+    if PECS = Nil then raise ECryptoException.Create('Error signing');
+
+    i := BN_num_bytes(PECS^._r);
+    SetLength(Result.r,i);
+    p := @Result.r[1];
+    i := BN_bn2bin(PECS^._r,p);
+
+    i := BN_num_bytes(PECS^._s);
+    SetLength(Result.s,i);
+    p := @Result.s[1];
+    i := BN_bn2bin(PECS^._s,p);
+  Finally
+    ECDSA_SIG_free(PECS);
+  End;
+end;
+
+class function TCrypto.ECDSAVerify(EC_OpenSSL_NID : Word; PubKey: EC_POINT; const digest: AnsiString; Signature: TECDSA_SIG): Boolean;
+Var PECS : PECDSA_SIG;
+  PK : PEC_KEY;
+begin
+  PECS := ECDSA_SIG_new;
+  Try
+    BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),PECS^._r);
+    BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),PECS^._s);
+
+    PK := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
+    EC_KEY_set_public_key(PK,@PubKey);
+    Case ECDSA_do_verify(PAnsiChar(digest),length(digest),PECS,PK) of
+      1 : Result := true;
+      0 : Result := false;
+    Else
+      raise ECryptoException.Create('Error on Verify');
+    End;
+    EC_KEY_free(PK);
+  Finally
+    ECDSA_SIG_free(PECS);
+  End;
+end;
+
+class function TCrypto.ECDSAVerify(PubKey: TECDSA_Public; const digest: AnsiString; Signature: TECDSA_SIG): Boolean;
+Var BNx,BNy : PBIGNUM;
+  ECG : PEC_GROUP;
+  ctx : PBN_CTX;
+  pub_key : PEC_POINT;
+begin
+  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
+  BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);
+
+  ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
+  pub_key := EC_POINT_new(ECG);
+  ctx := BN_CTX_new;
+  if EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1 then begin
+    Result := ECDSAVerify(PubKey.EC_OpenSSL_NID, pub_key^,digest,signature);
+  end else begin
+    Result := false;
+  end;
+  BN_CTX_free(ctx);
+  EC_POINT_free(pub_key);
+  EC_GROUP_free(ECG);
+  BN_free(BNx);
+  BN_free(BNy);
+end;
+
+class function TCrypto.HexaToRaw(const HexaString: AnsiString): TRawBytes;
+Var P : PAnsiChar;
+ lc : AnsiString;
+ i : Integer;
+begin
+  Result := '';
+  if ((length(HexaString) MOD 2)<>0) Or (length(HexaString)=0) then exit;
+  SetLength(result,length(HexaString) DIV 2);
+  P := @Result[1];
+  lc := LowerCase(HexaString);
+  i := HexToBin(PAnsiChar(@lc[1]),P,length(Result));
+end;
+
+class procedure TCrypto.InitCrypto;
+begin
+  _DoInit;
+end;
+
+class function TCrypto.IsHumanReadable(const ReadableText: TRawBytes): Boolean;
+Var i : Integer;
+Begin
+  Result := true;
+  for i := 1 to length(ReadableText) do begin
+    if (ord(ReadableText[i])<32) Or (ord(ReadableText[i])>=127) then begin
+      Result := false;
+      Exit;
+    end;
+  end;
+end;
+
+class function TCrypto.PrivateKey2Hexa(Key: PEC_KEY): AnsiString;
+Var p : PAnsiChar;
+begin
+  p := BN_bn2hex(Key^.priv_key);
+  Result := strpas(p);
+  OPENSSL_free(p);
+end;
+
+class function TCrypto.ToHexaString(const raw: TRawBytes): AnsiString;
+Var i : Integer;
+  s : AnsiString;
+  b : Byte;
+begin
+  SetLength(Result,length(raw)*2);
+  for i := 0 to length(raw)-1 do begin
+    b := Ord(raw[i+1]);
+    s := IntToHex(b,2);
+    Result[(i*2)+1] := s[1];
+    Result[(i*2)+2] := s[2];
+  end;
+end;
+
+{ TBigNum }
+
+function TBigNum.Add(BN: TBigNum): TBigNum;
+begin
+  BN_add(FBN,BN.FBN,FBN);
+  Result := Self;
+end;
+
+function TBigNum.Add(int: Int64): TBigNum;
+Var bn : TBigNum;
+begin
+  bn := TBigNum.Create(int);
+  Result := Add(bn);
+  bn.Free;
+end;
+
+function TBigNum.CompareTo(BN: TBigNum): Integer;
+begin
+  Result := BN_cmp(FBN,BN.FBN);
+end;
+
+function TBigNum.Copy: TBigNum;
+begin
+  Result := TBigNum.Create(0);
+  BN_copy(Result.FBN,FBN);
+end;
+
+constructor TBigNum.Create;
+begin
+  Create(0);
+end;
+
+constructor TBigNum.Create(hexaValue: AnsiString);
+begin
+  Create(0);
+  SetHexaValue(hexaValue);
+end;
+
+constructor TBigNum.Create(initialValue : Int64);
+begin
+  FBN := BN_new;
+  SetValue(initialValue);
+end;
+
+destructor TBigNum.Destroy;
+begin
+  BN_free(FBN);
+  inherited;
+end;
+
+procedure TBigNum.Divide(dividend, remainder: TBigNum);
+Var ctx : PBN_CTX;
+begin
+  ctx := BN_CTX_new;
+  BN_div(FBN,remainder.FBN,FBN,dividend.FBN,ctx);
+  BN_CTX_free(ctx);
+end;
+
+function TBigNum.Divide(int: Int64): TBigNum;
+Var bn : TBigNum;
+begin
+  bn := TBigNum.Create(int);
+  Result := Divide(bn);
+  bn.Free;
+end;
+
+function TBigNum.Divide(BN: TBigNum): TBigNum;
+Var _div,_rem : PBIGNUM;
+  ctx : PBN_CTX;
+begin
+  _div := BN_new;
+  _rem := BN_new;
+  ctx := BN_CTX_new;
+  BN_div(FBN,_rem,FBN,BN.FBN,ctx);
+  BN_free(_div);
+  BN_free(_rem);
+  BN_CTX_free(ctx);
+  Result := Self;
+end;
+
+function TBigNum.GetDecimalValue: AnsiString;
+var p : PAnsiChar;
+begin
+  p := BN_bn2dec(FBN);
+  Result := strpas(p);
+  OpenSSL_free(p);
+end;
+
+function TBigNum.GetHexaValue: AnsiString;
+Var p : PAnsiChar;
+begin
+  p := BN_bn2hex(FBN);
+  Result := strpas( p );
+  OPENSSL_free(p);
+end;
+
+function TBigNum.GetRawValue: TRawBytes;
+Var p : PAnsiChar;
+  i : Integer;
+begin
+  i := BN_num_bytes(FBN);
+  SetLength(Result,i);
+  p := @Result[1];
+  i := BN_bn2bin(FBN,p);
+end;
+
+function TBigNum.GetValue: Int64;
+Var p : PAnsiChar;
+  a : AnsiString;
+  err : Integer;
+begin
+  p := BN_bn2dec(FBN);
+  a := strpas(p);
+  OPENSSL_free(p);
+  val(a,Result,err);
+end;
+
+class function TBigNum.HexaToDecimal(hexa: AnsiString): AnsiString;
+Var bn : TBigNum;
+begin
+  bn := TBigNum.Create(hexa);
+  result := bn.ToDecimal;
+  bn.Free;
+end;
+
+function TBigNum.IsZero: Boolean;
+Var dv : AnsiString;
+begin
+  dv := DecimalValue;
+  Result := dv='0';
+end;
+
+function TBigNum.LShift(nbits: Integer): TBigNum;
+begin
+  if BN_lshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
+  Result := Self;
+end;
+
+function TBigNum.Multiply(int: Int64): TBigNum;
+Var n : TBigNum;
+  ctx : PBN_CTX;
+begin
+  n := TBigNum.Create(int);
+  ctx := BN_CTX_new;
+  if BN_mul(FBN,FBN,n.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
+  BN_CTX_free(ctx);
+  Result := Self;
+end;
+
+function TBigNum.RShift(nbits: Integer): TBigNum;
+begin
+  if BN_rshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
+  Result := Self;
+end;
+
+function TBigNum.Multiply(BN: TBigNum): TBigNum;
+Var ctx : PBN_CTX;
+begin
+  ctx := BN_CTX_new;
+  if BN_mul(FBN,FBN,BN.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
+  Result := Self;
+  BN_CTX_free(ctx);
+  Result := Self;
+end;
+
+procedure TBigNum.SetDecimalValue(const Value: AnsiString);
+Var i : Integer;
+begin
+  if BN_dec2bn(@FBN,PAnsiChar(Value))=0 then raise ECryptoException.Create('Error on dec2bn');
+end;
+
+procedure TBigNum.SetHexaValue(const Value: AnsiString);
+Var i : Integer;
+begin
+  i := BN_hex2bn(@FBN,PAnsiChar(Value));
+  if i=0 then begin
+      Raise ECryptoException.Create('Invalid Hexadecimal value:'+Value);
+  end;
+end;
+
+procedure TBigNum.SetRawValue(const Value: TRawBytes);
+var p : PBIGNUM;
+begin
+  p := BN_bin2bn(PAnsiChar(Value),length(Value),FBN);
+  if (p<>FBN) Or (p=Nil) then Raise ECryptoException.Create('Error decoding Raw value to BigNum "'+TCrypto.ToHexaString(Value)+'" ('+inttostr(length(value))+')'+#10+
+    ERR_error_string(ERR_get_error(),nil));
+end;
+
+procedure TBigNum.SetValue(const Value: Int64);
+var a : UInt64;
+begin
+  if Value<0 then a := (Value * (-1))
+  else a := Value;
+  if BN_set_word(FBN,a)<>1 then raise ECryptoException.Create('Error on set Value');
+  if Value<0 then BN_set_negative(FBN,1)
+  else BN_set_negative(FBN,0);
+end;
+
+function TBigNum.Sub(BN: TBigNum): TBigNum;
+begin
+  BN_sub(FBN,FBN,BN.FBN);
+  Result := Self;
+end;
+
+function TBigNum.Sub(int: Int64): TBigNum;
+Var bn : TBigNum;
+begin
+  bn := TBigNum.Create(int);
+  Result := Sub(bn);
+  bn.Free;
+end;
+
+function TBigNum.ToDecimal: AnsiString;
+var p : PAnsiChar;
+begin
+  p := BN_bn2dec(FBN);
+  Result := strpas(p);
+  OpenSSL_free(p);
+end;
+
+function TBigNum.ToInt64(var int: Int64): TBigNum;
+Var s : AnsiString;
+ err : Integer;
+ p : PAnsiChar;
+begin
+  p := BN_bn2dec(FBN);
+  s := strpas( p );
+  OPENSSL_free(p);
+  val(s,int,err);
+  if err<>0 then int := 0;
+  Result := Self;
+end;
+
+
+initialization
+finalization
+end.

+ 1006 - 0
Units/PascalCoin/UDBStorage.pas

@@ -0,0 +1,1006 @@
+unit UDBStorage;
+
+{ 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
+
+  }
+
+{ ABOUT THIS UNIT:
+  This unit is used only in Windows version to use an Microsoft Access database
+  to store data to be more accessible and quickly readable.
+
+  All the blockchain is stored in a table "tblockchain" in stream format, but
+  also the operationblock is also saved in individual fields to easily
+  search it with SQL select statements.
+
+  This unit will store 2 additional tables:
+  First table is "tbank" that will help this application when reading all
+  the blockchain and only will need to read from "last stored bank"
+
+  Second table is "toperations" thans stores all the operations made to
+  the accounts (transfers, change key...). It's usefull to quickly search
+  operations that affects an account.
+
+  FINAL NOTE:
+  To compile in other OS you can use "UFileStorage" or another unit to
+  store data in other database types or simply as a file }
+
+interface
+
+uses
+  UBlockChain, Classes, db, ADODB, SysUtils, UAccounts, ULog, Variants, UCrypto,
+  UConst, UOpTransaction;
+
+Const
+  CT_TblName_BlockChain = 'tblockchain';
+  CT_TblName_Operations = 'toperations';
+  CT_TblName_Bank = 'tbank';
+
+  CT_TblFld_BlockChain_id = 'idblockchain';
+  CT_TblFld_BlockChain_block = 'block';
+  CT_TblFld_BlockChain_accountkey = 'accountkey';
+  CT_TblFld_BlockChain_reward = 'reward';
+  CT_TblFld_BlockChain_fee = 'fee';
+  CT_TblFld_BlockChain_protocol_version = 'protocol_version';
+  CT_TblFld_BlockChain_protocol_available = 'protocol_available';
+  CT_TblFld_BlockChain_timestamp = 'timestamp';
+  CT_TblFld_BlockChain_compact_target = 'compact_target';
+  CT_TblFld_BlockChain_nonce = 'nonce';
+  CT_TblFld_BlockChain_rawpayload = 'rawpayload';
+  CT_TblFld_BlockChain_safe_box_hash = 'safe_box_hash';
+  CT_TblFld_BlockChain_operations_hash = 'operations_hash';
+  CT_TblFld_BlockChain_proof_of_work = 'proof_of_work';
+  CT_TblFld_BlockChain_operations_stream = 'operations_stream';
+  CT_TblFld_BlockChain_orphan = 'orphan';
+  CT_TblFld_BlockChain_operations_count = 'operations_count';
+
+  CT_TblFld_Operations_id = 'idoperation';
+  CT_TblFld_Operations_optype = 'optype';
+  CT_TblFld_Operations_block = 'block';
+  CT_TblFld_Operations_timestamp = 'timestamp';
+  CT_TblFld_Operations_nopblock = 'nopblock';
+  CT_TblFld_Operations_account = 'account';
+  CT_TblFld_Operations_other_account = 'other_account';
+  CT_TblFld_Operations_n_operation = 'n_operation';
+  CT_TblFld_Operations_optype_op = 'optype_op';
+  CT_TblFld_Operations_amount = 'amount';
+  CT_TblFld_Operations_fee = 'fee';
+  CT_TblFld_Operations_balance = 'balance';
+  CT_TblFld_Operations_rawpayload = 'payload';
+//  CT_TblFld_Operations_payload_stream = 'payload_stream';
+  CT_TblFld_Operations_newaccountkey = 'newaccountkey';
+  CT_TblFld_Operations_orphan = 'orphan';
+
+  CT_TblFld_Bank_id = 'idbank';
+  CT_TblFld_Bank_block = 'block';
+  CT_TblFld_Bank_bank_stream = 'bank_stream';
+  CT_TblFld_Bank_orphan = 'orphan';
+
+Type
+  TOperationResume = Record
+    Block : Cardinal;
+    time : Cardinal;
+    AffectedAccount : Cardinal;
+    OperationTxt : AnsiString;
+    Amount : Int64;
+    Fee : Int64;
+    Balance : Int64;
+    OriginalPayload : TRawBytes;
+    PrintablePayload : AnsiString;
+  end;
+
+Const
+  CT_TOperationResume_NUL : TOperationResume = (Block:0;time:0;AffectedAccount:0; OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:'';PrintablePayload:'');
+
+Type
+
+  TOperationsResumeList = Class
+  private
+    FList : TThreadList;
+    function GetOperationResume(index: Integer): TOperationResume;
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    Procedure Add(Const OperationResume : TOperationResume);
+    Function Count : Integer;
+    Procedure Delete(index : Integer);
+    Procedure Clear;
+    Property OperationResume[index : Integer] : TOperationResume read GetOperationResume; default;
+  End;
+
+  TDBStorage = Class(TStorage)
+  private
+    FAdoConnection : TADOConnection;
+    FAccessFileName: AnsiString;
+    procedure SetAccessFileName(const Value: AnsiString);
+  protected
+    Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
+    Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
+    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan) : Boolean; override;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); override;
+    Function DoSaveBank : Boolean; override;
+    Function DoRestoreBank(max_block : Int64) : Boolean; override;
+    Function BlockExists(Block : Cardinal) : Boolean; override;
+  public
+    Constructor Create(AOwner : TComponent); Override;
+    Destructor Destroy; override;
+    Property AccessFileName : AnsiString read FAccessFileName write SetAccessFileName;
+    Function SQL_UPDATE(const TblName : AnsiString; Const SetValue : AnsiString; const Where : String): Boolean;
+    Function SQL_DELETE(const TblName : AnsiString; const Where : String): Boolean;
+    Function ValueToSql(const Value : Variant) : String;
+    Procedure CopyConfiguration(Const CopyFrom : TStorage); override;
+    Property ADOConnection : TADOConnection read FAdoConnection;
+    Class Function DBPayloadToReadableText(Const DBRawPayload : TRawBytes; Var ReadableText : AnsiString) : Boolean;
+    //
+    Class Function OperationToOperationResume(Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;
+    Function GetOperationsFromAccount(Const OperationsResume : TOperationsResumeList; account_number : Cardinal; start,max : Integer) :Integer;
+    Class Function GetOperationsFromAccountSQL(account_number, block_start, block_end : Int64; dateStart,dateEnd : TDate; start,max : Integer) :String;
+    Class Function GetBlockChainSQL(block_start, block_end : Int64; dateStart,dateEnd : TDate; start,max : Integer) :String;
+    Class Function BlobSaveToStream(Blob : TBlobField; Stream : TStream) : Boolean;
+    Class Procedure BlobLoadFromStream(Blob : TBlobField; Stream : TStream);
+    //
+    Class Function DBStringFieldToRaw(Field : TField; var raw : TRawBytes) : Boolean;
+    Class Procedure DBRawToStringField(Field : TField; const raw : TRawBytes);
+  End;
+
+implementation
+
+uses
+  Forms, UTime;
+
+{ TDBStorage }
+
+class procedure TDBStorage.BlobLoadFromStream(Blob: TBlobField; Stream: TStream);
+Var ms : TMemoryStream;
+  b : Byte;
+begin
+  { NOTE:
+    Access database has a "bug" when storing data as a memo if data size is odd.
+    So we need to indicate when data size is odd in order to read correctly
+    data when reading
+  }
+  ms := TMemoryStream.Create;
+  try
+    if stream.Size MOD 2=0 then b := 0
+    else b := 1;
+    ms.Write(b,1);
+    Stream.Position := 0;
+    ms.CopyFrom(Stream,Stream.Size);
+    ms.Position := ms.Size;
+    ms.Write(b,1);
+    ms.Position := 0;
+    Blob.LoadFromStream(ms);
+  finally
+    ms.Free;
+  end;
+end;
+
+class function TDBStorage.BlobSaveToStream(Blob: TBlobField; Stream: TStream) : Boolean;
+Var ms : TMemoryStream;
+  ac : AnsiChar;
+  b : Byte;
+begin
+  { NOTE:
+    Access database has a "bug" when storing data as a memo if data size is odd.
+    So we need to indicate when data size is odd in order to read correctly
+    data when reading
+  }
+  Result := false;
+  ms := TMemoryStream.Create;
+  try
+    Blob.SaveToStream(ms);
+    if ms.size>0 then begin
+      ms.Position := 0;
+      ms.Read(b,1);
+      if (b<>0) and (b<>1) then exit;
+      stream.size := 0;
+      if b=0 then stream.CopyFrom(ms,ms.Size-2)
+      else stream.CopyFrom(ms,ms.Size-1);
+      Result := true;
+    end else Result := true;
+  finally
+    ms.Free;
+  end;
+end;
+
+function TDBStorage.BlockExists(Block: Cardinal): Boolean;
+Var ds : TADOQuery;
+  whereorphan : AnsiString;
+begin
+  if Orphan='' then whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)'
+  else whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+  ds := TADOQuery.Create(Self);
+  Try
+    ds.Connection := FAdoConnection;
+    ds.SQL.Text := 'SELECT * FROM '+CT_TblName_BlockChain+' WHERE ('+CT_TblFld_BlockChain_block+'='+Inttostr(Block)+') AND '+whereorphan+
+      ' ORDER BY '+CT_TblFld_BlockChain_id;
+    ds.Open;
+    Result := Not ds.IsEmpty;
+  Finally
+    ds.Free;
+  End;
+end;
+
+procedure TDBStorage.CopyConfiguration(const CopyFrom: TStorage);
+begin
+  inherited;
+  if CopyFrom is TDBStorage then AccessFileName := TDBStorage(CopyFrom).AccessFileName;
+end;
+
+constructor TDBStorage.Create(AOwner: TComponent);
+begin
+  inherited;
+  FAdoConnection := TADOConnection.Create(Self);
+end;
+
+class function TDBStorage.DBPayloadToReadableText(const DBRawPayload: TRawBytes; var ReadableText: AnsiString): Boolean;
+Var i : Integer;
+  P : PAnsiChar;
+  lc : AnsiString;
+begin
+  Result := false;
+  for i := 1 to length(DBRawPayload) do begin
+    if (ord(DBRawPayload[i])<32) Or (ord(DBRawPayload[i])>=127) then begin
+      Exit;
+    end;
+  end;
+  Result := true;
+  ReadableText := DBRawPayload;
+end;
+
+class procedure TDBStorage.DBRawToStringField(Field: TField; const raw: TRawBytes);
+  {
+  Microsoft Access Database does not store string values as a Raw value... if there
+  is a "0" character bad things happen... This function ensures that there will
+  not be any "0" in a saved string.
+  }
+Var dbraw : TRawBytes;
+  i : Integer;
+begin
+  dbraw := '';
+  for i := 1 to length(raw) do begin
+    if raw[i]=chr(0) then dbraw := dbraw + chr(255)+chr(1)
+    else if raw[i]=chr(255) then dbraw := dbraw + chr(255)+chr(2)
+    else dbraw := dbraw + raw[i];
+  end;
+  Field.AsAnsiString := dbRaw;
+end;
+
+class function TDBStorage.DBStringFieldToRaw(Field: TField; var raw: TRawBytes): Boolean;
+  {
+  Microsoft Access Database does not store string values as a Raw value... if there
+  is a "0" character bad things happen... This function restores a previously
+  raw value saved without "0" character.
+  }
+Var dbraw : TRawBytes;
+  i : Integer;
+begin
+  dbraw := Field.AsAnsiString;
+  Result := false;
+  raw := '';
+  i := 1;
+  while (i<=length(dbraw)) do begin
+    if (dbraw[i]=chr(255)) then begin
+      if (i<length(dbraw)) then begin
+        inc(i);
+        if dbraw[i]=chr(1) then raw := raw + chr(0)
+        else if dbraw[i]=chr(2) then raw := raw + chr(255)
+        else exit;
+      end else exit;
+    end else raw := raw + dbraw[i];
+    inc(i);
+  end;
+  Result := true;
+end;
+
+destructor TDBStorage.Destroy;
+begin
+  FreeAndNil(FAdoConnection);
+  inherited;
+end;
+
+procedure TDBStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
+Var ds : TADOQuery;
+  whereorphan : AnsiString;
+begin
+  if Orphan='' then begin
+    whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)';
+    TLog.NewLog(ltdebug,Classname,Format('DoDeleteBlockChainBlocks Starting Block:%d (Main blockchain)',[StartingDeleteBlock,Orphan]));
+  end else begin
+    whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+    TLog.NewLog(ltdebug,Classname,Format('DoDeleteBlockChainBlocks Starting Block:%d (Orphan:%s)',[StartingDeleteBlock,Orphan]));
+  end;
+  SQL_DELETE(CT_TblName_BlockChain,'('+CT_TblFld_BlockChain_block+'>='+inttostr(StartingDeleteBlock)+') AND '+whereorphan);
+  if Orphan='' then begin
+    whereorphan := '('+CT_TblFld_Bank_orphan+' IS NULL)';
+  end else begin
+    whereorphan := '('+CT_TblFld_Bank_orphan+' LIKE '''+Orphan+''')';
+  end;
+  SQL_DELETE(CT_TblName_Bank,'('+CT_TblFld_Bank_block+'>='+inttostr(StartingDeleteBlock)+') AND '+whereorphan);
+  if Orphan='' then begin
+    whereorphan := '('+CT_TblFld_Operations_orphan+' IS NULL)';
+  end else begin
+    whereorphan := '('+CT_TblFld_Operations_orphan+' LIKE '''+Orphan+''')';
+  end;
+  SQL_DELETE(CT_TblName_Operations,'('+CT_TblFld_Operations_block+'>='+inttostr(StartingDeleteBlock)+') AND '+whereorphan);
+end;
+
+function TDBStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
+Var ds : TADOQuery;
+  whereorphan : AnsiString;
+  bf : TBlobField;
+  ms : TMemoryStream;
+  errors : AnsiString;
+begin
+  if Orphan='' then begin
+    whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)';
+    TLog.NewLog(ltdebug,Classname,Format('DoLoadBlockChain Block:%d (Main blockchain)',[Block,Orphan]));
+  end else begin
+    whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+    TLog.NewLog(ltdebug,Classname,Format('DoLoadBlockChain Block:%d (Orphan:%s)',[Block,Orphan]));
+  end;
+  Result := false;
+  Operations.Clear(true);
+  ds := TADOQuery.Create(Self);
+  Try
+    ds.Connection := FAdoConnection;
+    ds.SQL.Text := 'SELECT * FROM '+CT_TblName_BlockChain+' WHERE ('+CT_TblFld_BlockChain_block+'='+Inttostr(Block)+') AND '+whereorphan+
+      ' ORDER BY '+CT_TblFld_BlockChain_id;
+    ds.Open;
+    if Not ds.IsEmpty then begin
+      bf := ds.FieldByName(CT_TblFld_BlockChain_operations_stream) as TBlobField;
+      ms := TMemoryStream.Create;
+      try
+        BlobSaveToStream(bf,ms);
+        ms.Position := 0;
+        Result := Operations.LoadFromStream(false,false,ms,errors);
+        if Not Result then begin
+          TLog.NewLog(lterror,Classname,Format('Error reading databse Block %d: %s Stream size:%d',[Block,errors,ms.Size]));
+        end;
+      finally
+        ms.Free;
+      end;
+    end else begin
+      TLog.NewLog(lterror,Classname,Format('Block %d not found in database. SQL:%s',[Block,ds.SQL.Text]));
+    end;
+  Finally
+    ds.Free;
+  End;
+end;
+
+function TDBStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan): Boolean;
+Var whereorphan : AnsiString;
+  setvalue : AnsiString;
+begin
+  Result := true;
+  FAdoConnection.BeginTrans;
+  try
+    TLog.NewLog(ltdebug,Classname,Format('DoMoveBlockChain start:%d From:%s to Dest:%s',[start_block,Orphan,DestOrphan]));
+    // Moving BlockChain table
+    if Orphan='' then whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)'
+    else whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+    if DestOrphan='' then setvalue := CT_TblFld_BlockChain_orphan+'=NULL'
+    else setvalue := CT_TblFld_BlockChain_orphan+ '='''+DestOrphan+'''';
+    SQL_UPDATE(CT_TblName_BlockChain,setvalue,whereorphan+' AND ('+CT_TblFld_BlockChain_block+'>='+inttostr(Start_Block)+')');
+
+    // Moving Operations table
+    if Orphan='' then whereorphan := '('+CT_TblFld_Operations_orphan+' IS NULL)'
+    else whereorphan := '('+CT_TblFld_Operations_orphan+' LIKE '''+Orphan+''')';
+    if DestOrphan='' then setvalue := CT_TblFld_Operations_orphan+'=NULL'
+    else setvalue := CT_TblFld_Operations_orphan+ '='''+DestOrphan+'''';
+    SQL_UPDATE(CT_TblName_Operations,setvalue,whereorphan+' AND ('+CT_TblFld_Operations_block+'>='+inttostr(Start_Block)+')');
+
+    // Moving Bank table
+    if Orphan='' then whereorphan := '('+CT_TblFld_Bank_orphan+' IS NULL)'
+    else whereorphan := '('+CT_TblFld_Bank_orphan+' LIKE '''+Orphan+''')';
+    if DestOrphan='' then setvalue := CT_TblFld_Bank_orphan+'=NULL'
+    else setvalue := CT_TblFld_Bank_orphan+ '='''+DestOrphan+'''';
+    SQL_UPDATE(CT_TblName_Bank,setvalue,whereorphan+' AND ('+CT_TblFld_Bank_block+'>='+inttostr(Start_Block)+')');
+    FAdoConnection.CommitTrans;
+  Except
+    // Rollback changes
+    On E:Exception do begin
+      FAdoConnection.RollbackTrans;
+      Result :=False;
+      Raise;
+    end;
+  end;
+end;
+
+function TDBStorage.DoRestoreBank(max_block: Int64): Boolean;
+var ds : TAdoQuery;
+  whereorphan : AnsiString;
+  sql,errors : AnsiString;
+  bf : TBlobField;
+  ms : TMemoryStream;
+begin
+  Result := false;
+  TLog.NewLog(ltdebug,Classname,'DoRestoreBank max:'+inttostr(max_block));
+  if Orphan='' then whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)'
+  else whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+  sql := 'SELECT * FROM '+CT_TblName_Bank+' WHERE ('+whereorphan+')';
+  if max_block<MaxInt then sql := sql+ ' AND ('+CT_TblFld_Bank_block+'<='+Inttostr(max_block)+') ';
+  sql := sql +' ORDER BY '+CT_TblFld_Bank_block+' DESC';
+  ds := TADOQuery.Create(Self);
+  Try
+    ds.Connection := FAdoConnection;
+    ds.CursorLocation := clUseServer; // Important: This improves speed!
+    ds.SQL.Text := sql;
+    ds.Open;
+    while (Not ds.Eof) do begin
+      bf := ds.FieldByName(CT_TblFld_Bank_bank_stream) as TBlobField;
+      ms := TMemoryStream.Create;
+      try
+        BlobSaveToStream(bf,ms);
+        ms.Position := 0;
+        Result := Bank.LoadFromStream(ms,errors);
+        if Not Result then begin
+          TLog.NewLog(lterror,Classname,Format('Error reading databse Bank block %d: %s',[ds.FieldByName(CT_TblFld_Bank_block).AsInteger,errors]));
+        end else begin
+          // Bye bye
+          Result := true;
+          break;
+        end;
+      finally
+        ms.Free;
+      end;
+      ds.Next;
+    end;
+  Finally
+    ds.Free;
+  End;
+end;
+
+function TDBStorage.DoSaveBank: Boolean;
+var ds : TAdoQuery;
+  whereorphan : AnsiString;
+  sql,errors : AnsiString;
+  bf : TBlobField;
+  ms : TMemoryStream;
+begin
+  Result := false;
+  FAdoConnection.BeginTrans;
+  try
+    TLog.NewLog(ltdebug,Classname,'DoSaveBank blockscount:'+inttostr(Bank.BlocksCount));
+    if Orphan='' then whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)'
+    else whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+    SQL_DELETE(CT_TblName_Bank,whereorphan+' AND ('+CT_TblFld_Bank_block+'='+inttostr(Bank.BlocksCount)+')');
+
+    ds := TADOQuery.Create(Self);
+    try
+      ds.Connection := FAdoConnection;
+      SQL := 'SELECT * FROM '+CT_TblName_Bank+' WHERE (1=0)';
+      ds.SQL.Text := sql;
+      ds.Open;
+      ds.Insert;
+      ds.FieldByName(CT_TblFld_Bank_block).AsInteger := Bank.BlocksCount;
+      if Orphan<>'' then ds.FieldByName(CT_TblFld_Bank_orphan).AsAnsiString := Orphan
+      else ds.FieldByName(CT_TblFld_Bank_orphan).Value := Null;
+      bf := ds.FieldByName(CT_TblFld_Bank_bank_stream) as TBlobField;
+      ms := TMemoryStream.Create;
+      Try
+        Bank.SaveToStream(ms);
+        ms.Position := 0;
+        BlobLoadFromStream(bf,ms);
+        TLog.NewLog(ltdebug,Classname,Format('Saving bank of block %d with stream size %d bytes',[Bank.BlocksCount,ms.Size]));
+      Finally
+        ms.Free;
+      End;
+      ds.Post;
+      Result := true;
+    finally
+      ds.Free;
+    end;
+    FAdoConnection.CommitTrans;
+  Except
+    FAdoConnection.RollbackTrans;
+    raise;
+  end;
+end;
+
+function TDBStorage.DoSaveBlockChain(Operations: TPCOperationsComp): Boolean;
+  Function GetOperationBalance(iOperation : Integer; nAccount : Cardinal) : Int64;
+  Var i : Integer;
+    op : TPCOperation;
+  Begin
+    Result := Operations.bank.SafeBox.Account(nAccount).balance;
+    for i := (Operations.OperationsHashTree.OperationsCount-1) downto (iOperation+1) do begin
+      op := Operations.OperationsHashTree.GetOperation(i);
+      Case op.OpType of
+        CT_Op_Transaction : Begin
+          if TOpTransaction(op).Data.sender=nAccount then Result := Result + op.OperationAmount + op.OperationFee
+          else if TOpTransaction(op).Data.target=nAccount then Result := Result - op.OperationAmount;
+        End;
+      End;
+    end;
+  End;
+var ds : TADOQuery;
+  whereorphan : AnsiString;
+  sql,errors,aux : AnsiString;
+  bf : TBlobField;
+  ms : TMemoryStream;
+  i : Integer;
+  op : TPCOperation;
+  vOrphan : Variant;
+  trans : Integer;
+begin
+  Result := false;
+  trans := FAdoConnection.BeginTrans;
+  try
+    TLog.NewLog(ltdebug,Classname,'DoSaveBlockChain:'+TPCOperationsComp.OperationBlockToText(Operations.OperationBlock));
+    if Orphan='' then begin
+      whereorphan := '('+CT_TblFld_BlockChain_orphan+' IS NULL)';
+      vOrphan := Null;
+    end else begin
+      whereorphan := '('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''')';
+      vOrphan := Orphan;
+    end;
+    SQL_DELETE(CT_TblName_BlockChain,whereorphan+' AND ('+CT_TblFld_BlockChain_block+'='+Inttostr(Operations.OperationBlock.block)+')');
+
+    if Orphan='' then whereorphan := '('+CT_TblFld_Operations_orphan+' IS NULL)'
+    else whereorphan := '('+CT_TblFld_Operations_orphan+' LIKE '''+Orphan+''')';
+    SQL_DELETE(CT_TblName_Operations,whereorphan+' AND ('+CT_TblFld_Operations_block+'='+Inttostr(Operations.OperationBlock.block)+')');
+
+    ds := TADOQuery.Create(Self);
+    ms := TMemoryStream.Create;
+    try
+      ds.Connection := FAdoConnection;
+      //
+      SQL := 'SELECT * FROM '+CT_TblName_BlockChain+' WHERE (1=0)';
+      ds.SQL.Text := SQL;
+      ds.Open;
+      ds.Insert;
+      ds.FieldByName(CT_TblFld_BlockChain_block).Value := Operations.OperationBlock.block;
+      aux := TCrypto.ToHexaString( TAccountComp.AccountKey2RawString(Operations.OperationBlock.account_key) );
+      if (length(aux)>255) then aux := copy(aux,1,252)+'...';
+      ds.FieldByName(CT_TblFld_BlockChain_accountkey).Value := Copy(aux,1,255);
+      ds.FieldByName(CT_TblFld_BlockChain_reward).Value := Operations.OperationBlock.reward;
+      ds.FieldByName(CT_TblFld_BlockChain_fee).Value := Operations.OperationBlock.fee;
+      ds.FieldByName(CT_TblFld_BlockChain_protocol_version).Value := Operations.OperationBlock.protocol_version;
+      ds.FieldByName(CT_TblFld_BlockChain_protocol_available).Value := Operations.OperationBlock.protocol_available;
+      ds.FieldByName(CT_TblFld_BlockChain_timestamp).Value := Operations.OperationBlock.timestamp;
+      ds.FieldByName(CT_TblFld_BlockChain_compact_target).Value := Operations.OperationBlock.compact_target;
+      ds.FieldByName(CT_TblFld_BlockChain_nonce).Value := Operations.OperationBlock.nonce;
+      DBRawToStringField(ds.FieldByName(CT_TblFld_BlockChain_rawpayload),Operations.OperationBlock.block_payload);
+      ds.FieldByName(CT_TblFld_BlockChain_safe_box_hash).Value := TCrypto.ToHexaString( Operations.OperationBlock.initial_safe_box_hash );
+      ds.FieldByName(CT_TblFld_BlockChain_operations_hash).Value := TCrypto.ToHexaString( Operations.OperationBlock.operations_hash );
+      ds.FieldByName(CT_TblFld_BlockChain_proof_of_work).Value :=  TCrypto.ToHexaString( Operations.OperationBlock.proof_of_work );
+      ds.FieldByName(CT_TblFld_BlockChain_orphan).Value := vOrphan;
+      ds.FieldByName(CT_TblFld_BlockChain_operations_count).Value := Operations.Count;
+      bf := ds.FieldByName(CT_TblFld_BlockChain_operations_stream) as TBlobField;
+      Operations.SaveToStream(False,False,ms);
+      ms.Position := 0;
+      BlobLoadFromStream(bf,ms);
+      ds.Post;
+      // Save operations:
+      ds.Close;
+      ds.SQL.Text := 'SELECT * FROM '+CT_TblName_Operations+' WHERE (1=0)';
+      ds.Open;
+      // First insert reward
+      ds.Insert;
+      ds.FieldByName(CT_TblFld_Operations_optype).Value := 0;
+      ds.FieldByName(CT_TblFld_Operations_block).Value := Operations.OperationBlock.block;
+      ds.FieldByName(CT_TblFld_Operations_timestamp).Value := Operations.OperationBlock.timestamp;
+      ds.FieldByName(CT_TblFld_Operations_nopblock).Value := -1;
+      ds.FieldByName(CT_TblFld_Operations_optype_op).Value := 0;
+      ds.FieldByName(CT_TblFld_Operations_fee).Value := 0;
+      ds.FieldByName(CT_TblFld_Operations_orphan).Value := vOrphan;
+      ds.FieldByName(CT_TblFld_Operations_n_operation).Value := 0;
+      ds.FieldByName(CT_TblFld_Operations_account).Value := Operations.OperationBlock.block * CT_AccountsPerBlock;
+      ds.FieldByName(CT_TblFld_Operations_other_account).Value := Operations.OperationBlock.block * CT_AccountsPerBlock;
+      ds.FieldByName(CT_TblFld_Operations_amount).Value := Operations.OperationBlock.reward+Operations.OperationBlock.fee;
+      ds.FieldByName(CT_TblFld_Operations_balance).Value := Operations.OperationBlock.reward+Operations.OperationBlock.fee;
+      ds.Post;
+      // Insert operations
+      for i := 0 to Operations.Count-1 do begin
+        ds.Insert;
+        op := Operations.Operation[i];
+        ds.FieldByName(CT_TblFld_Operations_optype).Value := op.OpType;
+        ds.FieldByName(CT_TblFld_Operations_block).Value := Operations.OperationBlock.block;
+        ds.FieldByName(CT_TblFld_Operations_timestamp).Value := Operations.OperationBlock.timestamp;
+        ds.FieldByName(CT_TblFld_Operations_nopblock).Value := i;
+        ds.FieldByName(CT_TblFld_Operations_optype_op).Value := 0;
+        ds.FieldByName(CT_TblFld_Operations_fee).Value := (-1)*op.OperationFee; // Fee is a Negative number
+        ds.FieldByName(CT_TblFld_Operations_orphan).Value := vOrphan;
+        case op.OpType of
+          CT_Op_Transaction : Begin
+            ds.FieldByName(CT_TblFld_Operations_n_operation).Value := TOpTransaction(op).Data.n_operation;
+            ds.FieldByName(CT_TblFld_Operations_account).Value :=  TOpTransaction(op).Data.sender;
+            ds.FieldByName(CT_TblFld_Operations_other_account).Value :=  TOpTransaction(op).Data.target;
+            ds.FieldByName(CT_TblFld_Operations_amount).Value := (-1)*TOpTransaction(op).Data.amount;
+            DBRawToStringField(ds.FieldByName(CT_TblFld_Operations_rawpayload),TOpTransaction(op).Data.payload);
+            ds.FieldByName(CT_TblFld_Operations_balance).Value := GetOperationBalance(i,TOpTransaction(op).Data.sender);
+            ds.Post;
+            ds.Insert;
+            ds.FieldByName(CT_TblFld_Operations_optype).Value := op.OpType;
+            ds.FieldByName(CT_TblFld_Operations_block).Value := Operations.OperationBlock.block;
+            ds.FieldByName(CT_TblFld_Operations_timestamp).Value := Operations.OperationBlock.timestamp;
+            ds.FieldByName(CT_TblFld_Operations_nopblock).Value := i;
+            ds.FieldByName(CT_TblFld_Operations_optype_op).Value := 1; // Receive transaction
+            ds.FieldByName(CT_TblFld_Operations_fee).Value := 0;  // No fee for receiver
+            ds.FieldByName(CT_TblFld_Operations_balance).Value := GetOperationBalance(i,TOpTransaction(op).Data.target);
+            ds.FieldByName(CT_TblFld_Operations_orphan).Value := vOrphan;
+            ds.FieldByName(CT_TblFld_Operations_n_operation).Value := 0; // No n_operation for receiver
+            ds.FieldByName(CT_TblFld_Operations_account).Value :=  TOpTransaction(op).Data.target;
+            ds.FieldByName(CT_TblFld_Operations_other_account).Value :=  TOpTransaction(op).Data.sender;
+            ds.FieldByName(CT_TblFld_Operations_amount).Value := TOpTransaction(op).Data.amount;
+            DBRawToStringField(ds.FieldByName(CT_TblFld_Operations_rawpayload),TOpTransaction(op).Data.payload);
+            ds.Post;
+          End;
+          CT_Op_Changekey : Begin
+            ds.FieldByName(CT_TblFld_Operations_n_operation).Value := TOpChangeKey(op).Data.n_operation;
+            ds.FieldByName(CT_TblFld_Operations_account).Value :=  TOpChangeKey(op).Data.account;
+            ds.FieldByName(CT_TblFld_Operations_amount).Value := 0;
+            DBRawToStringField(ds.FieldByName(CT_TblFld_Operations_rawpayload),TOpChangeKey(op).Data.payload);
+            ds.FieldByName(CT_TblFld_Operations_balance).Value := GetOperationBalance(i,TOpChangeKey(op).Data.account); //Operations.bank.SafeBox.Account( TOpChangeKey(op).Data.account ).balance;
+            aux := TCrypto.ToHexaString( TAccountComp.AccountKey2RawString(TOpChangeKey(op).Data.new_accountkey) );
+            if (length(aux)>255) then aux := copy(aux,1,252)+'...';
+            ds.FieldByName(CT_TblFld_Operations_newaccountkey).Value := Copy(aux,1,255);
+            ds.Post;
+          End;
+          CT_Op_Recover : Begin
+            ds.FieldByName(CT_TblFld_Operations_n_operation).Value := TOpRecoverFounds(op).Data.n_operation;
+            ds.FieldByName(CT_TblFld_Operations_account).Value :=  TOpRecoverFounds(op).Data.account;
+            ds.FieldByName(CT_TblFld_Operations_amount).Value := 0;
+            ds.FieldByName(CT_TblFld_Operations_balance).Value := GetOperationBalance(i,TOpRecoverFounds(op).Data.account); //Operations.bank.SafeBox.Account( TOpRecoverFounds(op).Data.account ).balance;
+            ds.Post;
+          End;
+        else raise Exception.Create('Development error: OpType not available to save to the Database '+Inttostr(Op.OpType));
+        end;
+      end;
+    finally
+      ms.Free;
+      ds.Free;
+    end;
+    SaveBank;
+    FAdoConnection.CommitTrans;
+    Result := true;
+  Except
+    FAdoConnection.RollbackTrans;
+    raise;
+  end;
+end;
+
+class function TDBStorage.GetBlockChainSQL(block_start, block_end: Int64;
+  dateStart, dateEnd: TDate; start, max: Integer): String;
+var sqltop : String;
+  whereorphan, where : String;
+begin
+  if (start=0) AND (max>0) then sqltop := ' TOP '+inttostr(max)+' '
+  else sqltop := '';
+  where := 'WHERE ('+CT_TblFld_Operations_orphan+' IS NULL)';
+  if (block_start>=0) then begin
+    where := where + ' AND ('+CT_TblFld_BlockChain_block+'>='+IntToStr(block_start)+')';
+  end;
+  if (block_end>=0) then begin
+    where := where + ' AND ('+CT_TblFld_BlockChain_block+'<='+IntToStr(block_end)+')';
+  end;
+  if (dateStart>1000) then begin
+    where := where + ' AND ('+CT_TblFld_BlockChain_timestamp+'>='+IntToStr(UnivDateTimeToUnix(DateTime2UnivDateTime(dateStart)))+')';
+  end;
+  if (dateEnd>1000) then begin
+    where := where + ' AND ('+CT_TblFld_BlockChain_timestamp+'<'+IntToStr(UnivDateTimeToUnix(DateTime2UnivDateTime(dateEnd+1)))+')';
+  end;
+
+  Result := 'SELECT '+sqltop+' * '+
+    ' FROM '+CT_TblName_BlockChain+
+    ' '+where+
+    ' ORDER BY '+CT_TblFld_BlockChain_block+' DESC';
+end;
+
+function TDBStorage.GetOperationsFromAccount(Const OperationsResume : TOperationsResumeList; account_number : Cardinal; start,max : Integer) :Integer;
+var sqltop,whereorphan,sql,spayload : AnsiString;
+  ds : TADOQuery;
+  OPR : TOperationResume;
+  nrow : Integer;
+  ms : TMemoryStream;
+begin
+  if (start=0) AND (max>0) then sqltop := ' TOP '+inttostr(max)+' '
+  else sqltop := '';
+  if Orphan='' then whereorphan := '(('+CT_TblFld_BlockChain_orphan+' IS NULL) AND ('+CT_TblFld_Operations_orphan+' IS NULL))'
+  else whereorphan := '(('+CT_TblFld_BlockChain_orphan+' LIKE '''+Orphan+''') AND ('+CT_TblFld_Operations_orphan+' LIKE '''+Orphan+'''))';
+  sql := 'SELECT '+sqltop+' * '+
+    ' FROM '+CT_TblName_Operations+
+    ' WHERE ('+CT_TblFld_Operations_account+'='+inttostr(account_number)+') AND '+whereorphan+
+    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_optype+' DESC';
+  ms := TMemoryStream.Create;
+    ds := TADOQuery.Create(Self);
+    try
+      ds.Connection := FAdoConnection;
+      ds.SQL.Text := sql;
+      ds.Open;
+      nrow := 0;
+      while not ds.Eof do begin
+        if (nrow>=start) then begin
+          OPR := CT_TOperationResume_NUL;
+          OPR.Block := ds.FieldByName(CT_TblFld_Operations_block).AsInteger;
+          OPR.AffectedAccount := account_number;
+          OPR.time := ds.FieldByName(CT_TblFld_BlockChain_timestamp).AsInteger;
+          case ds.FieldByName(CT_TblFld_Operations_optype).AsInteger of
+            0 : OPR.OperationTxt := 'Blockchain reward';
+            CT_Op_Transaction : begin
+              if ds.FieldByName(CT_TblFld_Operations_optype_op).AsInteger=0 then begin
+                OPR.OperationTxt := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(ds.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end else begin
+                OPR.OperationTxt := 'Transaction Received from '+TAccountComp.AccountNumberToAccountTxtNumber(ds.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end;
+            end;
+            CT_Op_Changekey : Begin
+              OPR.OperationTxt := 'Change Key';
+            End;
+            CT_Op_Recover : begin
+              OPR.OperationTxt := 'Recover founds';
+            end;
+          else
+            OPR.OperationTxt := 'Unknown OpType:'+Inttostr(ds.FieldByName(CT_TblFld_Operations_optype).AsInteger);
+          end;
+          OPR.Amount := ds.FieldByName(CT_TblFld_Operations_amount).AsLargeInt;
+          OPR.Fee := ds.FieldByName(CT_TblFld_Operations_fee).AsLargeInt;
+          OPR.Balance := ds.FieldByName(CT_TblFld_Operations_balance).AsLargeInt;
+          DBStringFieldToRaw(ds.FieldByName(CT_TblFld_Operations_rawpayload),OPR.OriginalPayload);
+          If DBPayloadToReadableText(OPR.OriginalPayload,spayload) then OPR.PrintablePayload := spayload
+          else OPR.PrintablePayload := TCrypto.ToHexaString(OPR.OriginalPayload);
+          OperationsResume.Add(OPR);
+          //
+          if (max>0) And (OperationsResume.Count>=max) then exit;
+        end;
+        ds.Next;
+        inc(nrow);
+      end;
+    finally
+      ds.Free;
+      ms.Free;
+    end;
+end;
+
+class function TDBStorage.GetOperationsFromAccountSQL(account_number, block_start, block_end : Int64; dateStart,dateEnd : TDate; start,max : Integer) :String;
+var sqltop : String;
+  whereorphan, where : String;
+begin
+  if (start=0) AND (max>0) then sqltop := ' TOP '+inttostr(max)+' '
+  else sqltop := '';
+  where := 'WHERE ('+CT_TblFld_Operations_orphan+' IS NULL)';
+  if account_number>=0 then begin
+    where := where + ' AND ('+CT_TblFld_Operations_account+'='+inttostr(account_number)+')';
+  end;
+  if (block_start>=0) then begin
+    where := where + ' AND ('+CT_TblFld_Operations_block+'>='+IntToStr(block_start)+')';
+  end;
+  if (block_end>=0) then begin
+    where := where + ' AND ('+CT_TblFld_Operations_block+'<='+IntToStr(block_end)+')';
+  end;
+  if (dateStart>1000) then begin
+    where := where + ' AND ('+CT_TblFld_Operations_timestamp+'>='+IntToStr(UnivDateTimeToUnix(DateTime2UnivDateTime(dateStart)))+')';
+  end;
+  if (dateEnd>1000) then begin
+    where := where + ' AND ('+CT_TblFld_Operations_timestamp+'<'+IntToStr(UnivDateTimeToUnix(DateTime2UnivDateTime(dateEnd+1)))+')';
+  end;
+
+  Result := 'SELECT '+sqltop+' * '+
+    ' FROM '+CT_TblName_Operations+
+    ' '+where+
+    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_optype+' DESC';
+end;
+
+class function TDBStorage.OperationToOperationResume(Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;
+Var spayload : AnsiString;
+begin
+  OperationResume := CT_TOperationResume_NUL;
+  OperationResume.Fee := (-1)*Operation.OperationFee;
+  OperationResume.AffectedAccount := Affected_account_number;
+  Result := false;
+  case Operation.OpType of
+    CT_Op_Transaction : Begin
+      if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
+        OperationResume.OperationTxt := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
+        OperationResume.Amount := TOpTransaction(Operation).Data.amount * (-1);
+        Result := true;
+      end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
+        OperationResume.OperationTxt := 'Transaction Received from '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender);
+        OperationResume.Amount := TOpTransaction(Operation).Data.amount;
+        OperationResume.Fee := 0;
+        Result := true;
+      end else exit;
+    End;
+    CT_Op_Changekey : Begin
+      OperationResume.OperationTxt := 'Change Key';
+      OperationResume.Fee := TOpChangeKey(Operation).Data.fee;
+      Result := true;
+    End;
+    CT_Op_Recover : Begin
+      OperationResume.OperationTxt := 'Recover founds';
+      OperationResume.Fee := TOpRecoverFounds(Operation).Data.fee;
+    End;
+  else Exit;
+  end;
+  OperationResume.OriginalPayload := Operation.OperationPayload;
+  If DBPayloadToReadableText(OperationResume.OriginalPayload,spayload) then OperationResume.PrintablePayload := spayload
+  else OperationResume.PrintablePayload := TCrypto.ToHexaString(OperationResume.OriginalPayload);
+end;
+
+procedure TDBStorage.SetAccessFileName(const Value: AnsiString);
+begin
+  if FAccessFileName=Value then exit;
+  FAccessFileName := Value;
+  FAdoConnection.LoginPrompt := false;
+  FAdoConnection.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+Value+';Persist Security Info=False';
+  FAdoConnection.Connected := true;
+end;
+
+function TDBStorage.SQL_DELETE(const TblName: AnsiString; const Where: String): Boolean;
+Var sql : AnsiString;
+  ds : TADOQuery;
+begin
+  sql := 'DELETE * FROM '+TblName+' WHERE '+Where;
+  Try
+    ds := TADOQuery.Create(Self);
+    try
+      ds.Connection := FAdoConnection;
+      ds.SQL.Text := sql;
+      ds.ExecSQL;
+    finally
+      ds.Free;
+    end;
+  Except
+    On E:Exception do begin
+      E.Message := 'Error deleting'+#10+E.Message+#10+'SQL: '+sql;
+      raise;
+    end;
+  End;
+end;
+
+function TDBStorage.SQL_UPDATE(const TblName : AnsiString; Const SetValue : AnsiString; const Where : String): Boolean;
+Var sql : AnsiString;
+  ds : TADOQuery;
+begin
+  sql := 'UPDATE '+TblName+' SET '+SetValue+' WHERE '+Where;
+  try
+    ds := TADOQuery.Create(Self);
+    try
+      ds.Connection := FAdoConnection;
+      ds.SQL.Text := sql;
+      ds.ExecSQL;
+    finally
+      ds.Free;
+    end;
+  Except
+    On E:Exception do begin
+      E.Message := 'Error updating'+#10+E.Message+#10+'SQL: '+sql;
+      raise;
+    end;
+  End;
+end;
+
+function TDBStorage.ValueToSql(const Value: Variant): String;
+Var decs,ths : Char;
+  dates,times : char;
+begin
+   decs := DecimalSeparator;
+   ths := ThousandSeparator;
+   times := TimeSeparator;
+   dates := DateSeparator;
+   try
+     DecimalSeparator := '.';
+     ThousandSeparator := ',';
+     TimeSeparator := ':';
+     DateSeparator := '-';
+
+     Case VarType(Value) of
+         varNull, varEmpty : Begin
+            Result := 'NULL';
+                   End;
+         varSmallint,
+         varInteger, varByte, varWord,
+         varLongWord,varInt64,varUInt64 : Result := IntToStr(Value);
+         varSingle,varDouble : begin
+           Result := FormatFloat('0.0#################',Value);
+         end;
+         varCurrency : Result := FormatCurr('0.0#######',Value);
+         varDate : Begin
+           Result := FormatFloat('0.0#################',Value);
+                   End;
+         varBoolean : Begin
+           If Value then Result := 'TRUE'
+           Else Result := 'FALSE'
+                      End;
+         varString,varOleStr,varUString  : Begin
+             Result := VarToStr(Value);
+           End;
+      Else // Case
+        Raise Exception.Create('Invalid variant Type: '+InttoHex(VarType(Value),8));
+      End;
+   finally
+     DecimalSeparator := decs;
+     ThousandSeparator := ths;
+     TimeSeparator := times;
+     DateSeparator := dates;
+   end;
+end;
+
+{ TOperationsResumeList }
+
+Type POperationResume = ^TOperationResume;
+
+procedure TOperationsResumeList.Add(const OperationResume: TOperationResume);
+Var P : POperationResume;
+begin
+  New(P);
+  P^ := OperationResume;
+  FList.Add(P);
+end;
+
+procedure TOperationsResumeList.Clear;
+Var P : POperationResume;
+  i : Integer;
+  l : TList;
+begin
+  l := FList.LockList;
+  try
+    for i := 0 to l.Count - 1 do begin
+      P := l[i];
+      Dispose(P);
+    end;
+    l.Clear;
+  finally
+    FList.UnlockList;
+  end;
+end;
+
+function TOperationsResumeList.Count: Integer;
+Var l : TList;
+begin
+  l := FList.LockList;
+  Try
+    Result := l.Count;
+  Finally
+    FList.UnlockList;
+  End;
+end;
+
+constructor TOperationsResumeList.Create;
+begin
+  FList := TThreadList.Create;
+end;
+
+procedure TOperationsResumeList.Delete(index: Integer);
+Var P : POperationResume;
+  l : TList;
+begin
+  l := FList.LockList;
+  Try
+    P := l[index];
+    l.Delete(index);
+    Dispose(P);
+  Finally
+    FList.UnlockList;
+  End;
+end;
+
+destructor TOperationsResumeList.Destroy;
+begin
+  Clear;
+  FList.Free;
+  inherited;
+end;
+
+function TOperationsResumeList.GetOperationResume(index: Integer): TOperationResume;
+Var l : TList;
+begin
+  l := FList.LockList;
+  try
+    if index<l.Count then Result := POperationResume(l[index])^
+    else Result := CT_TOperationResume_NUL;
+  finally
+    FList.UnlockList;
+  end;
+end;
+
+end.

+ 456 - 0
Units/PascalCoin/UECIES.pas

@@ -0,0 +1,456 @@
+unit UECIES;
+
+{ 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
+
+  }
+
+{ Pascal implementation of ECIES functions to encrypt/decrypt data using AES
+  and EC keys.
+
+  Original C Source code by Ladar Levison
+  http://www.mail-archive.com/[email protected]/msg28042.html
+
+  Adapted to use different EC types and with a md5 hash (because no need strong verify
+  due it will be included in block chain, so no change will be made)
+  and a head with lower bytes because all the data is small
+
+  This unit works with all EC types defined in OpenSLL, can encrypt using the public
+  EC key and decrypt using the private EC key.
+
+  When encrypting uses a MD5, so less space (original source code used SHA512), but I don't need
+  to use strong anti-corruption in encrypted data because I will include it
+  in a blockchain, so corruption is not possible. (for now ;-). Using MD5 safe space
+  for me.
+
+  }
+
+interface
+
+Uses ssl_ecdh,ssl_types,ssl_evp,ssl_const,ssl_ec, UCrypto, ULog, ssl_hmac, ssl_err, UConst;
+
+Const CT_Max_Bytes_To_Encrypt = 32000;
+
+function ECIESEncrypt(const ECDSAPubKey: TECDSA_Public; const MessageToEncrypt: AnsiString): TRawBytes; overload;
+function ECIESEncrypt(EC_OpenSSL_NID : Word; PubKey: EC_POINT; const MessageToEncrypt: AnsiString): TRawBytes; overload;
+function ECIESDecrypt(EC_OpenSSL_NID : Word; PrivateKey: PEC_KEY; logErrors : Boolean; const MessageToDecrypt: TRawBytes; Var Decrypted : AnsiString): Boolean;
+
+implementation
+
+uses
+  ssl_sha, Windows, SysUtils, ssl_bn;
+
+Type
+  Psecure_t = Pointer;
+  secure_head_t = Record
+    key : byte;
+    mac : byte;
+    orig : word;
+    body : word;
+  End;
+  Psecure_head_t = ^secure_head_t;
+
+function secure_key_length(cryptex : Psecure_t): UInt64;
+begin
+  Result := Psecure_head_t(cryptex)^.key;
+end;
+
+function secure_mac_length(cryptex : Psecure_t): UInt64;
+begin
+  Result := Psecure_head_t(cryptex)^.mac;
+end;
+
+function secure_body_length(cryptex : Psecure_t): UInt64;
+begin
+  Result := Psecure_head_t(cryptex)^.body;
+end;
+
+function secure_orig_length(cryptex : Psecure_t): UInt64;
+begin
+  Result := Psecure_head_t(cryptex)^.orig;
+end;
+
+function secure_total_length(cryptex : Psecure_t): UInt64;
+begin
+  Result := Sizeof(secure_head_t) + Psecure_head_t(cryptex)^.key +
+    Psecure_head_t(cryptex)^.mac + Psecure_head_t(cryptex)^.body;
+end;
+
+function secure_key_data(cryptex : Psecure_t): Pointer;
+begin
+  Result := Pointer(Integer(cryptex) + Sizeof(secure_head_t));
+end;
+
+function secure_mac_data(cryptex : Psecure_t): Pointer;
+begin
+  Result := Pointer(Integer(cryptex) + Sizeof(secure_head_t) + Psecure_head_t(cryptex)^.key);
+end;
+
+function secure_body_data(cryptex : Psecure_t): Pointer;
+begin
+  Result := Pointer(Integer(cryptex) + Sizeof(secure_head_t) + Psecure_head_t(cryptex)^.key + Psecure_head_t(cryptex)^.mac);
+end;
+
+function secure_alloc(key, mac, orig, body : UInt64) : Psecure_t;
+Var psh : Psecure_head_t;
+begin
+  Result := AllocMem(Sizeof(secure_head_t) + key + mac + body);
+  psh := Result;
+  psh^.key := key;
+  psh^.mac := mac;
+  psh^.orig := orig;
+  psh^.body := body;
+end;
+
+procedure secure_free(cryptex : Psecure_t);
+begin
+  FreeMemory(cryptex);
+end;
+
+function ecies_key_derivation_512(const _in: Pointer; _inlen: TC_SIZE_T; _out: Pointer; var _outlen: TC_SIZE_T): pointer; cdecl;
+begin
+  if ( _outlen < SHA512_DIGEST_LENGTH) then begin
+     Result := Nil;
+  end;
+  _outlen := SHA512_DIGEST_LENGTH;
+  Result := SHA512(_in,_inlen,_out);
+end;
+
+function ECIES_HASHER : PEVP_MD;
+begin
+  // NOTE: Original C source code uses EVP_sha512() as Hash function to check
+  // corrupted data... But I need to save space, so I use EVP_md5
+  Result := EVP_md5;
+end;
+
+function ECIESEncrypt(const ECDSAPubKey: TECDSA_Public; const MessageToEncrypt: AnsiString): TRawBytes;
+Var BNx,BNy : PBIGNUM;
+  ECG : PEC_GROUP;
+  ctx : PBN_CTX;
+  pub_key : PEC_POINT;
+  s : String;
+begin
+  Result := '';
+  BNx := BN_bin2bn(PAnsiChar(ECDSAPubKey.x),length(ECDSAPubKey.x),nil);
+  BNy := BN_bin2bn(PAnsiChar(ECDSAPubKey.y),length(ECDSAPubKey.y),nil);
+  Try
+
+    ECG := EC_GROUP_new_by_curve_name(ECDSAPubKey.EC_OpenSSL_NID);
+    if ECG=Nil then begin
+      s := Format('An error occurred while trying to generate curve group {error = %s}',
+         [ERR_error_string(ERR_get_error(),nil)]);
+      TLog.NewLog(lterror,'ECIES',s);
+      exit;
+    end;
+    pub_key := EC_POINT_new(ECG);
+    ctx := BN_CTX_new;
+    if EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1 then begin
+      Result := ECIESEncrypt(ECDSAPubKey.EC_OpenSSL_NID,pub_key^,MessageToEncrypt);
+    end else begin
+      s := Format('An error occurred while trying to convert public key to public point {error = %s}',
+         [ERR_error_string(ERR_get_error(),nil)]);
+      TLog.NewLog(lterror,'ECIES',s);
+    end;
+    BN_CTX_free(ctx);
+    EC_POINT_free(pub_key);
+    EC_GROUP_free(ECG);
+  Finally
+    BN_free(BNx);
+    BN_free(BNy);
+  End;
+End;
+
+function ECIESEncrypt(EC_OpenSSL_NID : Word; PubKey: EC_POINT; const MessageToEncrypt: AnsiString): TRawBytes;
+Var PK,PEphemeral : PEC_KEY;
+  i,key_length,block_length,envelope_length,body_length : Integer;
+  mac_length : Cardinal;
+  envelope_key : Array[1..SHA512_DIGEST_LENGTH] of byte;
+  iv: Array[1..EVP_MAX_IV_LENGTH] of byte;
+  block:Array[1..EVP_MAX_BLOCK_LENGTH] of byte;
+  cryptex : Psecure_t;
+  cipher : EVP_CIPHER_CTX;
+  body,aux : Pointer;
+  hmac : HMAC_CTX;
+begin
+  Result := '';
+  if length(MessageToEncrypt)>CT_Max_Bytes_To_Encrypt then begin
+    TLog.NewLog(lterror,'ECIES','Max bytes to encrypt: '+inttostr(length(MessageToEncrypt))+'>'+Inttostr(CT_Max_Bytes_To_Encrypt));
+    exit;
+  end;
+  // Make sure we are generating enough key material for the symmetric ciphers.
+  key_length := (EVP_CIPHER_key_length(EVP_aes_256_cbc));
+  if (key_length*2)>SHA512_DIGEST_LENGTH then begin
+    TLog.NewLog(lterror,'ECIES',Format('The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %i / required = %zu}',
+      [SHA512_DIGEST_LENGTH DIV 8,(key_length * 2) DIV 8]));
+    exit;
+  end;
+  // Convert the user's public key from hex into a full EC_KEY structure.
+  PK := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
+  PEphemeral := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
+  try
+    if (EC_KEY_set_public_key(PK,@PubKey)<>1) then begin
+      TLog.NewLog(lterror,'ECIES','Invalid public key provided');
+      exit;
+    end;
+    if (EC_KEY_generate_key(PEphemeral)<>1) then begin
+      TLog.NewLog(lterror,'ECIES','An error occurred while trying to generate the ephemeral key');
+      exit;
+    end;
+    // Use the intersection of the provided keys to generate the envelope data used by the ciphers below. The ecies_key_derivation_512() function uses
+    // SHA 512 to ensure we have a sufficient amount of envelope key material and that the material created is sufficiently secure.
+    if (ECDH_compute_key(@envelope_key,SHA512_DIGEST_LENGTH,EC_KEY_get0_public_key(PK),PEphemeral,ecies_key_derivation_512)<>SHA512_DIGEST_LENGTH) then begin
+      TLog.NewLog(lterror,'ECIES',Format('An error occurred while trying to compute the envelope key {error = %s}',[ERR_error_string(ERR_get_error(), nil)]));
+      exit;
+    end;
+    // Determine the envelope and block lengths so we can allocate a buffer for the result.
+    block_length := EVP_CIPHER_block_size(EVP_aes_256_cbc);
+    if (block_length=0) or (block_length>EVP_MAX_BLOCK_LENGTH) then begin
+      TLog.NewLog(lterror,'ECIES',Format('Invalid block length {block = %zu}',[block_length]));
+      exit;
+    end;
+    envelope_length := EC_POINT_point2oct(EC_KEY_get0_group(PEphemeral),EC_KEY_get0_public_key(PEphemeral),POINT_CONVERSION_COMPRESSED,nil,0,nil);
+    if (envelope_length=0) then begin
+      TLog.NewLog(lterror,'ECIES',Format('Invalid envelope length {envelope = %zu}',[envelope_length]));
+      exit;
+    end;
+    // We use a conditional to pad the length if the input buffer is not evenly divisible by the block size.
+    if (Length(MessageToEncrypt) MOD block_length)=0 then i := 0
+    else i := block_length - (Length(MessageToEncrypt) MOD block_length);
+    cryptex := secure_alloc(envelope_length,EVP_MD_size(ECIES_HASHER),Length(MessageToEncrypt), Length(MessageToEncrypt) + i);
+    try
+      // Store the public key portion of the ephemeral key.
+      If EC_POINT_point2oct(EC_KEY_get0_group(PEphemeral),EC_KEY_get0_public_key(PEphemeral),
+           POINT_CONVERSION_COMPRESSED,secure_key_data(cryptex),envelope_length,Nil)<>envelope_length then begin
+        TLog.NewLog(lterror,'ECIES',Format('An error occurred while trying to record the public portion of the envelope key {error = %s}',
+          [ERR_error_string(ERR_get_error(),nil)]));
+        exit;
+      end;
+      // For now we use an empty initialization vector.
+      FillMemory(@iv,EVP_MAX_IV_LENGTH,0);
+      // Setup the cipher context, the body length, and store a pointer to the body buffer location.
+      EVP_CIPHER_CTX_init(@cipher);
+      try
+        body := secure_body_data(cryptex);
+        body_length := secure_body_length(cryptex);
+        // Initialize the cipher with the envelope key.
+        if (EVP_EncryptInit_ex(@cipher,EVP_aes_256_cbc,nil,@envelope_key,@iv)<>1) or
+          (EVP_CIPHER_CTX_set_padding(@cipher,0)<>1) or
+          (EVP_EncryptUpdate(@cipher,body,body_length,@MessageToEncrypt[1],
+            Length(MessageToEncrypt) - (Length(MessageToEncrypt) MOD block_length))<>1) then begin
+              TLog.NewLog(lterror,'ECIES',Format('An error occurred while trying to secure the data using the chosen symmetric cipher. {error = %s}',
+              [ERR_error_string(ERR_get_error(),nil)]));
+              exit;
+            end;
+        // Check whether all of the data was encrypted. If they don't match up, we either have a partial block remaining, or an error occurred.
+        if (body_length<>Length(MessageToEncrypt)) then begin
+          // Make sure all that remains is a partial block, and their wasn't an error
+          if (Length(MessageToEncrypt) - body_length >= block_length) then begin
+            TLog.NewLog(lterror,'ECIES',Format('Unable to secure the data using the chosen symmetric cipher. {error = %s}',
+            [ERR_error_string(ERR_get_error(),nil)]));
+            exit;
+          end;
+          // Copy the remaining data into our partial block buffer. The memset() call ensures any extra bytes will be zero'ed out.
+          //SetLength(block,EVP_MAX_BLOCK_LENGTH);
+          FillMemory(@block,length(block),0);
+          CopyMemory(@block,Pointer(Integer(@MessageToEncrypt[1])+body_length),Length(MessageToEncrypt)-body_length);
+          // Advance the body pointer to the location of the remaining space, and calculate just how much room is still available.
+          body := Pointer(integer(body)+body_length);
+          body_length := secure_body_length(cryptex) - body_length;
+          if (body_length <0) then begin
+             TLog.NewLog(lterror,'ECIES','The symmetric cipher overflowed!');
+             exit;
+          end;
+          // Pass the final partially filled data block into the cipher as a complete block.
+          // The padding will be removed during the decryption process.
+          if (EVP_EncryptUpdate(@cipher, body, body_length, @block, block_length)<>1) then begin
+            TLog.NewLog(lterror,'ECIES',Format('Unable to secure the data using the chosen symmetric cipher. {error = %s}',
+            [ERR_error_string(ERR_get_error(),nil)]));
+            exit;
+          end;
+        end;
+        // Advance the pointer, then use pointer arithmetic to calculate how much of the body buffer has been used. The complex logic is needed so that we get
+        // the correct status regardless of whether there was a partial data block.
+        body := Pointer(integer(body)+body_length);
+        body_length := secure_body_length(cryptex) - (Integer(body)-Integer(secure_body_data(cryptex)));
+        if (body_length < 0) then begin
+          TLog.NewLog(lterror,'ECIES','The symmetric cipher overflowed!');
+          exit;
+        end;
+        if (EVP_EncryptFinal_ex(@cipher, body, body_length)<>1) then begin
+          TLog.NewLog(lterror,'ECIES',Format('Unable to secure the data using the chosen symmetric cipher. {error = %s}',
+          [ERR_error_string(ERR_get_error(),nil)]));
+          exit;
+        end;
+      finally
+        EVP_CIPHER_CTX_cleanup(@cipher);
+      end;
+      // Generate an authenticated hash which can be used to validate the data during decryption.
+      HMAC_CTX_init(@hmac);
+      Try
+        mac_length := secure_mac_length(cryptex);
+        // At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
+        aux := Pointer(Integer(@envelope_key) + key_length);
+        if (HMAC_Init_ex(@hmac, aux, key_length, ECIES_HASHER, nil)<>1)
+          OR (HMAC_Update(@hmac, secure_body_data(cryptex), secure_body_length(cryptex))<>1)
+          OR (HMAC_Final(@hmac, secure_mac_data(cryptex),mac_length)<>1) then begin
+          TLog.NewLog(lterror,'ECIES',Format('Unable to generate a data authentication code. {error = %s}',
+          [ERR_error_string(ERR_get_error(),nil)]));
+          exit;
+        end;
+      Finally
+        HMAC_CTX_cleanup(@hmac);
+      End;
+      SetLength(Result,secure_total_length(cryptex));
+      CopyMemory(@Result[1],cryptex,length(Result));
+    finally
+      secure_free(cryptex);
+    end;
+  finally
+    EC_KEY_free(PK);
+    EC_KEY_free(PEphemeral);
+  end;
+end;
+
+function ecies_key_create_public_octets(EC_OpenSSL_NID : Word; octets : PAnsiChar; length : size_t) : PEC_KEY;
+Var group : PEC_GROUP;
+  key : PEC_KEY;
+  point : PEC_POINT;
+Begin
+  Result := Nil;
+  key := EC_KEY_new();
+  group := EC_GROUP_new_by_curve_name(EC_OpenSSL_NID);
+  try
+    if (EC_KEY_set_group(key, group)<>1) then exit;
+    point := EC_POINT_new(group);
+    if (point = Nil) then exit;
+    try
+      if (EC_POINT_oct2point(group, point, octets, length, nil) <> 1) then exit;
+      if (EC_KEY_set_public_key(key, point)<>1) then exit;
+    finally
+      EC_POINT_free(point);
+    end;
+    if (EC_KEY_check_key(key)<>1) then exit;
+    Result := key;
+  finally
+    EC_GROUP_free(group);
+    if (Result=Nil) then EC_KEY_free(key);
+  end;
+End;
+
+
+function ECIESDecrypt(EC_OpenSSL_NID : Word; PrivateKey: PEC_KEY; logErrors : Boolean; const MessageToDecrypt: TRawBytes; Var Decrypted : AnsiString): Boolean;
+var
+  cryptex : Psecure_t;
+  hmac : HMAC_CTX;
+  key_length : size_t;
+  ephemeral : PEC_KEY;
+  envelope_key : Array[1..SHA512_DIGEST_LENGTH] of byte;
+  iv : Array[1..EVP_MAX_IV_LENGTH] of byte;
+  md : Array[1..EVP_MAX_MD_SIZE] of byte;
+  block, output : Pointer;
+  mac_length : Cardinal;
+  aux : Pointer;
+  output_length : Integer;
+  cipher : EVP_CIPHER_CTX;
+Begin
+  Result := false;
+  Decrypted := '';
+  cryptex := Psecure_t(@MessageToDecrypt[1]);
+  // Make sure we are generating enough key material for the symmetric ciphers.
+  key_length := EVP_CIPHER_key_length(EVP_aes_256_cbc);
+  if (key_length*2>SHA512_DIGEST_LENGTH) then begin
+    if logErrors then TLog.NewLog(lterror,'ECIES',Format('The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %d / required = %d)',
+      [SHA512_DIGEST_LENGTH DIV 8,(key_length * 2) DIV 8]));
+    exit;
+  end;
+  // Create the ephemeral key used specifically for this block of data.
+  ephemeral := ecies_key_create_public_octets(EC_OpenSSL_NID,secure_key_data(cryptex),secure_key_length(cryptex));
+  if (ephemeral=Nil) then begin
+    if logErrors then TLog.NewLog(lterror,'ECIES','An error occurred while trying to recreate the ephemeral key.');
+    exit;
+  end;
+  try
+    // Use the intersection of the provided keys to generate the envelope data used by the ciphers below.
+    // The ecies_key_derivation() function uses SHA 512 to ensure we have a sufficient amount of envelope key
+    // material and that the material created is sufficiently secure.
+    FillMemory(@envelope_key,length(envelope_key),0);
+    if (ECDH_compute_key(@envelope_key,SHA512_DIGEST_LENGTH,EC_KEY_get0_public_key(ephemeral),
+      PrivateKey, ecies_key_derivation_512)<>SHA512_DIGEST_LENGTH) then begin
+      if logErrors then TLog.NewLog(lterror,'ECIES',Format('An error occurred while trying to compute the envelope key. {error = %s}',[ERR_error_string(ERR_get_error, nil)]));
+      exit;
+    end;
+    // The envelope key material has been extracted, so we no longer need the user and ephemeral keys.
+  finally
+    EC_KEY_free(ephemeral);
+  end;
+  // Use the authenticated hash of the ciphered data to ensure it was not modified after being encrypted.
+  HMAC_CTX_init(@hmac);
+  try
+    // At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
+    aux := Pointer(Integer(@envelope_key) + key_length);
+    if (HMAC_Init_ex(@hmac, aux, key_length, ECIES_HASHER, Nil)<>1) Or
+       (HMAC_Update(@hmac, secure_body_data(cryptex), secure_body_length(cryptex))<>1) Or
+       (HMAC_Final(@hmac, @md, mac_length)<>1) then begin
+       if logErrors then TLog.NewLog(lterror,'ECIES',Format('Unable to generate the authentication code needed for validation. {error = %s}',
+         [ERR_error_string(ERR_get_error(), nil)]));
+       exit;
+    end;
+  finally
+    HMAC_CTX_cleanup(@hmac);
+  end;
+  // We can use the generated hash to ensure the encrypted data was not altered after being encrypted.
+  if (mac_length<>secure_mac_length(cryptex)) OR ( Not CompareMem(@md,secure_mac_data(cryptex), mac_length)) then begin
+    if logErrors then TLog.NewLog(lterror,'ECIES','The authentication code was invalid! The ciphered data has been corrupted!');
+    exit;
+  end;
+  // Create a buffer to hold the result.
+  output_length := secure_body_length(cryptex);
+  output := AllocMem(output_length+1);
+  block := output;
+  try
+    // For now we use an empty initialization vector. We also clear out the result buffer just to be on the safe side.
+    FillMemory(@iv,EVP_MAX_IV_LENGTH,0);
+    FillMemory(output,output_length+1,0);
+    EVP_CIPHER_CTX_init(@cipher);
+    try
+      // Decrypt the data using the chosen symmetric cipher.
+      if (EVP_DecryptInit_ex(@cipher, EVP_aes_256_cbc, nil,@envelope_key, @iv)<>1) Or
+         (EVP_CIPHER_CTX_set_padding(@cipher, 0)<>1) Or
+         (EVP_DecryptUpdate(@cipher, block, output_length, secure_body_data(cryptex), secure_body_length(cryptex))<>1) then begin
+         if logErrors then TLog.NewLog(lterror,'ECIES',Format('Unable to decrypt the data using the chosen symmetric cipher. {error = %s}',[ERR_error_string(ERR_get_error(), nil)]));
+         exit;
+      end;
+      block := Pointer(Integer(block) + output_length);
+      output_length := secure_body_length(cryptex) - output_length;
+      if (output_length<>0) then begin
+        if logErrors then TLog.NewLog(lterror,'ECIES',Format('The symmetric cipher failed to properly decrypt the correct amount of data! {output_length:%d}',[output_length]));
+        exit;
+      end;
+      if (EVP_DecryptFinal_ex(@cipher,block,output_length)<>1) then begin
+        if logErrors then TLog.NewLog(lterror,'ECIES',Format('Unable to decrypt the data using the chosen symmetric cipher. {error = %s}',[ERR_error_string(ERR_get_error(), nil)]));
+        exit;
+      end;
+      SetLength(Decrypted,secure_orig_length(cryptex));
+      CopyMemory(@Decrypted[1],output,length(Decrypted));
+      Result := true;
+    finally
+      FreeMemory(output);
+    end;
+  finally
+     EVP_CIPHER_CTX_cleanup(@cipher);
+  end;
+End;
+
+
+initialization
+finalization
+end.
+

+ 286 - 0
Units/PascalCoin/UFileStorage.pas

@@ -0,0 +1,286 @@
+unit UFileStorage;
+
+{ 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, UBlockChain;
+
+Type
+  TFileStorage = Class(TStorage)
+  private
+    FBaseDataFolder : AnsiString;
+    FIsRestoring : Boolean;
+    Function GetFolder(Const AOrphan : TOrphan): AnsiString;
+  protected
+    Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
+    Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
+    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan) : Boolean; override;
+    Function DoSaveBank : Boolean; override;
+    Function DoRestoreBank(max_block : Int64) : Boolean; override;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); override;
+    Function BlockExists(Block : Cardinal) : Boolean; override;
+  public
+    Constructor Create(AOwner : TComponent); Override;
+    Class Function GetBlockChainFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+    Class Function GetBankFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+  End;
+
+implementation
+
+uses
+  SysUtils, ULog, Forms, UConst;
+
+{ TFileStorage }
+
+procedure _CopyFile(const FileName, DestName: string);
+var CopyBuffer   : Pointer; { buffer for copying }
+  BytesCopied  : Longint;
+  Source, Dest : Integer; { handles }
+  Destination  : TFileName; { holder for expanded destination name }
+const ChunkSize  : Longint = 8192; { copy in 8K chunks }
+begin
+  Destination := DestName;
+  GetMem(CopyBuffer, ChunkSize); { allocate the buffer }
+  try
+   Source := FileOpen(FileName, fmShareDenyWrite); { open source file }
+   if (Source<0) then raise EFOpenError.CreateFmt('Error: Can''t open file!', [FileName]);
+   try
+     Dest := FileCreate(Destination); { create output file; overwrite existing }
+     if (Dest<0) then raise EFCreateError.CreateFmt('Error: Can''t create file!', [Destination]);
+     try
+       repeat
+         BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize); { read chunk }
+         if BytesCopied > 0  {if we read anything... }
+            then FileWrite(Dest, CopyBuffer^, BytesCopied); { ...write chunk }
+       until BytesCopied < ChunkSize; { until we run out of chunks }
+     finally
+       FileClose(Dest); { close the destination file }
+     end;
+   finally
+     FileClose(Source); { close the source file }
+   end;
+  finally
+   FreeMem(CopyBuffer, ChunkSize); { free the buffer }
+  end;
+end;
+
+function TFileStorage.BlockExists(Block: Cardinal): Boolean;
+begin
+  Result := FileExists(GetBlockChainFileName(GetFolder(Orphan),Block));
+end;
+
+constructor TFileStorage.Create(AOwner: TComponent);
+begin
+  FIsRestoring := false;
+  FBaseDataFolder := '';
+  inherited;
+end;
+
+procedure TFileStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
+Var bfn : AnsiString;
+begin
+  while (BlockExists(StartingDeleteBlock)) do begin
+    DeleteFile(GetBlockChainFileName(GetFolder(Orphan),StartingDeleteBlock));
+    inc(StartingDeleteBlock);
+    bfn := GetBankFileName(GetFolder(Orphan),StartingDeleteBlock);
+    if FileExists(bfn) then begin
+      DeleteFile(bfn);
+    end;
+  end;
+end;
+
+function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
+Var filename : AnsiString;
+  fs : TFileStream;
+  e : AnsiString;
+begin
+  Result := false;
+  filename :=  GetBlockChainFileName(GetFolder(Orphan),Block);
+  if Not FileExists(filename) then begin
+    TLog.NewLog(lterror,Classname,'Operations ('+inttostr(Block)+') file not found: '+filename);
+    exit;
+  end;
+  fs := TFileStream.Create(filename, fmOpenRead);
+  try
+    If Operations.LoadFromStream(false,true,fs, e) then result := true
+    else begin
+      TLog.NewLog(lterror,Classname,'Error reading file: '+filename+' Errors: '+e);
+    end;
+  finally
+    fs.Free;
+  end;
+end;
+
+function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan): Boolean;
+Var fn,destfn,bankfilename : AnsiString;
+  i : Cardinal;
+begin
+  if Bank.BlocksCount<Start_Block then exit;
+  TLog.NewLog(ltInfo,Classname,'Moving operations ('+inttostr(start_block)+' to '+inttostr(Bank.BlocksCount)+' from '+GetFolder('')+' to '+GetFolder(DestOrphan));
+  for i := start_block to Bank.BlocksCount - 1 do begin
+    fn := GetBlockChainFileName(GetFolder(Orphan),i);
+    destfn := GetBlockChainFileName(GetFolder(DestOrphan),i);
+    if FileExists(destfn) then TLog.NewLog(lterror,Classname,'File exists '+fn);
+    if Not FileExists(fn) then TLog.NewLog(lterror,Classname,'File not exists '+fn)
+    else _CopyFile(fn,destfn);
+    // Delete banks:
+    bankfilename := GetBankFileName(GetFolder(Orphan),i);
+    if (bankfilename<>'') then begin
+      if not DeleteFile(bankfilename) then begin
+        TLog.NewLog(lterror,Classname,'Cannot delete old bank file: '+bankfilename);
+      end;
+    end;
+  end;
+end;
+
+function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
+  function LoadOperationsFromFile(blockcount : Cardinal; Operations : TPCOperationsComp; var errors : AnsiString) : Boolean;
+  var filename : AnsiString;
+    fs : TFileStream;
+  Begin
+    Result := false;
+    filename := GetBlockChainFileName(GetFolder(Orphan),blockcount);
+    if FileExists(filename) then begin
+      fs := TFileStream.Create(filename, fmOpenRead);
+      try
+          Result := Operations.LoadFromStream(false,true,fs, errors);
+      finally
+        fs.Free;
+      end;
+    end else errors := 'File operations (Block: '+inttostr(blockcount)+') not exists:'+filename;
+  End;
+var
+    sr: TSearchRec;
+    FileAttrs,errcode: Integer;
+    folder : AnsiString;
+    filename,auxfn : AnsiString;
+    fs : TFileStream;
+    errors : AnsiString;
+    c,lastc : Cardinal;
+    operations : TPCOperationsComp;
+begin
+  FileAttrs := faArchive;
+  folder := GetFolder(Orphan);
+  filename := '';
+  operations := TPCOperationsComp.Create(Nil);
+  try
+    if SysUtils.FindFirst(folder+'\bank*.bank', FileAttrs, sr) = 0 then begin
+      lastc := 0;
+      repeat
+        if (sr.Attr and FileAttrs) = sr.Attr then begin
+          auxfn := ChangeFileExt(sr.Name,'');
+          val(copy(auxfn,5,length(auxfn)),c,errcode);
+          if (errcode=0) And ((c<=max_block)) then begin
+            if LoadOperationsFromFile(c,operations,errors) then begin
+              if (filename='') then begin
+                filename := sr.Name;
+                lastc := c;
+              end else if (lastc<c) then begin
+                filename := sr.Name;
+                lastc := c;
+              end;
+            end else begin
+              TLog.NewLog(lterror,ClassName,'Found a bank in file:'+filename+' but not operations:'+errors);
+            end;
+          end;
+        end;
+      until FindNext(sr) <> 0;
+      FindClose(sr);
+    end;
+    if (filename<>'') then begin
+      fs := TFileStream.Create(folder+'\'+filename,fmOpenRead);
+      try
+        if not Bank.LoadFromStream(fs,errors) then begin
+          TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
+        end;
+      finally
+        fs.Free;
+      end;
+    end;
+  finally
+    operations.Free;
+  end;
+end;
+
+function TFileStorage.DoSaveBank: Boolean;
+var fs: TFileStream;
+    bankfilename: AnsiString;
+begin
+  Result := true;
+  bankfilename := GetBankFileName(GetFolder(Orphan),Bank.BlocksCount);
+  if (bankfilename<>'') then begin
+    fs := TFileStream.Create(bankfilename,fmCreate);
+    try
+      fs.Size := 0;
+      Bank.SaveToStream(fs);
+    finally
+      fs.Free;
+    end;
+  end;
+end;
+
+function TFileStorage.DoSaveBlockChain(Operations: TPCOperationsComp): Boolean;
+Var
+  fs: TFileStream;
+  bankfilename,folder: AnsiString;
+begin
+  folder := GetFolder(Orphan);
+  If Not ForceDirectories(folder) then exit;
+  if Not FIsRestoring then begin
+    fs := TFileStream.Create(GetBlockChainFileName(folder,Operations.OperationBlock.block), fmCreate);
+    try
+      fs.Size := 0;
+      Operations.SaveToStream(true,false,fs);
+    finally
+      fs.Free;
+    end;
+  end;
+  Result := true;
+  SaveBank;
+end;
+
+class function TFileStorage.GetBankFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
+Var c : Cardinal;
+  folder : AnsiString;
+begin
+  Result := '';
+  if (Block MOD CT_BankToDiskEveryNBlocks)<>0 then exit; // No bank!
+  If not ForceDirectories(BaseDataFolder) then exit;
+  Result := BaseDataFolder + '\bank'+Format('%.6d',[Block])+'.bank';
+end;
+
+class function TFileStorage.GetBlockChainFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
+Var c : Cardinal;
+  folder : AnsiString;
+begin
+  Result := '';
+  c := block DIV 1000;
+  folder := BaseDataFolder+'\block'+Format('%.4d',[c]);
+  If not ForceDirectories(folder) then exit;
+  Result :=  folder+'\op'+Format('%.6d',[block])+ '.bco';
+end;
+
+
+function TFileStorage.GetFolder(const AOrphan: TOrphan): AnsiString;
+begin
+  if FBaseDataFolder = '' then begin
+    FBaseDataFolder := ExtractFileDir(Application.ExeName)+'\'+ChangeFileExt(ExtractFileName(Application.ExeName),'')+ '\BCOperations';
+  end;
+  if AOrphan<>'' then Result := FBaseDataFolder + '\'+AOrphan
+  else Result := FBaseDataFolder;
+end;
+
+end.

+ 212 - 0
Units/PascalCoin/ULog.pas

@@ -0,0 +1,212 @@
+unit ULog;
+
+{ 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, UThread;
+
+type
+  TLogType = (ltinfo, ltupdate, lterror, ltdebug);
+  TLogTypes = set of TLogType;
+
+  TNewLogEvent = procedure(logtype : TLogType; Time : TDateTime; ThreadID : Cardinal; Const sender, logtext : AnsiString) of object;
+
+  TLog = Class;
+  TThreadSafeLogEvent = Class(TPCThread)
+    FLog : TLog;
+    Procedure SynchronizedProcess;
+  protected
+    procedure BCExecute; override;
+  End;
+
+  TLogData = Record
+    Logtype : TLogType;
+    Time : TDateTime;
+    ThreadID : Cardinal;
+    Sender, Logtext : AnsiString
+  End;
+
+  TLog = Class(TComponent)
+  private
+    FLogDataList : TThreadList;
+    FOnNewLog: TNewLogEvent;
+    FFileStream : TFileStream;
+    FFileName: AnsiString;
+    FSaveTypes: TLogTypes;
+    FThreadSafeLogEvent : TThreadSafeLogEvent;
+    Procedure NotifyNewLog(logtype : TLogType; Const sender, logtext : AnsiString);
+    procedure SetFileName(const Value: AnsiString);
+  protected
+    Procedure DoLog(logtype : TLogType; sender, logtext : AnsiString); virtual;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Class Procedure NewLog(logtype : TLogType; Const sender, logtext : AnsiString);
+    Property OnNewLog : TNewLogEvent read FOnNewLog write FOnNewLog;
+    Property FileName : AnsiString read FFileName write SetFileName;
+    Property SaveTypes : TLogTypes read FSaveTypes write FSaveTypes;
+  End;
+
+Const
+  CT_LogType : Array[TLogType] of AnsiString = ('Info','Update','Error','Debug');
+  CT_TLogTypes_ALL : TLogTypes = [ltinfo, ltupdate, lterror, ltdebug];
+  CT_TLogTypes_DEFAULT : TLogTypes = [ltinfo, ltupdate, lterror];
+
+
+implementation
+
+uses SysUtils;
+
+var _logs : TList;
+Type PLogData = ^TLogData;
+
+{ TLog }
+
+constructor TLog.Create(AOwner: TComponent);
+Var l : TList;
+begin
+  inherited;
+
+  FLogDataList := TThreadList.Create;
+  FFileStream := Nil;
+  FFileName := '';
+  FSaveTypes := [ltinfo, ltupdate, lterror];
+  if (Not assigned(_logs)) then _logs := TList.Create;
+  _logs.Add(self);
+  FThreadSafeLogEvent := TThreadSafeLogEvent.Create(true);
+  FThreadSafeLogEvent.FLog := Self;
+  FThreadSafeLogEvent.Suspended := false;
+end;
+
+destructor TLog.Destroy;
+var f : TFileStream;
+  l : TList;
+  i : Integer;
+  P : PLogData;
+begin
+  FThreadSafeLogEvent.Terminate;
+  FThreadSafeLogEvent.WaitFor;
+  FThreadSafeLogEvent.Free;
+  _logs.Remove(Self);
+  f := FFileStream;
+  FFileStream := Nil;
+  f.Free;
+  l := FLogDataList.LockList;
+  try
+    for i := 0 to l.Count - 1 do begin
+      P := PLogData(l[i]);
+      Dispose(P);
+    end;
+    l.Clear;
+  finally
+    FLogDataList.UnlockList;
+  end;
+  FLogDataList.Free;
+  inherited;
+end;
+
+procedure TLog.DoLog(logtype: TLogType; sender, logtext: AnsiString);
+begin
+//
+end;
+
+class procedure TLog.NewLog(logtype: TLogType; Const sender, logtext: AnsiString);
+var i : Integer;
+begin
+  if (Not Assigned(_logs)) then exit;
+  for i := 0 to _logs.Count - 1 do begin
+    TLog(_logs[i]).NotifyNewLog(logtype,sender,logtext);
+  end;
+end;
+
+procedure TLog.NotifyNewLog(logtype: TLogType; Const sender, logtext: AnsiString);
+Var s,tid : AnsiString;
+  tsle : TThreadSafeLogEvent;
+  P : PLogData;
+begin
+  if assigned(FFileStream) And (logType in FSaveTypes) then begin
+    if TThread.CurrentThread.ThreadID=MainThreadID then tid := ' MAIN:' else tid:=' TID:';
+    s := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',now)+tid+IntToHex(TThread.CurrentThread.ThreadID,8)+' ['+CT_LogType[logtype]+'] <'+sender+'> '+logtext+#13#10;
+    FFileStream.Write(s[1],length(s));
+  end;
+  // Add to a thread safe list
+  New(P);
+  P^.Logtype := logtype;
+  P^.Time := now;
+  P^.ThreadID :=TThread.CurrentThread.ThreadID;
+  P^.Sender := sender;
+  P^.Logtext := logtext;
+  FLogDataList.Add(P);
+  DoLog(logtype,sender,logtext);
+end;
+
+procedure TLog.SetFileName(const Value: AnsiString);
+var fm : Word;
+begin
+  if FFileName = Value then exit;
+  if assigned(FFileStream) then Begin
+    FreeAndNil(FFileStream);
+  End;
+  FFileName := Value;
+  if (FFileName<>'') then begin
+    If Not ForceDirectories(ExtractFileDir(FFileName)) then exit;
+    if FileExists(FFileName) then fm := fmOpenWrite + fmShareDenyWrite
+    else fm := fmCreate + fmShareDenyWrite;
+    FFileStream := TFileStream.Create(FFileName,fm);
+    FFileStream.Position := FFileStream.size; // To the end!
+    NotifyNewLog(ltinfo,Classname,'Log file start: '+FFileName);
+  end;
+end;
+
+{ TThreadSafeLogEvent }
+
+procedure TThreadSafeLogEvent.BCExecute;
+begin
+  while (not Terminated) do begin
+    sleep(100);
+    If Not Terminated then Synchronize(SynchronizedProcess);
+  end;
+end;
+
+procedure TThreadSafeLogEvent.SynchronizedProcess;
+Var l : TList;
+  i : Integer;
+  P : PLogData;
+begin
+  // This event is thread safe and will do OnNewLog on main thread
+  l := FLog.FLogDataList.LockList;
+  try
+    try
+      if Assigned(FLog.FOnNewLog) then begin
+        for i := 0 to l.Count - 1 do begin
+          P := PLogData(l[i]);
+          FLog.OnNewLog( P^.Logtype,P^.Time,P^.ThreadID,P^.Sender,P^.Logtext );
+          Dispose(P);
+        end;
+      end;
+    finally
+      // Protection for possible raise
+      l.Clear;
+    end;
+  finally
+    FLog.FLogDataList.UnlockList;
+  end;
+end;
+
+initialization
+  _logs := Nil;
+finalization
+end.

+ 202 - 0
Units/PascalCoin/UMiner.pas

@@ -0,0 +1,202 @@
+unit UMiner;
+
+{ 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 UBlockChain, Classes, SyncObjs, Windows, UAccounts, UThread;
+
+Type
+  TMinerThread = Class;
+
+  TMinerNewAccountFound = procedure(sender : TMinerThread; Operations : TPCOperationsComp) of object;
+  TMinerErrorFound = procedure(sender : TMinerThread; Operations : TPCOperationsComp; errors : String) of object;
+
+  TMinerThread = Class(TPCThread)
+  private
+    FOperations : TPCOperationsComp;
+    FLock: TRTLCriticalSection;
+    FPlayCount : Int64;
+    FTotalActiveTime : Int64;
+    FLastStartTickCount : Cardinal;
+    //
+    errors : AnsiString;
+    FOnNewAccountFound: TMinerNewAccountFound;
+    FOnErrorFound: TMinerErrorFound;
+    FAccountKey: TAccountKey;
+    FPaused: Boolean;
+    procedure SynchronizedNewBlockFound;
+    procedure SynchronizedError;
+    procedure SetAccountKey(const Value: TAccountKey);
+    Procedure CheckIfCanRecoverBlocks;
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(Bank : TPCBank; minerAccountKey : TAccountKey; AOnNewAccountFound : TMinerNewAccountFound; AOnErrorFound : TMinerErrorFound);
+    destructor Destroy; override;
+    Function MinerLockOperations : TPCOperationsComp;
+    Procedure MinerUnLockOperations(IsNewBlock : Boolean);
+    Property OnNewAccountFound : TMinerNewAccountFound read FOnNewAccountFound write FOnNewAccountFound;
+    Property OnErrorFound : TMinerErrorFound read FOnErrorFound write FOnErrorFound;
+    Property PlayCount : Int64 read FPlayCount;
+    Property AccountKey : TAccountKey read FAccountKey write SetAccountKey;
+    Property Paused : Boolean read FPaused Write FPaused;
+    Function HashRate : Int64;
+  End;
+
+implementation
+
+uses UNode, ULog, SysUtils, UConst, UOpTransaction;
+
+{ TMinerThread }
+
+procedure TMinerThread.CheckIfCanRecoverBlocks;
+Var n_account : Cardinal;
+  recover_block : Cardinal;
+  OpR : TOpRecoverFounds;
+  recover_q : UInt64;
+  errors : AnsiString;
+Begin
+  if FOperations.bank.SafeBox.BlocksCount<CT_RecoverFoundsWaitInactiveCount then exit;
+  recover_block := FOperations.bank.SafeBox.BlocksCount-CT_RecoverFoundsWaitInactiveCount;
+  n_account := 0;
+  while (n_account < FOperations.bank.SafeBox.AccountsCount) do begin
+   If FOperations.SafeBoxTransaction.Account(n_account).updated_block<recover_block then begin
+     recover_q := FOperations.SafeBoxTransaction.Account(n_account).balance;
+     if (recover_q>0) then begin
+       // Recover it!
+       if (recover_q > CT_MaxTransactionFee) then recover_q := CT_MaxTransactionFee;
+       OpR := TOpRecoverFounds.Create(n_account,FOperations.SafeBoxTransaction.Account(n_account).n_operation+1,recover_q);
+       If FOperations.AddOperation(true,OpR,errors) then begin
+         TLog.NewLog(ltinfo,Classname,Format('Recover founds executed at account: %d amount: %s Operation: %s',[n_account,TAccountComp.FormatMoney(recover_q),OpR.ToString]));
+       end else begin
+         TLog.NewLog(lterror,Classname,Format('Error recovering founds executed at account: %d amount: %s Error: %s',[n_account,TAccountComp.FormatMoney(recover_q),errors]));
+       end;
+     end;
+   end;
+   inc(n_account);
+  end;
+End;
+
+constructor TMinerThread.Create(Bank : TPCBank; minerAccountKey : TAccountKey; AOnNewAccountFound : TMinerNewAccountFound; AOnErrorFound : TMinerErrorFound);
+begin
+  inherited Create(true);
+  FTotalActiveTime := 0;
+  FLastStartTickCount := 0;
+  FPaused := true;
+  FPlayCount := 0;
+  FAccountKey := minerAccountKey;
+  InitializeCriticalSection(FLock);
+  FOperations := TPCOperationsComp.Create(nil);
+  FOperations.Bank := Bank;
+  FOperations.AccountKey := AccountKey;
+  FOnNewAccountFound := AOnNewAccountFound;
+  FOnErrorFound := AOnErrorFound;
+  Suspended := false;
+end;
+
+procedure TMinerThread.BCExecute;
+Var i : Integer;
+  winner : Boolean;
+  newBlockAccount : TBlockAccount;
+begin
+  TLog.NewLog(ltinfo,ClassName,'New miner');
+  while (not Terminated) do begin
+    while (FPaused) And (Not Terminated) do begin
+      sleep(1);
+    end;
+    FLastStartTickCount := GetTickCount;
+    Try
+      if Terminated then exit;
+      winner := false;
+      TPCThread.ProtectEnterCriticalSection(Self,FLock);
+      try
+        FOperations.UpdateTimestamp;
+        FOperations.AccountKey := FAccountKey;
+        for I := 0 to 50000 do begin
+          inc(FPlayCount);
+          if FOperations.IncrementNOnce then begin
+            winner := true;
+            break;
+          end;
+        end;
+      finally
+        LeaveCriticalSection(FLock);
+      end;
+      if (winner) then begin
+        Try
+          If TNode.Node.AddNewBlockChain(Self,nil,FOperations,newBlockAccount,errors) then begin
+            Synchronize(SynchronizedNewBlockFound);
+          end else begin
+            Synchronize(SynchronizedError);
+          end;
+        Except
+          On E:Exception do begin
+            TLog.NewLog(lterror,Classname,'Exception on adding new block by miner: '+E.Message);
+            Raise;
+          end;
+        End;
+      end;
+    Finally
+      if FLastStartTickCount>0 then begin
+        FTotalActiveTime := FTotalActiveTime + (GetTickCount - FLastStartTickCount);
+      end;
+    End;
+    if Not Terminated then Sleep(1);
+  end;
+  TLog.NewLog(ltinfo,ClassName,'Destroying Miner');
+end;
+
+destructor TMinerThread.Destroy;
+begin
+  DeleteCriticalSection(Flock);
+  FOperations.Free;
+  inherited;
+end;
+
+function TMinerThread.HashRate: Int64;
+begin
+  if FTotalActiveTime>0 then
+    Result := (FPlayCount*1000) DIV FTotalActiveTime
+  else Result := 0;
+end;
+
+function TMinerThread.MinerLockOperations: TPCOperationsComp;
+begin
+  TPCThread.ProtectEnterCriticalSection(Self,FLock);
+  Result := FOperations;
+end;
+
+procedure TMinerThread.MinerUnLockOperations(IsNewBlock : Boolean);
+begin
+  LeaveCriticalSection(FLock);
+  if IsNewBlock then CheckIfCanRecoverBlocks;
+end;
+
+procedure TMinerThread.SetAccountKey(const Value: TAccountKey);
+begin
+  FAccountKey := Value;
+end;
+
+procedure TMinerThread.SynchronizedError;
+begin
+  if assigned(FOnErrorFound) then FOnErrorFound(self,FOperations,errors);
+end;
+
+procedure TMinerThread.SynchronizedNewBlockFound;
+begin
+  if assigned(FOnNewAccountFound) then FOnNewAccountFound(self,FOperations);
+end;
+
+end.

+ 2413 - 0
Units/PascalCoin/UNetProtocol.pas

@@ -0,0 +1,2413 @@
+unit UNetProtocol;
+
+{ 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 UBlockChain, Classes, SysUtils, UAccounts, UThread, Sockets, ExtCtrls,
+  UCrypto,
+  Windows;
+
+Const
+  CT_MagicNetIdentification = $0A043580; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicRequest = $0001;
+  CT_MagicResponse = $0002;
+  CT_MagicAutoSend = $0003;
+
+  CT_NetOp_Hello = $0001;              // Sends my last operationblock + servers. Receive last operationblock + servers + same operationblock number of sender
+  CT_NetOp_Error = $0002;
+  CT_NetOp_Message = $0003;
+  CT_NetOp_GetBlocks = $0010;
+  CT_NetOp_GetOperationsBlock = $0005; // Sends from and to. Receive a number of OperationsBlock to check
+  CT_NetOp_NewBlock = $0011;
+  CT_NetOp_AddOperations = $0020;
+
+
+  CT_NetError_InvalidProtocolVersion = $0001;
+  CT_NetError_IPBlackListed = $0002;
+  CT_NetError_InvalidDataBufferInfo = $0010;
+  CT_NetError_InternalServerError = $0011;
+  CT_NetError_InvalidNewAccount = $0012;
+
+Type
+  {
+  Net Protocol:
+
+  3 different types: Request,Response or Auto-send
+  Request:   <Magic Net Identification (4b)><request  (2b)><operation (2b)><0x0000 (2b)><request_id(4b)><protocol info(4b)><data_length(4b)><request_data (data_length bytes)>
+  Response:  <Magic Net Identification (4b)><response (2b)><operation (2b)><error_code (2b)><request_id(4b)><protocol info(4b)><data_length(4b)><response_data (data_length bytes)>
+  Auto-send: <Magic Net Identification (4b)><autosend (2b)><operation (2b)><0x0000 (2b)><0x00000000 (4b)><protocol info(4b)><data_length(4b)><data (data_length bytes)>
+
+  Min size: 4b+2b+2b+2b+4b+4b+4b = 22 bytes
+  Max size: (depends on last 4 bytes) = 22..65K
+  }
+
+  TNetTcpIpClient = TCustomIpClient;
+
+  TNetTransferType = (ntp_unknown, ntp_request, ntp_response, ntp_autosend);
+
+  TNetProtocolVersion = Record
+    protocol_version,
+    protocol_available : Word;
+  end;
+
+  TNetHeaderData = Record
+    header_type : TNetTransferType;
+    protocol : TNetProtocolVersion;
+    operation : Word;
+    request_id : Cardinal;
+    buffer_data_length : Cardinal;
+    //
+    is_error : Boolean;
+    error_code : Integer;
+    error_text : AnsiString;
+  end;
+
+  TNetConnection = Class;
+
+  TNodeServerAddress = Record
+    ip : AnsiString;
+    port : Word;
+    last_connection : Cardinal;
+    //
+    netConnection : TNetConnection;
+    its_myself : Boolean;
+    last_attempt_to_connect : TDateTime;
+    total_failed_attemps_to_connect : Integer;
+    BlackListText : String;
+  end;
+  PNodeServerAddress = ^TNodeServerAddress;
+
+  TNetMessage_Hello = Record
+     last_operation : TOperationBlock;
+     servers_address : Array of TNodeServerAddress;
+  end;
+
+  TNetRequestRegistered = Record
+    NetClient : TNetConnection;
+    Operation : Word;
+    RequestId : Cardinal;
+    SendTime : TDateTime;
+  end;
+
+  TThreadCheckConnections = Class(TPCThread)
+  private
+    FLastCheckTS : Cardinal;
+  protected
+    procedure BCExecute; override;
+  public
+  End;
+
+  TNetStatistics = Record
+    ActiveConnections : Integer; // All connections wiht "connected" state
+    ClientsConnections : Integer; // All clients connected to me like a server with "connected" state
+    ServersConnections : Integer; // All servers where I'm connected
+    TotalConnections : Integer;
+    TotalClientsConnections : Integer;
+    TotalServersConnections : Integer;
+    BytesReceived : Int64;
+    BytesSend : Int64;
+  end;
+
+  TNetData = Class(TComponent)
+  private
+    FNodePrivateKey : TECPrivateKey;
+    FNetConnections : TThreadList;
+    FNodeServers : TThreadList;
+    FBlackList : TThreadList;
+    FLastRequestId : Cardinal;
+    FRegisteredRequests : TThreadList;
+    FIsDiscoveringServers : Boolean;
+    FIsGettingNewBlockChainFromClient : Boolean;
+    FOnNetConnectionsUpdated: TNotifyEvent;
+    FOnNodeServersUpdated: TNotifyEvent;
+    FOnBlackListUpdated: TNotifyEvent;
+    FThreadCheckConnections : TThreadCheckConnections;
+    FOnReceivedHelloResponse: TNotifyEvent;
+    FNetStatistics: TNetStatistics;
+    FOnStatisticsChanged: TNotifyEvent;
+    FMaxRemoteOperationBlock : TOperationBlock;
+    Procedure IncStatistics(incActiveConnections,incClientsConnections,incServersConnections : Integer; incBytesReceived,incBytesSend : Int64);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    Function IndexOfNetClient(ListToSearch : TList; ip : AnsiString; port : Word; indexStart : Integer = 0) : Integer;
+    Procedure DeleteNetClient(List : TList; index : Integer);
+    Procedure CleanBlackList;
+    Procedure DiscoverServersTerminated(Sender : TObject);
+  public
+    Class function HeaderDataToText(const HeaderData : TNetHeaderData) : AnsiString;
+    Class function ExtractHeaderInfo(buffer : TStream; var HeaderData : TNetHeaderData; DataBuffer : TStream; var IsValidHeaderButNeedMoreData : Boolean) : Boolean;
+    Class Function OperationToText(operation : Word) : AnsiString;
+    // Only 1 NetData
+    Class Function NetData : TNetData;
+    //
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Function Bank : TPCBank;
+    Function NewRequestId : Cardinal;
+    Procedure RegisterRequest(Sender: TNetConnection; operation : Word; request_id : Cardinal);
+    Function UnRegisterRequest(Sender: TNetConnection; operation : Word; request_id : Cardinal) : Boolean;
+    Function PendingRequest(Sender : TNetConnection; var requests_data : AnsiString ) : Integer;
+    Procedure AddServer(NodeServerAddress : TNodeServerAddress);
+    Function IsBlackListed(const ip : AnsiString; port : Word) : Boolean;
+    //
+    Function ConnectionsCount(CountOnlyNetClients : Boolean) : Integer;
+    Function Connection(index : Integer) : TNetConnection;
+    Function ConnectionExists(ObjectPointer : TObject) : Boolean;
+    Function FindConnectionByClientRandomValue(Sender : TNetConnection) : TNetConnection;
+    Procedure DiscoverServers;
+    Procedure DisconnectClients;
+    Procedure GetNewBlockChainFromClient(Connection : TNetConnection);
+    Property OnNetConnectionsUpdated : TNotifyEvent read FOnNetConnectionsUpdated write FOnNetConnectionsUpdated;
+    Property OnNodeServersUpdated : TNotifyEvent read FOnNodeServersUpdated write FOnNodeServersUpdated;
+    Property OnBlackListUpdated : TNotifyEvent read FOnBlackListUpdated write FOnBlackListUpdated;
+    Property BlackList : TThreadList read FBlackList;
+    Property NodeServers : TThreadList read FNodeServers;
+    Property NetConnections : TThreadList read FNetConnections;
+    Property OnReceivedHelloResponse : TNotifyEvent read FOnReceivedHelloResponse write FOnReceivedHelloResponse;
+    Property NetStatistics : TNetStatistics read FNetStatistics;
+    Property OnStatisticsChanged : TNotifyEvent read FOnStatisticsChanged write FOnStatisticsChanged;
+    Property IsDiscoveringServers : Boolean read FIsDiscoveringServers;
+    Property IsGettingNewBlockChainFromClient : Boolean read FIsGettingNewBlockChainFromClient;
+    Property MaxRemoteOperationBlock : TOperationBlock read FMaxRemoteOperationBlock;
+    Property NodePrivateKey : TECPrivateKey read FNodePrivateKey;
+  End;
+
+  TNetConnection = Class(TComponent)
+  private
+    FClient : TNetTcpIpClient;
+    FRemoteOperationBlock : TOperationBlock;
+    FSocketError : Integer;
+    FLastDataReceivedTS : Cardinal;
+    FLastDataSendedTS : Cardinal;
+    FClientBufferRead : TStream;
+    FNetLock : TRTLCriticalSection;
+    FIsWaitingForResponse : Boolean;
+    FLastKnownTimestampDiff : Int64;
+    FIsMyselfServer : Boolean;
+    FClientPublicKey : TAccountKey;
+    function GetConnected: Boolean;
+    procedure SetConnected(const Value: Boolean);
+    procedure TcpClient_OnError(Sender: TObject; SocketError: Integer);
+    procedure TcpClient_OnConnect(Sender: TObject);
+    procedure TcpClient_OnDisconnect(Sender: TObject);
+    procedure TcpClient_OnReceive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
+    procedure TcpClient_OnSend(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
+    procedure TcpClient_OnCreateHandle(Sender : TObject);
+    procedure TcpClient_OnDestroyHandle(Sender : TObject);
+    Function DoSendAndWaitForResponse(operation: Word; RequestId: Integer; SendDataBuffer, ReceiveDataBuffer: TStream; MaxWaitTime : Cardinal; var HeaderData : TNetHeaderData) : Boolean;
+    procedure DoProcessBuffer;
+    Procedure DoProcess_Hello(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_Message(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_GetBlocks_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_GetBlocks_Response(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_GetOperationsBlock_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_NewBlock(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_AddOperations(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);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    Procedure Send(NetTranferType : TNetTransferType; operation, errorcode : Word; request_id : Integer; DataBuffer : TStream);
+    Procedure SendError(NetTranferType : TNetTransferType; operation, request_id : Integer; error_code : Integer; error_text : AnsiString);
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Function ConnectTo(ServerIP: String; ServerPort:Word) : Boolean;
+    Property Connected : Boolean read GetConnected write SetConnected;
+    Function Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
+    Function Send_NewBlockFound : Boolean;
+    Function Send_GetBlocks(StartAddress, quantity : Cardinal; var request_id : Cardinal) : Boolean;
+    Function Send_AddOperations(Operations : TOperationsHashTree) : Boolean;
+    Function Send_Message(Const TheMessage : AnsiString) : Boolean;
+    Property Client : TNetTcpIpClient read FClient;
+    Property IsMyselfServer : Boolean read FIsMyselfServer;
+  End;
+
+  TNetClient = Class;
+  TNetClientThread = Class(TPCThread)
+  private
+    FNetClient : TNetClient;
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(NetClient : TNetClient);
+  End;
+
+  TNetClient = Class(TNetConnection)
+  private
+    FNetClientThread : TNetClientThread;
+    Procedure OnNetClientThreadTerminated(Sender : TObject);
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+  End;
+
+  TNetServerClient = Class(TNetConnection);
+
+  TNetServer = Class(TComponent)
+  private
+    FNetClients : TList;  // When a connection is established to a new client, a TNetConnection is created (p2p)
+    FTCPServer : TTcpServer;
+    FPort: Word;
+    function GetActive: Boolean;
+    procedure SetActive(const Value: Boolean);
+    procedure OnTcpServerCreateHandle(Sender : TObject);
+    procedure OnTcpServerDestroyHandle(Sender : TObject);
+    procedure OnTcpServerListening(Sender : TObject);
+    procedure OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
+    procedure OnTcpServerGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
+    procedure SetPort(const Value: Word);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property Active : Boolean read GetActive write SetActive;
+    Property Port : Word read FPort Write SetPort;
+  End;
+
+  TThreadDiscoverConnection = Class(TPCThread)
+    FNodeServerAddress : TNodeServerAddress;
+    procedure Synchronized_notify;
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(NodeServerAddress: TNodeServerAddress; NotifyOnTerminate : TNotifyEvent);
+  End;
+
+  TThreadGetNewBlockChainFromClient = Class(TPCThread)
+  protected
+    procedure BCExecute; override;
+  End;
+
+
+Const
+  CT_TNodeServerAddress_NUL : TNodeServerAddress = (ip:'';port:0;last_connection:0; netConnection:nil;its_myself:false;last_attempt_to_connect:0;total_failed_attemps_to_connect:0;BlackListText:'');
+  CT_TNetStatistics_NUL : TNetStatistics = (ActiveConnections:0;ClientsConnections:0;ServersConnections:0;TotalConnections:0;TotalClientsConnections:0;TotalServersConnections:0;BytesReceived:0;BytesSend:0);
+
+implementation
+
+uses
+  UConst, ULog, UNode, UTime, UECIES;
+
+Const
+  CT_NetTransferType : Array[TNetTransferType] of AnsiString = ('Unknown','Request','Response','Autosend');
+  CT_NetHeaderData : TNetHeaderData = (header_type:ntp_unknown;protocol:(protocol_version:0;protocol_available:0);operation:0;request_id:0;buffer_data_length:0;is_error:false;error_code:0;error_text:'');
+
+{ TNetData }
+
+Var _NetData : TNetData = nil;
+
+Type PNetRequestRegistered = ^TNetRequestRegistered;
+
+procedure TNetData.AddServer(NodeServerAddress: TNodeServerAddress);
+Var P : PNodeServerAddress;
+  i : Integer;
+  l : TList;
+begin
+  l := FNodeServers.LockList;
+  try
+    i := IndexOfNetClient(l,NodeServerAddress.ip,NodeServerAddress.port);
+    if i>=0 then exit;
+    New(P);
+    P^ := NodeServerAddress;
+    l.Add(P);
+    TLog.NewLog(ltdebug,Classname,'Adding new server: '+NodeServerAddress.ip+':'+Inttostr(NodeServerAddress.port));
+  finally
+    FNodeServers.UnlockList;
+  end;
+  if Assigned(FOnNodeServersUpdated) then FOnNodeServersUpdated(Self);
+end;
+
+function TNetData.Bank: TPCBank;
+begin
+  Result := TNode.Node.Bank;
+end;
+
+procedure TNetData.CleanBlackList;
+Var P : PNodeServerAddress;
+  i,n : Integer;
+  l : TList;
+begin
+  // This procedure cleans old blacklisted IPs
+  n := 0;
+  l := FBlackList.LockList;
+  Try
+    for i := l.Count - 1 downto 0 do begin
+      P := l[i];
+      if
+        // Is an old blacklisted IP? (More than 1 hour)
+        (((P^.last_connection+(60*60)) < (UnivDateTimeToUnix(DateTime2UnivDateTime(now)))) And (Not P^.its_myself))
+        then begin
+        l.Delete(i);
+        Dispose(P);
+        inc(n);
+      end;
+    end;
+  Finally
+    FBlackList.UnlockList;
+  End;
+  if (n>0) And (Assigned(FOnBlackListUpdated)) then FOnBlackListUpdated(Self);
+end;
+
+function TNetData.Connection(index: Integer): TNetConnection;
+Var l : TList;
+begin
+  l := FNetConnections.LockList;
+  try
+    Result := TNetConnection( l[index] );
+  finally
+    FNetConnections.UnlockList;
+  end;
+end;
+
+function TNetData.ConnectionExists(ObjectPointer: TObject): Boolean;
+var i : Integer;
+  l : TList;
+begin
+  Result := false;
+  l := FNetConnections.LockList;
+  try
+    for i := 0 to l.Count - 1 do begin
+      if TObject(l[i])=ObjectPointer then begin
+        Result := TNetConnection(ObjectPointer).Connected;
+        exit;
+      end;
+    end;
+  finally
+    FNetConnections.UnlockList;
+  end;
+end;
+
+function TNetData.ConnectionsCount(CountOnlyNetClients : Boolean): Integer;
+var i : Integer;
+  l : TList;
+begin
+  l := FNetConnections.LockList;
+  try
+    if CountOnlyNetClients then begin
+      Result := 0;
+      for i := 0 to l.Count - 1 do begin
+        if TObject(l[i]) is TNetClient then inc(Result);
+      end;
+    end else Result := l.Count;
+  finally
+    FNetConnections.UnlockList;
+  end;
+end;
+
+constructor TNetData.Create;
+begin
+  FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
+  FNetStatistics := CT_TNetStatistics_NUL;
+  FOnStatisticsChanged := Nil;
+  FOnNetConnectionsUpdated := Nil;
+  FOnNodeServersUpdated := Nil;
+  FOnBlackListUpdated := Nil;
+  FIsDiscoveringServers := false;
+  FNodeServers := TThreadList.Create;
+  FRegisteredRequests := TThreadList.Create;
+  FLastRequestId := 0;
+  FNetConnections := TThreadList.Create;
+  FBlackList := TThreadList.Create;
+  FIsGettingNewBlockChainFromClient := false;
+  FNodePrivateKey := TECPrivateKey.Create;
+  FNodePrivateKey.GenerateRandomPrivateKey(CT_Default_EC_OpenSSL_NID);
+  FThreadCheckConnections := TThreadCheckConnections.Create(false);
+end;
+
+procedure TNetData.DeleteNetClient(List: TList; index: Integer);
+Var P : PNodeServerAddress;
+begin
+  P := List.Items[index];
+  List.Delete(index);
+  Dispose(P);
+end;
+
+destructor TNetData.Destroy;
+Var l : TList;
+begin
+  FOnStatisticsChanged := Nil;
+  FOnNetConnectionsUpdated := Nil;
+  FOnNodeServersUpdated := Nil;
+  FOnBlackListUpdated := Nil;
+  FThreadCheckConnections.Terminate;
+  FThreadCheckConnections.WaitFor;
+  CleanBlackList;
+  l := FNodeServers.LockList;
+  try
+    while (l.Count>0) do DeleteNetClient(l,l.Count-1);
+  finally
+    FNodeServers.UnlockList;
+    FNodeServers.Free;
+  end;
+  l := FBlackList.LockList;
+  try
+    while (l.Count>0) do DeleteNetClient(l,l.Count-1);
+  finally
+    FBlackList.UnlockList;
+    FBlackList.Free;
+  end;
+  FNetConnections.Free;
+  FNodePrivateKey.Free;
+  inherited;
+end;
+
+procedure TNetData.DisconnectClients;
+var i : Integer;
+  l : TList;
+begin
+  l := FNetConnections.LockList;
+  Try
+    for i := l.Count - 1 downto 0 do begin
+      if TObject(l[i]) is TNetClient then begin
+        TNetClient(l[i]).Connected := false;
+      end;
+    end;
+  Finally
+    FNetConnections.UnlockList;
+  End;
+end;
+
+procedure TNetData.DiscoverServers;
+  Procedure sw(l : TList);
+  Var i,j,x,y : Integer;
+  begin
+    if l.Count<=1 then exit;
+    j := Random(l.Count);
+    for i := 0 to j do begin
+      x := Random(l.Count);
+      y := Random(l.Count);
+      if x<>y then l.Exchange(x,y);
+    end;
+  end;
+Var P : PNodeServerAddress;
+  i,j : Integer;
+  l,lns : TList;
+  tdc : TThreadDiscoverConnection;
+begin
+  if TPCThread.ThreadClassFound(TThreadDiscoverConnection,nil)>=0 then begin
+    TLog.NewLog(ltInfo,ClassName,'Allready discovering servers...');
+    exit;
+  end;
+  CleanBlackList;
+  j := CT_MaxServersConnected - ConnectionsCount(true);
+  if j<=0 then exit;
+  // can discover up to j servers
+  l := TList.Create;
+  try
+    lns := FNodeServers.LockList;
+    try
+      for i:=0 to lns.Count-1 do begin
+        P := lns[i];
+        If (Not Assigned(P.netConnection)) AND (Not IsBlackListed(P^.ip,P^.port)) AND (Not P^.its_myself) And
+          ((P^.last_attempt_to_connect=0) Or ((P^.last_attempt_to_connect+EncodeTime(0,5,0,0)<now))) And
+          ((P^.total_failed_attemps_to_connect<3) Or (P^.last_attempt_to_connect+EncodeTime(2,0,0,0)<now)) then begin
+          l.Add(P);
+        end;
+      end;
+    Finally
+      FNodeServers.UnlockList;
+    end;
+    if l.Count<=0 then exit;
+    sw(l);
+    if j>=l.Count then j:=l.Count-1;
+    TLog.NewLog(ltDebug,Classname,'Start discovering up to '+inttostr(j+1)+' servers... (max:'+inttostr(l.count)+')');
+    //
+    for i := 0 to j do begin
+      FIsDiscoveringServers := true;
+      P := PNodeServerAddress(l[i]);
+      tdc := TThreadDiscoverConnection.Create(P^,DiscoverServersTerminated);
+    end;
+  finally
+    l.Free;
+  end;
+end;
+
+procedure TNetData.DiscoverServersTerminated(Sender: TObject);
+begin
+  if Assigned(FOnNodeServersUpdated) then FOnNodeServersUpdated(Self);
+  if TPCThread.ThreadClassFound(TThreadDiscoverConnection,Sender)>=0 then exit;
+  FIsDiscoveringServers := false;
+  // If here, discover servers finished, so we can try to get/receive data
+  TLog.NewLog(ltDebug,Classname,Format('Discovering servers finished. Now we have %d active connections and %d connections to other servers',
+    [ConnectionsCount(false),ConnectionsCount(true)]));
+  if TPCThread.ThreadClassFound(TThreadGetNewBlockChainFromClient,nil)>=0 then exit;
+  TThreadGetNewBlockChainFromClient.Create(false).FreeOnTerminate := true;
+end;
+
+class function TNetData.ExtractHeaderInfo(buffer : TStream; var HeaderData : TNetHeaderData; DataBuffer : TStream; var IsValidHeaderButNeedMoreData : Boolean) : Boolean;
+Var lastp : Integer;
+  c : Cardinal;
+  w : Word;
+begin
+  HeaderData := CT_NetHeaderData;
+  Result := false;
+  IsValidHeaderButNeedMoreData := false;
+  lastp := buffer.Position;
+  Try
+    if buffer.Size-buffer.Position < 22 then exit;
+    buffer.Read(c,4);
+    if (c<>CT_MagicNetIdentification) then exit;
+    buffer.Read(w,2);
+    case w of
+      CT_MagicRequest : HeaderData.header_type := ntp_request;
+      CT_MagicResponse : HeaderData.header_type := ntp_response;
+      CT_MagicAutoSend : HeaderData.header_type := ntp_autosend;
+    else
+      HeaderData.header_type := ntp_unknown;
+      exit;
+    end;
+    buffer.Read(HeaderData.operation,2);
+    buffer.Read(HeaderData.error_code,2);
+    buffer.Read(HeaderData.request_id,4);
+    buffer.Read(HeaderData.protocol.protocol_version,2);
+    buffer.Read(HeaderData.protocol.protocol_available,2);
+    buffer.Read(c,4);
+    DataBuffer.Size := 0;
+    if buffer.Size - buffer.Position < c then begin
+      IsValidHeaderButNeedMoreData := true;
+      exit;
+    end;
+    DataBuffer.CopyFrom(buffer,c);
+    DataBuffer.Position := 0;
+    HeaderData.buffer_data_length := c;
+    //
+    if HeaderData.header_type=ntp_response then begin
+      HeaderData.is_error := HeaderData.error_code<>0;
+      if HeaderData.is_error then begin
+        TStreamOp.ReadAnsiString(DataBuffer,HeaderData.error_text);
+      end;
+    end else begin
+      HeaderData.is_error := HeaderData.error_code<>0;
+      if HeaderData.is_error then begin
+        TStreamOp.ReadAnsiString(DataBuffer,HeaderData.error_text);
+      end;
+    end;
+    if (HeaderData.is_error) then begin
+      TLog.NewLog(lterror,Classname,'Response with error ('+IntToHex(HeaderData.error_code,4)+'): '+HeaderData.error_text+' ...on '+
+        'operation: '+OperationToText(HeaderData.operation)+' id: '+Inttostr(HeaderData.request_id));
+    end;
+    Result := true;
+  Finally
+    if Not Result then buffer.Position := lastp;
+  End;
+end;
+
+function TNetData.FindConnectionByClientRandomValue(Sender: TNetConnection): TNetConnection;
+Var l : TList;
+  i : Integer;
+begin
+  l := FNetConnections.LockList;
+  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;
+    end;
+  finally
+    FNetConnections.UnlockList;
+  end;
+  Result := Nil;
+end;
+
+procedure TNetData.GetNewBlockChainFromClient(Connection: TNetConnection);
+Const CT_LogSender = 'GetNewBlockChainFromClient';
+
+  function Do_GetOperationsBlock(AssignToBank : TPCBank; block_start,block_end : Cardinal; OnlyOperationBlock : Boolean; BlocksList : TList) : Boolean;
+  Var SendData,ReceiveData : TMemoryStream;
+    headerdata : TNetHeaderData;
+    op : TPCOperationsComp;
+    request_id,opcount,i : Cardinal;
+    errors : AnsiString;
+    noperation : Integer;
+  begin
+    Result := false;
+    BlocksList.Count := 0;
+    if (Connection.FRemoteOperationBlock.block<block_end) then block_end := Connection.FRemoteOperationBlock.block;
+    // First receive operations from
+    SendData := TMemoryStream.Create;
+    ReceiveData := TMemoryStream.Create;
+    try
+      if OnlyOperationBlock then begin
+        noperation := CT_NetOp_GetOperationsBlock;
+      end else begin
+        noperation := CT_NetOp_GetBlocks;
+      end;
+      TLog.NewLog(ltdebug,CT_LogSender,Format('Sending %s from block %d to %d (Total: %d)',
+        [TNetData.OperationToText(noperation),block_start,block_end,block_end-block_start+1]));
+      SendData.Write(block_start,4);
+      SendData.Write(block_end,4);
+      request_id := TNetData.NetData.NewRequestId;
+      if Connection.DoSendAndWaitForResponse(noperation,request_id,SendData,ReceiveData,5000,headerdata) then begin
+        if HeaderData.is_error then exit;
+        if ReceiveData.Read(opcount,4)<4 then exit; // Error in data
+        i := 0;
+        while (i<opcount) do begin
+          // decode data
+          op := TPCOperationsComp.Create(AssignToBank);
+          If op.LoadFromStream(false,false,ReceiveData,errors) then begin
+            BlocksList.Add(op);
+          end else begin
+            TLog.NewLog(lterror,CT_LogSender,Format('Error reading OperationBlock from received stream %d/%d: %s',[i+1,opcount,errors]));
+            op.free;
+            break;
+          end;
+          inc(i);
+        end;
+        Result := true;
+      end else begin
+        TLog.NewLog(lterror,CT_LogSender,Format('No received response after waiting request id %d operation %s',[request_id,TNetData.OperationToText(noperation)]));
+      end;
+    finally
+      SendData.Free;
+      ReceiveData.free;
+    end;
+  end;
+
+  function Do_GetOperationBlock(block : Cardinal; var OperationBlock : TOperationBlock) : Boolean;
+  Var BlocksList : TList;
+    i : Integer;
+  begin
+    OperationBlock := CT_OperationBlock_NUL;
+    BlocksList := TList.Create;
+    try
+      Result := Do_GetOperationsBlock(TNode.Node.Bank,block,block,false,BlocksList);
+      if (Result) And (BlocksList.Count=1) then begin
+        OperationBlock := TPCOperationsComp(BlocksList[0]).OperationBlock;
+      end;
+    finally
+      for i := 0 to BlocksList.Count - 1 do TPCOperationsComp(BlocksList[i]).Free;
+      BlocksList.Free;
+    end;
+  end;
+
+  Function FindLastSameBlockByOperationsBlock(min,max : Cardinal; var OperationBlock : TOperationBlock) : Boolean;
+  var i : Integer;
+    ant_nblock : Int64;
+    myops : TPCOperationsComp;
+    auxBlock : TOperationBlock;
+    distinctmax,distinctmin : Cardinal;
+    BlocksList : TList;
+  Begin
+    Result := false;
+    OperationBlock := CT_OperationBlock_NUL;
+    repeat
+      BlocksList := TList.Create;
+      try
+        If Not Do_GetOperationsBlock(Nil,min,max,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;
+          end;
+        finally
+          myops.Free;
+        end;
+        min := distinctmin;
+        max := distinctmax;
+      finally
+        for i := 0 to BlocksList.Count - 1 do begin
+          TPCOperationsComp(BlocksList[i]).Free;
+        end;
+        BlocksList.Free;
+      end;
+    until (distinctmin=distinctmax);
+    Result := (OperationBlock.proof_of_work <> CT_OperationBlock_NUL.proof_of_work);
+  End;
+
+  Function GetNewBank(start_block : Int64) : Boolean;
+  Var BlocksList : TList;
+    i : Integer;
+    tempfolder : AnsiString;
+    OpComp,OpExecute : TPCOperationsComp;
+    newBlock : TBlockAccount;
+    errors : AnsiString;
+    start : Cardinal;
+    finished : Boolean;
+    Bank : TPCBank;
+    ms : TMemoryStream;
+  Begin
+    TLog.NewLog(ltdebug,CT_LogSender,Format('GetNewBank(new_start_block:%d)',[start_block]));
+    Bank := TPCBank.Create(Nil);
+    try
+
+      Bank.StorageClass := TNode.Node.Bank.StorageClass;
+      Bank.Storage.Orphan := TNode.Node.Bank.Storage.Orphan;
+      Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
+      if start_block>=0 then begin
+        // Restore a part
+        Bank.DiskRestoreFromOperations(start_block);
+        start := start_block + 1;
+      end else begin
+        start := 0;
+        start_block := 0;
+      end;
+      Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+      // Receive new blocks:
+      finished := false;
+      repeat
+        BlocksList := TList.Create;
+        try
+          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,false,BlocksList);
+          i := 0;
+          while (i<BlocksList.Count) And (Not finished) do begin
+            OpComp := TPCOperationsComp(BlocksList[i]);
+            ms := TMemoryStream.Create;
+            OpExecute := TPCOperationsComp.Create(Bank);
+            try
+              OpComp.SaveToStream(false,false,ms);
+              ms.Position := 0;
+              OpExecute.LoadFromStream(false,false,ms,errors);
+              if Bank.AddNewBlockChainBlock(OpExecute,newBlock,errors) then begin
+                inc(i);
+              end else begin
+                TLog.NewLog(lterror,CT_LogSender,'Error creating new bank with client Operations. Block:'+TPCOperationsComp.OperationBlockToText(OpExecute.OperationBlock)+' Error:'+errors);
+                // Add to blacklist !
+                Connection.DisconnectInvalidClient(false,'Invalid BlockChain on Block '+TPCOperationsComp.OperationBlockToText(OpExecute.OperationBlock)+' with errors:'+errors);
+                finished := true;
+                break;
+              end;
+            finally
+              ms.Free;
+              OpExecute.Free;
+            end;
+          end;
+        finally
+          for i := 0 to BlocksList.Count - 1 do TPCOperationsComp(BlocksList[i]).Free;
+          BlocksList.Free;
+        end;
+        start := Bank.BlocksCount;
+      until (Bank.BlocksCount=Connection.FRemoteOperationBlock.block+1) Or (finished);
+      if Bank.BlocksCount>TNode.Node.Bank.BlocksCount then begin
+        // I'm an orphan blockchain...
+        TLog.NewLog(ltinfo,CT_LogSender,'New valid blockchain found. My block count='+inttostr(TNode.Node.Bank.BlocksCount)+
+          ' found='+inttostr(Bank.BlocksCount)+' starting at block '+inttostr(start_block));
+        TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,Inttostr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)));
+        Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan);
+        TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+      end;
+    finally
+      Bank.Free;
+    end;
+  End;
+
+var rid : Cardinal;
+  bufferdata : TMemoryStream;
+  headerdata : TNetHeaderData;
+  op : TOperationBlock;
+begin
+  // Protection against discovering servers...
+  if FIsDiscoveringServers then begin
+    TLog.NewLog(ltdebug,CT_LogSender,'Is discovering servers...');
+    exit;
+  end;
+  //
+  If FIsGettingNewBlockChainFromClient then begin
+    TLog.NewLog(ltdebug,CT_LogSender,'Is getting new blockchain from client...');
+    exit;
+  end else TLog.NewLog(ltdebug,CT_LogSender,'Starting receiving');
+  Try
+    FIsGettingNewBlockChainFromClient := true;
+    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);
+      exit;
+    end;
+    TLog.NewLog(ltdebug,CT_LogSender,'Starting GetNewBlockChainFromClient at client:'+Connection.FClient.RemoteHost+':'+Connection.FClient.RemotePort+
+      ' with OperationBlock:'+TPCOperationsComp.OperationBlockToText(Connection.FRemoteOperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock));
+    // NOTE: FRemoteOperationBlock.block >= TNode.Node.Bank.BlocksCount
+    // First capture same block than me (TNode.Node.Bank.BlocksCount-1) to check if i'm an orphan block...
+    If Not Do_GetOperationBlock(TNode.Node.Bank.BlocksCount-1,op) then begin
+      exit;
+    end;
+    if op.proof_of_work<>TNode.Node.Bank.LastOperationBlock.proof_of_work then begin
+      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is incorrect... received: '+TPCOperationsComp.OperationBlockToText(op)+' My: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock));
+      if Not FindLastSameBlockByOperationsBlock(0,op.block,op) then begin
+        TLog.NewLog(ltinfo,CT_LogSender,'No found base block to start process... Receiving ALL');
+        GetNewBank(-1);
+      end else begin
+        TLog.NewLog(ltinfo,CT_LogSender,'Found base new block: '+TPCOperationsComp.OperationBlockToText(op));
+        // Move operations to orphan folder... (temporal... waiting for a confirmation)
+        GetNewBank(op.block);
+      end;
+    end else begin
+      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is ok! Need to download new blocks starting at '+inttostr(TNode.Node.Bank.BlocksCount));
+      // High to new value:
+      Connection.Send_GetBlocks(TNode.Node.Bank.BlocksCount,0,rid);
+    end;
+  Finally
+    TLog.NewLog(ltdebug,CT_LogSender,'Finalizing');
+    FIsGettingNewBlockChainFromClient := false;
+  end;
+end;
+
+class function TNetData.HeaderDataToText(const HeaderData: TNetHeaderData): AnsiString;
+begin
+  Result := CT_NetTransferType[HeaderData.header_type]+' Operation:'+TNetData.OperationToText(HeaderData.operation);
+  if HeaderData.is_error then begin
+    Result := Result +' ERROR:'+Inttostr(HeaderData.error_code)+' ERROR TEXT:'+HeaderData.error_text;
+  end else begin
+    Result := Result +' ReqId:'+Inttostr(HeaderData.request_id)+' BufferSize:'+Inttostr(HeaderData.buffer_data_length);
+  end;
+end;
+
+procedure TNetData.IncStatistics(incActiveConnections, incClientsConnections,
+  incServersConnections: Integer; incBytesReceived, incBytesSend: Int64);
+begin
+  FNetStatistics.ActiveConnections := FNetStatistics.ActiveConnections + incActiveConnections;
+  FNetStatistics.ClientsConnections := FNetStatistics.ClientsConnections + incClientsConnections;
+  FNetStatistics.ServersConnections := FNetStatistics.ServersConnections + incServersConnections;
+  if (incActiveConnections>0) then FNetStatistics.TotalConnections := FNetStatistics.TotalConnections + incActiveConnections;
+  if (incClientsConnections>0) then FNetStatistics.TotalClientsConnections := FNetStatistics.TotalClientsConnections + incClientsConnections;
+  if (incServersConnections>0) then FNetStatistics.TotalServersConnections := FNetStatistics.TotalServersConnections + incServersConnections;
+  FNetStatistics.BytesReceived := FNetStatistics.BytesReceived + incBytesReceived;
+  FNetStatistics.BytesSend := FNetStatistics.BytesSend + incBytesSend;
+  if Assigned(FOnStatisticsChanged) then FOnStatisticsChanged(Self);
+  if (incBytesReceived<>0) Or (incBytesSend<>0) then begin
+    if Assigned(FOnNetConnectionsUpdated) then begin
+      FOnNetConnectionsUpdated(Self);
+    end;
+  end;
+end;
+
+function TNetData.IndexOfNetClient(ListToSearch: TList; ip: AnsiString; port: Word; indexStart : Integer = 0): Integer;
+Var P : PNodeServerAddress;
+begin
+  if indexStart<0 then indexStart:=0;
+  for Result := indexStart to ListToSearch.Count - 1 do begin
+    P := ListToSearch[Result];
+    if (AnsiSameText( P^.ip,ip)) And ((port=0) Or (P^.port=port)) then exit;
+  end;
+  Result := -1;
+end;
+
+function TNetData.IsBlackListed(const ip: AnsiString; port: Word): Boolean;
+Var l : TList;
+  i : Integer;
+begin
+  Result := false;
+  l := FBlackList.LockList;
+  Try
+    i := -1;
+    repeat
+      i := IndexOfNetClient(l,ip,port,i+1);
+      if (i>=0) then begin
+        Result := Not PNodeServerAddress(l[i])^.its_myself;
+      end;
+    until (i<0) Or (Result);
+  Finally
+    FBlackList.UnlockList;
+  End;
+end;
+
+class function TNetData.NetData: TNetData;
+begin
+  if Not Assigned(_NetData) then begin
+    _NetData := TNetData.Create(nil);
+  end;
+  result := _NetData;
+end;
+
+function TNetData.NewRequestId: Cardinal;
+begin
+  Inc(FLastRequestId);
+  Result := FLastRequestId;
+end;
+
+procedure TNetData.Notification(AComponent: TComponent; Operation: TOperation);
+Var l : TList;
+begin
+  inherited;
+  if Operation=OpRemove then begin
+    if not (csDestroying in ComponentState) then begin
+      l := FNetConnections.LockList;
+      try
+        if l.Remove(AComponent)>=0 then begin
+          if Assigned(FOnNetConnectionsUpdated) then FOnNetConnectionsUpdated(Self);
+        end;
+      finally
+        FNetConnections.UnlockList;
+      end;
+    end;
+  end;
+end;
+
+class function TNetData.OperationToText(operation: Word): AnsiString;
+begin
+  case operation of
+    CT_NetOp_Hello : Result := 'HELLO';
+    CT_NetOp_Error : Result := 'ERROR';
+    CT_NetOp_GetBlocks : Result := 'GET BLOCKS';
+    CT_NetOp_Message : Result := 'MESSAGE';
+    CT_NetOp_GetOperationsBlock : Result := 'GET OPERATIONS BLOCK';
+    CT_NetOp_NewBlock : Result := 'NEW BLOCK';
+    CT_NetOp_AddOperations : Result := 'ADD OPERATIONS';
+  else Result := 'UNKNOWN OPERATION '+Inttohex(operation,4);
+  end;
+end;
+
+function TNetData.PendingRequest(Sender: TNetConnection; var requests_data : AnsiString): Integer;
+Var P : PNetRequestRegistered;
+  i : Integer;
+  l : TList;
+begin
+  requests_data := '';
+  l := FRegisteredRequests.LockList;
+  Try
+    if Assigned(Sender) then begin
+      Result := 0;
+      for i := l.Count - 1 downto 0 do begin
+        if (PNetRequestRegistered(l[i])^.NetClient=Sender) then begin
+          requests_data := requests_data+'Op:'+OperationToText(PNetRequestRegistered(l[i])^.Operation)+' Id:'+Inttostr(PNetRequestRegistered(l[i])^.RequestId)+' - ';
+          inc(Result);
+        end;
+      end;
+    end else Result := l.Count;
+  Finally
+    FRegisteredRequests.UnlockList;
+  End;
+end;
+
+procedure TNetData.RegisterRequest(Sender: TNetConnection; operation: Word; request_id: Cardinal);
+Var P : PNetRequestRegistered;
+  l : TList;
+begin
+  l := FRegisteredRequests.LockList;
+  Try
+    New(P);
+    P^.NetClient := Sender;
+    P^.Operation := operation;
+    P^.RequestId := request_id;
+    P^.SendTime := Now;
+    l.Add(P);
+    TLog.NewLog(ltdebug,Classname,'Registering request to '+Sender.FClient.RemoteHost+':'+Sender.FClient.RemotePort+' Op:'+OperationToText(operation)+' Id:'+inttostr(request_id)+' Total pending:'+Inttostr(l.Count));
+  Finally
+    FRegisteredRequests.UnlockList;
+  End;
+end;
+
+function TNetData.UnRegisterRequest(Sender: TNetConnection; operation: Word; request_id: Cardinal): Boolean;
+Var P : PNetRequestRegistered;
+  i : Integer;
+  l : TList;
+begin
+  Result := false;
+  l := FRegisteredRequests.LockList;
+  try
+    for i := l.Count - 1 downto 0 do begin
+      P := l[i];
+      if (P^.NetClient=Sender) And
+        ( ((Operation=P^.Operation) And (request_id = P^.RequestId))
+          Or
+          ((operation=0) And (request_id=0)) ) then begin
+        l.Delete(i);
+        Dispose(P);
+        Result := true;
+        if Assigned(Sender.FClient) then begin
+          TLog.NewLog(ltdebug,Classname,'Unregistering request to '+Sender.FClient.RemoteHost+':'+Sender.FClient.RemotePort+' Op:'+OperationToText(operation)+' Id:'+inttostr(request_id)+' Total pending:'+Inttostr(l.Count));
+        end;
+        exit;
+      end;
+    end;
+  finally
+    FRegisteredRequests.UnlockList;
+  end;
+end;
+
+{ TNetServer }
+
+constructor TNetServer.Create(AOwner: TComponent);
+begin
+  inherited;
+  FNetClients := TList.Create;
+  FPort := CT_NetServer_Port;
+  FTCPServer := TTcpServer.Create(Self);
+  FTCPServer.LocalPort := Inttostr(CT_NetServer_Port);
+  FTCPServer.OnAccept := OnTcpServerAccept;
+  FTCPServer.OnGetThread := OnTcpServerGetThread;
+  FTCPServer.OnListening := OnTcpServerListening;
+  FTCPServer.OnCreateHandle := OnTcpServerCreateHandle;
+  FTCPServer.OnDestroyHandle := OnTcpServerDestroyHandle;
+end;
+
+destructor TNetServer.Destroy;
+begin
+  FTCPServer.Free;
+  FNetClients.Free;
+  inherited;
+end;
+
+function TNetServer.GetActive: Boolean;
+begin
+  Result := FTCPServer.Active;
+end;
+
+procedure TNetServer.Notification(AComponent: TComponent; Operation: TOperation);
+var i : Integer;
+begin
+  inherited;
+  if (Operation=opRemove) then begin
+    i := FNetClients.IndexOf(AComponent);
+    if (i>=0) then begin
+      FNetClients.Delete(i);
+      TLog.NewLog(ltdebug,ClassName,'TNetConnection destroyed. Remaining: '+Inttostr(FNetclients.Count));
+    end;
+  end;
+end;
+
+procedure TNetServer.OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
+Var n : TNetServerClient;
+begin
+  // NOTE: I'm in a separate thread
+  // While in this function the ClientSocket connection will be active, when finishes the ClientSocket will be destroyed
+  TLog.NewLog(ltInfo,Classname,'Starting ClientSocket accept '+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
+  n := TNetServerClient.Create(Self);
+  Try
+    n.SetClient(ClientSocket);
+    TNetData.NetData.CleanBlackList;
+    if (TNetData.NetData.IsBlackListed(ClientSocket.RemoteHost,0)) then begin
+      // Invalid!
+      TLog.NewLog(ltinfo,Classname,'Refusing Blacklist ip: '+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
+      n.SendError(ntp_autosend,CT_NetOp_Error, 0,CT_NetError_IPBlackListed,'Your IP is blacklisted:'+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
+      // Wait some time before close connection
+      sleep(5000);
+    end else begin
+      FNetClients.Add(n);
+      TNetData.NetData.IncStatistics(1,1,0,0,0);
+      while (n.Connected) And (FTCPServer.Active) do begin
+        n.DoProcessBuffer;
+        Sleep(10);
+      end;
+    end;
+  Finally
+    n.Free;
+  End;
+end;
+
+procedure TNetServer.OnTcpServerCreateHandle(Sender: TObject);
+begin
+//  TLog.NewLog(ltdebug,Classname,'ServerCreateHandle');
+end;
+
+procedure TNetServer.OnTcpServerDestroyHandle(Sender: TObject);
+begin
+//  TLog.NewLog(ltdebug,Classname,'ServerDestroyHandle');
+end;
+
+procedure TNetServer.OnTcpServerGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
+begin
+//  TLog.NewLog(ltdebug,Classname,'ClientSocket Get Thread');
+end;
+
+procedure TNetServer.OnTcpServerListening(Sender: TObject);
+begin
+  TLog.NewLog(ltinfo,Classname,'Server listening');
+end;
+
+procedure TNetServer.SetActive(const Value: Boolean);
+begin
+  if Value then begin
+    TLog.NewLog(ltinfo,Classname,'Activating server on port '+FTCPServer.LocalPort);
+  end else begin
+    TLog.NewLog(ltinfo,Classname,'Closing server');
+  end;
+  FTCPServer.Active := Value;
+  if FTCPServer.Active then begin
+    TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+  end else begin
+    TNetData.NetData.DisconnectClients;
+  end;
+end;
+
+procedure TNetServer.SetPort(const Value: Word);
+begin
+  if FPort=Value then exit;
+  Active := false;
+  FPort := Value;
+  FTCPServer.LocalPort := Inttostr(Value);
+end;
+
+{ TNetConnection }
+
+function TNetConnection.ConnectTo(ServerIP: String; ServerPort: Word) : Boolean;
+Var Pnsa : PNodeServerAddress;
+  lns : TList;
+  i : Integer;
+begin
+  if FClient.Connected then FClient.Disconnect;
+  lns := TNetData.NetData.FNodeServers.LockList;
+  try
+    i := TNetData.NetData.IndexOfNetClient(lns,ServerIp,ServerPort);
+    if (i>=0) then Pnsa := lns[i]
+    else Pnsa := Nil;
+  finally
+    TNetData.NetData.FNodeServers.UnlockList;
+  end;
+  if Assigned(Pnsa) then Pnsa^.netConnection := Self;
+
+  FClient.RemoteHost := ServerIP;
+  if ServerPort<=0 then ServerPort := CT_NetServer_Port;
+  FClient.RemotePort := Inttostr(ServerPort);
+  TLog.NewLog(ltDebug,Classname,'Trying to connect to a server at: '+FClient.RemoteHost+':'+FClient.RemotePort);
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+  Result := FClient.Connect;
+  if Result then begin
+    TLog.NewLog(ltDebug,Classname,'Connected to a possible server at: '+FClient.RemoteHost+':'+FClient.RemotePort);
+    Result := Send_Hello(ntp_request,TNetData.NetData.NewRequestId);
+  end else begin
+    TLog.NewLog(ltDebug,Classname,'Cannot connect to a server at: '+FClient.RemoteHost+':'+FClient.RemotePort);
+  end;
+end;
+
+constructor TNetConnection.Create(AOwner: TComponent);
+begin
+  inherited;
+  FClientPublicKey := CT_TECDSA_Public_Nul;
+  FIsMyselfServer := false;
+  FLastKnownTimestampDiff := 0;
+  FIsWaitingForResponse := false;
+  FClientBufferRead := TMemoryStream.Create;
+  InitializeCriticalSection(FNetLock);
+  FLastDataReceivedTS := 0;
+  FLastDataSendedTS := 0;
+  FClient := Nil;
+  SetClient( TTcpClient.Create(Self) );
+  FRemoteOperationBlock := CT_OperationBlock_NUL;
+  FSocketError := 0;
+  TNetData.NetData.FNetConnections.Add(Self);
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+end;
+
+destructor TNetConnection.Destroy;
+Var Pnsa : PNodeServerAddress;
+  lns : TList;
+  i : Integer;
+begin
+  Connected := false;
+
+  lns := TNetData.NetData.FNodeServers.LockList;
+  try
+    for i := 0 to lns.Count - 1 do begin
+      Pnsa := lns[i];
+      if Pnsa^.netConnection=Self then Pnsa^.netConnection := Nil;
+    end;
+  finally
+    TNetData.NetData.FNodeServers.UnlockList;
+  end;
+  TNetData.NetData.UnRegisterRequest(Self,0,0);
+  TNetData.NetData.FNetConnections.Remove(Self);
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+  //
+  DeleteCriticalSection(FNetLock);
+  if FClient.Owner=Self then FClient.Free;
+  FClientBufferRead.Free;
+  inherited;
+end;
+
+procedure TNetConnection.DisconnectInvalidClient(ItsMyself : Boolean; const why: AnsiString);
+Var P : PNodeServerAddress;
+  l : TList;
+  i : Integer;
+begin
+  if ItsMyself then begin
+    TLog.NewLog(ltInfo,Classname,'Disconecting myself '+FClient.RemoteHost+':'+FClient.RemotePort+' > '+Why)
+  end else begin
+    TLog.NewLog(lterror,Classname,'Disconecting '+FClient.RemoteHost+':'+FClient.RemotePort+' > '+Why);
+  end;
+  FIsMyselfServer := ItsMyself;
+  l := TNetData.NetData.FBlackList.LockList;
+  try
+    i := TNetData.NetData.IndexOfNetClient(l,FClient.RemoteHost,StrToIntDef( FClient.RemotePort,CT_NetServer_Port));
+    if i<0 then begin
+      new(P);
+      P^ := CT_TNodeServerAddress_NUL;
+      TNetData.NetData.FBlackList.add(P);
+    end else P := l[i];
+    P^.ip := Fclient.RemoteHost;
+    P^.port := StrToIntDef( FClient.RemotePort,CT_NetServer_Port);
+    P^.last_connection := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    P^.its_myself := ItsMyself;
+    P^.BlackListText := Why;
+  finally
+    TNetData.NetData.FBlackList.UnlockList;
+  end;
+  if ItsMyself then begin
+    l := TNetData.NetData.FNodeServers.LockList;
+    try
+      i := TNetData.NetData.IndexOfNetClient(l,FClient.RemoteHost,StrToIntDef( FClient.RemotePort,CT_NetServer_Port));
+      if i>=0 then begin
+        P := l[i];
+        P^.its_myself := true;
+      end;
+    finally
+      TNetData.NetData.FNodeServers.UnlockList;
+    end;
+  end;
+  Connected := False;
+  if Assigned(TNetData.NetData.FOnBlackListUpdated) then TNetData.NetData.FOnBlackListUpdated(Self);
+  if Assigned(TNetData.NetData.FOnNodeServersUpdated) then TNetData.NetData.FOnNodeServersUpdated(Self);
+end;
+
+Procedure TNetConnection.DoProcessBuffer;
+Var HeaderData : TNetHeaderData;
+  ms : TMemoryStream;
+  ops : AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  try
+    TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+    Try
+      if Not FIsWaitingForResponse then begin
+        DoSendAndWaitForResponse(0,0,Nil,ms,0,HeaderData);
+      end;
+    Finally
+      LeaveCriticalSection(FNetLock);
+    End;
+  finally
+    ms.Free;
+  end;
+  if ((FLastDataReceivedTS+(1000*60)<GetTickCount) Or (FLastDataSendedTS+(1000*60)<GetTickCount)) then begin
+    If TNetData.NetData.PendingRequest(Self,ops)>=2 then begin
+      TLog.NewLog(lterror,Classname,'Pending requests without response... closing connection to '+FClient.RemoteHost+':'+FClient.RemotePort+' > '+ops);
+      Connected := false;
+    end else begin
+      TLog.NewLog(ltDebug,Classname,'Sending Hello to check connection to '+FClient.RemoteHost+':'+FClient.RemotePort+' > '+ops);
+      Send_Hello(ntp_request,TNetData.NetData.NewRequestId);
+    end;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_AddOperations(HeaderData: TNetHeaderData; DataBuffer: TStream);
+var c,i : Integer;
+    optype : Byte;
+    opclass : TPCOperationClass;
+    op : TPCOperation;
+    operations : TOperationsHashTree;
+    errors : AnsiString;
+  DoDisconnect : Boolean;
+begin
+  DoDisconnect := true;
+  operations := TOperationsHashTree.Create;
+  try
+    if HeaderData.header_type<>ntp_autosend then begin
+      errors := 'Not autosend';
+      exit;
+    end;
+    if DataBuffer.Size<4 then begin
+      errors := 'Invalid databuffer size';
+      exit;
+    end;
+    DataBuffer.Read(c,4);
+    for i := 1 to c do begin
+      errors := 'Invalid operation '+inttostr(i)+'/'+inttostr(c);
+      if not DataBuffer.Read(optype,1)=1 then exit;
+      opclass := TPCOperationsComp.GetOperationClassByOpType(optype);
+      if Not Assigned(opclass) then exit;
+      op := opclass.Create;
+      op.LoadFromStream(DataBuffer);
+      operations.AddOperationToHashTree(op);
+    end;
+    DoDisconnect := false;
+  finally
+    try
+      if DoDisconnect then begin
+        DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+      end else begin
+        TNode.Node.AddOperations(Self,operations,errors);
+      end;
+    finally
+      operations.Free;
+    end;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_GetBlocks_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
+Var b,b_start,b_end:Cardinal;
+    op : TPCOperationsComp;
+    db : TMemoryStream;
+    c : Cardinal;
+  errors : AnsiString;
+  DoDisconnect : Boolean;
+begin
+  DoDisconnect := true;
+  try
+    if HeaderData.header_type<>ntp_request then begin
+      errors := 'Not request';
+      exit;
+    end;
+     // DataBuffer contains: from and to
+     errors := 'Invalid structure';
+     if (DataBuffer.Size-DataBuffer.Position<8) then begin
+       exit;
+     end;
+     DataBuffer.Read(b_start,4);
+     DataBuffer.Read(b_end,4);
+     if (b_start<0) Or (b_start>b_end) then begin
+       errors := 'Invalid structure start or end: '+Inttostr(b_start)+' '+Inttostr(b_end);
+       exit;
+     end;
+     if (b_end>=TNetData.NetData.Bank.BlocksCount) then b_end := TNetData.NetData.Bank.BlocksCount-1;
+
+     DoDisconnect := false;
+
+     db := TMemoryStream.Create;
+     try
+       op := TPCOperationsComp.Create(TNetData.NetData.bank);
+       try
+         c := b_end - b_start + 1;
+         db.Write(c,4);
+         for b := b_start to b_end do begin
+           If TNetData.NetData.bank.LoadOperations(op,b) then begin
+             op.SaveToStream(false,false,db);
+           end else begin
+             SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,'Operations of block:'+inttostr(b)+' not found');
+             exit;
+           end;
+         end;
+         Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
+       finally
+         op.Free;
+       end;
+     finally
+       db.Free;
+     end;
+     TLog.NewLog(ltdebug,Classname,'Sending operations from block '+inttostr(b_start)+' to '+inttostr(b_end));
+  finally
+    if DoDisconnect then begin
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+    end;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_GetBlocks_Response(HeaderData: TNetHeaderData; DataBuffer: TStream);
+  var op, localop : TPCOperationsComp;
+    opcount,i : Cardinal;
+    newBlockAccount : TBlockAccount;
+  errors : AnsiString;
+  DoDisconnect : Boolean;
+begin
+  DoDisconnect := true;
+  try
+    if HeaderData.header_type<>ntp_response then begin
+      errors := 'Not response';
+      exit;
+    end;
+    // DataBuffer contains: from and to
+    errors := 'Invalid structure';
+    op := TPCOperationsComp.Create(Self);
+    Try
+      op.bank := TNode.Node.Bank;
+      if DataBuffer.Size-DataBuffer.Position<4 then begin
+        DisconnectInvalidClient(false,'DoProcess_GetBlocks_Response invalid format: '+errors);
+        exit;
+      end;
+      DataBuffer.Read(opcount,4);
+      DoDisconnect :=false;
+      for I := 1 to opcount do begin
+        if Not op.LoadFromStream(false,false,DataBuffer,errors) then begin
+           errors := 'Error decoding block '+inttostr(i)+'/'+inttostr(opcount)+' Errors:'+errors;
+           DoDisconnect := true;
+           exit;
+        end;
+        if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+          if (TNode.Node.Bank.AddNewBlockChainBlock(op,newBlockAccount,errors)) then begin
+            // Ok, one more!
+          end else begin
+            // Is not a valid entry????
+            // Perhaps an orphan blockchain: Me or Client!
+            localop := TPCOperationsComp.Create(Self);
+            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;
+          end;
+        end else begin
+          // Receiving an unexpected operationblock
+          TLog.NewLog(lterror,classname,'ReceivedGetOperations an unexpected operationblock: '+TPCOperationsComp.OperationBlockToText(op.OperationBlock));
+          exit;
+        end;
+      end;
+      if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
+        Send_GetBlocks(TNode.Node.Bank.BlocksCount,5,i);
+      end;
+    Finally
+      op.Free;
+    End;
+  Finally
+    if DoDisconnect then begin
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+    end;
+  end;
+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;
+begin
+  blocksstr := '';
+  DoDisconnect := true;
+  try
+    if HeaderData.header_type<>ntp_request then begin
+      errors := 'Not request';
+      exit;
+    end;
+    errors := 'Invalid structure';
+    if (DataBuffer.Size-DataBuffer.Position<8) then begin
+       exit;
+    end;
+    DataBuffer.Read(b_start,4);
+    DataBuffer.Read(b_end,4);
+    if (b_start<0) Or (b_start>b_end) Or (b_start>=TNode.Node.Bank.BlocksCount) then begin
+      errors := 'Invalid start ('+Inttostr(b_start)+') or end ('+Inttostr(b_end)+') of count ('+Inttostr(TNode.Node.Bank.BlocksCount)+')';
+      exit;
+    end;
+
+    DoDisconnect := false;
+
+    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.SaveToStream(false,true,msops);
+           blocksstr := blocksstr + inttostr(b)+',';
+           b := b + inc_b;
+           inc(total_b);
+         end else begin
+           SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,'Operations of block:'+inttostr(b)+' not found');
+           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);
+  finally
+    if DoDisconnect then begin
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+    end;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_Hello(HeaderData: TNetHeaderData; DataBuffer: TStream);
+var op, myLastOp : TPCOperationsComp;
+    errors : AnsiString;
+    messagehello : TNetMessage_Hello;
+    connection_has_a_server : Word;
+    i,c : Integer;
+    nsa : TNodeServerAddress;
+    rid : Cardinal;
+    connection_ts : Cardinal;
+   Duplicate : TNetConnection;
+   RawAccountKey : TRawBytes;
+Begin
+  SetLength(messagehello.servers_address,0);
+  op := TPCOperationsComp.Create(Nil);
+  try
+    DataBuffer.Position:=0;
+    if DataBuffer.Read(connection_has_a_server,2)<2 then begin
+      DisconnectInvalidClient(false,'Invalid data on buffer: '+TNetData.HeaderDataToText(HeaderData));
+      exit;
+    end;
+    If TStreamOp.ReadAnsiString(DataBuffer,RawAccountKey)<0 then begin
+      DisconnectInvalidClient(false,'Invalid data on buffer. No Public key: '+TNetData.HeaderDataToText(HeaderData));
+      exit;
+    end;
+    FClientPublicKey := TAccountComp.RawString2Accountkey(RawAccountKey);
+    If Not TAccountComp.IsValidAccountKey(FClientPublicKey,errors) then begin
+      DisconnectInvalidClient(false,'Invalid Public key: '+TNetData.HeaderDataToText(HeaderData)+' errors: '+errors);
+      exit;
+    end;
+    if DataBuffer.Read(connection_ts,4)<4 then begin
+      DisconnectInvalidClient(false,'Invalid data on buffer. No TS: '+TNetData.HeaderDataToText(HeaderData));
+      exit;
+    end;
+    FLastKnownTimestampDiff := Int64(connection_ts) - Int64(UnivDateTimeToUnix( DateTime2UnivDateTime(now)));
+    if (FLastKnownTimestampDiff<>0) then begin
+      TLog.NewLog(ltdebug,Classname,'Processing a hello from a client with different time. Difference: '+Inttostr(FLastKnownTimestampDiff));
+    end;
+    If (connection_ts > (UnivDateTimeToUnix(DateTime2UnivDateTime(now))+CT_MaxSecondsDifferenceOfNetworkNodes)) then begin
+      DisconnectInvalidClient(false,'Invalid remote timestamp. Difference:'+inttostr(FLastKnownTimestampDiff)+' > '+inttostr(CT_MaxSecondsDifferenceOfNetworkNodes));
+    end;
+    if (connection_has_a_server>0) And (Not SameText(FClient.RemoteHost,'localhost')) And (Not SameText(FClient.RemoteHost,'127.0.0.1'))
+      And (Not SameText('192.168',Copy(FClient.RemoteHost,1,7)))
+      And (Not TAccountComp.Equal(FClientPublicKey,TNetData.NetData.FNodePrivateKey.PublicKey)) then begin
+      nsa := CT_TNodeServerAddress_NUL;
+      nsa.ip := FClient.RemoteHost;
+      nsa.port := connection_has_a_server;
+      nsa.last_connection := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+      TNetData.NetData.AddServer(nsa);
+    end;
+
+    if op.LoadFromStream(false,false,DataBuffer,errors) then begin
+      messagehello.last_operation := op.OperationBlock;
+      FRemoteOperationBlock := op.OperationBlock;
+      If (TNetData.NetData.FMaxRemoteOperationBlock.block<FRemoteOperationBlock.block) then begin
+        TNetData.NetData.FMaxRemoteOperationBlock := FRemoteOperationBlock;
+        if TPCThread.ThreadClassFound(TThreadGetNewBlockChainFromClient,nil)<0 then begin
+          TThreadGetNewBlockChainFromClient.Create(false).FreeOnTerminate := true;
+        end;
+      end;
+      if (DataBuffer.Size-DataBuffer.Position>=4) then begin
+        DataBuffer.Read(c,4);
+        for i := 1 to c do begin
+          nsa := CT_TNodeServerAddress_NUL;
+          TStreamOp.ReadAnsiString(DataBuffer,nsa.ip);
+          DataBuffer.Read(nsa.port,2);
+          DataBuffer.Read(nsa.last_connection,4);
+          SetLength(messagehello.servers_address,length(messagehello.servers_address)+1);
+          messagehello.servers_address[high(messagehello.servers_address)] := nsa;
+          TNetData.NetData.AddServer(nsa);
+        end;
+      end;
+      TLog.NewLog(ltdebug,Classname,'Hello received: '+TPCOperationsComp.OperationBlockToText(FRemoteOperationBlock));
+      if (HeaderData.header_type in [ntp_request,ntp_response]) then begin
+        // Response:
+        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
+          DisconnectInvalidClient(true,'MySelf disconnecting...');
+          exit;
+        end;
+        Duplicate := TNetData.NetData.FindConnectionByClientRandomValue(Self);
+        if (Duplicate<>Nil) And (Duplicate.Connected) then begin
+          DisconnectInvalidClient(true,'Duplicate connection with '+Duplicate.FClient.RemoteHost+':'+Duplicate.FClient.RemotePort);
+          exit;
+        end;
+
+        if (HeaderData.header_type = ntp_response) then begin
+          if Assigned(TNetData.NetData.OnReceivedHelloResponse) then TNetData.NetData.OnReceivedHelloResponse(Self);
+        end;
+      end else begin
+        DisconnectInvalidClient(false,'Invalid header type > '+TNetData.HeaderDataToText(HeaderData));
+      end;
+    end else begin
+      TLog.NewLog(lterror,Classname,'Error decoding operations of HELLO: '+errors);
+      DisconnectInvalidClient(false,'Error decoding operations of HELLO: '+errors);
+    end;
+  finally
+    op.Free;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_Message(HeaderData: TNetHeaderData; DataBuffer: TStream);
+Var   errors : AnsiString;
+  decrypted,messagecrypted : AnsiString;
+  DoDisconnect : boolean;
+begin
+  errors := '';
+  DoDisconnect := true;
+  try
+    if HeaderData.header_type<>ntp_autosend then begin
+      errors := 'Not autosend';
+      exit;
+    end;
+    If TStreamOp.ReadAnsiString(DataBuffer,messagecrypted)<0 then begin
+      errors := 'Invalid message data';
+      exit;
+    end;
+    If Not ECIESDecrypt(TNetData.NetData.FNodePrivateKey.EC_OpenSSL_NID,TNetData.NetData.FNodePrivateKey.PrivateKey,false,messagecrypted,decrypted) then begin
+      errors := 'Error on decrypting message';
+      exit;
+    end;
+
+    DoDisconnect := false;
+    if TCrypto.IsHumanReadable(decrypted) then
+      TLog.NewLog(ltinfo,Classname,'Received new message from '+FClient.RemoteHost+':'+Fclient.RemotePort+' Message ('+inttostr(length(decrypted))+' bytes): '+decrypted)
+    else
+      TLog.NewLog(ltinfo,Classname,'Received new message from '+FClient.RemoteHost+':'+Fclient.RemotePort+' Message ('+inttostr(length(decrypted))+' bytes) in hexadecimal: '+TCrypto.ToHexaString(decrypted));
+    Try
+      TNode.Node.NotifyNetClientMessage(Self,decrypted);
+    Except
+      On E:Exception do begin
+        TLog.NewLog(lterror,Classname,'Error processing received message. '+E.ClassName+' '+E.Message);
+      end;
+    end;
+  finally
+    if DoDisconnect then begin
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+    end;
+  end;
+end;
+
+procedure TNetConnection.DoProcess_NewBlock(HeaderData: TNetHeaderData; DataBuffer: TStream);
+var bacc : TBlockAccount;
+    op : TPCOperationsComp;
+  errors : AnsiString;
+  DoDisconnect : Boolean;
+begin
+  errors := '';
+  DoDisconnect := true;
+  try
+    if HeaderData.header_type<>ntp_autosend then begin
+      errors := 'Not autosend';
+      exit;
+    end;
+    op := TPCOperationsComp.Create(Self);
+    try
+      op.bank := TNode.Node.Bank;
+      if Not op.LoadFromStream(false,false,DataBuffer,errors) then begin
+        errors := 'Error decoding new account: '+errors;
+        exit;
+      end else begin
+        DoDisconnect := false;
+        FRemoteOperationBlock := op.OperationBlock;
+        //
+        if (op.OperationBlock.block>TNode.Node.Bank.BlocksCount) then begin
+          TNetData.NetData.GetNewBlockChainFromClient(Self);
+        end else if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+          // New block candidate:
+          If Not TNode.Node.AddNewBlockChain(nil,Self,op,bacc,errors) then begin
+            // Received a new invalid block... perhaps I'm an orphan blockchain
+            TNetData.NetData.GetNewBlockChainFromClient(Self);
+          end;
+        end;
+      end;
+    finally
+      op.Free;
+    end;
+  finally
+    if DoDisconnect then begin
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+    end;
+  end;
+end;
+
+function TNetConnection.DoSendAndWaitForResponse(operation: Word;
+  RequestId: Integer; SendDataBuffer, ReceiveDataBuffer: TStream;
+  MaxWaitTime: Cardinal; var HeaderData: TNetHeaderData): Boolean;
+var tc : Cardinal;
+  was_waiting_for_response : Boolean;
+begin
+  Result := false;
+  HeaderData := CT_NetHeaderData;
+  If FIsWaitingForResponse then begin
+    TLog.NewLog(ltdebug,Classname,'Is waiting for response ...');
+    exit;
+  end;
+  If Not Assigned(FClient) then exit;
+  if Not FClient.Active then exit;
+  TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+  Try
+    was_waiting_for_response := RequestId>0;
+    try
+      if was_waiting_for_response then begin
+        FIsWaitingForResponse := true;
+        Send(ntp_request,operation,0,RequestId,SendDataBuffer);
+      end;
+      FSocketError := 0;
+      tc := GetTickCount;
+      Repeat
+        if Not FClient.WaitForData(100) then begin
+          If FSocketError<>0 then begin
+            TLog.NewLog(ltdebug,classname,'Broken connection by error '+Inttostr(FSocketError)+' to '+FClient.RemoteHost+':'+FClient.RemotePort);
+            Connected := false;
+            exit;
+          end;
+          if (GetTickCount-tc < 50) then begin
+            // Broken!
+            TLog.NewLog(ltdebug,classname,'Broken connection to '+FClient.RemoteHost+':'+FClient.RemotePort);
+            Connected := false;
+            exit;
+          end;
+          if (FClientBufferRead.Size=0) And (RequestId=0) then exit; // Nothing to read nor wait
+        end;
+        if (ReadTcpClientBuffer(MaxWaitTime,HeaderData,ReceiveDataBuffer)) then begin
+          TLog.NewLog(ltDebug,Classname,'Received '+CT_NetTransferType[HeaderData.header_type]+' operation:'+TNetData.OperationToText(HeaderData.operation)+' id:'+Inttostr(HeaderData.request_id)+' Buffer size:'+Inttostr(HeaderData.buffer_data_length) );
+          if (RequestId=HeaderData.request_id) And (HeaderData.header_type=ntp_response) then begin
+            Result := true;
+          end else begin
+            case HeaderData.operation of
+              CT_NetOp_Hello : Begin
+                DoProcess_Hello(HeaderData,ReceiveDataBuffer);
+              End;
+              CT_NetOp_Message : Begin
+                DoProcess_Message(HeaderData,ReceiveDataBuffer);
+              End;
+              CT_NetOp_GetBlocks : Begin
+                if HeaderData.header_type=ntp_request then
+                  DoProcess_GetBlocks_Request(HeaderData,ReceiveDataBuffer)
+                else if HeaderData.header_type=ntp_response then
+                  DoProcess_GetBlocks_Response(HeaderData,ReceiveDataBuffer)
+                else DisconnectInvalidClient(false,'Not resquest or response: '+TNetData.HeaderDataToText(HeaderData));
+              End;
+              CT_NetOp_GetOperationsBlock : Begin
+                if HeaderData.header_type=ntp_request then
+                  DoProcess_GetOperationsBlock_Request(HeaderData,ReceiveDataBuffer)
+                else TLog.NewLog(ltdebug,Classname,'Received old response of: '+TNetData.HeaderDataToText(HeaderData));
+              End;
+              CT_NetOp_NewBlock : Begin
+                DoProcess_NewBlock(HeaderData,ReceiveDataBuffer);
+              End;
+              CT_NetOp_AddOperations : Begin
+                DoProcess_AddOperations(HeaderData,ReceiveDataBuffer);
+              End;
+            else
+              DisconnectInvalidClient(false,'Invalid operation: '+TNetData.HeaderDataToText(HeaderData));
+            end;
+          end;
+        end;
+      Until (Result) Or (GetTickCount>(MaxWaitTime+tc));
+    finally
+      if was_waiting_for_response then FIsWaitingForResponse := false;
+    end;
+  Finally
+    LeaveCriticalSection(FNetLock);
+  End;
+end;
+
+function TNetConnection.GetConnected: Boolean;
+begin
+  Result := Assigned(FClient) And (FClient.Connected);
+end;
+
+procedure TNetConnection.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if (Operation=opRemove) And (AComponent = FClient) then FClient := Nil;
+end;
+
+function TNetConnection.ReadTcpClientBuffer(MaxWaitMiliseconds: Cardinal; var HeaderData: TNetHeaderData; BufferData: TStream): Boolean;
+var buffer : Array[1..4096] of byte;
+  auxstream : TMemoryStream;
+  tc : Cardinal;
+  last_bytes_read : Integer;
+  //
+  operation : Word;
+  request_id : Integer;
+  IsValidHeaderButNeedMoreData : Boolean;
+begin
+  Result := false;
+  HeaderData := CT_NetHeaderData;
+  BufferData.Size := 0;
+  TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+  try
+    If not Connected then exit;
+    if Not FClient.Active then exit;
+    tc := GetTickCount;
+    repeat
+      last_bytes_read := 0;
+      FClientBufferRead.Position := 0;
+      Result := TNetData.ExtractHeaderInfo(FClientBufferRead,HeaderData,BufferData,IsValidHeaderButNeedMoreData);
+      if Result then begin
+        if HeaderData.protocol.protocol_version<>CT_Protocol_Version then begin
+          DisconnectInvalidClient(false,Format('Invalid protocol version found: %d available: %d',[HeaderData.protocol.protocol_version,HeaderData.protocol.protocol_available]));
+          Result := false;
+          exit;
+        end else begin
+          auxstream := TMemoryStream.Create;
+          try
+            if FClientBufferRead.Position<FClientBufferRead.Size then begin
+              auxstream.CopyFrom(FClientBufferRead,FClientBufferRead.Size-FClientBufferRead.Position);
+            end;
+            FClientBufferRead.Size := 0;
+            FClientBufferRead.CopyFrom(auxstream,0);
+          finally
+            auxstream.Free;
+          end;
+        end;
+      end else begin
+        if Not FClient.WaitForData(100) then begin
+          exit;
+        end;
+        last_bytes_read := FClient.ReceiveBuf(buffer,sizeof(buffer));
+        if last_bytes_read>0 then begin
+          FLastDataReceivedTS := GetTickCount;
+          TNetData.NetData.IncStatistics(0,0,0,last_bytes_read,0);
+        end;
+        FClientBufferRead.Position := FClientBufferRead.size; // Go to the end
+        FClientBufferRead.Write(buffer,last_bytes_read);
+        FClientBufferRead.Position := 0;
+      end;
+    until (Result) Or ((GetTickCount > (tc+MaxWaitMiliseconds)) And (last_bytes_read=0));
+  finally
+    if (Not Result) And (FClientBufferRead.Size>0) And (Not IsValidHeaderButNeedMoreData) then begin
+      TLog.NewLog(lterror,ClassName,Format('Deleting %d bytes from TcpClient buffer of %s:%s after max %d miliseconds. Passed: %d',
+        [FClientBufferRead.Size, FClient.RemoteHost,FClient.RemotePort,MaxWaitMiliseconds,GetTickCount-tc]));
+      FClientBufferRead.Size:=0;
+    end;
+    LeaveCriticalSection(FNetLock);
+  end;
+  if (Result) And (HeaderData.header_type=ntp_response) then begin
+    TNetData.NetData.UnRegisterRequest(Self,HeaderData.operation,HeaderData.request_id);
+  end;
+end;
+
+procedure TNetConnection.Send(NetTranferType: TNetTransferType; operation, errorcode: Word; request_id: Integer; DataBuffer: TStream);
+Var l : Cardinal;
+   w : Word;
+  Buffer : TStream;
+  s : AnsiString;
+begin
+  Buffer := TMemoryStream.Create;
+  try
+    l := CT_MagicNetIdentification;
+    Buffer.Write(l,4);
+    case NetTranferType of
+      ntp_request: begin
+        w := CT_MagicRequest;
+        Buffer.Write(w,2);
+        Buffer.Write(operation,2);
+        w := 0;
+        Buffer.Write(w,2);
+        Buffer.Write(request_id,4);
+      end;
+      ntp_response: begin
+        w := CT_MagicResponse;
+        Buffer.Write(w,2);
+        Buffer.Write(operation,2);
+        Buffer.Write(errorcode,2);
+        Buffer.Write(request_id,4);
+      end;
+      ntp_autosend: begin
+        w := CT_MagicAutoSend;
+        Buffer.Write(w,2);
+        Buffer.Write(operation,2);
+        w := errorcode;
+        Buffer.Write(w,2);
+        l := 0;
+        Buffer.Write(l,4);
+      end
+    else
+      raise Exception.Create('Invalid encoding');
+    end;
+    l := CT_Protocol_Version;
+    Buffer.Write(l,2);
+    l := CT_Protocol_Available;
+    Buffer.Write(l,2);
+    if Assigned(DataBuffer) then begin
+      l := DataBuffer.Size;
+      Buffer.Write(l,4);
+      DataBuffer.Position := 0;
+      Buffer.CopyFrom(DataBuffer,DataBuffer.Size);
+      s := '(Data:'+inttostr(DataBuffer.Size)+'b) ';
+    end else begin
+      l := 0;
+      Buffer.Write(l,4);
+      s := '';
+    end;
+    Buffer.Position := 0;
+    TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
+    Try
+      TLog.NewLog(ltDebug,Classname,'Sending: '+CT_NetTransferType[NetTranferType]+' operation:'+
+        TNetData.OperationToText(operation)+' id:'+Inttostr(request_id)+' errorcode:'+InttoStr(errorcode)+
+        ' Size:'+InttoStr(Buffer.Size)+'b '+s+'to '+
+        FClient.RemoteHost+':'+FClient.RemotePort);
+      FClient.SendStream(Buffer);
+      FLastDataSendedTS := GetTickCount;
+      TNetData.NetData.IncStatistics(0,0,0,0,Buffer.Size);
+    Finally
+      LeaveCriticalSection(FNetLock);
+    End;
+  finally
+    Buffer.Free;
+  end;
+end;
+
+procedure TNetConnection.SendError(NetTranferType: TNetTransferType; operation, request_id, error_code: Integer; error_text: AnsiString);
+var buffer : TStream;
+begin
+  buffer := TMemoryStream.Create;
+  Try
+    TStreamOp.WriteAnsiString(buffer,error_text);
+    Send(NetTranferType,operation,error_code,request_id,buffer);
+  Finally
+    buffer.Free;
+  End;
+end;
+
+function TNetConnection.Send_AddOperations(Operations : TOperationsHashTree) : Boolean;
+Var data : TMemoryStream;
+  c1,c2,request_id : Cardinal;
+  i : Integer;
+  optype : Byte;
+begin
+  Result := false;
+  data := TMemoryStream.Create;
+  try
+    request_id := TNetData.NetData.NewRequestId;
+    c1 := Operations.OperationsCount;
+    data.Write(c1,4);
+    for i := 0 to Operations.OperationsCount-1 do begin
+      optype := Operations.GetOperation(i).OpType;
+      data.Write(optype,1);
+      Operations.GetOperation(i).SaveToStream(data);
+    end;
+    Send(ntp_autosend,CT_NetOp_AddOperations,0,request_id,data);
+    Result := FClient.Active;
+  finally
+    data.Free;
+  end;
+end;
+
+function TNetConnection.Send_GetBlocks(StartAddress, quantity : Cardinal; var request_id : Cardinal) : Boolean;
+Var data : TMemoryStream;
+  c1,c2 : Cardinal;
+begin
+  Result := false;
+  request_id := 0;
+  if (FRemoteOperationBlock.block<TNetData.NetData.Bank.BlocksCount) Or (FRemoteOperationBlock.block=0) then exit;
+  // First receive operations from
+  data := TMemoryStream.Create;
+  try
+    if TNetData.NetData.Bank.BlocksCount=0 then c1:=0
+    else c1:=StartAddress;
+    if (quantity=0) then begin
+      if FRemoteOperationBlock.block>0 then c2 := FRemoteOperationBlock.block
+      else c2 := c1+100;
+    end else c2 := c1+quantity-1;
+    if (FRemoteOperationBlock.block>0) And (c2>FRemoteOperationBlock.block) then c2 := FRemoteOperationBlock.block;
+    data.Write(c1,4);
+    data.Write(c2,4);
+    request_id := TNetData.NetData.NewRequestId;
+    TNetData.NetData.RegisterRequest(Self,CT_NetOp_GetBlocks,request_id);
+    TLog.NewLog(ltdebug,ClassName,Format('Send GET BLOCKS start:%d quantity:%d (from:%d to %d)',[StartAddress,quantity,StartAddress,quantity+StartAddress]));
+    Send(ntp_request,CT_NetOp_GetBlocks,0,request_id,data);
+    Result := FClient.Active;
+  finally
+    data.Free;
+  end;
+end;
+
+function TNetConnection.Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
+  { HELLO command:
+    - Operation stream
+    - My Active server port (0 if no active). (2 bytes)
+    - A Random Longint (4 bytes) to check if its myself connection to my server socket
+    - My Unix Timestamp (4 bytes)
+    - Registered node servers count
+      (For each)
+      - ip (string)
+      - port (2 bytes)
+      - last_connection UTS (4 bytes)
+    - My Server port (2 bytes)
+    - If this is a response:
+      - If remote operation block is lower than me:
+        - Send My Operation Stream in the same block thant requester
+      }
+var data : TStream;
+  i : Integer;
+  op : TPCOperationsComp;
+  nsa : TNodeServerAddress;
+  nsarr : Array of TNodeServerAddress;
+  w : Word;
+  c : Cardinal;
+  l : TList;
+begin
+  Result := false;
+  if Not Connected then exit;
+  // Send Hello command:
+  data := TMemoryStream.Create;
+  try
+    if NetTranferType=ntp_request then begin
+      TNetData.NetData.RegisterRequest(Self,CT_NetOp_Hello,request_id);
+    end;
+    If TNode.Node.NetServer.Active then
+      w := TNode.Node.NetServer.Port
+    else w := 0;
+    // Save active server port (2 bytes). 0 = No active server port
+    data.Write(w,2);
+    // Save a random value (4 bytes)
+    TStreamOp.WriteAnsiString(data,TAccountComp.AccountKey2RawString(TNetData.NetData.FNodePrivateKey.PublicKey));
+    // Save my Unix timestamp (4 bytes)
+    c := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    data.Write(c,4);
+    // Save last operations block
+    op := TPCOperationsComp.Create(TNode.Node.Bank);
+    try
+      if (TNode.Node.Bank.BlocksCount>0) then TNode.Node.Bank.LoadOperations(op,TNode.Node.Bank.BlocksCount-1);
+      op.SaveToStream(false,true,data);
+      SetLength(nsarr,0);
+      // Save other node servers
+      l := TNetData.NetData.FNodeServers.LockList;
+      try
+        for i := 0 to l.Count - 1 do begin
+          nsa := PNodeServerAddress( l[i] )^;
+          if (Not (nsa.its_myself)) And
+            (nsa.BlackListText='') And
+            (nsa.last_connection>0) then begin
+            SetLength(nsarr,length(nsarr)+1);
+            nsarr[high(nsarr)] := nsa;
+          end;
+        end;
+      finally
+        TNetData.NetData.FNodeServers.UnlockList;
+      end;
+      i := length(nsarr);
+      data.Write(i,4);
+      for i := 0 to High(nsarr) do begin
+        nsa := nsarr[i];
+        TStreamOp.WriteAnsiString(data, nsa.ip);
+        data.Write(nsa.port,2);
+        data.Write(nsa.last_connection,4);
+      end;
+      //
+      if NetTranferType=ntp_response then begin
+        if (TNode.Node.Bank.BlocksCount>0) AND (FRemoteOperationBlock.block<TNode.Node.Bank.BlocksCount-1) then begin
+          TNode.Node.Bank.LoadOperations(op,FRemoteOperationBlock.block);
+          if FRemoteOperationBlock.proof_of_work<>op.OperationBlock.proof_of_work then begin
+            TLog.NewLog(ltinfo,Classname,'Found a possible orphan block lower than me (me:'+inttostr(TNode.Node.Bank.BlocksCount-1)+' client:'+
+              inttostr(FRemoteOperationBlock.block)+') ... at '+FClient.RemoteHost+':'+FClient.RemotePort );
+          end;
+          op.SaveToStream(false,true,data);
+        end;
+      end;
+    finally
+      op.free;
+    end;
+    //
+    Send(NetTranferType,CT_NetOp_Hello,0,request_id,data);
+    Result := FClient.Active;
+  finally
+    data.Free;
+  end;
+end;
+
+function TNetConnection.Send_Message(const TheMessage: AnsiString): Boolean;
+Var data : TStream;
+  cyp : TRawBytes;
+begin
+  Result := false;
+  if Not Connected then exit;
+  data := TMemoryStream.Create;
+  Try
+    // Cypher message:
+    cyp := ECIESEncrypt(FClientPublicKey,TheMessage);
+    TStreamOp.WriteAnsiString(data,cyp);
+    Send(ntp_autosend,CT_NetOp_Message,0,0,data);
+    Result := true;
+  Finally
+    data.Free;
+  End;
+end;
+
+function TNetConnection.Send_NewBlockFound: Boolean;
+var data : TStream;
+  request_id : Integer;
+  op : TPCOperationsComp;
+begin
+  Result := false;
+  if TNetData.NetData.Bank.BlocksCount=0 then exit;
+  if Connected then begin
+    // Send Hello command:
+    data := TMemoryStream.Create;
+    try
+      request_id := TNetData.NetData.NewRequestId;
+      op := TPCOperationsComp.Create(Self);
+      try
+        op.bank := TNetData.NetData.Bank;
+        if Not TNetData.NetData.Bank.LoadOperations(op,TNetData.NetData.Bank.BlocksCount-1) then begin
+          TLog.NewLog(lterror,Classname,'Error on Send_NewBlockFound. Cannot load BlockOperations '+inttostr(TNetData.NetData.Bank.BlocksCount-1));
+          exit;
+        end;
+        op.SaveToStream(false,false,data);
+        Send(ntp_autosend,CT_NetOp_NewBlock,0,request_id,data);
+      finally
+        op.free;
+      end;
+    finally
+      data.Free;
+    end;
+    Result := FClient.Active;
+  end;
+end;
+
+procedure TNetConnection.SetClient(const Value: TNetTcpIpClient);
+begin
+  if FClient<>Value then begin
+    if Assigned(FClient) then begin
+      FClient.RemoveFreeNotification(Self);
+    end;
+    TNetData.NetData.UnRegisterRequest(Self,0,0);
+    FClient.Free;
+    FClient := Value;
+  end;
+  if Assigned(FClient) then begin
+    FClient.FreeNotification(Self);
+    FClient.OnConnect := TcpClient_OnConnect;
+    FClient.OnCreateHandle := TcpClient_OnCreateHandle;
+    FClient.OnDestroyHandle := TcpClient_OnDestroyHandle;
+    FClient.OnError := TcpClient_OnError;
+    FClient.OnDisconnect := TcpClient_OnDisconnect;
+    FClient.OnReceive := TcpClient_OnReceive;
+    FClient.OnSend := TcpClient_OnSend;
+  end;
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+end;
+
+procedure TNetConnection.SetConnected(const Value: Boolean);
+begin
+  if (Value = GetConnected) then exit;
+  if Value then ConnectTo(FClient.RemoteHost,StrToIntDef(FClient.RemotePort,CT_NetServer_Port))
+  else FClient.Disconnect;
+end;
+
+procedure TNetConnection.TcpClient_OnConnect(Sender: TObject);
+begin
+  TNetData.NetData.IncStatistics(1,0,1,0,0);
+  TLog.NewLog(ltInfo,Classname,'Connected to a server '+FClient.RemoteHost+':'+FClient.RemotePort);
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+end;
+
+procedure TNetConnection.TcpClient_OnCreateHandle(Sender: TObject);
+begin
+  //
+end;
+
+procedure TNetConnection.TcpClient_OnDestroyHandle(Sender: TObject);
+begin
+  //
+end;
+
+procedure TNetConnection.TcpClient_OnDisconnect(Sender: TObject);
+begin
+  if self is TNetServerClient then TNetData.NetData.IncStatistics(-1,-1,0,0,0)
+  else TNetData.NetData.IncStatistics(-1,0,-1,0,0);
+  TLog.NewLog(ltInfo,Classname,'Disconnected from '+FClient.RemoteHost+':'+FClient.RemotePort);
+  if Assigned(TNetData.NetData.FOnNetConnectionsUpdated) then TNetData.NetData.FOnNetConnectionsUpdated(Self);
+end;
+
+procedure TNetConnection.TcpClient_OnError(Sender: TObject; SocketError: Integer);
+begin
+  FSocketError := SocketError;
+  TLog.NewLog(ltdebug,Classname,'Error '+inttohex(SocketError,8)+' with connection to '+FClient.RemoteHost+':'+FClient.RemotePort);
+end;
+
+procedure TNetConnection.TcpClient_OnReceive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
+begin
+  //
+end;
+
+procedure TNetConnection.TcpClient_OnSend(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
+begin
+  //
+end;
+
+{ TNetClientThread }
+
+procedure TNetClientThread.BCExecute;
+begin
+  while (Not Terminated) do begin
+    If FNetClient.Connected then
+      FNetClient.DoProcessBuffer;
+    Sleep(1);
+  end;
+end;
+
+constructor TNetClientThread.Create(NetClient: TNetClient);
+begin
+  FNetClient := NetClient;
+  inherited Create(false);
+end;
+
+{ TNetClient }
+
+constructor TNetClient.Create(AOwner: TComponent);
+begin
+  inherited;
+  FNetClientThread := TNetClientThread.Create(Self);
+  FNetClientThread.OnTerminate := OnNetClientThreadTerminated;
+  FNetClientThread.FreeOnTerminate := false;
+end;
+
+destructor TNetClient.Destroy;
+begin
+  if Not FNetClientThread.Terminated then begin
+    FNetClientThread.Terminate;
+    FNetClientThread.WaitFor;
+  end;
+  inherited;
+end;
+
+procedure TNetClient.OnNetClientThreadTerminated(Sender: TObject);
+begin
+
+end;
+
+{ TThreadDiscoverConnection }
+
+procedure TThreadDiscoverConnection.BCExecute;
+Var NC : TNetClient;
+  ok : Boolean;
+  lns : TList;
+  i : Integer;
+  Pnsa : PNodeServerAddress;
+begin
+  Pnsa := Nil;
+  // Register attempt
+  lns := TNetData.NetData.FNodeServers.LockList;
+  try
+    i := TNetData.NetData.IndexOfNetClient(lns,FNodeServerAddress.ip,FNodeServerAddress.port);
+    if i>=0 then begin
+      Pnsa := PNodeServerAddress(lns[i]);
+      Pnsa.last_attempt_to_connect := Now;
+      Inc(Pnsa.total_failed_attemps_to_connect);
+    end;
+  finally
+    TNetData.NetData.FNodeServers.UnlockList;
+  end;
+  Synchronize(Synchronized_notify);
+  // Try to connect
+  ok := false;
+  NC := TNetClient.Create(Nil);
+  Try
+    If NC.ConnectTo(FNodeServerAddress.ip,FNodeServerAddress.port) then begin
+      Sleep(500);
+      ok :=NC.Connected;
+      lns := TNetData.NetData.FNodeServers.LockList;
+      try
+        i := TNetData.NetData.IndexOfNetClient(lns,FNodeServerAddress.ip,FNodeServerAddress.port);
+        if i>=0 then begin
+          PNodeServerAddress(lns[i])^.last_connection := (UnivDateTimeToUnix(DateTime2UnivDateTime(now)));
+          PNodeServerAddress(lns[i])^.total_failed_attemps_to_connect := 0; // Clean attemps counter
+        end;
+      finally
+        TNetData.NetData.FNodeServers.UnlockList;
+      end;
+    end;
+  Finally
+    if not ok then begin
+      NC.Free;
+    end;
+  End;
+  Synchronize(Synchronized_notify);
+end;
+
+constructor TThreadDiscoverConnection.Create(NodeServerAddress: TNodeServerAddress; NotifyOnTerminate : TNotifyEvent);
+begin
+  FNodeServerAddress := NodeServerAddress;
+  inherited Create(true);
+  OnTerminate := NotifyOnTerminate;
+  FreeOnTerminate := true;
+  Suspended := false;
+end;
+
+procedure TThreadDiscoverConnection.Synchronized_notify;
+begin
+  if Assigned(TNetData.NetData.FOnNodeServersUpdated) then TNetData.NetData.FOnNodeServersUpdated(Self);
+end;
+
+{ TThreadCheckConnections }
+
+procedure TThreadCheckConnections.BCExecute;
+Var l : TList;
+  i, nactive,ndeleted,ntotal : Integer;
+begin
+  while (Not Terminated) do begin
+    if ((GetTickCount>(FLastCheckTS+30000)) AND (Not TNetData.NetData.FIsDiscoveringServers)) then begin
+      nactive := 0;
+      ndeleted := 0;
+      ntotal := 0;
+      FLastCheckTS := GetTickCount;
+      l := TNetData.NetData.FNetConnections.LockList;
+      try
+        ntotal := l.Count;
+        for i := l.Count-1 downto 0 do begin
+          if (TObject(l.Items[i]) is TNetClient) then begin
+            if Not TNetClient(l[i]).Connected then begin
+              // Free this!
+              TNetClient(l[i]).Free;
+              inc(ndeleted);
+            end else inc(nactive);
+          end;
+        end;
+      finally
+        TNetData.NetData.FNetConnections.UnlockList;
+      end;
+      if (nactive<=CT_MaxServersConnected) And (Not Terminated) then begin
+        // Discover
+        TNetData.NetData.DiscoverServers;
+      end;
+    end;
+    sleep(100);
+  end;
+end;
+
+{ TThreadGetNewBlockChainFromClient }
+
+procedure TThreadGetNewBlockChainFromClient.BCExecute;
+Var i : Integer;
+  nsa : TNodeServerAddress;
+  candidates : TList;
+  lop : TOperationBlock;
+begin
+  // Search better candidates:
+  candidates := TList.Create;
+  try
+    lop := CT_OperationBlock_NUL;
+    for i := 0 to TNetData.NetData.ConnectionsCount(false) - 1 do begin
+      TNetData.NetData.FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
+      if (TNetData.NetData.Connection(i).FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount) And
+         (TNetData.NetData.Connection(i).FRemoteOperationBlock.block>=lop.block)
+         then begin
+         candidates.Add(TNetData.NetData.Connection(i));
+         lop := TNetData.NetData.Connection(i).FRemoteOperationBlock;
+      end;
+    end;
+    TNetData.NetData.FMaxRemoteOperationBlock := lop;
+    if (candidates.Count>0) then begin
+      // Random a candidate
+      i := Random(candidates.Count); // i = 0..count-1
+      TNetData.NetData.GetNewBlockChainFromClient(TNetConnection(candidates[i]));
+    end;
+  finally
+    candidates.Free;
+  end;
+end;
+
+end.

+ 683 - 0
Units/PascalCoin/UNode.pas

@@ -0,0 +1,683 @@
+unit UNode;
+
+{ 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
+
+  }
+
+{ UNode contains the basic structure to operate
+  - An app can only contains 1 node.
+  - A node contains:
+    - 1 Bank
+    - 1 NetServer  (Accepting incoming connections)
+    - 1 Operations (Operations has actual BlockChain with Operations and SafeBankTransaction to operate with the Bank)
+    - 0..x NetClients
+    - 0..x Miners
+    }
+
+interface
+
+uses
+  Classes, UBlockChain, UNetProtocol, UMiner, UAccounts, UCrypto, Windows, UThread;
+
+Type
+  TNode = Class(TComponent)
+  private
+    FLockNodeOperations : TRTLCriticalSection;
+    FNotifyList : TList;
+    FBank : TPCBank;
+    FOperations : TPCOperationsComp;
+    FNetServer : TNetServer;
+    FMinerThreads : TThreadList;
+    FBCBankNotify : TPCBankNotify;
+    Procedure OnBankNewBlock(Sender : TObject);
+    Procedure StartLocking(MaxWaitMilliseconds : Cardinal);
+    Procedure EndLocking;
+    Procedure OnMinerThreadTerminate(Sender : TObject);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  public
+    Class Function Node : TNode;
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property Bank : TPCBank read FBank;
+    Function NetServer : TNetServer;
+    Function MinersCount : Integer;
+    Property MinerThreads : TThreadList read FMinerThreads;
+    Function AddMiner(AccountKey : TAccountKey) : TMinerThread;
+    Procedure DeleteMiner(index : Integer);
+    Procedure NotifyNetClientMessage(Sender : TNetConnection; Const TheMessage : AnsiString);
+    //
+    Property Operations : TPCOperationsComp read FOperations;
+    //
+    Function AddNewBlockChain(SenderMiner : TMinerThread; SenderConnection : TNetConnection; NewBlockOperations: TPCOperationsComp; var newBlockAccount: TBlockAccount; var errors: AnsiString): Boolean;
+    Function AddOperations(SenderConnection : TNetConnection; Operations : TOperationsHashTree; var errors: AnsiString): Integer;
+    Function AddOperation(SenderConnection : TNetConnection; Operation : TPCOperation; var errors: AnsiString): Boolean;
+    Function SendNodeMessage(Target : TNetConnection; TheMessage : AnsiString; var errors : AnsiString) : Boolean;
+
+    Procedure AutoDiscoverNodes(Const ips : AnsiString);
+    Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
+    Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
+  End;
+
+  TNodeNotifyEvents = Class;
+
+  TThreadSafeNodeNotifyEvent = Class(TPCThread)
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FNotifyBlocksChanged : Boolean;
+    FNotifyOperationsChanged : Boolean;
+    Procedure SynchronizedProcess;
+  protected
+    procedure BCExecute; override;
+  End;
+
+  TNodeMessageEvent = Procedure(NetConnection : TNetConnection; MessageData : TRawBytes) of object;
+  { TNodeNotifyEvents is ThreadSafe and will only notify in the main thread }
+  TNodeNotifyEvents = Class(TComponent)
+  private
+    FNode: TNode;
+    FPendingNotificationsList : TThreadList;
+    FThreadSafeNodeNotifyEvent : TThreadSafeNodeNotifyEvent;
+    FOnBlocksChanged: TNotifyEvent;
+    FOnOperationsChanged: TNotifyEvent;
+    FMessages : TStringList;
+    FOnNodeMessageEvent: TNodeMessageEvent;
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    procedure SetNode(const Value: TNode);
+    Procedure NotifyBlocksChanged;
+    Procedure NotifyOperationsChanged;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property Node : TNode read FNode write SetNode;
+    Property OnBlocksChanged : TNotifyEvent read FOnBlocksChanged write FOnBlocksChanged;
+    Property OnOperationsChanged : TNotifyEvent read FOnOperationsChanged write FOnOperationsChanged;
+    Property OnNodeMessageEvent : TNodeMessageEvent read FOnNodeMessageEvent write FOnNodeMessageEvent;
+  End;
+
+implementation
+
+Uses UOpTransaction, SysUtils, ULog, Forms, UConst, UTime;
+
+var _Node : TNode;
+
+{ TNode }
+
+function TNode.AddMiner(AccountKey : TAccountKey) : TMinerThread;
+Var op : TPCOperationsComp;
+begin
+  Result := Nil;
+  TLog.NewLog(ltinfo,ClassName,'Creating a new miner');
+  Result := TMinerThread.Create(Bank,AccountKey,nil,nil);
+  Result.OnTerminate := OnMinerThreadTerminate;
+  op := Result.MinerLockOperations;
+  try
+    op.CopyFromExceptAddressKey(FOperations);
+  finally
+    Result.MinerUnLockOperations(True);
+  end;
+  FMinerThreads.Add(Result);
+end;
+
+function TNode.AddNewBlockChain(SenderMiner: TMinerThread; SenderConnection: TNetConnection; NewBlockOperations: TPCOperationsComp;
+  var newBlockAccount: TBlockAccount; var errors: AnsiString): Boolean;
+Var i : Integer;
+  operationscomp : TPCOperationsComp;
+  nc : TNetConnection;
+  ms : TMemoryStream;
+  mtl : TList;
+begin
+  TLog.NewLog(ltdebug,Classname,Format('AddNewBlockChain Miner:%s Connection:%s NewBlock:%s',[Inttohex(Integer(SenderMiner),8),
+    Inttohex(Integer(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock)]));
+  Try
+    StartLocking(2000);
+  Except
+    On E: Exception do begin
+      TLog.NewLog(lterror,Classname,'Fatal Error at AddNewBlockChain: '+e.Message);
+      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
+    end;
+  End;
+  try
+    ms := TMemoryStream.Create;
+    try
+      FOperations.SaveToStream(false,false,ms);
+      Result := Bank.AddNewBlockChainBlock(NewBlockOperations,newBlockAccount,errors);
+      FOperations.Clear(true);
+      ms.Position:=0;
+      If Not FOperations.LoadFromStream(false,false,ms,errors) then begin
+        TLog.NewLog(lterror,Classname,'Error recovering operations to sanitize: '+errors);
+      end;
+    finally
+      ms.Free;
+    end;
+    if Result then begin
+      FOperations.SanitizeOperations;
+      // Notify to all clients and other miners
+      mtl := FMinerThreads.LockList;
+      try
+        for i := 0 to mtl.Count - 1 do begin
+          if (mtl[i]<>SenderMiner) OR (1=1) then begin
+            operationscomp := TMinerThread(mtl[i]).MinerLockOperations;
+            try
+              operationscomp.CopyFromExceptAddressKey(FOperations);
+            finally
+              TMinerThread(mtl[i]).MinerUnLockOperations(true);
+            end;
+          end else begin
+            //
+          end;
+        end;
+      finally
+        FMinerThreads.UnlockList;
+      end;
+      // Notify to clients
+      for i:=0 to TNetData.NetData.ConnectionsCount(false)-1 do begin
+        nc := TNetData.NetData.Connection(i);
+        if (SenderConnection<>nc) then begin
+          nc.Send_NewBlockFound;
+        end;
+      end;
+      // Send sanitized operations to other nodes
+      if FOperations.OperationsHashTree.OperationsCount>0 then begin
+        TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(FOperations.OperationsHashTree.OperationsCount)+' sanitized operations to nodes');
+        for i:=0 to TNetData.NetData.ConnectionsCount(false)-1 do begin
+          nc := TNetData.NetData.Connection(i);
+          nc.Send_AddOperations(FOperations.OperationsHashTree);
+        end;
+      end;
+    end else begin
+      // If error is on a SenderMiner its a hole
+      FOperations.SanitizeOperations;
+      if Assigned(SenderMiner) then begin
+        TLog.NewLog(lterror,SenderMiner.Classname,'Invalid calculated PoW... reseting from Node Operations: '+TPCOperationsComp.OperationBlockToText(FOperations.OperationBlock));
+        operationscomp := SenderMiner.MinerLockOperations;
+        try
+          operationscomp.CopyFromExceptAddressKey(FOperations);
+        finally
+          SenderMiner.MinerUnLockOperations(true);
+        end;
+        // Reset others:
+        mtl := FMinerThreads.LockList;
+        try
+          for i := 0 to mtl.Count - 1 do begin
+            if (TMinerThread(mtl[i])<>SenderMiner) then begin
+              operationscomp := TMinerThread(mtl[i]).MinerLockOperations;
+              try
+                operationscomp.CopyFromExceptAddressKey(FOperations);
+              finally
+                TMinerThread(mtl[i]).MinerUnLockOperations(true);
+              end;
+            end;
+          end;
+        finally
+          FMinerThreads.UnlockList;
+        end;
+      end;
+    end;
+  finally
+    EndLocking;
+    TLog.NewLog(ltdebug,Classname,Format('Finalizing AddNewBlockChain Miner:%s Connection:%s NewBlock:%s',[Inttohex(Integer(SenderMiner),8),
+      Inttohex(Integer(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock) ]));
+  End;
+  if Result then begin
+    // Notify it!
+    for i := 0 to FNotifyList.Count-1 do begin
+      TNodeNotifyEvents( FNotifyList[i] ).NotifyBlocksChanged;
+    end;
+  end;
+end;
+
+function TNode.AddOperation(SenderConnection : TNetConnection; Operation: TPCOperation; var errors: AnsiString): Boolean;
+var ops : TOperationsHashTree;
+begin
+  ops := TOperationsHashTree.Create;
+  Try
+    ops.AddOperationToHashTree(Operation);
+    Result := AddOperations(SenderConnection,ops,errors)=1;
+  Finally
+    ops.Free;
+  End;
+end;
+
+function TNode.AddOperations(SenderConnection : TNetConnection; Operations : TOperationsHashTree; var errors: AnsiString): Integer;
+Var
+  i,j : Integer;
+  operationscomp : TPCOperationsComp;
+  valids_operations : TOperationsHashTree;
+  nc : TNetConnection;
+  e : AnsiString;
+  mtl : TList;
+begin
+  Result := -1;
+  TLog.NewLog(ltdebug,Classname,Format('AddOperations Connection:%s Operations:%d',[
+    Inttohex(Integer(SenderConnection),8),Operations.OperationsCount]));
+  Try
+    StartLocking(4000);
+  Except
+    On E: Exception do begin
+      TLog.NewLog(lterror,Classname,'Fatal Error at AddOperations: '+e.Message);
+      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
+    end;
+  End;
+  try
+    Result := 0;
+    errors := '';
+    valids_operations := TOperationsHashTree.Create;
+    try
+      for j := 0 to Operations.OperationsCount-1 do begin
+        TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),Operations.OperationsCount,Operations.GetOperation(j).ToString]));
+        if (FOperations.AddOperation(true,Operations.GetOperation(j),e)) then begin
+          inc(Result);
+          valids_operations.AddOperationToHashTree(Operations.GetOperation(j));
+        end else begin
+          if (errors<>'') then errors := errors+' ';
+          errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(Operations.OperationsCount)+':'+e;
+        end;
+      end;
+      if Result=0 then exit;
+      // Send to miners
+      mtl := FMinerThreads.LockList;
+      Try
+        for i := 0 to mtl.Count - 1 do begin
+          operationscomp := TMinerThread(mtl[i]).MinerLockOperations;
+          try
+            operationscomp.CopyFromExceptAddressKey(FOperations);
+          finally
+            TMinerThread(mtl[i]).MinerUnLockOperations(false);
+          end;
+        end;
+      Finally
+        FMinerThreads.UnlockList;
+      End;
+      // Send to other nodes
+      for i:=0 to TNetData.NetData.ConnectionsCount(false)-1 do begin
+        nc := TNetData.NetData.Connection(i);
+        if (nc<>SenderConnection) then begin
+          nc.Send_AddOperations(valids_operations);
+        end;
+      end;
+    finally
+      valids_operations.Free;
+    end;
+  finally
+    EndLocking;
+    TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations Connection:%s Operations:%d LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
+      Inttohex(Integer(SenderConnection),8),Operations.OperationsCount,FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]));
+  end;
+  // Notify it!
+  for i := 0 to FNotifyList.Count-1 do begin
+    TNodeNotifyEvents( FNotifyList[i] ).NotifyOperationsChanged;
+  end;
+end;
+
+procedure TNode.AutoDiscoverNodes(Const ips : AnsiString);
+  Function GetIp(var ips_string : AnsiString; var nsa : TNodeServerAddress) : Boolean;
+  Const CT_IP_CHARS = ['a'..'z','A'..'Z','0'..'9','.','-','_'];
+  var i : Integer;
+    port : AnsiString;
+  begin
+    nsa := CT_TNodeServerAddress_NUL;
+    Result := false;
+    if length(trim(ips_string))=0 then begin
+      ips_string := '';
+      exit;
+    end;
+    i := 1;
+    while (i<length(ips_string)) AND (NOT (ips_string[i] IN CT_IP_CHARS)) do inc(i);
+    if (i>1) then ips_string := copy(ips_string,i,length(ips_string));
+    //
+    i := 1;
+    while (i<=length(ips_string)) and (ips_string[i] in CT_IP_CHARS) do inc(i);
+    nsa.ip := copy(ips_string,1,i-1);
+    if (i<=length(ips_string)) and (ips_string[i]=':') then begin
+      inc(i);
+      port := '';
+      while (i<=length(ips_string)) and (ips_string[i] in ['0'..'9']) do begin
+        port := port + ips_string[i];
+        inc(i);
+      end;
+      nsa.port := StrToIntDef(port,0);
+    end;
+    ips_string := copy(ips_string,i+1,length(ips_string));
+    if nsa.port=0 then nsa.port := CT_NetServer_Port;
+    Result := (trim(nsa.ip)<>'');
+  end;
+Var i,j : Integer;
+  ips_string : AnsiString;
+  nsa : TNodeServerAddress;
+begin
+  ips_string := ips;
+  repeat
+    If GetIp(ips_string,nsa) then begin
+      TNetData.NetData.AddServer(nsa);
+    end;
+  until (ips_string='');
+  //
+  j := (CT_MaxServersConnected -  TNetData.NetData.ConnectionsCount(true));
+  if j<=0 then exit;
+  TNetData.NetData.DiscoverServers;
+end;
+
+constructor TNode.Create(AOwner: TComponent);
+begin
+  if Assigned(_Node) then raise Exception.Create('Duplicate nodes protection');
+  inherited;
+  InitializeCriticalSection(FLockNodeOperations);
+  TLog.NewLog(ltdebug,Classname,Format('Initialize LockOperations LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
+    FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]));
+  FBank := TPCBank.Create(Self);
+  FBCBankNotify := TPCBankNotify.Create(Self);
+  FBCBankNotify.Bank := FBank;
+  FBCBankNotify.OnNewBlock := OnBankNewBlock;
+  FNetServer := TNetServer.Create(Self);
+  FMinerThreads := TThreadList.Create;
+  FOperations := TPCOperationsComp.Create(Self);
+  FOperations.bank := FBank;
+  FNotifyList := TList.Create;
+  if Not Assigned(_Node) then _Node := Self;
+end;
+
+procedure TNode.DeleteMiner(index: Integer);
+Var m : TMinerThread;
+  mtl : TList;
+begin
+  mtl := FMinerThreads.LockList;
+  Try
+    m := TMinerThread(mtl[index]);
+    m.Suspended := false;
+    m.Paused := false;
+    mtl.Delete(index);
+  Finally
+    FMinerThreads.UnlockList;
+  End;
+  m.Terminate;
+  m.WaitFor;
+end;
+
+destructor TNode.Destroy;
+Var step : String;
+begin
+  TLog.NewLog(ltinfo,Classname,'Destroying Node - START');
+  Try
+    step := 'Deleting critical section';
+    DeleteCriticalSection(FLockNodeOperations);
+
+    step := 'Desactivating server';
+    FNetServer.Active := false;
+
+    step := 'Deleting miners';
+    while (MinersCount>0) do DeleteMiner(0);
+
+    step := 'Destroying NetServer';
+    FNetServer.Free;
+    step := 'Destroying MinerThreads';
+    FMinerThreads.Free;
+
+    step := 'Destroying NotifyList';
+    FNotifyList.Free;
+    step := 'Destroying Operations';
+    FOperations.Free;
+    step := 'Assigning NIL to node var';
+    if _Node=Self then _Node := Nil;
+
+    step := 'Destroying Bank';
+    FBank.Free;
+
+    step := 'inherited';
+    inherited;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,'Error destroying Node step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
+      Raise;
+    end;
+  End;
+  TLog.NewLog(ltinfo,Classname,'Destroying Node - END');
+end;
+
+procedure TNode.EndLocking;
+begin
+  LeaveCriticalSection(FLockNodeOperations);
+end;
+
+function TNode.IsBlockChainValid(var WhyNot : AnsiString): Boolean;
+Var unixtimediff : Integer;
+begin
+  Result :=false;
+  if (TNetData.NetData.NetStatistics.ActiveConnections<=0)  then begin
+    WhyNot := 'No connection to check blockchain';
+    exit;
+  end;
+  if (Bank.LastOperationBlock.block<=0) then begin
+    WhyNot := 'No blockchain';
+    exit;
+  end;
+  unixtimediff := UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - Bank.LastOperationBlock.timestamp;
+  If (unixtimediff<0) then begin
+    WhyNot := 'Invalid Last Block Time';
+    exit;
+  end;
+  if (unixtimediff>(CT_NewLineSecondsAvg*10)) then begin
+    WhyNot := 'Last block has a long time ago... '+inttostr(unixtimediff);
+    exit;
+  end;
+  Result := true;
+end;
+
+function TNode.IsReady(var CurrentProcess: AnsiString): Boolean;
+begin
+  Result := false;
+  CurrentProcess := '';
+  if FBank.IsReady(CurrentProcess) then begin
+    if FNetServer.Active then begin
+      if TNetData.NetData.MaxRemoteOperationBlock.block>FOperations.OperationBlock.block then begin
+        CurrentProcess := 'Found block '+inttostr(TNetData.NetData.MaxRemoteOperationBlock.block);
+      end else begin
+        Result := true;
+      end;
+    end else begin
+      CurrentProcess := 'Server not active';
+    end;
+  end;
+end;
+
+function TNode.MinersCount : Integer;
+Var mtl : TList;
+begin
+  mtl := FMinerThreads.LockList;
+  Try
+    Result := mtl.Count;
+  Finally
+    FMinerThreads.UnlockList;
+  End;
+end;
+
+function TNode.NetServer: TNetServer;
+begin
+  Result := FNetServer;
+end;
+
+class function TNode.Node: TNode;
+begin
+  if not assigned(_Node) then _Node := TNode.Create(Nil);
+  Result := _Node;
+end;
+
+procedure TNode.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+  inherited;
+end;
+
+procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: AnsiString);
+Var i : Integer;
+  s : AnsiString;
+begin
+  for i := 0 to FNotifyList.Count-1 do begin
+    if Assigned( TNodeNotifyEvents( FNotifyList[i] ).OnNodeMessageEvent) then begin
+      TNodeNotifyEvents( FNotifyList[i] ).FMessages.AddObject(TheMessage,Sender);
+    end;
+  end;
+end;
+
+procedure TNode.OnBankNewBlock(Sender: TObject);
+begin
+  FOperations.SanitizeOperations;
+end;
+
+procedure TNode.OnMinerThreadTerminate(Sender: TObject);
+begin
+  FMinerThreads.Remove(Sender);
+end;
+
+function TNode.SendNodeMessage(Target: TNetConnection; TheMessage: AnsiString; var errors: AnsiString): Boolean;
+Var i : Integer;
+  nc : TNetConnection;
+begin
+  Try
+    StartLocking(4000);
+  Except
+    On E: Exception do begin
+      TLog.NewLog(lterror,Classname,'Fatal Error at SendNodeMessage: '+e.Message);
+      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
+    end;
+  End;
+  try
+    Result := false;
+    errors := '';
+    if assigned(Target) then begin 
+      Target.Send_Message(TheMessage);
+    end else begin
+      for i:=0 to TNetData.NetData.ConnectionsCount(false)-1 do begin
+        nc := TNetData.NetData.Connection(i);
+        nc.Send_Message(TheMessage);
+      end;        
+    end;
+    result := true;
+  finally
+    EndLocking;
+  end;
+end;
+
+procedure TNode.StartLocking(MaxWaitMilliseconds : Cardinal);
+Var tc : Cardinal;
+  s : String;
+  IsLocked : Boolean;
+begin
+  if MaxWaitMilliseconds>60000 then MaxWaitMilliseconds := 60000;
+  tc := GetTickCount;
+  Repeat
+    IsLocked := TryEnterCriticalSection(FLockNodeOperations);
+    if Not IsLocked then sleep(1);
+  Until (IsLocked) Or (GetTickCount > (tc + MaxWaitMilliseconds));
+  if Not IsLocked then begin
+    s := Format('Cannot lock operations node - LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
+      FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]);
+    TLog.NewLog(lterror,Classname,s);
+    raise Exception.Create(s);
+  end;
+end;
+
+{ TNodeNotifyEvents }
+
+constructor TNodeNotifyEvents.Create(AOwner: TComponent);
+begin
+  inherited;
+  FOnOperationsChanged := Nil;
+  FOnBlocksChanged := Nil;
+  FOnNodeMessageEvent := Nil;
+  FMessages := TStringList.Create;
+  FPendingNotificationsList := TThreadList.Create;
+  FThreadSafeNodeNotifyEvent := TThreadSafeNodeNotifyEvent.Create(false);
+  FThreadSafeNodeNotifyEvent.FNodeNotifyEvents := Self;
+  FThreadSafeNodeNotifyEvent.FreeOnTerminate := true;
+  Node := _Node;
+end;
+
+destructor TNodeNotifyEvents.Destroy;
+begin
+  if Assigned(FNode) then FNode.FNotifyList.Remove(Self);
+  FThreadSafeNodeNotifyEvent.FNodeNotifyEvents := Nil;
+  FThreadSafeNodeNotifyEvent.Terminate;
+  FPendingNotificationsList.Free;
+  FMessages.Free;
+  inherited;
+end;
+
+procedure TNodeNotifyEvents.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+  inherited;
+  if (Operation=opremove) then begin
+    if AComponent=FNode then FNode := Nil;
+  end;
+end;
+
+procedure TNodeNotifyEvents.NotifyBlocksChanged;
+begin
+  if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyBlocksChanged := true;
+end;
+
+procedure TNodeNotifyEvents.NotifyOperationsChanged;
+begin
+  if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyOperationsChanged := true;
+end;
+
+procedure TNodeNotifyEvents.SetNode(const Value: TNode);
+begin
+  if FNode=Value then exit;
+  if Assigned(FNode) then begin
+    FNode.RemoveFreeNotification(Self);
+    FNode.FNotifyList.Add(Self);
+  end;
+  FNode := Value;
+  if Assigned(FNode) then begin
+    FNode.FreeNotification(Self);
+    FNode.FNotifyList.Add(Self);
+  end;
+end;
+
+{ TThreadSafeNodeNotifyEvent }
+
+procedure TThreadSafeNodeNotifyEvent.BCExecute;
+begin
+  while (not Terminated) AND (Assigned(FNodeNotifyEvents)) do begin
+    if (FNotifyOperationsChanged) Or (FNotifyBlocksChanged) Or (FNodeNotifyEvents.FMessages.Count>0) then Synchronize(SynchronizedProcess);
+    Sleep(1);
+  end;
+end;
+
+procedure TThreadSafeNodeNotifyEvent.SynchronizedProcess;
+Var i : Integer;
+begin
+  If (Terminated) Or (Not Assigned(FNodeNotifyEvents)) then exit;
+  if FNotifyBlocksChanged then begin
+    FNotifyBlocksChanged := false;
+    if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnBlocksChanged)) then
+      FNodeNotifyEvents.FOnBlocksChanged(FNodeNotifyEvents);
+  end;
+  if FNotifyOperationsChanged then begin
+    FNotifyOperationsChanged := false;
+    if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnOperationsChanged)) then
+      FNodeNotifyEvents.FOnOperationsChanged(FNodeNotifyEvents);
+  end;
+  if FNodeNotifyEvents.FMessages.Count>0 then begin
+    if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnNodeMessageEvent)) then begin
+      for i := 0 to FNodeNotifyEvents.FMessages.Count - 1 do begin
+        FNodeNotifyEvents.FOnNodeMessageEvent(TNetConnection(FNodeNotifyEvents.FMessages.Objects[i]),FNodeNotifyEvents.FMessages.Strings[i]);
+      end;
+    end;
+    FNodeNotifyEvents.FMessages.Clear;
+  end;
+end;
+
+initialization
+  _Node := Nil;
+finalization
+  _Node.Free;
+end.

+ 644 - 0
Units/PascalCoin/UOpTransaction.pas

@@ -0,0 +1,644 @@
+unit UOpTransaction;
+
+{ 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 UCrypto, UBlockChain, Classes, UAccounts;
+
+Type
+  // Operations Type
+  TOpTransactionData = Record
+    sender: Cardinal;
+    n_operation : Cardinal;
+    target: Cardinal;
+    amount: UInt64;
+    fee: UInt64;
+    payload: AnsiString;
+    public_key: TECDSA_Public;
+    sign: TECDSA_SIG;
+  End;
+
+  TOpChangeKeyData = Record
+    account: Cardinal;
+    n_operation : Cardinal;
+    fee: UInt64;
+    payload: AnsiString;
+    public_key: TECDSA_Public;
+    new_accountkey: TAccountKey;
+    sign: TECDSA_SIG;
+  End;
+
+  TOpRecoverFoundsData = Record
+    account: Cardinal;
+    n_operation : Cardinal;
+    fee: UInt64;
+  End;
+
+  TOpTransaction = Class(TPCOperation)
+  private
+    FData : TOpTransactionData;
+  public
+    function GetOperationBufferToHash : TRawBytes; override;
+    function DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
+    function SaveToStream(Stream : TStream) : Boolean; override;
+    function LoadFromStream(Stream : TStream) : Boolean; override;
+    procedure AffectedAccounts(list : TList); override;
+    //
+    Class Function GetTransactionHasthToSign(const trans : TOpTransactionData) : TRawBytes;
+    Class Function DoSignOperation(key : TECPrivateKey; var trans : TOpTransactionData) : Boolean;
+    class function OpType : Byte; override;
+    function OperationAmount : Int64; override;
+    function OperationFee : UInt64; override;
+    function OperationPayload : TRawBytes; override;
+    function SenderAccount : Cardinal; override;
+    Property Data : TOpTransactionData read FData;
+
+    Constructor Create(sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; payload: AnsiString);
+    Function toString : String; Override;
+  End;
+
+  TOpChangeKey = Class(TPCOperation)
+  private
+    FData : TOpChangeKeyData;
+  public
+    Class Function GetOperationHasthToSign(const op : TOpChangeKeyData) : TRawBytes;
+    Class Function DoSignOperation(key : TECPrivateKey; var op : TOpChangeKeyData) : Boolean;
+    class function OpType : Byte; override;
+
+    function GetOperationBufferToHash : TRawBytes; override;
+    function DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
+    function SaveToStream(Stream : TStream) : Boolean; override;
+    function LoadFromStream(Stream : TStream) : Boolean; override;
+    function OperationAmount : Int64; override;
+    function OperationFee : UInt64; override;
+    function OperationPayload : TRawBytes; override;
+    function SenderAccount : Cardinal; override;
+    procedure AffectedAccounts(list : TList); override;
+    Constructor Create(account_number, n_operation: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: AnsiString);
+    Property Data : TOpChangeKeyData read FData;
+    Function toString : String; Override;
+  End;
+
+  TOpRecoverFounds = Class(TPCOperation)
+  private
+    FData : TOpRecoverFoundsData;
+  public
+    class function OpType : Byte; override;
+
+    function GetOperationBufferToHash : TRawBytes; override;
+    function DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
+    function SaveToStream(Stream : TStream) : Boolean; override;
+    function LoadFromStream(Stream : TStream) : Boolean; override;
+    function OperationAmount : Int64; override;
+    function OperationFee : UInt64; override;
+    function OperationPayload : TRawBytes; override;
+    function SenderAccount : Cardinal; override;
+    procedure AffectedAccounts(list : TList); override;
+    Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
+    Property Data : TOpRecoverFoundsData read FData;
+    Function toString : String; Override;
+  End;
+
+
+
+implementation
+
+uses
+  SysUtils, UConst, ULog;
+
+
+{ TOpTransaction }
+
+procedure TOpTransaction.AffectedAccounts(list: TList);
+begin
+  list.Add(TObject(FData.sender));
+  list.Add(TObject(FData.target));
+end;
+
+constructor TOpTransaction.Create(sender, n_operation, target: Cardinal;
+  key: TECPrivateKey; amount, fee: UInt64; payload: AnsiString);
+begin
+  FData.sender := sender;
+  FData.n_operation := n_operation;
+  FData.target := target;
+  FData.amount := amount;
+  FData.fee := fee;
+  FData.payload := payload;
+  FData.public_key := key.PublicKey;
+  If Not DoSignOperation(key,FData) then begin
+    TLog.NewLog(lterror,Classname,'Error signing a new Transaction');
+  end;
+end;
+
+function TOpTransaction.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString): Boolean;
+Var s_new, t_new : Int64;
+  totalamount : Cardinal;
+  sender,target : TAccount;
+  _h : TRawBytes;
+begin
+  Result := false;
+  errors := '';
+  //
+  if (FData.sender>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := Format('Invalid sender %d',[FData.sender]);
+    Exit;
+  end;
+  if (FData.target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := Format('Invalid target %d',[FData.target]);
+    Exit;
+  end;
+  if (FData.sender=FData.target) then begin
+    errors := Format('Sender=Target %d',[FData.sender]);
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.sender,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := Format('sender (%d) is blocked for protocol',[FData.sender]);
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.target,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := Format('target (%d) is blocked for protocol',[FData.target]);
+    Exit;
+  end;
+  if (FData.amount<=0) Or (FData.amount>CT_MaxTransactionAmount) then begin
+    errors := Format('Invalid amount %d (0 or max: %d)',[FData.amount,CT_MaxTransactionAmount]);
+    Exit;
+  end;
+  if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
+    errors := Format('Invalid fee %d (max %d)',[FData.fee,CT_MaxTransactionFee]);
+    Exit;
+  end;
+  if (length(FData.payload)>CT_MaxPayloadSize) then begin
+    errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
+  end;
+
+  sender := AccountTransaction.Account(FData.sender);
+  target := AccountTransaction.Account(FData.target);
+  if ((sender.n_operation+1)<>FData.n_operation) then begin
+    errors := Format('Invalid n_operation %d (expected %d)',[FData.n_operation,sender.n_operation+1]);
+    Exit;
+  end;
+  totalamount := FData.amount + FData.fee;
+  if (sender.balance<totalamount) then begin
+    errors := Format('Insuficient founds %d < (%d + %d = %d)',[sender.balance,FData.amount,FData.fee,totalamount]);
+    Exit;
+  end;
+  if (target.balance+FData.amount>CT_MaxWalletAmount) then begin
+    errors := Format('Target cannot accept this transaction due to max amount %d+%d=%d > %d',[target.balance,FData.amount,target.balance+FData.amount,CT_MaxWalletAmount]);
+    Exit;
+  end;
+  // Check signature
+  _h := GetTransactionHasthToSign(FData);
+  if (Not TCrypto.ECDSAVerify(FData.public_key,_h,FData.sign)) then begin
+    errors := 'Invalid sign';
+    Exit;
+  end;
+  // Do operation
+  Result := AccountTransaction.TransferAmount(FData.sender,FData.target,FData.n_operation,FData.amount,FData.fee,errors);
+end;
+
+class function TOpTransaction.DoSignOperation(key : TECPrivateKey; var trans : TOpTransactionData) : Boolean;
+var s : AnsiString;
+  _sign : TECDSA_SIG;
+begin
+  s := GetTransactionHasthToSign(trans);
+  Try
+    _sign := TCrypto.ECDSASign(key.PrivateKey,s);
+    trans.sign := _sign;
+    Result := true;
+  Except
+    trans.sign.r:='';
+    trans.sign.s:='';
+    Result := false;
+  End;
+end;
+
+function TOpTransaction.GetOperationBufferToHash: TRawBytes;
+Var ms : TMemoryStream;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.sender,Sizeof(FData.sender));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.target,Sizeof(FData.target));
+    ms.Write(FData.amount,Sizeof(FData.amount));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    ms.WriteBuffer(FData.payload[1],length(FData.payload));
+    ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+    ms.WriteBuffer(FData.public_key.x[1],length(FData.public_key.x));
+    ms.WriteBuffer(FData.public_key.y[1],length(FData.public_key.y));
+    ms.WriteBuffer(FData.sign.r[1],length(FData.sign.r));
+    ms.WriteBuffer(FData.sign.s[1],length(FData.sign.s));
+    SetLength(Result,ms.Size);
+    ms.Position := 0;
+    ms.ReadBuffer(Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+class function TOpTransaction.GetTransactionHasthToSign(const trans: TOpTransactionData): TRawBytes;
+Var ms : TMemoryStream;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(trans.sender,Sizeof(trans.sender));
+    ms.Write(trans.n_operation,Sizeof(trans.n_operation));
+    ms.Write(trans.target,Sizeof(trans.target));
+    ms.Write(trans.amount,Sizeof(trans.amount));
+    ms.Write(trans.fee,Sizeof(trans.fee));
+    ms.WriteBuffer(trans.payload[1],length(trans.payload));
+    ms.Write(trans.public_key.EC_OpenSSL_NID,Sizeof(trans.public_key.EC_OpenSSL_NID));
+    ms.WriteBuffer(trans.public_key.x[1],length(trans.public_key.x));
+    ms.WriteBuffer(trans.public_key.y[1],length(trans.public_key.y));
+    SetLength(Result,ms.Size);
+    ms.Position := 0;
+    ms.ReadBuffer(Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+function TOpTransaction.LoadFromStream(Stream: TStream): Boolean;
+begin
+  Result := false;
+  if Stream.Size-Stream.Position < 28  then exit; // Invalid stream
+  Stream.Read(FData.sender,Sizeof(FData.sender));
+  Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Read(FData.target,Sizeof(FData.target));
+  Stream.Read(FData.amount,Sizeof(FData.amount));
+  Stream.Read(FData.fee,Sizeof(FData.fee));
+  if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
+  if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
+  Result := true;
+end;
+
+function TOpTransaction.OperationAmount: Int64;
+begin
+  Result := FData.amount;
+end;
+
+function TOpTransaction.OperationFee: UInt64;
+begin
+  Result := FData.fee;
+end;
+
+function TOpTransaction.OperationPayload: TRawBytes;
+begin
+  Result := FData.payload;
+end;
+
+class function TOpTransaction.OpType: Byte;
+begin
+  Result := CT_Op_Transaction;
+end;
+
+function TOpTransaction.SaveToStream(Stream: TStream): Boolean;
+begin
+  Stream.Write(FData.sender,Sizeof(FData.sender));
+  Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Write(FData.target,Sizeof(FData.target));
+  Stream.Write(FData.amount,Sizeof(FData.amount));
+  Stream.Write(FData.fee,Sizeof(FData.fee));
+  TStreamOp.WriteAnsiString(Stream,FData.payload);
+  Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+  TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
+  TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
+  TStreamOp.WriteAnsiString(Stream,FData.sign.r);
+  TStreamOp.WriteAnsiString(Stream,FData.sign.s);
+  Result := true;
+end;
+
+
+function TOpTransaction.SenderAccount: Cardinal;
+begin
+  Result := FData.sender;
+end;
+
+function TOpTransaction.toString: String;
+begin
+  Result := Format('Transaction from %s to %s amount:%s fee:%s (n_op:%d) payload size:%d',[
+     TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
+     TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
+     TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
+end;
+
+{ TOpChangeKey }
+
+procedure TOpChangeKey.AffectedAccounts(list: TList);
+begin
+  list.Add(TObject(FData.account));
+end;
+
+constructor TOpChangeKey.Create(account_number, n_operation: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: AnsiString);
+begin
+  FData.account := account_number;
+  FData.n_operation := n_operation;
+  FData.fee := fee;
+  FData.payload := payload;
+  FData.public_key := key.PublicKey;
+  FData.new_accountkey := new_account_key;
+  If Not DoSignOperation(key,FData) then begin
+    TLog.NewLog(lterror,Classname,'Error signing a new Change key');
+  end;
+end;
+
+function TOpChangeKey.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors: AnsiString): Boolean;
+Var account : TAccount;
+begin
+  Result := false;
+  if (FData.account>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := 'Invalid account number';
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.account, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := 'account is blocked for protocol';
+    Exit;
+  end;
+  if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
+    errors := 'Invalid fee: '+Inttostr(FData.fee);
+    exit;
+  end;
+  account := AccountTransaction.Account(FData.account);
+  if ((account.n_operation+1)<>FData.n_operation) then begin
+    errors := 'Invalid n_operation';
+    Exit;
+  end;
+  if (account.balance<FData.fee) then begin
+    errors := 'Insuficient founds';
+    exit;
+  end;
+  if (length(FData.payload)>CT_MaxPayloadSize) then begin
+    errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
+  end;
+  If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, errors ) then begin
+    exit;
+  end;
+  If Not TCrypto.ECDSAVerify(FData.public_key,GetOperationHasthToSign(FData),FData.sign) then begin
+    errors := 'Invalid sign';
+    exit;
+  end;
+  Result := AccountTransaction.UpdateAccountkey(FData.account,FData.n_operation,FData.new_accountkey,FData.fee,errors);
+end;
+
+class function TOpChangeKey.DoSignOperation(key: TECPrivateKey; var op: TOpChangeKeyData): Boolean;
+var s : AnsiString;
+  _sign : TECDSA_SIG;
+begin
+  s := GetOperationHasthToSign(op);
+  Try
+    _sign := TCrypto.ECDSASign(key.PrivateKey,s);
+    op.sign := _sign;
+    Result := true;
+  Except
+    On E:Exception do begin
+      Result := false;
+      TLog.NewLog(lterror,ClassName,'Error signing ChangeKey operation: '+E.Message);
+    end;
+  End;
+end;
+
+function TOpChangeKey.GetOperationBufferToHash: TRawBytes;
+var ms : TMemoryStream;
+  s : AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.account,Sizeof(FData.account));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    ms.WriteBuffer(FData.payload[1],length(FData.payload));
+    ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+    ms.WriteBuffer(FData.public_key.x[1],length(FData.public_key.x));
+    ms.WriteBuffer(FData.public_key.y[1],length(FData.public_key.y));
+    s := TAccountComp.AccountKey2RawString(FData.new_accountkey);
+    ms.WriteBuffer(s[1],length(s));
+    ms.WriteBuffer(FData.sign.r[1],length(FData.sign.r));
+    ms.WriteBuffer(FData.sign.s[1],length(FData.sign.s));
+    ms.Position := 0;
+    setlength(Result,ms.Size);
+    ms.ReadBuffer(Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+class function TOpChangeKey.GetOperationHasthToSign(const op: TOpChangeKeyData): TRawBytes;
+var ms : TMemoryStream;
+  s : AnsiString;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(op.account,Sizeof(op.account));
+    ms.Write(op.n_operation,Sizeof(op.n_operation));
+    ms.Write(op.fee,Sizeof(op.fee));
+    ms.WriteBuffer(op.payload[1],length(op.payload));
+    ms.Write(op.public_key.EC_OpenSSL_NID,Sizeof(op.public_key.EC_OpenSSL_NID));
+    ms.WriteBuffer(op.public_key.x[1],length(op.public_key.x));
+    ms.WriteBuffer(op.public_key.y[1],length(op.public_key.y));
+    s := TAccountComp.AccountKey2RawString(op.new_accountkey);
+    ms.WriteBuffer(s[1],length(s));
+    ms.Position := 0;
+    setlength(Result,ms.Size);
+    ms.ReadBuffer(Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+
+function TOpChangeKey.LoadFromStream(Stream: TStream): Boolean;
+var s : AnsiString;
+begin
+  Result := false;
+  if Stream.Size-Stream.Position < 16  then exit; // Invalid stream
+  Stream.Read(FData.account,Sizeof(FData.account));
+  Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Read(FData.fee,Sizeof(FData.fee));
+  if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
+  if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+  FData.new_accountkey := TAccountComp.RawString2Accountkey(s);
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
+  Result := true;
+end;
+
+function TOpChangeKey.OperationAmount: Int64;
+begin
+  Result := 0;
+end;
+
+function TOpChangeKey.OperationFee: UInt64;
+begin
+  Result := FData.fee;
+end;
+
+function TOpChangeKey.OperationPayload: TRawBytes;
+begin
+  Result := FData.payload;
+end;
+
+class function TOpChangeKey.OpType: Byte;
+begin
+  Result := CT_Op_Changekey;
+end;
+
+function TOpChangeKey.SaveToStream(Stream: TStream): Boolean;
+begin
+  Stream.Write(FData.account,Sizeof(FData.account));
+  Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Write(FData.fee,Sizeof(FData.fee));
+  TStreamOp.WriteAnsiString(Stream,FData.payload);
+  Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+  TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
+  TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
+  TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FData.new_accountkey));
+  TStreamOp.WriteAnsiString(Stream,FData.sign.r);
+  TStreamOp.WriteAnsiString(Stream,FData.sign.s);
+  Result := true;
+end;
+
+function TOpChangeKey.SenderAccount: Cardinal;
+begin
+  Result := FData.account;
+end;
+
+function TOpChangeKey.toString: String;
+begin
+  Result := Format('Change key of %s to new key: %s fee:%s (n_op:%d) payload size:%d',[
+    TAccountComp.AccountNumberToAccountTxtNumber(FData.account),
+    TAccountComp.GetECInfoTxt(FData.new_accountkey.EC_OpenSSL_NID),
+    TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
+end;
+
+{ TOpRecoverFounds }
+
+procedure TOpRecoverFounds.AffectedAccounts(list: TList);
+begin
+  list.Add(TObject(FData.account));
+end;
+
+constructor TOpRecoverFounds.Create(account_number, n_operation : Cardinal; fee: UInt64);
+begin
+  FData.account := account_number;
+  FData.n_operation := n_operation;
+  FData.fee := fee;
+end;
+
+function TOpRecoverFounds.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors: AnsiString): Boolean;
+Var acc : TAccount;
+begin
+  Result := false;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.account,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := 'account is blocked for protocol';
+    Exit;
+  end;
+  acc := AccountTransaction.Account(FData.account);
+  if (acc.updated_block + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := Format('Is active to recover founds! Account %d Updated %d + %d >= BlockCount : %d',[FData.account,acc.updated_block,CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
+    Exit;
+  end;
+  if ((acc.n_operation+1)<>FData.n_operation) then begin
+    errors := 'Invalid n_operation';
+    Exit;
+  end;
+  if (FData.fee<=0) Or (FData.fee>CT_MaxTransactionFee) then begin
+    errors := 'Invalid fee '+Inttostr(FData.fee);
+    exit;
+  end;
+  if (acc.balance<FData.fee) then begin
+    errors := 'Insuficient founds';
+    exit;
+  end;
+  Result := AccountTransaction.TransferAmount(FData.account,FData.account,FData.n_operation,0,FData.fee,errors);
+end;
+
+function TOpRecoverFounds.GetOperationBufferToHash: TRawBytes;
+var ms : TMemoryStream;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.account,Sizeof(FData.account));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    ms.Position := 0;
+    SetLength(Result,ms.Size);
+    ms.ReadBuffer(Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+function TOpRecoverFounds.LoadFromStream(Stream: TStream): Boolean;
+begin
+  Result := false;
+  if Stream.Size - Stream.Position<16 then exit;
+  Stream.Read(FData.account,Sizeof(FData.account));
+  Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Read(FData.fee,Sizeof(FData.fee));
+  Result := true;
+end;
+
+function TOpRecoverFounds.OperationAmount: Int64;
+begin
+  Result := 0;
+end;
+
+function TOpRecoverFounds.OperationFee: UInt64;
+begin
+  Result := FData.fee;
+end;
+
+function TOpRecoverFounds.OperationPayload: TRawBytes;
+begin
+  Result := '';
+end;
+
+class function TOpRecoverFounds.OpType: Byte;
+begin
+  Result := CT_Op_Recover;
+end;
+
+function TOpRecoverFounds.SaveToStream(Stream: TStream): Boolean;
+begin
+  Stream.Write(FData.account,Sizeof(FData.account));
+  Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+  Stream.Write(FData.fee,Sizeof(FData.fee));
+  Result := true;
+end;
+
+function TOpRecoverFounds.SenderAccount: Cardinal;
+begin
+  Result := FData.account;
+end;
+
+function TOpRecoverFounds.toString: String;
+begin
+  Result := Format('Recover founds of account %s fee:%s (n_op:%d)',[
+    TAccountComp.AccountNumberToAccountTxtNumber(FData.account),
+    TAccountComp.FormatMoney(FData.fee),fData.n_operation]);
+end;
+
+initialization
+  TPCOperationsComp.RegisterOperationClass(TOpTransaction);
+  TPCOperationsComp.RegisterOperationClass(TOpChangeKey);
+  TPCOperationsComp.RegisterOperationClass(TOpRecoverFounds);
+end.

+ 156 - 0
Units/PascalCoin/UThread.pas

@@ -0,0 +1,156 @@
+unit UThread;
+
+{ 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, SyncObjs, Windows;
+
+Type
+  TPCThread = Class;
+  TPCThreadClass = Class of TPCThread;
+  TPCThread = Class(TThread)
+  private
+  protected
+    procedure DoTerminate; override;
+    procedure Execute; override;
+    procedure BCExecute; virtual; abstract;
+  public
+    Class function ThreadClassFound(tclass : TPCThreadClass; Exclude : TObject) : Integer;
+    Class function ThreadCount : Integer;
+    Class function GetThread(index : Integer) : TPCThread;
+    Class function TerminateAllThreads : Integer;
+    Class Procedure ProtectEnterCriticalSection(Const Sender : TObject; var Lock : TRTLCriticalSection);
+  End;
+
+implementation
+
+uses
+  SysUtils, ULog;
+
+{ TPCThread }
+
+Var _threads,_aux : TThreadList;
+
+procedure TPCThread.DoTerminate;
+begin
+  inherited;
+end;
+
+procedure TPCThread.Execute;
+Var l : TList;
+begin
+  _threads.Add(Self);
+  try
+    TLog.NewLog(ltdebug,Classname,'Starting Thread');
+    Try
+      Try
+        BCExecute;
+      Except
+        On E:Exception do begin
+          TLog.NewLog(lterror,Classname,'Exception inside a Thread ('+E.ClassName+'): '+E.Message);
+          Raise;
+        end;
+      End;
+    Finally
+      TLog.NewLog(ltdebug,Classname,'Finalizing Thread');
+    End;
+  finally
+    if (Assigned(_threads)) then begin
+      l := _threads.LockList;
+      Try
+        l.Remove(Self);
+      Finally
+        _threads.UnlockList;
+      End;
+    end;
+  end;
+end;
+
+class function TPCThread.GetThread(index: Integer): TPCThread;
+Var l : TList;
+begin
+  Result := Nil;
+  l := _threads.LockList;
+  try
+    if (index<0) or (index>=l.Count) then exit;
+    Result := TPCThread(l[index]);
+  finally
+    _threads.UnlockList;
+  end;
+end;
+
+class procedure TPCThread.ProtectEnterCriticalSection(Const Sender : TObject; var Lock: TRTLCriticalSection);
+begin
+  if Not TryEnterCriticalSection(Lock) then begin
+    TLog.NewLog(ltdebug,Sender.Classname,Format('Entering to a Locked critical section. LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
+      Lock.LockCount,Lock.RecursionCount,Lock.LockSemaphore,IntToHex(Lock.OwningThread,8) ]));
+    EnterCriticalSection(Lock);
+  end;
+end;
+
+class function TPCThread.TerminateAllThreads: Integer;
+Var l : TList;
+  i : Integer;
+begin
+  Result := -1;
+  if Not Assigned(_threads) then exit;
+  l := _threads.LockList;
+  try
+    for i :=l.Count - 1 downto 0 do begin
+      TPCThread(l[i]).Terminate;
+      if TPCThread(l[i]).Suspended then TPCThread(l[i]).Suspended := false;
+      TPCThread(l[i]).WaitFor;
+    end;
+    Result := l.Count;
+  finally
+    _threads.UnlockList;
+  end;
+end;
+
+class function TPCThread.ThreadClassFound(tclass: TPCThreadClass; Exclude : TObject): Integer;
+Var l : TList;
+begin
+  Result := -1;
+  if Not Assigned(_threads) then exit;
+  l := _threads.LockList;
+  try
+    for Result := 0 to l.Count - 1 do begin
+      if (TPCThread(l[Result]) is tclass) And ((l[Result])<>Exclude) then exit;
+    end;
+    Result := -1;
+  finally
+    _threads.UnlockList;
+  end;
+end;
+
+class function TPCThread.ThreadCount: Integer;
+Var l : TList;
+begin
+  l := _threads.LockList;
+  try
+    Result := l.Count;
+  finally
+    _threads.UnlockList;
+  end;
+end;
+
+initialization
+  _threads := TThreadList.Create;
+finalization
+  _aux := _threads;
+  _threads := Nil;
+  _aux.Free;
+end.

+ 86 - 0
Units/PascalCoin/UTime.pas

@@ -0,0 +1,86 @@
+unit UTime;
+
+{ 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 SysUtils, Windows;
+
+function TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation: PTimeZoneInformation; var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall;
+function SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation: PTimeZoneInformation; var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall;
+
+Function DateTime2UnivDateTime(d:TDateTime):TDateTime;
+Function UnivDateTime2LocalDateTime(d:TDateTime):TDateTime;
+
+function UnivDateTimeToUnix(dtDate: TDateTime): Longint;
+function UnixToUnivDateTime(USec: Longint): TDateTime;
+
+function UnixTimeToLocalElapsedTime(USec : Longint) : AnsiString;
+
+implementation
+
+
+function TzSpecificLocalTimeToSystemTime; external kernel32 name 'TzSpecificLocalTimeToSystemTime';
+function SystemTimeToTzSpecificLocalTime; external kernel32 name 'SystemTimeToTzSpecificLocalTime';
+
+const
+    UnixStartDate: TDateTime = 25569.0; // 01/01/1970
+
+function UnixTimeToLocalElapsedTime(USec : Longint) : AnsiString;
+Var diff, positivediff : Longint;
+Begin
+  diff := UnivDateTimeToUnix(DateTime2UnivDateTime(now)) - Usec;
+  if diff<0 then positivediff := diff * (-1)
+  else positivediff := diff;
+  if positivediff<60 then Result := inttostr(diff)+' seconds ago'
+  else if positivediff<(60*2) then Result := '1 minute ago'
+  else if positivediff<(60*60) then Result := inttostr(diff DIV 60)+' minutes ago'
+  else if positivediff<(60*60*2) then Result := '1 hour ago'
+  else if positivediff<(60*60*24) then Result := inttostr(diff DIV (60*60))+' hours ago'
+  else Result := inttostr(diff DIV (60*60*24))+' days ago';
+End;
+
+Function DateTime2UnivDateTime(d:TDateTime):TDateTime;
+var
+ TZI:TTimeZoneInformation;
+ LocalTime, UniversalTime:TSystemTime;
+begin
+  GetTimeZoneInformation(tzi);
+  DateTimeToSystemTime(d,LocalTime);
+  TzSpecificLocalTimeToSystemTime(@tzi,LocalTime,UniversalTime);
+  Result := SystemTimeToDateTime(UniversalTime);
+end;
+
+Function UnivDateTime2LocalDateTime(d:TDateTime):TDateTime;
+var
+ TZI:TTimeZoneInformation;
+ LocalTime, UniversalTime:TSystemTime;
+begin
+  GetTimeZoneInformation(tzi);
+  DateTimeToSystemTime(d,UniversalTime);
+  SystemTimeToTzSpecificLocalTime(@tzi,UniversalTime,LocalTime);
+  Result := SystemTimeToDateTime(LocalTime);
+end;
+
+function UnivDateTimeToUnix(dtDate: TDateTime): Longint;
+begin
+  Result := Round((dtDate - UnixStartDate) * 86400);
+end;
+
+function UnixToUnivDateTime(USec: Longint): TDateTime;
+begin
+  Result := (Usec / 86400) + UnixStartDate;
+end;
+
+end.

+ 319 - 0
Units/PascalCoin/UWalletKeys.pas

@@ -0,0 +1,319 @@
+unit UWalletKeys;
+
+{ 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, UBlockChain, UAccounts, UCrypto, ssl_types;
+
+Type
+  TWalletKey = Record
+    Name : AnsiString;
+    AccountKey : TAccountKey;
+    CryptedKey : TRawBytes;
+    PrivateKey : TECPrivateKey;
+  End;
+
+  TWalletKeys = Class(TComponent)
+  private
+    FKeys : TList;
+    FFileName: AnsiString;
+    FWalletPassword: AnsiString;
+    FWalletFileStream : TFileStream;
+    FIsValidPassword: Boolean;
+    FWalletFileName: AnsiString;
+    FIsReadingStream : Boolean;
+    FOnChanged: TNotifyEvent;
+    function GetKey(index: Integer): TWalletKey;
+    procedure SetWalletPassword(const Value: AnsiString);
+    Procedure GeneratePrivateKeysFromPassword;
+    procedure SetWalletFileName(const Value: AnsiString);
+  public
+    Property Key[index : Integer] : TWalletKey read GetKey; default;
+    Constructor Create(AOwner : TComponent);
+    Destructor destroy;
+    Procedure LoadFromStream(Stream : TStream);
+    Procedure SaveToStream(Stream : TStream);
+    Property IsValidPassword : Boolean read FIsValidPassword;
+    Property WalletPassword : AnsiString read FWalletPassword write SetWalletPassword;
+    Function AddPrivateKey(Const Name : AnsiString; ECPrivateKey : TECPrivateKey) : Integer;
+    Function AddPublicKey(Const Name : AnsiString; ECDSA_Public : TECDSA_Public) : Integer;
+    Function IndexOfAccountKey(AccountKey : TAccountKey) : Integer;
+    Procedure Delete(index : Integer);
+    Procedure Clear;
+    Function Count : Integer;
+    Property WalletFileName : AnsiString read FWalletFileName write SetWalletFileName;
+    Property OnChanged : TNotifyEvent read FOnChanged write FOnChanged;
+    Procedure SetName(index : Integer; Const newName : AnsiString);
+  End;
+
+Const CT_TWalletKey_NUL  : TWalletKey = (Name:'';AccountKey:(EC_OpenSSL_NID:0;x:'';y:'');CryptedKey:'';PrivateKey:Nil);
+
+implementation
+
+uses
+  SysUtils, UConst, ULog, UAES;
+
+Const
+  CT_PrivateKeyFile_Magic = 'TWalletKeys';
+  CT_PrivateKeyFile_Version = 100;
+
+{ TWalletKeys }
+
+Type PWalletKey = ^TWalletKey;
+
+function TWalletKeys.AddPrivateKey(Const Name : AnsiString; ECPrivateKey: TECPrivateKey): Integer;
+Var P : PWalletKey;
+  s : AnsiString;
+begin
+  Result := IndexOfAccountKey(ECPrivateKey.PublicKey);
+  if Result<0 then begin
+    New(P);
+    P^ := CT_TWalletKey_NUL;
+    P^.Name := Name;
+    P^.AccountKey := ECPrivateKey.PublicKey;
+    P^.CryptedKey := TAESComp.EVP_Encrypt_AES256(TCrypto.PrivateKey2Hexa(ECPrivateKey.PrivateKey),WalletPassword);
+    P^.PrivateKey := TECPrivateKey.Create;
+    P^.PrivateKey.SetPrivateKeyFromHexa(ECPrivateKey.EC_OpenSSL_NID, TCrypto.PrivateKey2Hexa(ECPrivateKey.PrivateKey));
+    Result := FKeys.Add(P);
+  end else begin
+    P := Fkeys[Result];
+    P^.Name := Name;
+  end;
+  if Not FIsReadingStream then SaveToStream(FWalletFileStream);
+  if Assigned(FOnChanged) then  FOnChanged(Self);
+end;
+
+function TWalletKeys.AddPublicKey(const Name: AnsiString; ECDSA_Public: TECDSA_Public): Integer;
+Var P : PWalletKey;
+begin
+  Result := IndexOfAccountKey(ECDSA_Public);
+  if Result<0 then begin
+    New(P);
+    P^ := CT_TWalletKey_NUL;
+    P^.Name := Name;
+    P^.AccountKey := ECDSA_Public;
+    P^.PrivateKey := Nil;
+    Result := FKeys.Add(P);
+  end else begin
+    P := Fkeys[Result];
+    P^.Name := Name;
+  end;
+  if Not FIsReadingStream then SaveToStream(FWalletFileStream);
+  if Assigned(FOnChanged) then  FOnChanged(Self);
+end;
+
+procedure TWalletKeys.Clear;
+Var P : PWalletKey;
+  i : Integer;
+begin
+  for i := FKeys.Count - 1 downto 0 do begin
+    P := FKeys[i];
+    P^.PrivateKey.Free;
+    Dispose(P);
+  end;
+  FKeys.Clear;
+  FIsValidPassword := true;
+end;
+
+function TWalletKeys.Count: Integer;
+begin
+  Result := FKeys.Count;
+end;
+
+constructor TWalletKeys.Create(AOwner : TComponent);
+begin
+  inherited;
+  FIsValidPassword := false;
+  FWalletFileStream := Nil;
+  FWalletPassword := '';
+  FKeys := TList.Create;
+  FIsReadingStream := false;
+  FOnChanged := Nil;
+end;
+
+procedure TWalletKeys.Delete(index: Integer);
+Var P : PWalletKey;
+begin
+  P := FKeys[index];
+  P^.PrivateKey.Free;
+  Dispose(P);
+  FKeys.Delete(index);
+  SaveToStream(FWalletFileStream);
+  if Assigned(FOnChanged) then FOnChanged(Self);
+end;
+
+destructor TWalletKeys.destroy;
+begin
+  FOnChanged := Nil;
+  FreeAndNil(FWalletFileStream);
+  Clear;
+  FKeys.Free;
+end;
+
+procedure TWalletKeys.GeneratePrivateKeysFromPassword;
+Var i : Integer;
+ P : PWalletKey;
+ s : TRawBytes;
+ isOk : Boolean;
+begin
+  FIsValidPassword := false;
+  isOk := true;
+  for i := 0 to FKeys.Count - 1 do begin
+    P := FKeys[i];
+    FreeAndNil(P^.PrivateKey);
+  end;
+  // try to unencrypt
+  for i := 0 to FKeys.Count - 1 do begin
+    P := FKeys[i];
+    if P^.CryptedKey<>'' then begin
+      isOk := TAESComp.EVP_Decrypt_AES256( P^.CryptedKey, FWalletPassword, s );
+      If isOk then begin
+        P^.PrivateKey := TECPrivateKey.Create;
+        try
+          P^.PrivateKey.SetPrivateKeyFromHexa(P^.AccountKey.EC_OpenSSL_NID,s);
+        except on E: Exception do begin
+            P^.PrivateKey.Free;
+            P^.PrivateKey := Nil;
+            TLog.NewLog(lterror,ClassName,Format('Fatal error when generating EC private key %d/%d: %s',[i+1,FKeys.Count,E.Message]));
+            exit;
+          end;
+        end;
+      end;
+    end;// else isOk := false;
+  end;
+  FIsValidPassword := isOk;
+end;
+
+function TWalletKeys.GetKey(index: Integer): TWalletKey;
+begin
+  Result := PWalletKey(FKeys[index])^;
+end;
+
+function TWalletKeys.IndexOfAccountKey(AccountKey: TAccountKey): Integer;
+begin
+  for result := 0 to FKeys.Count - 1 do begin
+    if TAccountComp.equal( PWalletKey(FKeys[result])^.AccountKey, AccountKey) then exit;
+  end;
+  Result := -1;
+end;
+
+procedure TWalletKeys.LoadFromStream(Stream: TStream);
+Var fileversion,i,l : Integer;
+  s : AnsiString;
+  P : PWalletKey;
+begin
+  Clear;
+  FIsValidPassword := false;
+  FIsReadingStream := true;
+  try
+    if Stream.Size - Stream.Position > 0 then begin
+      TStreamOp.ReadAnsiString(Stream,s);
+      if Not AnsiSameStr(s,CT_PrivateKeyFile_Magic) then raise Exception.Create('Invalid '+Classname+' stream');
+      // Read version:
+      Stream.Read(fileversion,4);
+      if (fileversion<>CT_PrivateKeyFile_Version) then begin
+        // Old version
+        Stream.Position := Stream.Position-4;
+        TLog.NewLog(lterror,ClassName,'Invalid PrivateKeys file version: '+Inttostr(fileversion));
+      end;
+      Stream.Read(l,4);
+      for i := 0 to l - 1 do begin
+        New(P);
+        P^ := CT_TWalletKey_NUL;
+        TStreamOp.ReadAnsiString(Stream,P^.Name);
+        Stream.Read(P^.AccountKey.EC_OpenSSL_NID,sizeof(P^.AccountKey.EC_OpenSSL_NID));
+        TStreamOp.ReadAnsiString(Stream,P^.AccountKey.x);
+        TStreamOp.ReadAnsiString(Stream,P^.AccountKey.y);
+        TStreamOp.ReadAnsiString(Stream,P^.CryptedKey);
+        P^.PrivateKey := Nil;
+        FKeys.Add(P);
+      end;
+    end;
+    GeneratePrivateKeysFromPassword;
+  finally
+    FIsReadingStream := false;
+  end;
+  if Assigned(FOnChanged) then FOnChanged(Self);
+end;
+
+procedure TWalletKeys.SaveToStream(Stream: TStream);
+var i : Integer;
+  P : PWalletKey;
+begin
+  if FIsReadingStream then exit;
+  if Not Assigned(Stream) then exit;
+  Stream.Size := 0;
+  TStreamOp.WriteAnsiString(Stream,CT_PrivateKeyFile_Magic);
+  i := CT_PrivateKeyFile_Version;
+  Stream.Write(i,4);
+  i := FKeys.Count;
+  Stream.Write(i,4);
+  for i := 0 to FKeys.Count - 1 do begin
+    P := FKeys[i];
+    TStreamOp.WriteAnsiString(Stream,P^.Name);
+    Stream.Write(P^.AccountKey.EC_OpenSSL_NID,sizeof(P^.AccountKey.EC_OpenSSL_NID));
+    TStreamOp.WriteAnsiString(Stream,P^.AccountKey.x);
+    TStreamOp.WriteAnsiString(Stream,P^.AccountKey.y);
+    TStreamOp.WriteAnsiString(Stream,P^.CryptedKey);
+  end;
+end;
+
+procedure TWalletKeys.SetName(index: Integer; const newName: AnsiString);
+begin
+  if PWalletKey(FKeys[index])^.Name=newName then exit;
+  PWalletKey(FKeys[index])^.Name := newName;
+  SaveToStream(FWalletFileStream);
+  if Assigned(FOnChanged) then  FOnChanged(Self);
+end;
+
+procedure TWalletKeys.SetWalletFileName(const Value: AnsiString);
+var fm : Word;
+begin
+  if FWalletFileName = Value then exit;
+  FWalletFileName := Value;
+  if Assigned(FWalletFileStream) then FWalletFileStream.Free;
+  FWalletFileStream := Nil;
+  if Value<>'' then begin
+    if FileExists(Value) then fm := fmOpenReadWrite
+    else fm := fmCreate;
+    FWalletFileStream := TFileStream.Create(WalletfileName,fm+fmShareDenyWrite);
+    LoadFromStream(FWalletFileStream);
+  end;
+end;
+
+procedure TWalletKeys.SetWalletPassword(const Value: AnsiString);
+Var i : Integer;
+  P : PWalletKey;
+begin
+  if FWalletPassword=Value then exit;
+  FWalletPassword := Value;
+  for i := 0 to FKeys.Count - 1 do begin
+    P := FKeys[i];
+    If Assigned(P^.PrivateKey) then begin
+      P^.CryptedKey := TAESComp.EVP_Encrypt_AES256(TCrypto.PrivateKey2Hexa(P^.PrivateKey.PrivateKey),FWalletPassword);
+    end else begin
+      if FIsValidPassword then begin
+        TLog.NewLog(lterror,Classname,Format('Fatal error: Private key not found %d/%d',[i+1,FKeys.Count]));
+      end;
+      FIsValidPassword := false;
+    end;
+  end;
+  // Try if password is Ok
+  GeneratePrivateKeysFromPassword;
+  if FIsValidPassword then SaveToStream(FWalletFileStream);
+end;
+
+end.

+ 215 - 0
Units/Utils/UAES.pas

@@ -0,0 +1,215 @@
+unit UAES;
+
+{ 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
+
+  }
+
+{ This unit is used to encrypt/decrypt using AES256
+  Basic source code found at internet:
+  http://stackoverflow.com/questions/9723963/delphi-pascal-example-for-calling-openssl-evp-functions
+
+  Original source code probably copyright of Marco Ferrante ([email protected]) and "shunty" user:
+  http://stackoverflow.com/users/197962/shunty
+
+  }
+
+interface
+
+uses
+  SysUtils, UCrypto;
+
+Type
+  TAESComp = Class
+  private
+  public
+    Class function EVP_Encrypt_AES256(Value: TBytes; APassword: TBytes): TBytes; overload;
+    Class function EVP_Decrypt_AES256(const Value: TBytes; APassword: TBytes; var Decrypted: TBytes) : Boolean; overload;
+    Class function EVP_Encrypt_AES256(Const TheMessage, APassword : AnsiString): AnsiString; overload;
+    Class function EVP_Decrypt_AES256(const EncryptedMessage: TRawBytes; APassword: AnsiString; var Decrypted : AnsiString) : Boolean; overload;
+  End;
+
+implementation
+
+uses
+  ssl_const, ssl_bn, ssl_types, ssl_evp, Windows;
+
+
+CONST SALT_MAGIC: AnsiString = 'Salted__'; SALT_MAGIC_LEN: integer = 8; SALT_SIZE = 8;
+
+function EVP_GetSalt: TBytes;
+begin
+  SetLength(result, PKCS5_SALT_LEN);
+  RAND_pseudo_bytes(@result[0], PKCS5_SALT_LEN);
+end;
+
+Function EVP_GetKeyIV(APassword: TBytes; ACipher: PEVP_CIPHER; const ASalt: TBytes; out Key, IV: TBytes) : Boolean;
+var
+  ctx: EVP_MD_CTX;
+  hash: PEVP_MD;
+  mdbuff: TBytes;
+  mds: integer;
+  nkey, niv: integer;
+begin
+  Result := false;
+  hash := EVP_sha256;
+  mds := 0;
+  SetLength(mdbuff, EVP_MAX_MD_SIZE);
+
+  nkey := ACipher.key_len;
+  niv := ACipher.iv_len;
+  SetLength(Key, nkey);
+  SetLength(IV, nkey);  // Max size to start then reduce it at the end
+
+  Assert(hash.md_size >= nkey);
+  Assert(hash.md_size >= niv);
+
+  // This is pretty much the same way that EVP_BytesToKey works. But that
+  // allows multiple passes through the hashing loop and also allows to
+  // choose different hashing methods. We have no need for this. The
+  // OpenSSL docs say it is out of date and internet sources suggest using
+  // something like PKCS5_v2_PBE_keyivgen and/or PKCS5_PBKDF2_HMAC_SHA1
+  // but this method is easy to port to the DEC and DCP routines and easy to
+  // use in other environments. Ultimately the Key and IV rely on the password
+  // and the salt and can be easily reformed.
+
+  // This method relies on the fact that the hashing method produces a key of
+  // the correct size. EVP_BytesToKey goes through muptiple hashing passes if
+  // necessary to make the key big enough when using smaller hashes.
+
+  EVP_MD_CTX_init(@ctx);
+  try
+    // Key first
+    If EVP_DigestInit_ex(@ctx, hash, nil)<>1 then exit;
+    If EVP_DigestUpdate(@ctx, @APassword[0], Length(APassword))<>1 then exit;
+    if (ASalt <> nil) then begin
+      if EVP_DigestUpdate(@ctx, @ASalt[0], Length(ASalt))<>1 then exit;
+    end;
+    if (EVP_DigestFinal_ex(@ctx, @Key[0], mds)<>1) then exit;
+
+    // Derive IV next
+    If EVP_DigestInit_ex(@ctx, hash, nil)<>1 then exit;
+    If EVP_DigestUpdate(@ctx, @Key[0], mds)<>1 then exit;
+    If EVP_DigestUpdate(@ctx, @APassword[0], Length(APassword))<>1 then exit;
+    if (ASalt <> nil) then begin
+      if EVP_DigestUpdate(@ctx, @ASalt[0], Length(ASalt))<>1 then exit;
+    end;
+    If EVP_DigestFinal_ex(@ctx, @IV[0], mds)<>1 then exit;
+
+    SetLength(IV, niv);
+    Result := true;
+  finally
+    EVP_MD_CTX_cleanup(@ctx);
+  end;
+end;
+
+{ TAESComp }
+
+class function TAESComp.EVP_Decrypt_AES256(const EncryptedMessage: TRawBytes; APassword: AnsiString; var Decrypted : AnsiString) : Boolean;
+Var bytes_encrypted, bytes_password, bytes_result : TBytes;
+begin
+  SetLength(bytes_encrypted,length(EncryptedMessage));
+  CopyMemory(bytes_encrypted,@EncryptedMessage[1],length(EncryptedMessage));
+  SetLength(bytes_password,length(APassword));
+  CopyMemory(bytes_password,@APassword[1],length(APassword));
+  Result := EVP_Decrypt_AES256(bytes_encrypted,bytes_password,bytes_result);
+  if Result then begin
+    SetLength(Decrypted,length(bytes_result));
+    CopyMemory(@Decrypted[1],bytes_result,length(bytes_result));
+  end else Decrypted := '';
+end;
+
+class function TAESComp.EVP_Decrypt_AES256(const Value: TBytes; APassword: TBytes; var Decrypted: TBytes) : Boolean;
+var
+  cipher: PEVP_CIPHER;
+  ctx: EVP_CIPHER_CTX;
+  salt, key, iv, buf: TBytes;
+  src_start, buf_start, out_len: integer;
+begin
+  Result := false;
+  cipher := EVP_aes_256_cbc;
+  SetLength(salt, SALT_SIZE);
+  // First read the magic text and the salt - if any
+  if (length(Value)>=SALT_MAGIC_LEN) AND (AnsiString(TEncoding.ASCII.GetString(Value, 0, SALT_MAGIC_LEN)) = SALT_MAGIC) then
+  begin
+    Move(Value[SALT_MAGIC_LEN], salt[0], SALT_SIZE);
+    If Not EVP_GetKeyIV(APassword, cipher, salt, key, iv) then exit;
+    src_start := SALT_MAGIC_LEN + SALT_SIZE;
+  end
+  else
+  begin
+    If Not EVP_GetKeyIV(APassword, cipher, nil, key, iv) then exit;
+    src_start := 0;
+  end;
+
+  EVP_CIPHER_CTX_init(@ctx);
+  try
+    If EVP_DecryptInit(@ctx, cipher, @key[0], @iv[0])<>1 then exit;
+    SetLength(buf, Length(Value));
+    buf_start := 0;
+    If EVP_DecryptUpdate(@ctx, @buf[buf_start], out_len, @Value[src_start], Length(Value) - src_start)<>1 then exit;
+    Inc(buf_start, out_len);
+    If EVP_DecryptFinal(@ctx, @buf[buf_start], out_len)<>1 then exit;
+    Inc(buf_start, out_len);
+    SetLength(buf, buf_start);
+    Decrypted := buf;
+    Result := true;
+  finally
+    EVP_CIPHER_CTX_cleanup(@ctx);
+  end;
+end;
+
+class function TAESComp.EVP_Encrypt_AES256(const TheMessage, APassword: AnsiString): AnsiString;
+Var bytes_message, bytes_password, bytes_result : TBytes;
+begin
+  SetLength(bytes_message,length(TheMessage));
+  CopyMemory(bytes_message,@TheMessage[1],length(TheMessage));
+  SetLength(bytes_password,length(APassword));
+  CopyMemory(bytes_password,@APassword[1],length(APassword));
+  bytes_result := EVP_Encrypt_AES256(bytes_message,bytes_password);
+  SetLength(Result,length(bytes_result));
+  CopyMemory(@Result[1],bytes_result,length(bytes_result));
+end;
+
+class function TAESComp.EVP_Encrypt_AES256(Value, APassword: TBytes): TBytes;
+var
+  cipher: PEVP_CIPHER;
+  ctx: EVP_CIPHER_CTX;
+  salt, key, iv, buf: TBytes;
+  block_size: integer;
+  buf_start, out_len: integer;
+begin
+  cipher := EVP_aes_256_cbc;
+  salt := EVP_GetSalt;
+  EVP_GetKeyIV(APassword, cipher, salt, key, iv);
+
+  EVP_CIPHER_CTX_init(@ctx);
+  try
+    EVP_EncryptInit(@ctx, cipher, @key[0], @iv[0]);
+    block_size := EVP_CIPHER_CTX_block_size(@ctx);
+    SetLength(buf, Length(Value) + block_size + SALT_MAGIC_LEN + PKCS5_SALT_LEN);
+    buf_start := 0;
+    Move(PAnsiChar(SALT_MAGIC)^, buf[buf_start], SALT_MAGIC_LEN);
+    Inc(buf_start, SALT_MAGIC_LEN);
+    Move(salt[0], buf[buf_start], PKCS5_SALT_LEN);
+    Inc(buf_start, PKCS5_SALT_LEN);
+    EVP_EncryptUpdate(@ctx, @buf[buf_start], out_len, @Value[0], Length(Value));
+    Inc(buf_start, out_len);
+    EVP_EncryptFinal(@ctx, @buf[buf_start], out_len);
+    Inc(buf_start, out_len);
+    SetLength(buf, buf_start);
+    result := buf;
+  finally
+    EVP_CIPHER_CTX_cleanup(@ctx);
+  end;
+end;
+
+end.

+ 480 - 0
Units/Utils/UAppParams.pas

@@ -0,0 +1,480 @@
+unit UAppParams;
+
+{ 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;
+
+Type
+  TAppParamType = (ptString, ptInteger, ptLongWord, ptInt64, ptBoolean, ptStream);
+
+  TAppParams = Class;
+
+  TAppParam = Class
+    FAppParams : TAppParams;
+    Function LoadFromStream(Stream : TStream) : Boolean;
+    Procedure SaveToStream(Stream : TStream);
+  private
+    FParamName: AnsiString;
+    FValue: Variant;
+    FParamType: TAppParamType;
+    procedure SetParamName(const Value: AnsiString);
+    procedure SetValue(const Value: Variant);
+    procedure SetParamType(const Value: TAppParamType);
+    function GetIsNull: Boolean;
+  protected
+  published
+  public
+    Constructor Create(AParamName : AnsiString);
+    Property ParamName : AnsiString read FParamName write SetParamName;
+    Property Value : Variant read FValue write SetValue;
+    Property ParamType : TAppParamType read FParamType write SetParamType;
+    Procedure SetAsInteger(IntValue : Integer);
+    Procedure SetAsCardinal(CardValue : Cardinal);
+    Procedure SetAsString(StringValue : AnsiString);
+    Procedure SetAsInt64(Int64Value : Int64);
+    Procedure SetAsBoolean(BoolValue : Boolean);
+    Procedure SetAsStream(Stream : TStream);
+    Property IsNull : Boolean read GetIsNull;
+    function GetAsString(Const DefValue : AnsiString): AnsiString;
+    function GetAsBoolean(Const DefValue : Boolean): Boolean;
+    function GetAsInteger(Const DefValue : Integer): Integer;
+    function GetAsInt64(Const DefValue : Int64): Int64;
+    function GetAsStream(Stream : TStream) : Integer;
+  End;
+
+  TAppParams = Class(TComponent)
+  private
+    FParamsStream : TFileStream;
+    FParams : TList;
+    FFileName: AnsiString;
+    Function LoadFromStream(Stream : TStream) : Boolean;
+    Procedure SaveToStream(Stream : TStream);
+    function GetParam(ParamName: AnsiString): TAppParam;
+    Procedure InternalClear;
+    Function IndexOfParam(Const ParamName : AnsiString) : Integer;
+    procedure SetFileName(const Value: AnsiString);
+    Procedure Save;
+  protected
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Class function AppParams : TAppParams;
+    Property FileName : AnsiString read FFileName write SetFileName;
+    Property ParamByName[ParamName : AnsiString] : TAppParam read GetParam;
+    Procedure Clear;
+    Procedure Delete(Const ParamName : AnsiString);
+    Function Count : Integer;
+    Function Param(index : Integer) : TAppParam;
+    Function FindParam(Const ParamName : AnsiString) : TAppParam;
+  End;
+
+implementation
+
+uses
+  Variants, UAccounts, SysUtils;
+
+Const
+  CT_AppParams_File_Magic = 'TAppParams';
+
+Var _appParams : TAppParams;
+
+{ TAppParam }
+
+constructor TAppParam.Create(AParamName: AnsiString);
+begin
+  FAppParams := Nil;
+  FParamName := AParamName;
+  FValue := Null;
+end;
+
+function TAppParam.GetAsBoolean(const DefValue: Boolean): Boolean;
+begin
+  if IsNull then Result := DefValue
+  else begin
+    Try
+      Result := FValue;
+    Except
+      Result := DefValue;
+    End;
+  end;
+end;
+
+function TAppParam.GetAsInt64(const DefValue: Int64): Int64;
+begin
+  if IsNull then Result := DefValue
+  else begin
+    Try
+      Result := FValue;
+    Except
+      Result := DefValue;
+    End;
+  end;
+end;
+
+function TAppParam.GetAsInteger(const DefValue: Integer): Integer;
+begin
+  if IsNull then Result := DefValue
+  else begin
+    Try
+      Result := FValue;
+    Except
+      Result := DefValue;
+    End;
+  end;
+end;
+
+function TAppParam.GetAsStream(Stream: TStream): Integer;
+var s : AnsiString;
+begin
+  Stream.Size := 0;
+  if IsNull then Result := 0
+  else begin
+    s := VarToStrDef(FValue,'');
+    Stream.Size := 0;
+    Stream.WriteBuffer(s[1],length(s));
+    Stream.Position := 0;
+  end;
+end;
+
+function TAppParam.GetAsString(Const DefValue : AnsiString): AnsiString;
+begin
+  if IsNull then Result := DefValue
+  else Result := VarToStrDef(FValue,DefValue);
+end;
+
+function TAppParam.GetIsNull: Boolean;
+begin
+  Result := VarIsNull( FValue );
+end;
+
+Function TAppParam.LoadFromStream(Stream: TStream)  : Boolean;
+Var bpt : Byte;
+  pt : TAppParamType;
+  s : AnsiString;
+  i : Integer;
+  c : Cardinal;
+  i64 : Int64;
+begin
+  Result := false;
+  if TStreamOp.ReadAnsiString(Stream,FParamName)<0 then exit;
+  Stream.Read(bpt,1);
+  if (bpt>=Integer(low(pt))) And (bpt<=Integer(high(pt))) then pt := TAppParamType(bpt)
+  else pt := ptString;
+  FParamType := pt;
+  Stream.Read(bpt,1);
+  if bpt=0 then FValue := Null
+  else begin
+    case pt of
+      ptString : begin
+        If TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+        FValue := s;
+      end;
+      ptInteger : begin
+        if Stream.Read(i,sizeof(i))<sizeof(i) then exit;
+        FValue := i;
+      end;
+      ptLongWord : Begin
+        if Stream.Read(c,sizeof(c))<sizeof(c) then exit;
+        FValue := c;
+      End;
+      ptInt64 : Begin
+        if Stream.Read(i64,sizeof(i64))<sizeof(i64) then exit;
+        FValue := i64;
+      End;
+      ptBoolean : Begin
+        if Stream.Read(bpt,sizeof(bpt))<sizeof(bpt) then exit;
+        if bpt=0 then FValue := false
+        else FValue := true;
+      End;
+      ptStream : Begin
+        if TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+        FValue := s;
+      End
+    else
+      raise Exception.Create('Development error 20160613-1');
+    end;
+  end;
+  Result := true;
+end;
+
+procedure TAppParam.SaveToStream(Stream: TStream);
+var b : Byte;
+  i : Integer;
+  c : Cardinal;
+  i64 : Int64;
+begin
+  TStreamOp.WriteAnsiString(Stream,FParamName);
+  b := Byte(FParamType);
+  Stream.Write(b,1);
+  if IsNull then begin
+    b := 0;
+    Stream.Write(b,1);
+  end else begin
+    b := 1;
+    Stream.Write(b,1);
+    case FParamType of
+      ptString : begin
+        TStreamOp.WriteAnsiString(Stream,VarToStr(FValue));
+      end;
+      ptInteger : begin
+        i := FValue;
+        Stream.Write(i,sizeof(i));
+      end;
+      ptLongWord : begin
+        c := FValue;
+        Stream.Write(c,sizeof(c));
+      end;
+      ptInt64 : begin
+        i64 := FValue;
+        Stream.Write(i64,sizeof(i64));
+      end;
+      ptBoolean : Begin
+        if FValue then b := 1
+        else b := 0;
+        Stream.Write(b,sizeof(b));
+      End;
+      ptStream : Begin
+        TStreamOp.WriteAnsiString(Stream,VarToStrDef(FValue,''));
+      End
+    else
+      raise Exception.Create('Development error 20160613-2');
+    end;
+  end;
+end;
+
+procedure TAppParam.SetAsBoolean(BoolValue: Boolean);
+begin
+  FParamType := ptBoolean;
+  FValue := BoolValue;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetAsCardinal(CardValue: Cardinal);
+begin
+  FParamType := ptLongWord;
+  FValue := CardValue;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetAsInt64(Int64Value: Int64);
+begin
+  FParamType := ptInt64;
+  FValue := Int64Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetAsInteger(IntValue: Integer);
+begin
+  FParamType := ptInteger;
+  FValue := IntValue;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetAsStream(Stream: TStream);
+var s : AnsiString;
+begin
+  Stream.Position := 0;
+  setlength(s,Stream.Size);
+  Stream.ReadBuffer(s[1],Stream.Size);
+  FParamType := ptString;
+  FValue := s;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetAsString(StringValue: AnsiString);
+begin
+  FParamType := ptString;
+  FValue := StringValue;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetParamName(const Value: AnsiString);
+begin
+  FParamName := Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetParamType(const Value: TAppParamType);
+begin
+  FParamType := Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+procedure TAppParam.SetValue(const Value: Variant);
+begin
+  FValue := Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
+{ TAppParams }
+
+class function TAppParams.AppParams: TAppParams;
+begin
+  if Not Assigned(_appParams) then begin
+    _appParams := TAppParams.Create(Nil);
+  end;
+  Result := _appParams;
+end;
+
+procedure TAppParams.Clear;
+begin
+  InternalClear;
+  Save;
+end;
+
+function TAppParams.Count: Integer;
+begin
+  Result := FParams.Count;
+end;
+
+constructor TAppParams.Create(AOwner: TComponent);
+begin
+  inherited;
+  FParams := TList.Create;
+  FFileName := '';
+  FParamsStream := Nil;
+  if _appParams=Nil then _appParams := Self;
+
+end;
+
+procedure TAppParams.Delete(const ParamName: AnsiString);
+Var P : TAppParam;
+  i : Integer;
+begin
+  i := IndexOfParam(ParamName);
+  if i<0 then exit;
+  P := FParams[i];
+  FParams.Delete(i);
+  P.Free;
+  Save;
+end;
+
+destructor TAppParams.Destroy;
+begin
+  FParamsStream.Free;
+  InternalClear;
+  FParams.Free;
+  inherited;
+  if _appParams=Self then _appParams := Nil;
+
+end;
+
+function TAppParams.FindParam(const ParamName: AnsiString): TAppParam;
+Var i : Integer;
+begin
+  i := IndexOfParam(ParamName);
+  if i>=0 then Result := FParams[i]
+  else Result := Nil;
+end;
+
+function TAppParams.GetParam(ParamName: AnsiString): TAppParam;
+Var i : Integer;
+  P : TAppParam;
+begin
+  i := IndexOfParam(ParamName);
+  if i<0 then begin
+    P := TAppParam.Create(ParamName);
+    P.FAppParams := Self;
+    FParams.Add(P);
+  end else P := FParams[i];
+  Result := P;
+end;
+
+function TAppParams.IndexOfParam(const ParamName: AnsiString): Integer;
+begin
+  for Result := 0 to FParams.Count - 1 do begin
+    if AnsiSameText(ParamName,TAppParam(FParams[Result]).ParamName) then exit;
+  end;
+  Result := -1;
+end;
+
+procedure TAppParams.InternalClear;
+Var P : TAppParam;
+  i : Integer;
+begin
+  for i := 0 to FParams.Count - 1 do begin
+    P := FParams[i];
+    P.Free;
+  end;
+  FParams.Clear;
+end;
+
+function TAppParams.LoadFromStream(Stream: TStream): Boolean;
+Var s : AnsiString;
+  i,c : Integer;
+  P : TAppParam;
+begin
+  Result := false;
+  InternalClear;
+  If TStreamOp.ReadAnsiString(Stream,s)<0 then exit;
+  If s<>CT_AppParams_File_Magic then raise Exception.Create('Invalid file type');
+  Stream.Read(c,sizeof(c));
+  for i := 0 to c-1 do begin
+    P := TAppParam(TAppParam.NewInstance);
+    P.FAppParams := Self;
+    FParams.Add(P);
+    If Not P.LoadFromStream(Stream) then exit;
+  end;
+  Result := true;
+end;
+
+function TAppParams.Param(index: Integer): TAppParam;
+begin
+  Result := TAppParam(FParams[index]);
+end;
+
+procedure TAppParams.Save;
+begin
+  if Assigned(FParamsStream) then begin
+    FParamsStream.Position := 0;
+    FParamsStream.Size := 0;
+    SaveToStream(FParamsStream);
+  end;
+end;
+
+procedure TAppParams.SaveToStream(Stream: TStream);
+Var s : AnsiString;
+  i : Integer;
+begin
+  s := CT_AppParams_File_Magic;
+  TStreamOp.WriteAnsiString(Stream,s);
+  i := FParams.Count;
+  Stream.Write(i,sizeof(i));
+  for i := 0 to FParams.Count - 1 do begin
+    TAppParam(FParams[i]).SaveToStream(Stream);
+  end;
+end;
+
+procedure TAppParams.SetFileName(const Value: AnsiString);
+Var fm : Word;
+begin
+  if FFileName=Value then exit;
+  if Assigned(FParamsStream) then FParamsStream.Free;
+  FParamsStream := Nil;
+  FFileName := Value;
+  if Value<>'' then begin
+    if FileExists(Value) then fm := fmOpenReadWrite
+    else fm := fmCreate;
+
+    FParamsStream := TFileStream.Create(Value,fm+fmShareExclusive);
+    LoadFromStream(FParamsStream);
+  end;
+end;
+
+initialization
+  _appParams := Nil;
+end.

+ 155 - 0
Units/Utils/UFolderHelper.pas

@@ -0,0 +1,155 @@
+unit UFolderHelper;
+
+{ 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
+
+Type TFileVersionInfo = record
+       HasInfo : Boolean;
+       CompanyName : String;
+       FileDescription : String;
+       FileVersion : String;
+       InternalName : String;
+       LegalCopyRight : String;
+       LegalTradeMarks : String;
+       OriginalFileName : String;
+       ProductName : String;
+       ProductVersion : String;
+       Comments : String;
+       Debug : Boolean;
+       Pre_Release : Boolean;
+       Patched : Boolean;
+       PrivateBuild : Boolean;
+       InfoInferred : Boolean;
+       SpecialBuild : Boolean;
+     End;
+
+  TFolderHelper = record
+  strict private
+    class function GetFolder(const aCSIDL: Integer): string; static;
+    class function GetAppDataFolder : string; static;
+    class function GetUserDataFolder : string; static;
+  public
+    class function GetPascalCoinDataFolder : string; static;
+    class Function GetTFileVersionInfo(Const FileName : String) : TFileVersionInfo; static;
+  end;
+
+implementation
+
+uses
+  ShlObj, Windows, SysUtils;
+
+function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle;
+  dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;
+  forward;
+function SHGetFolderPath; external 'SHFolder.dll' name 'SHGetFolderPathW';
+
+class function TFolderHelper.GetAppDataFolder: string;
+begin
+  Result := GetFolder(CSIDL_APPDATA); //User Name / AppData
+end;
+
+class function TFolderHelper.GetFolder(const aCSIDL: Integer): string;
+var
+  FolderPath: array[0 .. MAX_PATH] of Char;
+begin
+  Result := '';
+  SetLastError(ERROR_SUCCESS);
+  if SHGetFolderPath(0, aCSIDL, 0, 0, @FolderPath) = S_OK then
+    Result := FolderPath;
+end;
+
+class function TFolderHelper.GetPascalCoinDataFolder: string;
+begin
+  Result := GetAppDataFolder+'\PascalCoin';
+end;
+
+class function TFolderHelper.GetTFileVersionInfo(Const FileName: String): TFileVersionInfo;
+Var verInfoSize : DWord;
+    GetInfoSizeJunk : DWord;
+    VersionInfo,
+    Translation,
+    InfoPointer : Pointer;
+    VersionInfoSize: UINT;
+    VersionValue :  string;
+Begin
+   With result do
+   Begin
+     HasInfo := False;
+     CompanyName := '';
+     FileDescription := '';
+     FileVersion := '';
+     InternalName := '';
+     LegalCopyRight := '';
+     LegalTradeMarks := '';
+     OriginalFileName := '';
+     ProductName := '';
+     ProductVersion := '';
+     Comments := '';
+     Debug := False;
+     Pre_Release := False;
+     Patched := False;
+     PrivateBuild := False;
+     InfoInferred := False;
+     SpecialBuild := False;
+   End;
+   VerInfoSize := GetFileVersionInfoSize(PChar(FileName),GetInfoSizeJunk);
+   If verInfoSize>0 Then
+     Begin
+       Result.HasInfo := True;
+       GetMem(VersionInfo,VerInfoSize);
+       GetFileVersionInfo(PChar(FileName),0,VerInfoSize,VersionInfo);
+       VerQueryValue(VersionInfo,'\\VarFileInfo\\Translation',Translation,VersionInfoSize);
+       VersionValue := '\\StringFileInfo\\'+
+         inttohex(LoWord(LongInt(Translation^)),4)+
+         inttohex(HiWord(LongInt(Translation^)),4)+ '\\';
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'CompanyName'),InfoPointer,VersionInfoSize) Then
+         Result.CompanyName := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'FileDescription'),InfoPointer,VersionInfoSize) Then
+         Result.FileDescription := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'FileVersion'),InfoPointer,VersionInfoSize) Then
+         Result.FileVersion := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'InternalName'),InfoPointer,VersionInfoSize) Then
+         Result.InternalName := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'LegalCopyright'),InfoPointer,VersionInfoSize) Then
+         Result.LegalCopyRight := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'LegalTrademarks'),InfoPointer,VersionInfoSize) Then
+         Result.LegalTradeMarks := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'OriginalFilename'),InfoPointer,VersionInfoSize) Then
+         Result.OriginalFileName := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'ProductName'),InfoPointer,VersionInfoSize) Then
+         Result.ProductName := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'ProductVersion'),InfoPointer,VersionInfoSize) Then
+         Result.ProductVersion := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,PChar(VersionValue+'Comments'),InfoPointer,VersionInfoSize) Then
+         Result.Comments := String(PChar(InfoPointer));
+       If VerQueryValue(VersionInfo,'\',InfoPointer,VersionInfoSize) Then
+         Begin
+           Result.Debug := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_DEBUG);
+           Result.Pre_Release := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_PRERELEASE);
+           Result.Patched := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_PATCHED);
+           Result.PrivateBuild := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_PRIVATEBUILD);
+           Result.InfoInferred := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_INFOINFERRED);
+           Result.SpecialBuild := BOOL(TVSFixedFileInfo(InfoPointer^).dwFileFlags AND VS_FF_SPECIALBUILD);
+         End;
+       FreeMem(VersionInfo,VerInfoSize);
+     End;
+end;
+
+class function TFolderHelper.GetUserDataFolder: string;
+begin
+  Result := GetFolder(CSIDL_APPDATA);
+end;
+
+end.

+ 1439 - 0
Units/Utils/UGridUtils.pas

@@ -0,0 +1,1439 @@
+unit UGridUtils;
+
+{ 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, Grids, Windows, UNode, UAccounts, UBlockChain, UDBStorage, DBGrids, DB, ADODB,
+  UWalletKeys, UAppParams;
+
+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);
+  TAccountColumn = Record
+    ColumnType : TAccountColumnType;
+    width : Integer;
+  End;
+
+  TOrderedCardinalList = Class
+  private
+    FOrderedList : TList;
+    Function Find(const Value: Cardinal; var Index: Integer): Boolean;
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    Function Add(Value : Cardinal) : Integer;
+    Procedure Remove(Value : Cardinal);
+    Procedure Clear;
+    Function Get(index : Integer) : Cardinal;
+    Function Count : Integer;
+  End;
+
+  TAccountsGrid = Class(TComponent)
+  private
+    FAccountsList : TOrderedCardinalList;
+    FColumns : Array of TAccountColumn;
+    FDrawGrid : TDrawGrid;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FShowAllAccounts: Boolean;
+    procedure SetDrawGrid(const Value: TDrawGrid);
+    Procedure InitGrid;
+    Procedure OnNodeNewOperation(Sender : TObject);
+    procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
+    procedure SetNode(const Value: TNode);
+    function GetNode: TNode;
+    procedure SetShowAllAccounts(const Value: Boolean);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DrawGrid : TDrawGrid read FDrawGrid write SetDrawGrid;
+    Function LockAccountsList : TOrderedCardinalList;
+    Procedure UnlockAccountsList;
+    Property Node : TNode read GetNode write SetNode;
+    Function AccountNumber(GridRow : Integer) : Int64;
+    Procedure SaveToStream(Stream : TStream);
+    Procedure LoadFromStream(Stream : TStream);
+    Property ShowAllAccounts : Boolean read FShowAllAccounts write SetShowAllAccounts;
+  End;
+
+  TOperationsGrid = Class(TComponent)
+  private
+    FDrawGrid: TDrawGrid;
+    FAccountNumber: Int64;
+    FOperationsResume : TOperationsResumeList;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    Procedure OnNodeNewOperation(Sender : TObject);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    Procedure InitGrid;
+    procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
+    procedure SetDrawGrid(const Value: TDrawGrid);
+    procedure SetAccountNumber(const Value: Int64);
+    procedure SetNode(const Value: TNode);
+    function GetNode: TNode;
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DrawGrid : TDrawGrid read FDrawGrid write SetDrawGrid;
+    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
+    Property Node : TNode read GetNode write SetNode;
+    Procedure UpdateAccountOperations;
+    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+  End;
+
+  TOperationsDBGrid = Class(TComponent)
+  private
+    FDisableds : Integer;
+    FQrySQL : TAdoQuery;
+    FDBGrid: TDBGrid;
+    FAccountNumber: Int64;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FDataSource : TDataSource;
+    FDateEnd: TDate;
+    FBlockStart: Int64;
+    FDateStart: TDate;
+    FBlockEnd: Int64;
+    FNeedRefreshSQL : Boolean;
+    function GetNode: TNode;
+    procedure SetAccountNumber(const Value: Int64);
+    procedure SetDBGrid(const Value: TDBGrid);
+    procedure SetNode(const Value: TNode);
+    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+    procedure SetAdoConnection(const Value: TADOConnection);
+    function GetAdoConnection: TADOConnection;
+    Procedure OnQryCalcFields(DataSet: TDataSet);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    procedure SetBlockEnd(const Value: Int64);
+    procedure SetBlockStart(const Value: Int64);
+    procedure SetDateEnd(const Value: TDate);
+    procedure SetDateStart(const Value: TDate);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  published
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
+    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
+    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
+    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
+    Procedure SetBlocks(bstart,bend : Int64);
+    Property DateStart : TDate read FDateStart write SetDateStart;
+    Property DateEnd : TDate read FDateEnd write SetDateEnd;
+    Procedure SetDates(dStart,dEnd : TDate);
+    Property Node : TNode read GetNode write SetNode;
+    Procedure RefreshData;
+    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
+    Procedure Disable;
+    Procedure Enable;
+    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+  End;
+
+  TBlockChainDBGrid = Class(TComponent)
+  private
+    FDisableds : Integer;
+    FQrySQL : TAdoQuery;
+    FDBGrid: TDBGrid;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FDataSource : TDataSource;
+    FDateEnd: TDate;
+    FBlockStart: Int64;
+    FDateStart: TDate;
+    FBlockEnd: Int64;
+    FAccountNumber: Int64;
+    FNeedRefreshSQL : Boolean;
+    function GetNode: TNode;
+    procedure SetDBGrid(const Value: TDBGrid);
+    procedure SetNode(const Value: TNode);
+    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+    procedure SetAdoConnection(const Value: TADOConnection);
+    function GetAdoConnection: TADOConnection;
+    Procedure OnQryCalcFields(DataSet: TDataSet);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    procedure SetBlockEnd(const Value: Int64);
+    procedure SetBlockStart(const Value: Int64);
+    procedure SetDateEnd(const Value: TDate);
+    procedure SetDateStart(const Value: TDate);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  published
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
+    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
+    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
+    Procedure SetBlocks(bstart,bend : Int64);
+    Property DateStart : TDate read FDateStart write SetDateStart;
+    Property DateEnd : TDate read FDateEnd write SetDateEnd;
+    Procedure SetDates(dStart,dEnd : TDate);
+    Property Node : TNode read GetNode write SetNode;
+    Procedure RefreshData;
+    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
+    Procedure Disable;
+    Procedure Enable;
+  End;
+
+implementation
+
+uses
+  Graphics, UCrypto, SysUtils, UTime, UOpTransaction, UConst,
+  UFRMPayloadDecoder, ULog;
+
+{ TAccountsGrid }
+
+Const CT_ColumnHeader : Array[TAccountColumnType] Of String =
+  ('Account N.','Key','Balance','Updated','N Oper.','State');
+
+function TAccountsGrid.AccountNumber(GridRow: Integer): Int64;
+begin
+  if GridRow<1 then Result := -1
+  else if FShowAllAccounts then begin
+    if Assigned(Node) then begin
+      Result := GridRow-1;
+    end else Result := -1;
+  end else if GridRow<=FAccountsList.Count then begin
+    Result := Integer(FAccountsList.Get(GridRow-1));
+  end else Result := -1;
+end;
+
+constructor TAccountsGrid.Create(AOwner: TComponent);
+Var i : Integer;
+begin
+  inherited;
+  FShowAllAccounts := false;
+  FAccountsList := TOrderedCardinalList.Create;
+  FDrawGrid := Nil;
+  SetLength(FColumns,4);
+  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[3].width := 50;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
+end;
+
+destructor TAccountsGrid.Destroy;
+begin
+  FNodeNotifyEvents.Free;
+  FAccountsList.Free;
+  inherited;
+end;
+
+function TAccountsGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TAccountsGrid.InitGrid;
+Var i : Integer;
+begin
+  if Not assigned(DrawGrid) then exit;
+  if FShowAllAccounts then begin
+    if Assigned(Node) then begin
+      if Node.Bank.AccountsCount<1 then DrawGrid.RowCount := 2
+      else DrawGrid.RowCount := Node.Bank.AccountsCount+1;
+    end else DrawGrid.RowCount := 2;
+  end else begin
+    if FAccountsList.Count<1 then DrawGrid.RowCount := 2
+    else DrawGrid.RowCount := FAccountsList.Count+1;
+  end;
+  DrawGrid.FixedRows := 1;
+  if Length(FColumns)=0 then DrawGrid.ColCount := 1
+  else DrawGrid.ColCount := Length(FColumns);
+  DrawGrid.FixedCols := 0;
+  for i := low(FColumns) to high(FColumns) do begin
+    DrawGrid.ColWidths[i] := FColumns[i].width;
+  end;
+  FDrawGrid.DefaultRowHeight := 18;
+  DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
+    {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
+    {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
+    goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
+  FDrawGrid.Invalidate;
+end;
+
+procedure TAccountsGrid.LoadFromStream(Stream: TStream);
+Var c,i,j : Integer;
+begin
+  if Stream.Read(c,sizeof(c))<sizeof(c) then exit;
+  if c<=0 then exit;
+  SetLength(FColumns,c);
+  for i := 0 to c - 1 do begin
+    Stream.Read(j,sizeof(j));
+    if (j>=Integer(Low(TAccountColumnType))) And (j<=Integer(High(TAccountColumnType))) then begin
+      FColumns[i].ColumnType := TAccountColumnType(j);
+    end else FColumns[i].ColumnType := act_account_number;
+    Stream.Read(FColumns[i].width,sizeof(FColumns[i].width));
+  end;
+  Stream.Read(j,sizeof(j));
+  If Assigned(FDrawGrid) then FDrawGrid.Width := j;
+  Stream.Read(j,sizeof(j));
+  If Assigned(FDrawGrid) then FDrawGrid.Height := j;
+end;
+
+function TAccountsGrid.LockAccountsList: TOrderedCardinalList;
+begin
+  Result := FAccountsList;
+end;
+
+procedure TAccountsGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDrawGrid) then begin
+      SetDrawGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TAccountsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
+  Function FromColorToColor(colorstart,colordest : Integer; step,totalsteps : Integer) : Integer;
+  var sr,sg,sb,dr,dg,db : Byte;
+    i : Integer;
+  begin
+    i := colorstart;
+    sr := GetRValue(i);
+    sg := GetGValue(i);
+    sb := GetBValue(i);
+    i := colordest;
+    dr := GetRValue(i);
+    dg := GetGValue(i);
+    db := GetBValue(i);
+    sr := sr + (((dr-sr) DIV totalsteps)*step);
+    sg := sg + (((dg-sg) DIV totalsteps)*step);
+    sb := sb + (((db-sb) DIV totalsteps)*step);
+    Result :=RGB(sr,sg,sb);
+  end;
+Var C : TAccountColumn;
+  s : String;
+  n_acc : Int64;
+  account : TAccount;
+  ndiff : Cardinal;
+begin
+  if Not Assigned(Node) then exit;
+
+  if (ACol>=0) AND (ACol<length(FColumns)) then begin
+    C := FColumns[ACol];
+  end else begin
+    C.ColumnType := act_account_number;
+    C.width := -1;
+  end;
+  if (ARow=0) then begin
+    // Header
+    s := CT_ColumnHeader[C.ColumnType];
+    DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter]);
+  end else begin
+    n_acc := AccountNumber(ARow);
+    if (n_acc>=0) then begin
+      if (n_acc>=Node.Bank.AccountsCount) then account := CT_Account_NUL
+      else account := Node.Operations.SafeBoxTransaction.Account(n_acc);
+      ndiff := Node.Bank.BlocksCount - account.updated_block;
+      if (gdSelected in State) then
+        If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
+        else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
+      else DrawGrid.Canvas.Brush.Color := clWindow;
+      DrawGrid.Canvas.FillRect(Rect);
+      InflateRect(Rect,-2,-1);
+      case C.ColumnType of
+        act_account_number : Begin
+          s := TAccountComp.AccountNumberToAccountTxtNumber(n_acc);
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_account_key : Begin
+          s := Tcrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountkey));
+          DrawGrid.Canvas.TextRect(Rect,s,[tfLeft,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_balance : Begin
+          if ndiff=0 then begin
+            // Pending operation... showing final balance
+            DrawGrid.Canvas.Font.Color := clBlue;
+            s := '('+TAccountComp.FormatMoney(account.balance)+')';
+          end else begin
+            s := TAccountComp.FormatMoney(account.balance);
+            if account.balance>0 then DrawGrid.Canvas.Font.Color := ClGreen
+            else if account.balance=0 then DrawGrid.Canvas.Font.Color := clGrayText
+            else DrawGrid.Canvas.Font.Color := clRed;
+          end;
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_updated : Begin
+          s := Inttostr(account.updated_block);
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_n_operation : Begin
+          s := InttoStr(account.n_operation);
+          DrawGrid.Canvas.TextRect(Rect,s,[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);
+          end else begin
+            DrawGrid.Canvas.Brush.Color := clGreen;
+            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          end;
+        End;
+      else
+        s := '(???)';
+        DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter,tfSingleLine]);
+      end;
+    end;
+  end;
+end;
+
+procedure TAccountsGrid.OnNodeNewOperation(Sender: TObject);
+begin
+  If Assigned(FDrawGrid) then FDrawGrid.Invalidate;
+end;
+
+procedure TAccountsGrid.SaveToStream(Stream: TStream);
+Var c,i,j : Integer;
+begin
+  c := Length(FColumns);
+  Stream.Write(c,sizeof(c));
+  for i := 0 to c - 1 do begin
+    j := Integer(FColumns[i].ColumnType);
+    Stream.Write(j,sizeof(j));
+    if Assigned(FDrawGrid) then begin
+      FColumns[i].width := FDrawGrid.ColWidths[i];
+    end;
+    Stream.Write(FColumns[i].width,sizeof(FColumns[i].width));
+  end;
+  j := FDrawGrid.Width;
+  Stream.Write(j,sizeof(j));
+  j := FDrawGrid.Height;
+  Stream.Write(j,sizeof(j));
+end;
+
+procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
+begin
+  if FDrawGrid=Value then exit;
+  FDrawGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    FDrawGrid.OnDrawCell := OnGridDrawCell;
+    InitGrid;
+  end;
+end;
+
+procedure TAccountsGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node := Value;
+  InitGrid;
+end;
+
+procedure TAccountsGrid.SetShowAllAccounts(const Value: Boolean);
+begin
+  if FShowAllAccounts=Value then exit;
+  FShowAllAccounts := Value;
+  InitGrid;
+end;
+
+procedure TAccountsGrid.UnlockAccountsList;
+begin
+  InitGrid;
+end;
+
+{ TOrderedCardinalList }
+
+function TOrderedCardinalList.Add(Value: Cardinal): Integer;
+begin
+  if Find(Value,Result) then exit
+  else begin
+    FOrderedList.Insert(Result,TObject(Value));
+  end;
+end;
+
+procedure TOrderedCardinalList.Clear;
+begin
+  FOrderedList.Clear;
+end;
+
+function TOrderedCardinalList.Count: Integer;
+begin
+  Result := FOrderedList.Count;
+end;
+
+constructor TOrderedCardinalList.Create;
+begin
+  FOrderedList := TList.Create;
+end;
+
+destructor TOrderedCardinalList.Destroy;
+begin
+  FOrderedList.Free;
+  inherited;
+end;
+
+function TOrderedCardinalList.Find(const Value: Cardinal; var Index: Integer): Boolean;
+var L, H, I: Integer;
+  C : Int64;
+begin
+  Result := False;
+  L := 0;
+  H := FOrderedList.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := Int64(FOrderedList[I]) - Int64(Value);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function TOrderedCardinalList.Get(index: Integer): Cardinal;
+begin
+  Result := Cardinal(FOrderedList[index]);
+end;
+
+procedure TOrderedCardinalList.Remove(Value: Cardinal);
+Var i : Integer;
+begin
+  if Find(Value,i) then FOrderedList.Delete(i);
+end;
+
+{ TOperationsGrid }
+
+constructor TOperationsGrid.Create(AOwner: TComponent);
+begin
+  FDrawGrid := Nil;
+  FOperationsResume := TOperationsResumeList.Create;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
+  inherited;
+end;
+
+destructor TOperationsGrid.Destroy;
+begin
+  FOperationsResume.Free;
+  FNodeNotifyEvents.Free;
+  inherited;
+end;
+
+function TOperationsGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TOperationsGrid.InitGrid;
+begin
+  if Not Assigned(FDrawGrid) then exit;
+  if FOperationsResume.Count>0 then FDrawGrid.RowCount := FOperationsResume.Count+1
+  else FDrawGrid.RowCount := 2;
+  DrawGrid.FixedRows := 1;
+  DrawGrid.DefaultDrawing := true;
+  DrawGrid.FixedCols := 0;
+  DrawGrid.ColCount := 8;
+  DrawGrid.ColWidths[0] := 110; // Time
+  DrawGrid.ColWidths[1] := 50; // Block
+  DrawGrid.ColWidths[2] := 60; // Account
+  DrawGrid.ColWidths[3] := 150; // OpType
+  DrawGrid.ColWidths[4] := 70; // Amount
+  DrawGrid.ColWidths[5] := 60; // Operation Fee
+  DrawGrid.ColWidths[6] := 70; // Balance
+  DrawGrid.ColWidths[7] := 500; // Payload
+  FDrawGrid.DefaultRowHeight := 18;
+  FDrawGrid.Invalidate;
+  DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
+    {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
+    {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
+    goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
+end;
+
+procedure TOperationsGrid.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDrawGrid) then begin
+      SetDrawGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
+Var s : String;
+  opr : TOperationResume;
+begin
+  opr := CT_TOperationResume_NUL;
+  Try
+  if (ARow=0) then begin
+    // Header
+    case ACol of
+      0 : s := 'Time';
+      1 : s := 'Block';
+      2 : s := 'Account';
+      3 : s := 'Operation';
+      4 : s := 'Amount';
+      5 : s := 'Fee';
+      6 : s := 'Balance';
+      7 : s := 'Payload';
+    else s:= '';
+    end;
+    DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter]);
+  end else begin
+    if (gdSelected in State) then
+      If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
+      else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
+    else DrawGrid.Canvas.Brush.Color := clWindow;
+    DrawGrid.Canvas.FillRect(Rect);
+    InflateRect(Rect,-2,-1);
+    if (ARow<=FOperationsResume.Count) then begin
+      opr := FOperationsResume.OperationResume[ARow-1];
+      if ACol=0 then begin
+        if opr.time=0 then s := '(Pending)'
+        else s := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime(opr.time)));
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=1 then begin
+        s := Inttostr(opr.Block);
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=2 then begin
+        s := TAccountComp.AccountNumberToAccountTxtNumber(opr.AffectedAccount);
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=3 then begin
+        s := opr.OperationTxt;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=4 then begin
+        s := TAccountComp.FormatMoney(opr.Amount);
+        if opr.Amount>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        else if opr.Amount=0 then DrawGrid.Canvas.Font.Color := clGrayText
+        else DrawGrid.Canvas.Font.Color := clRed;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=5 then begin
+        s := TAccountComp.FormatMoney(opr.Fee);
+        if opr.Fee>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        else if opr.Fee=0 then DrawGrid.Canvas.Font.Color := clGrayText
+        else DrawGrid.Canvas.Font.Color := clRed;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=6 then begin
+        if opr.time=0 then begin
+          // Pending operation... showing final balance
+          DrawGrid.Canvas.Font.Color := clBlue;
+          s := '('+TAccountComp.FormatMoney(opr.Balance)+')';
+        end else begin
+          s := TAccountComp.FormatMoney(opr.Balance);
+          if opr.Balance>0 then DrawGrid.Canvas.Font.Color := ClGreen
+          else if opr.Balance=0 then DrawGrid.Canvas.Font.Color := clGrayText
+          else DrawGrid.Canvas.Font.Color := clRed;
+        end;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=7 then begin
+        s := opr.PrintablePayload;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfLeft,tfVerticalCenter,tfSingleLine]);
+      end else begin
+        s := '(???)';
+        DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter,tfSingleLine]);
+      end;
+    end;
+  end;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,Format('Error at OnGridDrawCell row %d col %d Block %d - %s',[ARow,ACol,opr.Block,E.Message]));
+    end;
+  End;
+end;
+
+procedure TOperationsGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  UpdateAccountOperations;
+end;
+
+procedure TOperationsGrid.OnNodeNewOperation(Sender: TObject);
+Var Op : TPCOperation;
+  l : TList;
+begin
+  if AccountNumber<0 then UpdateAccountOperations
+  else begin
+    Op := TPCOperation(Sender);
+    l := TList.Create;
+    Try
+      Op.AffectedAccounts(l);
+      if l.IndexOf(TObject(Integer(AccountNumber)))>=0 then UpdateAccountOperations;
+    Finally
+      l.Free;
+    End;
+  end;
+end;
+
+procedure TOperationsGrid.SetAccountNumber(const Value: Int64);
+begin
+  if FAccountNumber=Value then exit;
+  FAccountNumber := Value;
+  UpdateAccountOperations;
+end;
+
+procedure TOperationsGrid.SetDrawGrid(const Value: TDrawGrid);
+begin
+  if FDrawGrid=Value then exit;
+  FDrawGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    FDrawGrid.OnDrawCell := OnGridDrawCell;
+    InitGrid;
+  end;
+end;
+
+procedure TOperationsGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node := Value;
+  InitGrid;
+end;
+
+procedure TOperationsGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+Var i : Integer;
+  opr : TOperationResume;
+  FRM : TFRMPayloadDecoder;
+begin
+  if Not Assigned(FDrawGrid) then exit;
+  if (FDrawGrid.Row<=0) Or (FDrawGrid.Row>FOperationsResume.Count) then exit;
+  opr := FOperationsResume.OperationResume[FDrawGrid.Row-1];
+  FRM := TFRMPayloadDecoder.Create(FDrawGrid.Owner);
+  try
+    FRM.Init(opr.Block,opr.time,opr.OperationTxt,opr.OriginalPayload,WalletKeys,AppParams);
+    FRM.ShowModal;
+  finally
+    FRM.Free;
+  end;
+end;
+
+procedure TOperationsGrid.UpdateAccountOperations;
+Var list : TList;
+  i,j : Integer;
+  OPR : TOperationResume;
+  bbalance : Int64;
+  Op : TPCOperation;
+begin
+  FOperationsResume.Clear;
+  Try
+    if Not Assigned(Node) then exit;
+    if AccountNumber<0 then begin
+      list := TList.Create;
+      try
+        for i := 0 to Node.Operations.Count-1 do begin
+          Op := Node.Operations.Operation[i];
+          If TDBStorage.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
+            OPR.Block := Node.Operations.OperationBlock.block;
+            OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
+            FOperationsResume.Add(OPR);
+          end;
+        end;
+      finally
+        list.Free;
+      end;
+    end else begin
+      list := TList.Create;
+      Try
+        Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
+//        bbalance := Node.Bank.SafeBox.Account(AccountNumber).balance;
+        for i := list.Count - 1 downto 0 do begin
+          Op := Node.Operations.OperationsHashTree.GetOperation(Integer(list[i]));
+          If TDBStorage.OperationToOperationResume(Op,AccountNumber,OPR) then begin
+            OPR.Block := Node.Operations.OperationBlock.block;
+//            bbalance := bbalance + OPR.Amount - OPR.Fee;
+//            OPR.Balance := bbalance;
+            OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
+            FOperationsResume.Add(OPR);
+          end;
+        end;
+      Finally
+        list.Free;
+      End;
+      if Node.Bank.Storage is TDBStorage then begin
+        TDBStorage(Node.Bank.Storage).GetOperationsFromAccount(FOperationsResume,AccountNumber,0,20);
+      end;
+    end;
+  Finally
+    InitGrid;
+  End;
+end;
+
+{ TOperationsDBGrid }
+
+Type TAuxDBGrid = Class(TDBGrid);
+
+constructor TOperationsDBGrid.Create(AOwner: TComponent);
+Var i : Integer;
+  fld : TField;
+begin
+  inherited;
+  FNeedRefreshSQL := false;
+  FDisableds := 0;
+  FDBGrid := Nil;
+  FQrySQL := TADOQuery.Create(Self);
+  FQrySQL.OnCalcFields := OnQryCalcFields;
+  FqrySQL.CursorLocation := clUseClient;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FDataSource := TDataSource.Create(Self);
+  FDataSource.DataSet := FQrySQL;
+  FAccountNumber := -1; // all
+  FBlockStart := -1;
+  FBlockEnd := -1;
+  FDateStart := 0;
+  FDateEnd := 0;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_block;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_timestamp;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_optype;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_optype_op;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_account;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_other_account;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TDateTimeField.Create(FQrySQL);
+  fld.FieldName := 'datetime';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'op_account';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 100;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'operation';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 100;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'payload_txt';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 600;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_amount;
+  fld.DataSet := FQrySQL;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_fee;
+  fld.DataSet := FQrySQL;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_balance;
+  fld.DataSet := FQrySQL;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_rawpayload;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 255;
+end;
+
+destructor TOperationsDBGrid.Destroy;
+begin
+  FNodeNotifyEvents.Free;
+  FDataSource.Free;
+  FQrySQL.Free;
+  inherited;
+end;
+
+procedure TOperationsDBGrid.Disable;
+begin
+  inc(FDisableds);
+end;
+
+procedure TOperationsDBGrid.Enable;
+begin
+  dec(FDisableds);
+  if FDisableds>0 then exit;
+  FDisableds := 0;
+  if FNeedRefreshSQL then RefreshData;
+end;
+
+function TOperationsDBGrid.GetAdoConnection: TADOConnection;
+begin
+  Result := FQrySQL.Connection;
+end;
+
+function TOperationsDBGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TOperationsDBGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDBGrid) then begin
+      SetDBGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TOperationsDBGrid.OnGridDrawColumnCell(Sender: TObject;
+  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+Var c : TCanvas;
+  s : String;
+  r : TRect;
+begin
+  c := TDBGrid(Sender).Canvas;
+  r := Rect;
+  if (gdSelected in State) then
+    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
+    else c.Brush.Color := clGradientInactiveCaption
+  else c.Brush.Color := clWindow;
+  c.FillRect(Rect);
+  if SameText(Column.FieldName,CT_TblFld_Operations_amount) Or
+     SameText(Column.FieldName,CT_TblFld_Operations_fee) Or
+     SameText(Column.FieldName,CT_TblFld_Operations_balance)
+      then begin
+    c.FillRect(Rect);
+    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
+    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
+    else c.Font.Color := clRed;
+    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
+    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+  end else begin
+    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
+  end;
+
+  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
+     not (gdPressed in State) then
+  begin
+    InflateRect(r, 1, 1);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
+  end;
+  //
+end;
+
+procedure TOperationsDBGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.OnQryCalcFields(DataSet: TDataSet);
+Var fld : TField;
+  raw : TRawBytes;
+  s : AnsiString;
+begin
+  fld := DataSet.FieldByName('datetime');
+  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime(DataSet.FieldByName(CT_TblFld_Operations_timestamp).AsInteger));
+  fld := DataSet.FieldByName('op_account');
+  fld.AsString := TAccountComp.AccountNumberToAccountTxtNumber(DataSet.FieldByName(CT_TblFld_Operations_account).AsInteger);
+  fld := DataSet.FieldByName('payload_txt');
+//  TDBStorage.BlobSaveToRaw(DataSet.FieldByName(CT_TblFld_Operations_payload_stream) as TBlobField,raw);
+  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_Operations_rawpayload),raw);
+//  raw := DataSet.FieldByName(CT_TblFld_Operations_payload).AsAnsiString;
+  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
+    fld.AsString := s;
+  end else begin
+    fld.AsString := TCrypto.ToHexaString(raw);
+  end;
+  fld := DataSet.FieldByName('operation');
+          case dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger of
+            0 : fld.AsString := 'Blockchain reward';
+            CT_Op_Transaction : begin
+              if dataset.FieldByName(CT_TblFld_Operations_optype_op).AsInteger=0 then begin
+                fld.AsString := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end else begin
+                fld.AsString := 'Transaction Received from '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end;
+            end;
+            CT_Op_Changekey : Begin
+              fld.AsString := 'Change Key';
+            End;
+            CT_Op_Recover : begin
+              fld.AsString := 'Recover founds';
+            end;
+          else
+            fld.AsString := 'Unknown OpType:'+Inttostr(dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger);
+          end;
+end;
+
+procedure TOperationsDBGrid.RefreshData;
+Var sql : AnsiString;
+begin
+  if FDisableds>0 then begin
+    FNeedRefreshSQL := true;
+    exit;
+  end;
+  FNeedRefreshSQL := false;
+  sql := TDBStorage.GetOperationsFromAccountSQL(AccountNumber,BlockStart,BlockEnd,DateStart,DateEnd, 0,0);
+  If FQrySQL.Connection=Nil then exit;
+  if Node=Nil then exit;
+  if FDBGrid=Nil then exit;
+
+  FQrySQL.DisableControls;
+  try
+    FQrySQL.Close;
+    FQrySQL.SQL.Text := sql;
+    FQrySQL.Open;
+  finally
+    FQrySQL.EnableControls;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetAccountNumber(const Value: Int64);
+begin
+  if FAccountNumber=Value then exit;
+  FAccountNumber := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetAdoConnection(const Value: TADOConnection);
+begin
+  FQrySQL.Connection := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlockEnd(const Value: Int64);
+begin
+  if FBlockEnd=Value then exit;
+  FBlockEnd := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlocks(bstart, bend: Int64);
+begin
+  FBlockStart := bstart;
+  FBlockEnd := bend;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlockStart(const Value: Int64);
+begin
+  if FBlockStart=Value then exit;
+  FBlockStart := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDateEnd(const Value: TDate);
+Var d,h,m,y : word;
+begin
+  if FDateEnd=Value then exit;
+  decodedate(value,y,m,d);
+  FDateEnd := EncodeDate(y,m,d);
+  if FDateStart>=FDateEnd then FDateStart := FDateEnd;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDates(dStart, dEnd: TDate);
+begin
+  Disable;
+  try
+    DateStart := dStart;
+    DateEnd := dEnd;
+  finally
+    Enable;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetDateStart(const Value: TDate);
+Var d,h,m,y : word;
+begin
+  if FDateStart=Value then exit;
+  decodedate(value,y,m,d);
+  FDateStart := EncodeDate(y,m,d);
+  if FDateStart>=FDateEnd then FDateEnd := FDateStart;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDBGrid(const Value: TDBGrid);
+  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
+  Var c : TColumn;
+  begin
+    c := DBgrid.Columns.Add;
+    c.FieldName := fieldname;
+    c.Title.Caption := displayname;
+    c.Width := colwidth;
+    c.Title.Font.Style := [fsBold];
+    c.Title.Alignment := taCenter;
+  end;
+begin
+  if FDBGrid=Value then exit;
+  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
+  FDBGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    Value.DataSource := FDataSource;
+    Value.ReadOnly := true;
+    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
+    FDBGrid.DefaultDrawing := false;
+    FDBGrid.Columns.Clear;
+    AddColumn(CT_TblFld_Operations_block,'Block',50);
+    AddColumn('datetime','Date/Time',120);
+    AddColumn('op_account','Account',70);
+    AddColumn('operation','Operation',150);
+    AddColumn(CT_TblFld_Operations_amount,'Amount',80);
+    AddColumn(CT_TblFld_Operations_fee,'Fee',60);
+    AddColumn(CT_TblFld_Operations_balance,'Balance',80);
+    AddColumn('payload_txt','Payload',280);
+    RefreshData;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node:= value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams: TAppParams);
+Var FRM : TFRMPayloadDecoder;
+  raw : TRawBytes;
+begin
+  if Not Assigned(FDBGrid) Or Not Assigned(FQrySQL) then exit;
+  If FQrySQL.Eof then exit;
+  FRM := TFRMPayloadDecoder.Create(FDBGrid.Owner);
+  try
+    TDBStorage.DBStringFieldToRaw(FQrySQL.FieldByName(CT_TblFld_Operations_rawpayload),raw);
+    FRM.Init(FQrySQL.FieldByName(CT_TblFld_Operations_block).AsInteger,FQrySQL.FieldByName(CT_TblFld_Operations_timestamp).AsInteger,
+      FQrySQL.FieldByName('operation').AsString,
+      raw,WalletKeys,AppParams);
+    FRM.ShowModal;
+  finally
+    FRM.Free;
+  end;
+end;
+
+{ TBlockChainDBGrid }
+
+constructor TBlockChainDBGrid.Create(AOwner: TComponent);
+Var fld : TField;
+begin
+  inherited;
+  FNeedRefreshSQL := False;
+  FBlockStart := -1;
+  FBlockEnd := -1;
+  FDateEnd := 0;
+  FDateStart := 0;
+  FDisableds := 0;
+  FQrySQL := TADOQuery.Create(Self);
+  FQrySQL.OnCalcFields := OnQryCalcFields;
+  FqrySQL.CursorLocation := clUseClient;
+  FDBGrid := Nil;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FDataSource := TDataSource.Create(Self);
+  FDataSource.DataSet := FQrySQL;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_block;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_timestamp;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_reward;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_fee;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_compact_target;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_operations_count;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TDateTimeField.Create(FQrySQL);
+  fld.FieldName := 'datetime';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_proof_of_work;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  TStringField(fld).Size := 64;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_safe_box_hash;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  TStringField(fld).Size := 64;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'target';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 8;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_rawpayload;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 255;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'payload_decoded';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 600;
+end;
+
+destructor TBlockChainDBGrid.Destroy;
+begin
+  SetDBGrid(Nil);
+  FNodeNotifyEvents.Free;
+  FDataSource.Free;
+  FQrySQL.Free;
+  inherited;
+end;
+
+procedure TBlockChainDBGrid.Disable;
+begin
+  Inc(FDisableds);
+end;
+
+procedure TBlockChainDBGrid.Enable;
+begin
+  if FDisableds<0 then exit;
+  Dec(FDisableds);
+  if FNeedRefreshSQL then RefreshData;
+end;
+
+function TBlockChainDBGrid.GetAdoConnection: TADOConnection;
+begin
+  Result := FQrySQL.Connection;
+end;
+
+function TBlockChainDBGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TBlockChainDBGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=OpRemove then begin
+    if AComponent=FDBGrid then FDBGrid:=Nil;
+  end;
+end;
+
+procedure TBlockChainDBGrid.OnGridDrawColumnCell(Sender: TObject;
+  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+Var c : TCanvas;
+  s : String;
+  r : TRect;
+begin
+  c := TDBGrid(Sender).Canvas;
+  r := Rect;
+  if (gdSelected in State) then
+    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
+    else c.Brush.Color := clGradientInactiveCaption
+  else c.Brush.Color := clWindow;
+  c.FillRect(Rect);
+  if SameText(Column.FieldName,CT_TblFld_BlockChain_reward) Or
+     SameText(Column.FieldName,CT_TblFld_BlockChain_fee)
+      then begin
+    c.FillRect(Rect);
+    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
+    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
+    else c.Font.Color := clRed;
+    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
+    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+  end else begin
+    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
+  end;
+
+  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
+     not (gdPressed in State) then
+  begin
+    InflateRect(r, 1, 1);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
+  end;
+  //
+end;
+
+procedure TBlockChainDBGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.OnQryCalcFields(DataSet: TDataSet);
+Var fld : TField;
+  raw : TRawBytes;
+  s : AnsiString;
+begin
+  fld := DataSet.FieldByName('datetime');
+  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime(DataSet.FieldByName(CT_TblFld_BlockChain_timestamp).AsInteger));
+  fld := DataSet.FieldByName('target');
+  fld.AsString :=  IntToHex(DataSet.FieldByName(CT_TblFld_BlockChain_compact_target).AsInteger,8);
+  fld := DataSet.FieldByName('payload_decoded');
+  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_BlockChain_rawpayload),raw);
+  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
+    fld.AsString := s;
+  end else begin
+    fld.AsString := TCrypto.ToHexaString(raw);
+  end;
+end;
+
+procedure TBlockChainDBGrid.RefreshData;
+Var sql : AnsiString;
+begin
+  if FDisableds>0 then begin
+    FNeedRefreshSQL := true;
+    exit;
+  end;
+  FNeedRefreshSQL := false;
+  sql := TDBStorage.GetBlockChainSQL(BlockStart,BlockEnd,DateStart,DateEnd, 0,100);
+  If FQrySQL.Connection=Nil then exit;
+  if Node=Nil then exit;
+  if FDBGrid=Nil then exit;
+
+  FQrySQL.DisableControls;
+  try
+    FQrySQL.Close;
+    FQrySQL.SQL.Text := sql;
+    FQrySQL.Open;
+  finally
+    FQrySQL.EnableControls;
+  end;
+end;
+
+procedure TBlockChainDBGrid.SetAdoConnection(const Value: TADOConnection);
+begin
+  FQrySQL.Connection := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetBlockEnd(const Value: Int64);
+begin
+  if FBlockEnd=Value then exit;
+  FBlockEnd := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetBlocks(bstart, bend: Int64);
+begin
+  disable;
+  Try
+    BlockStart := bstart;
+    BlockEnd := bEnd;
+  Finally
+    Enable;
+  End;
+end;
+
+procedure TBlockChainDBGrid.SetBlockStart(const Value: Int64);
+begin
+  if FBlockStart=Value then exit;
+  FBlockStart:=Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDateEnd(const Value: TDate);
+begin
+  if FDateEnd=Value then exit;
+  FDateEnd := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDates(dStart, dEnd: TDate);
+begin
+  Disable;
+  Try
+    DateStart := dStart;
+    DateEnd := dEnd;
+  Finally
+    Enable;
+  End;
+end;
+
+procedure TBlockChainDBGrid.SetDateStart(const Value: TDate);
+begin
+  if FDateStart=Value then exit;
+  FDateStart := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDBGrid(const Value: TDBGrid);
+  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
+  Var c : TColumn;
+  begin
+    c := DBgrid.Columns.Add;
+    c.FieldName := fieldname;
+    c.Title.Caption := displayname;
+    c.Width := colwidth;
+    c.Title.Font.Style := [fsBold];
+    c.Title.Alignment := taCenter;
+  end;
+begin
+  if FDBGrid=Value then exit;
+  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
+  FDBGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    Value.DataSource := FDataSource;
+    Value.ReadOnly := true;
+    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
+    FDBGrid.DefaultDrawing := false;
+    FDBGrid.Columns.Clear;
+    AddColumn(CT_TblFld_BlockChain_block,'Block',50);
+    AddColumn('datetime','Date/Time',120);
+    AddColumn(CT_TblFld_BlockChain_operations_count,'Ops.',50);
+    AddColumn(CT_TblFld_BlockChain_reward,'Reward',80);
+    AddColumn(CT_TblFld_BlockChain_fee,'Fee',60);
+    AddColumn('target','Target',65);
+    AddColumn(CT_TblFld_BlockChain_safe_box_hash,'Safe Box Hash',350);
+    AddColumn('payload_decoded','Miner Payload',150);
+    AddColumn(CT_TblFld_BlockChain_proof_of_work,'Proof of Work',350);
+    RefreshData;
+  end;
+end;
+
+procedure TBlockChainDBGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node := Value;
+  RefreshData;
+end;
+
+end.

BIN
libeay32.dll


BIN
pascalcoin.mdb