Browse Source

Improvements on TStorage to allow better performance and AbstractMem library usage

Pascal Coin 3 years ago
parent
commit
de07cad441
3 changed files with 441 additions and 408 deletions
  1. 353 105
      src/core/UBlockChain.pas
  2. 5 70
      src/core/UFileStorage.pas
  3. 83 233
      src/core/UNode.pas

+ 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);

+ 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 := '';