Browse Source

Build 1.1

Build 1.1 with JSON-RPC server
PascalCoin 8 years ago
parent
commit
f371c4c11c

+ 0 - 135
COMPILE_INSTRUCTIONS.txt

@@ -1,135 +0,0 @@
-*********************************
-*** HOW TO COMPILE PASCALCOIN ***
-*********************************
-(File updated 2016-10-20)
-
-Instruction about how to compile Pascal Coin Wallet 1.0.9  (2016-10-21)
-For newer versions... I hope will be similar ;-)
-
-IMPORTANT NOTE:
-- In Windows, it's tested with XP, W7 and W10.  In Linux, is tested on Ubuntu 16.04.1 (Graphical version) 64 bits
-- In Ubuntu 16.04.1, there is a Lazarus installation issue. See FAQ 01 (at the end of this document)
-
-- You can compile with Delphi (Tested Delphi version 2010), or with Lazarus (Tested version 1.6)
-- Cross-compatible if using Lazarus  (Windows + Linux + Mac)  (Note: Not tested in Mac, only Windows or Ubuntu)
-
-
-PLEASE READ CAREFULLY:
-
-
-**************************
-** DOWNLOAD SOURCE CODE **
-**************************
-
-- Create a source code folder, we will call this folder "SOURCE_CODE_FOLDER" 
-
-- Download PascalCoin source code from: https://github.com/PascalCoin/PascalCoin  (Tested with Build 1.0.9)
-- Download Synapse source code from: http://www.ararat.cz/synapse/lib/exe/fetch.php/file:synapse40.zip  (Tested with version Synapse40)
-  - Also you can download a new version from svn: https://sourceforge.net/p/synalist/code/HEAD/tree/trunk/
-  - If URL is broken, try searching "Synapse ararat" at Google and Download
-
-** WINDOWS **
-- If you want to compile for ** WINDOWS **, oobtain OpenSSL DLL's from: https://indy.fulgan.com/SSL/
-  - If compiler is Delphi, you must obtain a 32 bits DLL file
-  - If compiler is Lazarus, you must obtain a 64 bits DLL file
-  - You must unzip file "libeay32.dll" and put in "SOURCE_CODE_FOLDER"
-  - If you compile with Lazarus, RENAME this file to "libeay64.dll" because is 64 bits version
-
-** LAZARUS **
-- If you want to compile for Lazarus, obtain OpenSSL source code from: https://www.openssl.org/source/
-  - See section ** BUILD OPENSSL FOR LINUX **
-  - Tested with openssl-1.1.0b.targ.gz (2016-Sep-26)  
-
-*************************
-** DOWNLOAD BLOCKCHAIN **
-*************************
-
-- You don't need to obtain Blockchain, but is more quickly if you have downloaded it than to wait App download it from other nodes...
-- You will find a valid BlockChain at SourceForge PascalCoin downloads: https://sourceforge.net/projects/pascalcoin/files/ - Search for "BlockChain" file
-- Blockchain must be unziped and stored at PascalCoin data folder: (We will call it "PASCALCOIN_DATA_FOLDER")
-  - PascalCoin data folder for Windows:   c:\Users\(your user)\AppData\Roaming\PascalCoin\Data
-  - PascalCoin data folder for Linux: /home/(your user)/PascalCoin/Data
-
-REMEMBER TO PUT BLOCKCHAIN IN CORRECT "PASCALCOIN_DATA_FOLDER"
-REMEMBER TO GRANT PERMISIONS TO USER AT "PASCALCOIN_DATA_FOLDER"
-
-  
-*************************
-** COMPILE WITH DELPHI **
-*************************
-
-(In Delphi, you can only run in Windows)
-
-- Just open Delphi 2010, open project "PascalCoinWallet.dpr" located at "SOURCE_CODE_FOLDER"
-- Go to "Project > Options" and put "Synapse\lib" folder at Search Path option  (This will enable Delphi to find Synapse source code)
-- Build + Compile
-- Exe file will be: "PascalCoinWallet.exe"
-
-*************************
-** COMPILE WITH LAZARUS **
-*************************
-
-- Just open Lazarus (tested with Lazarus 1.6 and FPC 3.0)
-- Open project "PascalCoinWalletLazarus.lpi"
-- Check that 
-- Build + Compile
-- Executable file will be: "PascalCoinWalletLazarus"
-
-*****************************
-** BUILD OPENSSL FOR LINUX **
-*****************************
-
-Build OpenSSL is only necessary in Linux. In Windows you can download a build version from https://indy.fulgan.com/SSL/
-If you want to build on Windows, search how to at Google.
-Note: Tested with OpenSSL 1.1.0b (2016-Sep-26)
-- LINUX INSTRUCTIONS
-- Download OpenSSL from www.openssl.org (Preferred version 1.1.0b)
-- Unzip files to any folder, we will call it "OpenSSLSourceFolder"
-- Open a linux terminal
-  - Go to "OpenSSLSourceFolder"
-    cd /"OpenSSLSourceFolder"
-  - Create a temporal destination folder
-    mkdir tmp_openssl
-  - Config build options with this temporal destination dir
-    ./config shared --prefix=/"OpenSSLSourceFolder"/tmp_openssl
-  - Execute make and install
-    make
-	install
-  - We will find a file called "libcrypto.so.1.1" at folder "OpenSSLSourceFolder"/tmp_openssl/lib
-  - copy "libcrypto.so.1.1" to your executable folder (where you have been compiled PascalCoinWalletLazarus = "SOURCE_CODE_FOLDER")
-  - DON'T RENAME this file, because PascalCoin executable needs a file called "libcrypto.so.1.1" when compiled in Linux with OpenSSLv1.1 option enabled
-- If you download a version 1.0, then file name of compiled OpenSSL library must be "libcrypto.so.1.0.0" and save it at "SOURCE_CODE_FOLDER"
-
-*******************
-** FAQ's section **
-*******************
-
-************
-** FAQ 01 **
-************
-
-FAQ 01 -> Lazarus does not compile with Linux (example: file "interfaces" not found or missing)
-Explanation: In a fresh Ubuntu 16.04.1 (perhaps other versions too) with Lazarus 1.6 (downloaded and fresh installed) you cannot compile PascalCoin (and nor anyone other Pascal program)
-This is because you must build Lazarus core.
-Solution:
-- Open Lazarus and close all projects
-- Go to menu "Tools" > "Configure Build Lazarus"
-- Select Profile to build "Clean Up+ Build all"
-- Click to "Build"
-  - Lazarus will build itself and reboot
-- Open Lazarus again and check if you can compile without "interfaces" file error
-  - If not, then try again to build Lazarus selecting other Profile to build options (Perhaps "Debug IDE")
-  - If not... sorry... google search ;-)
-
-************
-** FAQ 02 **
-************
-
-If Application does not start showing a "file not found libeay32.dll" or "libcrypto.so.1.1" or "libeay64.dll"...
-Solution: Read "DOWNLOAD SOURCE CODE" section
-
-
-Copyright (c) 2016 Albert Molina
-
-If you like it, consider a donation using BitCoin:
-16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk

+ 19 - 5
PascalCoinServer.dpr

@@ -5,14 +5,28 @@ program PascalCoinServer;
 uses
   SysUtils,
   SyncObjs,
-  UAES in 'Units\Utils\UAES.pas',
-  UJSONFunctions in 'Units\Utils\UJSONFunctions.pas',
+  UAES in 'Units\PascalCoin\UAES.pas',
   UCrypto in 'Units\PascalCoin\UCrypto.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',
-  UServerApp in 'Units\PascalCoin\UServerApp.pas';
+  UServerApp in 'Units\PascalCoin\UServerApp.pas',
+  UAccounts in 'Units\PascalCoin\UAccounts.pas',
+  UBlockChain in 'Units\PascalCoin\UBlockChain.pas',
+  UECIES in 'Units\PascalCoin\UECIES.pas',
+  UFileStorage in 'Units\PascalCoin\UFileStorage.pas',
+  UNetProtocol in 'Units\PascalCoin\UNetProtocol.pas',
+  UNode in 'Units\PascalCoin\UNode.pas',
+  UOpenSSL in 'Units\PascalCoin\UOpenSSL.pas',
+  UOpenSSLdef in 'Units\PascalCoin\UOpenSSLdef.pas',
+  UOpTransaction in 'Units\PascalCoin\UOpTransaction.pas',
+  URPC in 'Units\PascalCoin\URPC.pas',
+  UTCPIP in 'Units\PascalCoin\UTCPIP.pas',
+  UThread in 'Units\PascalCoin\UThread.pas',
+  UTime in 'Units\PascalCoin\UTime.pas',
+  UWalletKeys in 'Units\PascalCoin\UWalletKeys.pas',
+  UMiner in 'Units\PascalCoin\UMiner.pas',
+  UFolderHelper in 'Units\Utils\UFolderHelper.pas',
+  UJSONFunctions in 'Units\Utils\UJSONFunctions.pas';
 
 type
   TOutputLogger = class

BIN
PascalCoinWallet.res


+ 0 - 1
PascalCoinWalletLazarus.lpi

@@ -14,7 +14,6 @@
       <Title Value="PascalCoinWalletLazarus"/>
       <UseAppBundle Value="False"/>
       <ResourceType Value="res"/>
-      <Icon Value="0"/>
     </General>
     <i18n>
       <EnableI18N LFM="False"/>

BIN
PascalCoinWalletLazarus.res


+ 58 - 53
README.md

@@ -1,80 +1,83 @@
-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.9 of Pascal Coin Wallet can be compiled with Delphi and Lazarus and run in Windows or Linux
+# 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:  
+  
+See instructions at GitHub Wiki: https://github.com/PascalCoin/PascalCoin/wiki
+  
+  
+Enjoy Pascal Coin!
+  
+## Donations  
+  
+If you like it, consider a donation using BitCoin:
+16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
 
-- Included source code available at https://github.com/PascalCoin/PascalCoin
-- Read "COMPILE_INSTRUCTIONS" file with instructions
+Also, consider a donation at PascalCoin development account: "0-10"
 
-To run:
-- Windows: libeay32.dll - Can be downloaded from https://indy.fulgan.com/SSL/
-- Linux: libcrypto.so.1.0.0 or libcrypto.so.1.1 (Instructions in "COMPILE_INSTRUCTIONS" file)
+## History:  
 
+### Build 1.1.0.0 - 2016-11-03
 
-Enjoy Pascal Coin!
+- JSON-RPC Server included
+- Minor changes
 
-If you like it, consider a donation using BitCoin:
-16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
 
-History:
+### Build 1.0.9.0 - 2016-10-21
 
-Build 1.0.9.0 - 2016-10-21
---------------------------
 - Corrected a BUG (BUG-101) that causes blocking connections when received more than 100 connections, causing "alone in the world" after a cert period time.
 - It's necessary to update because new version will refuse old versions to make network stable.
 
 
-Build 1.0.8.0 - 2016-10-20
---------------------------
+### Build 1.0.8.0 - 2016-10-20
+
 - Cross compatible
 - Can compile with Delphi or Lazarus (Free Pascal)
 - New storage system. No more access database
 - Network hashrate calculation
 
 
-Build 1.0.7.0 - 2016-10-10
---------------------------
+### Build 1.0.7.0 - 2016-10-10
+
 - Introducing basic JSON-RPC to allow GPU miners development (Third party).
 - See file "HOWTO_DEVELOP_GPU_MINER_FOR_PASCALCOIN.txt"
 - No more CPU mining due exists GPU mining
 
 
-Build 1.0.6.0 - 2016-10-04
---------------------------
+### Build 1.0.6.0 - 2016-10-04
+
 - Memory leaks corrections
 - Introducing net protocol 2-3
 - Source code modified, next build will be compiled with Lazarus and FPC
 
 
-Build 1.0.5.0 - 2016-09-21
---------------------------
+### Build 1.0.5.0 - 2016-09-21
+
 - Massive operations, selecting multiple accounts
 - Filter accounts by balance
 - Correct operations explorer order of operations for each block (descending order)
 - Minor changes
 
 
-Build 1.0.4.0 - 2016-09-16
---------------------------
+### Build 1.0.4.0 - 2016-09-16
+
 - IMPORTANT: Introducing net protocol changes: Must update!
 - More and more and more stable
 - Prevents "Alone in the world" if everybody is updated ;-)
@@ -82,8 +85,8 @@ Build 1.0.4.0 - 2016-09-16
 - IP nodes configurator
 
 
-Build 1.0.3.0 - 2016-09-08
---------------------------
+### Build 1.0.3.0 - 2016-09-08
+
 - Important changes to database 
 - Peer cache
 - Issues with Connections
@@ -92,8 +95,8 @@ Build 1.0.3.0 - 2016-09-08
 - Invalid local time detector
 
 
-Build 1.0.2.0 - 2016-08-31
---------------------------
+### Build 1.0.2.0 - 2016-08-31
+
 - Improved hashing speed
 - Allow mining without opening external ports
 - Choose how many CPU's want to mine
@@ -101,12 +104,14 @@ Build 1.0.2.0 - 2016-08-31
 - More stable
 - Some miner modifications
 
-Build 1.0.1.0 - 2016-08-12
---------------------------
+### 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.
+### Build 1.0.0.0 - 2016-08-11
+
+- First stable version.
+- Created with Genesis block hardcoded
+- Published at same time than Genesis block. NO PREMINE

+ 58 - 53
README.txt

@@ -1,80 +1,83 @@
-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.9 of Pascal Coin Wallet can be compiled with Delphi and Lazarus and run in Windows or Linux
+# 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:  
+  
+See instructions at GitHub Wiki: https://github.com/PascalCoin/PascalCoin/wiki
+  
+  
+Enjoy Pascal Coin!
+  
+## Donations  
+  
+If you like it, consider a donation using BitCoin:
+16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
 
-- Included source code available at https://github.com/PascalCoin/PascalCoin
-- Read "COMPILE_INSTRUCTIONS" file with instructions
+Also, consider a donation at PascalCoin development account: "0-10"
 
-To run:
-- Windows: libeay32.dll - Can be downloaded from https://indy.fulgan.com/SSL/
-- Linux: libcrypto.so.1.0.0 or libcrypto.so.1.1 (Instructions in "COMPILE_INSTRUCTIONS" file)
+## History:  
 
+### Build 1.1.0.0 - 2016-11-03
 
-Enjoy Pascal Coin!
+- JSON-RPC Server included
+- Minor changes
 
-If you like it, consider a donation using BitCoin:
-16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
 
-History:
+### Build 1.0.9.0 - 2016-10-21
 
-Build 1.0.9.0 - 2016-10-21
---------------------------
 - Corrected a BUG (BUG-101) that causes blocking connections when received more than 100 connections, causing "alone in the world" after a cert period time.
 - It's necessary to update because new version will refuse old versions to make network stable.
 
 
-Build 1.0.8.0 - 2016-10-20
---------------------------
+### Build 1.0.8.0 - 2016-10-20
+
 - Cross compatible
 - Can compile with Delphi or Lazarus (Free Pascal)
 - New storage system. No more access database
 - Network hashrate calculation
 
 
-Build 1.0.7.0 - 2016-10-10
---------------------------
+### Build 1.0.7.0 - 2016-10-10
+
 - Introducing basic JSON-RPC to allow GPU miners development (Third party).
 - See file "HOWTO_DEVELOP_GPU_MINER_FOR_PASCALCOIN.txt"
 - No more CPU mining due exists GPU mining
 
 
-Build 1.0.6.0 - 2016-10-04
---------------------------
+### Build 1.0.6.0 - 2016-10-04
+
 - Memory leaks corrections
 - Introducing net protocol 2-3
 - Source code modified, next build will be compiled with Lazarus and FPC
 
 
-Build 1.0.5.0 - 2016-09-21
---------------------------
+### Build 1.0.5.0 - 2016-09-21
+
 - Massive operations, selecting multiple accounts
 - Filter accounts by balance
 - Correct operations explorer order of operations for each block (descending order)
 - Minor changes
 
 
-Build 1.0.4.0 - 2016-09-16
---------------------------
+### Build 1.0.4.0 - 2016-09-16
+
 - IMPORTANT: Introducing net protocol changes: Must update!
 - More and more and more stable
 - Prevents "Alone in the world" if everybody is updated ;-)
@@ -82,8 +85,8 @@ Build 1.0.4.0 - 2016-09-16
 - IP nodes configurator
 
 
-Build 1.0.3.0 - 2016-09-08
---------------------------
+### Build 1.0.3.0 - 2016-09-08
+
 - Important changes to database 
 - Peer cache
 - Issues with Connections
@@ -92,8 +95,8 @@ Build 1.0.3.0 - 2016-09-08
 - Invalid local time detector
 
 
-Build 1.0.2.0 - 2016-08-31
---------------------------
+### Build 1.0.2.0 - 2016-08-31
+
 - Improved hashing speed
 - Allow mining without opening external ports
 - Choose how many CPU's want to mine
@@ -101,12 +104,14 @@ Build 1.0.2.0 - 2016-08-31
 - More stable
 - Some miner modifications
 
-Build 1.0.1.0 - 2016-08-12
---------------------------
+### 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.
+### Build 1.0.0.0 - 2016-08-11
+
+- First stable version.
+- Created with Genesis block hardcoded
+- Published at same time than Genesis block. NO PREMINE

+ 17 - 11
Units/Forms/UFRMAbout.lfm

@@ -151,7 +151,7 @@ object FRMAbout: TFRMAbout
     Left = 90
     Height = 25
     Top = 15
-    Width = 425
+    Width = 382
     Caption = 'Pascal Coin Wallet, Miner && Explorer'
     Font.Color = clBlack
     Font.Height = -21
@@ -164,7 +164,7 @@ object FRMAbout: TFRMAbout
     Left = 15
     Height = 13
     Top = 356
-    Width = 36
+    Width = 30
     Caption = 'Build:'
     Font.Color = clWindowText
     Font.Height = -11
@@ -177,7 +177,7 @@ object FRMAbout: TFRMAbout
     Left = 15
     Height = 13
     Top = 375
-    Width = 57
+    Width = 50
     Caption = 'Protocol:'
     Font.Color = clWindowText
     Font.Height = -11
@@ -190,7 +190,7 @@ object FRMAbout: TFRMAbout
     Left = 90
     Height = 13
     Top = 320
-    Width = 76
+    Width = 65
     Caption = 'Source Code:'
     ParentColor = False
   end
@@ -198,7 +198,7 @@ object FRMAbout: TFRMAbout
     Left = 90
     Height = 13
     Top = 339
-    Width = 154
+    Width = 135
     Caption = 'Check For New Versions:'
     Font.Color = clWindowText
     Font.Height = -11
@@ -212,7 +212,7 @@ object FRMAbout: TFRMAbout
     Left = 170
     Height = 13
     Top = 320
-    Width = 260
+    Width = 240
     Caption = 'https://github.com/PascalCoin/PascalCoin'
     Font.Color = clBlue
     Font.Height = -11
@@ -227,7 +227,7 @@ object FRMAbout: TFRMAbout
     Left = 237
     Height = 13
     Top = 339
-    Width = 270
+    Width = 253
     Caption = 'https://sourceforge.net/projects/pascalcoin'
     Font.Color = clBlue
     Font.Height = -11
@@ -246,14 +246,20 @@ object FRMAbout: TFRMAbout
     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 and Explore blocks and operations.'
+      'This software is a Node of the Pascal Coin P2P Cryptocurrency. A Crypto currency without '
+      'need of historical operations. It can be used to Mine and Explore blocks and operations.'
       ''
-      'Distributed under the MIT software license, see the accompanying file LICENSE  or visit http://www.opensource.org/licenses/mit-license.php.'
+      '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 only.'
       ''
-      'This product includes software developed by the OpenSSL Project and Denis Grinyuk (https://github.com/Arvur/OpenSSL-Delphi), some cryptographic functions inspirated in code written by Ladar Levison and Marco Ferrante, and Synapse Socket code copyright of Lukas Gebauer.'
-      'Original source code is written in Pascal Language and is available at https://github.com/PascalCoin/PascalCoin'
+      'This product includes software developed by the OpenSSL Project and Denis Grinyuk '
+      '(https://github.com/Arvur/OpenSSL-Delphi), some cryptographic functions inspirated in code '
+      'written by Ladar Levison and Marco Ferrante, and Synapse Socket code copyright of Lukas '
+      'Gebauer.'
+      'Original source code is written in Pascal Language and is available at '
+      'https://github.com/PascalCoin/PascalCoin'
       ''
       'If you like it, consider a donation using BitCoin:'
       '16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk'

+ 37 - 1
Units/Forms/UFRMWallet.dfm

@@ -374,6 +374,10 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
       Caption = 'Accounts Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
         Left = 380
         Top = 66
@@ -579,6 +583,10 @@ object FRMWallet: TFRMWallet
         TabOrder = 2
         object tsAccountOperations: TTabSheet
           Caption = 'Operations of selected Account'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
             Left = 0
             Top = 0
@@ -598,6 +606,10 @@ object FRMWallet: TFRMWallet
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected accounts for massive operations'
           ImageIndex = 1
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgSelectedAccounts: TDrawGrid
             Left = 41
             Top = 31
@@ -787,6 +799,10 @@ object FRMWallet: TFRMWallet
     object tsPendingOperations: TTabSheet
       Caption = 'Pending Operations'
       ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object dgPendingOperations: TDrawGrid
         Left = 0
         Top = 86
@@ -834,6 +850,10 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
       Caption = 'BlockChain Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel2: TPanel
         Left = 0
         Top = 0
@@ -879,6 +899,10 @@ object FRMWallet: TFRMWallet
     object tsOperations: TTabSheet
       Caption = 'Operations Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel1: TPanel
         Left = 0
         Top = 0
@@ -924,6 +948,10 @@ object FRMWallet: TFRMWallet
     object tsLogs: TTabSheet
       Caption = 'Logs'
       ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object pnlTopLogs: TPanel
         Left = 0
         Top = 0
@@ -953,6 +981,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         841
         404)
@@ -1015,6 +1047,10 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       ImageIndex = 6
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         841
         404)
@@ -1243,7 +1279,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Top = 180
     Bitmap = {
-      494C010102000800640110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C0101020008007C0110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 26 - 10
Units/Forms/UFRMWallet.pas

@@ -29,7 +29,7 @@ uses
   Dialogs, ExtCtrls, ComCtrls, UWalletKeys, StdCtrls,
   ULog, DB, Grids, DBGrids, UAppParams,
   UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
-  UNetProtocol, UCrypto, Buttons, UPoolMining;
+  UNetProtocol, UCrypto, Buttons, UPoolMining, URPC;
 
 type
   TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
@@ -213,7 +213,7 @@ type
     { Private declarations }
     FNode : TNode;
     FIsActivated : Boolean;
-    FWalletKeys : TWalletKeys;
+    FWalletKeys : TWalletKeysExt;
     FLog : TLog;
     FAppParams : TAppParams;
     FNodeNotifyEvents : TNodeNotifyEvents;
@@ -230,6 +230,7 @@ type
     FMinAccountBalance : Int64;
     FMaxAccountBalance : Int64;
     FPoolMiningServer : TPoolMiningServer;
+    FRPCServer : TRPCServer;
     //Procedure CheckMining;
     Procedure OnNewAccount(Sender : TObject);
     Procedure OnReceivedHelloMessage(Sender : TObject);
@@ -259,7 +260,7 @@ type
     Function DoUpdateAccountsFilter : Boolean;
   public
     { Public declarations }
-    Property WalletKeys : TWalletKeys read FWalletKeys;
+    Property WalletKeys : TWalletKeysExt read FWalletKeys;
     Property MinersBlocksFound : Integer read FMinersBlocksFound write SetMinersBlocksFound;
   end;
 
@@ -326,6 +327,11 @@ begin
     FNode := TNode.Node;
     FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     FNode.PeerCache := FAppParams.ParamByName[CT_PARAM_PeerCache].GetAsString('')+';'+CT_Discover_IPs;
+    // Create RPC server
+    FRPCServer := TRPCServer.Create;
+    FRPCServer.WalletKeys := WalletKeys;
+    FRPCServer.Active := true;
+    WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
     TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
@@ -706,6 +712,7 @@ end;
 procedure TFRMWallet.FormCreate(Sender: TObject);
 Var i : Integer;
 begin
+  FRPCServer := Nil;
   FMinAccountBalance := 0;
   FMaxAccountBalance := CT_MaxWalletAmount;
   FMessagesUnreadCount := 0;
@@ -720,7 +727,7 @@ begin
   FOrderedAccountsKeyList := Nil;
   TimerUpdateStatus.Enabled := false;
   FIsActivated := false;
-  FWalletKeys := TWalletKeys.Create(Self);
+  FWalletKeys := TWalletKeysExt.Create(Self);
   for i := 0 to StatusBar.Panels.Count - 1 do begin
     StatusBar.Panels[i].Text := '';
   end;
@@ -779,6 +786,7 @@ Var i : Integer;
 begin
   TLog.NewLog(ltinfo,Classname,'Destroying form - START');
   Try
+  FreeAndNil(FRPCServer);
   FreeAndNil(FPoolMiningServer);
   step := 'Saving params';
   SaveAppParams;
@@ -1201,9 +1209,16 @@ begin
         if P.last_connection>0 then begin
           s := s + ' Last connection: '+DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection)));
         end;
+        if P.last_connection_by_server>0 then begin
+          s := s + ' Last server connection: '+DateTimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(P^.last_connection_by_server)));
+        end;
         if (P.last_attempt_to_connect>0) then begin
-          s := s + ' Last attempt to connect: '+DateTimeToStr(P^.last_attempt_to_connect)+' (Attempts: '+inttostr(P^.total_failed_attemps_to_connect)+')';
+          s := s + ' Last attempt to connect: '+DateTimeToStr(P^.last_attempt_to_connect);
+        end;
+        if (P.total_failed_attemps_to_connect>0) then begin
+          s := s + ' (Attempts: '+inttostr(P^.total_failed_attemps_to_connect)+')';
         end;
+
         strings.Add(s);
       end;
     Finally
@@ -1455,7 +1470,7 @@ end;
 
 procedure TFRMWallet.UpdateAccounts(RefreshData : Boolean);
 Var accl : TOrderedCardinalList;
-  l : TList;
+  l : TOrderedCardinalList;
   i,j,k : Integer;
   c  : Cardinal;
   applyfilter : Boolean;
@@ -1482,9 +1497,9 @@ begin
               l := FOrderedAccountsKeyList.AccountKeyList[j];
               for k := 0 to l.Count - 1 do begin
                 if applyfilter then begin
-                  acc := FNode.Bank.SafeBox.Account(Cardinal(l[k]));
+                  acc := FNode.Bank.SafeBox.Account(l.Get(k));
                   if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
-                end else accl.Add(Cardinal(l[k]));
+                end else accl.Add(l.Get(k));
               end;
             end;
           end;
@@ -1496,9 +1511,9 @@ begin
               l := FOrderedAccountsKeyList.AccountKeyList[j];
               for k := 0 to l.Count - 1 do begin
                 if applyfilter then begin
-                  acc := FNode.Bank.SafeBox.Account(Cardinal(l[k]));
+                  acc := FNode.Bank.SafeBox.Account(l.Get(k));
                   if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
-                end else accl.Add(Cardinal(l[k]));
+                end else accl.Add(l.Get(k));
               end;
             end;
           end;
@@ -1638,6 +1653,7 @@ begin
     FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     FNode.NetServer.Active := wa;
     FNode.Operations.BlockPayload := FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('');
+    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
   end;
   if Assigned(FPoolMiningServer) then begin
     if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin

+ 1 - 1
Units/PascalCoin/UAES.pas

@@ -52,7 +52,7 @@ uses
 {$IFnDEF FPC}
   Windows,
 {$ELSE}
-  LCLIntf, LCLType, LMessages,
+  {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   UOpenSSL, UOpenSSLdef;
 

+ 49 - 50
Units/PascalCoin/UAccounts.pas

@@ -75,6 +75,28 @@ Type
     4 + (5 * 220) + 4 + 32 = 1140 max aprox
   }
 
+  TOrderedCardinalList = Class
+  private
+    FOrderedList : TList;
+    FDisabledsCount : Integer;
+    FModifiedWhileDisabled : Boolean;
+    FOnListChanged: TNotifyEvent;
+    Procedure NotifyChanged;
+  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;
+    Function Find(const Value: Cardinal; var Index: Integer): Boolean;
+    Procedure Disable;
+    Procedure Enable;
+    Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
+    Procedure CopyFrom(Sender : TOrderedCardinalList);
+  End;
+
   TPCSafeBox = Class;
 
   // This is a class to quickly find accountkeys and their respective account number/s
@@ -84,10 +106,10 @@ Type
     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 GetAccountKeyList(index: Integer): TOrderedCardinalList;
     function GetAccountKey(index: Integer): TAccountKey;
   protected
-    Procedure Clear(RemoveAccountList : Boolean);
+    Procedure ClearAccounts(RemoveAccountList : Boolean);
   public
     Constructor Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
     Destructor Destroy; override;
@@ -96,9 +118,11 @@ Type
     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 AccountKeyList[index : Integer] : TOrderedCardinalList read GetAccountKeyList;
     Property AccountKey[index : Integer] : TAccountKey read GetAccountKey;
     Function Count : Integer;
+    Property SafeBox : TPCSafeBox read FAccountList;
+    Procedure Clear;
   End;
 
 
@@ -193,28 +217,6 @@ Type
     class Function ReadAnsiString(Stream: TStream; var value: AnsiString): Integer;
   End;
 
-  TOrderedCardinalList = Class
-  private
-    FOrderedList : TList;
-    FDisabledsCount : Integer;
-    FModifiedWhileDisabled : Boolean;
-    FOnListChanged: TNotifyEvent;
-    Procedure NotifyChanged;
-  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;
-    Function Find(const Value: Cardinal; var Index: Integer): Boolean;
-    Procedure Disable;
-    Procedure Enable;
-    Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
-    Procedure CopyFrom(Sender : TOrderedCardinalList);
-  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 = (
@@ -765,7 +767,7 @@ begin
   end;
   FBlockAccountsList.Clear;
   For i:=0 to FListOfOrderedAccountKeysList.count-1 do begin
-    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).Clear(False);
+    TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).ClearAccounts(False);
   end;
   FBufferBlocksHash := '';
   FTotalBalance := 0;
@@ -1271,7 +1273,7 @@ end;
 Type
   TOrderedAccountKeyList = Record
     rawaccountkey : TRawBytes;
-    accounts : TList;
+    accounts_number : TOrderedCardinalList;
   end;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
 
@@ -1287,7 +1289,7 @@ begin
   if Not Find(AccountKey,i) then begin
     New(P);
     P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
-    P^.accounts := TList.Create;
+    P^.accounts_number := TOrderedCardinalList.Create;
     FOrderedAccountKeysList.Insert(i,P);
     // Search this key in the AccountsList and add all...
     j := 0;
@@ -1295,7 +1297,7 @@ 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));
+          P^.accounts_number.Add(i);
         end;
       end;
       TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d of %d) %s',[j,FAccountList.AccountsCount,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
@@ -1307,40 +1309,37 @@ end;
 
 procedure TOrderedAccountKeysList.AddAccounts(const AccountKey: TAccountKey; accounts: array of Cardinal);
 Var P : POrderedAccountKeyList;
-  i : Integer;
+  i,i2 : 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;
+    P^.accounts_number := TOrderedCardinalList.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;
-    {$IFDEF FPC}
-    P^.accounts.Sort(SortOrdered);
-    {$ELSE}
-    P^.accounts.SortList(SortOrdered);
-    {$ENDIF}
+    P^.accounts_number.Add(accounts[i]);
   end;
 end;
 
-procedure TOrderedAccountKeysList.Clear(RemoveAccountList : Boolean);
+procedure TOrderedAccountKeysList.Clear;
+begin
+  ClearAccounts(true);
+end;
+
+procedure TOrderedAccountKeysList.ClearAccounts(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;
+      P^.accounts_number.Free;
       Dispose(P);
     end else begin
-      P^.accounts.Clear;
+      P^.accounts_number.Clear;
     end;
   end;
   if RemoveAccountList then begin
@@ -1376,7 +1375,7 @@ begin
   if Assigned(FAccountList) then begin
     FAccountList.FListOfOrderedAccountKeysList.Remove(Self);
   end;
-  Clear(true);
+  ClearAccounts(true);
   FreeAndNil(FOrderedAccountKeysList);
   inherited;
 end;
@@ -1413,9 +1412,9 @@ begin
   Result := TAccountComp.RawString2Accountkey(raw);
 end;
 
-function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TList;
+function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
 begin
-  Result := POrderedAccountKeyList(FOrderedAccountKeysList[index]).accounts;
+  Result := POrderedAccountKeyList(FOrderedAccountKeysList[index]).accounts_number;
 end;
 
 function TOrderedAccountKeysList.IndexOfAccountKey(const AccountKey: TAccountKey): Integer;
@@ -1430,13 +1429,13 @@ 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]));
+    P^.accounts_number.Remove(accounts[j]);
   end;
-  if (P^.accounts.Count=0) And (FAutoAddAll) then begin
+  if (P^.accounts_number.Count=0) And (FAutoAddAll) then begin
     // Remove from list
     FOrderedAccountKeysList.Delete(i);
     // Free it
-    P^.accounts.free;
+    P^.accounts_number.free;
     Dispose(P);
   end;
 end;
@@ -1450,7 +1449,7 @@ begin
   // Remove from list
   FOrderedAccountKeysList.Delete(i);
   // Free it
-  P^.accounts.free;
+  P^.accounts_number.free;
   Dispose(P);
 end;
 

+ 421 - 256
Units/PascalCoin/UBlockChain.pas

@@ -132,14 +132,19 @@ Type
   TOperationResume = Record
     Block : Cardinal;
     NOpInsideBlock : Integer;
+    OpType : Word;
     time : Cardinal;
     AffectedAccount : Cardinal;
+    SenderAccount : Int64; // only used when is a transaction
+    DestAccount : Int64; // only used when is a transaction
+    newKey : TAccountKey;
     OperationTxt : AnsiString;
     Amount : Int64;
     Fee : Int64;
     Balance : Int64;
     OriginalPayload : TRawBytes;
     PrintablePayload : AnsiString;
+    OperationHash : TRawBytes;
   end;
 
   TOperationsResumeList = Class
@@ -156,6 +161,8 @@ Type
     Property OperationResume[index : Integer] : TOperationResume read GetOperationResume; default;
   End;
 
+  { TPCOperation }
+
   TPCOperation = Class
   Private
     Ftag: integer;
@@ -170,11 +177,12 @@ Type
     function LoadFromStream(Stream: TStream): Boolean; virtual; abstract;
     procedure AffectedAccounts(list : TList); virtual; abstract;
     class function OpType: Byte; virtual; abstract;
-    Class Function OperationToOperationResume(Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;
+    Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;
     function OperationAmount : Int64; virtual; abstract;
     function OperationFee: UInt64; virtual; abstract;
     function OperationPayload : TRawBytes; virtual; abstract;
     function SenderAccount : Cardinal; virtual; abstract;
+    function N_Operation : Cardinal; virtual; abstract;
     Property tag : integer read Ftag Write Ftag;
     // Property AuxBalance : Int64 read FAuxBalance Write FAuxBalance; Deprecated, not used
     // New Build 1.0.8 To save previous updated block in storage
@@ -182,6 +190,8 @@ Type
     function LoadFromStorage(Stream: TStream): Boolean;
     Property Previous_Sender_updated_block : Cardinal read FPrevious_Sender_updated_block;
     Property Previous_Destination_updated_block : Cardinal read FPrevious_Destination_updated_block;
+    Class function OperationHash(op : TPCOperation; Block : Cardinal) : TRawBytes;
+    Class function DecodeOperationHash(Const operationHash : TRawBytes; var block, account,n_operation : Cardinal) : Boolean;
   End;
 
   TOperationsHashTree = Class
@@ -205,6 +215,8 @@ Type
     Property TotalFee : Int64 read FTotalFee;
   End;
 
+  { TPCOperationsComp }
+
   TPCOperationsComp = Class(TComponent)
   private
     FBank: TPCBank;
@@ -217,6 +229,7 @@ Type
     FIsOnlyOperationBlock: Boolean;
     FStreamPoW : TMemoryStream;
     FDisableds : Integer;
+    FOperationsLock : TCriticalSection;
     function GetOperation(index: Integer): TPCOperation;
     procedure SetBank(const value: TPCBank);
     procedure SetnOnce(const value: Cardinal);
@@ -260,6 +273,8 @@ Type
     //
     Function ValidateOperationBlock(var errors : AnsiString) : Boolean;
     Property IsOnlyOperationBlock : Boolean read FIsOnlyOperationBlock;
+    Procedure Lock;
+    Procedure Unlock;
     //
     Procedure SanitizeOperations;
 
@@ -377,7 +392,7 @@ Type
   End;
 
 Const
-  CT_TOperationResume_NUL : TOperationResume = (Block:0;NOpInsideBlock:-1;time:0;AffectedAccount:0; OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:'';PrintablePayload:'');
+  CT_TOperationResume_NUL : TOperationResume = (Block:0;NOpInsideBlock:-1;OpType:0;time:0;AffectedAccount:0;SenderAccount:-1;DestAccount:-1;newKey:(EC_OpenSSL_NID:0;x:'';y:'');OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:'';PrintablePayload:'';OperationHash:'');
 
   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:'');
@@ -385,7 +400,8 @@ Const
 implementation
 
 uses
-  Messages, SysUtils, Variants, {Graphics,}
+  {Messages, }
+  SysUtils, Variants, {Graphics,}
   {Controls, Forms,}
   Dialogs, {StdCtrls,}
   UTime, UConst, UOpTransaction;
@@ -942,27 +958,33 @@ end;
 var
   _OperationsClass: Array of TPCOperationClass;
 
-Function TPCOperationsComp.AddOperation(Execute : Boolean; op: TPCOperation; var errors: AnsiString): Boolean;
+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;
+  Lock;
+  Try
+    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;
+      if FDisableds<=0 then Calc_Digest_Parts;
     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;
-    if FDisableds<=0 then Calc_Digest_Parts;
+  finally
+    Unlock;
   end;
 End;
 
@@ -971,24 +993,30 @@ function TPCOperationsComp.AddOperations(operations: TOperationsHashTree; var er
 Var i : Integer;
   e : AnsiString;
 begin
-  Result := 0;
-  errors := '';
-  if operations=FOperationsHashTree then exit;
-  inc(FDisableds);
+  Lock;
   try
-    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);
+    Result := 0;
+    errors := '';
+    if operations=FOperationsHashTree then exit;
+    inc(FDisableds);
+    try
+      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;
+    finally
+      Dec(FDisableds);
+      Calc_Digest_Parts;
     end;
   finally
-    Dec(FDisableds);
-    Calc_Digest_Parts;
+    Unlock;
   end;
 end;
 
-Procedure TPCOperationsComp.CalcProofOfWork(fullcalculation : Boolean; var PoW: TRawBytes);
+procedure TPCOperationsComp.CalcProofOfWork(fullcalculation: Boolean;
+  var PoW: TRawBytes);
 begin
   if fullcalculation then begin
     Calc_Digest_Parts;
@@ -1034,6 +1062,7 @@ end;
 
 procedure TPCOperationsComp.Clear(DeleteOperations : Boolean);
 begin
+  Lock;
   Try
     if DeleteOperations then begin
       FOperationsHashTree.ClearHastThree;
@@ -1066,22 +1095,33 @@ begin
     FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
     FIsOnlyOperationBlock := false;
   Finally
-    CalcProofOfWork(true,FOperationBlock.proof_of_work);
+    try
+      CalcProofOfWork(true,FOperationBlock.proof_of_work);
+    finally
+      Unlock;
+    end;
   End;
 end;
 
 procedure TPCOperationsComp.CopyFrom(Operations: TPCOperationsComp);
 begin
   if Self=Operations then exit;
-  FOperationBlock := Operations.FOperationBlock;
-  FIsOnlyOperationBlock := Operations.FIsOnlyOperationBlock;
-  FOperationsHashTree.CopyFromHashTree(Operations.FOperationsHashTree);
-  if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
-    FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
+  Lock;
+  Operations.Lock;
+  Try
+    FOperationBlock := Operations.FOperationBlock;
+    FIsOnlyOperationBlock := Operations.FIsOnlyOperationBlock;
+    FOperationsHashTree.CopyFromHashTree(Operations.FOperationsHashTree);
+    if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
+      FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
+    end;
+    FDigest_Part1 := Operations.FDigest_Part1;
+    FDigest_Part2_Payload := Operations.FDigest_Part2_Payload;
+    FDigest_Part3 := Operations.FDigest_Part3;
+  finally
+    Operations.Unlock;
+    Unlock;
   end;
-  FDigest_Part1 := Operations.FDigest_Part1;
-  FDigest_Part2_Payload := Operations.FDigest_Part2_Payload;
-  FDigest_Part3 := Operations.FDigest_Part3;
 end;
 
 function TPCOperationsComp.CopyFromAndValidate(Operations: TPCOperationsComp; var errors: AnsiString): Boolean;
@@ -1089,44 +1129,54 @@ 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;
+  Lock;
+  Try
+    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;
-    FOperationsHashTree.AddOperationToHashTree(op);
+    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);
+  finally
+    Unlock;
   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;
-  if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
-    FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
+  Lock;
+  Try
+    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;
+    if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
+      FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
+    end;
+    // Recalc all
+    CalcProofOfWork(true,FOperationBlock.proof_of_work);
+  finally
+    Unlock;
   end;
-  // Recalc all
-  CalcProofOfWork(true,FOperationBlock.proof_of_work);
 end;
 
 function TPCOperationsComp.Count: Integer;
@@ -1137,6 +1187,7 @@ end;
 constructor TPCOperationsComp.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
+  FOperationsLock := TCriticalSection.Create;
   FDisableds := 0;
   FStreamPoW := TMemoryStream.Create;
   FStreamPoW.Position := 0;
@@ -1151,12 +1202,17 @@ end;
 
 destructor TPCOperationsComp.Destroy;
 begin
-  Clear(true);
-  FreeAndNil(FOperationsHashTree);
-  if Assigned(FSafeBoxTransaction) then begin
-    FreeAndNil(FSafeBoxTransaction);
+  FOperationsLock.Acquire;
+  try
+    Clear(true);
+    FreeAndNil(FOperationsHashTree);
+    if Assigned(FSafeBoxTransaction) then begin
+      FreeAndNil(FSafeBoxTransaction);
+    end;
+    FreeAndNil(FStreamPoW);
+  finally
+    FreeAndNil(FOperationsLock);
   end;
-  FreeAndNil(FStreamPoW);
   inherited;
 end;
 
@@ -1277,92 +1333,97 @@ Var
   OpClass: TPCOperationClass;
   errors2 : AnsiString;
 begin
-  Clear(true);
-  Result := False;
-  //
-  errors := 'Invalid protocol structure. Check application version!';
-  if (Stream.Size - Stream.Position < 5) then exit;
-  Stream.Read(soob,1);
-  // About soob var:
-  // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
-  // In build 1.0.4 soob can has 2 more values: 2 or 3
-  // In future, old values 0 and 1 will no longer be used!
-  // - Value 0 and 2 means that contains also operations
-  // - Value 1 and 3 means that only contains operationblock info
-  // - Value 2 and 3 means that contains protocol info prior to block number
-  if (soob=0) or (soob=2) then FIsOnlyOperationBlock:=false
-  else if (soob=1) or (soob=3) then FIsOnlyOperationBlock:=true
-  else begin
-    errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
-    exit;
-  end;
+  Lock;
+  Try
+    Clear(true);
+    Result := False;
+    //
+    errors := 'Invalid protocol structure. Check application version!';
+    if (Stream.Size - Stream.Position < 5) then exit;
+    Stream.Read(soob,1);
+    // About soob var:
+    // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
+    // In build 1.0.4 soob can has 2 more values: 2 or 3
+    // In future, old values 0 and 1 will no longer be used!
+    // - Value 0 and 2 means that contains also operations
+    // - Value 1 and 3 means that only contains operationblock info
+    // - Value 2 and 3 means that contains protocol info prior to block number
+    if (soob=0) or (soob=2) then FIsOnlyOperationBlock:=false
+    else if (soob=1) or (soob=3) then FIsOnlyOperationBlock:=true
+    else begin
+      errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
+      exit;
+    end;
 
-  if (soob=2) or (soob=3) then begin
-    Stream.Read(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
-    Stream.Read(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
-  end;
+    if (soob=2) or (soob=3) then begin
+      Stream.Read(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
+      Stream.Read(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
+    end;
 
-  if Stream.Read(FOperationBlock.block, Sizeof(FOperationBlock.block))<0 then exit;
-
-  if TStreamOp.ReadAnsiString(Stream, m) < 0 then exit;
-  FOperationBlock.account_key := TAccountComp.RawString2Accountkey(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;
-  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
-    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);
+    if Stream.Read(FOperationBlock.block, Sizeof(FOperationBlock.block))<0 then exit;
+
+    if TStreamOp.ReadAnsiString(Stream, m) < 0 then exit;
+    FOperationBlock.account_key := TAccountComp.RawString2Accountkey(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;
+    If FIsOnlyOperationBlock then begin
+      Result := true;
       exit;
     end;
-    errors := 'Invalid operation ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
-    bcop := OpClass.Create;
-    Try
-      if LoadingFromStorage then begin
-        If not bcop.LoadFromStorage(Stream) then exit;
-      end else if not bcop.LoadFromStream(Stream) then begin
+    // 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
+      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;
-      if Not AddOperation(false,bcop, errors2) then begin
-        errors := errors + ' '+errors2+' '+bcop.ToString;
-        exit;
+      errors := 'Invalid operation ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
+      bcop := OpClass.Create;
+      Try
+        if LoadingFromStorage then begin
+          If not bcop.LoadFromStorage(Stream) then exit;
+        end else if not bcop.LoadFromStream(Stream) then begin
+          exit;
+        end;
+        if Not AddOperation(false,bcop, errors2) then begin
+          errors := errors + ' '+errors2+' '+bcop.ToString;
+          exit;
+        end;
+      Finally
+        FreeAndNil(bcop);
       end;
-    Finally
-      FreeAndNil(bcop);
     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;
+    // 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 := false;
-    exit;
+    Result := true;
+  finally
+    Unlock;
   end;
-  Result := true;
 end;
 
 procedure TPCOperationsComp.Notification(AComponent: TComponent;
@@ -1405,6 +1466,7 @@ Var i,n,lastn : Integer;
   errors : AnsiString;
   aux,aux2 : TOperationsHashTree;
 begin
+  Lock;
   Try
     FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
     if Assigned(FBank) then begin
@@ -1448,6 +1510,7 @@ begin
     End;
   Finally
     CalcProofOfWork(true,FOperationBlock.proof_of_work);
+    Unlock;
   End;
   if (n>0) then begin
     TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
@@ -1472,54 +1535,63 @@ Var
   bcop: TPCOperation;
   bcops, errors: AnsiString;
 begin
-  //
-  if save_only_OperationBlock then begin
-    if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 1
-    else soob := 3;
-  end else begin
-    if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 0
-    else soob := 2;
-  end;
-  Stream.Write(soob,1);
-  if (soob>=2) then begin
-    Stream.Write(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
-    Stream.Write(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
-  end;
-  //
-  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);
-      if SaveToStorage then bcop.SaveToStorage(Stream)
-      else bcop.SaveToStream(Stream);
+  Lock;
+  Try
+    //
+    if save_only_OperationBlock then begin
+      if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 1
+      else soob := 3;
+    end else begin
+      if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 0
+      else soob := 2;
+    end;
+    Stream.Write(soob,1);
+    if (soob>=2) then begin
+      Stream.Write(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
+      Stream.Write(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
+    end;
+    //
+    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);
+        if SaveToStorage then bcop.SaveToStorage(Stream)
+        else bcop.SaveToStream(Stream);
+      end;
     end;
+    Result := true;
+  finally
+    Unlock;
   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;
-  Calc_Digest_Parts;
+  Lock;
+  Try
+    if TAccountComp.AccountKey2RawString(value)=TAccountComp.AccountKey2RawString(FOperationBlock.account_key) then exit;
+    FOperationBlock.account_key := value;
+    Calc_Digest_Parts;
+  finally
+    Unlock;
+  end;
 end;
 
 procedure TPCOperationsComp.SetBank(const value: TPCBank);
@@ -1539,95 +1611,130 @@ end;
 procedure TPCOperationsComp.SetBlockPayload(const Value: TRawBytes);
 Var i : Integer;
 begin
-  if Value=FOperationBlock.block_payload then exit;
-  if Length(Value)>CT_MaxPayloadSize then Exit;
-  // Checking Miner Payload valid chars
-  for i := 1 to length(Value) do begin
-    if Not (Value[i] in [#32..#254]) then begin
-      exit;
+  Lock;
+  Try
+    if Value=FOperationBlock.block_payload then exit;
+    if Length(Value)>CT_MaxPayloadSize then Exit;
+    // Checking Miner Payload valid chars
+    for i := 1 to length(Value) do begin
+      if Not (Value[i] in [#32..#254]) then begin
+        exit;
+      end;
     end;
+    FOperationBlock.block_payload := Value;
+    CalcProofOfWork(true,FOperationBlock.proof_of_work);
+  finally
+    Unlock;
   end;
-  FOperationBlock.block_payload := Value;
-  CalcProofOfWork(true,FOperationBlock.proof_of_work);
 end;
 
 procedure TPCOperationsComp.SetnOnce(const value: Cardinal);
 begin
-  FOperationBlock.nonce := value;
-  CalcProofOfWork(false,FOperationBlock.proof_of_work);
+  Lock;
+  Try
+    FOperationBlock.nonce := value;
+    CalcProofOfWork(false,FOperationBlock.proof_of_work);
+  finally
+    Unlock;
+  end;
 end;
 
 procedure TPCOperationsComp.Settimestamp(const value: Cardinal);
 begin
-  if FOperationBlock.timestamp=Value then exit; // No change, nothing to do
-  FOperationBlock.timestamp := value;
-  CalcProofOfWork(false,FOperationBlock.proof_of_work);
+  Lock;
+  Try
+    if FOperationBlock.timestamp=Value then exit; // No change, nothing to do
+    FOperationBlock.timestamp := value;
+    CalcProofOfWork(false,FOperationBlock.proof_of_work);
+  finally
+    Unlock;
+  end;
 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;
+  Lock;
+  Try
+    ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    if Assigned(bank) then begin
+      If Bank.FLastOperationBlock.timestamp>ts then ts := Bank.FLastOperationBlock.timestamp;
+    end;
+    timestamp := ts;
+  finally
+    Unlock;
   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;
+  Lock;
+  Try
+    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;
+    // Checking Miner payload size
+    if length(BlockPayload)>CT_MaxPayloadSize then begin
+      errors := 'Invalid Miner Payload length: '+inttostr(Length(BlockPayload));
       exit;
     end;
-  end;
-  // Checking Miner payload size
-  if length(BlockPayload)>CT_MaxPayloadSize then begin
-    errors := 'Invalid Miner Payload length: '+inttostr(Length(BlockPayload));
-    exit;
-  end;
-  // Checking Miner Payload valid chars
-  for i := 1 to length(BlockPayload) do begin
-    if Not (BlockPayload[i] in [#32..#254]) then begin
-      errors := 'Invalid Miner Payload character at pos '+inttostr(i)+' value:'+inttostr(ord(BlockPayload[i]));
+    // Checking Miner Payload valid chars
+    for i := 1 to length(BlockPayload) do begin
+      if Not (BlockPayload[i] in [#32..#254]) then begin
+        errors := 'Invalid Miner Payload character at pos '+inttostr(i)+' value:'+inttostr(ord(BlockPayload[i]));
+        exit;
+      end;
+    end;
+
+    CalcProofOfWork(true,FOperationBlock.proof_of_work);
+    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;
-  end;
 
-  CalcProofOfWork(true,FOperationBlock.proof_of_work);
-  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;
+    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;
+  finally
+    Unlock;
   end;
+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;
+procedure TPCOperationsComp.Lock;
+begin
+  FOperationsLock.Acquire;
+end;
+
+procedure TPCOperationsComp.Unlock;
+begin
+  FOperationsLock.Release;
 end;
 
 { TPCBankNotify }
@@ -1908,17 +2015,72 @@ begin
   end;
 end;
 
-class function TPCOperation.OperationToOperationResume(Operation: TPCOperation;
+class function TPCOperation.OperationHash(op: TPCOperation; Block : Cardinal): TRawBytes;
+  { OperationHash is a 32 bytes value.
+    First 4 bytes (0..3) are Block in little endian
+    Next 4 bytes (4..7) are Account in little endian
+    Next 4 bytes (8..11) are N_Operation in little endian
+    Next 20 bytes (12..31) are a RipeMD160 of the operation buffer to hash
+    //
+    This format is easy to undecode because include account and n_operation
+   }
+var ms : TMemoryStream;
+  r : TRawBytes;
+  _a,_o : Cardinal;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(Block,4);
+    _a := op.SenderAccount;
+    _o := op.N_Operation;
+    ms.Write(_a,4);
+    ms.Write(_o,4);
+    ms.WriteBuffer(TCrypto.DoRipeMD160(op.GetOperationBufferToHash)[1],20);
+    SetLength(Result,ms.size);
+    ms.Position:=0;
+    ms.Read(Result[1],ms.size);
+  finally
+    ms.Free;
+  end;
+end;
+
+class function TPCOperation.DecodeOperationHash(const operationHash: TRawBytes;
+  var block, account, n_operation: Cardinal): Boolean;
+  { Decodes a previously generated OperationHash }
+var ms : TMemoryStream;
+begin
+  Result := false;
+  account:=0;
+  n_operation:=0;
+  if length(operationHash)<>32 then exit;
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(operationHash[1],length(operationHash));
+    ms.position := 0;
+    ms.Read(block,4);
+    ms.Read(account,4);
+    ms.Read(n_operation,4);
+    Result := true;
+  finally
+    ms.free;
+  end;
+end;
+
+class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation;
   Affected_account_number: Cardinal;
   var OperationResume: TOperationResume): Boolean;
 Var spayload : AnsiString;
 begin
   OperationResume := CT_TOperationResume_NUL;
+  OperationResume.Block:=Block;
   OperationResume.Fee := (-1)*Int64(Operation.OperationFee);
   OperationResume.AffectedAccount := Affected_account_number;
+  OperationResume.OpType:=Operation.OpType;
   Result := false;
   case Operation.OpType of
     CT_Op_Transaction : Begin
+      OperationResume.SenderAccount:=TOpTransaction(Operation).Data.sender;
+      OperationResume.DestAccount:=TOpTransaction(Operation).Data.target;
       if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
         OperationResume.OperationTxt := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
         OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
@@ -1931,6 +2093,7 @@ begin
       end else exit;
     End;
     CT_Op_Changekey : Begin
+      OperationResume.newKey := TOpChangeKey(Operation).Data.new_accountkey;
       OperationResume.OperationTxt := 'Change Key to '+TAccountComp.GetECInfoTxt( TOpChangeKey(Operation).Data.new_accountkey.EC_OpenSSL_NID );
       OperationResume.Fee := TOpChangeKey(Operation).Data.fee;
       Result := true;
@@ -1938,12 +2101,14 @@ begin
     CT_Op_Recover : Begin
       OperationResume.OperationTxt := 'Recover founds';
       OperationResume.Fee := TOpRecoverFounds(Operation).Data.fee;
+      Result := true;
     End;
   else Exit;
   end;
   OperationResume.OriginalPayload := Operation.OperationPayload;
   If TCrypto.IsHumanReadable(OperationResume.OriginalPayload) then OperationResume.PrintablePayload := OperationResume.OriginalPayload
   else OperationResume.PrintablePayload := TCrypto.ToHexaString(OperationResume.OriginalPayload);
+  OperationResume.OperationHash:=TPCOperation.OperationHash(Operation,Block);
 end;
 
 function TPCOperation.SaveToStorage(Stream: TStream): Boolean;

+ 3 - 1
Units/PascalCoin/UConst.pas

@@ -45,6 +45,7 @@ Const
 
   CT_NetServer_Port = {$IFDEF PRODUCTION}4004{$ELSE}{$IFDEF TESTNET}4104{$ELSE}{$ENDIF}{$ENDIF};
   CT_JSONRPCMinerServer_Port = {$IFDEF PRODUCTION}4009{$ELSE}{$IFDEF TESTNET}4109{$ELSE}{$ENDIF}{$ENDIF};
+  CT_JSONRPC_Port = {$IFDEF PRODUCTION}4003{$ELSE}{$IFDEF TESTNET}4103{$ELSE}{$ENDIF}{$ENDIF};
   CT_AccountsPerBlock = 5;
 
   CT_NewLineSecondsAvg: Cardinal = {$IFDEF PRODUCTION}300{$ELSE}{$IFDEF TESTNET}30{$ELSE}{$ENDIF}{$ENDIF};
@@ -68,6 +69,7 @@ Const
   CT_MinCompactTarget: Cardinal = {$IFDEF PRODUCTION}$19000000{$ELSE}{$IFDEF TESTNET}$17000000{$ELSE}{$ENDIF}{$ENDIF}; // First compact target of block 0
 
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
+  CT_MaxAccount : Cardinal = $FFFFFFFF;
   CT_MaxBlock : Cardinal = $FFFFFFFF;
 
   CT_MaxPayloadSize = 255; // Max payload size in bytes
@@ -101,7 +103,7 @@ Const
   CT_Op_Changekey = $02;
   CT_Op_Recover = $03;
 
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.0.9'{$ELSE}{$IFDEF TESTNET}'TESTNET'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.1.0'{$ELSE}{$IFDEF TESTNET}'TESTNET'{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.ddns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
 

+ 1 - 1
Units/PascalCoin/UCrypto.pas

@@ -22,7 +22,7 @@ unit UCrypto;
 interface
 
 uses
-  Classes, SysUtils, UOpenSSL, UOpenSSLDef;
+  Classes, SysUtils, UOpenSSL, UOpenSSLdef;
 
 Type
   ECryptoException = Class(Exception);

+ 30 - 17
Units/PascalCoin/ULog.pas

@@ -52,11 +52,12 @@ type
   private
     FLogDataList : TThreadList;
     FOnNewLog: TNewLogEvent;
+    FOnInThreadNewLog : TNewLogEvent;
     FFileStream : TFileStream;
     FFileName: AnsiString;
     FSaveTypes: TLogTypes;
     FThreadSafeLogEvent : TThreadSafeLogEvent;
-    Procedure NotifyNewLog(logtype : TLogType; Const sender, logtext : String);
+    FProcessGlobalLogs: Boolean;
     procedure SetFileName(const Value: AnsiString);
   protected
     Procedure DoLog(logtype : TLogType; sender, logtext : AnsiString); virtual;
@@ -64,9 +65,12 @@ type
     Constructor Create(AOwner : TComponent); override;
     Destructor Destroy; override;
     Class Procedure NewLog(logtype : TLogType; Const sender, logtext : String);
+    Property OnInThreadNewLog : TNewLogEvent read FOnInThreadNewLog write FOnInThreadNewLog;
     Property OnNewLog : TNewLogEvent read FOnNewLog write FOnNewLog;
     Property FileName : AnsiString read FFileName write SetFileName;
     Property SaveTypes : TLogTypes read FSaveTypes write FSaveTypes;
+    Property ProcessGlobalLogs : Boolean read FProcessGlobalLogs write FProcessGlobalLogs;
+    Procedure NotifyNewLog(logtype : TLogType; Const sender, logtext : String);
   End;
 
 Const
@@ -88,11 +92,11 @@ constructor TLog.Create(AOwner: TComponent);
 Var l : TList;
 begin
   inherited;
-
+  FProcessGlobalLogs := true;
   FLogDataList := TThreadList.Create;
   FFileStream := Nil;
   FFileName := '';
-  FSaveTypes := [ltinfo, ltupdate, lterror];
+  FSaveTypes := CT_TLogTypes_DEFAULT;
   if (Not assigned(_logs)) then _logs := TList.Create;
   _logs.Add(self);
   FThreadSafeLogEvent := TThreadSafeLogEvent.Create(true);
@@ -135,7 +139,9 @@ 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);
+    if (TLog(_logs[i]).FProcessGlobalLogs) then begin
+      TLog(_logs[i]).NotifyNewLog(logtype,sender,logtext);
+    end;
   end;
 end;
 
@@ -149,14 +155,19 @@ begin
     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);
+  if Assigned(FOnInThreadNewLog) then begin
+    FOnInThreadNewLog(logtype,now,TThread.CurrentThread.ThreadID,sender,logtext);
+  end;
+  if Assigned(FOnNewLog) then begin
+    // 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);
+  end;
   DoLog(logtype,sender,logtext);
 end;
 
@@ -184,7 +195,9 @@ procedure TThreadSafeLogEvent.BCExecute;
 begin
   while (not Terminated) do begin
     sleep(100);
-    If Not Terminated then Synchronize(SynchronizedProcess);
+    If (Not Terminated) And (Assigned(FLog.OnNewLog)) then begin
+      Synchronize(SynchronizedProcess);
+    end;
   end;
 end;
 
@@ -202,12 +215,12 @@ begin
   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]);
+      for i := 0 to l.Count - 1 do begin
+        P := PLogData(l[i]);
+        if Assigned(FLog.FOnNewLog) then begin
           FLog.OnNewLog( P^.Logtype,P^.Time,P^.ThreadID,P^.Sender,P^.Logtext );
-          Dispose(P);
         end;
+        Dispose(P);
       end;
     finally
       // Protection for possible raise

+ 64 - 34
Units/PascalCoin/UNetProtocol.pas

@@ -184,8 +184,10 @@ Type
     FMaxRemoteOperationBlock : TOperationBlock;
     FFixedServers : TNodeServerAddressArray;
     FNetClientsDestroyThread : TNetClientsDestroyThread;
+    FNetConnectionsActive: Boolean;
     Procedure IncStatistics(incActiveConnections,incClientsConnections,incServersConnections,incServersConnectionsWithResponse : Integer; incBytesReceived, incBytesSend : Int64);
-  protected
+
+    procedure SetNetConnectionsActive(const Value: Boolean);  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);
@@ -242,6 +244,7 @@ Type
     Procedure NotifyBlackListUpdated;
     Procedure NotifyReceivedHelloMessage;
     Procedure NotifyStatisticsChanged;
+    Property NetConnectionsActive : Boolean read FNetConnectionsActive write SetNetConnectionsActive;
   End;
 
   TNetConnection = Class(TComponent)
@@ -385,7 +388,13 @@ begin
   l := FNodeServers.LockList;
   try
     i := IndexOfNetClient(l,NodeServerAddress.ip,NodeServerAddress.port);
-    if i>=0 then exit;
+    if i>=0 then begin
+      P := PNodeServerAddress(l[i]);
+      if NodeServerAddress.last_connection>P^.last_connection then P^.last_connection := NodeServerAddress.last_connection;
+      if NodeServerAddress.last_connection_by_server>P^.last_connection_by_server then P^.last_connection_by_server := NodeServerAddress.last_connection_by_server;
+      if NodeServerAddress.last_attempt_to_connect>P^.last_attempt_to_connect then P^.last_attempt_to_connect := NodeServerAddress.last_attempt_to_connect;
+      exit;
+    end;
     New(P);
     P^ := NodeServerAddress;
     l.Add(P);
@@ -540,6 +549,7 @@ end;
 constructor TNetData.Create;
 begin
   TLog.NewLog(ltInfo,ClassName,'TNetData.Create');
+  FNetConnectionsActive := true;
   SetLength(FFixedServers,0);
   FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
   FNetStatistics := CT_TNetStatistics_NUL;
@@ -696,12 +706,12 @@ Var P : PNodeServerAddress;
   tdc : TThreadDiscoverConnection;
   canAdd : Boolean;
 begin
+  if Not FNetConnectionsActive then exit;
   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
@@ -1106,11 +1116,19 @@ begin
   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) And
-        ((Assigned(nsa.netConnection)) Or
-         (nsa.last_connection + (60*60*24) > (currunixtimestamp))) // Only If connected 24h before...
+      If ((nsa.its_myself) Or (nsa.BlackListText='')) // Not a blacklist IP except duplicate connections
+        And
+        ( // I've connected 24h before
+         ((nsa.last_connection>0) And ((Assigned(nsa.netConnection)) Or ((nsa.last_connection + (60*60*24)) > (currunixtimestamp))))
+         Or // Others have connected 24h before
+         ((nsa.last_connection_by_server>0) And ((nsa.last_connection_by_server + (60*60*24)) > (currunixtimestamp)))
+         Or // Peer cache
+         ((nsa.last_connection=0) And (nsa.last_connection_by_server=0))
+        )
+        And
+        ( // Never tried to connect or successfully connected
+          (nsa.total_failed_attemps_to_connect=0)
+        )
         then begin
         SetLength(Result,length(Result)+1);
         Result[high(Result)] := nsa;
@@ -1301,6 +1319,13 @@ begin
   End;
 end;
 
+procedure TNetData.SetNetConnectionsActive(const Value: Boolean);
+begin
+  FNetConnectionsActive := Value;
+  if FNetConnectionsActive then DiscoverServers
+  else DisconnectClients;
+end;
+
 function TNetData.UnRegisterRequest(Sender: TNetConnection; operation: Word; request_id: Cardinal): Boolean;
 Var P : PNetRequestRegistered;
   i : Integer;
@@ -1337,6 +1362,7 @@ begin
   inherited;
   MaxConnections := CT_MaxClientsConnected;
   NetTcpIpClientClass := TBufferedNetTcpIpClient;
+  Port := CT_NetServer_Port;
 end;
 
 procedure TNetServer.OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient);
@@ -1915,7 +1941,6 @@ procedure TNetConnection.DoProcess_Hello(HeaderData: TNetHeaderData; DataBuffer:
   End;
 var op, myLastOp : TPCOperationsComp;
     errors : AnsiString;
-    messagehello : TNetMessage_Hello;
     connection_has_a_server : Word;
     i,c : Integer;
     nsa : TNodeServerAddress;
@@ -1925,7 +1950,6 @@ var op, myLastOp : TPCOperationsComp;
    RawAccountKey : TRawBytes;
    other_version : AnsiString;
 Begin
-  SetLength(messagehello.servers_address,0);
   op := TPCOperationsComp.Create(Nil);
   try
     DataBuffer.Position:=0;
@@ -1963,7 +1987,6 @@ Begin
     end;
 
     if op.LoadBlockFromStream(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;
@@ -1977,14 +2000,8 @@ Begin
           nsa := CT_TNodeServerAddress_NUL;
           TStreamOp.ReadAnsiString(DataBuffer,nsa.ip);
           DataBuffer.Read(nsa.port,2);
-          // New in Build 1.0.2 saved at last_connection_by_server DataBuffer.Read(nsa.last_connection,4);
           DataBuffer.Read(nsa.last_connection_by_server,4);
-          if ((nsa.last_connection_by_server + (60*60*24)) > UnivDateTimeToUnix(DateTime2UnivDateTime(now))) then begin
-            // New in Build 1.0.2: Only stores if connected 24 h before
-            SetLength(messagehello.servers_address,length(messagehello.servers_address)+1);
-            messagehello.servers_address[high(messagehello.servers_address)] := nsa;
-            TNetData.NetData.AddServer(nsa);
-          end;
+          TNetData.NetData.AddServer(nsa);
         end;
         if TStreamOp.ReadAnsiString(DataBuffer,other_version)>=0 then begin
           // Captures version
@@ -2217,7 +2234,7 @@ function TNetConnection.ReadTcpClientBuffer(MaxWaitMiliseconds: Cardinal; var He
 var buffer : Array[1..4096] of byte;
   auxstream : TMemoryStream;
   tc : Cardinal;
-  last_bytes_read : Int64;
+  last_bytes_read, t_bytes_read : Int64;
   //
   operation : Word;
   request_id : Integer;
@@ -2226,6 +2243,7 @@ var buffer : Array[1..4096] of byte;
 
 
 begin
+  t_bytes_read := 0;
   Result := false;
   HeaderData := CT_NetHeaderData;
   BufferData.Size := 0;
@@ -2283,20 +2301,11 @@ begin
             FClientBufferRead.CopyFrom(auxstream,last_bytes_read);
             FClientBufferRead.Position := 0;
             auxstream.Size := 0;
+            inc(t_bytes_read,last_bytes_read);
           end;
         finally
           (Client as TBufferedNetTcpIpClient).ReadBufferUnlock;
         end;
-        if last_bytes_read>0 then begin
-          if Not FHasReceivedData then begin
-            FHasReceivedData := true;
-            if (Self is TNetClient) then
-              TNetData.NetData.IncStatistics(0,0,0,1,last_bytes_read,0)
-            else TNetData.NetData.IncStatistics(0,0,0,0,last_bytes_read,0);
-          end else begin
-            TNetData.NetData.IncStatistics(0,0,0,0,last_bytes_read,0);
-          end;
-        end;
       end;
     until (Result) Or ((GetTickCount > (tc+MaxWaitMiliseconds)) And (last_bytes_read=0));
   finally
@@ -2314,6 +2323,16 @@ begin
       FNetLock.Release;
     End;
   end;
+  if t_bytes_read>0 then begin
+    if Not FHasReceivedData then begin
+      FHasReceivedData := true;
+      if (Self is TNetClient) then
+        TNetData.NetData.IncStatistics(0,0,0,1,t_bytes_read,0)
+      else TNetData.NetData.IncStatistics(0,0,0,0,t_bytes_read,0);
+    end else begin
+      TNetData.NetData.IncStatistics(0,0,0,0,t_bytes_read,0);
+    end;
+  end;
   if (Result) And (HeaderData.header_type=ntp_response) then begin
     TNetData.NetData.UnRegisterRequest(Self,HeaderData.operation,HeaderData.request_id);
   end;
@@ -2516,10 +2535,7 @@ begin
         data.Write(nsa.last_connection,4);
       end;
       // Send client version
-// XXXXXXXXXXXXX
-//      TStreamOp.WriteAnsiString(data,CT_ClientAppVersion{$IFDEF LINUX}+'l'{$ELSE}+'w'{$IFDEF Synapse}+'s'{$ENDIF}{$ENDIF}{$IFDEF OpenSSL10}+'0'{$ELSE}+'1'{$ENDIF});
-      TStreamOp.WriteAnsiString(data,CT_ClientAppVersion{$IFDEF LINUX}+'l'{$ELSE}+'w'{$ENDIF});
-// XXXXXXXXXXXXX
+      TStreamOp.WriteAnsiString(data,CT_ClientAppVersion{$IFDEF LINUX}+'l'{$ELSE}+'w'{$IFDEF Synapse}+'s'{$ENDIF}{$ENDIF}{$IFDEF OpenSSL10}+'0'{$ELSE}+'1'{$ENDIF});
     finally
       op.free;
     end;
@@ -2692,7 +2708,7 @@ Var NC : TNetClient;
 begin
   if Terminated then exit;
 
-  TLog.NewLog(ltdebug,Classname,'Starting discovery of connection '+FNodeServerAddress.ip+':'+InttoStr(FNodeServerAddress.port));
+  TLog.NewLog(ltInfo,Classname,'Starting discovery of connection '+FNodeServerAddress.ip+':'+InttoStr(FNodeServerAddress.port));
   Pnsa := Nil;
   DebugStep := 'Locking list';
   // Register attempt
@@ -2756,6 +2772,7 @@ Var l : TList;
   netserverclientstop : TNetServerClient;
   aux : AnsiString;
   needother : Boolean;
+  newstats : TNetStatistics;
 begin
   FLastCheckTS := GetTickCount;
   while (Not Terminated) do begin
@@ -2770,15 +2787,23 @@ begin
       l := FNetData.FNetConnections.LockList;
       try
         ntotal := l.Count;
+        newstats := CT_TNetStatistics_NUL;
         for i := l.Count-1 downto 0 do begin
           netconn := TNetConnection(l.Items[i]);
           if (netconn is TNetClient) then begin
+            if (netconn.Connected) then begin
+              inc(newstats.ServersConnections);
+              if (netconn.FHasReceivedData) then inc(newstats.ServersConnectionsWithResponse);
+            end;
             if (Not TNetClient(netconn).Connected) And (netconn.CreatedTime+EncodeTime(0,0,5,0)<now) then begin
               // Free this!
               TNetClient(netconn).FinalizeConnection;
               inc(ndeleted);
             end else inc(nactive);
           end else if (netconn is TNetServerClient) then begin
+            if (netconn.Connected) then begin
+              inc(newstats.ClientsConnections);
+            end;
             inc(nserverclients);
             if (Not netconn.FDoFinalizeConnection) then begin
               // Build 1.0.9 BUG-101 Only disconnect old versions prior to 1.0.9
@@ -2797,6 +2822,11 @@ begin
             end;
           end;
         end;
+        // Update stats:
+        FNetData.FNetStatistics.ActiveConnections := newstats.ClientsConnections + newstats.ServersConnections;
+        FNetData.FNetStatistics.ClientsConnections := newstats.ClientsConnections;
+        FNetData.FNetStatistics.ServersConnections := newstats.ServersConnections;
+        FNetData.FNetStatistics.ServersConnectionsWithResponse := newstats.ServersConnectionsWithResponse;
         // Must stop clients?
         if (nserverclients>CT_MaxServersConnected) And // This is to ensure there are more serverclients than clients
            ((nserverclients + nactive + ndeleted)>=CT_MaxClientsConnected) And (Assigned(netserverclientstop)) then begin

+ 167 - 6
Units/PascalCoin/UNode.pas

@@ -30,11 +30,15 @@ unit UNode;
 interface
 
 uses
-  Classes, UBlockChain, UNetProtocol, UMiner, UAccounts, UCrypto, UThread, SyncObjs;
+  Classes, UBlockChain, UNetProtocol, UMiner, UAccounts, UCrypto, UThread, SyncObjs, ULog;
 
 Type
+
+  { TNode }
+
   TNode = Class(TComponent)
   private
+    FNodeLog : TLog;
     FLockNodeOperations : TCriticalSection;
     FNotifyList : TList;
     FBank : TPCBank;
@@ -47,6 +51,8 @@ Type
     Procedure OnBankNewBlock(Sender : TObject);
     Procedure OnMinerThreadTerminate(Sender : TObject);
     Procedure OnMinerNewBlockFound(sender : TMinerThread; Operations : TPCOperationsComp; Var Correct : Boolean);
+    procedure SetNodeLogFilename(const Value: AnsiString);
+    function GetNodeLogFilename: AnsiString;
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
   public
@@ -72,12 +78,16 @@ Type
     //
     Procedure NotifyBlocksChanged;
     //
+    procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDeep : Integer);
+    Function FindOperation(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : Boolean;
+    //
     Procedure AutoDiscoverNodes(Const ips : AnsiString);
     Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
     Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
     Property PeerCache : AnsiString read FPeerCache write FPeerCache;
     Procedure DisableNewBlocks;
     Procedure EnableNewBlocks;
+    Property NodeLogFilename : AnsiString read GetNodeLogFilename write SetNodeLogFilename;
   End;
 
   TNodeNotifyEvents = Class;
@@ -134,7 +144,7 @@ Type
 
 implementation
 
-Uses UOpTransaction, SysUtils, ULog, UConst, UTime;
+Uses UOpTransaction, SysUtils,  UConst, UTime;
 
 var _Node : TNode;
 
@@ -185,6 +195,13 @@ begin
     try
       FOperations.SaveBlockToStream(false,ms);
       Result := Bank.AddNewBlockChainBlock(NewBlockOperations,newBlockAccount,errors);
+      if Result then begin
+        if Assigned(SenderConnection) then begin
+          FNodeLog.NotifyNewLog(ltupdate,SenderConnection.ClassName,Format(';%d;%s;%s',[NewBlockOperations.OperationBlock.block,SenderConnection.ClientRemoteAddr,NewBlockOperations.OperationBlock.block_payload]));
+        end else begin
+          FNodeLog.NotifyNewLog(ltupdate,ClassName,Format(';%d;%s;%s',[NewBlockOperations.OperationBlock.block,'NIL',NewBlockOperations.OperationBlock.block_payload]));
+        end;
+      end;
       FOperations.Clear(true);
       ms.Position:=0;
       If Not FOperations.LoadBlockFromStream(ms,errors2) then begin
@@ -362,7 +379,7 @@ begin
   end;
 end;
 
-procedure TNode.AutoDiscoverNodes(Const ips : AnsiString);
+procedure TNode.AutoDiscoverNodes(const ips: AnsiString);
 Var i,j : Integer;
   nsarr : TNodeServerAddressArray;
 begin
@@ -377,6 +394,8 @@ end;
 
 constructor TNode.Create(AOwner: TComponent);
 begin
+  FNodeLog := TLog.Create(Self);
+  FNodeLog.ProcessGlobalLogs := false;
   RegisterOperationsClass;
   if Assigned(_Node) then raise Exception.Create('Duplicate nodes protection');
   TLog.NewLog(ltInfo,ClassName,'TNode.Create');
@@ -395,8 +414,8 @@ begin
   if Not Assigned(_Node) then _Node := Self;
 end;
 
-class procedure TNode.DecodeIpStringToNodeServerAddressArray(Const Ips: AnsiString;
-  var NodeServerAddressArray: TNodeServerAddressArray);
+class procedure TNode.DecodeIpStringToNodeServerAddressArray(
+  const Ips: AnsiString; var NodeServerAddressArray: TNodeServerAddressArray);
   Function GetIp(var ips_string : AnsiString; var nsa : TNodeServerAddress) : Boolean;
   Const CT_IP_CHARS = ['a'..'z','A'..'Z','0'..'9','.','-','_'];
   var i : Integer;
@@ -491,6 +510,7 @@ begin
     FreeAndNil(FBank);
 
     step := 'inherited';
+    FreeAndNil(FNodeLog);
     inherited;
   Except
     On E:Exception do begin
@@ -526,6 +546,11 @@ begin
   end;
 end;
 
+function TNode.GetNodeLogFilename: AnsiString;
+begin
+  Result := FNodeLog.FileName;
+end;
+
 function TNode.IsBlockChainValid(var WhyNot : AnsiString): Boolean;
 Var unixtimediff : Integer;
 begin
@@ -606,6 +631,136 @@ begin
   end;
 end;
 
+procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDeep: Integer);
+  Procedure DoGetFromBlock(block_number : Cardinal; last_balance : Int64; act_deep : Integer);
+  var opc : TPCOperationsComp;
+    op : TPCOperation;
+    OPR : TOperationResume;
+    l : TList;
+    i : Integer;
+    next_block_number : Cardinal;
+  begin
+    if (act_deep<=0) Or ((block_number<=0) And (block_number>0)) then exit;
+
+    opc := TPCOperationsComp.Create(Nil);
+    Try
+      If not Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
+        TLog.NewLog(lterror,ClassName,'Error searching for block '+inttostr(block_number));
+        exit;
+      end;
+      l := TList.Create;
+      try
+        next_block_number := 0;
+        opc.OperationsHashTree.GetOperationsAffectingAccount(account_number,l);
+        for i := l.Count - 1 downto 0 do begin
+          op := opc.Operation[PtrInt(l.Items[i])];
+          if (i=0) then begin
+            If op.SenderAccount=account_number then next_block_number := op.Previous_Sender_updated_block
+            else next_block_number := op.Previous_Destination_updated_block;
+          end;
+          If TPCOperation.OperationToOperationResume(block_number,Op,account_number,OPR) then begin
+            OPR.NOpInsideBlock := i;
+            OPR.time := opc.OperationBlock.timestamp;
+            OPR.Block := block_number;
+            OPR.Balance := last_balance;
+            last_balance := last_balance - ( OPR.Amount + OPR.Fee );
+            OperationsResume.Add(OPR);
+          end;
+        end;
+        // Is a new block operation?
+        if (TAccountComp.AccountBlock(account_number)=block_number) And ((account_number MOD CT_AccountsPerBlock)=0) then begin
+          OPR := CT_TOperationResume_NUL;
+          OPR.Block := block_number;
+          OPR.time := opc.OperationBlock.timestamp;
+          OPR.AffectedAccount := account_number;
+          OPR.Amount := opc.OperationBlock.reward;
+          OPR.Fee := opc.OperationBlock.fee;
+          OPR.Balance := last_balance;
+          OPR.OperationTxt := 'Blockchain reward';
+          OperationsResume.Add(OPR);
+        end;
+        //
+        opc.Clear(true);
+        if (next_block_number>=0) And (next_block_number<block_number) And (act_deep>0)
+           And (next_block_number >= (account_number DIV CT_AccountsPerBlock)) then DoGetFromBlock(next_block_number,last_balance,act_deep-1);
+      finally
+        l.Free;
+      end;
+    Finally
+      opc.Free;
+    End;
+  end;
+
+Var acc : TAccount;
+begin
+  if MaxDeep<0 then exit;
+  if account_number>=Bank.SafeBox.AccountsCount then exit;
+  acc := Bank.SafeBox.Account(account_number);
+  if (acc.updated_block>0) Or (acc.account=0) then DoGetFromBlock(acc.updated_block,acc.balance,MaxDeep);
+end;
+
+function TNode.FindOperation(const OperationComp: TPCOperationsComp;
+  const OperationHash: TRawBytes; var block: Cardinal;
+  var operation_block_index: Integer): Boolean;
+  { With a OperationHash, search it }
+var account,n_operation : Cardinal;
+  i : Integer;
+  op : TPCOperation;
+  initial_block, aux_block : Cardinal;
+begin
+  Result := False;
+  // Decode OperationHash
+  If not TPCOperation.DecodeOperationHash(OperationHash,block,account,n_operation) then exit;
+  initial_block := block;
+  //
+  If (account>=Bank.AccountsCount) then exit; // Invalid account number
+  // If block=0 then we must search in pending operations first
+  if (block=0) then begin
+    FOperations.Lock;
+    Try
+      For i:=0 to FOperations.Count-1 do begin
+        If (FOperations.Operation[i].SenderAccount=account) then begin
+          If (TPCOperation.OperationHash(FOperations.Operation[i],0)=OperationHash) then begin
+            operation_block_index:=i;
+            OperationComp.CopyFrom(FOperations);
+            Result := true;
+            exit;
+          end;
+        end;
+      end;
+    finally
+      FOperations.Unlock;
+    end;
+    // block=0 and not found... start searching at block updated by account updated_block
+    block := Bank.SafeBox.Account(account).updated_block;
+    if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
+  end;
+  if (block=0) or (block>=Bank.BlocksCount) then exit;
+  // Search in previous blocks
+  While (Not Result) And (block>0) do begin
+    aux_block := block;
+    If Not Bank.LoadOperations(OperationComp,block) then exit;
+    For i:=OperationComp.Count-1 downto 0 do begin
+      op := OperationComp.Operation[i];
+      if (op.SenderAccount=account) then begin
+        If (op.N_Operation<n_operation) then exit; // n_operation is greaten than found
+        If (op.N_Operation=n_operation) then begin
+          // Possible candidate or dead
+          If TPCOperation.OperationHash(op,initial_block)=OperationHash then begin
+            operation_block_index:=i;
+            Result := true;
+            exit;
+          end else exit; // Not found!
+        end;
+        If op.Previous_Sender_updated_block>block then exit;
+        block := op.Previous_Sender_updated_block;
+      end;
+    end;
+    if (block>=aux_block) then exit; // Error... not found a valid block positioning
+    if (initial_block<>0) then exit; // If not found in specified block, no valid hash
+  end;
+end;
+
 procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: AnsiString);
 Var i : Integer;
   s : AnsiString;
@@ -622,7 +777,8 @@ begin
   FOperations.SanitizeOperations;
 end;
 
-procedure TNode.OnMinerNewBlockFound(sender: TMinerThread; Operations: TPCOperationsComp; Var Correct : Boolean);
+procedure TNode.OnMinerNewBlockFound(sender: TMinerThread;
+  Operations: TPCOperationsComp; var Correct: Boolean);
 Var nba : TBlockAccount;
   errors : AnsiString;
 begin
@@ -671,6 +827,11 @@ begin
   end;
 end;
 
+procedure TNode.SetNodeLogFilename(const Value: AnsiString);
+begin
+  FNodeLog.FileName := Value;
+end;
+
 { TNodeNotifyEvents }
 
 constructor TNodeNotifyEvents.Create(AOwner: TComponent);

+ 30 - 0
Units/PascalCoin/UOpTransaction.pas

@@ -50,6 +50,8 @@ Type
     fee: UInt64;
   End;
 
+  { TOpTransaction }
+
   TOpTransaction = Class(TPCOperation)
   private
     FData : TOpTransactionData;
@@ -67,12 +69,15 @@ Type
     function OperationFee : UInt64; override;
     function OperationPayload : TRawBytes; override;
     function SenderAccount : Cardinal; override;
+    function N_Operation : 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 }
+
   TOpChangeKey = Class(TPCOperation)
   private
     FData : TOpChangeKeyData;
@@ -89,12 +94,15 @@ Type
     function OperationFee : UInt64; override;
     function OperationPayload : TRawBytes; override;
     function SenderAccount : Cardinal; override;
+    function N_Operation : 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 }
+
   TOpRecoverFounds = Class(TPCOperation)
   private
     FData : TOpRecoverFoundsData;
@@ -109,6 +117,7 @@ Type
     function OperationFee : UInt64; override;
     function OperationPayload : TRawBytes; override;
     function SenderAccount : Cardinal; override;
+    function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList); override;
     Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
     Property Data : TOpRecoverFoundsData read FData;
@@ -225,6 +234,12 @@ class function TOpTransaction.DoSignOperation(key : TECPrivateKey; var trans : T
 var s : AnsiString;
   _sign : TECDSA_SIG;
 begin
+  If Not Assigned(key.PrivateKey) then begin
+    Result := false;
+    trans.sign.r:='';
+    trans.sign.s:='';
+    exit;
+  end;
   s := GetTransactionHasthToSign(trans);
   Try
     _sign := TCrypto.ECDSASign(key.PrivateKey,s);
@@ -344,6 +359,11 @@ begin
   Result := FData.sender;
 end;
 
+function TOpTransaction.N_Operation: Cardinal;
+begin
+  Result := FData.n_operation;
+end;
+
 function TOpTransaction.toString: String;
 begin
   Result := Format('Transaction from %s to %s amount:%s fee:%s (n_op:%d) payload size:%d',[
@@ -536,6 +556,11 @@ begin
   Result := FData.account;
 end;
 
+function TOpChangeKey.N_Operation: Cardinal;
+begin
+  Result := FData.n_operation;
+end;
+
 function TOpChangeKey.toString: String;
 begin
   Result := Format('Change key of %s to new key: %s fee:%s (n_op:%d) payload size:%d',[
@@ -651,6 +676,11 @@ begin
   Result := FData.account;
 end;
 
+function TOpRecoverFounds.N_Operation: Cardinal;
+begin
+  Result := FData.n_operation;
+end;
+
 function TOpRecoverFounds.toString: String;
 begin
   Result := Format('Recover founds of account %s fee:%s (n_op:%d)',[

+ 4 - 0
Units/PascalCoin/UOpenSSL.pas

@@ -20,6 +20,10 @@ unit UOpenSSL;
 
 interface
 
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
 Uses UOpenSSLdef;
 {$I config.inc}
 

+ 4 - 0
Units/PascalCoin/UOpenSSLdef.pas

@@ -20,6 +20,10 @@ unit UOpenSSLdef;
 
 interface
 
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
 uses {$IFDEF UNIX}BaseUnix {$ELSE} Windows{$ENDIF};
 
 {$REGION 'OBJECT'}

+ 7 - 4
Units/PascalCoin/UPoolMining.pas

@@ -23,7 +23,7 @@ Uses
 {$IFnDEF FPC}
   Windows,
 {$ELSE}
-  LCLIntf, LCLType, LMessages,
+  {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   UTCPIP, SysUtils, UThread, SyncObjs, Classes, UJSONFunctions, UAES, UNode,
   UCrypto, UAccounts;
@@ -434,8 +434,8 @@ begin
       end;
     end;
     nbOperations.BlockPayload := payload;
-    nbOperations.timestamp := params.AsInteger('timestamp',0);
-    nbOperations.nonce := params.AsInteger('nonce',0);
+    nbOperations.timestamp := params.AsCardinal('timestamp',0);
+    nbOperations.nonce := params.AsCardinal('nonce',0);
     p1 := nbOperations.PoW_Digest_Part1;
     p2 := nbOperations.PoW_Digest_Part2_Payload;
     p3 := nbOperations.PoW_Digest_Part3;
@@ -445,13 +445,16 @@ begin
       try
         json.GetAsVariant('block').Value := FNodeNotifyEvents.Node.Bank.LastOperationBlock.block;
         json.GetAsVariant('pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.proof_of_work );
+        json.GetAsVariant('payload').Value := nbOperations.BlockPayload;
+        json.GetAsVariant('timestamp').Value := nbOperations.timestamp;
+        json.GetAsVariant('nonce').Value := nbOperations.nonce;
         inc(FClientsWins);
         Client.SendJSONRPCResponse(json,id);
       finally
         json.Free;
       end;
     end else begin
-      Client.SendJSONRPCErrorResponse(id,'Error: '+errors);
+      Client.SendJSONRPCErrorResponse(id,'Error: '+errors+' payload:'+nbOperations.BlockPayload+' timestamp:'+InttoStr(nbOperations.timestamp)+' nonce:'+IntToStr(nbOperations.nonce));
     end;
   finally
     nbOperations.Free;

+ 1128 - 7
Units/PascalCoin/URPC.pas

@@ -4,19 +4,1140 @@ unit URPC;
   {$MODE Delphi}
 {$ENDIF}
 
+{ Copyright (c) 2016 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
 interface
 
-Uses UTCPIP;
+Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
+  UNetProtocol, UOpTransaction, UWalletKeys, UTime, UAES, UECIES,
+  UJSONFunctions, classes, blcksock, synsock, IniFiles;
+
+Const
+  CT_RPC_ErrNum_InternalError = 100;
+
+  CT_RPC_ErrNum_MethodNotFound = 1001;
+  CT_RPC_ErrNum_InvalidAccount = 1002;
+  CT_RPC_ErrNum_InvalidBlock = 1003;
+  CT_RPC_ErrNum_InvalidOperation = 1004;
+  CT_RPC_ErrNum_InvalidPubKey = 1005;
+  CT_RPC_ErrNum_NotFound = 1010;
+  CT_RPC_ErrNum_WalletPasswordProtected = 1015;
+  CT_RPC_ErrNum_InvalidData = 1016;
+
+Type
+
+  { TRPCServer }
+
+  TRPCServerThread = Class;
+  TRPCServer = Class
+  private
+    FRPCServerThread : TRPCServerThread;
+    FActive: Boolean;
+    FWalletKeys: TWalletKeysExt;
+    FPort: Word;
+    FJSON20Strict: Boolean;
+    FIniFileName: AnsiString;
+    FIniFile : TIniFile;
+    procedure SetActive(AValue: Boolean);
+    procedure SetIniFileName(const Value: AnsiString);
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    Property Port : Word read FPort Write FPort;
+    Property Active : Boolean read FActive write SetActive;
+    Property WalletKeys : TWalletKeysExt read FWalletKeys write FWalletKeys;
+    //
+    Property JSON20Strict : Boolean read FJSON20Strict write FJSON20Strict;
+    Property IniFileName : AnsiString read FIniFileName write SetIniFileName;
+  end;
+
+  { TRPCServerThread }
 
-{Type
-  {TRPCServer = Class(TNetTcpIpServer)
+  TRPCServerThread = Class(TPCThread)
+    FServerSocket:TTCPBlockSocket;
+    FPort : Word;
   protected
-    Procedure OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient); override;
-    procedure SetActive(const Value: Boolean); override;
+    procedure BCExecute; override;
+  public
+    Constructor Create(Port : Word);
+    Destructor Destroy; Override;
+  End;
+
+  { TRPCProcess }
+
+  TRPCProcess = class(TPCThread)
+  private
+    FSock:TTCPBlockSocket;
   public
-    Constructor Create; override;
-  End;}
+    Constructor Create (hsock:tSocket);
+    Destructor Destroy; override;
+    procedure BCExecute; override;
+    function ProcessMethod(Const method : String; params : TPCJSONObject; jsonresponse : TPCJSONObject; Var ErrorNum : Integer; Var ErrorDesc : String) : Boolean;
+  end;
+
 
 implementation
 
+Uses SysUtils, Synautil;
+
+var _RPCServer : TRPCServer = Nil;
+
+{ TRPCServer }
+
+procedure TRPCServer.SetActive(AValue: Boolean);
+begin
+  if FActive=AValue then Exit;
+  FActive:=AValue;
+  if (FActive) then begin
+    FRPCServerThread := TRPCServerThread.Create(FPort);
+  end else begin
+    FRPCServerThread.Terminate;
+    FRPCServerThread.WaitFor;
+    FreeAndNil(FRPCServerThread);
+  end;
+end;
+
+procedure TRPCServer.SetIniFileName(const Value: AnsiString);
+begin
+  if FIniFileName=Value then exit;
+  FreeAndNil(FIniFile);
+  FIniFileName := Value;
+  if (FIniFileName<>'') And (FileExists(FIniFileName)) then begin
+    FIniFile := TIniFile.Create(FIniFileName);
+  end;
+  if Assigned(FIniFile) then begin
+    FJSON20Strict := FIniFile.ReadBool('general','json20strict',true)
+    // TODO
+  end;
+end;
+
+constructor TRPCServer.Create;
+begin
+  FIniFile := Nil;
+  FIniFileName := '';
+  FJSON20Strict := true;
+  FWalletKeys := Nil;
+  FRPCServerThread := Nil;
+  FPort := CT_JSONRPC_Port;
+  If Not assigned(_RPCServer) then _RPCServer := Self;
+end;
+
+destructor TRPCServer.Destroy;
+begin
+  active := false;
+  if _RPCServer=Self then _RPCServer:=Nil;
+  inherited Destroy;
+end;
+
+{ TRPCProcess }
+
+constructor TRPCProcess.Create(hsock: tSocket);
+begin
+  FSock:=TTCPBlockSocket.create;
+  FSock.socket:=HSock;
+  FreeOnTerminate:=true;
+  //Priority:=tpNormal;
+  inherited create(false);
+end;
+
+destructor TRPCProcess.Destroy;
+begin
+  FSock.free;
+  inherited Destroy;
+end;
+
+procedure TRPCProcess.BCExecute;
+var
+  timeout: integer;
+  s: string;
+  method, uri, protocol: string;
+  size: integer;
+  x, n: integer;
+  resultcode: integer;
+  inputdata : TBytes;
+  js,jsresult : TPCJSONData;
+  jsonobj,jsonresponse : TPCJSONObject;
+  errNum : Integer; errDesc : String;
+  jsonrequesttxt,
+  jsonresponsetxt : AnsiString;
+  valid : Boolean;
+  i : Integer;
+  Headers : TStringList;
+begin
+  Headers := TStringList.Create;
+  errNum := CT_RPC_ErrNum_InternalError;
+  errDesc := 'No data';
+  valid := false;
+  SetLength(inputdata,0);
+  jsonresponse := TPCJSONObject.Create;
+  try
+    timeout := 5000;
+    resultcode:= 200;
+    repeat
+      //read request line
+      s := Fsock.RecvString(timeout);
+      if Fsock.lasterror <> 0 then Exit;
+      if s = '' then Exit;
+      method := fetch(s, ' ');
+      if (s = '') or (method = '') then  Exit;
+      uri := fetch(s, ' '); if uri = '' then  Exit;
+      protocol := fetch(s, ' ');
+      headers.Clear;
+      size := -1;
+      //read request headers
+      if protocol <> '' then begin
+        if pos('HTTP/1.1', protocol) <> 1 then begin
+          errDesc := 'Invalid protocol '+protocol;
+          Exit;
+        end;
+        repeat
+          s := Fsock.RecvString(Timeout);
+          if Fsock.lasterror <> 0 then
+            Exit;
+          if Pos('CONTENT-LENGTH:', Uppercase(s)) = 1 then
+            Size := StrToIntDef(SeparateRight(s, ' '), -1);
+        until s = '';
+      end;
+      //recv document...
+      if size >= 0 then begin
+        setLength(inputdata,size);
+        x := FSock.RecvBufferEx(InputData, Size, Timeout);
+        if Fsock.lasterror <> 0 then
+          Exit;
+        if (x<>size) And (x>0) then
+          setLength(inputdata,x);
+      end else setlength(inputdata,0);
+      SetLength(jsonrequesttxt,length(inputdata));
+      for i:=0 to high(inputdata) do begin
+        jsonrequesttxt[i+1] := AnsiChar(inputdata[i]);
+      end;
+      // Convert InputData to JSON object
+      try
+        js := TPCJSONData.ParseJSONValue(jsonrequesttxt);
+      except
+        On E:Exception do begin
+          errDesc:='Error decoding JSON: '+E.Message;
+          TLog.NewLog(lterror,Classname,'Error decoding JSON: '+E.Message);
+          exit;
+        end;
+      end;
+      If Assigned(js) then begin
+        try
+          If (js is TPCJSONObject) then begin
+            jsonobj := TPCJSONObject(js);
+            errNum := 0;
+            errDesc := '';
+            try
+              TLog.NewLog(ltinfo,Classname,'Processing method '+jsonobj.AsString('method',''));
+              Valid := ProcessMethod(jsonobj.AsString('method',''),jsonobj.GetAsObject('params'),jsonresponse,errNum,errDesc);
+              if not Valid then begin
+                if (errNum<>0) or (errDesc<>'') then begin
+                  jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
+                  jsonresponse.GetAsObject('error').GetAsVariant('message').Value:=errDesc;
+                end else begin
+                  jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=CT_RPC_ErrNum_InternalError;
+                  jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
+                end;
+              end;
+            Except
+              on E:Exception do begin
+                TLog.NewLog(lterror,Classname,'Exception processing method'+jsonobj.AsString('method','')+' ('+E.ClassName+'): '+E.Message);
+                jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=CT_RPC_ErrNum_InternalError;
+                jsonresponse.GetAsObject('error').GetAsVariant('message').Value:=E.Message;
+                valid:=False;
+              end;
+            end;
+            jsonresponse.GetAsVariant('id').Value:= jsonobj.GetAsVariant('id').Value;
+            jsonresponse.GetAsVariant('jsonrpc').Value:= '2.0';
+          end;
+        finally
+          js.free;
+        end;
+      end else begin
+        TLog.NewLog(lterror,ClassName,'Received data is not a JSON: '+jsonrequesttxt+' (length '+inttostr(length(jsonrequesttxt))+' bytes)');
+      end;
+    until (FSock.LastError <> 0) Or (protocol<>'');
+  Finally
+    try
+      // Send result:
+      if Fsock.lasterror = 0 then begin
+        // Save JSON response:
+        If (Not Valid) then begin
+          if Not assigned(jsonresponse.FindName('error')) then begin
+            jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
+            jsonresponse.GetAsObject('error').GetAsVariant('message').Value:=errDesc;
+          end;
+        end;
+        jsonresponsetxt := jsonresponse.ToJSON(false);
+        Fsock.SendString(protocol + ' ' + IntTostr(ResultCode) + CRLF);
+        if (protocol <> '') then begin
+          headers.Add('Server: PascalCoin HTTP JSON-RPC Server');
+          headers.Add('Content-Type: application/json;charset=utf-8');
+          headers.Add('Content-length: ' + IntTostr(length(jsonresponsetxt)));
+          headers.Add('Connection: close');
+          headers.Add('Access-Control-Allow-Origin: *');
+          headers.Add('Date: ' + Rfc822DateTime(now));
+          headers.Add('');
+          for n := 0 to headers.count - 1 do
+            Fsock.sendstring(headers[n] + CRLF);
+        end;
+        if Fsock.lasterror = 0 then begin
+          FSock.SendBuffer(addr(jsonresponsetxt[1]),Length(jsonresponsetxt));
+        end;
+      end;
+    finally
+      jsonresponse.free;
+    end;
+  end;
+end;
+
+function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
+  jsonresponse: TPCJSONObject; var ErrorNum: Integer; var ErrorDesc: String): Boolean;
+  var _ro : TPCJSONObject;
+      _ra : TPCJSONArray;
+  Function GetResultObject : TPCJSONObject;
+  begin
+    if not assigned(_ro) then begin
+      _ro := jsonresponse.GetAsObject('result');
+      _ra := Nil;
+    end;
+    Result := _ro;
+  end;
+
+  Function GetResultArray : TPCJSONArray;
+  begin
+    if not assigned(_ro) then begin
+      _ra := jsonresponse.GetAsArray('result');
+      _ro := Nil;
+    end;
+    Result := _ra;
+  end;
+
+  Function ToJSONCurrency(pascalCoins : Int64) : Real;
+  Begin
+    Result := pascalCoins / 10000;
+  End;
+
+  Function ToPascalCoins(jsonCurr : Real) : Int64;
+  Begin
+    Result := Round(jsonCurr * 10000);
+  End;
+
+  Function GetBlock(nBlock : Cardinal; jsonObject : TPCJSONObject) : Boolean;
+  var pcops : TPCOperationsComp;
+  begin
+    pcops := TPCOperationsComp.Create(Nil);
+    try
+      If Not TNode.Node.Bank.LoadOperations(pcops,nBlock) then begin
+        ErrorNum := CT_RPC_ErrNum_InternalError;
+        ErrorDesc := 'Cannot load Block: '+IntToStr(nBlock);
+        Result := False;
+        Exit;
+      end;
+      jsonObject.GetAsVariant('block').Value:=pcops.OperationBlock.block;
+      jsonObject.GetAsVariant('enc_pubkey').Value := TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(pcops.OperationBlock.account_key));
+      jsonObject.GetAsVariant('reward').Value:=ToJSONCurrency(pcops.OperationBlock.reward);
+      jsonObject.GetAsVariant('fee').Value:=ToJSONCurrency(pcops.OperationBlock.fee);
+      jsonObject.GetAsVariant('ver').Value:=pcops.OperationBlock.protocol_version;
+      jsonObject.GetAsVariant('ver_a').Value:=pcops.OperationBlock.protocol_available;
+      jsonObject.GetAsVariant('timestamp').Value:=Int64(pcops.OperationBlock.timestamp);
+      jsonObject.GetAsVariant('target').Value:=Int64(pcops.OperationBlock.compact_target);
+      jsonObject.GetAsVariant('nonce').Value:=Int64(pcops.OperationBlock.nonce);
+      jsonObject.GetAsVariant('payload').Value:=pcops.OperationBlock.block_payload;
+      jsonObject.GetAsVariant('sbh').Value:=TCrypto.ToHexaString(pcops.OperationBlock.initial_safe_box_hash);
+      jsonObject.GetAsVariant('oph').Value:=TCrypto.ToHexaString(pcops.OperationBlock.operations_hash);
+      jsonObject.GetAsVariant('pow').Value:=TCrypto.ToHexaString(pcops.OperationBlock.proof_of_work);
+      jsonObject.GetAsVariant('operations').Value:=pcops.Count;
+      jsonObject.GetAsVariant('hashratekhs').Value := TNode.Node.Bank.SafeBox.CalcBlockHashRateInKhs(pcops.OperationBlock.Block,50);
+      jsonObject.GetAsVariant('maturation').Value := TNode.Node.Bank.BlocksCount - pcops.OperationBlock.block - 1;
+      Result := True;
+    finally
+      pcops.Free;
+    end;
+  end;
+
+  Procedure FillOperationResumeToJSONObject(Const OPR : TOperationResume; jsonObject : TPCJSONObject);
+  Begin
+    jsonObject.GetAsVariant('block').Value:=OPR.Block;
+    jsonObject.GetAsVariant('opblock').Value:=OPR.NOpInsideBlock;
+    jsonObject.GetAsVariant('optype').Value:=OPR.OpType;
+    jsonObject.GetAsVariant('time').Value:=OPR.time;
+    jsonObject.GetAsVariant('account').Value:=OPR.AffectedAccount;
+    jsonObject.GetAsVariant('optxt').Value:=OPR.OperationTxt;
+    jsonObject.GetAsVariant('amount').Value:=ToJSONCurrency(OPR.Amount);
+    jsonObject.GetAsVariant('fee').Value:=ToJSONCurrency(OPR.Fee);
+    if OPR.Balance>=0 then jsonObject.GetAsVariant('balance').Value:=ToJSONCurrency(OPR.Balance);
+    jsonObject.GetAsVariant('payload').Value:=TCrypto.ToHexaString(OPR.OriginalPayload);
+    If OPR.SenderAccount>=0 then begin
+      jsonObject.GetAsVariant('sender_account').Value:=OPR.SenderAccount;
+    end;
+    If OPR.DestAccount>=0 then begin
+      jsonObject.GetAsVariant('dest_account').Value:=OPR.DestAccount;
+    end;
+    If OPR.newKey.EC_OpenSSL_NID>0 then begin
+      jsonObject.GetAsVariant('enc_pubkey').Value := TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(OPR.newKey));
+    end;
+    jsonObject.GetAsVariant('ophash').Value := TCrypto.ToHexaString(OPR.OperationHash);
+  end;
+
+  Function GetAccountOperations(AccountNumber : Cardinal; jsonArray : TPCJSONArray; MaxBlocksDeep : Integer) : Boolean;
+  var list : TList;
+    Op : TPCOperation;
+    OPR : TOperationResume;
+    Obj : TPCJSONObject;
+    OperationsResume : TOperationsResumeList;
+    i : Integer;
+  Begin
+    OperationsResume := TOperationsResumeList.Create;
+    try
+      list := TList.Create;
+      Try
+        TNode.Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
+        for i := list.Count - 1 downto 0 do begin
+          Op := TNode.Node.Operations.OperationsHashTree.GetOperation(PtrInt(list[i]));
+          If TPCOperation.OperationToOperationResume(0,Op,AccountNumber,OPR) then begin
+            OPR.NOpInsideBlock := i;
+            OPR.Block := TNode.Node.Operations.OperationBlock.block;
+            OPR.Balance := TNode.Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
+            OperationsResume.Add(OPR);
+          end;
+        end;
+      Finally
+        list.Free;
+      End;
+      TNode.Node.GetStoredOperationsFromAccount(OperationsResume,AccountNumber,MaxBlocksDeep);
+      //
+      for i:=0 to OperationsResume.Count-1 do begin
+        Obj := jsonArray.GetAsObject(i);
+        OPR := OperationsResume[i];
+        FillOperationResumeToJSONObject(OPR,Obj);
+      end;
+      Result := True;
+    finally
+      OperationsResume.Free;
+    end;
+  end;
+  Procedure GetConnections;
+  var i : Integer;
+    l : TList;
+    nc : TNetConnection;
+    obj: TPCJSONObject;
+  Begin
+    l := TNetData.NetData.ConnectionsLock;
+    try
+      for i:=0 to l.Count-1 do begin
+        nc := TNetData.NetData.Connection(i);
+        obj := jsonresponse.GetAsArray('result').GetAsObject(i);
+        obj.GetAsVariant('server').Value := Not (nc is TNetServerClient);
+        obj.GetAsVariant('ip').Value:=nc.Client.RemoteHost;
+        obj.GetAsVariant('port').Value:=nc.Client.RemotePort;
+        obj.GetAsVariant('secs').Value:=UnivDateTimeToUnix(now) - UnivDateTimeToUnix(nc.CreatedTime);
+        obj.GetAsVariant('sent').Value:=nc.Client.BytesSent;
+        obj.GetAsVariant('recv').Value:=nc.Client.BytesReceived;
+        obj.GetAsVariant('appver').Value:=nc.ClientAppVersion;
+        obj.GetAsVariant('netver').Value:=nc.NetProtocolVersion.protocol_version;
+        obj.GetAsVariant('netver_a').Value:=nc.NetProtocolVersion.protocol_available;
+      end;
+    finally
+      TNetData.NetData.ConnectionsUnlock;
+    end;
+  end;
+
+  Function OpSendTo(sender, target : Cardinal; amount, fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
+  // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+  Var opt : TOpTransaction;
+    sacc,tacc : TAccount;
+    i : Integer;
+    errors : AnsiString;
+    opr : TOperationResume;
+    f_raw : TRawBytes;
+  begin
+    Result := false;
+    if (sender<0) or (sender>=TNode.Node.Bank.AccountsCount) then begin
+      If (sender=CT_MaxAccount) then ErrorDesc := 'Need sender'
+      else ErrorDesc:='Invalid sender account '+Inttostr(sender);
+      ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end;
+    if (target<0) or (target>=TNode.Node.Bank.AccountsCount) then begin
+      If (target=CT_MaxAccount) then ErrorDesc := 'Need target'
+      else ErrorDesc:='Invalid target account '+Inttostr(target);
+      ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end;
+    sacc := TNode.Node.Operations.SafeBoxTransaction.Account(sender);
+    tacc := TNode.Node.Operations.SafeBoxTransaction.Account(target);
+    _RPCServer.FWalletKeys.AccountsKeyList.IndexOfAccountKey(sacc.accountkey);
+    i := _RPCServer.FWalletKeys.IndexOfAccountKey(sacc.accountkey);
+    if (i<0) then begin
+      ErrorDesc:='Private key of sender account '+Inttostr(sender)+' not found in wallet';
+      ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end;
+    if (Not assigned(_RPCServer.FWalletKeys.Key[i].PrivateKey)) then begin
+      if _RPCServer.FWalletKeys.Key[i].CryptedKey<>'' then begin
+        // Wallet is password protected
+        ErrorDesc := 'Wallet is password protected';
+        ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      end else begin
+        ErrorDesc := 'Wallet private key not found in Wallet';
+        ErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      end;
+      exit;
+    end;
+    if (length(RawPayload)>0) then begin
+      if (Payload_method='none') then f_raw:=RawPayload
+      else if (Payload_method='dest') then begin
+        f_raw := ECIESEncrypt(tacc.accountkey,RawPayload);
+      end else if (Payload_method='sender') then begin
+        f_raw := ECIESEncrypt(sacc.accountkey,RawPayload);
+      end else if (Payload_method='aes') then begin
+        f_raw := TAESComp.EVP_Encrypt_AES256(RawPayload,EncodePwd);
+      end else begin
+        ErrorNum:=CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc:='Invalid encode payload method: '+Payload_method;
+        exit;
+      end;
+    end else f_raw := '';
+    opt := TOpTransaction.Create(sender,sacc.n_operation+1,target,_RPCServer.FWalletKeys.Key[i].PrivateKey,amount,fee,f_raw);
+    try
+      If not TNode.Node.AddOperation(Nil,opt,errors) then begin
+        ErrorDesc := 'Error adding operation: '+errors;
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        Exit;
+      end;
+      TPCOperation.OperationToOperationResume(0,opt,sender,opr);
+      FillOperationResumeToJSONObject(opr,GetResultObject);
+      Result := true;
+    finally
+      opt.free;
+    end;
+  end;
+
+  Function ChangeAccountKey(account_number : Cardinal; new_pub_key : TAccountKey; fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
+  // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+  Var opck : TOpChangeKey;
+    acc : TAccount;
+    i : Integer;
+    errors : AnsiString;
+    opr : TOperationResume;
+    f_raw : TRawBytes;
+  begin
+    Result := false;
+    if (account_number<0) or (account_number>=TNode.Node.Bank.AccountsCount) then begin
+      ErrorDesc:='Invalid account '+Inttostr(account_number);
+      ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end;
+    acc := TNode.Node.Operations.SafeBoxTransaction.Account(account_number);
+    _RPCServer.FWalletKeys.AccountsKeyList.IndexOfAccountKey(acc.accountkey);
+    i := _RPCServer.FWalletKeys.IndexOfAccountKey(acc.accountkey);
+    if (i<0) then begin
+      ErrorDesc:='Private key of account '+Inttostr(account_number)+' not found in wallet';
+      ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end;
+    if (Not assigned(_RPCServer.FWalletKeys.Key[i].PrivateKey)) then begin
+      if _RPCServer.FWalletKeys.Key[i].CryptedKey<>'' then begin
+        // Wallet is password protected
+        ErrorDesc := 'Wallet is password protected';
+        ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      end else begin
+        ErrorDesc := 'Wallet private key not found in Wallet';
+        ErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      end;
+      exit;
+    end;
+    if (length(RawPayload)>0) then begin
+      if (Payload_method='none') then f_raw:=RawPayload
+      else if (Payload_method='dest') then begin
+        f_raw := ECIESEncrypt(new_pub_key,RawPayload);
+      end else if (Payload_method='sender') then begin
+        f_raw := ECIESEncrypt(acc.accountkey,RawPayload);
+      end else if (Payload_method='aes') then begin
+        f_raw := TAESComp.EVP_Encrypt_AES256(RawPayload,EncodePwd);
+      end else begin
+        ErrorNum:=CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc:='Invalid encode payload method: '+Payload_method;
+        exit;
+      end;
+    end else f_raw := '';
+    opck := TOpChangeKey.Create(account_number,acc.n_operation+1,_RPCServer.FWalletKeys.Key[i].PrivateKey,new_pub_key,fee,f_raw);
+    try
+      If not TNode.Node.AddOperation(Nil,opck,errors) then begin
+        ErrorDesc := 'Error adding operation: '+errors;
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        Exit;
+      end;
+      TPCOperation.OperationToOperationResume(0,opck,account_number,opr);
+      FillOperationResumeToJSONObject(opr,GetResultObject);
+      Result := true;
+    finally
+      opck.free;
+    end;
+  end;
+
+  Procedure FillAccountObject(Const account : TAccount; jsonObj : TPCJSONObject);
+  Begin
+    jsonObj.GetAsVariant('account').Value:=account.account;
+    jsonObj.GetAsVariant('enc_pubkey').Value := TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountkey));
+    jsonObj.GetAsVariant('balance').Value:=ToJSONCurrency(account.balance);
+    jsonObj.GetAsVariant('n_operation').Value:=account.n_operation;
+    jsonObj.GetAsVariant('updated_b').Value:=account.updated_block;
+  end;
+
+  Function DoEncrypt(RawPayload : TRawBytes; pub_key : TAccountKey; Const Payload_method, EncodePwd : AnsiString) : Boolean;
+  Var f_raw : TRawBytes;
+  begin
+    Result := false;
+    if (length(RawPayload)>0) then begin
+      if (Payload_method='none') then f_raw:=RawPayload
+      else if (Payload_method='pubkey') then begin
+        f_raw := ECIESEncrypt(pub_key,RawPayload);
+      end else if (Payload_method='aes') then begin
+        f_raw := TAESComp.EVP_Encrypt_AES256(RawPayload,EncodePwd);
+      end else begin
+        ErrorNum:=CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc:='Invalid encode payload method: '+Payload_method;
+        exit;
+      end;
+    end else f_raw := '';
+    jsonresponse.GetAsVariant('result').Value := TCrypto.ToHexaString(f_raw);
+    Result := true;
+  end;
+
+  Function DoDecrypt(RawEncryptedPayload : TRawBytes; jsonArrayPwds : TPCJSONArray) : Boolean;
+  var i : Integer;
+    pkey : TECPrivateKey;
+    decrypted_payload : TRawBytes;
+  Begin
+    Result := false;
+    if RawEncryptedPayload='' then begin
+      GetResultObject.GetAsVariant('result').Value:= False;
+      GetResultObject.GetAsVariant('enc_payload').Value:= '';
+      // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+      Result := true;
+      exit;
+    end;
+    for i := 0 to _RPCServer.WalletKeys.Count - 1 do begin
+      pkey := _RPCServer.WalletKeys.Key[i].PrivateKey;
+      if (assigned(pkey)) then begin
+        If ECIESDecrypt(pkey.EC_OpenSSL_NID,pkey.PrivateKey,false,RawEncryptedPayload,decrypted_payload) then begin
+          GetResultObject.GetAsVariant('result').Value:= true;
+          GetResultObject.GetAsVariant('enc_payload').Value:= TCrypto.ToHexaString(RawEncryptedPayload);
+          GetResultObject.GetAsVariant('unenc_payload').Value:= decrypted_payload;
+          GetResultObject.GetAsVariant('payload_method').Value:= 'key';
+          GetResultObject.GetAsVariant('enc_pubkey').Value:= TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(pkey.PublicKey));
+          // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+          Result := true;
+          exit;
+        end;
+      end;
+    end;
+    for i := 0 to jsonArrayPwds.Count - 1 do begin
+      if TAESComp.EVP_Decrypt_AES256(RawEncryptedPayload,jsonArrayPwds.GetAsVariant(i).AsString(''),decrypted_payload) then begin
+        GetResultObject.GetAsVariant('result').Value:= true;
+        GetResultObject.GetAsVariant('enc_payload').Value:= TCrypto.ToHexaString(RawEncryptedPayload);
+        GetResultObject.GetAsVariant('unenc_payload').Value:= decrypted_payload;
+        GetResultObject.GetAsVariant('payload_method').Value:= 'pwd';
+        GetResultObject.GetAsVariant('pwd').Value:= jsonArrayPwds.GetAsVariant(i).AsString('');
+        // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+        Result := true;
+        exit;
+      end;
+    end;
+    // Not found
+    GetResultObject.GetAsVariant('result').Value:= False;
+    GetResultObject.GetAsVariant('enc_payload').Value:= TCrypto.ToHexaString(RawEncryptedPayload);
+    Result := true;
+  End;
+
+Var c,c2 : Cardinal;
+  i,j : Integer;
+  account : TAccount;
+  ansistr : AnsiString;
+  nsaarr : TNodeServerAddressArray;
+  pcops : TPCOperationsComp;
+  ecpkey : TECPrivateKey;
+  opr : TOperationResume;
+  r : TRawBytes;
+  ocl : TOrderedCardinalList;
+  jsonarr : TPCJSONArray;
+  jso : TPCJSONObject;
+begin
+  _ro := Nil;
+  ErrorNum:=0;
+  ErrorDesc:='';
+  Result := false;
+  TLog.NewLog(ltdebug,ClassName,'Processing RPC-JSON method '+method);
+  if (method='addnode') then begin
+    // Param "nodes" contains ip's and ports in format "ip1:port1;ip2:port2 ...". If port is not specified, use default
+    // Returns quantity of nodes added
+    TNode.DecodeIpStringToNodeServerAddressArray(params.AsString('nodes',''),nsaarr);
+    for i:=low(nsaarr) to high(nsaarr) do begin
+      TNetData.NetData.AddServer(nsaarr[i]);
+    end;
+    jsonresponse.GetAsVariant('result').Value:=length(nsaarr);
+    Result := true;
+  end else if (method='getaccount') then begin
+    // Param "account" contains account number
+    // Returns JSON Object with account information based on BlockChain + Pending operations
+    c := params.GetAsVariant('account').AsCardinal(CT_MaxAccount);
+    if (c>=0) And (c<TNode.Node.Bank.AccountsCount) then begin
+      account := TNode.Node.Operations.SafeBoxTransaction.Account(c);
+      FillAccountObject(account,GetResultObject);
+      Result := True;
+    end else begin
+      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      if (c=CT_MaxAccount) then ErrorDesc := 'Need "account" param'
+      else ErrorDesc := 'Account not found: '+IntToStr(c);
+    end;
+  end else if (method='getwalletaccounts') then begin
+    // Returns JSON array with accounts in Wallet
+    jsonarr := jsonresponse.GetAsArray('result');
+    for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
+      ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+      for j := 0 to ocl.Count - 1 do begin
+        account := TNode.Node.Operations.SafeBoxTransaction.Account(ocl.Get(j));
+        FillAccountObject(account,jsonarr.GetAsObject(jsonarr.Count));
+      end;
+    end;
+    Result := true;
+  end else if (method='getwalletpubkeys') then begin
+    // Returns JSON array with pubkeys in wallet
+    jsonarr := jsonresponse.GetAsArray('result');
+    for i:=0 to _RPCServer.WalletKeys.Count-1 do begin
+      jsonarr.GetAsObject(i).GetAsVariant('name').Value := _RPCServer.WalletKeys.Key[i].Name;
+      jsonarr.GetAsObject(i).GetAsVariant('enc_pubkey').Value := TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(_RPCServer.WalletKeys.Key[i].AccountKey));
+      jsonarr.GetAsObject(i).GetAsVariant('can_use').Value := (_RPCServer.WalletKeys.Key[i].CryptedKey<>'');
+    end;
+    Result := true;
+  end else if (method='getblock') then begin
+    // Param "block" contains block number (0..getblockcount-1)
+    // Returns JSON object with block information
+    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
+    if (c>=0) And (c<TNode.Node.Bank.BlocksCount) then begin
+      Result := GetBlock(c,GetResultObject);
+    end else begin
+      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
+      if (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
+      else ErrorDesc := 'Block not found: '+IntToStr(c);
+    end;
+  end else if (method='getblocks') then begin
+    // Param "start" "end" contains blocks number (0..getblockcount-1)
+    // Returns JSON Array with blocks information (limited to 1000 blocks)
+    // Sorted by DESCENDING blocknumber
+    i := params.AsCardinal('last',0);
+    if (i>0) then begin
+      if (i>1000) then i := 1000;
+      c2 := TNode.Node.Bank.BlocksCount-1;
+      if (TNode.Node.Bank.BlocksCount>=i) then
+        c := (TNode.Node.Bank.BlocksCount) - i
+      else c := 0;
+    end else begin
+      c := params.GetAsVariant('start').AsCardinal(CT_MaxBlock);
+      c2 := params.GetAsVariant('end').AsCardinal(CT_MaxBlock);
+    end;
+    if ((c>=0) And (c<TNode.Node.Bank.BlocksCount)) And (c2>=c) And (c2<TNode.Node.Bank.BlocksCount) then begin
+      i := 0; Result := true;
+      while (c<=c2) And (Result) And (i<1000) do begin
+        Result := GetBlock(c2,jsonresponse.GetAsArray('result').GetAsObject(i));
+        dec(c2); inc(i);
+      end;
+    end else begin
+      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
+      if (c>c2) then ErrorDesc := 'Block start > block end'
+      else if (c=CT_MaxBlock) Or (c2=CT_MaxBlock) then ErrorDesc:='Need param "last" or "start" and "end"'
+      else if (c2>=TNode.Node.Bank.BlocksCount) then ErrorDesc := 'Block higher or equal to getblockccount: '+IntToStr(c2)
+      else  ErrorDesc := 'Block not found: '+IntToStr(c);
+    end;
+  end else if (method='getblockcount') then begin
+    // Returns a number with Node blocks count
+    jsonresponse.GetAsVariant('result').Value:=TNode.Node.Bank.BlocksCount;
+    Result := True;
+  end else if (method='getblockoperation') then begin
+    // Param "block" contains block. Null = Pending operation
+    // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
+    // Returns a JSON object with operation values as "Operation resume format"
+    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
+    if (c>=0) And (c<TNode.Node.Bank.BlocksCount) then begin
+      pcops := TPCOperationsComp.Create(Nil);
+      try
+        If Not TNode.Node.Bank.LoadOperations(pcops,c) then begin
+          ErrorNum := CT_RPC_ErrNum_InternalError;
+          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
+          Exit;
+        end;
+        i := params.GetAsVariant('opblock').AsInteger(0);
+        if (i<0) Or (i>=pcops.Count) then begin
+          ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+          ErrorDesc := 'Block/Operation not found: '+IntToStr(c)+'/'+IntToStr(i)+' BlockOperations:'+IntToStr(pcops.Count);
+          Exit;
+        end;
+        If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],pcops.Operation[i].SenderAccount,opr) then begin
+          opr.NOpInsideBlock:=i;
+          opr.time:=pcops.OperationBlock.timestamp;
+          opr.Balance := -1;
+          FillOperationResumeToJSONObject(opr,GetResultObject);
+        end;
+        Result := True;
+      finally
+        pcops.Free;
+      end;
+    end else begin
+      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
+      else ErrorDesc := 'Block not found: '+IntToStr(c);
+      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
+    end;
+  end else if (method='getblockoperations') then begin
+    // Param "block" contains block
+    // Returns a JSON array with items as "Operation resume format"
+    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
+    if (c>=0) And (c<TNode.Node.Bank.BlocksCount) then begin
+      pcops := TPCOperationsComp.Create(Nil);
+      try
+        If Not TNode.Node.Bank.LoadOperations(pcops,c) then begin
+          ErrorNum := CT_RPC_ErrNum_InternalError;
+          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
+          Exit;
+        end;
+        GetResultArray;
+        for i := pcops.Count - 1 downto 0 do begin
+          If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],pcops.Operation[i].SenderAccount,opr) then begin
+            opr.NOpInsideBlock:=i;
+            opr.time:=pcops.OperationBlock.timestamp;
+            opr.Balance := -1; // Don't include!
+            FillOperationResumeToJSONObject(opr,GetResultArray.GetAsObject(pcops.Count - 1 - i));
+          end;
+        end;
+        Result := True;
+      finally
+        pcops.Free;
+      end;
+    end else begin
+      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
+      else ErrorDesc := 'Block not found: '+IntToStr(c);
+      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
+    end;
+  end else if (method='getaccountoperations') then begin
+    // Returns all the operations affecting an account in "Operation resume format" as an array
+    // Param "account" contains account number
+    // Param "deep" (optional) contains max blocks deep to search (Default: 100)
+    c := params.GetAsVariant('account').AsCardinal(CT_MaxAccount);
+    if ((c>=0) And (c<TNode.Node.Bank.AccountsCount)) then begin
+      Result := GetAccountOperations(c,GetResultArray,params.AsInteger('deep',100));
+    end else begin
+      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      If (c=CT_MaxAccount) then ErrorDesc := 'Need account param'
+      else ErrorDesc := 'Account not found: '+IntToStr(c);
+    end;
+  end else if (method='getpendings') then begin
+    // Returns all the operations pending to be included in a block in "Operation resume format" as an array
+    // Create result
+    GetResultArray;
+    for i:=TNode.Node.Operations.Count-1 downto 0 do begin
+      if not TPCOperation.OperationToOperationResume(0,TNode.Node.Operations.Operation[i],TNode.Node.Operations.Operation[i].SenderAccount,opr) then begin
+        ErrorNum := CT_RPC_ErrNum_InternalError;
+        ErrorDesc := 'Error converting data';
+        exit;
+      end;
+      opr.NOpInsideBlock:=i;
+      opr.Balance := TNode.Node.Operations.SafeBoxTransaction.Account(TNode.Node.Operations.Operation[i].SenderAccount).balance;
+      FillOperationResumeToJSONObject(opr,GetResultArray.GetAsObject( TNode.Node.Operations.Count-1-i ));
+    end;
+    Result := true;
+  end else if (method='findoperation') then begin
+    // Search for an operation based on "ophash"
+    r := TCrypto.HexaToRaw(params.AsString('ophash',''));
+    if (r='') then begin
+      ErrorNum:=CT_RPC_ErrNum_NotFound;
+      ErrorDesc:='param ophash not found or invalid value "'+params.AsString('ophash','')+'"';
+      exit;
+    end;
+    pcops := TPCOperationsComp.Create(Nil);
+    try
+      If not TNode.Node.FindOperation(pcops,r,c,i) then begin
+        ErrorNum:=CT_RPC_ErrNum_NotFound;
+        ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
+        exit;
+      end;
+      If not TPCOperation.OperationToOperationResume(c,pcops.Operation[i],pcops.Operation[i].SenderAccount,opr) then begin
+        ErrorNum := CT_RPC_ErrNum_InternalError;
+        ErrorDesc := 'Error 20161026-1';
+      end;
+      opr.NOpInsideBlock:=i;
+      opr.time:=pcops.OperationBlock.timestamp;
+      FillOperationResumeToJSONObject(opr,GetResultObject);
+      Result := True;
+    finally
+      pcops.Free;
+    end;
+  end else if (method='sendto') then begin
+    // Sends "amount" coins from "sender" to "target" with "fee"
+    // If "payload" is present, it will be encoded using "payload_method"
+    // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+    // Returns a JSON "Operation Resume format" object when successfull
+    // Note: "ophash" will contain block "0" = "pending block"
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      ErrorDesc := 'Wallet is password protected. Unlock first';
+      exit;
+    end;
+    Result := OpSendTo(params.AsCardinal('sender',CT_MaxAccount),params.AsCardinal('target',CT_MaxAccount),
+       ToPascalCoins(params.AsDouble('amount',0)),
+       ToPascalCoins(params.AsDouble('fee',0)),
+       TCrypto.HexaToRaw(params.AsString('payload','')),
+       params.AsString('payload_method','dest'),params.AsString('pwd',''));
+  end else if (method='changekey') then begin
+    // Change key of "account" to "new_enc_pubkey" (encoded public key format) with "fee"
+    // If "payload" is present, it will be encoded using "payload_method"
+    // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
+    // Returns a JSON "Operation Resume format" object when successfull
+    // Note: "ophash" will contain block "0" = "pending block"
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      ErrorDesc := 'Wallet is password protected. Unlock first';
+      exit;
+    end;
+    Result := ChangeAccountKey(params.AsCardinal('account',CT_MaxAccount),
+       TAccountComp.RawString2Accountkey(TCrypto.HexaToRaw(params.AsString('new_enc_pubkey',''))),
+       ToPascalCoins(params.AsDouble('fee',0)),
+       TCrypto.HexaToRaw(params.AsString('payload','')),
+       params.AsString('payload_method','dest'),params.AsString('pwd',''));
+  end else if (method='nodestatus') then begin
+    // Returns a JSON Object with Node status
+    GetResultObject.GetAsVariant('ready').Value := False;
+    If TNode.Node.IsReady(ansistr) then begin
+      GetResultObject.GetAsVariant('ready_s').Value := ansistr;
+      if TNetData.NetData.NetStatistics.ActiveConnections>0 then begin
+        GetResultObject.GetAsVariant('ready').Value := True;
+        if TNetData.NetData.IsDiscoveringServers then begin
+          GetResultObject.GetAsVariant('status_s').Value := 'Discovering servers';
+        end else if TNetData.NetData.IsGettingNewBlockChainFromClient then begin
+          GetResultObject.GetAsVariant('status_s').Value := 'Obtaining new blockchain';
+        end else begin
+          GetResultObject.GetAsVariant('status_s').Value := 'Running';
+        end;
+      end else begin
+        GetResultObject.GetAsVariant('ready_s').Value := 'Alone in the world...';
+      end;
+    end else begin
+      GetResultObject.GetAsVariant('ready_s').Value := ansistr;
+    end;
+    GetResultObject.GetAsVariant('port').Value:=TNode.Node.NetServer.Port;
+    GetResultObject.GetAsVariant('locked').Value:=Not _RPCServer.WalletKeys.IsValidPassword;
+    GetResultObject.GetAsVariant('timestamp').Value:=UnivDateTimeToUnix(DateTime2UnivDateTime(now));
+    GetResultObject.GetAsVariant('version').Value:=CT_ClientAppVersion;
+    GetResultObject.GetAsObject('netprotocol').GetAsVariant('ver').Value := CT_NetProtocol_Version;
+    GetResultObject.GetAsObject('netprotocol').GetAsVariant('ver_a').Value := CT_NetProtocol_Available;
+    GetResultObject.GetAsVariant('blocks').Value:=TNode.Node.Bank.BlocksCount;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('active').Value:=TNetData.NetData.NetStatistics.ActiveConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('clients').Value:=TNetData.NetData.NetStatistics.ClientsConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('servers').Value:=TNetData.NetData.NetStatistics.ServersConnectionsWithResponse;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('servers_t').Value:=TNetData.NetData.NetStatistics.ServersConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('total').Value:=TNetData.NetData.NetStatistics.TotalConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('tclients').Value:=TNetData.NetData.NetStatistics.TotalClientsConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('tservers').Value:=TNetData.NetData.NetStatistics.TotalServersConnections;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('breceived').Value:=TNetData.NetData.NetStatistics.BytesReceived;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('bsend').Value:=TNetData.NetData.NetStatistics.BytesSend;
+    nsaarr := TNetData.NetData.GetValidNodeServers;
+    for i := low(nsaarr) to High(nsaarr) do begin
+      jso := GetResultObject.GetAsArray('nodeservers').GetAsObject(i);
+      jso.GetAsVariant('ip').Value := nsaarr[i].ip;
+      jso.GetAsVariant('port').Value := nsaarr[i].port;
+      jso.GetAsVariant('lastcon').Value := nsaarr[i].last_connection;
+      jso.GetAsVariant('attempts').Value := nsaarr[i].total_failed_attemps_to_connect;
+    end;
+    Result := True;
+  end else if (method='encodepubkey') then begin
+    // Creates a encoded public key based on params
+    // Param "ec_nid" can be 714=secp256k1 715=secp384r1 729=secp283k1 716=secp521r1
+    // Param "x","y" are x and y ec public keys values in hexadecimal based on ec_nid
+    // Returns a hexadecimal value containing encoded public key
+    account.accountkey.EC_OpenSSL_NID:=params.AsInteger('ec_nid',0);
+    account.accountkey.x:=TCrypto.HexaToRaw(params.AsString('x',''));
+    account.accountkey.y:=TCrypto.HexaToRaw(params.AsString('y',''));
+    if (account.accountkey.EC_OpenSSL_NID=0) Or (account.accountkey.x='') Or (account.accountkey.y='') then begin
+      ErrorDesc:= 'Need params "ec_nid","x","y" to encodepubkey';
+      ErrorNum:= CT_RPC_ErrNum_InvalidPubKey;
+      exit;
+    end;
+    if TAccountComp.IsValidAccountKey(account.accountkey,ansistr) then begin
+      jsonresponse.GetAsVariant('result').Value:=TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountkey));
+      Result := True;
+    end else begin
+      ErrorDesc:= ansistr;
+      ErrorNum:= CT_RPC_ErrNum_InvalidPubKey;
+    end;
+  end else if (method='decodepubkey') then begin
+    // Returns "ec_nid", "x" and "y" of an encoded public key (x and y in hexadecimal)
+    // Param "enc_pubkey" is an hexadecimal encoded address (see 'encodepubkey')
+    if (params.AsString('enc_pubkey','')='') then begin
+      ErrorDesc:= 'Need param "enc_pubkey"';
+      ErrorNum:= CT_RPC_ErrNum_InvalidPubKey;
+      exit;
+    end;
+    account.accountkey := CT_Account_NUL.accountkey;
+    account.accountkey := TAccountComp.RawString2Accountkey(TCrypto.HexaToRaw(params.AsString('enc_pubkey','')));
+    if (TAccountComp.IsValidAccountKey(account.accountkey,ansistr)) then begin
+      GetResultObject.GetAsVariant('ec_nid').Value := account.accountkey.EC_OpenSSL_NID;
+      GetResultObject.GetAsVariant('x').Value := TCrypto.ToHexaString(account.accountkey.x);
+      GetResultObject.GetAsVariant('y').Value := TCrypto.ToHexaString(account.accountkey.y);
+      Result := True;
+    end else begin
+      ErrorDesc:= ansistr;
+      ErrorNum:= CT_RPC_ErrNum_InvalidPubKey;
+    end;
+  end else if (method='payloadencrypt') then begin
+    // Encrypts a "payload" using "payload_method"
+    // "payload_method" types: "none","pubkey"(must provide "enc_pubkey"),"aes"(must provide "pwd" param)
+    // If payload is "pubkey"
+    // Returns an hexa string with encrypted payload
+    if (params.AsString('payload','')='') then begin
+      ErrorNum:= CT_RPC_ErrNum_InvalidData;
+      ErrorDesc := 'Need param "payload"';
+      exit;
+    end;
+    Result := DoEncrypt(TCrypto.HexaToRaw(params.AsString('payload','')),
+       TAccountComp.RawString2Accountkey(TCrypto.HexaToRaw(params.AsString('enc_pubkey',''))),
+       params.AsString('payload_method',''),params.AsString('pwd',''));
+  end else if (method='payloaddecrypt') then begin
+    // Decrypts a "payload" searching for wallet private keys and for array of strings in "pwds" param
+    // Returns an JSON Object with "result" (Boolean) and
+    if (params.AsString('payload','')='') then begin
+      ErrorNum:= CT_RPC_ErrNum_InvalidData;
+      ErrorDesc := 'Need param "payload"';
+      exit;
+    end;
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      ErrorDesc := 'Wallet is password protected. Unlock first';
+      exit;
+    end;
+    Result := DoDecrypt(TCrypto.HexaToRaw(params.AsString('payload','')),params.GetAsArray('pwds'));
+  end else if (method='getconnections') then begin
+    // Returns an array of connections objects with info about state
+    GetConnections;
+    Result := true;
+  end else if (method='addnewkey') then begin
+    // Creates a new private key and stores it on the wallet, returning encoded value
+    // Param "ec_nid" can be 714=secp256k1 715=secp384r1 729=secp283k1 716=secp521r1. (Default = CT_Default_EC_OpenSSL_NID)
+    // Param "name" is name for this address
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      ErrorDesc := 'Wallet is password protected. Unlock first';
+      exit;
+    end;
+    ecpkey := TECPrivateKey.Create;
+    try
+      ecpkey.GenerateRandomPrivateKey(params.AsInteger('ec_nid',CT_Default_EC_OpenSSL_NID));
+      _RPCServer.FWalletKeys.AddPrivateKey(params.AsString('name',DateTimeToStr(now)),ecpkey);
+      jsonresponse.GetAsVariant('result').Value:=TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(ecpkey.PublicKey));
+      Result := true;
+    finally
+      ecpkey.Free;
+    end;
+  end else if (method='unlock') then begin
+    // Unlocks the Wallet with "pwd" password
+    // Returns Boolean if wallet is unlocked
+    if (params.AsString('pwd','')='') then begin
+      ErrorNum:= CT_RPC_ErrNum_InvalidData;
+      ErrorDesc := 'Need param "pwd"';
+      exit;
+    end;
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      _RPCServer.WalletKeys.WalletPassword:=params.AsString('pwd','');
+    end;
+    jsonresponse.GetAsVariant('result').Value := _RPCServer.WalletKeys.IsValidPassword;
+    Result := true;
+  end else if (method='setwalletpassword') then begin
+    // Changes the Wallet password with "pwd" param
+    // Must be unlocked first
+    // Returns Boolean if wallet password changed
+    If Not _RPCServer.WalletKeys.IsValidPassword then begin
+      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+      ErrorDesc := 'Wallet is password protected. Unlock first';
+      exit;
+    end;
+    //
+    if (params.IndexOfName('pwd')<0) then begin
+      ErrorNum:= CT_RPC_ErrNum_InvalidData;
+      ErrorDesc := 'Need param "pwd"';
+      exit;
+    end;
+    _RPCServer.WalletKeys.WalletPassword:=params.AsString('pwd','');
+    jsonresponse.GetAsVariant('result').Value := _RPCServer.WalletKeys.IsValidPassword;
+    Result := true;
+  end else if (method='stopnode') then begin
+    // Stops communications to other nodes
+    TNode.Node.NetServer.Active := false;
+    TNetData.NetData.NetConnectionsActive:=false;
+    jsonresponse.GetAsVariant('result').Value := true;
+    Result := true;
+  end else if (method='startnode') then begin
+    // Stops communications to other nodes
+    TNode.Node.NetServer.Active := true;
+    TNetData.NetData.NetConnectionsActive:=true;
+    jsonresponse.GetAsVariant('result').Value := true;
+    Result := true;
+  end else begin
+    ErrorNum := CT_RPC_ErrNum_MethodNotFound;
+    ErrorDesc := 'Method not found: "'+method+'"';
+  end;
+end;
+
+{ TRPCServerThread }
+
+procedure TRPCServerThread.BCExecute;
+var
+  ClientSock:TSocket;
+begin
+  with FServerSocket do begin
+    CreateSocket;
+    setLinger(true,10000);
+    bind('0.0.0.0',Inttostr(FPort));
+    listen;
+    repeat
+      if terminated then break;
+      Try
+        if canread(1000) then begin
+          ClientSock:=accept;
+          if lastError=0 then TRPCProcess.create(ClientSock);
+        end;
+      Except
+        On E:Exception do begin
+          TLog.NewLog(ltError,Classname,'Error '+E.ClassName+':'+E.Message);
+        end;
+      End;
+      sleep(1);
+    until false;
+  end;
+end;
+
+constructor TRPCServerThread.Create(Port: Word);
+begin
+  TLog.NewLog(ltInfo,ClassName,'Activating RPC-JSON Server on port '+inttostr(Port));
+  FServerSocket:=TTCPBlockSocket.create;
+  FPort := Port;
+  inherited create(false);
+end;
+
+destructor TRPCServerThread.Destroy;
+begin
+  TLog.NewLog(ltInfo,ClassName,'Stoping RPC-JSON Server');
+  FreeAndNil(FServerSocket);
+  inherited Destroy;
+end;
+
 end.

+ 69 - 12
Units/PascalCoin/UServerApp.pas

@@ -1,5 +1,9 @@
 unit UServerApp;
 
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
 {$IFDEF MSWINDOWS}
   {$DEFINE OS_MSWIN}
 {$ENDIF}
@@ -7,11 +11,16 @@ unit UServerApp;
 interface
 
 uses
+  {$IFDEF LCL}
+  interfaces,
+  {$ENDIF}
   {$IFDEF OS_MSWIN}
   Windows,
   Messages,
   {$ENDIF}
-  SyncObjs;
+  SyncObjs,
+  UOpenSSL, UCrypto, UNode, UFileStorage, UFolderHelper, UWalletKeys, UConst, ULog, UNetProtocol,
+  URPC;
 
 type
   TPascalCoinServerLogType = (sltDebug, sltInfo, sltError, sltWarning);
@@ -19,14 +28,20 @@ type
   TPascalCoinServerAppLogEvent = procedure (LogType: TPascalCoinServerLogType;
       Msg: String; Level: Integer) of object;
 
+  { TPascalCoinServerApp }
+
   TPascalCoinServerApp = class
   private
     FLock : TCriticalSection;
+    FOnLog: TPascalCoinServerAppLogEvent;
     FTerminated : Boolean;
     {$IFDEF OS_MSWIN}
     hStdIn : THandle;
     {$ENDIF}
-    FOnLog : TPascalCoinServerAppLogEvent;
+    FNode : TNode;
+    FWalletKeys : TWalletKeysExt;
+    FRPC : TRPCServer;
+    FLog : TLog;
 
     procedure Lock;
     procedure Unlock;
@@ -35,6 +50,7 @@ type
               const Level: Integer = 0); overload;
     procedure Log(const LogType: TPascalCoinServerLogType; const Msg: String;
               const Params: array of const; const Level: Integer = 0); overload;
+    procedure OnPascalCoinLog(logtype : TLogType; Time : TDateTime; ThreadID : Cardinal; Const sender, logtext : AnsiString);
 
     function  GetTerminated: Boolean;
     procedure SetTerminated;
@@ -49,13 +65,12 @@ type
     constructor Create;
     destructor Destroy; override;
 
-    property  OnLog: TPascalCoinServerAppLogEvent read FOnLog write FOnLog;
-
     procedure Init;
     procedure Run;
     procedure Stop;
 
     property  Terminated: Boolean read GetTerminated;
+    property  OnLog: TPascalCoinServerAppLogEvent read FOnLog write FOnLog;
   end;
 
 var
@@ -64,7 +79,7 @@ var
 implementation
 
 uses
-  SysUtils;
+  SysUtils, Classes;
 
 { TPascalCoinServerApp }
 
@@ -72,14 +87,20 @@ constructor TPascalCoinServerApp.Create;
 begin
   inherited Create;
   FLock := TCriticalSection.Create;
+  FLog := TLog.Create(Nil);
+  FLog.OnInThreadNewLog:=OnPascalCoinLog;
   {$IFDEF OS_MSWIN}
   // get the console input handle
   hStdIn := GetStdHandle(STD_INPUT_HANDLE);
   {$ENDIF}
+  FWalletKeys := TWalletKeysExt.Create(Nil);
 end;
 
 destructor TPascalCoinServerApp.Destroy;
 begin
+  FreeAndNil(FWalletKeys);
+  FLog.OnNewLog:=Nil;
+  FreeAndNil(FLog);
   FreeAndNil(FLock);
   inherited Destroy;
 end;
@@ -89,8 +110,13 @@ begin
   FLock.Acquire;
 end;
 
+procedure TPascalCoinServerApp.Unlock;
+begin
+  FLock.Release;
+end;
+
 procedure TPascalCoinServerApp.Log(const LogType: TPascalCoinServerLogType;
-          const Msg: String; const Level: Integer);
+  const Msg: String; const Level: Integer);
 begin
   if Assigned(FOnLog) then
     FOnLog(LogType, Msg, Level);
@@ -102,9 +128,13 @@ begin
   Log(LogType, Format(Msg, Params), Level);
 end;
 
-procedure TPascalCoinServerApp.Unlock;
+procedure TPascalCoinServerApp.OnPascalCoinLog(logtype: TLogType;
+  Time: TDateTime; ThreadID: Cardinal; const sender, logtext: AnsiString);
+Var s : AnsiString;
 begin
-  FLock.Release;
+  if (logtype=ltdebug)  then exit;
+  if ThreadID=MainThreadID then s := ' MAIN:' else s:=' TID:';
+  Log(sltInfo,s+IntToHex(ThreadID,8)+' ['+CT_LogType[Logtype]+'] <'+sender+'> '+logtext);
 end;
 
 function TPascalCoinServerApp.GetTerminated: Boolean;
@@ -233,20 +263,47 @@ end;
 
 procedure TPascalCoinServerApp.Init;
 begin
-  Log(sltInfo, 'PascalCoin Server');
+  TLog.NewLog(ltinfo,Classname,'PascalCoin Server');
+  // Load Node
+  // 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 the application');
+  TCrypto.InitCrypto;
+  FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+  // Creating Node:
+  FNode := TNode.Node;
+  // RPC Server
+  Log(sltInfo,'Activating RPC server');
+  FRPC := TRPCServer.Create;
+  FRPC.WalletKeys := FWalletKeys;
+  FRPC.Active:=true;
+  // Check Database
+  FNode.Bank.StorageClass := TFileStorage;
+  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+  // Reading database
+  Log(sltInfo,'Reading database and constructing PascalCoin accounts');
+  FNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+  FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
+  Log(sltInfo,'Start discovering nodes');
+  FNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+  FNode.Node.NetServer.Active := true;
 end;
 
 procedure TPascalCoinServerApp.Run;
 begin
-  Log(sltInfo, 'Start');
-  Log(sltInfo, 'Running (press Q to stop)');
+  Log(sltinfo,'Start');
+  Log(sltinfo,'Running (press Q to stop)');
   while not GetTerminated do
     ProcessOrWait;
 end;
 
 procedure TPascalCoinServerApp.Stop;
 begin
-  Log(sltInfo, 'Stop');
+  Log(sltinfo,'Stop');
+  FreeAndNil(FRPC);
+  FNode.NetServer.Active := false;
+  TNetData.NetData.Free;
+  FreeAndNil(FNode);
+  Log(sltinfo,'Finalized');
 end;
 
 end.

+ 13 - 5
Units/PascalCoin/UTCPIP.pas

@@ -51,6 +51,7 @@ type
     FRemoteHost : AnsiString;
     FRemotePort : Word;
     FBytesReceived, FBytesSent : Int64;
+    FLock : TCriticalSection;
     {$ENDIF}
     FOnConnect: TNotifyEvent;
     FOnDisconnect: TNotifyEvent;
@@ -188,7 +189,7 @@ uses
 {$IFnDEF FPC}
   Windows,
 {$ELSE}
-  LCLIntf, LCLType, LMessages,
+  {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   UConst, ULog;
 
@@ -263,6 +264,7 @@ begin
   FTcpBlockSocket.OnError := TCustomIpClient_OnError;
   {$ENDIF}
   {$IFDEF Synapse}
+  FLock := TCriticalSection.Create;
   FTcpBlockSocket := TTCPBlockSocket.Create;
   FTcpBlockSocket.OnAfterConnect := OnConnect;
   FTcpBlockSocket.SocksTimeout := 10000;
@@ -277,6 +279,7 @@ end;
 destructor TNetTcpIpClient.Destroy;
 begin
   Disconnect;
+  FreeAndNil(FLock);
   inherited;
   FreeAndNil(FTcpBlockSocket);
   TLog.NewLog(ltdebug,ClassName,'Destroying Socket end');
@@ -288,10 +291,15 @@ begin
   FTcpBlockSocket.Disconnect;
   {$ENDIF}
   {$IFDEF Synapse}
-  if Not FConnected then exit;
-  FConnected := false;
-  FTcpBlockSocket.CloseSocket;
-  if Assigned(FOnDisconnect) then FOnDisconnect(Self);
+  FLock.Acquire;
+  Try
+    if Not FConnected then exit;
+    FConnected := false;
+    FTcpBlockSocket.CloseSocket;
+    if Assigned(FOnDisconnect) then FOnDisconnect(Self);
+  Finally
+    FLock.Release;
+  End;
   {$ENDIF}
 end;
 

+ 94 - 4
Units/PascalCoin/UWalletKeys.pas

@@ -52,17 +52,35 @@ Type
     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 AddPrivateKey(Const Name : AnsiString; ECPrivateKey : TECPrivateKey) : Integer; virtual;
+    Function AddPublicKey(Const Name : AnsiString; ECDSA_Public : TECDSA_Public) : Integer; virtual;
     Function IndexOfAccountKey(AccountKey : TAccountKey) : Integer;
-    Procedure Delete(index : Integer);
-    Procedure Clear;
+    Procedure Delete(index : Integer); virtual;
+    Procedure Clear; virtual;
     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;
 
+  TWalletKeysExt = Class(TWalletKeys)
+  private
+    FOrderedAccountKeysList : TOrderedAccountKeysList;
+    procedure SetSafeBox(const Value: TPCSafeBox);
+    function GetSafeBox: TPCSafeBox;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor destroy; override;
+    Function AddPrivateKey(Const Name : AnsiString; ECPrivateKey : TECPrivateKey) : Integer; override;
+    Function AddPublicKey(Const Name : AnsiString; ECDSA_Public : TECDSA_Public) : Integer; override;
+    Procedure Delete(index : Integer); override;
+    Procedure Clear; override;
+    //
+    Property AccountsKeyList : TOrderedAccountKeysList read FOrderedAccountKeysList;
+    Property SafeBox : TPCSafeBox read GetSafeBox write SetSafeBox;
+  End;
+
+
 Const CT_TWalletKey_NUL  : TWalletKey = (Name:'';AccountKey:(EC_OpenSSL_NID:0;x:'';y:'');CryptedKey:'';PrivateKey:Nil);
 
 implementation
@@ -323,4 +341,76 @@ begin
   if FIsValidPassword then SaveToStream(FWalletFileStream);
 end;
 
+{ TWalletKeysExt }
+
+function TWalletKeysExt.AddPrivateKey(const Name: AnsiString;
+  ECPrivateKey: TECPrivateKey): Integer;
+begin
+  Result := inherited AddPrivateKey(Name,ECPrivateKey);
+  if Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.AddAccountKey(ECPrivateKey.PublicKey);
+  end;
+end;
+
+function TWalletKeysExt.AddPublicKey(const Name: AnsiString;
+  ECDSA_Public: TECDSA_Public): Integer;
+begin
+  Result := inherited AddPublicKey(Name,ECDSA_Public);
+  if Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.AddAccountKey(ECDSA_Public);
+  end;
+end;
+
+procedure TWalletKeysExt.Clear;
+begin
+  inherited;
+  if Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.Clear;
+  end;
+end;
+
+constructor TWalletKeysExt.Create(AOwner: TComponent);
+begin
+  inherited;
+  FOrderedAccountKeysList := Nil;
+end;
+
+procedure TWalletKeysExt.Delete(index: Integer);
+begin
+  if Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.RemoveAccountKey( Key[index].AccountKey );
+  end;
+  inherited;
+end;
+
+destructor TWalletKeysExt.destroy;
+begin
+  FreeAndnil(FOrderedAccountKeysList);
+  inherited;
+end;
+
+function TWalletKeysExt.GetSafeBox: TPCSafeBox;
+begin
+  Result := Nil;
+  if Assigned(FOrderedAccountKeysList) then begin
+    Result := FOrderedAccountKeysList.SafeBox;
+  end;
+end;
+
+procedure TWalletKeysExt.SetSafeBox(const Value: TPCSafeBox);
+Var i : Integer;
+begin
+  if Assigned(FOrderedAccountKeysList) then begin
+    if FOrderedAccountKeysList.SafeBox<>Value then FreeAndNil(FOrderedAccountKeysList)
+    else exit;
+  end;
+  if Assigned(Value) then begin
+    // Initialize
+    FOrderedAccountKeysList := TOrderedAccountKeysList.Create(Value,false);
+    for i := 0 to Count - 1 do begin
+      FOrderedAccountKeysList.AddAccountKey(Key[i].AccountKey);
+    end;
+  end;
+end;
+
 end.

+ 3 - 1
Units/Utils/UFolderHelper.pas

@@ -61,7 +61,7 @@ uses
   Windows,
   {$DEFINE FILEVERSIONINFO}
   {$ENDIF}
-  LCLIntf, LCLType, LMessages,
+  {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   SysUtils;
 
@@ -104,6 +104,7 @@ begin
 end;
 
 class function TFolderHelper.GetTFileVersionInfo(Const FileName: String): TFileVersionInfo;
+{$IFDEF FILEVERSIONINFO}
 Var verInfoSize : DWord;
     GetInfoSizeJunk : DWord;
     VersionInfo,
@@ -111,6 +112,7 @@ Var verInfoSize : DWord;
     InfoPointer : Pointer;
     VersionInfoSize: UINT;
     VersionValue :  string;
+{$ENDIF}
 Begin
    With result do
    Begin

+ 4 - 91
Units/Utils/UGridUtils.pas

@@ -94,7 +94,6 @@ Type
 
     procedure SetBlockEnd(const Value: Int64);
     procedure SetBlockStart(const Value: Int64);protected
-    Procedure GetStoredOperationsFromAccount(Const OperationsResume : TOperationsResumeList; account_number : Cardinal; MaxDeep : Integer); virtual;
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   public
@@ -360,25 +359,6 @@ Begin
   ts.Opaque:=false;
   ts.ShowPrefix:= not (tfNoPrefix in TextFormat);
   ts.SystemFont:=false;
-  { XXXXXXXXXXX
-  if (gdSelected in State) Or (gdFocused in State) then begin
-    if (gdFocused in State) then begin
-      Canvas.Font.Color:=clHighlightText;
-    end;
-    if (State=[gdSelected]) then begin
-      Canvas.Font.Color:=clHighlightText;
-    end else begin
-      Canvas.Brush.Color:= clHighlight;
-      Canvas.Font.Color:=clHighlightText;
-    end;
-    Canvas.Brush.Color:= clHighlight;
-    Canvas.Font.Color:=clHighlightText;
-  end else if (gdRowHighlight in State) then begin
-    Canvas.Brush.Color:= clHighlight;
-    Canvas.Font.Color:=clHighlightText;
-  end;
-  Canvas.FillRect(Rect);
-  }
   Canvas.TextRect(Rect,Rect.Left,Rect.Top,Text,ts);
 end;
 {$ELSE}
@@ -574,73 +554,6 @@ begin
   Result := FNodeNotifyEvents.Node;
 end;
 
-procedure TOperationsGrid.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDeep : Integer);
-  Procedure DoGetFromBlock(block_number : Cardinal; last_balance : Int64; act_deep : Integer);
-  var opc : TPCOperationsComp;
-    op : TPCOperation;
-    OPR : TOperationResume;
-    l : TList;
-    i : Integer;
-    next_block_number : Cardinal;
-  begin
-    if (act_deep<=0) Or ((block_number<=0) And (block_number>0)) then exit;
-
-    opc := TPCOperationsComp.Create(Nil);
-    Try
-      If not Node.Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
-        TLog.NewLog(lterror,ClassName,'Error searching for block '+inttostr(block_number));
-        exit;
-      end;
-      l := TList.Create;
-      try
-        next_block_number := 0;
-        opc.OperationsHashTree.GetOperationsAffectingAccount(account_number,l);
-        for i := l.Count - 1 downto 0 do begin
-          op := opc.Operation[PtrInt(l.Items[i])];
-          if (i=0) then begin
-            If op.SenderAccount=account_number then next_block_number := op.Previous_Sender_updated_block
-            else next_block_number := op.Previous_Destination_updated_block;
-          end;
-          If TPCOperation.OperationToOperationResume(Op,account_number,OPR) then begin
-            OPR.NOpInsideBlock := i;
-            OPR.time := opc.OperationBlock.timestamp;
-            OPR.Block := block_number;
-            OPR.Balance := last_balance;
-            last_balance := last_balance - ( OPR.Amount + OPR.Fee );
-            OperationsResume.Add(OPR);
-          end;
-        end;
-        // Is a new block operation?
-        if (TAccountComp.AccountBlock(account_number)=block_number) And ((account_number MOD CT_AccountsPerBlock)=0) then begin
-          OPR := CT_TOperationResume_NUL;
-          OPR.Block := block_number;
-          OPR.time := opc.OperationBlock.timestamp;
-          OPR.AffectedAccount := account_number;
-          OPR.Amount := opc.OperationBlock.reward;
-          OPR.Fee := opc.OperationBlock.fee;
-          OPR.Balance := last_balance;
-          OPR.OperationTxt := 'Blockchain reward';
-          OperationsResume.Add(OPR);
-        end;
-        //
-        opc.Clear(true);
-        if (next_block_number>0) And (next_block_number<block_number) And (act_deep>0) then DoGetFromBlock(next_block_number,last_balance,act_deep-1);
-      finally
-        l.Free;
-      end;
-    Finally
-      opc.Free;
-    End;
-  end;
-
-Var acc : TAccount;
-begin
-  if MaxDeep<0 then exit;
-  if account_number>=Node.Bank.SafeBox.AccountsCount then exit;
-  acc := Node.Bank.SafeBox.Account(account_number);
-  if (acc.updated_block>0) Or (acc.account=0) then DoGetFromBlock(acc.updated_block,acc.balance,MaxDeep);
-end;
-
 procedure TOperationsGrid.InitGrid;
 begin
   if Not Assigned(FDrawGrid) then exit;
@@ -887,7 +800,7 @@ begin
     if FPendingOperations then begin
       for i := Node.Operations.Count - 1 downto 0 do begin
         Op := Node.Operations.OperationsHashTree.GetOperation(i);
-        If TPCOperation.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
+        If TPCOperation.OperationToOperationResume(0,Op,Op.SenderAccount,OPR) then begin
           OPR.NOpInsideBlock := i;
           OPR.Block := Node.Operations.OperationBlock.block;
           OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
@@ -924,7 +837,7 @@ begin
               FOperationsResume.Add(OPR);
               // Reverse operations inside a block
               for i := opc.Count - 1 downto 0 do begin
-                if TPCOperation.OperationToOperationResume(opc.Operation[i],opc.Operation[i].SenderAccount,opr) then begin
+                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],opc.Operation[i].SenderAccount,opr) then begin
                   opr.NOpInsideBlock := i;
                   opr.Block := bend;
                   opr.time := opc.OperationBlock.timestamp;
@@ -944,7 +857,7 @@ begin
           Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
           for i := list.Count - 1 downto 0 do begin
             Op := Node.Operations.OperationsHashTree.GetOperation(PtrInt(list[i]));
-            If TPCOperation.OperationToOperationResume(Op,AccountNumber,OPR) then begin
+            If TPCOperation.OperationToOperationResume(0,Op,AccountNumber,OPR) then begin
               OPR.NOpInsideBlock := i;
               OPR.Block := Node.Operations.OperationBlock.block;
               OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
@@ -954,7 +867,7 @@ begin
         Finally
           list.Free;
         End;
-        GetStoredOperationsFromAccount(FOperationsResume,AccountNumber,100);
+        Node.GetStoredOperationsFromAccount(FOperationsResume,AccountNumber,100);
       end;
     end;
   Finally

+ 56 - 15
Units/Utils/UJSONFunctions.pas

@@ -50,6 +50,8 @@ Type
 
   TPCJSONDataClass = Class of TPCJSONData;
 
+  { TPCJSONVariantValue }
+
   TPCJSONVariantValue = Class(TPCJSONData)
   private
     FOldValue : Variant;
@@ -64,10 +66,12 @@ Type
     Property Value : Variant read FValue write SetValue;
     Function AsString(DefValue : String) : String;
     Function AsInteger(DefValue : Integer) : Integer;
+    Function AsInt64(DefValue : Int64) : Int64;
     Function AsDouble(DefValue : Double) : Double;
     Function AsBoolean(DefValue : Boolean) : Boolean;
     Function AsDateTime(DefValue : TDateTime) : TDateTime;
     Function AsCurrency(DefValue : Currency) : Currency;
+    Function AsCardinal(DefValue : Cardinal) : Cardinal;
   End;
 
   TPCJSONNameValue = Class(TPCJSONData)
@@ -143,6 +147,8 @@ Type
     Function GetAsArray(Name : String) : TPCJSONArray;
     Function AsString(ParamName : String; DefValue : String) : String;
     Function AsInteger(ParamName : String; DefValue : Integer) : Integer;
+    Function AsCardinal(ParamName : String; DefValue : Cardinal) : Cardinal;
+    Function AsInt64(ParamName : String; DefValue : Int64) : Int64;
     Function AsDouble(ParamName : String; DefValue : Double) : Double;
     Function AsBoolean(ParamName : String; DefValue : Boolean) : Boolean;
     Function AsDateTime(ParamName : String; DefValue : TDateTime) : TDateTime;
@@ -408,6 +414,11 @@ begin
   end;
 end;
 
+function TPCJSONVariantValue.AsCardinal(DefValue: Cardinal): Cardinal;
+begin
+  Result := Cardinal( StrToIntDef(VarToStrDef(Value,''),DefValue) );
+end;
+
 function TPCJSONVariantValue.AsDateTime(DefValue: TDateTime): TDateTime;
 begin
   try
@@ -426,13 +437,14 @@ begin
   end;
 end;
 
+function TPCJSONVariantValue.AsInt64(DefValue: Int64): Int64;
+begin
+  Result := StrToInt64Def(VarToStrDef(Value,''),DefValue);
+end;
+
 function TPCJSONVariantValue.AsInteger(DefValue: Integer): Integer;
 begin
-  try
-    Result := VarAsType(Value,varInteger);
-  except
-    Result := DefValue;
-  end;
+  Result := StrToIntDef(VarToStrDef(Value,''),DefValue);
 end;
 
 function TPCJSONVariantValue.AsString(DefValue: String): String;
@@ -467,7 +479,7 @@ end;
 constructor TPCJSONVariantValue.CreateFromJSONValue(JSONValue: TJSONValue);
 {$IFnDEF FPC}
 Var d : Double;
-    i : Integer;
+    i64 : Integer;
   ds,ts : Char;
 {$ENDIF}
 begin
@@ -477,15 +489,15 @@ begin
   {$ELSE}
   if JSONValue is TJSONNumber then begin
     d := TJSONNumber(JSONValue).AsDouble;
-    if Pos('.',JSONValue.ToString)>0 then i := 0
-    else i := TJSONNumber(JSONValue).AsInt;
+    if Pos('.',JSONValue.ToString)>0 then i64 := 0
+    else i64 := TJSONNumber(JSONValue).AsInt;
     ds := DecimalSeparator;
     ts := ThousandSeparator;
     DecimalSeparator := '.';
     ThousandSeparator := ',';
     Try
-      if FormatFloat('0.###########',d)=inttostr(i) then
-        Value := i
+      if FormatFloat('0.###########',d)=inttostr(i64) then
+        Value := i64
       else Value := d;
     Finally
       DecimalSeparator := ds;
@@ -508,8 +520,8 @@ function TPCJSONVariantValue.ToJSONFormatted(pretty: Boolean; const prefix: Ansi
 Var   ds,ts : Char;
 begin
   Case VarType(Value) of
-    varSmallint,varInteger,varByte,varWord : Result := IntToStr(Value);
-    varLongWord,varInt64 : Result := IntToStr(Value);
+    varSmallint,varInteger,varByte,varWord,
+    varLongWord,varInt64 : Result := VarToStr(Value);
     varBoolean : if (Value) then Result := 'true' else Result:='false';
     varNull : Result := 'null';
     varDate,varDouble : begin
@@ -549,6 +561,11 @@ begin
   end;
 end;
 
+function TPCJSONObject.AsCardinal(ParamName: String; DefValue: Cardinal): Cardinal;
+begin
+  Result := Cardinal(AsInt64(ParamName,DefValue));
+end;
+
 function TPCJSONObject.AsCurrency(ParamName: String; DefValue: Currency): Currency;
 Var v : Variant;
   VV : TPCJSONVariantValue;
@@ -604,6 +621,24 @@ begin
   end;
 end;
 
+function TPCJSONObject.AsInt64(ParamName: String; DefValue: Int64): Int64;
+Var v : Variant;
+  VV : TPCJSONVariantValue;
+begin
+  VV := GetAsVariant(ParamName);
+  if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
+    Result := DefValue;
+    Exit;
+  end;
+  v := GetAsVariant(ParamName).Value;
+  try
+    if VarIsNull(v) then Result := DefValue
+    else Result := StrToInt64Def(VarToStrDef(v,''),DefValue);
+  except
+    Result := DefValue;
+  end;
+end;
+
 function TPCJSONObject.AsInteger(ParamName: String; DefValue: Integer): Integer;
 Var v : Variant;
   VV : TPCJSONVariantValue;
@@ -616,7 +651,7 @@ begin
   v := GetAsVariant(ParamName).Value;
   try
     if VarIsNull(v) then Result := DefValue
-    else Result := VarAsType(v,varInteger);
+    else Result := StrToIntDef(VarToStrDef(v,''),DefValue);
   except
     Result := DefValue;
   end;
@@ -935,10 +970,10 @@ Var JS : TJSONValue;
   {$ENDIF}
 begin
   Result := Nil;
+  JS := Nil;
   {$IFDEF FPC}
   SetLength(jss,length(JSONObject));
   for i:=0 to High(JSONObject) do jss[i+1] := AnsiChar( JSONObject[i] );
-  JS := Nil;
   Try
     JS := GetJSON(jss);
   Except
@@ -947,7 +982,13 @@ begin
     end;
   end;
   {$ELSE}
-  JS := TJSONObject.ParseJSONValue(JSONObject,0);
+  Try
+    JS := TJSONObject.ParseJSONValue(JSONObject,0);
+  Except
+    On E:Exception do begin
+      TLog.NewLog(ltDebug,ClassName,'Error processing JSON: '+E.Message);
+    end;
+  End;
   {$ENDIF}
   if Not Assigned(JS) then exit;
   Try

+ 204 - 0
pascalcoin_daemon.pp

@@ -0,0 +1,204 @@
+program pascalcoin_daemon;
+
+{$mode objfpc}{$H+}
+{$define usecthreads}
+{$apptype gui}
+
+uses
+  {$IFDEF UNIX}{$IFDEF UseCThreads}
+  cthreads,
+  {$ENDIF}{$ENDIF}
+  sysutils,
+  Classes, daemonapp, 
+  SyncObjs,
+  UOpenSSL, UCrypto, UNode, UFileStorage, UFolderHelper, UWalletKeys, UConst, ULog, UNetProtocol,
+  URPC;
+
+Type
+
+  { TPCDaemonThread }
+
+  TPCDaemonThread = Class(TThread)
+    procedure OnPascalCoinLog(logtype : TLogType; Time : TDateTime; AThreadID : Cardinal; Const sender, logtext : AnsiString);
+    Procedure Execute; override;
+  end;
+
+  { TPCDaemon }
+
+  TPCDaemon = Class(TCustomDaemon)
+  Private
+    FThread : TPCDaemonThread;
+    Procedure ThreadStopped (Sender : TObject);
+  public
+    Function Start : Boolean; override;
+    Function Stop : Boolean; override;
+    Function Pause : Boolean; override;
+    Function Continue : Boolean; override;
+    Function Execute : Boolean; override;
+    Function ShutDown : Boolean; override;
+    Function Install : Boolean; override;
+    Function UnInstall: boolean; override;
+  end;
+
+{ TPCDaemonThread }
+
+procedure TPCDaemonThread.OnPascalCoinLog(logtype: TLogType; Time: TDateTime;
+  AThreadID: Cardinal; const sender, logtext: AnsiString);
+Var s : AnsiString;
+begin
+  if (logtype=ltdebug)  then exit;
+  if AThreadID=MainThreadID then s := ' MAIN:' else s:=' TID:';
+  WriteLn(formatDateTime('dd/mm/yyyy hh:nn:ss.zzz',Time)+s+IntToHex(AThreadID,8)+' ['+CT_LogType[Logtype]+'] <'+sender+'> '+logtext);
+end;
+
+Procedure AWriteln(MSg : String; B : Boolean);
+begin
+  Application.Log(etcustom,Msg+BoolToStr(B));
+end;
+
+procedure TPCDaemonThread.Execute;
+var
+  FNode : TNode;
+  FWalletKeys : TWalletKeysExt;
+  FRPC : TRPCServer;
+  FLog : TLog;
+begin
+  FLog := TLog.Create(Nil);
+  FLog.OnInThreadNewLog:=@OnPascalCoinLog;
+  try
+  FWalletKeys := TWalletKeysExt.Create(Nil);
+
+  TLog.NewLog(ltinfo,Classname,'PascalCoin Server');
+  // Load Node
+  // 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 the application');
+  TCrypto.InitCrypto;
+  FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+  // Creating Node:
+  FNode := TNode.Node;
+  // RPC Server
+  FRPC := TRPCServer.Create;
+  FRPC.WalletKeys := FWalletKeys;
+  FRPC.Active:=true;
+  // Check Database
+  FNode.Bank.StorageClass := TFileStorage;
+  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+  // Reading database
+  FNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+  FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
+  FNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+  FNode.Node.NetServer.Active := true;
+
+  Repeat
+    Sleep(100);
+  Until Terminated;
+
+
+
+  FreeAndNil(FRPC);
+  FNode.NetServer.Active := false;
+  TNetData.NetData.Free;
+  FreeAndNil(FNode);
+  except
+    on e:Exception do begin
+      TLog.NewLog(lterror,Classname,'Exception '+E.Classname+': '+E.Message);
+      AWriteln('Exception '+E.Classname+': '+E.Message,false);
+    end;
+  end;
+end;
+
+{ TPCDaemon }
+
+procedure TPCDaemon.ThreadStopped(Sender: TObject);
+begin
+  FreeAndNil(FThread);
+end;
+
+function TPCDaemon.Start: Boolean;
+begin
+  Result:=inherited Start;
+  AWriteln('Daemon Start',Result);
+  FThread:=TPCDaemonThread.Create(True);
+  FThread.OnTerminate:=@ThreadStopped;
+  FThread.FreeOnTerminate:=False;
+  FThread.Resume;
+end;
+
+function TPCDaemon.Stop: Boolean;
+begin
+  Result:=inherited Stop;
+  AWriteln('Daemon Stop: ',Result);
+  FThread.Terminate;
+end;
+
+function TPCDaemon.Pause: Boolean;
+begin
+  Result:=inherited Pause;
+  AWriteln('Daemon pause: ',Result);
+  FThread.Suspend;
+end;
+
+function TPCDaemon.Continue: Boolean;
+begin
+  Result:=inherited Continue;
+  AWriteln('Daemon continue: ',Result);
+  FThread.Resume;
+end;
+
+function TPCDaemon.Execute: Boolean;
+begin
+  Result:=inherited Execute;
+  AWriteln('Daemon execute: ',Result);
+end;
+
+function TPCDaemon.ShutDown: Boolean;
+begin
+  Result:=inherited ShutDown;
+  AWriteln('Daemon Shutdown: ',Result);
+  FThread.Terminate;
+end;
+
+function TPCDaemon.Install: Boolean;
+begin
+  Result:=inherited Install;
+  AWriteln('Daemon Install: ',Result);
+end;
+
+function TPCDaemon.UnInstall: boolean;
+begin
+  Result:=inherited UnInstall;
+  AWriteln('Daemon UnInstall: ',Result);
+end;
+
+Type
+
+  { TPCDaemonMapper }
+
+  TPCDaemonMapper = Class(TCustomDaemonMapper)
+    Constructor Create(AOwner : TComponent); override;
+  end;
+
+{ TPCDaemonMapper }
+
+constructor TPCDaemonMapper.Create(AOwner: TComponent);
+
+Var
+  D : TDaemonDef;
+
+begin
+  inherited Create(AOwner);
+  D:=DaemonDefs.Add as TDaemonDef;
+  D.DisplayName:='Pascal Coin Daemon';
+  D.Name:='PascalCoinDaemon';
+  D.DaemonClassName:='TPCDaemon';
+  D.WinBindings.ServiceType:=stWin32;
+end;
+
+begin
+  TCrypto.InitCrypto;
+  RegisterDaemonClass(TPCDaemon);
+  RegisterDaemonMapper(TPCDaemonMapper);
+  Application.Title:='Daemon application';
+  Application.Run;
+end.
+