Herman Schoenfeld 6 жил өмнө
parent
commit
83078b01a2
34 өөрчлөгдсөн 1501 нэмэгдсэн , 445 устгасан
  1. 12 10
      src/core/UAccounts.pas
  2. 1 1
      src/core/UBaseTypes.pas
  3. 93 16
      src/core/UBlockChain.pas
  4. 9 0
      src/core/UCrypto.pas
  5. 49 19
      src/core/UNetProtocol.pas
  6. 35 9
      src/core/UNode.pas
  7. 49 0
      src/core/UOpTransaction.pas
  8. 15 2
      src/core/UPCCryptoLib4Pascal.pas
  9. 217 0
      src/core/UPCOperationsBlockValidator.pas
  10. 271 0
      src/core/UPCOperationsSignatureValidator.pas
  11. 1 1
      src/core/URPC.pas
  12. 8 0
      src/core/UTxMultiOperation.pas
  13. 0 101
      src/gui-classic/UFRMAccountInfo.dfm
  14. 0 29
      src/gui-classic/UFRMAccountInfo.pas
  15. 5 4
      src/gui-classic/UFRMNewPrivateKeyType.pas
  16. 291 183
      src/gui-classic/UGridUtils.pas
  17. 21 19
      src/libraries/cryptolib4pascal/ClpBits.pas
  18. 1 1
      src/libraries/cryptolib4pascal/ClpIECC.pas
  19. 1 1
      src/libraries/cryptolib4pascal/ClpPascalCoinIESEngine.pas
  20. 11 5
      src/libraries/cryptolib4pascal/CryptoLib.inc
  21. 1 1
      src/libraries/cryptolib4pascal/LICENSE
  22. 1 1
      src/libraries/cryptolib4pascal/README.md
  23. 6 10
      src/libraries/hashlib4pascal/HashLib.inc
  24. 8 6
      src/libraries/hashlib4pascal/HlpBits.pas
  25. 2 2
      src/libraries/hashlib4pascal/HlpBlake2B.pas
  26. 231 0
      src/libraries/hashlib4pascal/HlpCRC32Fast.pas
  27. 1 1
      src/libraries/hashlib4pascal/HlpFNV64.pas
  28. 9 8
      src/libraries/hashlib4pascal/HlpHashFactory.pas
  29. 21 0
      src/libraries/hashlib4pascal/LICENSE
  30. 110 0
      src/libraries/hashlib4pascal/README.md
  31. 13 6
      src/libraries/pascalcoin/UFolderHelper.pas
  32. 1 1
      src/libraries/simplebaselib4pascal/LICENSE
  33. 7 7
      src/libraries/sphere10/UCommon.pas
  34. 0 1
      src/pascalcoin_wallet_classic.dpr

+ 12 - 10
src/core/UAccounts.pas

@@ -126,7 +126,7 @@ Type
     Class Function IsAccountForSale(const accountInfo: TAccountInfo) : Boolean;
     Class Function IsAccountForSaleAcceptingTransactions(const accountInfo: TAccountInfo) : Boolean;
     Class Function GetECInfoTxt(Const EC_OpenSSL_NID: Word) : String;
-    Class Procedure ValidsEC_OpenSSL_NID(list : TList);
+    Class Procedure ValidsEC_OpenSSL_NID(list : TList<Word>);
     Class Function AccountKey2RawString(const account: TAccountKey): TRawBytes; overload;
     Class procedure AccountKey2RawString(const account: TAccountKey; var dest: TRawBytes); overload;
     Class Function RawString2Accountkey(const rawaccstr: TRawBytes): TAccountKey; overload;
@@ -308,7 +308,7 @@ Type
     class Function ConcatSafeBoxStream(Source1, Source2, Dest : TStream; var errors : String) : Boolean;
     class function ValidAccountName(const new_name : TRawBytes; var errors : String) : Boolean;
 
-    Function IsValidNewOperationsBlock(Const newOperationBlock : TOperationBlock; checkSafeBoxHash : Boolean; var errors : String) : Boolean;
+    Function IsValidNewOperationsBlock(Const newOperationBlock : TOperationBlock; checkSafeBoxHash, checkValidOperationsBlock : Boolean; var errors : String) : Boolean;
     class Function IsValidOperationBlock(Const newOperationBlock : TOperationBlock; var errors : String) : Boolean;
     Function GetActualTargetHash(protocolVersion : Word): TRawBytes;
     Function GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
@@ -1479,13 +1479,13 @@ begin
   end;
 end;
 
-class procedure TAccountComp.ValidsEC_OpenSSL_NID(list: TList);
+class procedure TAccountComp.ValidsEC_OpenSSL_NID(list: TList<Word>);
 begin
   list.Clear;
-  list.Add(TObject(CT_NID_secp256k1)); // = 714
-  list.Add(TObject(CT_NID_secp384r1)); // = 715
-  list.Add(TObject(CT_NID_sect283k1)); // = 729
-  list.Add(TObject(CT_NID_secp521r1)); // = 716
+  list.Add((CT_NID_secp256k1)); // = 714
+  list.Add((CT_NID_secp384r1)); // = 715
+  list.Add((CT_NID_sect283k1)); // = 729
+  list.Add((CT_NID_secp521r1)); // = 716
 end;
 
 { TProgressNotifyManyHelper }
@@ -2801,7 +2801,7 @@ begin
               // For TESTNET increase speed purposes, will only check latests blocks
             if ((iblock + (CT_BankToDiskEveryNBlocks * 10)) >= sbHeader.blockscount) then begin
             {$ENDIF}
-              If not IsValidNewOperationsBlock(block.blockchainInfo,False,aux_errors) then begin
+              If not IsValidNewOperationsBlock(block.blockchainInfo,False,True,aux_errors) then begin
                 errors := errors + ' > ' + aux_errors;
                 exit;
               end;
@@ -3279,7 +3279,7 @@ begin
   Result := Copy(_initialSafeboxHash);
 end;
 
-function TPCSafeBox.IsValidNewOperationsBlock(const newOperationBlock: TOperationBlock; checkSafeBoxHash : Boolean; var errors: String): Boolean;
+function TPCSafeBox.IsValidNewOperationsBlock(const newOperationBlock: TOperationBlock; checkSafeBoxHash, checkValidOperationsBlock : Boolean; var errors: String): Boolean;
   { This function will check a OperationBlock info as a valid candidate to be included in the safebox
 
     TOperationBlock contains the info of the new block EXCEPT the operations, including only operations_hash value (SHA256 of the Operations)
@@ -3373,7 +3373,9 @@ begin
     exit;
   end;
   {$ENDIF}
-  Result := IsValidOperationBlock(newOperationBlock,errors);
+  if checkValidOperationsBlock then begin
+    Result := IsValidOperationBlock(newOperationBlock,errors);
+  end else Result := True;
 end;
 
 class function TPCSafeBox.IsValidOperationBlock(const newOperationBlock: TOperationBlock; var errors: String): Boolean;

+ 1 - 1
src/core/UBaseTypes.pas

@@ -443,7 +443,7 @@ end;
 class function TPlatform.GetTickCount: TTickCount;
 begin
   Result := {$IFDEF CPU64}GetTickCount64{$ELSE}
-   {$IFDEF FPC}System.GetTickCount{$ELSE}
+   {$IFDEF FPC}SysUtils.GetTickCount{$ELSE}
      {$IFDEF MSWINDOWS}Windows.GetTickCount{$ELSE}
      TThread.GetTickCount;
      {$ENDIF}

+ 93 - 16
src/core/UBlockChain.pas

@@ -257,6 +257,8 @@ Type
     //
     function GetOperationStreamData : TBytes;
     class function GetOperationFromStreamData(StreamData : TBytes) : TPCOperation;
+    //
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; virtual; abstract;
   End;
 
   TPCOperationStorage = Record
@@ -339,6 +341,9 @@ Type
     Property OnChanged : TNotifyEvent read FOnChanged write FOnChanged;
     Property Max0feeOperationsBySigner : Integer Read FMax0feeOperationsBySigner write SetMax0feeOperationsBySigner;
     procedure MarkVerifiedECDSASignatures(operationsHashTreeToMark : TOperationsHashTree);
+
+    // Will add all operations of the HashTree to then end of AList without removing previous objects
+    function GetOperationsList(AList : TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
   End;
 
   { TPCOperationsComp }
@@ -357,6 +362,7 @@ Type
     FDisableds : Integer;
     FOperationsLock : TPCCriticalSection;
     FPreviousUpdatedBlocks : TAccountPreviousBlockInfo; // New Protocol V3 struct to store previous updated blocks
+    FHasValidOperationBlockInfo : Boolean;
     function GetOperation(index: Integer): TPCOperation;
     procedure SetBank(const value: TPCBank);
     procedure SetnOnce(const value: Cardinal);
@@ -421,6 +427,7 @@ Type
     Property PoW_Digest_Part3 : TRawBytes read FDigest_Part3;
     //
     Property PreviousUpdatedBlocks : TAccountPreviousBlockInfo read FPreviousUpdatedBlocks; // New Protocol V3 struct to store previous updated blocks
+    Property HasValidOperationBlockInfo : Boolean read FHasValidOperationBlockInfo write FHasValidOperationBlockInfo;
   End;
 
   TPCBankLog = procedure(sender: TPCBank; Operations: TPCOperationsComp; Logtype: TLogType ; const Logtxt: String) of object;
@@ -546,7 +553,9 @@ implementation
 
 uses
   Variants,
-  UTime, UConst, UOpTransaction;
+  UTime, UConst, UOpTransaction, UPCOrderedLists,
+  UPCOperationsSignatureValidator,
+  UPCOperationsBlockValidator;
 
 { TPCOperationsStorage }
 
@@ -890,9 +899,12 @@ procedure TPCBank.DiskRestoreFromOperations(max_block : Int64; restoreProgressNo
 Var
   errors: String;
   newBlock: TBlockAccount;
-  Operations: TPCOperationsComp;
   n : Int64;
   tc : TTickCount;
+  LBlocks : TList<TPCOperationsComp>;
+  LTmpPCOperationsComp : TPCOperationsComp;
+  i,j : Integer;
+  LSafeboxTransaction : TPCSafeBoxTransaction;
 begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
@@ -919,16 +931,42 @@ begin
         end;
       end;
       NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') BlockCount: '+inttostr(BlocksCount)+' Orphan: ' +Storage.Orphan);
-      Operations := TPCOperationsComp.Create(Self);
+      LBlocks := TList<TPCOperationsComp>.Create;
       try
         while ((BlocksCount<=max_block)) do begin
-          if Storage.BlockExists(BlocksCount) then begin
-            if Storage.LoadBlockChainBlock(Operations,BlocksCount) then begin
+          i := BlocksCount;
+          j := i + 99;
+          // Load a batch of TPCOperationsComp;
+          try
+            while ((i<=max_block) and (i<=j)) do begin
+              if Storage.BlockExists(i) then begin
+                LTmpPCOperationsComp := TPCOperationsComp.Create(Self);
+                if Storage.LoadBlockChainBlock(LTmpPCOperationsComp,i) then begin
+                  LBlocks.Add(LTmpPCOperationsComp);
+                  inc(i);
+                end else begin
+                  LTmpPCOperationsComp.Free;
+                  Break;
+                end;
+              end else Break;
+            end;
+
+            if (LBlocks.Count=0) then Exit;
+            
+            TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(LBlocks);
+            LSafeboxTransaction := TPCSafeBoxTransaction.Create(SafeBox);
+            try
+              TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LSafeboxTransaction,LBlocks);
+            finally
+              LSafeboxTransaction.Free;
+            end;
+
+            for i := 0 to LBlocks.Count-1 do begin
               SetLength(errors,0);
-              if Not AddNewBlockChainBlock(Operations,0,newBlock,errors) then begin
-                NewLog(Operations, lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
+              if Not AddNewBlockChainBlock(LBlocks[i],0,newBlock,errors) then begin
+                NewLog(LBlocks[i], lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
                 Storage.DeleteBlockChainBlocks(BlocksCount);
-                break;
+                Exit;
               end else begin
                 // To prevent continuous saving...
                 if ((BlocksCount+(CT_BankToDiskEveryNBlocks*2)) >= Storage.LastBlock ) or
@@ -937,17 +975,26 @@ begin
                 end;
                 if (Assigned(restoreProgressNotify)) And (TPlatform.GetElapsedMilliseconds(tc)>1000) then begin
                   tc := TPlatform.GetTickCount;
-                  restoreProgressNotify(Self,Format('Reading blockchain block %d/%d',[Operations.OperationBlock.block,Storage.LastBlock]),BlocksCount,Storage.LastBlock);
+                  restoreProgressNotify(Self,Format('Reading blockchain block %d/%d',[LBlocks[i].OperationBlock.block,Storage.LastBlock]),BlocksCount,Storage.LastBlock);
                 end;
               end;
-            end else break;
-          end else break;
-        end;
-        if FUpgradingToV2 then Storage.CleanupVersion1Data;
+            end;
+          finally
+            // Free blocks
+            for i := 0 to LBlocks.Count-1 do begin
+              LBlocks[i].Free;
+            end;
+            LBlocks.Clear;
+          end;
+
+        end; // while
+
       finally
-        Operations.Free;
+        LBlocks.Free;
+        if FUpgradingToV2 then Storage.CleanupVersion1Data;
+        NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
       end;
-      NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
+
     finally
       FIsRestoringFromFile := False;
       FUpgradingToV2 := false;
@@ -1249,6 +1296,8 @@ begin
     // Note:
     // This function does not initializes "account_key" nor "block_payload" fields
 
+    FHasValidOperationBlockInfo := False;
+
     FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
     if Assigned(FBank) then begin
       resetNewTarget := False;
@@ -1315,6 +1364,7 @@ begin
     FDigest_Part1 := Operations.FDigest_Part1;
     FDigest_Part2_Payload := Operations.FDigest_Part2_Payload;
     FDigest_Part3 := Operations.FDigest_Part3;
+    FHasValidOperationBlockInfo := Operations.FHasValidOperationBlockInfo;
   finally
     Operations.Unlock;
     Unlock;
@@ -1338,6 +1388,8 @@ begin
       FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
     end;
     FPreviousUpdatedBlocks.CopyFrom(Operations.FPreviousUpdatedBlocks);
+
+    FHasValidOperationBlockInfo := False;
     // Recalc all
     Calc_Digest_Parts; // Does not need to recalc PoW
   finally
@@ -1362,6 +1414,7 @@ begin
   FOperationBlock := GetFirstBlock;
   FSafeBoxTransaction := Nil;
   FPreviousUpdatedBlocks := TAccountPreviousBlockInfo.Create;
+  FHasValidOperationBlockInfo := False;
   if Assigned(ABank) then begin
     SetBank( TPCBank(ABank) );
   end else Clear(true);
@@ -1896,10 +1949,13 @@ begin
       exit;
     end;
     // Check OperationBlock info:
-    If not SafeBoxTransaction.FreezedSafeBox.IsValidNewOperationsBlock(OperationBlock,True,errors) then exit;
+    If not SafeBoxTransaction.FreezedSafeBox.IsValidNewOperationsBlock(OperationBlock,True,Not HasValidOperationBlockInfo, errors) then exit;
     // Execute SafeBoxTransaction operations:
     SafeBoxTransaction.Rollback;
     FPreviousUpdatedBlocks.Clear;
+    //
+    TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree);
+    //
     for i := 0 to Count - 1 do begin
       If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
         errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors;
@@ -2253,6 +2309,27 @@ begin
   end;
 end;
 
+function TOperationsHashTree.GetOperationsList(AList: TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
+Var LList : TList<Pointer>;
+  i : Integer;
+  LOp : TPCOperation;
+begin
+  Result := 0;
+  LList := FHashTreeOperations.LockList;
+  try
+    for i := 0 to LList.Count-1 do begin
+      LOp := POperationHashTreeReg(LList[i])^.Op;
+      if (Not AAddOnlyOperationsWithoutNotVerifiedSignature) or
+        (AAddOnlyOperationsWithoutNotVerifiedSignature and (not LOp.HasValidSignature)) then begin
+        AList.Add( LOp );
+        inc(Result);
+      end;
+    end;
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
 function TOperationsHashTree.IndexOfOperation(op: TPCOperation): Integer;
 Var iPosInOrdered : Integer;
   l : TList<Pointer>;

+ 9 - 0
src/core/UCrypto.pas

@@ -49,6 +49,7 @@ Type
   TECPrivateKey = Class
   private
     FPrivateKeyInfo: TECPrivateKeyInfo;
+    FBufferedPublicKey : TECDSA_Public;
     procedure SetPrivateKeyInfo(const Value: TECPrivateKeyInfo);
     function GetPublicKey: TECDSA_Public;
     function GetEC_OpenSSL_NID: Word;
@@ -191,6 +192,7 @@ begin
   FPrivateKeyInfo.EC_KEY_Ptr := Nil;
   FPrivateKeyInfo.RAW_PrivKey := Nil;
   FPrivateKeyInfo.EC_OpenSSL_NID := CT_Default_EC_OpenSSL_NID;
+  FBufferedPublicKey := CT_TECDSA_Public_Nul;
 end;
 
 destructor TECPrivateKey.Destroy;
@@ -261,6 +263,7 @@ begin
   {$ELSE}
   FPrivateKeyInfo.RAW_PrivKey := Nil;
   {$ENDIF}
+  FBufferedPublicKey := CT_TECDSA_Public_Nul;
 end;
 
 function TECPrivateKey.GetEC_OpenSSL_NID: Word;
@@ -274,6 +277,9 @@ var BNx,BNy : PBIGNUM;
   ctx : PBN_CTX;
 {$ENDIF}
 begin
+  if FBufferedPublicKey.EC_OpenSSL_NID<>CT_TECDSA_Public_Nul.EC_OpenSSL_NID then begin
+    Exit(FBufferedPublicKey);
+  end;
 {$IFDEF Use_OpenSSL}
   Result.EC_OpenSSL_NID := FPrivateKeyInfo.EC_OpenSSL_NID;
   ctx := BN_CTX_new;
@@ -293,6 +299,7 @@ begin
 {$ELSE}
   Result := TPCCryptoLib4Pascal.DoGetPublicKey(EC_OpenSSL_NID,FPrivateKeyInfo.RAW_PrivKey);
 {$ENDIF}
+  FBufferedPublicKey := Result;
 end;
 
 function TECPrivateKey.HasPrivateKey: Boolean;
@@ -430,6 +437,7 @@ begin
   {$IFNDEF Use_OpenSSL}
   FPrivateKeyInfo.EC_KEY_Ptr := Nil;
   {$ENDIF}
+  FBufferedPublicKey := CT_TECDSA_Public_Nul;
 end;
 
 function TECPrivateKey.SetPrivateKeyFromHexa(AEC_OpenSSL_NID : Word; const hexa : String) : Boolean;
@@ -477,6 +485,7 @@ begin
   // TODO: Check is valid!
   {$ENDIF}
   Result := True;
+  FBufferedPublicKey := CT_TECDSA_Public_Nul;
 end;
 
 { TCrypto }

+ 49 - 19
src/core/UNetProtocol.pas

@@ -500,7 +500,8 @@ Const
 implementation
 
 uses
-  UConst, ULog, UNode, UTime, UPCEncryption, UChunk;
+  UConst, ULog, UNode, UTime, UPCEncryption, UChunk,
+  UPCOperationsBlockValidator, UPCOperationsSignatureValidator;
 
 Const
   CT_NetTransferType : Array[TNetTransferType] of String = ('Unknown','Request','Response','Autosend');
@@ -2893,11 +2894,15 @@ begin
 end;
 
 procedure TNetConnection.DoProcess_GetBlocks_Response(HeaderData: TNetHeaderData; DataBuffer: TStream);
-  var op : TPCOperationsComp;
-    opcount,i : Cardinal;
+  var LTmpOp : TPCOperationsComp;
+    LOpCountCardinal,c : Cardinal;
+    LOpCount : Integer;
+    i : Integer;
     newBlockAccount : TBlockAccount;
   errors : String;
   DoDisconnect : Boolean;
+  LBlocks : TList<TPCOperationsComp>;
+  LSafeboxTransaction : TPCSafeBoxTransaction;
 begin
   DoDisconnect := true;
   try
@@ -2911,50 +2916,75 @@ begin
     end;
     // DataBuffer contains: from and to
     errors := 'Invalid structure';
-    op := TPCOperationsComp.Create(nil);
+    LBlocks := TList<TPCOperationsComp>.Create;
     Try
-      op.bank := TNode.Node.Bank;
       if DataBuffer.Size-DataBuffer.Position<4 then begin
         DisconnectInvalidClient(false,'DoProcess_GetBlocks_Response invalid format: '+errors);
         exit;
       end;
-      DataBuffer.Read(opcount,4);
+      DataBuffer.Read(LOpCountCardinal,4);
+      LOpCount := LOpCountCardinal;
       DoDisconnect :=false;
-      for I := 1 to opcount do begin
-        if Not op.LoadBlockFromStream(DataBuffer,errors) then begin
-           errors := 'Error decoding block '+inttostr(i)+'/'+inttostr(opcount)+' Errors:'+errors;
-           DoDisconnect := true;
-           exit;
+      for i := 0 to LOpCount-1 do begin
+        LTmpOp := TPCOperationsComp.Create(nil);
+        try
+          LTmpOp.bank := TNode.Node.Bank;
+          if Not LTmpOp.LoadBlockFromStream(DataBuffer,errors) then begin
+             errors := 'Error decoding block '+inttostr(i+1)+'/'+inttostr(LOpCount)+' Errors:'+errors;
+             DoDisconnect := true;
+             Exit;
+          end;
+
+          if (LTmpOp.OperationBlock.block=TNode.Node.Bank.BlocksCount+i) then begin
+            TNode.Node.MarkVerifiedECDSASignaturesFromMemPool(LTmpOp); // Improvement speed v4.0.2
+            LBlocks.Add(LTmpOp);
+            LTmpOp := Nil;
+          end else Break;
+        finally
+          FreeAndNil(LTmpOp);
         end;
-        if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
-          TNode.Node.MarkVerifiedECDSASignaturesFromMemPool(op); // Improvement speed v4.0.2
-          if (TNode.Node.Bank.AddNewBlockChainBlock(op,TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock, newBlockAccount,errors)) then begin
+      end;
+
+      TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(LBlocks);
+      LSafeboxTransaction := TPCSafeBoxTransaction.Create(TNode.Node.Bank.SafeBox);
+      try
+        TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LSafeboxTransaction,LBlocks);
+      finally
+        LSafeboxTransaction.Free;
+      end;
+
+      for i := 0 to LBlocks.Count-1 do begin
+        if (LBlocks[i].OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+          if (TNode.Node.Bank.AddNewBlockChainBlock(LBlocks[i],TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock, newBlockAccount,errors)) then begin
             // Ok, one more!
           end else begin
             // Is not a valid entry????
             // Perhaps an orphan blockchain: Me or Client!
             TLog.NewLog(ltinfo,Classname,'Distinct operation block found! My:'+
                 TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.SafeBox.Block(TNode.Node.Bank.BlocksCount-1).blockchainInfo)+
-                ' remote:'+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' Errors: '+errors);
+                ' remote:'+TPCOperationsComp.OperationBlockToText(LBlocks[i].OperationBlock)+' Errors: '+errors);
           end;
         end else begin
           // Receiving an unexpected operationblock
-          TLog.NewLog(lterror,classname,'Received a distinct block, finalizing: '+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')' );
+          TLog.NewLog(lterror,classname,'Received a distinct block, finalizing: '+TPCOperationsComp.OperationBlockToText(LBlocks[i].OperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')' );
           FIsDownloadingBlocks := false;
           exit;
         end;
         sleep(1);
       end;
       FIsDownloadingBlocks := false;
-      if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
-        Send_GetBlocks(TNode.Node.Bank.BlocksCount,100,i);
+      if ((LOpCount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
+        Send_GetBlocks(TNode.Node.Bank.BlocksCount,100,c);
       end else begin
         // No more blocks to download, download Pending operations
         DoProcess_GetPendingOperations;
       end;
       TNode.Node.NotifyBlocksChanged;
     Finally
-      op.Free;
+      for i := 0 to LBlocks.Count-1 do begin
+        LBlocks[i].Free;
+      end;
+      LBlocks.Free;
     End;
   Finally
     if DoDisconnect then begin

+ 35 - 9
src/core/UNode.pas

@@ -91,7 +91,8 @@ Type
     //
     Procedure NotifyBlocksChanged;
     //
-    procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0);
+    procedure GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
+    procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
     Function FindOperation(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : Boolean;
     Function FindOperationExt(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : TSearchOperationResult;
     Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOperationResult;
@@ -193,7 +194,7 @@ Type
 
 implementation
 
-Uses UOpTransaction, UConst, UTime, UCommon;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator;
 
 var _Node : TNode;
 
@@ -423,6 +424,7 @@ begin
       {$IFDEF BufferOfFutureOperations}
       Process_BufferOfFutureOperations(valids_operations);
       {$ENDIF}
+
       for j := 0 to OperationsHashTree.OperationsCount-1 do begin
         ActOp := OperationsHashTree.GetOperation(j);
         If (FOperations.OperationsHashTree.IndexOfOperation(ActOp)<0) then begin
@@ -476,12 +478,14 @@ begin
               {$IFDEF BufferOfFutureOperations}
               // Used to solve 2.0.0 "invalid order of operations" bug
               If (Assigned(SenderConnection)) Then begin
-                sAcc := FOperations.SafeBoxTransaction.Account(ActOp.SignerAccount);
-                If (sAcc.n_operation<ActOp.N_Operation) Or
-                   ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance=0) And (ActOp.OperationFee>0) And (ActOp.OpType = CT_Op_Changekey)) then begin
-                  If FBufferAuxWaitingOperations.IndexOfOperation(ActOp)<0 then begin
-                    FBufferAuxWaitingOperations.AddOperationToHashTree(ActOp);
-                    TLog.NewLog(ltInfo,Classname,Format('New FromBufferWaitingOperations %d/%d (new buffer size:%d): %s',[j+1,OperationsHashTree.OperationsCount,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
+                if ActOp.SignerAccount<FOperations.SafeBoxTransaction.FreezedSafeBox.AccountsCount then begin
+                  sAcc := FOperations.SafeBoxTransaction.Account(ActOp.SignerAccount);
+                  If (sAcc.n_operation<ActOp.N_Operation) Or
+                     ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance=0) And (ActOp.OperationFee>0) And (ActOp.OpType = CT_Op_Changekey)) then begin
+                    If FBufferAuxWaitingOperations.IndexOfOperation(ActOp)<0 then begin
+                      FBufferAuxWaitingOperations.AddOperationToHashTree(ActOp);
+                      TLog.NewLog(ltInfo,Classname,Format('New FromBufferWaitingOperations %d/%d (new buffer size:%d): %s',[j+1,OperationsHashTree.OperationsCount,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
+                    end;
                   end;
                 end;
               end;
@@ -808,7 +812,8 @@ begin
   end;
 end;
 
-procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock : Cardinal = 0);
+procedure TNode.GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal;
+  MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock: Cardinal);
   // Optimization:
   // For better performance, will only include at "OperationsResume" values betweeen "startOperation" and "endOperation"
 
@@ -824,6 +829,9 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
     acc_0_miner_reward, acc_4_dev_reward : Int64;
     acc_4_for_dev : Boolean;
   begin
+    if Assigned(AOwnerThread) then begin
+      if AOwnerThread.terminated then Exit;
+    end;
     if (act_depth<=0) then exit;
     opc := TPCOperationsComp.Create(Nil);
     Try
@@ -833,6 +841,9 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
         while (last_block_number>block_number) And (act_depth>0)
           And (block_number >= (account_number DIV CT_AccountsPerBlock))
           And (nOpsCounter <= EndOperation) do begin
+          if Assigned(AOwnerThread) then begin
+            if AOwnerThread.terminated then Exit;
+          end;
           found_in_block := False;
           last_block_number := block_number;
           l.Clear;
@@ -926,6 +937,21 @@ begin
   end;
 end;
 
+procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock : Cardinal = 0);
+var LOpList : TList<TOperationResume>;
+  i : Integer;
+begin
+  LOpList := TList<TOperationResume>.Create;
+  try
+    GetStoredOperationsFromAccount(Nil,LOpList,account_number,MaxDepth,StartOperation,EndOperation,SearchBackwardsStartingAtBlock);
+    for i := 0 to LOpList.Count-1 do begin
+      OperationsResume.Add(LOpList[i]);
+    end;
+  finally
+    LOpList.Free;
+  end;
+end;
+
 function TNode.FindNOperation(block, account, n_operation: Cardinal;
   var OpResume: TOperationResume): TSearchOperationResult;
   // Note: block = 0 search in all blocks. If Block>0 must match a valid block with operation with this account

+ 49 - 0
src/core/UOpTransaction.pas

@@ -98,6 +98,8 @@ Type
     Constructor CreateTransaction(current_protocol : Word; sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   { TOpChangeKey }
@@ -127,6 +129,8 @@ Type
     Property Data : TOpChangeKeyData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   { TOpChangeKeySigned }
@@ -163,6 +167,7 @@ Type
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   // NEW OPERATIONS PROTOCOL 2
@@ -232,6 +237,7 @@ Type
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   TOpListAccountForSale = Class(TOpListAccount)
@@ -290,6 +296,8 @@ Type
     Property Data : TOpChangeAccountInfoData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 
@@ -338,6 +346,7 @@ Type
     Property Data : TOpDataData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 Const
@@ -372,6 +381,13 @@ begin
   FData := CT_TOpChangeAccountInfoData_NUL;
 end;
 
+function TOpChangeAccountInfo.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpChangeAccountInfo.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
 var b : byte;
 begin
@@ -923,6 +939,13 @@ begin
   FData := CT_TOpTransactionData_NUL;
 end;
 
+function TOpTransaction.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.sender);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpTransaction.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var b : Byte;
 begin
@@ -1338,6 +1361,13 @@ begin
   FData := CT_TOpChangeKeyData_NUL;
 end;
 
+function TOpChangeKey.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpChangeKey.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var raw : TRawBytes;
 begin
@@ -1564,6 +1594,11 @@ begin
   FData := CT_TOpRecoverFoundsData_NUL;
 end;
 
+function TOpRecoverFounds.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+begin
+  Result := True; // Nobody signs here
+end;
+
 function TOpRecoverFounds.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 begin
   Result := false;
@@ -1804,6 +1839,13 @@ begin
   Result := (Not IsDelist) And (FData.new_public_key.EC_OpenSSL_NID<>0);
 end;
 
+function TOpListAccount.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpListAccount.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var raw : TRawBytes;
   w : Word;
@@ -2124,6 +2166,13 @@ begin
   FData := CT_TOpDataData_NUL;
 end;
 
+function TOpData.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
 begin
   Stream.Write(FData.account_signer,Sizeof(FData.account_signer));

+ 15 - 2
src/core/UPCCryptoLib4Pascal.pas

@@ -145,6 +145,7 @@ Uses
   ClpECKeyPairGenerator,
   //
   HlpSHA2_256,
+  HlpRIPEMD160,
   //
   UAccounts,
   UConst,
@@ -381,13 +382,25 @@ begin
 end;
 
 class procedure TPCCryptoLib4Pascal.DoRIPEMD160(const AInput: TBytes; var AOutput: TBytes);
+Var Lrmd160 : TRIPEMD160;
 begin
-  AOutput := TDigestUtilities.CalculateDigest('RIPEMD160', AInput);
+  Lrmd160 := TRIPEMD160.Create;
+  try
+    AOutput := Lrmd160.ComputeBytes(AInput).GetBytes;
+  finally
+    Lrmd160.Free;
+  end;
 end;
 
 class procedure TPCCryptoLib4Pascal.DoSHA256(const AInput: TBytes; var AOutput: TBytes);
+var Lsha : TSHA2_256;
 begin
-  AOutput := TDigestUtilities.CalculateDigest('SHA-256', AInput);
+  Lsha := TSHA2_256.Create;
+  try
+    AOutput := Lsha.ComputeBytes(AInput).GetBytes;
+  finally
+    Lsha.Free;
+  end;
 end;
 
 class function TPCCryptoLib4Pascal.EVP_GetKeyIV(const APasswordBytes,

+ 217 - 0
src/core/UPCOperationsBlockValidator.pas

@@ -0,0 +1,217 @@
+unit UPCOperationsBlockValidator;
+
+{ Copyright (c) 2019 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 the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{
+  This unit adds a TPCOperationsBlockValidator class that will check
+  TOperationBlock in a multithread mode
+
+  NOTE: This object is only a helper for fast processing speed when
+  multithreading can help. There is no warranty that will validate all
+
+  }
+
+
+interface
+
+{$I config.inc}
+
+Uses UThread, UAccounts, UPCOrderedLists, UBlockChain,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
+
+type
+  TPCOperationsBlockValidator = Class;
+
+  TPCOperationsBlockValidatorThread = Class(TPCThread)
+  private
+    FValidator : TPCOperationsBlockValidator;
+    //
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(AValidator : TPCOperationsBlockValidator);
+  End;
+
+  TPCOperationsBlockValidator = Class
+  private
+    FLock : TPCCriticalSection;
+    //
+    FPCOperationsCompList : TList<TPCOperationsComp>;
+    FLastIndexOperationsBlock : Integer;
+    //
+    FValidatedOkCount : Integer;
+    FValidatedErrorCount : Integer;
+  protected
+    function GetNextOperationBlock(var ANextOperationBlock : TOperationBlock; var AIndex : Integer) : Boolean;
+    procedure SetOperationBlockResult(const AOperationBlock : TOperationBlock; AIndex : Integer; AValidated : Boolean);
+  public
+    Constructor Create;
+    destructor Destroy; override;
+    function Validate(APCOperationsCompList : TList<TPCOperationsComp>; var AValidatedOkCount, AValidatedErrorCount : Integer) : Integer;
+    class function MultiThreadValidateOperationsBlock(APCOperationsCompList : TList<TPCOperationsComp>) : Boolean;
+  End;
+
+implementation
+
+Uses
+  SysUtils,
+  ULog, UBaseTypes,
+  UCommon;
+
+var _Cpus : Integer = 0;
+
+{ TPCOperationsBlockValidator }
+
+constructor TPCOperationsBlockValidator.Create;
+begin
+  FLastIndexOperationsBlock := -1;
+  FLock := TPCCriticalSection.Create('');
+end;
+
+destructor TPCOperationsBlockValidator.Destroy;
+begin
+  FreeAndNil(FLock);
+  inherited;
+end;
+
+function TPCOperationsBlockValidator.GetNextOperationBlock(var ANextOperationBlock : TOperationBlock; var AIndex : Integer) : Boolean;
+begin
+  FLock.Acquire;
+  try
+    // Search new
+    AIndex := FLastIndexOperationsBlock + 1; // Move to next
+    if (AIndex<FPCOperationsCompList.Count) then begin
+      ANextOperationBlock := FPCOperationsCompList[AIndex].OperationBlock;
+      Result := True;
+      FLastIndexOperationsBlock := AIndex;
+    end else Result := False;
+  finally
+    FLock.Release;
+  end;
+end;
+
+class function TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(APCOperationsCompList: TList<TPCOperationsComp>): Boolean;
+var LMultiThreadValidator : TPCOperationsBlockValidator;
+  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LTC : TTickCount;
+begin
+  if _Cpus<=0 then begin
+    _Cpus := TLogicalCPUCount.GetLogicalCPUCount;
+  end;
+  if _Cpus<=1 then Exit;
+  if APCOperationsCompList.Count<_Cpus then Exit;   // If less than cpus, no need for multithreading...
+
+  LTC := TPlatform.GetTickCount;
+  LMultiThreadValidator := TPCOperationsBlockValidator.Create;
+  try
+    LValidatedTotal := LMultiThreadValidator.Validate(APCOperationsCompList,LValidatedOk,LValidatedError);
+    LTC := TPlatform.GetElapsedMilliseconds(LTC);
+    if (LValidatedTotal>0) and (LTC>0) then begin
+      TLog.NewLog(ltdebug,ClassName,Format('Validated %d Operation blocks info with %d valids and %d Errors in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+    end;
+    Result := LValidatedOk = LValidatedTotal;
+  finally
+    LMultiThreadValidator.Free;
+  end;
+end;
+
+procedure TPCOperationsBlockValidator.SetOperationBlockResult(const AOperationBlock : TOperationBlock; AIndex : Integer; AValidated: Boolean);
+begin
+  FLock.Acquire;
+  try
+    if AValidated then inc(FValidatedOkCount)
+    else inc(FValidatedErrorCount);
+    FPCOperationsCompList[AIndex].HasValidOperationBlockInfo := AValidated;
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TPCOperationsBlockValidator.Validate(APCOperationsCompList : TList<TPCOperationsComp>; var AValidatedOkCount, AValidatedErrorCount : Integer) : Integer;
+var LMaxThreads : Integer;
+  LThreads : TList<TPCOperationsBlockValidatorThread>;
+  i,LTerminatedThreads : Integer;
+begin
+  FValidatedOkCount := 0;
+  FValidatedErrorCount := 0;
+  if APCOperationsCompList.Count<=0 then Exit(0);
+
+  FLastIndexOperationsBlock := -1;
+
+  if _Cpus<=0 then begin
+    _Cpus := TLogicalCPUCount.GetLogicalCPUCount;
+  end;
+  LMaxThreads := _Cpus-1;
+  if (LMaxThreads<=0) then LMaxThreads := 1;
+  LThreads := TList<TPCOperationsBlockValidatorThread>.Create;
+  Try
+    // Init values
+    FLastIndexOperationsBlock := -1;
+    FPCOperationsCompList := APCOperationsCompList;
+
+    // Step 1: Create the threads:
+    for i := 1 to LMaxThreads do begin
+      LThreads.Add( TPCOperationsBlockValidatorThread.Create(Self) );
+    end;
+    // Step 2: Start the threads
+    for i := 0 to LThreads.Count-1 do begin
+      LThreads[i].Suspended := False;
+    end;
+    // Step 3: Wait until error of finalized
+    repeat
+      LTerminatedThreads := 0;
+      for i := 0 to LThreads.Count-1 do begin
+        if LThreads[i].Terminated then inc(LTerminatedThreads);
+      end;
+      Sleep(1);
+    until (LTerminatedThreads>=LThreads.Count);
+  Finally
+    for i := 0 to LThreads.Count-1 do begin
+      LThreads[i].Terminate;
+      LThreads[i].WaitFor;
+      LThreads[i].Free;
+    end;
+  End;
+  AValidatedOkCount := FValidatedOkCount;
+  AValidatedErrorCount := FValidatedErrorCount;
+  Result := FPCOperationsCompList.Count;
+end;
+
+{ TPCOperationsBlockValidatorThread }
+
+procedure TPCOperationsBlockValidatorThread.BCExecute;
+var LOperationBlock : TOperationBlock;
+  LErrors : String;
+  LValidated : Boolean;
+  LIndex : Integer;
+begin
+  repeat
+    if FValidator.GetNextOperationBlock( LOperationBlock, LIndex ) then begin
+      LValidated := TPCSafeBox.IsValidOperationBlock(LOperationBlock,LErrors);
+      FValidator.SetOperationBlockResult(LOperationBlock, LIndex, LValidated);
+    end else Terminate;
+  until (Terminated);
+end;
+
+constructor TPCOperationsBlockValidatorThread.Create(AValidator : TPCOperationsBlockValidator);
+begin
+  FValidator := AValidator;
+  inherited Create(True);
+  FreeOnTerminate := False;
+end;
+
+end.

+ 271 - 0
src/core/UPCOperationsSignatureValidator.pas

@@ -0,0 +1,271 @@
+unit UPCOperationsSignatureValidator;
+
+{ Copyright (c) 2019 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 the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{
+  This unit adds a TPCOperationsSignatureValidator class that will check
+  signature of operations in a multithread mode
+
+  NOTE: This object is only a helper for fast processing speed when
+  multithreading can help. There is no warranty that will validate all
+
+  }
+
+
+interface
+
+{$I config.inc}
+
+Uses UThread, UAccounts, UPCOrderedLists, UBlockChain,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
+
+type
+  TPCOperationsSignatureValidator = Class;
+
+  TPCOperationsSignatureValidatorThread = Class(TPCThread)
+  private
+    FValidator : TPCOperationsSignatureValidator;
+    //
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(AValidator : TPCOperationsSignatureValidator);
+  End;
+
+  TPCOperationsSignatureValidator = Class
+  private
+    FLock : TPCCriticalSection;
+    //
+    FOperationsList : TList<TPCOperation>;
+    FLastIndexOperations : Integer;
+    //
+    FSafeBoxTransaction : TPCSafeBoxTransaction;
+    FValidatedOkCount : Integer;
+    FValidatedErrorCount : Integer;
+  protected
+    function GetNextOperation(AValidatorThread : TPCOperationsSignatureValidatorThread) : TPCOperation;
+    procedure SetOperationCheckResult(AValidatorThread : TPCOperationsSignatureValidatorThread; APCOperation : TPCOperation; AValidated : Boolean);
+  public
+    Constructor Create(ASafeBoxTransaction : TPCSafeBoxTransaction);
+    destructor Destroy; override;
+    function Validate(AOperationsList : TList<TPCOperation>) : Integer;
+    class procedure MultiThreadPreValidateSignatures(ASafeBoxTransaction : TPCSafeBoxTransaction; AOperationsHashTree : TOperationsHashTree); overload;
+    class procedure MultiThreadPreValidateSignatures(ASafeBoxTransaction : TPCSafeBoxTransaction; APCOperationsCompList: TList<TPCOperationsComp>); overload;
+  End;
+
+implementation
+
+Uses
+  SysUtils,
+  ULog, UBaseTypes,
+  UCommon;
+
+var _Cpus : Integer = 0;
+
+{ TPCOperationsSignatureValidator }
+
+constructor TPCOperationsSignatureValidator.Create(ASafeBoxTransaction: TPCSafeBoxTransaction);
+begin
+  FSafeBoxTransaction := ASafeBoxTransaction;
+  FLastIndexOperations := -1;
+  FLock := TPCCriticalSection.Create('');
+  FValidatedOkCount := 0;
+  FValidatedErrorCount := 0;
+end;
+
+destructor TPCOperationsSignatureValidator.Destroy;
+begin
+  FreeAndNil(FLock);
+  inherited;
+end;
+
+function TPCOperationsSignatureValidator.GetNextOperation(AValidatorThread : TPCOperationsSignatureValidatorThread) : TPCOperation;
+var LIndex : Integer;
+begin
+  FLock.Acquire;
+  try
+    // Search new
+    LIndex := FLastIndexOperations + 1; // Move to next
+    if (LIndex<FOperationsList.Count) then begin
+      Result := FOperationsList[LIndex];
+      FLastIndexOperations := Lindex;
+    end else Result := Nil;
+  finally
+    FLock.Release;
+  end;
+end;
+
+class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(
+  ASafeBoxTransaction: TPCSafeBoxTransaction; APCOperationsCompList: TList<TPCOperationsComp>);
+var LList : TList<TPCOperation>;
+  i : Integer;
+  LMultiThreadValidator : TPCOperationsSignatureValidator;
+  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LTC : TTickCount;
+begin
+  if _Cpus<=0 then begin
+    _Cpus := TLogicalCPUCount.GetLogicalCPUCount;
+  end;
+  if _Cpus<=1 then Exit;
+
+  LList := TList<TPCOperation>.Create;
+  Try
+    for i := 0 to APCOperationsCompList.Count-1 do begin
+      APCOperationsCompList[i].OperationsHashTree.GetOperationsList(LList,True);
+    end;
+    LTC := TPlatform.GetTickCount;
+    LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction);
+    try
+      LValidatedTotal := LMultiThreadValidator.Validate(LList);
+      LValidatedOk := LMultiThreadValidator.FValidatedOkCount;
+      LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
+      LTC := TPlatform.GetElapsedMilliseconds(LTC);
+      if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations from %d Blocks with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,APCOperationsCompList.Count,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+      end;
+    finally
+      LMultiThreadValidator.Free;
+    end;
+
+  Finally
+    LList.Free;
+  End;
+
+end;
+
+
+class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(
+  ASafeBoxTransaction: TPCSafeBoxTransaction; AOperationsHashTree: TOperationsHashTree);
+var LMultiThreadValidator : TPCOperationsSignatureValidator;
+  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LTC : TTickCount;
+  LList : TList<TPCOperation>;
+begin
+  if _Cpus<=0 then begin
+    _Cpus := TLogicalCPUCount.GetLogicalCPUCount;
+  end;
+  if _Cpus<=1 then Exit;
+  if AOperationsHashTree.OperationsCount<_Cpus then Exit;   // If less than cpus, no need for multithreading...
+
+  LTC := TPlatform.GetTickCount;
+  LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction);
+  try
+    LList := TList<TPCOperation>.Create;
+    Try
+      AOperationsHashTree.GetOperationsList(Llist,True);
+      if LList.Count<_Cpus then Exit; // No need for multithreading...
+
+      LValidatedTotal := LMultiThreadValidator.Validate(LList);
+      LValidatedOk := LMultiThreadValidator.FValidatedOkCount;
+      LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
+      LTC := TPlatform.GetElapsedMilliseconds(LTC);
+      if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+      end;
+    Finally
+      LList.Free;
+    End;
+  finally
+    LMultiThreadValidator.Free;
+  end;
+end;
+
+procedure TPCOperationsSignatureValidator.SetOperationCheckResult(
+  AValidatorThread: TPCOperationsSignatureValidatorThread;
+  APCOperation: TPCOperation; AValidated: Boolean);
+begin
+  FLock.Acquire;
+  try
+    if AValidated then inc(FValidatedOkCount)
+    else inc(FValidatedErrorCount);
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TPCOperationsSignatureValidator.Validate(AOperationsList : TList<TPCOperation>) : Integer;
+var LMaxThreads : Integer;
+  LThreads : TList<TPCOperationsSignatureValidatorThread>;
+  i,LTerminatedThreads : Integer;
+begin
+  FValidatedOkCount := 0;
+  FValidatedErrorCount := 0;
+  if AOperationsList.Count<=0 then Exit(0);
+
+  FLastIndexOperations := -1;
+
+  if _Cpus<=0 then begin
+    _Cpus := TLogicalCPUCount.GetLogicalCPUCount;
+  end;
+  LMaxThreads := _Cpus-1;
+  if (LMaxThreads<=0) then LMaxThreads := 1;
+  LThreads := TList<TPCOperationsSignatureValidatorThread>.Create;
+  Try
+    // Init values
+    FLastIndexOperations := -1;
+    FOperationsList := AOperationsList;
+
+    // Step 1: Create the threads:
+    for i := 1 to LMaxThreads do begin
+      LThreads.Add( TPCOperationsSignatureValidatorThread.Create(Self) );
+    end;
+    // Step 2: Start the threads
+    for i := 0 to LThreads.Count-1 do begin
+      LThreads[i].Suspended := False;
+    end;
+    // Step 3: Wait until error of finalized
+    repeat
+      LTerminatedThreads := 0;
+      for i := 0 to LThreads.Count-1 do begin
+        if LThreads[i].Terminated then inc(LTerminatedThreads);
+      end;
+      Sleep(1);
+    until (LTerminatedThreads>=LThreads.Count);
+  Finally
+    for i := 0 to LThreads.Count-1 do begin
+      LThreads[i].Terminate;
+      LThreads[i].WaitFor;
+      LThreads[i].Free;
+    end;
+  End;
+  Result := FOperationsList.Count;
+end;
+
+{ TPCOperationsSignatureValidatorThread }
+
+procedure TPCOperationsSignatureValidatorThread.BCExecute;
+var LOperation : TPCOperation;
+begin
+  repeat
+    LOperation := FValidator.GetNextOperation(Self);
+    if Assigned(LOperation) then begin
+      if Not LOperation.HasValidSignature then begin
+        // Only will validate if HasValidSignature is False (Not validated before)
+        FValidator.SetOperationCheckResult(Self,LOperation, LOperation.IsValidSignatureBasedOnCurrentSafeboxState(FValidator.FSafeBoxTransaction));
+      end;
+    end;
+  until (Not Assigned(LOperation)) or (Terminated);
+end;
+
+constructor TPCOperationsSignatureValidatorThread.Create(AValidator: TPCOperationsSignatureValidator);
+begin
+  FValidator := AValidator;
+  inherited Create(True);
+  FreeOnTerminate := False;
+end;
+
+end.

+ 1 - 1
src/core/URPC.pas

@@ -671,7 +671,7 @@ begin
             Fsock.sendstring(headers[n] + CRLF);
         end;
         if Fsock.lasterror = 0 then begin
-          FSock.SendBuffer(addr(jsonresponsetxt[Low(jsonresponsetxt)]),Length(jsonresponsetxt));
+          FSock.SendString(jsonresponsetxt);
         end;
       end;
       _RPCServer.AddRPCLog(FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort),'Method:'+methodName+' Params:'+paramsTxt+' '+Inttostr(errNum)+':'+errDesc+' Time:'+FormatFloat('0.000',(TPlatform.GetElapsedMilliseconds(tc)/1000)));

+ 8 - 0
src/core/UTxMultiOperation.pas

@@ -156,6 +156,8 @@ Type
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 implementation
@@ -821,6 +823,12 @@ begin
   Result := (IndexOfAccountSender(account)>=0) Or (IndexOfAccountChanger(account)>=0);
 end;
 
+function TOpMultiOperation.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var errors : String;
+begin
+  Result := CheckSignatures(ASafeBoxTransaction,errors);
+end;
+
 function TOpMultiOperation.DestinationAccount: Int64;
 begin
   Result:=inherited DestinationAccount;

+ 0 - 101
src/gui-classic/UFRMAccountInfo.dfm

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

+ 0 - 29
src/gui-classic/UFRMAccountInfo.pas

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

+ 5 - 4
src/gui-classic/UFRMNewPrivateKeyType.pas

@@ -29,7 +29,8 @@ uses
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ExtCtrls, UWallet,UCrypto;
+  Dialogs, StdCtrls, Buttons, ExtCtrls, UWallet, UCrypto,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 type
   TFRMNewPrivateKeyType = class(TForm)
@@ -78,18 +79,18 @@ begin
 end;
 
 procedure TFRMNewPrivateKeyType.FormCreate(Sender: TObject);
-Var l : TList;
+Var l : TList<Word>;
   i : Integer;
 begin
   FGeneratedPrivateKey := Nil;
   FWalletKeys := Nil;
   ebName.Text := DateTimeToStr(now);
   rgKeyType.Items.Clear;
-  l := TList.Create;
+  l := TList<Word>.Create;
   Try
     TAccountComp.ValidsEC_OpenSSL_NID(l);
     for i := 0 to l.Count - 1 do begin
-      rgKeyType.Items.AddObject(TAccountComp.GetECInfoTxt(PtrInt(l[i])),l[i]);
+      rgKeyType.Items.AddObject(TAccountComp.GetECInfoTxt(l[i]),TObject(l[i]));
     end;
   Finally
     l.free;

+ 291 - 183
src/gui-classic/UGridUtils.pas

@@ -30,7 +30,7 @@ uses
 {$ELSE}
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
-  Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams,
+  Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread,
   UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
@@ -83,6 +83,17 @@ Type
     Function SelectedAccounts(accounts : TOrderedCardinalList) : Integer;
   End;
 
+  TOperationsGrid = Class;
+
+  TOperationsGridUpdateThread = Class(TPCThread)
+    FOperationsGrid : TOperationsGrid;
+    procedure DoUpdateOperationsGrid(ANode : TNode; var AList : TList<TOperationResume>);
+  protected
+    procedure BCExecute; override;
+  public
+    constructor Create(AOperationsGrid : TOperationsGrid);
+  End;
+
   TOperationsGrid = Class(TComponent)
   private
     FDrawGrid: TDrawGrid;
@@ -93,6 +104,7 @@ Type
     FBlockStart: Int64;
     FBlockEnd: Int64;
     FMustShowAlwaysAnAccount: Boolean;
+    FOperationsGridUpdateThread : TOperationsGridUpdateThread;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
@@ -151,7 +163,19 @@ Type
     TimeAverage25 : Real;
     TimeAverage10 : Real;
   End;
-  TBlockChainDataArray = Array of TBlockChainData;
+
+  TBlockChainGrid = Class;
+
+  TBlockChainGridUpdateThread = Class(TPCThread)
+    FBlockChainGrid : TBlockChainGrid;
+    FBlockStart, FBlockEnd : Int64;
+    procedure DoUpdateBlockChainGrid(ANode : TNode; var AList : TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
+  protected
+    procedure BCExecute; override;
+  public
+    constructor Create(ABlockChainGrid : TBlockChainGrid);
+  End;
+
 
   { TBlockChainGrid }
 
@@ -159,7 +183,7 @@ Type
 
   TBlockChainGrid = Class(TComponent)
   private
-    FBlockChainDataArray : TBlockChainDataArray;
+    FBlockChainDataList : TList<TBlockChainData>;
     FBlockStart: Int64;
     FHashRateAs: TShowHashRateAs;
     FMaxBlocks: Integer;
@@ -168,6 +192,7 @@ Type
     FNodeNotifyEvents : TNodeNotifyEvents;
     FHashRateAverageBlocksCount: Integer;
     FShowTimeAverageColumns: Boolean;
+    FBlockChainGridUpdateThread : TBlockChainGridUpdateThread;
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
     procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
@@ -448,9 +473,7 @@ begin
     C.ColumnType := act_account_number;
     C.width := -1;
   end;
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   if (ARow=0) then begin
     // Header
     s := CT_ColumnHeader[C.ColumnType];
@@ -621,6 +644,127 @@ begin
   InitGrid;
 end;
 
+{ TOperationsGridUpdateThread }
+
+procedure TOperationsGridUpdateThread.BCExecute;
+var list : TList<TOperationResume>;
+  i : Integer;
+begin
+  list := TList<TOperationResume>.Create;
+  try
+    DoUpdateOperationsGrid(FOperationsGrid.Node,list);
+    if (Not Terminated) then begin
+      FOperationsGrid.FOperationsResume.Clear;
+      for i := 0 to list.Count-1 do begin
+        FOperationsGrid.FOperationsResume.Add(list[i]);
+      end;
+      FOperationsGrid.InitGrid;
+    end;
+  finally
+    list.Free;
+  end;
+end;
+
+constructor TOperationsGridUpdateThread.Create(AOperationsGrid: TOperationsGrid);
+begin
+  FOperationsGrid := AOperationsGrid;
+  inherited Create(True);
+  FreeOnTerminate := False;
+  Suspended := False;
+end;
+
+procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(ANode: TNode; var AList: TList<TOperationResume>);
+Var list : TList<Cardinal>;
+  i,j : Integer;
+  OPR : TOperationResume;
+  Op : TPCOperation;
+  opc : TPCOperationsComp;
+  bstart,bend : int64;
+  LOperationsResume : TOperationsResumeList;
+begin
+  if Not Assigned(ANode) then exit;
+  AList.Clear;
+  Try
+    if (FOperationsGrid.MustShowAlwaysAnAccount) And (FOperationsGrid.AccountNumber<0) then exit;
+
+    if FOperationsGrid.FPendingOperations then begin
+      for i := ANode.Operations.Count - 1 downto 0 do begin
+        Op := ANode.Operations.OperationsHashTree.GetOperation(i);
+        If TPCOperation.OperationToOperationResume(0,Op,True,Op.SignerAccount,OPR) then begin
+          OPR.NOpInsideBlock := i;
+          OPR.Block := ANode.Bank.BlocksCount;
+          OPR.Balance := ANode.Operations.SafeBoxTransaction.Account(Op.SignerAccount).balance;
+          AList.Add(OPR);
+        end;
+      end;
+    end else begin
+      if FOperationsGrid.AccountNumber<0 then begin
+        opc := TPCOperationsComp.Create(Nil);
+        try
+          opc.bank := ANode.Bank;
+          If FOperationsGrid.FBlockEnd<0 then begin
+            If ANode.Bank.BlocksCount>0 then bend := ANode.Bank.BlocksCount-1
+            else bend := 0;
+          end else bend := FOperationsGrid.FBlockEnd;
+          if FOperationsGrid.FBlockStart<0 then begin
+            if (bend > 300) then bstart := bend - 300
+            else bstart := 0;
+          end else bstart:= FOperationsGrid.FBlockStart;
+          If bstart<0 then bstart := 0;
+          if bend>=ANode.Bank.BlocksCount then bend:=ANode.Bank.BlocksCount;
+          while (bstart<=bend) and (Not Terminated) do begin
+            opr := CT_TOperationResume_NUL;
+            if (ANode.Bank.Storage.LoadBlockChainBlock(opc,bend)) then begin
+              // Reward operation
+              OPR := CT_TOperationResume_NUL;
+              OPR.valid := true;
+              OPR.Block := bend;
+              OPR.time := opc.OperationBlock.timestamp;
+              OPR.AffectedAccount := bend * CT_AccountsPerBlock;
+              OPR.Amount := opc.OperationBlock.reward;
+              OPR.Fee := opc.OperationBlock.fee;
+              OPR.Balance := OPR.Amount+OPR.Fee;
+              OPR.OperationTxt := 'Blockchain reward';
+              AList.Add(OPR);
+              // Reverse operations inside a block
+              for i := opc.Count - 1 downto 0 do begin
+                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],True,opc.Operation[i].SignerAccount,opr) then begin
+                  opr.NOpInsideBlock := i;
+                  opr.Block := bend;
+                  opr.time := opc.OperationBlock.timestamp;
+                  AList.Add(opr);
+                end;
+              end;
+            end else break;
+            dec(bend);
+          end;
+        finally
+          opc.Free;
+        end;
+
+      end else begin
+        list := TList<Cardinal>.Create;
+        Try
+          ANode.Operations.OperationsHashTree.GetOperationsAffectingAccount(FOperationsGrid.AccountNumber,list);
+          for i := list.Count - 1 downto 0 do begin
+            Op := ANode.Operations.OperationsHashTree.GetOperation((list[i]));
+            If TPCOperation.OperationToOperationResume(0,Op,False,FOperationsGrid.AccountNumber,OPR) then begin
+              OPR.NOpInsideBlock := i;
+              OPR.Block := ANode.Operations.OperationBlock.block;
+              OPR.Balance := ANode.Operations.SafeBoxTransaction.Account(FOperationsGrid.AccountNumber).balance;
+              AList.Add(OPR);
+            end;
+          end;
+        Finally
+          list.Free;
+        End;
+        ANode.GetStoredOperationsFromAccount(Self,AList,FOperationsGrid.AccountNumber,100,0,5000);
+      end;
+    end;
+  Finally
+  End;
+end;
+
 { TOperationsGrid }
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
@@ -635,11 +779,17 @@ begin
   FBlockStart := -1;
   FBlockEnd := -1;
   FPendingOperations := false;
+  FOperationsGridUpdateThread := Nil;
   inherited;
 end;
 
 destructor TOperationsGrid.Destroy;
 begin
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
   FOperationsResume.Free;
   FNodeNotifyEvents.Free;
   inherited;
@@ -689,9 +839,7 @@ procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; R
 Var s : String;
   opr : TOperationResume;
 begin
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   opr := CT_TOperationResume_NUL;
   Try
   if (ARow=0) then begin
@@ -855,6 +1003,11 @@ end;
 procedure TOperationsGrid.SetNode(const Value: TNode);
 begin
   if GetNode=Value then exit;
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
   FNodeNotifyEvents.Node := Value;
   UpdateAccountOperations; // New Build 1.0.3
 end;
@@ -897,95 +1050,126 @@ begin
 end;
 
 procedure TOperationsGrid.UpdateAccountOperations;
-Var list : TList<Cardinal>;
-  i,j : Integer;
-  OPR : TOperationResume;
-  Op : TPCOperation;
-  opc : TPCOperationsComp;
-  bstart,bend : int64;
 begin
-  FOperationsResume.Clear;
-  Try
-    if Not Assigned(Node) then exit;
-    if (MustShowAlwaysAnAccount) And (AccountNumber<0) then exit;
+  if Not Assigned(Node) then exit;
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
+  FOperationsGridUpdateThread := TOperationsGridUpdateThread.Create(Self);
+end;
 
-    if FPendingOperations then begin
-      for i := Node.Operations.Count - 1 downto 0 do begin
-        Op := Node.Operations.OperationsHashTree.GetOperation(i);
-        If TPCOperation.OperationToOperationResume(0,Op,True,Op.SignerAccount,OPR) then begin
-          OPR.NOpInsideBlock := i;
-          OPR.Block := Node.Bank.BlocksCount;
-          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SignerAccount).balance;
-          FOperationsResume.Add(OPR);
-        end;
+{ TBlockChainGridUpdateThread }
+
+procedure TBlockChainGridUpdateThread.BCExecute;
+var Llist : TList<TBlockChainData>;
+  i : Integer;
+  LBlockStart, LBlockEnd : Integer;
+begin
+  if (Not Assigned(FBlockChainGrid.Node)) Or (Terminated) then Exit;
+
+  if (FBlockChainGrid.FBlockStart>FBlockChainGrid.FBlockEnd) And (FBlockChainGrid.FBlockStart>=0) then FBlockChainGrid.FBlockEnd := -1;
+  if (FBlockChainGrid.FBlockEnd>=0) And (FBlockChainGrid.FBlockEnd<FBlockChainGrid.FBlockStart) then FBlockChainGrid.FBlockStart:=-1;
+
+  if FBlockChainGrid.FBlockStart>(FBlockChainGrid.FNodeNotifyEvents.Node.Bank.BlocksCount-1) then FBlockChainGrid.FBlockStart := -1;
+  if (FBlockChainGrid.FBlockEnd>=0) And (FBlockChainGrid.FBlockEnd<FBlockChainGrid.Node.Bank.BlocksCount) then begin
+    LBlockEnd := FBlockChainGrid.FBlockEnd
+  end else begin
+    if (FBlockChainGrid.FBlockStart>=0) And (FBlockChainGrid.FBlockStart+FBlockChainGrid.MaxBlocks<=FBlockChainGrid.Node.Bank.BlocksCount) then LBlockEnd := FBlockChainGrid.FBlockStart + FBlockChainGrid.MaxBlocks - 1
+    else LBlockEnd := FBlockChainGrid.Node.Bank.BlocksCount-1;
+  end;
+
+  if (FBlockChainGrid.FBlockStart>=0) And (FBlockChainGrid.FBlockStart<FBlockChainGrid.Node.Bank.BlocksCount) then LBlockStart := FBlockChainGrid.FBlockStart
+  else begin
+    if LBlockEnd>FBlockChainGrid.MaxBlocks then LBlockStart := LBlockEnd - FBlockChainGrid.MaxBlocks + 1
+    else LBlockStart := 0;
+  end;
+
+
+  Llist := TList<TBlockChainData>.Create;
+  try
+    DoUpdateBlockChainGrid(FBlockChainGrid.Node,Llist,LBlockStart,LBlockEnd);
+    if (Not Terminated) then begin
+      FBlockChainGrid.FBlockChainDataList.clear;
+      for i := 0 to Llist.Count-1 do begin
+        FBlockChainGrid.FBlockChainDataList.Add(Llist[i]);
       end;
-    end else begin
-      if AccountNumber<0 then begin
-        opc := TPCOperationsComp.Create(Nil);
-        try
-          opc.bank := Node.Bank;
-          If FBlockEnd<0 then begin
-            If Node.Bank.BlocksCount>0 then bend := Node.Bank.BlocksCount-1
-            else bend := 0;
-          end else bend := FBlockEnd;
-          if FBlockStart<0 then begin
-            if (bend > 300) then bstart := bend - 300
-            else bstart := 0;
-          end else bstart:= FBlockStart;
-          If bstart<0 then bstart := 0;
-          if bend>=Node.Bank.BlocksCount then bend:=Node.Bank.BlocksCount;
-          while (bstart<=bend) do begin
-            opr := CT_TOperationResume_NUL;
-            if (Node.Bank.Storage.LoadBlockChainBlock(opc,bend)) then begin
-              // Reward operation
-              OPR := CT_TOperationResume_NUL;
-              OPR.valid := true;
-              OPR.Block := bend;
-              OPR.time := opc.OperationBlock.timestamp;
-              OPR.AffectedAccount := bend * CT_AccountsPerBlock;
-              OPR.Amount := opc.OperationBlock.reward;
-              OPR.Fee := opc.OperationBlock.fee;
-              OPR.Balance := OPR.Amount+OPR.Fee;
-              OPR.OperationTxt := 'Blockchain reward';
-              FOperationsResume.Add(OPR);
-              // Reverse operations inside a block
-              for i := opc.Count - 1 downto 0 do begin
-                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],True,opc.Operation[i].SignerAccount,opr) then begin
-                  opr.NOpInsideBlock := i;
-                  opr.Block := bend;
-                  opr.time := opc.OperationBlock.timestamp;
-                  FOperationsResume.Add(opr);
-                end;
-              end;
-            end else break;
-            dec(bend);
-          end;
-        finally
-          opc.Free;
-        end;
+      if Assigned(FBlockChainGrid.DrawGrid) then begin
+        if Llist.Count>0 then FBlockChainGrid.DrawGrid.RowCount := Llist.Count+1
+        else FBlockChainGrid.DrawGrid.RowCount := 2;
+        FBlockChainGrid.FDrawGrid.Invalidate;
+      end;
+    end;
+  finally
+    Llist.Free;
+  end;
+end;
 
-      end else begin
-        list := TList<Cardinal>.Create;
-        Try
-          Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
-          for i := list.Count - 1 downto 0 do begin
-            Op := Node.Operations.OperationsHashTree.GetOperation((list[i]));
-            If TPCOperation.OperationToOperationResume(0,Op,False,AccountNumber,OPR) then begin
-              OPR.NOpInsideBlock := i;
-              OPR.Block := Node.Operations.OperationBlock.block;
-              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
-              FOperationsResume.Add(OPR);
-            end;
-          end;
-        Finally
-          list.Free;
-        End;
-        Node.GetStoredOperationsFromAccount(FOperationsResume,AccountNumber,100,0,5000);
+constructor TBlockChainGridUpdateThread.Create(ABlockChainGrid : TBlockChainGrid);
+begin
+  FBlockChainGrid := ABlockChainGrid;
+  inherited Create(True);
+  FreeOnTerminate := False;
+  Suspended := False;
+end;
+
+procedure TBlockChainGridUpdateThread.DoUpdateBlockChainGrid(ANode: TNode; var AList: TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
+Var opc : TPCOperationsComp;
+  bcd : TBlockChainData;
+  opb : TOperationBlock;
+  bn : TBigNum;
+begin
+  opc := TPCOperationsComp.Create(Nil);
+  try
+    opc.bank := ANode.Bank;
+    while (ABlockStart<=ABlockEnd) and (Not Terminated) do begin
+      bcd := CT_TBlockChainData_NUL;
+      opb := ANode.Bank.SafeBox.Block(ABlockEnd).blockchainInfo;
+      bcd.Block:=opb.block;
+      bcd.Timestamp := opb.timestamp;
+      bcd.BlockProtocolVersion := opb.protocol_version;
+      bcd.BlockProtocolAvailable := opb.protocol_available;
+      bcd.Reward := opb.reward;
+      bcd.Fee := opb.fee;
+      bcd.Target := opb.compact_target;
+      bn := ANode.Bank.SafeBox.CalcBlockHashRateInHs(bcd.Block,FBlockChainGrid.HashRateAverageBlocksCount);
+      try
+        bcd.HashRateHs := bn.Value;
+        bcd.HashRateKhs := bn.Divide(1000).Value;
+      finally
+        bn.Free;
+      end;
+      bn := TBigNum.TargetToHashRate(opb.compact_target);
+      Try
+        bcd.HashRateTargetHs := bn.Value / (CT_NewLineSecondsAvg);
+        bcd.HashRateTargetKhs := bn.Divide(1000).Divide(CT_NewLineSecondsAvg).Value;
+      finally
+        bn.Free;
+      end;
+      bcd.MinerPayload := opb.block_payload;
+      bcd.PoW := opb.proof_of_work;
+      bcd.SafeBoxHash := opb.initial_safe_box_hash;
+      bcd.AccumulatedWork := ANode.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
+      if (Not Terminated) then begin
+        If (ANode.Bank.LoadOperations(opc,ABlockEnd)) then begin
+          bcd.OperationsCount := opc.Count;
+          bcd.Volume := opc.OperationsHashTree.TotalAmount + opc.OperationsHashTree.TotalFee;
+        end;
+        bcd.TimeAverage200:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,200);
+        bcd.TimeAverage150:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,150);
+        bcd.TimeAverage100:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,100);
+        bcd.TimeAverage75:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,75);
+        bcd.TimeAverage50:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,50);
+        bcd.TimeAverage25:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,25);
+        bcd.TimeAverage10:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,10);
+        AList.Add(bcd);
+        if (ABlockEnd>0) then dec(ABlockEnd) else Break;
       end;
     end;
-  Finally
-    InitGrid;
-  End;
+  finally
+    opc.Free;
+  end;
 end;
 
 { TBlockChainGrid }
@@ -1000,16 +1184,23 @@ begin
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
   FHashRateAverageBlocksCount := 50;
-  SetLength(FBlockChainDataArray,0);
+  FBlockChainDataList := TList<TBlockChainData>.Create;
   FShowTimeAverageColumns:=False;
   FHashRateAs:={$IFDEF PRODUCTION}hr_Giga{$ELSE}hr_Mega{$ENDIF};
+  FBlockChainGridUpdateThread := Nil;
 end;
 
 destructor TBlockChainGrid.Destroy;
 begin
+  If Assigned(FBlockChainGridUpdateThread) then begin
+    FBlockChainGridUpdateThread.Terminate;
+    FBlockChainGridUpdateThread.WaitFor;
+    FreeAndNil(FBlockChainGridUpdateThread);
+  end;
   FNodeNotifyEvents.OnBlocksChanged := Nil;
   FNodeNotifyEvents.Node := Nil;
   FreeAndNil(FNodeNotifyEvents);
+  FreeAndNil(FBlockChainDataList);
   inherited;
 end;
 
@@ -1072,9 +1263,7 @@ Var s : String;
   deviation : Real;
   hr_base : Int64;
 begin
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   if (ARow=0) then begin
     // Header
     case ACol of
@@ -1113,8 +1302,8 @@ begin
     else DrawGrid.Canvas.Brush.Color := clWindow;
     DrawGrid.Canvas.FillRect(Rect);
     InflateRect(Rect,-2,-1);
-    if ((ARow-1)<=High(FBlockChainDataArray)) then begin
-      bcd := FBlockChainDataArray[ARow-1];
+    if ((ARow-1)<FBlockChainDataList.Count) then begin
+      bcd := FBlockChainDataList[ARow-1];
       if ACol=0 then begin
         s := IntToStr(bcd.Block);
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter]);
@@ -1133,7 +1322,7 @@ begin
       end else if ACol=3 then begin
         if bcd.Volume>=0 then begin
           s := TAccountComp.FormatMoney(bcd.Volume);
-          if FBlockChainDataArray[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
+          if FBlockChainDataList[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
           else DrawGrid.Canvas.Font.Color := clGrayText;
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         end else begin
@@ -1143,7 +1332,7 @@ begin
         end;
       end else if ACol=4 then begin
         s := TAccountComp.FormatMoney(bcd.Reward);
-        if FBlockChainDataArray[ARow-1].Reward>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        if FBlockChainDataList[ARow-1].Reward>0 then DrawGrid.Canvas.Font.Color := ClGreen
         else DrawGrid.Canvas.Font.Color := clGrayText;
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
       end else if ACol=5 then begin
@@ -1285,94 +1474,13 @@ end;
 
 
 procedure TBlockChainGrid.UpdateBlockChainGrid;
-Var nstart,nend : Cardinal;
-  opc : TPCOperationsComp;
-  bcd : TBlockChainData;
-  i : Integer;
-  opb : TOperationBlock;
-  bn : TBigNum;
 begin
-  if (FBlockStart>FBlockEnd) And (FBlockStart>=0) then FBlockEnd := -1;
-  if (FBlockEnd>=0) And (FBlockEnd<FBlockStart) then FBlockStart:=-1;
-
-  if Not Assigned(FNodeNotifyEvents.Node) then exit;
-
-  if FBlockStart>(FNodeNotifyEvents.Node.Bank.BlocksCount-1) then FBlockStart := -1;
-
-  try
-    if Node.Bank.BlocksCount<=0 then begin
-      SetLength(FBlockChainDataArray,0);
-      exit;
-    end;
-    if (FBlockEnd>=0) And (FBlockEnd<Node.Bank.BlocksCount) then begin
-      nend := FBlockEnd
-    end else begin
-      if (FBlockStart>=0) And (FBlockStart+MaxBlocks<=Node.Bank.BlocksCount) then nend := FBlockStart + MaxBlocks - 1
-      else nend := Node.Bank.BlocksCount-1;
-    end;
-
-    if (FBlockStart>=0) And (FBlockStart<Node.Bank.BlocksCount) then nstart := FBlockStart
-    else begin
-      if nend>MaxBlocks then nstart := nend - MaxBlocks + 1
-      else nstart := 0;
-    end;
-    SetLength(FBlockChainDataArray,nend - nstart +1);
-    opc := TPCOperationsComp.Create(Nil);
-    try
-      opc.bank := Node.Bank;
-      while (nstart<=nend) do begin
-        i := length(FBlockChainDataArray) - (nend-nstart+1);
-        bcd := CT_TBlockChainData_NUL;
-        opb := Node.Bank.SafeBox.Block(nend).blockchainInfo;
-        bcd.Block:=opb.block;
-        bcd.Timestamp := opb.timestamp;
-        bcd.BlockProtocolVersion := opb.protocol_version;
-        bcd.BlockProtocolAvailable := opb.protocol_available;
-        bcd.Reward := opb.reward;
-        bcd.Fee := opb.fee;
-        bcd.Target := opb.compact_target;
-        bn := Node.Bank.SafeBox.CalcBlockHashRateInHs(bcd.Block,HashRateAverageBlocksCount);
-        try
-          bcd.HashRateHs := bn.Value;
-          bcd.HashRateKhs := bn.Divide(1000).Value;
-        finally
-          bn.Free;
-        end;
-        bn := TBigNum.TargetToHashRate(opb.compact_target);
-        Try
-          bcd.HashRateTargetHs := bn.Value / (CT_NewLineSecondsAvg);
-          bcd.HashRateTargetKhs := bn.Divide(1000).Divide(CT_NewLineSecondsAvg).Value;
-        finally
-          bn.Free;
-        end;
-        bcd.MinerPayload := opb.block_payload;
-        bcd.PoW := opb.proof_of_work;
-        bcd.SafeBoxHash := opb.initial_safe_box_hash;
-        bcd.AccumulatedWork := Node.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
-        if (Node.Bank.LoadOperations(opc,nend)) then begin
-          bcd.OperationsCount := opc.Count;
-          bcd.Volume := opc.OperationsHashTree.TotalAmount + opc.OperationsHashTree.TotalFee;
-        end;
-        bcd.TimeAverage200:=Node.Bank.GetTargetSecondsAverage(bcd.Block,200);
-        bcd.TimeAverage150:=Node.Bank.GetTargetSecondsAverage(bcd.Block,150);
-        bcd.TimeAverage100:=Node.Bank.GetTargetSecondsAverage(bcd.Block,100);
-        bcd.TimeAverage75:=Node.Bank.GetTargetSecondsAverage(bcd.Block,75);
-        bcd.TimeAverage50:=Node.Bank.GetTargetSecondsAverage(bcd.Block,50);
-        bcd.TimeAverage25:=Node.Bank.GetTargetSecondsAverage(bcd.Block,25);
-        bcd.TimeAverage10:=Node.Bank.GetTargetSecondsAverage(bcd.Block,10);
-        FBlockChainDataArray[i] := bcd;
-        if (nend>0) then dec(nend) else break;
-      end;
-    finally
-      opc.Free;
-    end;
-  finally
-    if Assigned(FDrawGrid) then begin
-      if Length(FBlockChainDataArray)>0 then FDrawGrid.RowCount := length(FBlockChainDataArray)+1
-      else FDrawGrid.RowCount := 2;
-      FDrawGrid.Invalidate;
-    end;
+  If Assigned(FBlockChainGridUpdateThread) then begin
+    FBlockChainGridUpdateThread.Terminate;
+    FBlockChainGridUpdateThread.WaitFor;
+    FreeAndNil(FBlockChainGridUpdateThread);
   end;
+  FBlockChainGridUpdateThread := TBlockChainGridUpdateThread.Create(Self);
 end;
 
 end.

+ 21 - 19
src/libraries/cryptolib4pascal/ClpBits.pas

@@ -259,20 +259,6 @@ begin
 {$ENDIF FPC}
 end;
 
-class function TBits.RotateLeft8(a_value: Byte; a_n: Int32): Byte;
-begin
-{$IFDEF DEBUG}
-  System.Assert(a_n >= 0);
-{$ENDIF DEBUG}
-{$IFDEF FPC}
-  Result := RolByte(a_value, a_n);
-{$ELSE}
-  a_n := a_n and 7;
-
-  Result := (a_value shl a_n) or (a_value shr (8 - a_n));
-{$ENDIF FPC}
-end;
-
 class function TBits.NegativeLeftShift32(Value: UInt32;
   ShiftBits: Int32): UInt32;
 begin
@@ -300,6 +286,19 @@ begin
   Result := Value shr (64 + ShiftBits);
 end;
 
+class function TBits.RotateLeft8(a_value: Byte; a_n: Int32): Byte;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RolByte(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 7;
+  Result := (a_value shl a_n) or (a_value shr (8 - a_n));
+{$ENDIF FPC}
+end;
+
 class function TBits.RotateLeft32(a_value: UInt32; a_n: Int32): UInt32;
 begin
 {$IFDEF DEBUG}
@@ -308,8 +307,9 @@ begin
 {$IFDEF FPC}
   Result := RolDWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 31;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shl a_n) or (a_value shr (32 - a_n));
 {$ENDIF FPC}
 end;
@@ -322,8 +322,9 @@ begin
 {$IFDEF FPC}
   Result := RolQWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 63;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shl a_n) or (a_value shr (64 - a_n));
 {$ENDIF FPC}
 end;
@@ -337,7 +338,6 @@ begin
   Result := RorByte(a_value, a_n);
 {$ELSE}
   a_n := a_n and 7;
-
   Result := (a_value shr a_n) or (a_value shl (8 - a_n));
 {$ENDIF FPC}
 end;
@@ -350,8 +350,9 @@ begin
 {$IFDEF FPC}
   Result := RorDWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 31;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shr a_n) or (a_value shl (32 - a_n));
 {$ENDIF FPC}
 end;
@@ -364,8 +365,9 @@ begin
 {$IFDEF FPC}
   Result := RorQWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 63;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shr a_n) or (a_value shl (64 - a_n));
 {$ENDIF FPC}
 end;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpIECC.pas

@@ -23,7 +23,7 @@ interface
 
 uses
   Generics.Collections,
-  ClpIPreCompCallback,
+  ClpIPreCompCallBack,
   ClpCryptoLibTypes,
   ClpIFiniteField,
   ClpIPreCompInfo,

+ 1 - 1
src/libraries/cryptolib4pascal/ClpPascalCoinIESEngine.pas

@@ -32,7 +32,7 @@ uses
   ClpParametersWithIV,
   ClpIKeyParser,
   ClpIEphemeralKeyPair,
-  ClpKDFParameters,
+  ClpKdfParameters,
   ClpIKdfParameters,
   ClpIIESWithCipherParameters,
   ClpIESEngine,

+ 11 - 5
src/libraries/cryptolib4pascal/CryptoLib.inc

@@ -120,6 +120,12 @@
 // XE3 and Below
 {$IF CompilerVersion <= 24.0}
 {$DEFINE DELPHIXE3_DOWN}
+{$IFEND}
+
+ // XE4 and Above
+{$IF CompilerVersion >= 25.0}
+{$DEFINE DELPHIXE4_UP}
+{$DEFINE SHIFT_OVERFLOW_BUG_FIXED}
 {$IFEND}
 
   // XE7 and Above
@@ -132,17 +138,17 @@
 {$DEFINE DELPHI10.2_TOKYO_UP}
 {$IFEND}
 
-  // 2010 and Above
-{$IFNDEF DELPHI2010_UP}
-{$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
-{$ENDIF}
-
   // 10.2 Tokyo and Above
 {$IFDEF DELPHI10.2_TOKYO_UP}
 {$WARN COMBINING_SIGNED_UNSIGNED OFF}
 {$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
 {$ENDIF}
 
+  // 10.2 Tokyo and Above
+{$IFNDEF DELPHI10.2_TOKYO_UP}
+{$MESSAGE ERROR 'This Library requires Delphi Tokyo or higher.'}
+{$ENDIF}
+
 
 {$ENDIF DELPHI}
 

+ 1 - 1
src/libraries/cryptolib4pascal/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018 Ugochukwu Mmaduekwe
+Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/libraries/cryptolib4pascal/README.md

@@ -58,7 +58,7 @@ secp112r2, sect163r2, secp128r2.
  
     FreePascal 3.0.4 and Above.
     
-    Delphi XE6 and Above. (might work in earlier versions though.)
+    Delphi Tokyo and Above. (might work in earlier versions though.)
 
 **Installing the Library.**
 

+ 6 - 10
src/libraries/hashlib4pascal/HashLib.inc

@@ -103,16 +103,17 @@
 // XE3 and Below
 {$IF CompilerVersion <= 24.0}
 {$DEFINE DELPHIXE3_DOWN}
+{$IFEND}
+
+ // XE4 and Above
+{$IF CompilerVersion >= 25.0}
+{$DEFINE DELPHIXE4_UP}
+{$DEFINE SHIFT_OVERFLOW_BUG_FIXED}
 {$IFEND}
 
   // XE7 and Above
 {$IF CompilerVersion >= 28.0}
 {$DEFINE DELPHIXE7_UP}
-{$IFEND}
-
-  // 10.2 Tokyo and Above
-{$IF CompilerVersion >= 32.0}
-{$DEFINE DELPHI10.2_TOKYO_UP}
 {$IFEND}
 
   // 2010 and Above
@@ -120,11 +121,6 @@
 {$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
 {$ENDIF}
 
-  // 10.2 Tokyo and Above
-{$IFDEF DELPHI10.2_TOKYO_UP}
-{$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
-{$ENDIF}
-
 
 {$ENDIF DELPHI}
 

+ 8 - 6
src/libraries/hashlib4pascal/HlpBits.pas

@@ -183,7 +183,6 @@ begin
   Result := RolByte(a_value, a_n);
 {$ELSE}
   a_n := a_n and 7;
-
   Result := (a_value shl a_n) or (a_value shr (8 - a_n));
 {$ENDIF FPC}
 end;
@@ -196,8 +195,9 @@ begin
 {$IFDEF FPC}
   Result := RolDWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 31;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shl a_n) or (a_value shr (32 - a_n));
 {$ENDIF FPC}
 end;
@@ -210,8 +210,9 @@ begin
 {$IFDEF FPC}
   Result := RolQWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 63;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shl a_n) or (a_value shr (64 - a_n));
 {$ENDIF FPC}
 end;
@@ -225,7 +226,6 @@ begin
   Result := RorByte(a_value, a_n);
 {$ELSE}
   a_n := a_n and 7;
-
   Result := (a_value shr a_n) or (a_value shl (8 - a_n));
 {$ENDIF FPC}
 end;
@@ -238,8 +238,9 @@ begin
 {$IFDEF FPC}
   Result := RorDWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 31;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shr a_n) or (a_value shl (32 - a_n));
 {$ENDIF FPC}
 end;
@@ -252,8 +253,9 @@ begin
 {$IFDEF FPC}
   Result := RorQWord(a_value, a_n);
 {$ELSE}
+{$IFNDEF SHIFT_OVERFLOW_BUG_FIXED}
   a_n := a_n and 63;
-
+{$ENDIF SHIFT_OVERFLOW_BUG_FIXED}
   Result := (a_value shr a_n) or (a_value shl (64 - a_n));
 {$ENDIF FPC}
 end;

+ 2 - 2
src/libraries/hashlib4pascal/HlpBlake2B.pas

@@ -1748,7 +1748,7 @@ begin
     begin
       System.Move(a_data[offset], F_buf[F_bufferFilled], bufferRemaining);
     end;
-    F_counter0 := F_counter0 + BlockSizeInBytes;
+    F_counter0 := F_counter0 + UInt64(BlockSizeInBytes);
     if (F_counter0 = 0) then
     begin
       System.Inc(F_counter1);
@@ -1761,7 +1761,7 @@ begin
 
   while (a_data_length > BlockSizeInBytes) do
   begin
-    F_counter0 := F_counter0 + BlockSizeInBytes;
+    F_counter0 := F_counter0 + UInt64(BlockSizeInBytes);
     if (F_counter0 = 0) then
     begin
       System.Inc(F_counter1);

+ 231 - 0
src/libraries/hashlib4pascal/HlpCRC32Fast.pas

@@ -0,0 +1,231 @@
+unit HlpCRC32Fast;
+
+{$I HashLib.inc}
+
+interface
+
+uses
+  HlpHashLibTypes,
+  HlpHash,
+  HlpIHash,
+  HlpIHashInfo,
+  HlpHashResult,
+  HlpIHashResult;
+
+type
+
+  TCRC32Fast = class(THash, IChecksum, IHash32, ITransformBlock)
+
+  strict protected
+  var
+    FCurrentCRC: UInt32;
+
+    procedure LocalCRCCompute(const ACRCTable: THashLibUInt32Array;
+      const AData: THashLibByteArray; AIndex, ALength: Int32);
+
+    class function Init_CRC_Table(APolynomial: UInt32)
+      : THashLibUInt32Array; static;
+
+  public
+
+    constructor Create();
+
+    procedure Initialize(); override;
+    function TransformFinal(): IHashResult; override;
+
+  end;
+
+  TCRC32_PKZIP = class sealed(TCRC32Fast)
+
+  strict private
+
+  const
+    CRC32_PKZIP_Polynomial = UInt32($EDB88320); // Polynomial Reversed
+    class var
+
+      FCRC32_PKZIP_Table: THashLibUInt32Array;
+
+    class constructor CRC32_PKZIP();
+
+  public
+    constructor Create();
+    procedure TransformBytes(const a_data: THashLibByteArray;
+      a_index, a_length: Int32); override;
+    function Clone(): IHash; override;
+
+  end;
+
+  TCRC32_CASTAGNOLI = class sealed(TCRC32Fast)
+
+  strict private
+
+  const
+    CRC32_CASTAGNOLI_Polynomial = UInt32($82F63B78); // Polynomial Reversed
+    class var
+
+      FCRC32_CASTAGNOLI_Table: THashLibUInt32Array;
+
+    class constructor CRC32_CASTAGNOLI();
+
+  public
+    constructor Create();
+    procedure TransformBytes(const a_data: THashLibByteArray;
+      a_index, a_length: Int32); override;
+    function Clone(): IHash; override;
+
+  end;
+
+implementation
+
+{ TCRC32Fast }
+
+class function TCRC32Fast.Init_CRC_Table(APolynomial: UInt32)
+  : THashLibUInt32Array;
+var
+  LIdx, LJIdx, LKIdx: Int32;
+  LRes: UInt32;
+begin
+  System.SetLength(Result, 16 * 256);
+  for LIdx := 0 to System.Pred(256) do
+  begin
+    LRes := LIdx;
+    for LJIdx := 0 to System.Pred(16) do
+    begin
+      LKIdx := 0;
+      while LKIdx < System.Pred(9) do
+      begin
+        if (LRes and 1) = 1 then
+        begin
+          LRes := APolynomial xor (LRes shr 1)
+        end
+        else
+        begin
+          LRes := LRes shr 1;
+        end;
+        Result[(LJIdx * 256) + LIdx] := LRes;
+        System.Inc(LKIdx);
+      end;
+    end;
+  end;
+end;
+
+procedure TCRC32Fast.LocalCRCCompute(const ACRCTable: THashLibUInt32Array;
+  const AData: THashLibByteArray; AIndex, ALength: Int32);
+var
+  LCRC, LA, LB, LC, LD: UInt32;
+  LCRCTable: THashLibUInt32Array;
+begin
+  LCRC := not FCurrentCRC;
+  LCRCTable := ACRCTable;
+  while ALength >= 16 do
+  begin
+
+    LA := LCRCTable[(3 * 256) + AData[AIndex + 12]] xor LCRCTable
+      [(2 * 256) + AData[AIndex + 13]] xor LCRCTable
+      [(1 * 256) + AData[AIndex + 14]] xor LCRCTable
+      [(0 * 256) + AData[AIndex + 15]];
+
+    LB := LCRCTable[(7 * 256) + AData[AIndex + 8]] xor LCRCTable
+      [(6 * 256) + AData[AIndex + 9]] xor LCRCTable
+      [(5 * 256) + AData[AIndex + 10]] xor LCRCTable
+      [(4 * 256) + AData[AIndex + 11]];
+
+    LC := LCRCTable[(11 * 256) + AData[AIndex + 4]] xor LCRCTable
+      [(10 * 256) + AData[AIndex + 5]] xor LCRCTable
+      [(9 * 256) + AData[AIndex + 6]] xor LCRCTable
+      [(8 * 256) + AData[AIndex + 7]];
+
+    LD := LCRCTable[(15 * 256) + (Byte(LCRC) xor AData[AIndex])] xor LCRCTable
+      [(14 * 256) + (Byte(LCRC shr 8) xor AData[AIndex + 1])] xor LCRCTable
+      [(13 * 256) + (Byte(LCRC shr 16) xor AData[AIndex + 2])] xor LCRCTable
+      [(12 * 256) + ((LCRC shr 24) xor AData[AIndex + 3])];
+
+    LCRC := LD xor LC xor LB xor LA;
+    System.Inc(AIndex, 16);
+    System.Dec(ALength, 16);
+  end;
+
+  System.Dec(ALength);
+  while (ALength >= 0) do
+  begin
+    LCRC := LCRCTable[Byte(LCRC xor AData[AIndex])] xor (LCRC shr 8);
+    System.Inc(AIndex);
+    System.Dec(ALength);
+  end;
+
+  FCurrentCRC := not LCRC;
+end;
+
+constructor TCRC32Fast.Create();
+begin
+  Inherited Create(4, 1);
+end;
+
+procedure TCRC32Fast.Initialize;
+begin
+  FCurrentCRC := 0;
+end;
+
+function TCRC32Fast.TransformFinal: IHashResult;
+begin
+  Result := THashResult.Create(FCurrentCRC);
+  Initialize();
+end;
+
+{ TCRC32_PKZIP }
+
+function TCRC32_PKZIP.Clone(): IHash;
+var
+  HashInstance: TCRC32_PKZIP;
+begin
+  HashInstance := TCRC32_PKZIP.Create();
+  HashInstance.FCurrentCRC := FCurrentCRC;
+  Result := HashInstance as IHash;
+  Result.BufferSize := BufferSize;
+end;
+
+constructor TCRC32_PKZIP.Create;
+begin
+  Inherited Create();
+end;
+
+procedure TCRC32_PKZIP.TransformBytes(const a_data: THashLibByteArray;
+  a_index, a_length: Int32);
+begin
+  LocalCRCCompute(FCRC32_PKZIP_Table, a_data, a_index, a_length);
+end;
+
+class constructor TCRC32_PKZIP.CRC32_PKZIP();
+begin
+  FCRC32_PKZIP_Table := Init_CRC_Table(CRC32_PKZIP_Polynomial);
+end;
+
+{ TCRC32_CASTAGNOLI }
+
+function TCRC32_CASTAGNOLI.Clone(): IHash;
+var
+  HashInstance: TCRC32_CASTAGNOLI;
+begin
+  HashInstance := TCRC32_CASTAGNOLI.Create();
+  HashInstance.FCurrentCRC := FCurrentCRC;
+  Result := HashInstance as IHash;
+  Result.BufferSize := BufferSize;
+end;
+
+constructor TCRC32_CASTAGNOLI.Create;
+begin
+  Inherited Create();
+end;
+
+procedure TCRC32_CASTAGNOLI.TransformBytes(const a_data: THashLibByteArray;
+  a_index, a_length: Int32);
+begin
+  LocalCRCCompute(FCRC32_CASTAGNOLI_Table, a_data, a_index, a_length);
+end;
+
+class constructor TCRC32_CASTAGNOLI.CRC32_CASTAGNOLI();
+begin
+  FCRC32_CASTAGNOLI_Table := Init_CRC_Table(CRC32_CASTAGNOLI_Polynomial);
+end;
+
+end.

+ 1 - 1
src/libraries/hashlib4pascal/HlpFNV64.pas

@@ -65,7 +65,7 @@ begin
   i := a_index;
   while a_length > 0 do
   begin
-    Fm_hash := UInt64(Fm_hash * 1099511628211) xor a_data[i];
+    Fm_hash := UInt64(Fm_hash * UInt64(1099511628211)) xor a_data[i];
     System.Inc(i);
     System.Dec(a_length);
   end;

+ 9 - 8
src/libraries/hashlib4pascal/HlpHashFactory.pas

@@ -17,6 +17,7 @@ uses
   HlpCRC,
   HlpCRC16,
   HlpCRC32,
+  HlpCRC32Fast,
   HlpCRC64,
   // Hash32 Units //
   HlpAP,
@@ -136,12 +137,12 @@ type
       class function CreateCRC16_BUYPASS(): IHash; static;
 
       /// <summary>
-      /// PKZIP, polynomial = $04C11DB7
+      /// PKZIP, polynomial = $04C11DB7, reversed = $EDB88320
       /// </summary>
       /// <returns></returns>
       class function CreateCRC32_PKZIP(): IHash; static;
       /// <summary>
-      /// Castagnoli, polynomial = $1EDC6F41
+      /// Castagnoli, polynomial = $1EDC6F41, reversed = $82F63B78
       /// </summary>
       /// <returns></returns>
       class function CreateCRC32_CASTAGNOLI(): IHash; static;
@@ -480,12 +481,12 @@ end;
 
 class function THashFactory.TChecksum.CreateCRC32_CASTAGNOLI: IHash;
 begin
-  Result := TCRC32_CASTAGNOLI.Create();
+  Result := HlpCRC32Fast.TCRC32_CASTAGNOLI.Create();
 end;
 
 class function THashFactory.TChecksum.CreateCRC32_PKZIP: IHash;
 begin
-  Result := TCRC32_PKZIP.Create();
+  Result := HlpCRC32Fast.TCRC32_PKZIP.Create();
 end;
 
 class function THashFactory.TChecksum.CreateCRC64(_poly, _Init: UInt64;
@@ -931,14 +932,14 @@ begin
   Result := TSHA3_512.Create();
 end;
 
-class function THashFactory.TCrypto.CreateShake_128
-  (a_xof_size_in_bits: Int32): IHash;
+class function THashFactory.TCrypto.CreateShake_128(a_xof_size_in_bits
+  : Int32): IHash;
 begin
   Result := (TShake_128.Create() as IXOF).SetXOFOutputSize(a_xof_size_in_bits);
 end;
 
-class function THashFactory.TCrypto.CreateShake_256
-  (a_xof_size_in_bits: Int32): IHash;
+class function THashFactory.TCrypto.CreateShake_256(a_xof_size_in_bits
+  : Int32): IHash;
 begin
   Result := (TShake_256.Create() as IXOF).SetXOFOutputSize(a_xof_size_in_bits);
 end;

+ 21 - 0
src/libraries/hashlib4pascal/LICENSE

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

+ 110 - 0
src/libraries/hashlib4pascal/README.md

@@ -0,0 +1,110 @@
+# HashLib4Pascal [![License](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Xor-el/HashLib4Pascal/blob/master/LICENSE)
+HashLib4Pascal is a Delphi/FreePascal compatible library that provides an easy to use interface for computing hashes and checksums of strings (with a specified encoding), files, streams, byte arrays and untyped data to mention but a few.
+It also supports Incremental Hashing.
+
+**Build Status**
+[![Build Status](https://travis-ci.org/Xor-el/HashLib4Pascal.svg?branch=master)](https://travis-ci.org/Xor-el/HashLib4Pascal)
+
+**Supported Algorithms:**
+
+    non-cryptographic 32-bits hash algorithms: AP, BKDR, Bernstein, Bernstein1, DEK, DJB, 
+    ELF, FNV, FNV1a, JS, Jenkins3, Murmur2, MurmurHash3_x86_32, OneAtTime, PJW, RS, 
+    Rotating, SDBM, ShiftAndXor, SuperFast, XXHash32.
+
+    non-cryptographic 64-bits algorithms: FNV, FNV1a, Murmur2_64, SipHash2_4, XXHash64.
+
+    non-cryptographic 128-bits algorithms: MurmurHash3_x86_128, MurmurHash3_x64_128. 
+
+    checksum algorithms: Adler32, All CRC Variants from CRC3 to CRC64. 
+
+    cryptographic algorithms: GOST, Grindahl, HAS160, Haval, MD2, MD4, MD5, Panama, 
+    RadioGatun, RIPEMD, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA0, SHA1, SHA2-224,
+    SHA2-256, SHA2-384, SHA2-512, SHA2-512-224, SHA2-512-256, SHA3-224, SHA3-256, SHA3-384, 
+    SHA3-512, Keccak-224, Keccak-256, Keccak-384, Keccak-512, Snefru128, Snefru256, Tiger, Tiger2, 
+    WhirlPool, Blake2B, Blake2S, Streebog (GOST3411_2012_256, GOST3411_2012_512), Shake_128, Shake_256.
+
+    HMAC for any of the above.
+    
+    PBKDF2_HMAC for any of the above.
+
+**Supported Compilers**
+ 
+    FreePascal 3.0.0 and Above.
+    
+    Delphi 2010 and Above.
+
+## Installing the Library. ##
+
+**Method One:**
+
+ - Use the Provided Packages in the "Packages" Folder.
+
+**Method Two:**
+
+- Add the Library Path and Sub Path to your Project Search Path.
+
+**Usage Examples.**
+
+- Coming Soon.
+- But in the mean time, you can poke around the sources and Unit Tests.
+
+ ## Unit Tests. ##
+
+To Run Unit Tests,
+
+**For FPC 3.0.0 and above**
+
+
+    Simply compile and run "HashLib.Tests" project in "FreePascal.Tests" Folder.
+
+**For Delphi 2010 and above**
+
+   **Method One (Using DUnit Test Runner)**
+
+     To Build and Run the Unit Tests For Delphi 10 Seattle (should be similar for 
+     other versions)
+    
+    1. Open Project Options of Unit Test (HashLib.Tests) in "Delphi.Tests" Folder.
+    
+    2. Change Target to All Configurations (Or "Base" In Older Delphi Versions.)
+    
+    3. In Output directory add ".\$(Platform)\$(Config)" without the quotes.
+    
+    4. In Search path add "$(BDS)\Source\DUnit\src" without the quotes.
+    
+    5. In Unit output directory add "." without the quotes.
+    
+    6. In Unit scope names (If Available), Delete "DUnitX" from the List.
+    
+    Press Ok and save, then build and run.
+    
+ **Method Two (Using TestInsight) (Preferred).**
+
+    1. Download and Install TestInsight.
+    
+    2. Open Project Options of Unit Test (HashLib.Tests.TestInsight) in "Delphi.Tests" 
+        Folder. 
+
+    3. Change Target to All Configurations (Or "Base" In Older Delphi Versions.)
+
+    4. In Unit scope names (If Available), Delete "DUnitX" from the List.
+
+    5. To Use TestInsight, right-click on the project, then select 
+    
+  ###### "Enable for TestInsight" or "TestInsight Project". ######
+        Save Project then Build and Run Test Project through TestInsight. 
+        
+  **Other Implementations**
+
+If you want implementations in other languages, you can check out these
+
+[HashLib4CPP By Mbadiwe Nnaemeka Ronald](https://github.com/ron4fun/HashLib4CPP)
+
+**License**
+
+This "Software" is Licensed Under  **`MIT License (MIT)`** .
+
+#### Tip Jar
+* :dollar: **Bitcoin**: `1MhFfW7tDuEHQSgie65uJcAfJgCNchGeKf`
+* :euro: **Ethereum**: `0x6c1DC21aeC49A822A4f1E3bf07c623C2C1978a98`
+* :pound: **Pascalcoin**: `345367-40`

+ 13 - 6
src/libraries/pascalcoin/UFolderHelper.pas

@@ -20,6 +20,8 @@ unit UFolderHelper;
   {$MODE Delphi}
 {$ENDIF}
 
+{$I config.inc}
+
 interface
 
 Type TFileVersionInfo = record
@@ -44,7 +46,7 @@ Type TFileVersionInfo = record
 
   TFolderHelper = record
   strict private
-    {$IFnDEF FPC}
+    {$IF (not Defined(FPC)) and (Defined(MSWINDOWS))}
     class function GetFolder(const aCSIDL: Integer): string; static;
     {$ENDIF}
     class function GetAppDataFolder : string; static;
@@ -57,20 +59,21 @@ implementation
 
 uses
 {$IFnDEF FPC}
+  {$IFDEF MSWINDOWS}
   Windows, ShlObj,
   {$DEFINE FILEVERSIONINFO}
+  {$ELSE}
+  System.IOUtils,
+  {$ENDIF}
 {$ELSE}
   {$IFDEF WIN}
   Windows,
   {$DEFINE FILEVERSIONINFO}
   {$ENDIF}
-  {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   SysUtils;
 
-{$I config.inc}
-
-{$IFnDEF FPC}
+{$IF (not Defined(FPC)) and (Defined(MSWINDOWS))}
 function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle;
   dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;
   forward;
@@ -86,11 +89,15 @@ begin
   Result :=GetEnvironmentVariable('HOME');
   {$ENDIF}
   {$ELSE}
+  {$IFDEF MSWINDOWS}
   Result := GetFolder(CSIDL_APPDATA); // c:\Users\(User Name)\AppData\Roaming
+  {$ELSE}
+  Result := TPath.GetDocumentsPath;
+  {$ENDIF}
   {$ENDIF}
 end;
 
-{$IFnDEF FPC}
+{$IF (not Defined(FPC)) and (Defined(MSWINDOWS))}
 class function TFolderHelper.GetFolder(const aCSIDL: Integer): string;
 var
   FolderPath: array[0 .. MAX_PATH] of Char;

+ 1 - 1
src/libraries/simplebaselib4pascal/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018 Ugochukwu Mmaduekwe
+Copyright (c) 2018 - 2019 Ugochukwu Mmaduekwe
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 7 - 7
src/libraries/sphere10/UCommon.pas

@@ -43,8 +43,8 @@ const
 { GLOBAL HELPER FUNCTIONS }
 
 function String2Hex(const Buffer: String): String;
-function Hex2Bytes(const AHexString: String): TBytes; overload;
-function TryHex2Bytes(const AHexString: String; out ABytes : TBytes): boolean; overload;
+//function Hex2Bytes(const AHexString: String): TBytes; overload;
+//function TryHex2Bytes(const AHexString: String; out ABytes : TBytes): boolean; overload;
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : String;
 function BinStrComp(const Str1, Str2 : String): Integer; // Binary-safe StrComp replacement. StrComp will return 0 for when str1 and str2 both start with NUL character.
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
@@ -442,13 +442,13 @@ begin
     Result := AnsiLowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
 end;
 
-function Hex2Bytes(const AHexString: String): TBytes;
+{function Hex2Bytes(const AHexString: String): TBytes;
 begin
   if NOT TryHex2Bytes(AHexString, Result) then
     raise EArgumentOutOfRangeException.Create('Invalidly formatted hexadecimal string.');
 end;
-
-function TryHex2Bytes(const AHexString: String; out ABytes : TBytes): boolean; overload;
+}
+{function TryHex2Bytes(const AHexString: String; out ABytes : TBytes): boolean; overload;
 var
   P : PAnsiChar;
   LHexString : String;
@@ -457,7 +457,7 @@ begin
   SetLength(ABytes, 0);
   LHexLength := System.Length(AHexString);
   LHexStart := 1;
-  if {$IFDEF FPC}AnsiStartsText{$ELSE}StartsText{$ENDIF}('0x', AHexString) then begin
+  if {$IFDEF FPCAnsiStartsText{$ELSEStartsText{$ENDIF('0x', AHexString) then begin
 
     // Special case: 0x0 = empty byte array
     if (LHexLength = 3) AND (AHexString[3] = '0') then
@@ -477,7 +477,7 @@ begin
   LHexString := LowerCase(AHexString);
   LHexIndex := HexToBin(PAnsiChar(@LHexString[LHexStart]), P, System.Length(ABytes));
   Result := (LHexIndex = (LHexLength DIV 2));
-end;
+end;}
 
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : String;
 var

+ 0 - 1
src/pascalcoin_wallet_classic.dpr

@@ -41,7 +41,6 @@ uses
   UTxMultiOperation in 'core\UTxMultiOperation.pas',
   UWallet in 'core\UWallet.pas',
   UFRMAbout in 'gui-classic\UFRMAbout.pas' {FRMAbout},
-  UFRMAccountInfo in 'gui-classic\UFRMAccountInfo.pas' {FRMAccountInfo},
   UFRMAccountSelect in 'gui-classic\UFRMAccountSelect.pas' {FRMAccountSelect},
   UFRMMemoText in 'gui-classic\UFRMMemoText.pas' {FRMMemoText},
   UFRMNewPrivateKeyType in 'gui-classic\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},