Browse Source

Merge pull request #62 from PascalCoinDev/master

Fixed compilers errors by adding new improvements
Albert Molina 3 years ago
parent
commit
82d535b97f

+ 353 - 105
src/core/UBlockChain.pas

@@ -28,7 +28,7 @@ uses
   Classes,{$IFnDEF FPC}Windows,{$ENDIF}UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   Classes,{$IFnDEF FPC}Windows,{$ENDIF}UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
   {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
-  UPCDataTypes, UChunk;
+  UPCDataTypes, UChunk, UOrderedList;
 
 
 {
 {
 
 
@@ -113,6 +113,7 @@ uses
 }
 }
 
 
 Type
 Type
+  TSearchOpHashResult = (OpHash_found, OpHash_invalid_params, OpHash_block_not_found);
   // Moved from UOpTransaction to here
   // Moved from UOpTransaction to here
   TOpChangeAccountInfoType = (public_key, account_name, account_type, list_for_public_sale, list_for_private_sale, delist, account_data, list_for_account_swap, list_for_coin_swap );
   TOpChangeAccountInfoType = (public_key, account_name, account_type, list_for_public_sale, list_for_private_sale, delist, account_data, list_for_account_swap, list_for_coin_swap );
   TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
   TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
@@ -204,19 +205,8 @@ Type
   TPCOperation = Class;
   TPCOperation = Class;
   TPCOperationClass = Class of TPCOperation;
   TPCOperationClass = Class of TPCOperation;
 
 
-  TOperationsResumeList = Class
-  private
-    FList : TPCThreadList<Pointer>;
-    function GetOperationResume(index: Integer): TOperationResume;
-  public
-    Constructor Create;
-    Destructor Destroy; override;
-    Procedure Add(Const OperationResume : TOperationResume);
-    Function Count : Integer;
-    Procedure Delete(index : Integer);
-    Procedure Clear;
-    Property OperationResume[index : Integer] : TOperationResume read GetOperationResume; default;
-  End;
+
+  TOperationsResumeList = TList<TOperationResume>;
 
 
   TOpReference = UInt64;
   TOpReference = UInt64;
   TOpReferenceArray = Array of TopReference;
   TOpReferenceArray = Array of TopReference;
@@ -244,7 +234,7 @@ Type
     property ProtocolVersion : Word read FProtocolVersion;
     property ProtocolVersion : Word read FProtocolVersion;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; virtual;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; virtual;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors: String): Boolean; virtual; abstract;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors: String): Boolean; virtual; abstract;
-    procedure AffectedAccounts(list : TList<Cardinal>); virtual; abstract;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     Function GetDigestToSign : TRawBytes; virtual; abstract;
     Function GetDigestToSign : TRawBytes; virtual; abstract;
@@ -489,9 +479,12 @@ Type
   private
   private
     FBank : TPCBank;
     FBank : TPCBank;
     FReadOnly: Boolean;
     FReadOnly: Boolean;
+    FPendingBufferOperationsStream : TFileStream;
     procedure SetBank(const Value: TPCBank);
     procedure SetBank(const Value: TPCBank);
+    Function GetPendingBufferOperationsStream : TFileStream;
   protected
   protected
     FIsMovingBlockchain : Boolean;
     FIsMovingBlockchain : Boolean;
+    FStorageFilename: String;
     procedure SetReadOnly(const Value: Boolean); virtual;
     procedure SetReadOnly(const Value: Boolean); virtual;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
@@ -502,15 +495,19 @@ Type
     function GetLastBlockNumber: Int64; virtual; abstract;
     function GetLastBlockNumber: Int64; virtual; abstract;
     function DoInitialize:Boolean; virtual; abstract;
     function DoInitialize:Boolean; virtual; abstract;
     Procedure DoEraseStorage; virtual; abstract;
     Procedure DoEraseStorage; virtual; abstract;
-    Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
-    Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
+    Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual;
+    Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual;
     Function DoGetBlockInformation(const ABlock : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64) : Boolean; virtual;
     Function DoGetBlockInformation(const ABlock : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64) : Boolean; virtual;
+    Function DoGetBlockOperations(ABlock, AOpBlockStartIndex, AMaxOperations : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64; const AOperationsResumeList:TOperationsResumeList) : Boolean; virtual;
+    Function DoGetAccountOperations(AAccount : Integer; AMaxDepth, AStartOperation, AMaxOperations, ASearchBackwardsStartingAtBlock: Integer; const AOperationsResumeList:TOperationsResumeList): Boolean; virtual;
+    function DoFindOperation(const AOpHash : TBytes; var AOperationResume : TOperationResume) : TSearchOpHashResult; virtual;
   public
   public
     Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
     Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Constructor Create(AOwner : TComponent); Override;
     Constructor Create(AOwner : TComponent); Override;
+    Destructor Destroy; override;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     Property Bank : TPCBank read FBank write SetBank;
     Property Bank : TPCBank read FBank write SetBank;
     Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
     Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
@@ -523,7 +520,11 @@ Type
     Function BlockExists(Block : Cardinal) : Boolean;
     Function BlockExists(Block : Cardinal) : Boolean;
 
 
     function Orphan : String;
     function Orphan : String;
-    Function GetBlockInformation(const ABlock : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64) : Boolean;
+    Function GetBlockInformation(ABlock : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64) : Boolean;
+    Function GetBlockOperations(ABlock, AOpBlockStartIndex, AMaxOperations : Integer; var AOperationBlock : TOperationBlock; var AOperationsCount : Integer; var AVolume : Int64; const AOperationsResumeList:TOperationsResumeList) : Boolean;
+    Function GetAccountOperations(AAccount : Integer; AMaxDepth, AStartOperation, AMaxOperations, ASearchBackwardsStartingAtBlock: Integer; const AOperationsResumeList:TOperationsResumeList): Boolean;
+    function FindOperation(const AOpHash : TBytes; var AOperationResume : TOperationResume) : TSearchOpHashResult;
+    property StorageFilename : String read FStorageFilename write FStorageFilename;
   End;
   End;
 
 
   TStorageClass = Class of TStorage;
   TStorageClass = Class of TStorage;
@@ -1063,6 +1064,25 @@ begin
   end;
   end;
 end;
 end;
 
 
+Procedure DoCopyFile(sourcefn,destfn : AnsiString);
+var sourceFS, destFS : TFileStream;
+Begin
+  if Not FileExists(sourcefn) then Raise Exception.Create('Source file not found: '+sourcefn);
+  sourceFS := TFileStream.Create(sourcefn,fmOpenRead+fmShareDenyNone);
+  try
+    sourceFS.Position:=0;
+    destFS := TFileStream.Create(destfn,fmCreate+fmShareDenyWrite);
+    try
+      destFS.Size:=0;
+      destFS.CopyFrom(sourceFS,sourceFS.Size);
+    finally
+      destFS.Free;
+    end;
+  finally
+    sourceFS.Free;
+  end;
+end;
+
 function TPCBank.DoSaveBank: Boolean;
 function TPCBank.DoSaveBank: Boolean;
 var fs: TFileStream;
 var fs: TFileStream;
     LBankfilename,Laux_newfilename: AnsiString;
     LBankfilename,Laux_newfilename: AnsiString;
@@ -1102,7 +1122,7 @@ begin
       Laux_newfilename := GetStorageFolder('') + PathDelim+'checkpoint_'+ inttostr(BlocksCount)+CT_Safebox_Extension;
       Laux_newfilename := GetStorageFolder('') + PathDelim+'checkpoint_'+ inttostr(BlocksCount)+CT_Safebox_Extension;
       try
       try
         {$IFDEF FPC}
         {$IFDEF FPC}
-        DoCopyFile(bankfilename,aux_newfilename);
+        DoCopyFile(LBankfilename,Laux_newfilename);
         {$ELSE}
         {$ELSE}
         CopyFile(PWideChar(LBankfilename),PWideChar(Laux_newfilename),False);
         CopyFile(PWideChar(LBankfilename),PWideChar(Laux_newfilename),False);
         {$ENDIF}
         {$ENDIF}
@@ -2764,13 +2784,13 @@ end;
 function TOperationsHashTree.GetOperationsAffectingAccount(account_number: Cardinal; List: TList<Cardinal>): Integer;
 function TOperationsHashTree.GetOperationsAffectingAccount(account_number: Cardinal; List: TList<Cardinal>): Integer;
   // This function retrieves operations from HashTree that affeccts to an account_number
   // This function retrieves operations from HashTree that affeccts to an account_number
 Var l : TList<Pointer>;
 Var l : TList<Pointer>;
-  intl : TList<Cardinal>;
+  intl : TOrderedList<Cardinal>;
   i,j : Integer;
   i,j : Integer;
 begin
 begin
   List.Clear;
   List.Clear;
   l := FHashTreeOperations.LockList;
   l := FHashTreeOperations.LockList;
   try
   try
-    intl := TList<Cardinal>.Create;
+    intl := TOrderedList<Cardinal>.Create(False,TComparison_Cardinal);
     try
     try
       for i := 0 to l.Count - 1 do begin
       for i := 0 to l.Count - 1 do begin
         intl.Clear;
         intl.Clear;
@@ -3246,6 +3266,7 @@ end;
 
 
 procedure TStorage.CopyConfiguration(const CopyFrom: TStorage);
 procedure TStorage.CopyConfiguration(const CopyFrom: TStorage);
 begin
 begin
+  ReadOnly := CopyFrom.ReadOnly;
 end;
 end;
 
 
 constructor TStorage.Create(AOwner: TComponent);
 constructor TStorage.Create(AOwner: TComponent);
@@ -3253,6 +3274,8 @@ begin
   inherited;
   inherited;
   FReadOnly := false;
   FReadOnly := false;
   FIsMovingBlockchain := False;
   FIsMovingBlockchain := False;
+  FPendingBufferOperationsStream := Nil;
+  FStorageFilename := '';
 end;
 end;
 
 
 procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
 procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
@@ -3261,6 +3284,198 @@ begin
   DoDeleteBlockChainBlocks(StartingDeleteBlock);
   DoDeleteBlockChainBlocks(StartingDeleteBlock);
 end;
 end;
 
 
+destructor TStorage.Destroy;
+begin
+  FreeAndNil(FPendingBufferOperationsStream);
+  inherited;
+end;
+
+function TStorage.DoFindOperation(const AOpHash: TBytes; var AOperationResume: TOperationResume): TSearchOpHashResult;
+var LBlock, LAccount, LN_Operation : Cardinal;
+  LMD160,LOpHashValid,LOpHashOld : TBytes;
+  i,LPreviousBlock, LAux_n_op,LInitialBlock : Integer;
+  LOperationsComp : TPCOperationsComp;
+  LOp : TPCOperation;
+begin
+  Result := OpHash_invalid_params;
+  If not TPCOperation.DecodeOperationHash(AOpHash,LBlock,LAccount,LN_Operation,LMD160) then exit;
+  LInitialBlock := LBlock;
+  If (LAccount>=Bank.AccountsCount) then exit; // Invalid account number
+  // If block=0 then we must search in pending operations first
+  if (LBlock=0) then begin
+    // block=0 and not found... start searching at block updated by account updated_block
+    LBlock := Bank.SafeBox.Account(LAccount).GetLastUpdatedBlock;
+  end;
+  if Bank.SafeBox.Account(LAccount).n_operation<LN_Operation then exit; // n_operation is greater than found in safebox
+  if (LBlock=0) or (LBlock>=Bank.BlocksCount) then exit;
+  //
+  // Search in previous blocks
+  LOperationsComp := TPCOperationsComp.Create(Bank);
+  try
+    While (LBlock>0) do begin
+      LPreviousBlock := LBlock;
+      If Not Bank.LoadOperations(LOperationsComp,LBlock) then begin
+        Result := OpHash_block_not_found;
+        exit;
+      end;
+      For i:=LOperationsComp.Count-1 downto 0 do begin
+        LOp := LOperationsComp.Operation[i];
+        if (LOp.IsSignerAccount(LAccount)) then begin
+          LAux_n_op := LOp.GetAccountN_Operation(LAccount);
+          If (LAux_n_op<LN_Operation) then exit; // n_operation is greaten than found
+          If (LAux_n_op=LN_Operation) then begin
+            // Possible candidate or dead
+            TPCOperation.OperationToOperationResume(LBlock,LOp,True,LAccount,AOperationResume);
+            AOperationResume.time := Bank.SafeBox.GetBlockInfo(LBlock).timestamp;
+            AOperationResume.NOpInsideBlock := i;
+            AOperationResume.Balance := -1;
+            LOpHashValid := TPCOperation.OperationHashValid(LOp,LInitialBlock);
+            If (TBaseType.Equals(LOpHashValid,AOpHash)) then begin
+              Result := OpHash_found;
+              exit;
+            end else if (LBlock<CT_Protocol_Upgrade_v2_MinBlock) then begin
+              LOpHashOld := TPCOperation.OperationHash_OLD(LOp,LInitialBlock);
+              if (TBaseType.Equals(LOpHashOld,AOpHash)) then begin
+                Result := OpHash_found;
+                exit;
+              end else exit; // Not found!
+            end else exit; // Not found!
+          end;
+        end;
+      end;
+      LBlock := LOperationsComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(LAccount,LBlock);
+      if (LBlock>=LPreviousBlock) then exit; // Error... not found a valid block positioning
+      if (LInitialBlock<>0) then exit; // If not found in specified block, no valid hash
+    end;
+  finally
+    LOperationsComp.Free;
+  end;
+end;
+
+function TStorage.DoGetAccountOperations(AAccount, AMaxDepth, AStartOperation,
+  AMaxOperations, ASearchBackwardsStartingAtBlock: Integer;
+  const AOperationsResumeList:TOperationsResumeList): Boolean;
+  // Optimization:
+  // For better performance, will only include at "OperationsResume" values betweeen "startOperation" and "endOperation"
+
+  // New use case: Will allow to start in an unknown block when first_block_is_unknows
+  Procedure DoGetFromBlock(block_number : Integer; last_balance : Int64; act_depth : Integer; nOpsCounter : Integer; first_block_is_unknown : Boolean);
+  var opc : TPCOperationsComp;
+    op : TPCOperation;
+    OPR : TOperationResume;
+    LAccounts : TList<Cardinal>;
+    i : Integer;
+    last_block_number : Integer;
+    found_in_block : Boolean;
+    acc_0_miner_reward, acc_4_dev_reward : Int64;
+    acc_4_for_dev : Boolean;
+  begin
+    if (act_depth<=0) then exit;
+    opc := TPCOperationsComp.Create(Nil);
+    Try
+      LAccounts := TList<Cardinal>.Create;
+      try
+        last_block_number := block_number+1;
+        while (last_block_number>block_number) And (act_depth>0)
+          And (block_number >= (AAccount DIV CT_AccountsPerBlock))
+          And (AMaxOperations<>0)
+          do begin
+          found_in_block := False;
+          last_block_number := block_number;
+          LAccounts.Clear;
+          If not Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
+            exit;
+          end;
+          opc.OperationsHashTree.GetOperationsAffectingAccount(AAccount,LAccounts);
+          for i := LAccounts.Count - 1 downto 0 do begin
+            op := opc.Operation[(LAccounts.Items[i])];
+            If TPCOperation.OperationToOperationResume(block_number,Op,False,AAccount,OPR) then begin
+              OPR.NOpInsideBlock := (LAccounts.Items[i]);
+              OPR.time := opc.OperationBlock.timestamp;
+              OPR.Block := block_number;
+              If last_balance>=0 then begin
+                OPR.Balance := last_balance;
+                last_balance := last_balance - ( OPR.Amount + OPR.Fee );
+              end else OPR.Balance := -1; // Undetermined
+              if (nOpsCounter>=AStartOperation) And (AMaxOperations<>0) then begin
+                AOperationsResumeList.Add(OPR);
+              end;
+              inc(nOpsCounter);
+              Dec(AMaxOperations);
+              found_in_block := True;
+            end;
+          end;
+
+          // Is a new block operation?
+          if (TAccountComp.AccountBlock(AAccount)=block_number) then begin
+            TPascalCoinProtocol.GetRewardDistributionForNewBlock(opc.OperationBlock,acc_0_miner_reward,acc_4_dev_reward,acc_4_for_dev);
+            If ((AAccount MOD CT_AccountsPerBlock)=0) Or
+               (  ((AAccount MOD CT_AccountsPerBlock)=CT_AccountsPerBlock-1) AND (acc_4_for_dev)  ) then begin
+              OPR := CT_TOperationResume_NUL;
+              OPR.OpType:=CT_PseudoOp_Reward;
+              OPR.valid := true;
+              OPR.Block := block_number;
+              OPR.time := opc.OperationBlock.timestamp;
+              OPR.AffectedAccount := AAccount;
+              If ((AAccount MOD CT_AccountsPerBlock)=0) then begin
+                OPR.Amount := acc_0_miner_reward;
+                OPR.OperationTxt := 'Miner reward';
+                OPR.OpSubtype:=CT_PseudoOpSubtype_Miner;
+              end else begin
+                OPR.Amount := acc_4_dev_reward;
+                OPR.OperationTxt := 'Dev reward';
+                OPR.OpSubtype:=CT_PseudoOpSubtype_Developer;
+              end;
+              If last_balance>=0 then begin
+               OPR.Balance := last_balance;
+              end else OPR.Balance := -1; // Undetermined
+              if (nOpsCounter>=AStartOperation) And (AMaxOperations<>0) then begin
+                AOperationsResumeList.Add(OPR);
+              end;
+              inc(nOpsCounter);
+              dec(AMaxOperations);
+              found_in_block := True;
+            end;
+          end;
+          //
+          dec(act_depth);
+          If (Not found_in_block) And (first_block_is_unknown) then begin
+            Dec(block_number);
+          end else begin
+            block_number := opc.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(AAccount,block_number);
+          end;
+          opc.Clear(true);
+        end;
+      finally
+        LAccounts.Free;
+      end;
+    Finally
+      opc.Free;
+    End;
+  end;
+
+Var acc : TAccount;
+  startBlock : Cardinal;
+  lastBalance : Int64;
+begin
+  Result := False;
+  if AMaxDepth<0 then Exit;
+  if AAccount>=Bank.SafeBox.AccountsCount then Exit;
+  if AMaxOperations=0 then Exit;
+  Result := True;
+  acc := Bank.SafeBox.Account(AAccount);
+  if (acc.GetLastUpdatedBlock>0) Or (acc.account=0) then Begin
+    if (ASearchBackwardsStartingAtBlock=0) Or (ASearchBackwardsStartingAtBlock>=acc.GetLastUpdatedBlock) then begin
+      startBlock := acc.GetLastUpdatedBlock;
+      lastBalance := acc.balance;
+    end else begin
+      startBlock := ASearchBackwardsStartingAtBlock;
+      lastBalance := -1;
+    end;
+    DoGetFromBlock(startBlock,lastBalance,AMaxDepth,0,startBlock<>acc.GetLastUpdatedBlock);
+  end;
+end;
+
 function TStorage.DoGetBlockInformation(const ABlock: Integer;
 function TStorage.DoGetBlockInformation(const ABlock: Integer;
   var AOperationBlock: TOperationBlock; var AOperationsCount: Integer;
   var AOperationBlock: TOperationBlock; var AOperationsCount: Integer;
   var AVolume: Int64): Boolean;
   var AVolume: Int64): Boolean;
@@ -3271,6 +3486,30 @@ begin
   AVolume := 0;
   AVolume := 0;
   //
   //
   LPCOperations := TPCOperationsComp.Create(Bank);
   LPCOperations := TPCOperationsComp.Create(Bank);
+  Try
+    if Not LoadBlockChainBlock(LPCOperations,ABlock) then begin
+      Exit(False);
+    end else Result := True;
+    AOperationBlock := LPCOperations.OperationBlock.GetCopy;
+    AOperationsCount := LPCOperations.Count;
+    AVolume := LPCOperations.OperationsHashTree.TotalAmount;
+  Finally
+    LPCOperations.Free;
+  End;
+end;
+
+function TStorage.DoGetBlockOperations(ABlock, AOpBlockStartIndex,
+  AMaxOperations: Integer; var AOperationBlock: TOperationBlock;
+  var AOperationsCount: Integer; var AVolume: Int64;
+  const AOperationsResumeList:TOperationsResumeList): Boolean;
+var LPCOperations : TPCOperationsComp;
+  LOpResume : TOperationResume;
+  LOp : TPCOperation;
+begin
+  AOperationBlock:=CT_OperationBlock_NUL;
+  AOperationsCount := 0;
+  AVolume := 0;
+  LPCOperations := TPCOperationsComp.Create(Bank);
   Try
   Try
     if Not LoadBlockChainBlock(LPCOperations,ABlock) then begin
     if Not LoadBlockChainBlock(LPCOperations,ABlock) then begin
       Exit(False);
       Exit(False);
@@ -3278,11 +3517,51 @@ begin
     AOperationBlock := LPCOperations.OperationBlock.GetCopy;
     AOperationBlock := LPCOperations.OperationBlock.GetCopy;
     AOperationsCount := LPCOperations.Count;
     AOperationsCount := LPCOperations.Count;
     AVolume := LPCOperations.OperationsHashTree.TotalAmount;
     AVolume := LPCOperations.OperationsHashTree.TotalAmount;
+    while (AMaxOperations<>0) and (AOpBlockStartIndex>=0) and (AOpBlockStartIndex<LPCOperations.OperationsHashTree.OperationsCount) do begin
+      LOp := LPCOperations.GetOperation(AOpBlockStartIndex);
+      if TPCOperation.OperationToOperationResume(ABlock,LOp,True,LOp.SignerAccount,LOpResume) then begin
+        LOpResume.NOpInsideBlock := AOpBlockStartIndex;
+        LOpResume.time := LPCOperations.OperationBlock.timestamp;
+        LOpResume.Balance := -1;
+        AOperationsResumeList.Add(LOpResume);
+      end;
+      Inc(AOpBlockStartIndex);
+      Dec(AMaxOperations);
+    end;
+    Result := True;
   Finally
   Finally
     LPCOperations.Free;
     LPCOperations.Free;
   End;
   End;
 end;
 end;
 
 
+procedure TStorage.DoLoadPendingBufferOperations(OperationsHashTree: TOperationsHashTree);
+Var fs : TFileStream;
+  errors : String;
+  n : Integer;
+  LCurrentProtocol : Word;
+begin
+  fs := GetPendingBufferOperationsStream;
+  fs.Position:=0;
+  if fs.Size>0 then begin
+    if Assigned(Bank) then LCurrentProtocol := Bank.SafeBox.CurrentProtocol
+    else LCurrentProtocol := CT_BUILD_PROTOCOL;
+    If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,LCurrentProtocol, Nil,errors) then begin
+      TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
+    end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR (Protocol %d): loaded operations:%d errors:%s',[LCurrentProtocol,OperationsHashTree.OperationsCount,errors]));
+  end;
+end;
+
+procedure TStorage.DoSavePendingBufferOperations(
+  OperationsHashTree: TOperationsHashTree);
+Var fs : TFileStream;
+begin
+  fs := GetPendingBufferOperationsStream;
+  fs.Position:=0;
+  fs.Size:=0;
+  OperationsHashTree.SaveOperationsHashTreeToStream(fs,true);
+  {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('DoSavePendingBufferOperations operations:%d',[OperationsHashTree.OperationsCount]));{$ENDIF}
+end;
+
 function TStorage.Initialize: Boolean;
 function TStorage.Initialize: Boolean;
 begin
 begin
   Result := DoInitialize;
   Result := DoInitialize;
@@ -3294,11 +3573,60 @@ begin
   DoEraseStorage;
   DoEraseStorage;
 end;
 end;
 
 
-function TStorage.GetBlockInformation(const ABlock: Integer;
+function TStorage.FindOperation(const AOpHash: TBytes;
+  var AOperationResume: TOperationResume): TSearchOpHashResult;
+begin
+  Result := DoFindOperation(AOpHash,AOperationResume);
+end;
+
+function TStorage.GetAccountOperations(AAccount, AMaxDepth, AStartOperation,
+  AMaxOperations, ASearchBackwardsStartingAtBlock: Integer;
+  const AOperationsResumeList:TOperationsResumeList): Boolean;
+begin
+  Result := DoGetAccountOperations(AAccount,AMaxDepth,AStartOperation,AMaxOperations,ASearchBackwardsStartingAtBlock,AOperationsResumeList);
+end;
+
+function TStorage.GetBlockInformation(ABlock: Integer;
   var AOperationBlock: TOperationBlock; var AOperationsCount: Integer;
   var AOperationBlock: TOperationBlock; var AOperationsCount: Integer;
   var AVolume: Int64): Boolean;
   var AVolume: Int64): Boolean;
 begin
 begin
-  Result := DoGetBlockInformation(ABlock,AOperationBlock,AOperationsCount,AVolume);
+  if (ABlock<FirstBlock) Or (ABlock>LastBlock) then begin
+    AOperationBlock := CT_OperationBlock_NUL;
+    AOperationsCount := 0;
+    AVolume := 0;
+    Result := false;
+  end else Result := DoGetBlockInformation(ABlock,AOperationBlock,AOperationsCount,AVolume);
+end;
+
+function TStorage.GetBlockOperations(ABlock, AOpBlockStartIndex,
+  AMaxOperations: Integer; var AOperationBlock: TOperationBlock;
+  var AOperationsCount: Integer; var AVolume: Int64;
+  const AOperationsResumeList:TOperationsResumeList): Boolean;
+begin
+  if (ABlock<FirstBlock) Or (ABlock>LastBlock) then begin
+    Result := false;
+  end else Result := DoGetBlockOperations(ABlock,AOpBlockStartIndex,AMaxOperations,AOperationBlock,AOperationsCount,AVolume,AOperationsResumeList);
+end;
+
+function TStorage.GetPendingBufferOperationsStream: TFileStream;
+Var fs : TFileStream;
+  fn : TFileName;
+  fm : Word;
+begin
+  If Not Assigned(FPendingBufferOperationsStream) then begin
+    fn := Bank.GetStorageFolder(Bank.Orphan)+PathDelim+'pendingbuffer.ops';
+    If FileExists(fn) then fm := fmOpenReadWrite+fmShareExclusive
+    else fm := fmCreate+fmShareExclusive;
+    Try
+      FPendingBufferOperationsStream := TFileStream.Create(fn,fm);
+    Except
+      On E:Exception do begin
+        TLog.NewLog(ltError,ClassName,'Error opening PendingBufferOperationsStream '+fn+' ('+E.ClassName+'):'+ E.Message);
+        Raise;
+      end;
+    end;
+  end;
+  Result := FPendingBufferOperationsStream;
 end;
 end;
 
 
 procedure TStorage.SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
 procedure TStorage.SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
@@ -4074,9 +4402,9 @@ begin
 end;
 end;
 
 
 function TPCOperation.IsAffectedAccount(account: Cardinal): Boolean;
 function TPCOperation.IsAffectedAccount(account: Cardinal): Boolean;
-Var l : TList<Cardinal>;
+Var l : TOrderedList<Cardinal>;
 begin
 begin
-  l := TList<Cardinal>.Create;
+  l := TOrderedList<Cardinal>.Create(False,TComparison_Cardinal);
   Try
   Try
     AffectedAccounts(l);
     AffectedAccounts(l);
     Result := (l.IndexOf(account)>=0);
     Result := (l.IndexOf(account)>=0);
@@ -4124,86 +4452,6 @@ begin
   Result := 0;
   Result := 0;
 end;
 end;
 
 
-{ TOperationsResumeList }
-
-Type POperationResume = ^TOperationResume;
-
-procedure TOperationsResumeList.Add(const OperationResume: TOperationResume);
-Var P : POperationResume;
-begin
-  New(P);
-  P^ := OperationResume;
-  FList.Add(P);
-end;
-
-procedure TOperationsResumeList.Clear;
-Var P : POperationResume;
-  i : Integer;
-  l : TList<Pointer>;
-begin
-  l := FList.LockList;
-  try
-    for i := 0 to l.Count - 1 do begin
-      P := l[i];
-      Dispose(P);
-    end;
-    l.Clear;
-  finally
-    FList.UnlockList;
-  end;
-end;
-
-function TOperationsResumeList.Count: Integer;
-Var l : TList<Pointer>;
-begin
-  l := FList.LockList;
-  Try
-    Result := l.Count;
-  Finally
-    FList.UnlockList;
-  End;
-end;
-
-constructor TOperationsResumeList.Create;
-begin
-  FList := TPCThreadList<Pointer>.Create('TOperationsResumeList_List');
-end;
-
-procedure TOperationsResumeList.Delete(index: Integer);
-Var P : POperationResume;
-  l : TList<Pointer>;
-begin
-  l := FList.LockList;
-  Try
-    P := l[index];
-    l.Delete(index);
-    Dispose(P);
-  Finally
-    FList.UnlockList;
-  End;
-end;
-
-destructor TOperationsResumeList.Destroy;
-begin
-  Clear;
-  FreeAndNil(FList);
-  inherited;
-end;
-
-function TOperationsResumeList.GetOperationResume(index: Integer): TOperationResume;
-Var l : TList<Pointer>;
-begin
-  l := FList.LockList;
-  try
-    if index<l.Count then Result := POperationResume(l[index])^
-    else begin
-      Result := CT_TOperationResume_NUL;
-    end;
-  finally
-    FList.UnlockList;
-  end;
-end;
-
 initialization
 initialization
   SetLength(_OperationsClass, 0);
   SetLength(_OperationsClass, 0);
   RegisterOperationsClass;
   RegisterOperationsClass;

+ 5 - 70
src/core/UFileStorage.pas

@@ -43,11 +43,9 @@ Type
   private
   private
     FStorageLock : TPCCriticalSection;
     FStorageLock : TPCCriticalSection;
     FBlockChainStream : TFileStream;
     FBlockChainStream : TFileStream;
-    FPendingBufferOperationsStream : TFileStream;
     FStreamFirstBlockNumber : Int64;
     FStreamFirstBlockNumber : Int64;
     FStreamLastBlockNumber : Int64;
     FStreamLastBlockNumber : Int64;
     FBlockHeadersFirstBytePosition : TArrayOfInt64;
     FBlockHeadersFirstBytePosition : TArrayOfInt64;
-    FBlockChainFileName : AnsiString;
     Function StreamReadBlockHeader(Stream: TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal; CanSearchBackward : Boolean; var BlockHeader : TBlockHeader): Boolean;
     Function StreamReadBlockHeader(Stream: TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal; CanSearchBackward : Boolean; var BlockHeader : TBlockHeader): Boolean;
     Function StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
@@ -55,7 +53,6 @@ Type
     Function GetBlockHeaderFixedSize : Int64;
     Function GetBlockHeaderFixedSize : Int64;
     Procedure ClearStream;
     Procedure ClearStream;
     Procedure GrowStreamUntilPos(Stream : TStream; newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
     Procedure GrowStreamUntilPos(Stream : TStream; newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
-    Function GetPendingBufferOperationsStream : TFileStream;
   protected
   protected
     procedure SetReadOnly(const Value: Boolean); override;
     procedure SetReadOnly(const Value: Boolean); override;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
@@ -69,8 +66,6 @@ Type
     function GetLastBlockNumber: Int64; override;
     function GetLastBlockNumber: Int64; override;
     function DoInitialize : Boolean; override;
     function DoInitialize : Boolean; override;
     Procedure DoEraseStorage; override;
     Procedure DoEraseStorage; override;
-    Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
-    Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
   public
   public
     Constructor Create(AOwner : TComponent); Override;
     Constructor Create(AOwner : TComponent); Override;
     Destructor Destroy; Override;
     Destructor Destroy; Override;
@@ -146,7 +141,6 @@ end;
 procedure TFileStorage.ClearStream;
 procedure TFileStorage.ClearStream;
 begin
 begin
   FreeAndNil(FBlockChainStream);
   FreeAndNil(FBlockChainStream);
-  FreeAndNil(FPendingBufferOperationsStream);
   FStreamFirstBlockNumber := 0;
   FStreamFirstBlockNumber := 0;
   FStreamLastBlockNumber := -1;
   FStreamLastBlockNumber := -1;
   SetLength(FBlockHeadersFirstBytePosition,0);
   SetLength(FBlockHeadersFirstBytePosition,0);
@@ -172,27 +166,6 @@ begin
   Stream.Position := newPos;
   Stream.Position := newPos;
 end;
 end;
 
 
-function TFileStorage.GetPendingBufferOperationsStream: TFileStream;
-Var fs : TFileStream;
-  fn : TFileName;
-  fm : Word;
-begin
-  If Not Assigned(FPendingBufferOperationsStream) then begin
-    fn := Bank.GetStorageFolder(Bank.Orphan)+PathDelim+'pendingbuffer.ops';
-    If FileExists(fn) then fm := fmOpenReadWrite+fmShareExclusive
-    else fm := fmCreate+fmShareExclusive;
-    Try
-      FPendingBufferOperationsStream := TFileStream.Create(fn,fm);
-    Except
-      On E:Exception do begin
-        TLog.NewLog(ltError,ClassName,'Error opening PendingBufferOperationsStream '+fn+' ('+E.ClassName+'):'+ E.Message);
-        Raise;
-      end;
-    end;
-  end;
-  Result := FPendingBufferOperationsStream;
-end;
-
 procedure TFileStorage.CopyConfiguration(const CopyFrom: TStorage);
 procedure TFileStorage.CopyConfiguration(const CopyFrom: TStorage);
 begin
 begin
   inherited;
   inherited;
@@ -201,12 +174,10 @@ end;
 constructor TFileStorage.Create(AOwner: TComponent);
 constructor TFileStorage.Create(AOwner: TComponent);
 begin
 begin
   inherited;
   inherited;
-  FBlockChainFileName := '';
   FBlockChainStream := Nil;
   FBlockChainStream := Nil;
   SetLength(FBlockHeadersFirstBytePosition,0);
   SetLength(FBlockHeadersFirstBytePosition,0);
   FStreamFirstBlockNumber := 0;
   FStreamFirstBlockNumber := 0;
   FStreamLastBlockNumber := -1;
   FStreamLastBlockNumber := -1;
-  FPendingBufferOperationsStream := Nil;
   FStorageLock := TPCCriticalSection.Create('TFileStorage_StorageLock');
   FStorageLock := TPCCriticalSection.Create('TFileStorage_StorageLock');
 end;
 end;
 
 
@@ -271,43 +242,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFileStorage.DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
-Var fs : TFileStream;
-begin
-  LockBlockChainStream;
-  Try
-    fs := GetPendingBufferOperationsStream;
-    fs.Position:=0;
-    fs.Size:=0;
-    OperationsHashTree.SaveOperationsHashTreeToStream(fs,true);
-    {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('DoSavePendingBufferOperations operations:%d',[OperationsHashTree.OperationsCount]));{$ENDIF}
-  finally
-    UnlockBlockChainStream;
-  end;
-end;
-
-procedure TFileStorage.DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
-Var fs : TFileStream;
-  errors : String;
-  n : Integer;
-  LCurrentProtocol : Word;
-begin
-  LockBlockChainStream;
-  Try
-    fs := GetPendingBufferOperationsStream;
-    fs.Position:=0;
-    if fs.Size>0 then begin
-      if Assigned(Bank) then LCurrentProtocol := Bank.SafeBox.CurrentProtocol
-      else LCurrentProtocol := CT_BUILD_PROTOCOL;
-      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,LCurrentProtocol, Nil,errors) then begin
-        TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
-      end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR (Protocol %d): loaded operations:%d errors:%s',[LCurrentProtocol,OperationsHashTree.OperationsCount,errors]));
-    end;
-  finally
-    UnlockBlockChainStream;
-  end;
-end;
-
 function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
 function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
 Var stream : TStream;
 Var stream : TStream;
   iBlockHeaders : Integer;
   iBlockHeaders : Integer;
@@ -353,7 +287,7 @@ begin
     try
     try
       db.Bank := Self.Bank;
       db.Bank := Self.Bank;
       db.FStreamFirstBlockNumber := Start_Block;
       db.FStreamFirstBlockNumber := Start_Block;
-      db.FBlockChainFileName := TPCBank.GetStorageFolder(DestOrphan)+PathDelim+'BlockChainStream.blocks';
+      db.FStorageFilename := TPCBank.GetStorageFolder(DestOrphan)+PathDelim+'BlockChainStream.blocks';
       db.LockBlockChainStream;
       db.LockBlockChainStream;
       try
       try
         db.FIsMovingBlockchain:=True;
         db.FIsMovingBlockchain:=True;
@@ -642,10 +576,11 @@ begin
   TPCThread.ProtectEnterCriticalSection(Self,FStorageLock);
   TPCThread.ProtectEnterCriticalSection(Self,FStorageLock);
   Try
   Try
     if Not Assigned(FBlockChainStream) then begin
     if Not Assigned(FBlockChainStream) then begin
-      if FBlockChainFileName<>'' then begin
-        fn := FBlockChainFileName
+      if FStorageFilename<>'' then begin
+        fn := FStorageFilename
       end else begin
       end else begin
         fn := TPCBank.GetStorageFolder(Orphan)+PathDelim+'BlockChainStream.blocks';
         fn := TPCBank.GetStorageFolder(Orphan)+PathDelim+'BlockChainStream.blocks';
+        FStorageFilename := fn;
       end;
       end;
       exists := FileExists(fn);
       exists := FileExists(fn);
       if ReadOnly then begin
       if ReadOnly then begin
@@ -674,7 +609,7 @@ end;
 procedure TFileStorage.SetBlockChainFile(BlockChainFileName: AnsiString);
 procedure TFileStorage.SetBlockChainFile(BlockChainFileName: AnsiString);
 begin
 begin
   ClearStream;
   ClearStream;
-  FBlockChainFileName := BlockChainFileName;
+  FStorageFilename := BlockChainFileName;
 end;
 end;
 
 
 procedure TFileStorage.SetReadOnly(const Value: Boolean);
 procedure TFileStorage.SetReadOnly(const Value: Boolean);

+ 7 - 6
src/core/UNetProtocol.pas

@@ -1659,8 +1659,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     try
     try
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.Orphan := TNode.Node.Bank.Orphan;
       Bank.Orphan := TNode.Node.Bank.Orphan;
-      Bank.Storage.ReadOnly := true;
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
+      Bank.Storage.ReadOnly := true;
 
 
 
 
       if start_block>=0 then begin
       if start_block>=0 then begin
@@ -1671,6 +1671,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
           IsUsingSnapshot := True;
           IsUsingSnapshot := True;
 
 
           Bank.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
           Bank.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+          Bank.Storage.StorageFilename := '';
           Bank.Storage.ReadOnly := false;
           Bank.Storage.ReadOnly := false;
 
 
         end else begin
         end else begin
@@ -4459,7 +4460,7 @@ begin
         nOpsToSend := Operations.OperationsCount;
         nOpsToSend := Operations.OperationsCount;
       end;
       end;
       if FBufferToSendOperations.OperationsCount>0 then begin
       if FBufferToSendOperations.OperationsCount>0 then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Sending %d Operations to %s (inProc:%d, Received:%d)',[FBufferToSendOperations.OperationsCount,ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count]));
+        {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Sending %d Operations to %s (inProc:%d, Received:%d)',[FBufferToSendOperations.OperationsCount,ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count]));{$ENDIF}
         LStream := TMemoryStream.Create;
         LStream := TMemoryStream.Create;
         try
         try
           request_id := TNetData.NetData.NewRequestId;
           request_id := TNetData.NetData.NewRequestId;
@@ -5166,7 +5167,7 @@ begin
     inc(P^.counter);
     inc(P^.counter);
     inc(FTotalCounter);
     inc(FTotalCounter);
     UpdateMedian(l);
     UpdateMedian(l);
-    TLog.NewLog(ltDebug,ClassName,Format('AddNewIp (%s,%d) - Total:%d/%d Offset:%d',[clientIp,clientTimestamp,l.Count,FTotalCounter,FTimeOffset]));
+    {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,Format('AddNewIp (%s,%d) - Total:%d/%d Offset:%d',[clientIp,clientTimestamp,l.Count,FTotalCounter,FTimeOffset]));{$ENDIF}
   finally
   finally
     FTimesList.UnlockList;
     FTimesList.UnlockList;
   end;
   end;
@@ -5241,9 +5242,9 @@ begin
       Dec(FTotalCounter);
       Dec(FTotalCounter);
     end;
     end;
     UpdateMedian(l);
     UpdateMedian(l);
-    if (i>=0) then
-      TLog.NewLog(ltDebug,ClassName,Format('RemoveIp (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
-    else TLog.NewLog(ltError,ClassName,Format('RemoveIp not found (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
+    if (i>=0) then begin
+      {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,Format('RemoveIp (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset])){$ENDIF}
+    end else TLog.NewLog(ltError,ClassName,Format('RemoveIp not found (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
   finally
   finally
     FTimesList.UnlockList;
     FTimesList.UnlockList;
   end;
   end;

+ 83 - 233
src/core/UNode.pas

@@ -44,10 +44,20 @@ Type
 
 
   { TNode }
   { TNode }
 
 
-  TSearchOperationResult = (found, invalid_params, blockchain_block_not_found);
-
   TNodeNotifyEvents = Class;
   TNodeNotifyEvents = Class;
 
 
+  TNode = Class;
+
+  TSaveMempoolOperationsThread = Class(TPCThread)
+  private
+    FNode : TNode;
+    FPendingToSave : Boolean;
+  protected
+    procedure BCExecute; override;
+  public
+    procedure Touch;
+  End;
+
   TNode = Class(TComponent)
   TNode = Class(TComponent)
   private
   private
     FNodeLog : TLog;
     FNodeLog : TLog;
@@ -65,6 +75,7 @@ Type
     FBroadcastData : Boolean;
     FBroadcastData : Boolean;
     FUpdateBlockchain: Boolean;
     FUpdateBlockchain: Boolean;
     FMaxPayToKeyPurchasePrice: Int64;
     FMaxPayToKeyPurchasePrice: Int64;
+    FSaveMempoolOperationsThread : TSaveMempoolOperationsThread;
     {$IFDEF BufferOfFutureOperations}
     {$IFDEF BufferOfFutureOperations}
     FBufferAuxWaitingOperations : TOperationsHashTree;
     FBufferAuxWaitingOperations : TOperationsHashTree;
     {$ENDIF}
     {$ENDIF}
@@ -101,12 +112,9 @@ Type
     //
     //
     Procedure NotifyBlocksChanged;
     Procedure NotifyBlocksChanged;
     //
     //
-    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;
-    Function FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high : Cardinal; OpResumeList : TOperationsResumeList) : TSearchOperationResult;
+    Function FindOperation(Const AOperationHash : TRawBytes; var AOperationResume : TOperationResume) : TSearchOpHashResult;
+    Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOpHashResult;
+    Function FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high : Cardinal; OpResumeList : TOperationsResumeList) : TSearchOpHashResult;
     //
     //
     Procedure InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
     Procedure InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
     Procedure AutoDiscoverNodes(Const ips : String);
     Procedure AutoDiscoverNodes(Const ips : String);
@@ -313,7 +321,7 @@ begin
               FSentOperations.SetTag(resendOp.Sha256,LLockedMempool.OperationBlock.block); // Set tag new value
               FSentOperations.SetTag(resendOp.Sha256,LLockedMempool.OperationBlock.block); // Set tag new value
               FSentOperations.Add(LLockedMempool.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
               FSentOperations.Add(LLockedMempool.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
             end else begin
             end else begin
-              {$IFDEF HIGHLOG}TLog.NewLog(ltInfo,ClassName,'Sanitized operation not included to resend (j:'+IntToStr(j)+'>'+inttostr(minBlockResend)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);{$ENDIF}
+//              {$IFDEF HIGHLOG}TLog.NewLog(ltInfo,ClassName,'Sanitized operation not included to resend (j:'+IntToStr(j)+'>'+inttostr(minBlockResend)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);{$ENDIF}
             end;
             end;
             inc(i);
             inc(i);
           end;
           end;
@@ -583,20 +591,14 @@ begin
         end;
         end;
       end; // for i
       end; // for i
       If Result<>0 then begin
       If Result<>0 then begin
-        LLockedMempool := LockMempoolRead;
-        try
-          // Save operations buffer
-          Bank.Storage.SavePendingBufferOperations(LLockedMempool.OperationsHashTree);
-        finally
-          UnlockMempoolRead;
-        end;
+        FSaveMempoolOperationsThread.Touch; // This will indicate to thread that mempool needs to be saved
         LTickCount := TPlatform.GetElapsedMilliseconds(LTickCount);
         LTickCount := TPlatform.GetElapsedMilliseconds(LTickCount);
         if LTickCount=0 then LTickCount:=1;
         if LTickCount=0 then LTickCount:=1;
         if Assigned(SenderConnection) then begin
         if Assigned(SenderConnection) then begin
           s := SenderConnection.ClientRemoteAddr;
           s := SenderConnection.ClientRemoteAddr;
         end else s := '(SELF)';
         end else s := '(SELF)';
-        TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations from %s Operations:%d of %d valids:%d spam:%d invalids:%d repeated:%d Miliseconds:%d %.1f ops/sec',
-          [s,LOpsToAdd.Count,AOperationsHashTreeToAdd.OperationsCount,Result,nSpam,nError,nRepeated,LTickCount,LOpsToAdd.Count * 1000 / LTickCount]));
+        {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations from %s Operations:%d of %d valids:%d spam:%d invalids:%d repeated:%d Miliseconds:%d %.1f ops/sec',
+          [s,LOpsToAdd.Count,AOperationsHashTreeToAdd.OperationsCount,Result,nSpam,nError,nRepeated,LTickCount,LOpsToAdd.Count * 1000 / LTickCount]));{$ENDIF}
         if FBroadcastData then begin
         if FBroadcastData then begin
           // Send to other nodes
           // Send to other nodes
           j := TNetData.NetData.ConnectionsCountAll;
           j := TNetData.NetData.ConnectionsCountAll;
@@ -669,6 +671,9 @@ begin
   {$ENDIF}
   {$ENDIF}
   FBroadcastData := True;
   FBroadcastData := True;
   FUpdateBlockchain := True;
   FUpdateBlockchain := True;
+  FSaveMempoolOperationsThread := TSaveMempoolOperationsThread.Create(True);
+  FSaveMempoolOperationsThread.FNode := Self;
+  FSaveMempoolOperationsThread.Resume;
   if Not Assigned(_Node) then _Node := Self;
   if Not Assigned(_Node) then _Node := Self;
 end;
 end;
 
 
@@ -728,6 +733,11 @@ Var step : String;
 begin
 begin
   TLog.NewLog(ltInfo,ClassName,'TNode.Destroy START');
   TLog.NewLog(ltInfo,ClassName,'TNode.Destroy START');
   Try
   Try
+    step := 'Deleting SaveMempoolOperationsThread';
+    FSaveMempoolOperationsThread.Terminate;
+    FSaveMempoolOperationsThread.WaitFor;
+    FreeAndNil(FSaveMempoolOperationsThread);
+
     step := 'Deleting critical section';
     step := 'Deleting critical section';
     FreeAndNil(FLockMempool);
     FreeAndNil(FLockMempool);
     FreeAndNil(FOperationSequenceLock);
     FreeAndNil(FOperationSequenceLock);
@@ -1078,148 +1088,8 @@ begin
   end;
   end;
 end;
 end;
 
 
-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"
-
-  // New use case: Will allow to start in an unknown block when first_block_is_unknows
-  Procedure DoGetFromBlock(block_number : Integer; last_balance : Int64; act_depth : Integer; nOpsCounter : Integer; first_block_is_unknown : Boolean);
-  var opc : TPCOperationsComp;
-    op : TPCOperation;
-    OPR : TOperationResume;
-    l : TList<Cardinal>;
-    i : Integer;
-    last_block_number : Integer;
-    found_in_block : Boolean;
-    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
-      l := TList<Cardinal>.Create;
-      try
-        last_block_number := block_number+1;
-        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;
-          If not Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
-            {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(block_number)+' not found. Cannot read operations');{$ENDIF}
-            exit;
-          end;
-          opc.OperationsHashTree.GetOperationsAffectingAccount(account_number,l);
-          for i := l.Count - 1 downto 0 do begin
-            op := opc.Operation[PtrInt(l.Items[i])];
-            If TPCOperation.OperationToOperationResume(block_number,Op,False,account_number,OPR) then begin
-              OPR.NOpInsideBlock := PtrInt(l.Items[i]);
-              OPR.time := opc.OperationBlock.timestamp;
-              OPR.Block := block_number;
-              If last_balance>=0 then begin
-                OPR.Balance := last_balance;
-                last_balance := last_balance - ( OPR.Amount + OPR.Fee );
-              end else OPR.Balance := -1; // Undetermined
-              if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
-                OperationsResume.Add(OPR);
-              end;
-              inc(nOpsCounter);
-              found_in_block := True;
-            end;
-          end;
-
-          // Is a new block operation?
-          if (TAccountComp.AccountBlock(account_number)=block_number) then begin
-            TPascalCoinProtocol.GetRewardDistributionForNewBlock(opc.OperationBlock,acc_0_miner_reward,acc_4_dev_reward,acc_4_for_dev);
-            If ((account_number MOD CT_AccountsPerBlock)=0) Or
-               (  ((account_number MOD CT_AccountsPerBlock)=CT_AccountsPerBlock-1) AND (acc_4_for_dev)  ) then begin
-              OPR := CT_TOperationResume_NUL;
-              OPR.OpType:=CT_PseudoOp_Reward;
-              OPR.valid := true;
-              OPR.Block := block_number;
-              OPR.time := opc.OperationBlock.timestamp;
-              OPR.AffectedAccount := account_number;
-              If ((account_number MOD CT_AccountsPerBlock)=0) then begin
-                OPR.Amount := acc_0_miner_reward;
-                OPR.OperationTxt := 'Miner reward';
-                OPR.OpSubtype:=CT_PseudoOpSubtype_Miner;
-              end else begin
-                OPR.Amount := acc_4_dev_reward;
-                OPR.OperationTxt := 'Dev reward';
-                OPR.OpSubtype:=CT_PseudoOpSubtype_Developer;
-              end;
-              If last_balance>=0 then begin
-               OPR.Balance := last_balance;
-              end else OPR.Balance := -1; // Undetermined
-              if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
-               OperationsResume.Add(OPR);
-              end;
-              inc(nOpsCounter);
-              found_in_block := True;
-            end;
-          end;
-          //
-          dec(act_depth);
-          If (Not found_in_block) And (first_block_is_unknown) then begin
-            Dec(block_number);
-          end else begin
-            block_number := opc.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(account_number,block_number);
-          end;
-          opc.Clear(true);
-        end;
-      finally
-        l.Free;
-      end;
-    Finally
-      opc.Free;
-    End;
-  end;
-
-Var acc : TAccount;
-  startBlock : Cardinal;
-  lastBalance : Int64;
-begin
-  if MaxDepth<0 then Exit;
-  if account_number>=Bank.SafeBox.AccountsCount then Exit;
-  if StartOperation>EndOperation then Exit;
-  acc := Bank.SafeBox.Account(account_number);
-  if (acc.GetLastUpdatedBlock>0) Or (acc.account=0) then Begin
-    if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.GetLastUpdatedBlock) then begin
-      startBlock := acc.GetLastUpdatedBlock;
-      lastBalance := acc.balance;
-    end else begin
-      startBlock := SearchBackwardsStartingAtBlock;
-      lastBalance := -1;
-    end;
-    DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.GetLastUpdatedBlock);
-  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;
 function TNode.FindNOperation(block, account, n_operation: Cardinal;
-  var OpResume: TOperationResume): TSearchOperationResult;
+  var OpResume: TOperationResume): TSearchOpHashResult;
   // Note: block = 0 search in all blocks. If Block>0 must match a valid block with operation with this account
   // Note: block = 0 search in all blocks. If Block>0 must match a valid block with operation with this account
 var oprl : TOperationsResumeList;
 var oprl : TOperationsResumeList;
 begin
 begin
@@ -1227,14 +1097,14 @@ begin
   try
   try
     Result := FindNOperations(account,block,block=0,n_operation,n_operation,oprl);
     Result := FindNOperations(account,block,block=0,n_operation,n_operation,oprl);
     If oprl.Count>0 then begin
     If oprl.Count>0 then begin
-      OpResume := oprl.OperationResume[0];
+      OpResume := oprl.Items[0];
     end else OpResume := CT_TOperationResume_NUL;
     end else OpResume := CT_TOperationResume_NUL;
   finally
   finally
     oprl.Free;
     oprl.Free;
   end;
   end;
 end;
 end;
 
 
-function TNode.FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high: Cardinal; OpResumeList: TOperationsResumeList): TSearchOperationResult;
+function TNode.FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high: Cardinal; OpResumeList: TOperationsResumeList): TSearchOpHashResult;
 var i : Integer;
 var i : Integer;
   op : TPCOperation;
   op : TPCOperation;
   aux_block, block : Cardinal;
   aux_block, block : Cardinal;
@@ -1244,7 +1114,7 @@ var i : Integer;
   LLockedMempool : TPCOperationsComp;
   LLockedMempool : TPCOperationsComp;
 begin
 begin
   OpResumeList.Clear;
   OpResumeList.Clear;
-  Result := invalid_params;
+  Result := OpHash_invalid_params;
   block := start_block;
   block := start_block;
   If (block>=Bank.BlocksCount) then exit; // Invalid block number
   If (block>=Bank.BlocksCount) then exit; // Invalid block number
   If (account>=Bank.AccountsCount) then exit; // Invalid account number
   If (account>=Bank.AccountsCount) then exit; // Invalid account number
@@ -1272,7 +1142,7 @@ begin
             OpResumeList.Add(opr);
             OpResumeList.Add(opr);
             if (n_operation>n_operation_low) then dec(n_operation)
             if (n_operation>n_operation_low) then dec(n_operation)
             else begin
             else begin
-              Result := found;
+              Result := OpHash_found;
               Exit;
               Exit;
             end;
             end;
           end;
           end;
@@ -1289,7 +1159,7 @@ begin
     While (n_operation>0) And (n_operation>=n_operation_low) And (block>0) do begin
     While (n_operation>0) And (n_operation>=n_operation_low) And (block>0) do begin
       aux_block := block;
       aux_block := block;
       If Not Bank.LoadOperations(OperationComp,block) then begin
       If Not Bank.LoadOperations(OperationComp,block) then begin
-        Result := blockchain_block_not_found; // Cannot continue searching!
+        Result := OpHash_block_not_found; // Cannot continue searching!
         exit;
         exit;
       end;
       end;
       For i:=OperationComp.Count-1 downto 0 do begin
       For i:=OperationComp.Count-1 downto 0 do begin
@@ -1305,12 +1175,12 @@ begin
             OpResumeList.Add(opr);
             OpResumeList.Add(opr);
             if (n_operation>n_operation_low) then dec(n_operation)
             if (n_operation>n_operation_low) then dec(n_operation)
             else begin
             else begin
-              Result := found;
+              Result := OpHash_found;
               Exit;
               Exit;
             end;
             end;
           end else begin
           end else begin
             If (op.GetAccountN_Operation(account) < n_operation) then begin
             If (op.GetAccountN_Operation(account) < n_operation) then begin
-              If (n_operation_high>n_operation_low) then Result := found; // multiple search, result is found (not an error)
+              If (n_operation_high>n_operation_low) then Result := OpHash_found; // multiple search, result is found (not an error)
               Exit // First occurrence is lower
               Exit // First occurrence is lower
             end;
             end;
           end;
           end;
@@ -1326,7 +1196,7 @@ begin
   finally
   finally
     OperationComp.Free;
     OperationComp.Free;
   end;
   end;
-  Result := found;
+  Result := OpHash_found;
 end;
 end;
 
 
 procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
 procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
@@ -1348,40 +1218,36 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TNode.FindOperationExt(const OperationComp: TPCOperationsComp;
-  const OperationHash: TRawBytes; var block: Cardinal;
-  var operation_block_index: Integer): TSearchOperationResult;
+function TNode.FindOperation(Const AOperationHash : TRawBytes; var AOperationResume : TOperationResume) : TSearchOpHashResult;
 { With a OperationHash, search it }
 { With a OperationHash, search it }
-var account,n_operation : Cardinal;
+var
   i : Integer;
   i : Integer;
   op : TPCOperation;
   op : TPCOperation;
-  initial_block, aux_block, aux_n_op : Cardinal;
-  opHashValid, opHash_OLD : TRawBytes;
+  opHashValid : TRawBytes;
   md160 : TRawBytes;
   md160 : TRawBytes;
   LLockedMempool : TPCOperationsComp;
   LLockedMempool : TPCOperationsComp;
+  LBlock, LAccount, LN_Operation : Cardinal;
 begin
 begin
-  Result := invalid_params;
+  Result := OpHash_invalid_params;
   // Decode OperationHash
   // Decode OperationHash
-  If not TPCOperation.DecodeOperationHash(OperationHash,block,account,n_operation,md160) then exit;
-  initial_block := block;
+  If not TPCOperation.DecodeOperationHash(AOperationHash,LBlock,LAccount,LN_Operation,md160) then exit;
   //
   //
-  If (account>=Bank.AccountsCount) then exit; // Invalid account number
+  If (LAccount>=Bank.AccountsCount) then exit; // Invalid account number
   // If block=0 then we must search in pending operations first
   // If block=0 then we must search in pending operations first
-  if (block=0) then begin
+  if (LBlock=0) then begin
     LLockedMempool := LockMempoolRead;
     LLockedMempool := LockMempoolRead;
     Try
     Try
       LLockedMempool.Lock;
       LLockedMempool.Lock;
       Try
       Try
         For i:=0 to LLockedMempool.Count-1 do begin
         For i:=0 to LLockedMempool.Count-1 do begin
           op := LLockedMempool.Operation[i];
           op := LLockedMempool.Operation[i];
-          If (op.SignerAccount=account) then begin
+          If (op.SignerAccount=LAccount) then begin
             opHashValid := TPCOperation.OperationHashValid(op,0);
             opHashValid := TPCOperation.OperationHashValid(op,0);
-            opHash_OLD := TPCOperation.OperationHash_OLD(op,0);
-            If TBaseType.Equals(opHashValid,OperationHash) or
-              ((FBank.BlocksCount<CT_Protocol_Upgrade_v2_MinBlock) And (TBaseType.Equals(opHash_OLD,OperationHash))) then begin
-              operation_block_index:=i;
-              OperationComp.CopyFrom(LLockedMempool);
-              Result := found;
+            If TBaseType.Equals(opHashValid,AOperationHash) then begin
+              TPCOperation.OperationToOperationResume(0,op,True,LAccount,AOperationResume);
+              AOperationResume.Balance := -1;
+              AOperationResume.NOpInsideBlock := i;
+              Result := OpHash_found;
               exit;
               exit;
             end;
             end;
           end;
           end;
@@ -1392,55 +1258,8 @@ begin
     Finally
     Finally
       UnlockMempoolRead;
       UnlockMempoolRead;
     End;
     End;
-    // block=0 and not found... start searching at block updated by account updated_block
-    block := Bank.SafeBox.Account(account).GetLastUpdatedBlock;
-    if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
-  end;
-  if (block=0) or (block>=Bank.BlocksCount) then exit;
-  // Search in previous blocks
-  While (block>0) do begin
-    aux_block := block;
-    If Not Bank.LoadOperations(OperationComp,block) then begin
-      Result := blockchain_block_not_found;
-      exit;
-    end;
-    For i:=OperationComp.Count-1 downto 0 do begin
-      op := OperationComp.Operation[i];
-      if (op.IsSignerAccount(account)) then begin
-        aux_n_op := op.GetAccountN_Operation(account);
-        If (aux_n_op<n_operation) then exit; // n_operation is greaten than found
-        If (aux_n_op=n_operation) then begin
-          // Possible candidate or dead
-          opHashValid := TPCOperation.OperationHashValid(op,initial_block);
-          If (TBaseType.Equals(opHashValid,OperationHash)) then begin
-            operation_block_index:=i;
-            Result := found;
-            exit;
-          end else if (block<CT_Protocol_Upgrade_v2_MinBlock) then begin
-            opHash_OLD := TPCOperation.OperationHash_OLD(op,initial_block);
-            if (TBaseType.Equals(opHash_OLD,OperationHash)) then begin
-              operation_block_index:=i;
-              Result := found;
-              exit;
-            end else exit; // Not found!
-          end else exit; // Not found!
-        end;
-      end;
-    end;
-    block := OperationComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(account,block);
-    if (block>=aux_block) then exit; // Error... not found a valid block positioning
-    if (initial_block<>0) then exit; // If not found in specified block, no valid hash
   end;
   end;
-end;
-
-function TNode.FindOperation(const OperationComp: TPCOperationsComp;
-  const OperationHash: TRawBytes; var block: Cardinal;
-  var operation_block_index: Integer): Boolean;
-  { With a OperationHash, search it }
-var sor : TSearchOperationResult;
-begin
-  sor := FindOperationExt(OperationComp,OperationHash,block,operation_block_index);
-  Result := sor = found;
+  Result := Bank.Storage.FindOperation(AOperationHash,AOperationResume);
 end;
 end;
 
 
 procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: String);
 procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: String);
@@ -1848,6 +1667,37 @@ begin
   inherited;
   inherited;
 end;
 end;
 
 
+{ TSaveMempoolOperationsThread }
+
+procedure TSaveMempoolOperationsThread.BCExecute;
+var i : Integer;
+  LLocked : TPCOperationsComp;
+begin
+  FPendingToSave := false;
+  repeat
+    if FPendingToSave then begin
+      LLocked := FNode.LockMempoolRead;
+      try
+        FPendingToSave := False;
+        FNode.Bank.Storage.SavePendingBufferOperations(LLocked.OperationsHashTree);
+      finally
+        FNode.UnlockMempoolRead;
+      end;
+    end;
+    // Wait 10 seconds prior to save updates on mempool
+    i := 0;
+    while (i<1000) and (Not Terminated) do begin
+      Sleep(10);
+      inc(i);
+    end;
+  until (false) or (Terminated);
+end;
+
+procedure TSaveMempoolOperationsThread.Touch;
+begin
+  FPendingToSave := True;
+end;
+
 initialization
 initialization
   _Node := Nil;
   _Node := Nil;
   _PascalCoinDataFolder := '';
   _PascalCoinDataFolder := '';

+ 13 - 13
src/core/UOpTransaction.pas

@@ -27,7 +27,7 @@ interface
 
 
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes, UEPasa;
+  UPCDataTypes, UEPasa, UOrderedList;
 
 
 Type
 Type
   // Operations Type
   // Operations Type
@@ -91,7 +91,7 @@ Type
   public
   public
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean; override;
     function DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     //
     //
     class function OpType : Byte; override;
     class function OpType : Byte; override;
     function OperationAmount : Int64; override;
     function OperationAmount : Int64; override;
@@ -132,7 +132,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
     Constructor Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
     Property Data : TOpChangeKeyData read FData;
     Property Data : TOpChangeKeyData read FData;
@@ -171,7 +171,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
     Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
     Property Data : TOpRecoverFoundsData read FData;
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
@@ -243,7 +243,7 @@ Type
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function SellerAccount : Int64; override;
     function SellerAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
@@ -297,7 +297,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
     Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
       account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
       account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
@@ -349,7 +349,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; AGUID : TGUID; amount, fee : UInt64; const payload: TOperationPayload);
     Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; AGUID : TGUID; amount, fee : UInt64; const payload: TOperationPayload);
     Property Data : TOpDataData read FData;
     Property Data : TOpDataData read FData;
@@ -655,7 +655,7 @@ begin
   Result := FData.n_operation;
   Result := FData.n_operation;
 end;
 end;
 
 
-procedure TOpChangeAccountInfo.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeAccountInfo.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -774,7 +774,7 @@ end;
 
 
 { TOpTransaction }
 { TOpTransaction }
 
 
-procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpTransaction.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.sender);
   list.Add(FData.sender);
   list.Add(FData.target);
   list.Add(FData.target);
@@ -1392,7 +1392,7 @@ end;
 
 
 { TOpChangeKey }
 { TOpChangeKey }
 
 
-procedure TOpChangeKey.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeKey.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -1739,7 +1739,7 @@ end;
 
 
 { TOpRecoverFounds }
 { TOpRecoverFounds }
 
 
-procedure TOpRecoverFounds.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpRecoverFounds.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account);
   list.Add(FData.account);
 end;
 end;
@@ -1930,7 +1930,7 @@ end;
 
 
 { TOpListAccount }
 { TOpListAccount }
 
 
-procedure TOpListAccount.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpListAccount.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if FData.account_signer<>FData.account_target then
   if FData.account_signer<>FData.account_target then
@@ -2817,7 +2817,7 @@ begin
   Result := FData.n_operation;
   Result := FData.n_operation;
 end;
 end;
 
 
-procedure TOpData.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpData.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_signer<>FData.account_sender) then begin
   if (FData.account_signer<>FData.account_sender) then begin

+ 5 - 8
src/core/UPCAbstractMem.pas

@@ -514,8 +514,7 @@ begin
     FAbstractMem := TMem.Create(0,AReadOnly);
     FAbstractMem := TMem.Create(0,AReadOnly);
   end;
   end;
   if FAbstractMem is TFileMem then begin
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
   end;
 
 
   DoInit(LIsNewStructure);
   DoInit(LIsNewStructure);
@@ -746,8 +745,7 @@ procedure TPCAbstractMem.SetMaxMemUsage(const Value: Integer);
 begin
 begin
   FMaxMemUsage := Value;
   FMaxMemUsage := Value;
   if FAbstractMem is TFileMem then begin
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
   end;
 end;
 end;
 
 
@@ -801,8 +799,7 @@ begin
     FAbstractMem := TMem.Create(0,LReadOnly);
     FAbstractMem := TMem.Create(0,LReadOnly);
   end;
   end;
   if FAbstractMem is TFileMem then begin
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
   end;
   DoInit(Ltmp);
   DoInit(Ltmp);
 end;
 end;
@@ -980,7 +977,7 @@ begin
   Lani.accountName := AName;
   Lani.accountName := AName;
   Lani.accountNumber := AAccountNumber;
   Lani.accountNumber := AAccountNumber;
   if Not AddData(Lani) then begin
   if Not AddData(Lani) then begin
-    if Not FindData(Lani,Lposition) then
+    if Not FindDataPos(Lani,Lposition) then
       raise EPCAbstractMem.Create(Format('Fatal error Cannot add account(%d) name %s',[AAccountNumber,AName]))
       raise EPCAbstractMem.Create(Format('Fatal error Cannot add account(%d) name %s',[AAccountNumber,AName]))
     else raise EPCAbstractMem.Create(Format('Cannot add account(%d) name %s because used by %d with %s',[AAccountNumber,AName,
     else raise EPCAbstractMem.Create(Format('Cannot add account(%d) name %s because used by %d with %s',[AAccountNumber,AName,
       Lani.accountNumber,Lani.accountName]));
       Lani.accountNumber,Lani.accountName]));
@@ -1012,7 +1009,7 @@ var Lani : TAccountNameInfo;
 begin
 begin
   Lani.accountName := AName;
   Lani.accountName := AName;
   Lani.accountNumber := 0;
   Lani.accountNumber := 0;
-  Result := FindData(Lani,AAbstractMemPosition);
+  Result := FindDataPos(Lani,AAbstractMemPosition);
 end;
 end;
 
 
 { TPCAbstractMemListBlocks }
 { TPCAbstractMemListBlocks }

+ 4 - 0
src/core/UPCOperationsSignatureValidator.pas

@@ -325,6 +325,9 @@ begin
     LOperation := FValidator.GetNextOperation(Self);
     LOperation := FValidator.GetNextOperation(Self);
     if Assigned(LOperation) then begin
     if Assigned(LOperation) then begin
       if Not LOperation.HasValidSignature then begin
       if Not LOperation.HasValidSignature then begin
+        {$IFDEF TESTING_NO_POW_CHECK}
+        LIsValid := True;
+        {$ELSE}
         // Only will validate if HasValidSignature is False (Not validated before)
         // Only will validate if HasValidSignature is False (Not validated before)
         try
         try
           LIsValid := LOperation.IsValidSignatureBasedOnCurrentSafeboxState(FValidator.FSafeBoxTransaction);
           LIsValid := LOperation.IsValidSignatureBasedOnCurrentSafeboxState(FValidator.FSafeBoxTransaction);
@@ -334,6 +337,7 @@ begin
             TLog.NewLog(lterror,ClassName,LOperation.ToString+' ERROR: ('+E.ClassName+') '+E.Message);
             TLog.NewLog(lterror,ClassName,LOperation.ToString+' ERROR: ('+E.ClassName+') '+E.Message);
           end;
           end;
         end;
         end;
+        {$ENDIF}
         FValidator.SetOperationCheckResult(Self,LOperation, LIsValid);
         FValidator.SetOperationCheckResult(Self,LOperation, LIsValid);
       end;
       end;
     end;
     end;

+ 1 - 1
src/core/UPCRPCOpData.pas

@@ -343,7 +343,7 @@ begin
     LResultArray := AJSONResponse.GetAsArray('result');
     LResultArray := AJSONResponse.GetAsArray('result');
 
 
     for i := 0 to LOperationsResumeList.Count-1 do begin
     for i := 0 to LOperationsResumeList.Count-1 do begin
-      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.Items[i],ASender.Node.Bank.BlocksCount,
         ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
         ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
         LResultArray.GetAsObject( LResultArray.Count ));
         LResultArray.GetAsObject( LResultArray.Count ));
     end;
     end;

+ 277 - 121
src/core/URPC.pas

@@ -24,6 +24,8 @@ interface
 
 
 {$I ./../config.inc}
 {$I ./../config.inc}
 
 
+{$DEFINE RPC_PROTECT_MASSIVE_CALLS}
+
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UJSONFunctions, classes, blcksock, synsock,
   UJSONFunctions, classes, blcksock, synsock,
@@ -52,6 +54,7 @@ Const
   CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
+  CT_RPC_ErrNum_MaxCalls = 1022;
 
 
 
 
 Type
 Type
@@ -162,6 +165,7 @@ Type
     class procedure RegisterProcessMethod(Const AMethodName : String; ARPCProcessMethod : TRPCProcessMethod);
     class procedure RegisterProcessMethod(Const AMethodName : String; ARPCProcessMethod : TRPCProcessMethod);
     class procedure UnregisterProcessMethod(Const AMethodName : String);
     class procedure UnregisterProcessMethod(Const AMethodName : String);
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
+    class procedure ProcessMethodCalled(Const AMethodName : String; AStartTickCount : TTickCount);
   end;
   end;
 
 
 implementation
 implementation
@@ -171,17 +175,27 @@ Uses
   SysUtils, Synautil,
   SysUtils, Synautil,
   UEPasaDecoder,
   UEPasaDecoder,
   UPCRPCSend,
   UPCRPCSend,
+  UOrderedList,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
 
 
 Type
 Type
   TRegisteredRPCProcessMethod = Record
   TRegisteredRPCProcessMethod = Record
     MethodName : String;
     MethodName : String;
     RPCProcessMethod : TRPCProcessMethod;
     RPCProcessMethod : TRPCProcessMethod;
+    CallsCounter : Integer;
+    ElapsedMilis : Int64;
+    procedure Clear;
   end;
   end;
+  PRegisteredRPCProcessMethod = ^TRegisteredRPCProcessMethod;
 
 
 var _RPCServer : TRPCServer = Nil;
 var _RPCServer : TRPCServer = Nil;
 
 
-  _RPCProcessMethods : TList<TRegisteredRPCProcessMethod> = Nil;
+  _RPCProcessMethods : TOrderedList<PRegisteredRPCProcessMethod> = Nil;
+
+function TRegisteredRPCProcessMethod_Comparer(const ALeft,ARight : PRegisteredRPCProcessMethod) : Integer;
+begin
+  Result := AnsiCompareText(ALeft.MethodName , ARight.MethodName);
+end;
 
 
 { TPascalCoinJSONComp }
 { TPascalCoinJSONComp }
 
 
@@ -292,7 +306,7 @@ Begin
   end;
   end;
   if OPR.valid then begin
   if OPR.valid then begin
     jsonObject.GetAsVariant('block').Value:=OPR.Block;
     jsonObject.GetAsVariant('block').Value:=OPR.Block;
-    jsonObject.GetAsVariant('time').Value:=OPR.time;
+    if OPR.time>0 then jsonObject.GetAsVariant('time').Value:=OPR.time;
     jsonObject.GetAsVariant('opblock').Value:=OPR.NOpInsideBlock;
     jsonObject.GetAsVariant('opblock').Value:=OPR.NOpInsideBlock;
     if (OPR.Block>0) And (OPR.Block<currentNodeBlocksCount) then
     if (OPR.Block>0) And (OPR.Block<currentNodeBlocksCount) then
       jsonObject.GetAsVariant('maturation').Value := currentNodeBlocksCount - OPR.Block - 1
       jsonObject.GetAsVariant('maturation').Value := currentNodeBlocksCount - OPR.Block - 1
@@ -1032,19 +1046,40 @@ end;
 
 
 class function TRPCProcess.FindRegisteredProcessMethod(const AMethodName: String): TRPCProcessMethod;
 class function TRPCProcess.FindRegisteredProcessMethod(const AMethodName: String): TRPCProcessMethod;
 var i : Integer;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
 begin
   Result := Nil;
   Result := Nil;
   if Not Assigned(_RPCProcessMethods) then Exit;
   if Not Assigned(_RPCProcessMethods) then Exit;
-  i := 0;
-  while (i<_RPCProcessMethods.Count) and (Not Assigned(Result)) do begin
-    if AnsiSameStr( _RPCProcessMethods.Items[i].MethodName , AMethodName) then begin
-      Result := _RPCProcessMethods.Items[i].RPCProcessMethod;
+  New(P);
+  Try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      Result := _RPCProcessMethods.Get(i).RPCProcessMethod;
     end;
     end;
-    inc(i);
-  end;
+  Finally
+    Dispose(P);
+  End;
 end;
 end;
 
 
 procedure TRPCProcess.BCExecute;
 procedure TRPCProcess.BCExecute;
+  function ValidMethodName(const AMethod : String) : Boolean;
+  var i : Integer;
+  begin
+    Result := False;
+    for i:=0 to AMethod.Length-1 do begin
+      case AMethod.Chars[i] of
+        'a'..'z',
+        'A'..'Z',
+        '0'..'9',
+        '_','.' : ; // Nothing to do
+        '-' : if i=0 then Exit; // Cannot start with "-"
+      else Exit; // Not a valid char
+      end;
+    end;
+    Result := True;
+  end;
+
 var
 var
   timeout: integer;
   timeout: integer;
   s: string;
   s: string;
@@ -1054,10 +1089,10 @@ var
   resultcode: integer;
   resultcode: integer;
   inputdata : TRawBytes;
   inputdata : TRawBytes;
   js,jsresult : TPCJSONData;
   js,jsresult : TPCJSONData;
-  jsonobj,jsonresponse : TPCJSONObject;
+  jsonobj,jsonresponse, paramsJSON : TPCJSONObject;
   errNum : Integer; errDesc : String;
   errNum : Integer; errDesc : String;
   jsonrequesttxt,
   jsonrequesttxt,
-  jsonresponsetxt, methodName, paramsTxt : String;
+  jsonresponsetxt, methodName, paramsTxt, senderIP : String;
   valid : Boolean;
   valid : Boolean;
   i : Integer;
   i : Integer;
   Headers : TStringList;
   Headers : TStringList;
@@ -1066,6 +1101,7 @@ var
   LOnStartLiveConnectionCount : Integer;
   LOnStartLiveConnectionCount : Integer;
 begin
 begin
   LOnStartLiveConnectionCount := FRPCServer.FLiveConnectionsCount;
   LOnStartLiveConnectionCount := FRPCServer.FLiveConnectionsCount;
+  senderIP := '';
   callcounter := _RPCServer.GetNewCallCounter;
   callcounter := _RPCServer.GetNewCallCounter;
   tc := TPlatform.GetTickCount;
   tc := TPlatform.GetTickCount;
   methodName := '';
   methodName := '';
@@ -1139,9 +1175,27 @@ begin
             errDesc := '';
             errDesc := '';
             try
             try
               methodName := jsonobj.AsString('method','');
               methodName := jsonobj.AsString('method','');
-              paramsTxt := jsonobj.GetAsObject('params').ToJSON(false);
+              paramsJSON := jsonobj.GetAsObject('params');
+              senderIP := Trim(jsonObj.AsString('remoteaddr','')); //
+              paramsTxt := paramsJSON.ToJSON(false);
               {$IFDEF HIGHLOG}TLog.NewLog(ltinfo,Classname,FSock.GetRemoteSinIP+':'+inttostr(FSock.GetRemoteSinPort)+' Processing method '+methodName+' params '+paramsTxt);{$ENDIF}
               {$IFDEF HIGHLOG}TLog.NewLog(ltinfo,Classname,FSock.GetRemoteSinIP+':'+inttostr(FSock.GetRemoteSinPort)+' Processing method '+methodName+' params '+paramsTxt);{$ENDIF}
-              Valid := ProcessMethod(methodName,jsonobj.GetAsObject('params'),jsonresponse,errNum,errDesc);
+              valid := True;
+              {$IFDEF RPC_PROTECT_MASSIVE_CALLS}
+              if (senderIP<>'') and (ValidMethodName(methodName)) then begin
+                if TNetData.NetData.IpInfos.Update_And_ReachesLimits(senderIP,'rpcmethod',methodName,0,True,
+                 TArray<TLimitLifetime>.Create(TLimitLifetime.Create(60,50,0),TLimitLifetime.Create(3600,500,0))) then  begin
+                   valid := false;
+                   errNum := CT_RPC_ErrNum_MaxCalls;
+                   errDesc := Format('IP:%s Reached limit %s',[senderIP,methodName]);
+                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
+                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:=errDesc;
+                 end;
+              end;
+              {$ENDIF}
+              if valid then begin
+
+              TRPCProcess.ProcessMethodCalled(methodName,tc);
+              Valid := ProcessMethod(methodName,paramsJSON,jsonresponse,errNum,errDesc);
               if not Valid then begin
               if not Valid then begin
                 if (errNum<>0) or (errDesc<>'') then begin
                 if (errNum<>0) or (errDesc<>'') then begin
                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
@@ -1151,6 +1205,8 @@ begin
                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
                 end;
                 end;
               end;
               end;
+
+              end;
             Except
             Except
               on E:Exception do begin
               on E:Exception do begin
                 TLog.NewLog(lterror,Classname,'Exception processing method'+methodName+' ('+E.ClassName+'): '+E.Message);
                 TLog.NewLog(lterror,Classname,'Exception processing method'+methodName+' ('+E.ClassName+'): '+E.Message);
@@ -1197,8 +1253,14 @@ begin
           FSock.SendString(jsonresponsetxt);
           FSock.SendString(jsonresponsetxt);
         end;
         end;
       end;
       end;
-      _RPCServer.AddRPCLog(FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort),callcounter,'Method:'+methodName+' Params:'+paramsTxt+' '+Inttostr(errNum)+':'+errDesc+' Time:'+FormatFloat('0.000',(TPlatform.GetElapsedMilliseconds(tc)/1000))
+      if senderIP<>'' then begin
+        senderIP := FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort) + ' @'+senderIP;
+      end else begin
+        senderIP := FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort);
+      end;
+      _RPCServer.AddRPCLog(senderIP,callcounter,'Method:'+methodName+' Params:'+paramsTxt+' '+Inttostr(errNum)+':'+errDesc+' Time:'+FormatFloat('0.000',(TPlatform.GetElapsedMilliseconds(tc)/1000))
         +' '+LOnStartLiveConnectionCount.ToString+'->'+FRPCServer.FLiveConnectionsCount.ToString);
         +' '+LOnStartLiveConnectionCount.ToString+'->'+FRPCServer.FLiveConnectionsCount.ToString);
+      TRPCProcess.ProcessMethodCalled(methodName,tc);
     finally
     finally
       jsonresponse.free;
       jsonresponse.free;
       Headers.Free;
       Headers.Free;
@@ -1284,6 +1346,68 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
+  Function GetBlockOperation(ABlock, AOpBlock : Integer; jsonObject : TPCJSONObject) : Boolean;
+  var LOpResumeList : TOperationsResumeList;
+    LOperationBlock : TOperationBlock;
+    LOperationsCount : Integer;
+    LOperationsAmount : Int64;
+  begin
+    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
+    try
+    LOpResumeList := TOperationsResumeList.Create;
+    Try
+      if not FNode.Bank.Storage.GetBlockOperations(ABlock,AOpBlock,1,LOperationBlock,LOperationsCount,LOperationsAmount,LOpResumeList) then begin
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc := 'Cannot load Block: '+ABlock.ToString+' OpBlock: '+AOpBlock.ToString;
+        Result := False;
+        Exit;
+      end;
+      if LOpResumeList.Count<>1 then Exit(False);
+      TPascalCoinJSONComp.FillOperationObject(LOpResumeList.Items[0],
+          FNode.Bank.BlocksCount,
+          Node,RPCServer.WalletKeys,RPCServer.PayloadPasswords,
+          jsonObject);
+      Result := True;
+    Finally
+      LOpResumeList.Free;
+    End;
+    finally
+      FNode.OperationSequenceLock.Release;
+    end;
+  end;
+
+
+  Function GetBlockOperations(ABlock, AOpBlockStartIndex, AMaxOperations : Integer; jsonArray : TPCJSONArray) : Boolean;
+  var LOpResumeList : TOperationsResumeList;
+    LOperationBlock : TOperationBlock;
+    LOperationsCount : Integer;
+    LOperationsAmount : Int64;
+    i : Integer;
+  begin
+    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
+    try
+    LOpResumeList := TOperationsResumeList.Create;
+    Try
+      if not FNode.Bank.Storage.GetBlockOperations(ABlock,AOpBlockStartIndex,AMaxOperations,LOperationBlock,LOperationsCount,LOperationsAmount,LOpResumeList) then begin
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc := 'Cannot load Block: '+ABlock.ToString+' OpBlock: '+AOpBlockStartIndex.ToString+' Max: '+AMaxOperations.ToString;
+        Result := False;
+        Exit;
+      end;
+      for i := 0 to LOpResumeList.Count-1 do begin
+        TPascalCoinJSONComp.FillOperationObject(LOpResumeList.Items[i],FNode.Bank.BlocksCount,
+            Node,RPCServer.WalletKeys,RPCServer.PayloadPasswords,
+            jsonArray.GetAsObject(jsonArray.Count));
+      end;
+      Result := True;
+    Finally
+      LOpResumeList.Free;
+    End;
+    finally
+      FNode.OperationSequenceLock.Release;
+    end;
+  end;
+
   Procedure FillOperationResumeToJSONObject(Const OPR : TOperationResume; jsonObject : TPCJSONObject);
   Procedure FillOperationResumeToJSONObject(Const OPR : TOperationResume; jsonObject : TPCJSONObject);
   Begin
   Begin
     TPascalCoinJSONComp.FillOperationObject(OPR,FNode.Bank.BlocksCount,
     TPascalCoinJSONComp.FillOperationObject(OPR,FNode.Bank.BlocksCount,
@@ -1340,7 +1464,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       end;
       if (nCounter<maxReg) then begin
       if (nCounter<maxReg) then begin
         if (startReg<0) then startReg := 0; // Prevent -1 value
         if (startReg<0) then startReg := 0; // Prevent -1 value
-        FNode.GetStoredOperationsFromAccount(OperationsResume,accountNumber,maxBlocksDepth,startReg,startReg+maxReg-1,forceStartBlock);
+        FNode.Bank.Storage.GetAccountOperations(accountNumber,maxBlocksDepth,startReg,maxReg,forceStartBlock,OperationsResume);
       end;
       end;
       for i:=0 to OperationsResume.Count-1 do begin
       for i:=0 to OperationsResume.Count-1 do begin
         Obj := jsonArray.GetAsObject(jsonArray.Count);
         Obj := jsonArray.GetAsObject(jsonArray.Count);
@@ -1434,6 +1558,38 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
+  Procedure GetMethodsCallsStats;
+  var i : Integer;
+    obj: TPCJSONObject;
+    P : PRegisteredRPCProcessMethod;
+    LCalls, LMilis : Int64;
+  Begin
+    if Not Assigned(_RPCProcessMethods) then Exit;
+    LCalls := 0;
+    LMilis := 0;
+    i := 0;
+    while (i<_RPCProcessMethods.Count) do begin
+      P := _RPCProcessMethods.Get(i);
+      obj := GetResultArray.GetAsObject(GetResultArray.Count);
+      obj.GetAsVariant('method').Value := P.MethodName;
+      obj.GetAsVariant('calls').Value := P.CallsCounter;
+      obj.GetAsVariant('seconds').Value := FormatFloat('0.000',P.ElapsedMilis/1000);
+      if P.CallsCounter>0 then begin
+        obj.GetAsVariant('secs_average').Value := FormatFloat('0.000',(P.ElapsedMilis/1000)/P.CallsCounter);
+      end;
+      inc(LCalls,P.CallsCounter);
+      inc(LMilis,P.ElapsedMilis);
+      inc(i);
+    end;
+    obj := GetResultArray.GetAsObject(GetResultArray.Count);
+    obj.GetAsVariant('method').Value := 'TOTAL';
+    obj.GetAsVariant('calls').Value := LCalls;
+    obj.GetAsVariant('seconds').Value := FormatFloat('0.000',LMilis/1000);
+    if LCalls>0 then begin
+      obj.GetAsVariant('secs_average').Value := FormatFloat('0.000',(LMilis/1000)/LCalls);
+    end;
+  end;
+
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
   Function CreateOperationTransaction(current_protocol : Word; sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
   Function CreateOperationTransaction(current_protocol : Word; sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
@@ -2552,7 +2708,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   function FindNOperations : Boolean;
   function FindNOperations : Boolean;
   Var oprl : TOperationsResumeList;
   Var oprl : TOperationsResumeList;
     start_block, account, n_operation_min, n_operation_max : Cardinal;
     start_block, account, n_operation_min, n_operation_max : Cardinal;
-    sor : TSearchOperationResult;
+    sor : TSearchOpHashResult;
     jsonarr : TPCJSONArray;
     jsonarr : TPCJSONArray;
     i : Integer;
     i : Integer;
   begin
   begin
@@ -2575,13 +2731,13 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       start_block := params.AsCardinal('start_block',0); // Optional: 0 = Search all
       start_block := params.AsCardinal('start_block',0); // Optional: 0 = Search all
       sor := FNode.FindNOperations(account,start_block,true,n_operation_min,n_operation_max,oprl);
       sor := FNode.FindNOperations(account,start_block,true,n_operation_min,n_operation_max,oprl);
       Case sor of
       Case sor of
-        found : Result := True;
-        invalid_params : begin
+        OpHash_found : Result := True;
+        OpHash_invalid_params : begin
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorDesc:='Not found using block/account/n_operation';
             ErrorDesc:='Not found using block/account/n_operation';
             exit;
             exit;
           end;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             ErrorNum := CT_RPC_ErrNum_InvalidBlock;
             ErrorNum := CT_RPC_ErrNum_InvalidBlock;
             ErrorDesc:='Blockchain file does not contain all blocks to find';
             ErrorDesc:='Blockchain file does not contain all blocks to find';
             exit;
             exit;
@@ -2591,7 +2747,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       jsonarr := jsonresponse.GetAsArray('result');
       jsonarr := jsonresponse.GetAsArray('result');
       if oprl.Count>0 then begin;
       if oprl.Count>0 then begin;
         for i:=0 to oprl.Count-1 do begin
         for i:=0 to oprl.Count-1 do begin
-          FillOperationResumeToJSONObject(oprl.OperationResume[i],jsonarr.GetAsObject(jsonarr.Count));
+          FillOperationResumeToJSONObject(oprl.Items[i],jsonarr.GetAsObject(jsonarr.Count));
         end;
         end;
       end;
       end;
     finally
     finally
@@ -3299,81 +3455,15 @@ begin
     // Param "block" contains block. Null = Pending operation
     // Param "block" contains block. Null = Pending operation
     // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
     // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
     // Returns a JSON object with operation values as "Operation resume format"
     // Returns a JSON object with operation values as "Operation resume format"
-    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
-    try
-    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
-    if (c>=0) And (c<FNode.Bank.BlocksCount) then begin
-      pcops := TPCOperationsComp.Create(Nil);
-      try
-        If Not FNode.Bank.LoadOperations(pcops,c) then begin
-          ErrorNum := CT_RPC_ErrNum_InternalError;
-          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
-          Exit;
-        end;
-        i := params.GetAsVariant('opblock').AsInteger(0);
-        if (i<0) Or (i>=pcops.Count) then begin
-          ErrorNum := CT_RPC_ErrNum_InvalidOperation;
-          ErrorDesc := 'Block/Operation not found: '+IntToStr(c)+'/'+IntToStr(i)+' BlockOperations:'+IntToStr(pcops.Count);
-          Exit;
-        end;
-        If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-          opr.NOpInsideBlock:=i;
-          opr.time:=pcops.OperationBlock.timestamp;
-          opr.Balance := -1;
-          FillOperationResumeToJSONObject(opr,GetResultObject);
-        end;
-        Result := True;
-      finally
-        pcops.Free;
-      end;
-    end else begin
-      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
-      else ErrorDesc := 'Block not found: '+IntToStr(c);
-      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
-    end;
-    finally
-      Node.OperationSequenceLock.Release;
-    end;
+    Result := GetBlockOperation(params.GetAsVariant('block').AsInteger(CT_MaxBlock),
+      params.GetAsVariant('opblock').AsInteger(CT_MaxBlock),GetResultObject);
   end else if (method='getblockoperations') then begin
   end else if (method='getblockoperations') then begin
     // Param "block" contains block
     // Param "block" contains block
     // Returns a JSON array with items as "Operation resume format"
     // Returns a JSON array with items as "Operation resume format"
-    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
-    try
-    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
-    if (c>=0) And (c<FNode.Bank.BlocksCount) then begin
-      pcops := TPCOperationsComp.Create(Nil);
-      try
-        If Not FNode.Bank.LoadOperations(pcops,c) then begin
-          ErrorNum := CT_RPC_ErrNum_InternalError;
-          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
-          Exit;
-        end;
-        jsonarr := GetResultArray;
-        k := params.AsInteger('max',100);
-        j := params.AsInteger('start',0);
-        for i := 0 to pcops.Count - 1 do begin
-          if (i>=j) then begin
-            If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-              opr.NOpInsideBlock:=i;
-              opr.time:=pcops.OperationBlock.timestamp;
-              opr.Balance := -1; // Don't include!
-              FillOperationResumeToJSONObject(opr,jsonarr.GetAsObject(jsonarr.Count));
-            end;
-          end;
-          if (k>0) And ((i+1)>=(j+k)) then break;
-        end;
-        Result := True;
-      finally
-        pcops.Free;
-      end;
-    end else begin
-      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
-      else ErrorDesc := 'Block not found: '+IntToStr(c);
-      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
-    end;
-    finally
-      FNode.OperationSequenceLock.Release;
-    end;
+    Result := GetBlockOperations(params.GetAsVariant('block').AsInteger(CT_MaxBlock),
+      params.GetAsVariant('start').AsInteger(0),
+      params.GetAsVariant('max').AsInteger(100),
+      GetResultArray);
   end else if (method='getaccountoperations') then begin
   end else if (method='getaccountoperations') then begin
     // Returns all the operations affecting an account in "Operation resume format" as an array
     // Returns all the operations affecting an account in "Operation resume format" as an array
     // Param "account" contains account number
     // Param "account" contains account number
@@ -3446,36 +3536,30 @@ begin
       ErrorDesc:='param ophash not found or invalid hexadecimal value "'+params.AsString('ophash','')+'"';
       ErrorDesc:='param ophash not found or invalid hexadecimal value "'+params.AsString('ophash','')+'"';
       exit;
       exit;
     end;
     end;
+    if (Length(r1)<>32) then begin
+      ErrorNum:=CT_RPC_ErrNum_InvalidOperation;
+      ErrorDesc:='param ophash with invalid length (Expected 64 chars for a 32bytes hexadecimal) value length = '+IntToStr(Length(r1));
+      exit;
+    end;
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     try
     try
-    pcops := TPCOperationsComp.Create(Nil);
-    try
-      Case FNode.FindOperationExt(pcops,r1,c,i) of
-        found : ;
-        invalid_params : begin
+      Case FNode.FindOperation(r1,opr) of
+        OpHash_found : ;
+        OpHash_invalid_params : begin
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
             ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
             exit;
             exit;
           end;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             ErrorNum := CT_RPC_ErrNum_InternalError;
             ErrorNum := CT_RPC_ErrNum_InternalError;
-            ErrorDesc:='Blockchain block '+IntToStr(c)+' not found to search ophash: "'+params.AsString('ophash','')+'"';
+            ErrorDesc:='Blockchain block not found to search ophash: "'+params.AsString('ophash','')+'"';
             exit;
             exit;
           end;
           end;
       else Raise Exception.Create('ERROR DEV 20171120-4');
       else Raise Exception.Create('ERROR DEV 20171120-4');
       end;
       end;
-      If not TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-        ErrorNum := CT_RPC_ErrNum_InternalError;
-        ErrorDesc := 'Error 20161026-1';
-      end;
-      opr.NOpInsideBlock:=i;
-      opr.time:=pcops.OperationBlock.timestamp;
       opr.Balance := -1; // don't include
       opr.Balance := -1; // don't include
       FillOperationResumeToJSONObject(opr,GetResultObject);
       FillOperationResumeToJSONObject(opr,GetResultObject);
       Result := True;
       Result := True;
-    finally
-      pcops.Free;
-    end;
     finally
     finally
       FNode.OperationSequenceLock.Release;
       FNode.OperationSequenceLock.Release;
     end;
     end;
@@ -3489,13 +3573,13 @@ begin
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     try
     try
     Case FNode.FindNOperation(params.AsCardinal('block',0),c,params.AsCardinal('n_operation',0),opr) of
     Case FNode.FindNOperation(params.AsCardinal('block',0),c,params.AsCardinal('n_operation',0),opr) of
-      found : ;
-      invalid_params : begin
+      OpHash_found : ;
+      OpHash_invalid_params : begin
           ErrorNum:=CT_RPC_ErrNum_NotFound;
           ErrorNum:=CT_RPC_ErrNum_NotFound;
           ErrorDesc:='Not found using block/account/n_operation';
           ErrorDesc:='Not found using block/account/n_operation';
           exit;
           exit;
         end;
         end;
-      blockchain_block_not_found : begin
+      OpHash_block_not_found : begin
           ErrorNum := CT_RPC_ErrNum_InvalidBlock;
           ErrorNum := CT_RPC_ErrNum_InvalidBlock;
           ErrorDesc:='Blockchain file does not contain all blocks to find';
           ErrorDesc:='Blockchain file does not contain all blocks to find';
           exit;
           exit;
@@ -3973,6 +4057,14 @@ begin
     end;
     end;
     Get_node_ip_stats;
     Get_node_ip_stats;
     Result := True;
     Result := True;
+  end else if (method='methods_stats') then begin
+    if (Not _RPCServer.AllowUsePrivateKeys) then begin
+      // Protection when server is locked to avoid private keys call
+      ErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+      Exit;
+    end;
+    GetMethodsCallsStats;
+    Result := True;
   end else begin
   end else begin
     LRPCProcessMethod := FindRegisteredProcessMethod(method);
     LRPCProcessMethod := FindRegisteredProcessMethod(method);
     if Assigned(LRPCProcessMethod) then begin
     if Assigned(LRPCProcessMethod) then begin
@@ -3984,26 +4076,77 @@ begin
   end;
   end;
 end;
 end;
 
 
+class procedure TRPCProcess.ProcessMethodCalled(const AMethodName: String;
+  AStartTickCount: TTickCount);
+var
+  P, PFound : PRegisteredRPCProcessMethod;
+  i : Integer;
+begin
+  if Not Assigned(_RPCProcessMethods) then begin
+    _RPCProcessMethods := TOrderedList<PRegisteredRPCProcessMethod>.Create(False,TRegisteredRPCProcessMethod_Comparer);
+  end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      PFound := _RPCProcessMethods.Get(i);
+    end else begin
+      // Create
+      New(PFound);
+      PFound.Clear;
+      PFound.MethodName := AMethodName;
+      _RPCProcessMethods.Add(PFound);
+    end;
+    if (AStartTickCount>0) then begin
+      inc(PFound.CallsCounter);
+      inc(PFound.ElapsedMilis,Int64(TPlatform.GetElapsedMilliseconds(AStartTickCount)));
+    end;
+  finally
+    Dispose(P);
+  end;
+end;
+
 class procedure TRPCProcess.RegisterProcessMethod(const AMethodName: String; ARPCProcessMethod: TRPCProcessMethod);
 class procedure TRPCProcess.RegisterProcessMethod(const AMethodName: String; ARPCProcessMethod: TRPCProcessMethod);
-var LRegistered : TRegisteredRPCProcessMethod;
+var
+  P, PFound : PRegisteredRPCProcessMethod;
+  i : Integer;
 begin
 begin
   if Not Assigned(_RPCProcessMethods) then begin
   if Not Assigned(_RPCProcessMethods) then begin
-    _RPCProcessMethods := TList<TRegisteredRPCProcessMethod>.Create;
+    _RPCProcessMethods := TOrderedList<PRegisteredRPCProcessMethod>.Create(False,TRegisteredRPCProcessMethod_Comparer);
+  end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      PFound := _RPCProcessMethods.Get(i);
+    end else begin
+      // Create
+      New(PFound);
+      PFound.Clear;
+      PFound.MethodName := AMethodName;
+      _RPCProcessMethods.Add(PFound);
+    end;
+    PFound.RPCProcessMethod := ARPCProcessMethod;
+  finally
+    Dispose(P);
   end;
   end;
-  if Assigned(FindRegisteredProcessMethod(AMethodName)) then Exit; // Duplicated!
-  LRegistered.MethodName := AMethodName;
-  LRegistered.RPCProcessMethod := ARPCProcessMethod;
-  _RPCProcessMethods.Add(LRegistered);
 end;
 end;
 
 
 class procedure TRPCProcess.UnregisterProcessMethod(const AMethodName: String);
 class procedure TRPCProcess.UnregisterProcessMethod(const AMethodName: String);
-var i : Integer;
+var
+  P : PRegisteredRPCProcessMethod;
+  i : Integer;
 begin
 begin
   if Not Assigned(_RPCProcessMethods) then Exit;
   if Not Assigned(_RPCProcessMethods) then Exit;
-  for i := _RPCProcessMethods.Count-1 downto 0 do begin
-    if AnsiSameStr(_RPCProcessMethods.Items[i].MethodName , AMethodName) then begin
-      _RPCProcessMethods.Delete(i);
-    end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    _RPCProcessMethods.Remove(P);
+  finally
+    Dispose(P);
   end;
   end;
 end;
 end;
 
 
@@ -4055,16 +4198,29 @@ end;
 
 
 procedure DoFinalize;
 procedure DoFinalize;
 var i : Integer;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
 begin
   if Assigned(_RPCProcessMethods) then begin
   if Assigned(_RPCProcessMethods) then begin
     for i := _RPCProcessMethods.Count-1 downto 0 do begin
     for i := _RPCProcessMethods.Count-1 downto 0 do begin
+      P := _RPCProcessMethods.Get(i);
       _RPCProcessMethods.Delete(i);
       _RPCProcessMethods.Delete(i);
+      Dispose(P);
     end;
     end;
   end;
   end;
   FreeAndNil(_RPCProcessMethods);
   FreeAndNil(_RPCProcessMethods);
   FreeAndNil(_RPCServer);
   FreeAndNil(_RPCServer);
 end;
 end;
 
 
+{ TRegisteredRPCProcessMethod }
+
+procedure TRegisteredRPCProcessMethod.Clear;
+begin
+  Self.MethodName := '';
+  Self.RPCProcessMethod := Nil;
+  Self.CallsCounter := 0;
+  Self.ElapsedMilis := 0;
+end;
+
 initialization
 initialization
 finalization
 finalization
   DoFinalize;
   DoFinalize;

+ 16 - 12
src/core/UThread.pas

@@ -184,7 +184,7 @@ Var _threads : TPCThreadList<TPCThread>;
 constructor TPCThread.Create(CreateSuspended: Boolean);
 constructor TPCThread.Create(CreateSuspended: Boolean);
 begin
 begin
   inherited Create(CreateSuspended);
   inherited Create(CreateSuspended);
-  {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Created Thread '+IntToHex(PtrInt(Self),8));{$ENDIF}
+  {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Created Thread '+IntToHex(PtrInt(Self),16));{$ENDIF}
 end;
 end;
 
 
 destructor TPCThread.Destroy;
 destructor TPCThread.Destroy;
@@ -205,7 +205,7 @@ begin
   FDebugStep := '';
   FDebugStep := '';
   i := _threads.Add(Self);
   i := _threads.Add(Self);
   try
   try
-    {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Starting Thread '+IntToHex(PtrInt(Self),8)+' in pos '+inttostr(i+1));{$ENDIF}
+    {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Starting Thread '+IntToHex(PtrInt(Self),16)+' in pos '+inttostr(i+1));{$ENDIF}
     Try
     Try
       Try
       Try
         BCExecute;
         BCExecute;
@@ -347,9 +347,9 @@ begin
     if lockStartedTimestamp=0 then tc3 := 0
     if lockStartedTimestamp=0 then tc3 := 0
     else tc3 := tc2-lockStartedTimestamp;
     else tc3 := tc2-lockStartedTimestamp;
     s := Format('Cannot Protect a critical section %s %s class %s after %d milis locked by %s waiting %d-%d elapsed milis: %d',
     s := Format('Cannot Protect a critical section %s %s class %s after %d milis locked by %s waiting %d-%d elapsed milis: %d',
-      [IntToHex(PtrInt(Lock),8),Lock.Name,
+      [IntToHex(PtrInt(Lock),16),Lock.Name,
       Sender.ClassName,tc2-tc,
       Sender.ClassName,tc2-tc,
-      IntToHex(lockCurrThread,8)+'-'+IntToHex(Lock.CurrentThread,8),
+      IntToHex(lockCurrThread,16)+'-'+IntToHex(Lock.CurrentThread,16),
       lockWatingForCounter,Lock.WaitingForCounter,
       lockWatingForCounter,Lock.WaitingForCounter,
       tc3
       tc3
       ]);
       ]);
@@ -435,9 +435,11 @@ end;
 {$IFDEF HIGHLOG}
 {$IFDEF HIGHLOG}
 procedure TPCCriticalSection.Acquire;
 procedure TPCCriticalSection.Acquire;
 Var continue, logged : Boolean;
 Var continue, logged : Boolean;
-  startTC : TTickCount;
+  startTC, LLastTC : TTickCount;
+  LWaitMillis : Int64;
 begin
 begin
   startTC := TPlatform.GetTickCount;
   startTC := TPlatform.GetTickCount;
+  LLastTC := startTC;
   FCounterLock.Acquire;
   FCounterLock.Acquire;
   try
   try
     FWaitingForCounter := FWaitingForCounter + 1;
     FWaitingForCounter := FWaitingForCounter + 1;
@@ -445,21 +447,23 @@ begin
     FCounterLock.Release;
     FCounterLock.Release;
   end;
   end;
   logged := false;
   logged := false;
+  LWaitMillis := 1000;
   Repeat
   Repeat
     continue := inherited TryEnter;
     continue := inherited TryEnter;
     if (Not continue) then begin
     if (Not continue) then begin
-      If (not logged) And (TPlatform.GetElapsedMilliseconds(startTC)>1000) then begin
+      If (TPlatform.GetElapsedMilliseconds(LLastTC)>LWaitMillis) then begin
+        LLastTC := TPlatform.GetTickCount;
+        inc(LWaitMillis,LWaitMillis);
         logged := true;
         logged := true;
-        TLog.NewLog(ltdebug,ClassName,'ALERT Critical section '+IntToHex(PtrInt(Self),8)+' '+Name+
-          ' locked by '+IntToHex(FCurrentThread,8)+' waiting '+
+        TLog.NewLog(ltdebug,ClassName,'ALERT Critical section '+IntToHex(PtrInt(Self),16)+' '+Name+
+          ' locked by '+IntToHex(FCurrentThread,16)+' waiting '+
           IntToStr(FWaitingForCounter)+' elapsed milis: '+IntToStr(TPlatform.GetElapsedMilliseconds(startTC)));
           IntToStr(FWaitingForCounter)+' elapsed milis: '+IntToStr(TPlatform.GetElapsedMilliseconds(startTC)));
-        continue := true;
-        inherited;
       end else sleep(1);
       end else sleep(1);
+      sleep(1);
     end;
     end;
   Until continue;
   Until continue;
   if (logged) then begin
   if (logged) then begin
-    TLog.NewLog(ltdebug,Classname,'ENTER Critical section '+IntToHex(PtrInt(Self),8)+' '+Name+' elapsed milis: '+IntToStr(TPlatform.GetElapsedMilliseconds(startTC)) );
+    TLog.NewLog(ltdebug,Classname,'ENTER Critical section '+IntToHex(PtrInt(Self),16)+' '+Name+' elapsed milis: '+IntToStr(TPlatform.GetElapsedMilliseconds(startTC)) );
   end;
   end;
   FCounterLock.Acquire;
   FCounterLock.Acquire;
   try
   try
@@ -480,7 +484,7 @@ begin
   FStartedTickCount := 0;
   FStartedTickCount := 0;
   FName := AName;
   FName := AName;
   inherited Create;
   inherited Create;
-  {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,'Created critical section '+IntToHex(PtrInt(Self),8)+' '+AName );{$ENDIF}
+  {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,'Created critical section '+IntToHex(PtrInt(Self),16)+' '+AName );{$ENDIF}
 end;
 end;
 
 
 destructor TPCCriticalSection.Destroy;
 destructor TPCCriticalSection.Destroy;

+ 3 - 3
src/core/UTxMultiOperation.pas

@@ -25,7 +25,7 @@ interface
 uses
 uses
   Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes, UEPasa,
   Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes, UEPasa,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes;
+  UPCDataTypes, UOrderedList;
 
 
 Type
 Type
 
 
@@ -122,7 +122,7 @@ Type
     function CheckSignatures(AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
     function CheckSignatures(AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
 
 
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     //
     //
     Function DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
     Function DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
@@ -763,7 +763,7 @@ begin
   Result := True;
   Result := True;
 end;
 end;
 
 
-procedure TOpMultiOperation.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpMultiOperation.AffectedAccounts(list: TOrderedList<Cardinal>);
 Var i : Integer;
 Var i : Integer;
   Procedure _doAdd(nAcc : Cardinal);
   Procedure _doAdd(nAcc : Cardinal);
   Begin
   Begin

+ 3 - 3
src/gui-classic/UFRMOperationsExplorer.pas

@@ -29,7 +29,7 @@ uses
   {$ENDIF}
   {$ENDIF}
   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
   Menus, ActnList, UAccounts, UBlockChain, UNode, UCrypto, UBaseTypes,
   Menus, ActnList, UAccounts, UBlockChain, UNode, UCrypto, UBaseTypes,
-  UFileStorage, UWallet, UConst, UTxMultiOperation, UOpTransaction, URPC,
+  UFileStorage, UWallet, UConst, UTxMultiOperation, UOpTransaction, URPC, UOrderedList,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UJSONFunctions;
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UJSONFunctions;
 
 
 
 
@@ -666,7 +666,7 @@ procedure TFRMOperationsExplorer.UpdateSelectedOperationInfo;
 Var op : TPCOperation;
 Var op : TPCOperation;
   opht : TOperationsHashTree;
   opht : TOperationsHashTree;
   i : Integer;
   i : Integer;
-  l : TList<Cardinal>;
+  l : TOrderedList<Cardinal>;
   aux : String;
   aux : String;
   raw : TRawBytes;
   raw : TRawBytes;
   ms : TMemoryStream;
   ms : TMemoryStream;
@@ -684,7 +684,7 @@ begin
     mOperationInfo.Lines.Add(Format('%s',[op.ToString]));
     mOperationInfo.Lines.Add(Format('%s',[op.ToString]));
     mOperationInfo.Lines.Add('');
     mOperationInfo.Lines.Add('');
     mOperationInfo.Lines.Add(Format('OpType:%d ClassName:%s Protocol:%d',[op.OpType,op.ClassName,op.ProtocolVersion]));
     mOperationInfo.Lines.Add(Format('OpType:%d ClassName:%s Protocol:%d',[op.OpType,op.ClassName,op.ProtocolVersion]));
-    l := TList<Cardinal>.Create;
+    l := TOrderedList<Cardinal>.Create(False,TComparison_Cardinal);
     Try
     Try
       op.AffectedAccounts(l); aux := '';
       op.AffectedAccounts(l); aux := '';
       For i:=0 to l.Count-1 do begin
       For i:=0 to l.Count-1 do begin

+ 4 - 0
src/gui-classic/UFRMPayloadDecoder.dfm

@@ -272,6 +272,10 @@ object FRMPayloadDecoder: TFRMPayloadDecoder
     object tsDecodeMethods: TTabSheet
     object tsDecodeMethods: TTabSheet
       Caption = 'Decode methods'
       Caption = 'Decode methods'
       ImageIndex = 1
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object lblPasswordsInfo: TLabel
       object lblPasswordsInfo: TLabel
         Left = 235
         Left = 235
         Top = 143
         Top = 143

+ 6 - 42
src/gui-classic/UFRMPayloadDecoder.pas

@@ -138,10 +138,7 @@ end;
 
 
 procedure TFRMPayloadDecoder.DoFind(Const OpHash : String);
 procedure TFRMPayloadDecoder.DoFind(Const OpHash : String);
 Var
 Var
-  r,md160 : TRawBytes;
-  pcops : TPCOperationsComp;
-  nBlock,nAccount,nN_Operation : Cardinal;
-  opbi : Integer;
+  r : TRawBytes;
   opr : TOperationResume;
   opr : TOperationResume;
   strings : TStrings;
   strings : TStrings;
   FRM : TFRMMemoText;
   FRM : TFRMMemoText;
@@ -156,46 +153,13 @@ begin
     if (Length(r)=0) then begin
     if (Length(r)=0) then begin
       raise Exception.Create('Value is not an hexadecimal string');
       raise Exception.Create('Value is not an hexadecimal string');
     end;
     end;
-    // Build 2.1.4 new decoder option: Check if OpHash is a posible double spend
-    If not TPCOperation.DecodeOperationHash(r,nBlock,nAccount,nN_Operation,md160) then begin
-      raise Exception.Create('Value is not a valid OPHASH because can''t extract Block/Account/N_Operation info');
-    end;
-    Case TNode.Node.FindNOperation(nBlock,nAccount,nN_Operation,opr) of
-      invalid_params : raise Exception.Create(Format('Not a valid OpHash searching at Block:%d Account:%d N_Operation:%d',[nBlock,nAccount,nN_Operation]));
-      blockchain_block_not_found : raise Exception.Create('Your blockchain file does not contain all blocks to find');
-      found : ;
+    case TNode.Node.FindOperation(r,opr) of
+      OpHash_invalid_params : raise Exception.Create(Format('Not a valid OpHash %s',[OpHash]));
+      OpHash_block_not_found : raise Exception.Create('Your blockchain file does not contain all blocks to find');
+      OpHash_found : ;
     else raise Exception.Create('ERROR DEV 20171120-6');
     else raise Exception.Create('ERROR DEV 20171120-6');
     end;
     end;
-    If (TPCOperation.EqualOperationHashes(opr.OperationHash,r)) Or
-       (TPCOperation.EqualOperationHashes(opr.OperationHash_OLD,r)) then begin
-      // Found!
-      OpResume := opr;
-    end else begin
-      // Not found!
-      strings := TStringList.Create;
-      try
-        strings.Add('Posible double spend detected!');
-        strings.Add(Format('OpHash: %s',[OpHash]));
-        strings.Add(Format('Decode OpHash info: Block:%d Account:%s N_Operation:%d',[nBlock,TAccountComp.AccountNumberToAccountTxtNumber(nAccount),nN_Operation]));
-        strings.Add('');
-        strings.Add('Real OpHash found in PascalCoin Blockchain:');
-        strings.Add(Format('OpHash: %s',[TCrypto.ToHexaString(opr.OperationHash)]));
-        strings.Add(Format('Decode OpHash info: Block:%d Account:%s N_Operation:%d',[opr.Block,TAccountComp.AccountNumberToAccountTxtNumber(opr.SignerAccount),opr.n_operation]));
-        If (opr.Block=0) then begin
-          strings.Add('* Note: This is a pending operation not included on Blockchain');
-        end;
-        OpResume := opr; // Do show operation resume!
-        FRM := TFRMMemoText.Create(Self);
-        try
-          FRM.InitData('Posible double spend detected',strings.Text);
-          FRM.ShowModal;
-        finally
-          FRM.Free;
-        end;
-      finally
-        strings.Free;
-      end;
-    end;
+    OpResume := opr;
   Except
   Except
     OpResume := CT_TOperationResume_NUL;
     OpResume := CT_TOperationResume_NUL;
     try
     try

+ 34 - 3
src/gui-classic/UFRMWallet.dfm

@@ -377,12 +377,16 @@ object FRMWallet: TFRMWallet
     Top = 91
     Top = 91
     Width = 865
     Width = 865
     Height = 466
     Height = 466
-    ActivePage = tsMyAccounts
+    ActivePage = tsMessages
     Align = alClient
     Align = alClient
     TabOrder = 2
     TabOrder = 2
     OnChange = PageControlChange
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
     object tsMyAccounts: TTabSheet
       Caption = 'Account Explorer'
       Caption = 'Account Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
       object Splitter1: TSplitter
         Left = 400
         Left = 400
         Top = 66
         Top = 66
@@ -620,6 +624,10 @@ object FRMWallet: TFRMWallet
         TabOrder = 2
         TabOrder = 2
         object tsAccountOperations: TTabSheet
         object tsAccountOperations: TTabSheet
           Caption = 'Account Operations'
           Caption = 'Account Operations'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
           object dgAccountOperations: TDrawGrid
             Left = 0
             Left = 0
             Top = 0
             Top = 0
@@ -633,6 +641,10 @@ object FRMWallet: TFRMWallet
         object tsMultiSelectAccounts: TTabSheet
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected Accounts For Batch Operation'
           Caption = 'Selected Accounts For Batch Operation'
           ImageIndex = 1
           ImageIndex = 1
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgSelectedAccounts: TDrawGrid
           object dgSelectedAccounts: TDrawGrid
             Left = 41
             Left = 41
             Top = 31
             Top = 31
@@ -824,6 +836,10 @@ object FRMWallet: TFRMWallet
     object tsPendingOperations: TTabSheet
     object tsPendingOperations: TTabSheet
       Caption = 'Pending Operations'
       Caption = 'Pending Operations'
       ImageIndex = 5
       ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object dgPendingOperations: TDrawGrid
       object dgPendingOperations: TDrawGrid
         Left = 0
         Left = 0
         Top = 86
         Top = 86
@@ -870,6 +886,10 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
     object tsBlockChain: TTabSheet
       Caption = 'Block Explorer'
       Caption = 'Block Explorer'
       ImageIndex = 1
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel2: TPanel
       object Panel2: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -962,6 +982,10 @@ object FRMWallet: TFRMWallet
     object tsOperations: TTabSheet
     object tsOperations: TTabSheet
       Caption = 'Operations Explorer'
       Caption = 'Operations Explorer'
       ImageIndex = 1
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel1: TPanel
       object Panel1: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1010,6 +1034,10 @@ object FRMWallet: TFRMWallet
     object tsLogs: TTabSheet
     object tsLogs: TTabSheet
       Caption = 'Logs'
       Caption = 'Logs'
       ImageIndex = 2
       ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object pnlTopLogs: TPanel
       object pnlTopLogs: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1041,6 +1069,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       Caption = 'Node Stats'
       ImageIndex = 3
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         857
         857
         438)
         438)
@@ -1188,8 +1220,7 @@ object FRMWallet: TFRMWallet
         Font.Name = 'Tahoma'
         Font.Name = 'Tahoma'
         Font.Style = []
         Font.Style = []
         Lines.Strings = (
         Lines.Strings = (
-          'dsfa '#195#177'ldsaf '#195#177'lk dasf'
-          'dsfklda'#195#177'fs '#195#177'l')
+          '(messages)')
         ParentFont = False
         ParentFont = False
         ReadOnly = True
         ReadOnly = True
         ScrollBars = ssBoth
         ScrollBars = ssBoth

+ 10 - 7
src/gui-classic/UFRMWallet.pas

@@ -243,11 +243,11 @@ type
     Procedure InitMenuForTesting;
     Procedure InitMenuForTesting;
     {$IFDEF TESTNET}
     {$IFDEF TESTNET}
     Procedure Test_RandomOperations(Sender: TObject);
     Procedure Test_RandomOperations(Sender: TObject);
+    Procedure Test_ConnectDisconnect(Sender: TObject);
+    {$ENDIF}
     {$IFDEF TESTING_NO_POW_CHECK}
     {$IFDEF TESTING_NO_POW_CHECK}
     Procedure Test_CreateABlock(Sender: TObject);
     Procedure Test_CreateABlock(Sender: TObject);
     {$ENDIF}
     {$ENDIF}
-    Procedure Test_ConnectDisconnect(Sender: TObject);
-    {$ENDIF}
     Procedure Test_ShowPublicKeys(Sender: TObject);
     Procedure Test_ShowPublicKeys(Sender: TObject);
     Procedure Test_ShowOperationsInMemory(Sender: TObject);
     Procedure Test_ShowOperationsInMemory(Sender: TObject);
     Procedure Test_FindAccountsForPrivateBuyOrSwapToMe(Sender : TObject);
     Procedure Test_FindAccountsForPrivateBuyOrSwapToMe(Sender : TObject);
@@ -452,9 +452,9 @@ begin
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
     FNode.Bank.StorageClass := TFileStorage;
-    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
-    TFileStorage(FNode.Bank.Storage).Initialize;
+    FNode.Bank.Storage.Initialize;
     // Init Grid
     // Init Grid
+
     FSelectedAccountsGrid.Node := FNode;
     FSelectedAccountsGrid.Node := FNode;
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FAccountsGrid.Node := FNode;
     FAccountsGrid.Node := FNode;
@@ -1421,6 +1421,9 @@ begin
   InitMacOSMenu;
   InitMacOSMenu;
   {$endif}
   {$endif}
   PageControl.ActivePageIndex := 0;
   PageControl.ActivePageIndex := 0;
+  {$IFDEF DEBUG}
+  System.ReportMemoryLeaksOnShutdown := True; // Delphi memory leaks testing
+  {$ENDIF}
 end;
 end;
 
 
 procedure TFRMWallet.ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
 procedure TFRMWallet.ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
@@ -1663,12 +1666,12 @@ begin
     if PageControl.ActivePage=tsOperations then begin
     if PageControl.ActivePage=tsOperations then begin
       i := FOperationsExplorerGrid.DrawGrid.Row;
       i := FOperationsExplorerGrid.DrawGrid.Row;
       if (i>0) and (i<=FOperationsExplorerGrid.OperationsResume.Count) then begin
       if (i>0) and (i<=FOperationsExplorerGrid.OperationsResume.Count) then begin
-        opr := FOperationsExplorerGrid.OperationsResume.OperationResume[i-1];
+        opr := FOperationsExplorerGrid.OperationsResume.Items[i-1];
       end;
       end;
     end else if PageControl.ActivePage=tsPendingOperations then begin
     end else if PageControl.ActivePage=tsPendingOperations then begin
       i := FPendingOperationsGrid.DrawGrid.Row;
       i := FPendingOperationsGrid.DrawGrid.Row;
       if (i>0) and (i<=FPendingOperationsGrid.OperationsResume.Count) then begin
       if (i>0) and (i<=FPendingOperationsGrid.OperationsResume.Count) then begin
-        opr := FPendingOperationsGrid.OperationsResume.OperationResume[i-1];
+        opr := FPendingOperationsGrid.OperationsResume.Items[i-1];
       end;
       end;
     end else if PageControl.ActivePage=tsMyAccounts then begin
     end else if PageControl.ActivePage=tsMyAccounts then begin
       accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
       accn := FAccountsGrid.AccountNumber(dgAccounts.Row);
@@ -1677,7 +1680,7 @@ begin
       title := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(accn)+' info';
       title := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(accn)+' info';
       i := FOperationsAccountGrid.DrawGrid.Row;
       i := FOperationsAccountGrid.DrawGrid.Row;
       if (i>0) and (i<=FOperationsAccountGrid.OperationsResume.Count) then begin
       if (i>0) and (i<=FOperationsAccountGrid.OperationsResume.Count) then begin
-        opr := FOperationsAccountGrid.OperationsResume.OperationResume[i-1];
+        opr := FOperationsAccountGrid.OperationsResume.Items[i-1];
       end;
       end;
     end;
     end;
     If (opr.valid) then begin
     If (opr.valid) then begin

+ 8 - 16
src/gui-classic/UGridUtils.pas

@@ -1065,7 +1065,7 @@ begin
         Finally
         Finally
           list.Free;
           list.Free;
         End;
         End;
-        ANode.GetStoredOperationsFromAccount(Self,AList,FOperationsGrid.AccountNumber,100,0,5000);
+        ANode.Bank.Storage.GetAccountOperations(FOperationsGrid.AccountNumber,-1,0,5000,0,AList);
       end;
       end;
     end;
     end;
   Finally
   Finally
@@ -1182,7 +1182,7 @@ begin
     DrawGrid.Canvas.FillRect(Rect);
     DrawGrid.Canvas.FillRect(Rect);
     InflateRect(Rect,-2,-1);
     InflateRect(Rect,-2,-1);
     if (ARow<=FOperationsResume.Count) then begin
     if (ARow<=FOperationsResume.Count) then begin
-      opr := FOperationsResume.OperationResume[ARow-1];
+      opr := FOperationsResume.Items[ARow-1];
       If (opr.AffectedAccount=opr.SignerAccount) then begin
       If (opr.AffectedAccount=opr.SignerAccount) then begin
       end else begin
       end else begin
         if (gdSelected in State) or (gdFocused in State) then begin
         if (gdSelected in State) or (gdFocused in State) then begin
@@ -1370,7 +1370,7 @@ begin
     Result := CT_TOperationResume_NUL;
     Result := CT_TOperationResume_NUL;
     exit;
     exit;
   end;
   end;
-  Result := FOperationsResume.OperationResume[FDrawGrid.Row-1];
+  Result := FOperationsResume.Items[FDrawGrid.Row-1];
 end;
 end;
 
 
 procedure TOperationsGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
 procedure TOperationsGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
@@ -1380,7 +1380,7 @@ Var i : Integer;
 begin
 begin
   if Not Assigned(FDrawGrid) then exit;
   if Not Assigned(FDrawGrid) then exit;
   if (FDrawGrid.Row<=0) Or (FDrawGrid.Row>FOperationsResume.Count) then exit;
   if (FDrawGrid.Row<=0) Or (FDrawGrid.Row>FOperationsResume.Count) then exit;
-  opr := FOperationsResume.OperationResume[FDrawGrid.Row-1];
+  opr := FOperationsResume.Items[FDrawGrid.Row-1];
   FRM := TFRMPayloadDecoder.Create(FDrawGrid.Owner);
   FRM := TFRMPayloadDecoder.Create(FDrawGrid.Owner);
   try
   try
     FRM.Init(opr,WalletKeys,AppParams);
     FRM.Init(opr,WalletKeys,AppParams);
@@ -1464,17 +1464,16 @@ begin
 end;
 end;
 
 
 procedure TBlockChainGridUpdateThread.DoUpdateBlockChainGrid(ANode: TNode; var AList: TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
 procedure TBlockChainGridUpdateThread.DoUpdateBlockChainGrid(ANode: TNode; var AList: TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
-Var opc : TPCOperationsComp;
+Var //opc : TPCOperationsComp;
   bcd : TBlockChainData;
   bcd : TBlockChainData;
   opb : TOperationBlock;
   opb : TOperationBlock;
   bn : TBigNum;
   bn : TBigNum;
 begin
 begin
-  opc := TPCOperationsComp.Create(Nil);
-  try
-    opc.bank := ANode.Bank;
     while (ABlockStart<=ABlockEnd) and (Not Terminated) do begin
     while (ABlockStart<=ABlockEnd) and (Not Terminated) do begin
       bcd := CT_TBlockChainData_NUL;
       bcd := CT_TBlockChainData_NUL;
-      opb := ANode.Bank.SafeBox.GetBlockInfo(ABlockEnd);
+      if Not ANode.Bank.Storage.GetBlockInformation(ABlockEnd,opb,bcd.OperationsCount,bcd.Volume) then begin
+        opb := ANode.Bank.SafeBox.GetBlockInfo(ABlockEnd);
+      end;
       bcd.Block:=opb.block;
       bcd.Block:=opb.block;
       bcd.Timestamp := opb.timestamp;
       bcd.Timestamp := opb.timestamp;
       bcd.BlockProtocolVersion := opb.protocol_version;
       bcd.BlockProtocolVersion := opb.protocol_version;
@@ -1500,10 +1499,6 @@ begin
       bcd.PoW := opb.proof_of_work;
       bcd.PoW := opb.proof_of_work;
       bcd.SafeBoxHash := opb.initial_safe_box_hash;
       bcd.SafeBoxHash := opb.initial_safe_box_hash;
       if (Not Terminated) then begin
       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.TimeAverage200:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,200);
         bcd.TimeAverage150:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,150);
         bcd.TimeAverage150:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,150);
         bcd.TimeAverage100:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,100);
         bcd.TimeAverage100:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,100);
@@ -1516,9 +1511,6 @@ begin
         if (ABlockEnd>0) then dec(ABlockEnd) else Break;
         if (ABlockEnd>0) then dec(ABlockEnd) else Break;
       end;
       end;
     end;
     end;
-  finally
-    opc.Free;
-  end;
 end;
 end;
 
 
 { TBlockChainGrid }
 { TBlockChainGrid }