ソースを参照

Merge pull request #62 from PascalCoinDev/master

Fixed compilers errors by adding new improvements
Albert Molina 3 年 前
コミット
82d535b97f

+ 353 - 105
src/core/UBlockChain.pas

@@ -28,7 +28,7 @@ uses
   Classes,{$IFnDEF FPC}Windows,{$ENDIF}UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
-  UPCDataTypes, UChunk;
+  UPCDataTypes, UChunk, UOrderedList;
 
 {
 
@@ -113,6 +113,7 @@ uses
 }
 
 Type
+  TSearchOpHashResult = (OpHash_found, OpHash_invalid_params, OpHash_block_not_found);
   // 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 );
   TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
@@ -204,19 +205,8 @@ Type
   TPCOperation = Class;
   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;
   TOpReferenceArray = Array of TopReference;
@@ -244,7 +234,7 @@ Type
     property ProtocolVersion : Word read FProtocolVersion;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; virtual;
     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 OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     Function GetDigestToSign : TRawBytes; virtual; abstract;
@@ -489,9 +479,12 @@ Type
   private
     FBank : TPCBank;
     FReadOnly: Boolean;
+    FPendingBufferOperationsStream : TFileStream;
     procedure SetBank(const Value: TPCBank);
+    Function GetPendingBufferOperationsStream : TFileStream;
   protected
     FIsMovingBlockchain : Boolean;
+    FStorageFilename: String;
     procedure SetReadOnly(const Value: Boolean); virtual;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
@@ -502,15 +495,19 @@ Type
     function GetLastBlockNumber: Int64; virtual; abstract;
     function DoInitialize:Boolean; 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 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
     Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Constructor Create(AOwner : TComponent); Override;
+    Destructor Destroy; override;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     Property Bank : TPCBank read FBank write SetBank;
     Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
@@ -523,7 +520,11 @@ Type
     Function BlockExists(Block : Cardinal) : Boolean;
 
     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;
 
   TStorageClass = Class of TStorage;
@@ -1063,6 +1064,25 @@ begin
   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;
 var fs: TFileStream;
     LBankfilename,Laux_newfilename: AnsiString;
@@ -1102,7 +1122,7 @@ begin
       Laux_newfilename := GetStorageFolder('') + PathDelim+'checkpoint_'+ inttostr(BlocksCount)+CT_Safebox_Extension;
       try
         {$IFDEF FPC}
-        DoCopyFile(bankfilename,aux_newfilename);
+        DoCopyFile(LBankfilename,Laux_newfilename);
         {$ELSE}
         CopyFile(PWideChar(LBankfilename),PWideChar(Laux_newfilename),False);
         {$ENDIF}
@@ -2764,13 +2784,13 @@ end;
 function TOperationsHashTree.GetOperationsAffectingAccount(account_number: Cardinal; List: TList<Cardinal>): Integer;
   // This function retrieves operations from HashTree that affeccts to an account_number
 Var l : TList<Pointer>;
-  intl : TList<Cardinal>;
+  intl : TOrderedList<Cardinal>;
   i,j : Integer;
 begin
   List.Clear;
   l := FHashTreeOperations.LockList;
   try
-    intl := TList<Cardinal>.Create;
+    intl := TOrderedList<Cardinal>.Create(False,TComparison_Cardinal);
     try
       for i := 0 to l.Count - 1 do begin
         intl.Clear;
@@ -3246,6 +3266,7 @@ end;
 
 procedure TStorage.CopyConfiguration(const CopyFrom: TStorage);
 begin
+  ReadOnly := CopyFrom.ReadOnly;
 end;
 
 constructor TStorage.Create(AOwner: TComponent);
@@ -3253,6 +3274,8 @@ begin
   inherited;
   FReadOnly := false;
   FIsMovingBlockchain := False;
+  FPendingBufferOperationsStream := Nil;
+  FStorageFilename := '';
 end;
 
 procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
@@ -3261,6 +3284,198 @@ begin
   DoDeleteBlockChainBlocks(StartingDeleteBlock);
 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;
   var AOperationBlock: TOperationBlock; var AOperationsCount: Integer;
   var AVolume: Int64): Boolean;
@@ -3271,6 +3486,30 @@ begin
   AVolume := 0;
   //
   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
     if Not LoadBlockChainBlock(LPCOperations,ABlock) then begin
       Exit(False);
@@ -3278,11 +3517,51 @@ begin
     AOperationBlock := LPCOperations.OperationBlock.GetCopy;
     AOperationsCount := LPCOperations.Count;
     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
     LPCOperations.Free;
   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;
 begin
   Result := DoInitialize;
@@ -3294,11 +3573,60 @@ begin
   DoEraseStorage;
 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 AVolume: Int64): Boolean;
 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;
 
 procedure TStorage.SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
@@ -4074,9 +4402,9 @@ begin
 end;
 
 function TPCOperation.IsAffectedAccount(account: Cardinal): Boolean;
-Var l : TList<Cardinal>;
+Var l : TOrderedList<Cardinal>;
 begin
-  l := TList<Cardinal>.Create;
+  l := TOrderedList<Cardinal>.Create(False,TComparison_Cardinal);
   Try
     AffectedAccounts(l);
     Result := (l.IndexOf(account)>=0);
@@ -4124,86 +4452,6 @@ begin
   Result := 0;
 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
   SetLength(_OperationsClass, 0);
   RegisterOperationsClass;

+ 5 - 70
src/core/UFileStorage.pas

@@ -43,11 +43,9 @@ Type
   private
     FStorageLock : TPCCriticalSection;
     FBlockChainStream : TFileStream;
-    FPendingBufferOperationsStream : TFileStream;
     FStreamFirstBlockNumber : Int64;
     FStreamLastBlockNumber : Int64;
     FBlockHeadersFirstBytePosition : TArrayOfInt64;
-    FBlockChainFileName : AnsiString;
     Function StreamReadBlockHeader(Stream: TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal; CanSearchBackward : Boolean; var BlockHeader : TBlockHeader): Boolean;
     Function StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
@@ -55,7 +53,6 @@ Type
     Function GetBlockHeaderFixedSize : Int64;
     Procedure ClearStream;
     Procedure GrowStreamUntilPos(Stream : TStream; newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
-    Function GetPendingBufferOperationsStream : TFileStream;
   protected
     procedure SetReadOnly(const Value: Boolean); override;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
@@ -69,8 +66,6 @@ Type
     function GetLastBlockNumber: Int64; override;
     function DoInitialize : Boolean; override;
     Procedure DoEraseStorage; override;
-    Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
-    Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
   public
     Constructor Create(AOwner : TComponent); Override;
     Destructor Destroy; Override;
@@ -146,7 +141,6 @@ end;
 procedure TFileStorage.ClearStream;
 begin
   FreeAndNil(FBlockChainStream);
-  FreeAndNil(FPendingBufferOperationsStream);
   FStreamFirstBlockNumber := 0;
   FStreamLastBlockNumber := -1;
   SetLength(FBlockHeadersFirstBytePosition,0);
@@ -172,27 +166,6 @@ begin
   Stream.Position := newPos;
 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);
 begin
   inherited;
@@ -201,12 +174,10 @@ end;
 constructor TFileStorage.Create(AOwner: TComponent);
 begin
   inherited;
-  FBlockChainFileName := '';
   FBlockChainStream := Nil;
   SetLength(FBlockHeadersFirstBytePosition,0);
   FStreamFirstBlockNumber := 0;
   FStreamLastBlockNumber := -1;
-  FPendingBufferOperationsStream := Nil;
   FStorageLock := TPCCriticalSection.Create('TFileStorage_StorageLock');
 end;
 
@@ -271,43 +242,6 @@ begin
   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;
 Var stream : TStream;
   iBlockHeaders : Integer;
@@ -353,7 +287,7 @@ begin
     try
       db.Bank := Self.Bank;
       db.FStreamFirstBlockNumber := Start_Block;
-      db.FBlockChainFileName := TPCBank.GetStorageFolder(DestOrphan)+PathDelim+'BlockChainStream.blocks';
+      db.FStorageFilename := TPCBank.GetStorageFolder(DestOrphan)+PathDelim+'BlockChainStream.blocks';
       db.LockBlockChainStream;
       try
         db.FIsMovingBlockchain:=True;
@@ -642,10 +576,11 @@ begin
   TPCThread.ProtectEnterCriticalSection(Self,FStorageLock);
   Try
     if Not Assigned(FBlockChainStream) then begin
-      if FBlockChainFileName<>'' then begin
-        fn := FBlockChainFileName
+      if FStorageFilename<>'' then begin
+        fn := FStorageFilename
       end else begin
         fn := TPCBank.GetStorageFolder(Orphan)+PathDelim+'BlockChainStream.blocks';
+        FStorageFilename := fn;
       end;
       exists := FileExists(fn);
       if ReadOnly then begin
@@ -674,7 +609,7 @@ end;
 procedure TFileStorage.SetBlockChainFile(BlockChainFileName: AnsiString);
 begin
   ClearStream;
-  FBlockChainFileName := BlockChainFileName;
+  FStorageFilename := BlockChainFileName;
 end;
 
 procedure TFileStorage.SetReadOnly(const Value: Boolean);

+ 7 - 6
src/core/UNetProtocol.pas

@@ -1659,8 +1659,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     try
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.Orphan := TNode.Node.Bank.Orphan;
-      Bank.Storage.ReadOnly := true;
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
+      Bank.Storage.ReadOnly := true;
 
 
       if start_block>=0 then begin
@@ -1671,6 +1671,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
           IsUsingSnapshot := True;
 
           Bank.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+          Bank.Storage.StorageFilename := '';
           Bank.Storage.ReadOnly := false;
 
         end else begin
@@ -4459,7 +4460,7 @@ begin
         nOpsToSend := Operations.OperationsCount;
       end;
       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;
         try
           request_id := TNetData.NetData.NewRequestId;
@@ -5166,7 +5167,7 @@ begin
     inc(P^.counter);
     inc(FTotalCounter);
     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
     FTimesList.UnlockList;
   end;
@@ -5241,9 +5242,9 @@ begin
       Dec(FTotalCounter);
     end;
     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
     FTimesList.UnlockList;
   end;

+ 83 - 233
src/core/UNode.pas

@@ -44,10 +44,20 @@ Type
 
   { TNode }
 
-  TSearchOperationResult = (found, invalid_params, blockchain_block_not_found);
-
   TNodeNotifyEvents = Class;
 
+  TNode = Class;
+
+  TSaveMempoolOperationsThread = Class(TPCThread)
+  private
+    FNode : TNode;
+    FPendingToSave : Boolean;
+  protected
+    procedure BCExecute; override;
+  public
+    procedure Touch;
+  End;
+
   TNode = Class(TComponent)
   private
     FNodeLog : TLog;
@@ -65,6 +75,7 @@ Type
     FBroadcastData : Boolean;
     FUpdateBlockchain: Boolean;
     FMaxPayToKeyPurchasePrice: Int64;
+    FSaveMempoolOperationsThread : TSaveMempoolOperationsThread;
     {$IFDEF BufferOfFutureOperations}
     FBufferAuxWaitingOperations : TOperationsHashTree;
     {$ENDIF}
@@ -101,12 +112,9 @@ Type
     //
     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 AutoDiscoverNodes(Const ips : String);
@@ -313,7 +321,7 @@ begin
               FSentOperations.SetTag(resendOp.Sha256,LLockedMempool.OperationBlock.block); // Set tag new value
               FSentOperations.Add(LLockedMempool.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
             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;
             inc(i);
           end;
@@ -583,20 +591,14 @@ begin
         end;
       end; // for i
       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);
         if LTickCount=0 then LTickCount:=1;
         if Assigned(SenderConnection) then begin
           s := SenderConnection.ClientRemoteAddr;
         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
           // Send to other nodes
           j := TNetData.NetData.ConnectionsCountAll;
@@ -669,6 +671,9 @@ begin
   {$ENDIF}
   FBroadcastData := True;
   FUpdateBlockchain := True;
+  FSaveMempoolOperationsThread := TSaveMempoolOperationsThread.Create(True);
+  FSaveMempoolOperationsThread.FNode := Self;
+  FSaveMempoolOperationsThread.Resume;
   if Not Assigned(_Node) then _Node := Self;
 end;
 
@@ -728,6 +733,11 @@ Var step : String;
 begin
   TLog.NewLog(ltInfo,ClassName,'TNode.Destroy START');
   Try
+    step := 'Deleting SaveMempoolOperationsThread';
+    FSaveMempoolOperationsThread.Terminate;
+    FSaveMempoolOperationsThread.WaitFor;
+    FreeAndNil(FSaveMempoolOperationsThread);
+
     step := 'Deleting critical section';
     FreeAndNil(FLockMempool);
     FreeAndNil(FOperationSequenceLock);
@@ -1078,148 +1088,8 @@ begin
   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;
-  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
 var oprl : TOperationsResumeList;
 begin
@@ -1227,14 +1097,14 @@ begin
   try
     Result := FindNOperations(account,block,block=0,n_operation,n_operation,oprl);
     If oprl.Count>0 then begin
-      OpResume := oprl.OperationResume[0];
+      OpResume := oprl.Items[0];
     end else OpResume := CT_TOperationResume_NUL;
   finally
     oprl.Free;
   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;
   op : TPCOperation;
   aux_block, block : Cardinal;
@@ -1244,7 +1114,7 @@ var i : Integer;
   LLockedMempool : TPCOperationsComp;
 begin
   OpResumeList.Clear;
-  Result := invalid_params;
+  Result := OpHash_invalid_params;
   block := start_block;
   If (block>=Bank.BlocksCount) then exit; // Invalid block number
   If (account>=Bank.AccountsCount) then exit; // Invalid account number
@@ -1272,7 +1142,7 @@ begin
             OpResumeList.Add(opr);
             if (n_operation>n_operation_low) then dec(n_operation)
             else begin
-              Result := found;
+              Result := OpHash_found;
               Exit;
             end;
           end;
@@ -1289,7 +1159,7 @@ begin
     While (n_operation>0) And (n_operation>=n_operation_low) And (block>0) do begin
       aux_block := block;
       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;
       end;
       For i:=OperationComp.Count-1 downto 0 do begin
@@ -1305,12 +1175,12 @@ begin
             OpResumeList.Add(opr);
             if (n_operation>n_operation_low) then dec(n_operation)
             else begin
-              Result := found;
+              Result := OpHash_found;
               Exit;
             end;
           end else 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
             end;
           end;
@@ -1326,7 +1196,7 @@ begin
   finally
     OperationComp.Free;
   end;
-  Result := found;
+  Result := OpHash_found;
 end;
 
 procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
@@ -1348,40 +1218,36 @@ begin
   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 }
-var account,n_operation : Cardinal;
+var
   i : Integer;
   op : TPCOperation;
-  initial_block, aux_block, aux_n_op : Cardinal;
-  opHashValid, opHash_OLD : TRawBytes;
+  opHashValid : TRawBytes;
   md160 : TRawBytes;
   LLockedMempool : TPCOperationsComp;
+  LBlock, LAccount, LN_Operation : Cardinal;
 begin
-  Result := invalid_params;
+  Result := OpHash_invalid_params;
   // 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 begin
+  if (LBlock=0) then begin
     LLockedMempool := LockMempoolRead;
     Try
       LLockedMempool.Lock;
       Try
         For i:=0 to LLockedMempool.Count-1 do begin
           op := LLockedMempool.Operation[i];
-          If (op.SignerAccount=account) then begin
+          If (op.SignerAccount=LAccount) then begin
             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;
             end;
           end;
@@ -1392,55 +1258,8 @@ begin
     Finally
       UnlockMempoolRead;
     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;
-
-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;
 
 procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: String);
@@ -1848,6 +1667,37 @@ begin
   inherited;
 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
   _Node := Nil;
   _PascalCoinDataFolder := '';

+ 13 - 13
src/core/UOpTransaction.pas

@@ -27,7 +27,7 @@ interface
 
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes, UEPasa;
+  UPCDataTypes, UEPasa, UOrderedList;
 
 Type
   // Operations Type
@@ -91,7 +91,7 @@ Type
   public
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; 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;
     function OperationAmount : Int64; override;
@@ -132,7 +132,7 @@ Type
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); 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);
     Property Data : TOpChangeKeyData read FData;
@@ -171,7 +171,7 @@ Type
     function SignerAccount : Cardinal; override;
     function N_Operation : Cardinal; 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);
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
@@ -243,7 +243,7 @@ Type
     function DestinationAccount : Int64; override;
     function SellerAccount : Int64; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
@@ -297,7 +297,7 @@ Type
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
       account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
@@ -349,7 +349,7 @@ Type
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); 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);
     Property Data : TOpDataData read FData;
@@ -655,7 +655,7 @@ begin
   Result := FData.n_operation;
 end;
 
-procedure TOpChangeAccountInfo.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeAccountInfo.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -774,7 +774,7 @@ end;
 
 { TOpTransaction }
 
-procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpTransaction.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.sender);
   list.Add(FData.target);
@@ -1392,7 +1392,7 @@ end;
 
 { TOpChangeKey }
 
-procedure TOpChangeKey.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeKey.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -1739,7 +1739,7 @@ end;
 
 { TOpRecoverFounds }
 
-procedure TOpRecoverFounds.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpRecoverFounds.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.account);
 end;
@@ -1930,7 +1930,7 @@ end;
 
 { TOpListAccount }
 
-procedure TOpListAccount.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpListAccount.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.account_signer);
   if FData.account_signer<>FData.account_target then
@@ -2817,7 +2817,7 @@ begin
   Result := FData.n_operation;
 end;
 
-procedure TOpData.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpData.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
   list.Add(FData.account_signer);
   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);
   end;
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
 
   DoInit(LIsNewStructure);
@@ -746,8 +745,7 @@ procedure TPCAbstractMem.SetMaxMemUsage(const Value: Integer);
 begin
   FMaxMemUsage := Value;
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
 end;
 
@@ -801,8 +799,7 @@ begin
     FAbstractMem := TMem.Create(0,LReadOnly);
   end;
   if FAbstractMem is TFileMem then begin
-    TFileMem(FAbstractMem).MaxCacheSize := FMaxMemUsage;
-    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+    TFileMem(FAbstractMem).SetCachePerformance(True,1024,FMaxMemUsage,200000);
   end;
   DoInit(Ltmp);
 end;
@@ -980,7 +977,7 @@ begin
   Lani.accountName := AName;
   Lani.accountNumber := AAccountNumber;
   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]))
     else raise EPCAbstractMem.Create(Format('Cannot add account(%d) name %s because used by %d with %s',[AAccountNumber,AName,
       Lani.accountNumber,Lani.accountName]));
@@ -1012,7 +1009,7 @@ var Lani : TAccountNameInfo;
 begin
   Lani.accountName := AName;
   Lani.accountNumber := 0;
-  Result := FindData(Lani,AAbstractMemPosition);
+  Result := FindDataPos(Lani,AAbstractMemPosition);
 end;
 
 { TPCAbstractMemListBlocks }

+ 4 - 0
src/core/UPCOperationsSignatureValidator.pas

@@ -325,6 +325,9 @@ begin
     LOperation := FValidator.GetNextOperation(Self);
     if Assigned(LOperation) 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)
         try
           LIsValid := LOperation.IsValidSignatureBasedOnCurrentSafeboxState(FValidator.FSafeBoxTransaction);
@@ -334,6 +337,7 @@ begin
             TLog.NewLog(lterror,ClassName,LOperation.ToString+' ERROR: ('+E.ClassName+') '+E.Message);
           end;
         end;
+        {$ENDIF}
         FValidator.SetOperationCheckResult(Self,LOperation, LIsValid);
       end;
     end;

+ 1 - 1
src/core/UPCRPCOpData.pas

@@ -343,7 +343,7 @@ begin
     LResultArray := AJSONResponse.GetAsArray('result');
 
     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,
         LResultArray.GetAsObject( LResultArray.Count ));
     end;

+ 277 - 121
src/core/URPC.pas

@@ -24,6 +24,8 @@ interface
 
 {$I ./../config.inc}
 
+{$DEFINE RPC_PROTECT_MASSIVE_CALLS}
+
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UJSONFunctions, classes, blcksock, synsock,
@@ -52,6 +54,7 @@ Const
   CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
+  CT_RPC_ErrNum_MaxCalls = 1022;
 
 
 Type
@@ -162,6 +165,7 @@ Type
     class procedure RegisterProcessMethod(Const AMethodName : String; ARPCProcessMethod : TRPCProcessMethod);
     class procedure UnregisterProcessMethod(Const AMethodName : String);
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
+    class procedure ProcessMethodCalled(Const AMethodName : String; AStartTickCount : TTickCount);
   end;
 
 implementation
@@ -171,17 +175,27 @@ Uses
   SysUtils, Synautil,
   UEPasaDecoder,
   UPCRPCSend,
+  UOrderedList,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
 
 Type
   TRegisteredRPCProcessMethod = Record
     MethodName : String;
     RPCProcessMethod : TRPCProcessMethod;
+    CallsCounter : Integer;
+    ElapsedMilis : Int64;
+    procedure Clear;
   end;
+  PRegisteredRPCProcessMethod = ^TRegisteredRPCProcessMethod;
 
 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 }
 
@@ -292,7 +306,7 @@ Begin
   end;
   if OPR.valid then begin
     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;
     if (OPR.Block>0) And (OPR.Block<currentNodeBlocksCount) then
       jsonObject.GetAsVariant('maturation').Value := currentNodeBlocksCount - OPR.Block - 1
@@ -1032,19 +1046,40 @@ end;
 
 class function TRPCProcess.FindRegisteredProcessMethod(const AMethodName: String): TRPCProcessMethod;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
   Result := Nil;
   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;
-    inc(i);
-  end;
+  Finally
+    Dispose(P);
+  End;
 end;
 
 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
   timeout: integer;
   s: string;
@@ -1054,10 +1089,10 @@ var
   resultcode: integer;
   inputdata : TRawBytes;
   js,jsresult : TPCJSONData;
-  jsonobj,jsonresponse : TPCJSONObject;
+  jsonobj,jsonresponse, paramsJSON : TPCJSONObject;
   errNum : Integer; errDesc : String;
   jsonrequesttxt,
-  jsonresponsetxt, methodName, paramsTxt : String;
+  jsonresponsetxt, methodName, paramsTxt, senderIP : String;
   valid : Boolean;
   i : Integer;
   Headers : TStringList;
@@ -1066,6 +1101,7 @@ var
   LOnStartLiveConnectionCount : Integer;
 begin
   LOnStartLiveConnectionCount := FRPCServer.FLiveConnectionsCount;
+  senderIP := '';
   callcounter := _RPCServer.GetNewCallCounter;
   tc := TPlatform.GetTickCount;
   methodName := '';
@@ -1139,9 +1175,27 @@ begin
             errDesc := '';
             try
               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}
-              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 (errNum<>0) or (errDesc<>'') then begin
                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
@@ -1151,6 +1205,8 @@ begin
                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
                 end;
               end;
+
+              end;
             Except
               on E:Exception do begin
                 TLog.NewLog(lterror,Classname,'Exception processing method'+methodName+' ('+E.ClassName+'): '+E.Message);
@@ -1197,8 +1253,14 @@ begin
           FSock.SendString(jsonresponsetxt);
         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);
+      TRPCProcess.ProcessMethodCalled(methodName,tc);
     finally
       jsonresponse.free;
       Headers.Free;
@@ -1284,6 +1346,68 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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);
   Begin
     TPascalCoinJSONComp.FillOperationObject(OPR,FNode.Bank.BlocksCount,
@@ -1340,7 +1464,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       if (nCounter<maxReg) then begin
         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;
       for i:=0 to OperationsResume.Count-1 do begin
         Obj := jsonArray.GetAsObject(jsonArray.Count);
@@ -1434,6 +1558,38 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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
   // 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;
@@ -2552,7 +2708,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   function FindNOperations : Boolean;
   Var oprl : TOperationsResumeList;
     start_block, account, n_operation_min, n_operation_max : Cardinal;
-    sor : TSearchOperationResult;
+    sor : TSearchOpHashResult;
     jsonarr : TPCJSONArray;
     i : Integer;
   begin
@@ -2575,13 +2731,13 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       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);
       Case sor of
-        found : Result := True;
-        invalid_params : begin
+        OpHash_found : Result := True;
+        OpHash_invalid_params : begin
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorDesc:='Not found using block/account/n_operation';
             exit;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             ErrorNum := CT_RPC_ErrNum_InvalidBlock;
             ErrorDesc:='Blockchain file does not contain all blocks to find';
             exit;
@@ -2591,7 +2747,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       jsonarr := jsonresponse.GetAsArray('result');
       if oprl.Count>0 then 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;
     finally
@@ -3299,81 +3455,15 @@ begin
     // Param "block" contains block. Null = Pending operation
     // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
     // Returns a JSON object with operation values as "Operation resume format"
-    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
     // Param "block" contains block
     // 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
     // Returns all the operations affecting an account in "Operation resume format" as an array
     // Param "account" contains account number
@@ -3446,36 +3536,30 @@ begin
       ErrorDesc:='param ophash not found or invalid hexadecimal value "'+params.AsString('ophash','')+'"';
       exit;
     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
     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;
             ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
             exit;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             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;
           end;
       else Raise Exception.Create('ERROR DEV 20171120-4');
       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
       FillOperationResumeToJSONObject(opr,GetResultObject);
       Result := True;
-    finally
-      pcops.Free;
-    end;
     finally
       FNode.OperationSequenceLock.Release;
     end;
@@ -3489,13 +3573,13 @@ begin
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     try
     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;
           ErrorDesc:='Not found using block/account/n_operation';
           exit;
         end;
-      blockchain_block_not_found : begin
+      OpHash_block_not_found : begin
           ErrorNum := CT_RPC_ErrNum_InvalidBlock;
           ErrorDesc:='Blockchain file does not contain all blocks to find';
           exit;
@@ -3973,6 +4057,14 @@ begin
     end;
     Get_node_ip_stats;
     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
     LRPCProcessMethod := FindRegisteredProcessMethod(method);
     if Assigned(LRPCProcessMethod) then begin
@@ -3984,26 +4076,77 @@ begin
   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);
-var LRegistered : TRegisteredRPCProcessMethod;
+var
+  P, PFound : PRegisteredRPCProcessMethod;
+  i : Integer;
 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;
-  if Assigned(FindRegisteredProcessMethod(AMethodName)) then Exit; // Duplicated!
-  LRegistered.MethodName := AMethodName;
-  LRegistered.RPCProcessMethod := ARPCProcessMethod;
-  _RPCProcessMethods.Add(LRegistered);
 end;
 
 class procedure TRPCProcess.UnregisterProcessMethod(const AMethodName: String);
-var i : Integer;
+var
+  P : PRegisteredRPCProcessMethod;
+  i : Integer;
 begin
   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;
 
@@ -4055,16 +4198,29 @@ end;
 
 procedure DoFinalize;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
   if Assigned(_RPCProcessMethods) then begin
     for i := _RPCProcessMethods.Count-1 downto 0 do begin
+      P := _RPCProcessMethods.Get(i);
       _RPCProcessMethods.Delete(i);
+      Dispose(P);
     end;
   end;
   FreeAndNil(_RPCProcessMethods);
   FreeAndNil(_RPCServer);
 end;
 
+{ TRegisteredRPCProcessMethod }
+
+procedure TRegisteredRPCProcessMethod.Clear;
+begin
+  Self.MethodName := '';
+  Self.RPCProcessMethod := Nil;
+  Self.CallsCounter := 0;
+  Self.ElapsedMilis := 0;
+end;
+
 initialization
 finalization
   DoFinalize;

+ 16 - 12
src/core/UThread.pas

@@ -184,7 +184,7 @@ Var _threads : TPCThreadList<TPCThread>;
 constructor TPCThread.Create(CreateSuspended: Boolean);
 begin
   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;
 
 destructor TPCThread.Destroy;
@@ -205,7 +205,7 @@ begin
   FDebugStep := '';
   i := _threads.Add(Self);
   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
         BCExecute;
@@ -347,9 +347,9 @@ begin
     if lockStartedTimestamp=0 then tc3 := 0
     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',
-      [IntToHex(PtrInt(Lock),8),Lock.Name,
+      [IntToHex(PtrInt(Lock),16),Lock.Name,
       Sender.ClassName,tc2-tc,
-      IntToHex(lockCurrThread,8)+'-'+IntToHex(Lock.CurrentThread,8),
+      IntToHex(lockCurrThread,16)+'-'+IntToHex(Lock.CurrentThread,16),
       lockWatingForCounter,Lock.WaitingForCounter,
       tc3
       ]);
@@ -435,9 +435,11 @@ end;
 {$IFDEF HIGHLOG}
 procedure TPCCriticalSection.Acquire;
 Var continue, logged : Boolean;
-  startTC : TTickCount;
+  startTC, LLastTC : TTickCount;
+  LWaitMillis : Int64;
 begin
   startTC := TPlatform.GetTickCount;
+  LLastTC := startTC;
   FCounterLock.Acquire;
   try
     FWaitingForCounter := FWaitingForCounter + 1;
@@ -445,21 +447,23 @@ begin
     FCounterLock.Release;
   end;
   logged := false;
+  LWaitMillis := 1000;
   Repeat
     continue := inherited TryEnter;
     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;
-        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)));
-        continue := true;
-        inherited;
       end else sleep(1);
+      sleep(1);
     end;
   Until continue;
   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;
   FCounterLock.Acquire;
   try
@@ -480,7 +484,7 @@ begin
   FStartedTickCount := 0;
   FName := AName;
   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;
 
 destructor TPCCriticalSection.Destroy;

+ 3 - 3
src/core/UTxMultiOperation.pas

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

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

@@ -29,7 +29,7 @@ uses
   {$ENDIF}
   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
   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;
 
 
@@ -666,7 +666,7 @@ procedure TFRMOperationsExplorer.UpdateSelectedOperationInfo;
 Var op : TPCOperation;
   opht : TOperationsHashTree;
   i : Integer;
-  l : TList<Cardinal>;
+  l : TOrderedList<Cardinal>;
   aux : String;
   raw : TRawBytes;
   ms : TMemoryStream;
@@ -684,7 +684,7 @@ begin
     mOperationInfo.Lines.Add(Format('%s',[op.ToString]));
     mOperationInfo.Lines.Add('');
     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
       op.AffectedAccounts(l); aux := '';
       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
       Caption = 'Decode methods'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object lblPasswordsInfo: TLabel
         Left = 235
         Top = 143

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

@@ -138,10 +138,7 @@ end;
 
 procedure TFRMPayloadDecoder.DoFind(Const OpHash : String);
 Var
-  r,md160 : TRawBytes;
-  pcops : TPCOperationsComp;
-  nBlock,nAccount,nN_Operation : Cardinal;
-  opbi : Integer;
+  r : TRawBytes;
   opr : TOperationResume;
   strings : TStrings;
   FRM : TFRMMemoText;
@@ -156,46 +153,13 @@ begin
     if (Length(r)=0) then begin
       raise Exception.Create('Value is not an hexadecimal string');
     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');
     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
     OpResume := CT_TOperationResume_NUL;
     try

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

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

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

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

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

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