Browse Source

Bug on TFileStorage.BlockExists

PascalCoin 7 years ago
parent
commit
088c08e3ed
2 changed files with 1077 additions and 1 deletions
  1. 2 1
      src/core/UFileStorage.pas
  2. 1075 0
      src/core/UFileStorage_.pas

+ 2 - 1
src/core/UFileStorage.pas

@@ -131,12 +131,13 @@ Var  iBlockHeaders : Integer;
   BlockHeader : TBlockHeader;
 begin
   Result := false;
+  BlockHeader := CT_TBlockHeader_NUL;
+  iBlockHeaders:=0; BlockHeaderFirstBlock:=0;
   stream := LockBlockChainStream;
   try
     if Not GetBlockHeaderFirstBytePosition(stream,Block,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
     if not StreamReadBlockHeader(stream,iBlockHeaders,BlockHeaderFirstBlock,Block,False,BlockHeader) then exit;
     Result := (BlockHeader.BlockNumber = Block) And
-        (((BlockHeader.BlockNumber MOD CT_GroupBlockSize)=0) OR (BlockHeader.StreamBlockRelStartPos>0)) And
         (BlockHeader.BlockSize>0);
   finally
     UnlockBlockChainStream;

+ 1075 - 0
src/core/UFileStorage_.pas

@@ -0,0 +1,1075 @@
+unit UFileStorage;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{ Copyright (c) 2016 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+uses
+  Classes, {$IFnDEF FPC}Windows,{$ENDIF} UBlockChain, SyncObjs, UThread, UAccounts, UCrypto;
+{$I config.inc}
+
+Type
+  TBlockHeader = Record
+    BlockNumber : Cardinal;
+    StreamBlockRelStartPos : Int64;
+    BlockSize : Cardinal;
+  end; // 16 bytes
+
+  TArrayOfInt64 = Array of Int64;
+
+  { TFileStorage }
+
+  TFileStorage = Class(TStorage)
+  private
+    FStorageLock : TPCCriticalSection;
+    FBlockChainStream : TFileStream;
+    FPendingBufferOperationsStream : TFileStream;
+    FStreamFirstBlockNumber : Int64;
+    FStreamLastBlockNumber : Int64;
+    FBlockHeadersFirstBytePosition : TArrayOfInt64;
+    FDatabaseFolder: AnsiString;
+    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;
+    Function GetFolder(Const AOrphan : TOrphan): AnsiString;
+    Function GetBlockHeaderFirstBytePosition(Stream : TStream; Block : Cardinal; CanInitialize : Boolean; var iBlockHeaders : Integer; var BlockHeaderFirstBlock : Cardinal) : Boolean;
+    Function GetBlockHeaderFixedSize : Int64;
+    procedure SetDatabaseFolder(const Value: AnsiString);
+    Procedure ClearStream;
+    Procedure GrowStreamUntilPos(Stream : TStream; newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
+    Function GetPendingBufferOperationsStream : TFileStream;
+  protected
+    procedure SetReadOnly(const Value: Boolean); override;
+    procedure SetOrphan(const Value: TOrphan); override;
+    Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
+    Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
+    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; override;
+    Function DoSaveBank : Boolean; override;
+    Function DoRestoreBank(max_block : Int64) : Boolean; override;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); override;
+    Function BlockExists(Block : Cardinal) : Boolean; override;
+    Function LockBlockChainStream : TFileStream;
+    Procedure UnlockBlockChainStream;
+    Function LoadBankFileInfo(Const Filename : AnsiString; var safeBoxHeader : TPCSafeBoxHeader) : Boolean;
+    function GetFirstBlockNumber: Int64; override;
+    function GetLastBlockNumber: Int64; override;
+    function DoInitialize : Boolean; override;
+    Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; override;
+    Procedure DoEraseStorage; override;
+    Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
+    Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
+  public
+    Constructor Create(AOwner : TComponent); Override;
+    Destructor Destroy; Override;
+    Class Function GetSafeboxCheckpointingFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+    Property DatabaseFolder : AnsiString read FDatabaseFolder write SetDatabaseFolder;
+    Procedure CopyConfiguration(Const CopyFrom : TStorage); override;
+    Procedure SetBlockChainFile(BlockChainFileName : AnsiString);
+    Function HasUpgradedToVersion2 : Boolean; override;
+    Procedure CleanupVersion1Data; override;
+  End;
+
+implementation
+
+Uses ULog, SysUtils, UConst;
+
+{ TFileStorage }
+
+Const CT_TBlockHeader_NUL : TBlockHeader = (BlockNumber:0;StreamBlockRelStartPos:0;BlockSize:0);
+
+  CT_GroupBlockSize = 1000;
+  CT_SizeOfBlockHeader = 16;
+  {
+  BlockChain file storage:
+
+  BlockHeader 0 -> From Block 0 to (CT_GroupBlockSize-1)
+    Foreach Block:
+      BlockNumber : 4 bytes
+      StreamBlockRelStartPos : 8 bytes  -> Start pos relative to End of BlockHeader
+      BlockSizeH : 4 bytes
+      -- Total size of BlockHeader: (4+8+4) * (CT_GroupBlockSize) = 16 * CT_GroupBlockSize
+    -- Note: If BlockHeader starts at pos X, it ends at pos X + (16*CT_GroupBlockSize)
+  Block 0
+    BlockSizeC: 4 bytes
+    Data: BlockSizeC bytes
+  Block 1
+    ...
+  Block CT_GroupBlockSize-1
+
+  BlockHeader 1 -> From Block CT_GroupBlockSize to ((CT_GroupBlockSize*2)-1)
+    (Same as BlockHeader 1)
+  Block CT_GroupBlockSize
+    ...
+  Block ((CT_GroupBlockSize*2)-1)
+
+  ...
+  BlockHeader X -> From (CT_GroupBlockSize*X) to ((CT_GroupBlockSize*(X+1))-1)
+  ...
+
+  }
+
+function TFileStorage.BlockExists(Block: Cardinal): Boolean;
+Var  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
+  stream : TStream;
+  BlockHeader : TBlockHeader;
+begin
+  Result := false;
+  stream := LockBlockChainStream;
+  try
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    if not StreamReadBlockHeader(stream,iBlockHeaders,BlockHeaderFirstBlock,Block,False,BlockHeader) then exit;
+    Result := (BlockHeader.BlockNumber = Block) And
+        (((BlockHeader.BlockNumber MOD CT_GroupBlockSize)=0) OR (BlockHeader.StreamBlockRelStartPos>0)) And
+        (BlockHeader.BlockSize>0);
+  finally
+    UnlockBlockChainStream;
+  end;
+end;
+
+procedure TFileStorage.ClearStream;
+begin
+  FreeAndNil(FBlockChainStream);
+  FreeAndNil(FPendingBufferOperationsStream);
+  FStreamFirstBlockNumber := 0;
+  FStreamLastBlockNumber := -1;
+  SetLength(FBlockHeadersFirstBytePosition,0);
+end;
+
+procedure TFileStorage.GrowStreamUntilPos(Stream : TStream; newPos: Int64; DeleteDataStartingAtCurrentPos: Boolean);
+Var null_buff : Array[1..CT_GroupBlockSize] of Byte;
+  i,antPos,antSize : Int64;
+begin
+  antPos := Stream.Position;
+  antSize := Stream.Size;
+  if Not DeleteDataStartingAtCurrentPos then begin
+    Stream.Position := Stream.Size;
+  end;
+  if (stream.Position<newPos) then begin
+    FillChar(null_buff,length(null_buff),0);
+    while (Stream.Position<newPos) do begin
+      i := newPos - Stream.Position;
+      if i>length(null_buff) then i := length(null_buff);
+      Stream.WriteBuffer(null_buff,i);
+    end;
+  end;
+  Stream.Position := newPos;
+end;
+
+function TFileStorage.GetPendingBufferOperationsStream: TFileStream;
+Var fs : TFileStream;
+  fn : TFileName;
+  fm : Word;
+begin
+  If Not Assigned(FPendingBufferOperationsStream) then begin
+    fn := GetFolder(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;
+  if CopyFrom is TFileStorage then begin
+    DatabaseFolder := TFileStorage(CopyFrom).DatabaseFolder;
+  end;
+end;
+
+constructor TFileStorage.Create(AOwner: TComponent);
+begin
+  inherited;
+  FDatabaseFolder := '';
+  FBlockChainFileName := '';
+  FBlockChainStream := Nil;
+  SetLength(FBlockHeadersFirstBytePosition,0);
+  FStreamFirstBlockNumber := 0;
+  FStreamLastBlockNumber := -1;
+  FPendingBufferOperationsStream := Nil;
+  FStorageLock := TPCCriticalSection.Create('TFileStorage_StorageLock');
+end;
+
+destructor TFileStorage.Destroy;
+begin
+  inherited;
+  ClearStream;
+  FreeAndNil(FStorageLock);
+end;
+
+procedure TFileStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
+Var stream : TStream;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
+  _Header : TBlockHeader;
+  _intBlockIndex : Cardinal;
+  p : Int64;
+begin
+  stream := LockBlockChainStream;
+  Try
+    if Not GetBlockHeaderFirstBytePosition(stream,StartingDeleteBlock,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    If Not StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,StartingDeleteBlock,True,_Header) then exit;
+    _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
+
+    TLog.NewLog(ltInfo,ClassName,Format('Deleting Blockchain block %d',[StartingDeleteBlock]));
+
+    p := FBlockHeadersFirstBytePosition[iBlockHeaders] + (Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader));
+
+    Stream.Position:=p;
+    // Write null data until end of header
+    GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[iBlockHeaders] + GetBlockHeaderFixedSize,true);
+    // Force to clean Block Headers future rows
+    SetLength(FBlockHeadersFirstBytePosition,iBlockHeaders+1); // Force to clear future blocks on next Block Headers row (Bug solved on 2.1.8)
+    FStreamLastBlockNumber:=Int64(StartingDeleteBlock)-1;
+    // End Stream at _Header
+    Stream.Size := Stream.Position + _Header.StreamBlockRelStartPos;
+  Finally
+    UnlockBlockChainStream;
+  End;
+end;
+
+function TFileStorage.DoInitialize: Boolean;
+Var stream : TStream;
+begin
+  stream := LockBlockChainStream;
+  Try
+    Result := true;
+  Finally
+    UnlockBlockChainStream;
+  End;
+end;
+
+function TFileStorage.DoCreateSafeBoxStream(blockCount: Cardinal): TStream;
+var fn : TFilename;
+  err : AnsiString;
+begin
+  Result := Nil;
+  fn := GetSafeboxCheckpointingFileName(GetFolder(Orphan),blockCount);
+  If (fn<>'') and (FileExists(fn)) then begin
+    Result := TFileStream.Create(fn,fmOpenRead+fmShareDenyWrite);
+  end;
+  If Not Assigned(Result) then begin
+    err := 'Cannot load SafeBoxStream (block:'+IntToStr(blockCount)+') file:'+fn;
+    TLog.NewLog(ltError,ClassName,err);
+  end;
+end;
+
+procedure TFileStorage.DoEraseStorage;
+Var stream : TStream;
+begin
+  stream := LockBlockChainStream;
+  try
+    stream.Size:=0; // Erase
+    ClearStream;
+  finally
+    UnlockBlockChainStream;
+  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);
+    TLog.NewLog(ltdebug,ClassName,Format('DoSavePendingBufferOperations operations:%d',[OperationsHashTree.OperationsCount]));
+  finally
+    UnlockBlockChainStream;
+  end;
+end;
+
+procedure TFileStorage.DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
+Var fs : TFileStream;
+  errors : AnsiString;
+  n : Integer;
+begin
+  LockBlockChainStream;
+  Try
+    fs := GetPendingBufferOperationsStream;
+    fs.Position:=0;
+    If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,CT_PROTOCOL_3,Nil,errors) then begin
+      TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
+    end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR: loaded operations:%d errors:%s',[OperationsHashTree.OperationsCount,errors]));
+  finally
+    UnlockBlockChainStream;
+  end;
+end;
+
+function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
+Var stream : TStream;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
+begin
+  Result := False;
+  stream := LockBlockChainStream;
+  Try
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,False,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockRead(stream,iBlockHeaders,BlockHeaderFirstBlock,Block,Operations);
+  Finally
+    UnlockBlockChainStream;
+  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 TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
+
+  Procedure DoCopySafebox;
+  var sr: TSearchRec;
+    FileAttrs: Integer;
+    folder : AnsiString;
+    sourcefn,destfn : AnsiString;
+  begin
+    FileAttrs := faArchive;
+    folder := GetFolder(Orphan);
+    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
+      repeat
+        if (sr.Attr and FileAttrs) = FileAttrs then begin
+          sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
+          destfn := GetFolder('')+PathDelim+sr.Name;
+          TLog.NewLog(ltInfo,ClassName,'Copying safebox file '+sourcefn+' to '+destfn);
+          Try
+            DoCopyFile(sourcefn,destfn);
+          Except
+            On E:Exception do begin
+              TLog.NewLog(ltError,Classname,'Error copying file: ('+E.ClassName+') '+E.Message);
+            end;
+          End;
+        end;
+      until FindNext(sr) <> 0;
+      FindClose(sr);
+    end;
+  End;
+
+Var db : TFileStorage;
+  i : Integer;
+  ops : TPCOperationsComp;
+  b : Cardinal;
+begin
+  Try
+    if (Assigned(DestStorage)) And (DestStorage is TFileStorage) then db := TFileStorage(DestStorage)
+    else db := Nil;
+    try
+      if Not assigned(db) then begin
+        db := TFileStorage.Create(Nil);
+        db.DatabaseFolder := Self.DatabaseFolder;
+        db.Bank := Self.Bank;
+        db.Orphan := DestOrphan;
+        db.FStreamFirstBlockNumber := Start_Block;
+      end;
+      if db is TFileStorage then TFileStorage(db).LockBlockChainStream;
+      try
+        db.FIsMovingBlockchain:=True;
+        ops := TPCOperationsComp.Create(Nil);
+        try
+          b := Start_Block;
+          while LoadBlockChainBlock(ops,b) do begin
+            inc(b);
+            TLog.NewLog(ltDebug,Classname,'Moving block from "'+Orphan+'" to "'+DestOrphan+'" '+TPCOperationsComp.OperationBlockToText(ops.OperationBlock));
+            db.SaveBlockChainBlock(ops);
+          end;
+          TLog.NewLog(ltdebug,Classname,'Moved blockchain from "'+Orphan+'" to "'+DestOrphan+'" from block '+inttostr(Start_Block)+' to '+inttostr(b-1));
+        finally
+          ops.Free;
+        end;
+        // If DestOrphan is empty, then copy possible updated safebox (because, perhaps current saved safebox is from invalid blockchain)
+        if (DestOrphan='') And (Orphan<>'') then begin
+          DoCopySafebox;
+        end;
+      finally
+        db.FIsMovingBlockchain:=False;
+        if db is TFileStorage then TFileStorage(db).UnlockBlockChainStream;
+      end;
+    Finally
+      If Not Assigned(DestStorage) then db.Free;
+    End;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,ClassName,'Error at DoMoveBlockChain: ('+E.ClassName+') '+E.Message);
+      Raise;
+    end;
+  End;
+end;
+
+function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
+var
+    sr: TSearchRec;
+    FileAttrs: Integer;
+    folder : AnsiString;
+    filename,auxfn : AnsiString;
+    fs : TFileStream;
+    ms : TMemoryStream;
+    errors : AnsiString;
+    blockscount : Cardinal;
+    sbHeader : TPCSafeBoxHeader;
+begin
+  LockBlockChainStream;
+  Try
+    FileAttrs := faArchive;
+    folder := GetFolder(Orphan);
+    filename := '';
+    blockscount := 0;
+    if SysUtils.FindFirst(folder+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
+      repeat
+        if (sr.Attr and FileAttrs) = FileAttrs then begin
+          auxfn := folder+PathDelim+sr.Name;
+          If LoadBankFileInfo(auxfn,sbHeader) then begin
+            if (((max_block<0) Or (sbHeader.blocksCount<=max_block)) AND (sbHeader.blocksCount>blockscount)) And
+              (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
+              filename := auxfn;
+              blockscount := sbHeader.blocksCount;
+            end;
+          end;
+        end;
+      until FindNext(sr) <> 0;
+      FindClose(sr);
+    end;
+    if (filename<>'') then begin
+      TLog.NewLog(ltinfo,Self.ClassName,'Loading SafeBox with '+inttostr(blockscount)+' blocks from file '+filename);
+      fs := TFileStream.Create(filename,fmOpenRead);
+      try
+        ms := TMemoryStream.Create;
+        Try
+          ms.CopyFrom(fs,0);
+          fs.Position := 0;
+          ms.Position := 0;
+          if not Bank.LoadBankFromStream(ms,False,errors) then begin
+            TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
+          end;
+        Finally
+          ms.Free;
+        End;
+      finally
+        fs.Free;
+      end;
+    end;
+  Finally
+    UnlockBlockChainStream;
+  End;
+end;
+
+function TFileStorage.DoSaveBank: Boolean;
+var fs: TFileStream;
+    bankfilename,aux_newfilename: AnsiString;
+    ms : TMemoryStream;
+begin
+  Result := true;
+  bankfilename := GetSafeboxCheckpointingFileName(GetFolder(Orphan),Bank.BlocksCount);
+  if (bankfilename<>'') then begin
+    TLog.NewLog(ltInfo,ClassName,'Saving Safebox blocks:'+IntToStr(Bank.BlocksCount)+' file:'+bankfilename);
+    fs := TFileStream.Create(bankfilename,fmCreate);
+    try
+      fs.Size := 0;
+      ms := TMemoryStream.Create;
+      try
+        Bank.SafeBox.SaveSafeBoxToAStream(ms,0,Bank.SafeBox.BlocksCount-1);
+        ms.Position := 0;
+        fs.Position := 0;
+        fs.CopyFrom(ms,0);
+      finally
+        ms.Free;
+      end;
+    finally
+      fs.Free;
+    end;
+    // Save a copy each 10000 blocks (aprox 1 month) only when not an orphan
+    if (Orphan='') And ((Bank.BlocksCount MOD (CT_BankToDiskEveryNBlocks*100))=0) then begin
+      aux_newfilename := GetFolder('') + PathDelim+'checkpoint_'+ inttostr(Bank.BlocksCount)+'.safebox';
+      try
+        {$IFDEF FPC}
+        DoCopyFile(bankfilename,aux_newfilename);
+        {$ELSE}
+        CopyFile(PWideChar(bankfilename),PWideChar(aux_newfilename),False);
+        {$ENDIF}
+      Except
+        On E:Exception do begin
+          TLog.NewLog(lterror,ClassName,'Exception copying extra safebox file '+aux_newfilename+' ('+E.ClassName+'):'+E.Message);
+        end;
+      end;
+    end;
+  end;
+end;
+
+function TFileStorage.DoSaveBlockChain(Operations: TPCOperationsComp): Boolean;
+Var stream : TStream;
+  iBlockHeaders : Integer;
+  BlockHeaderFirstBlock : Cardinal;
+begin
+  Result := False;
+  stream := LockBlockChainStream;
+  Try
+    if (Length(FBlockHeadersFirstBytePosition)=0) then begin
+      // Is saving first block on the stream?
+      if (Stream.Size=0) then begin
+        // Yes! Positioning
+        FStreamFirstBlockNumber := Operations.OperationBlock.block;
+      end;
+      TLog.NewLog(ltdebug,Classname,Format('Saving Block %d on a newer stream, stream first position=%d',[Operations.OperationBlock.block,FStreamFirstBlockNumber]));
+    end;
+    if Not GetBlockHeaderFirstBytePosition(stream,Operations.OperationBlock.block,True,iBlockHeaders,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockSave(stream,iBlockHeaders,BlockHeaderFirstBlock,Operations);
+  Finally
+    UnlockBlockChainStream;
+  End;
+  if Assigned(Bank) then SaveBank;
+end;
+
+Const CT_SafeboxsToStore = 10;
+
+class function TFileStorage.GetSafeboxCheckpointingFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
+begin
+  Result := '';
+  If not ForceDirectories(BaseDataFolder) then exit;
+  // We will store checkpointing
+  Result := BaseDataFolder + PathDelim+'checkpoint'+ inttostr((block DIV CT_BankToDiskEveryNBlocks) MOD CT_SafeboxsToStore)+'.safebox';
+end;
+
+function TFileStorage.GetBlockHeaderFirstBytePosition(Stream : TStream; Block: Cardinal; CanInitialize : Boolean; var iBlockHeaders : Integer; var BlockHeaderFirstBlock: Cardinal): Boolean;
+var iPos,start, nCurrBlock : Cardinal;
+  bh : TBlockHeader;
+  null_buff : Array[1..(CT_GroupBlockSize * CT_SizeOfBlockHeader)] of Byte;
+begin
+  Result := false;
+  if Block<FStreamFirstBlockNumber then begin
+    TLog.NewLog(lterror,Classname,Format('Block %d is lower than Stream First block %d',[Block,FStreamFirstBlockNumber]));
+    exit;
+  end;
+  iPos := (Block-FStreamFirstBlockNumber) DIV CT_GroupBlockSize;
+  if iPos>High(FBlockHeadersFirstBytePosition) then Begin
+    if Length(FBlockHeadersFirstBytePosition)>0 then begin
+      start := High(FBlockHeadersFirstBytePosition);
+    end else begin
+      If CanInitialize then begin
+        // Initialize and start at 0
+        SetLength(FBlockHeadersFirstBytePosition,1);
+        FBlockHeadersFirstBytePosition[0] := 0;
+        start := 0;
+      end else exit;
+    end;
+    while (start<iPos) do begin
+      // Read last start position
+      if (Stream.Size<(FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize)) then begin
+        // This position not exists...
+        If (CanInitialize) then begin
+          GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[start],false);
+          // Save BlockHeader values (initialized to 0)
+          FillChar(null_buff,length(null_buff),0);
+          Stream.WriteBuffer(null_buff,length(null_buff));
+        end else begin
+          // This is a Fatal error due must find previos block!
+          TLog.NewLog(ltError,Classname,Format('Stream size %d is lower than BlockHeader[%d] position %d + BlockHeaderSize %d',
+            [Stream.size,start,FBlockHeadersFirstBytePosition[start],GetBlockHeaderFixedSize]));
+          exit;
+        end;
+      end;
+      Stream.Position := FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize - CT_SizeOfBlockHeader;
+      // Read last saved Header
+      nCurrBlock := FStreamFirstBlockNumber + ((start+1) * CT_GroupBlockSize) - 1;
+      Repeat
+        Stream.Read(bh.BlockNumber,SizeOf(bh.BlockNumber));
+        Stream.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
+        Stream.Read(bh.BlockSize,sizeof(bh.BlockSize));
+        If (bh.BlockNumber<>nCurrBlock) then begin
+          if (bh.BlockNumber<>0) Or (bh.StreamBlockRelStartPos<>0) Or (bh.BlockSize<>0) then begin
+            TLog.NewLog(ltError,ClassName,Format('Fatal error. Found a Tblockheader with no 0 values searching for block:%d at nCurrBlock:%d - Number:%d RelStartPos:%d Size:%d',[block,nCurrBlock,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize]));
+            exit;
+          end;
+          if ((start=0) And (nCurrBlock>FStreamFirstBlockNumber))
+             Or
+             ((start>0) And (nCurrBlock>(FStreamFirstBlockNumber + ((start) * CT_GroupBlockSize)))) then begin
+            dec(nCurrBlock);
+            // Positioning for new read:
+            Stream.Seek(Int64(CT_SizeOfBlockHeader)*(-2),soFromCurrent);
+          end else begin
+            break; // End of blockheader!
+          end;
+        end;
+      until (bh.BlockNumber>0);
+      // Positioning!
+      Stream.Position := FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize;
+      //
+      SetLength(FBlockHeadersFirstBytePosition,length(FBlockHeadersFirstBytePosition)+1);
+      if bh.BlockNumber>0 then begin
+        FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position + bh.StreamBlockRelStartPos + bh.BlockSize;
+      end else begin
+        // Not found a block, starting at last pos
+        FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position;
+      end;
+      inc(start);
+
+      // Check if blockheader size is ok:
+      if (CanInitialize) And (Stream.Size<(FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize)) then begin
+        Stream.Position := FBlockHeadersFirstBytePosition[start];
+        TLog.NewLog(ltInfo,ClassName,Format('Increasing size for blockheader %d at pos:%d (current stream pos %d size %d) to position:%d',
+          [start,FBlockHeadersFirstBytePosition[start],Stream.Position,Stream.Size,
+          FBlockHeadersFirstBytePosition[start]+GetBlockHeaderFixedSize]));
+        GrowStreamUntilPos(Stream,FBlockHeadersFirstBytePosition[start]+GetBlockHeaderFixedSize,true);
+      end;
+
+    end;
+  End;
+  iBlockHeaders := iPos;
+  BlockHeaderFirstBlock := FStreamFirstBlockNumber + (iPos * CT_GroupBlockSize);
+  Result := true;
+end;
+
+function TFileStorage.GetBlockHeaderFixedSize: Int64;
+begin
+  Result := (CT_GroupBlockSize* CT_SizeOfBlockHeader);
+end;
+
+function TFileStorage.GetFirstBlockNumber: Int64;
+begin
+  Result := FStreamFirstBlockNumber;
+end;
+
+function TFileStorage.GetFolder(const AOrphan: TOrphan): AnsiString;
+begin
+  if FDatabaseFolder = '' then raise Exception.Create('No Database Folder');
+  if AOrphan<>'' then Result := FDatabaseFolder + PathDelim+AOrphan
+  else Result := FDatabaseFolder;
+  if not ForceDirectories(Result) then raise Exception.Create('Cannot create database folder: '+Result);
+end;
+
+function TFileStorage.GetLastBlockNumber: Int64;
+begin
+  Result := FStreamLastBlockNumber;
+end;
+
+function TFileStorage.LoadBankFileInfo(const Filename: AnsiString; var safeBoxHeader : TPCSafeBoxHeader) : Boolean;
+var fs: TFileStream;
+begin
+  Result := false;
+  safeBoxHeader := CT_PCSafeBoxHeader_NUL;
+  If Not FileExists(Filename) then exit;
+  fs := TFileStream.Create(Filename,fmOpenRead);
+  try
+    fs.Position:=0;
+    Result := Bank.SafeBox.LoadSafeBoxStreamHeader(fs,safeBoxHeader);
+  finally
+    fs.Free;
+  end;
+end;
+
+function TFileStorage.LockBlockChainStream: TFileStream;
+  function InitStreamInfo(Stream : TStream; var errors : String) : Boolean;
+  Var mem : TStream;
+    iPos : Int64;
+    i,j,k : Integer;
+    bh,lastbh : TBlockHeader;
+  begin
+    errors := '';
+    FStreamFirstBlockNumber := 0;
+    FStreamLastBlockNumber := -1;
+    SetLength(FBlockHeadersFirstBytePosition,0);
+    Result := False;
+    //
+    if stream.Size<GetBlockHeaderFixedSize then begin
+      if (stream.Size=0) then begin
+        Result := true;
+        exit;
+      end else begin
+        // Invalid stream!
+        if (ReadOnly) then begin
+          errors := Format('Invalid stream size %d. Lower than minimum %d',[stream.Size, GetBlockHeaderFixedSize]);
+          exit;
+        end else begin
+          // Clear it
+          TLog.NewLog(ltError,ClassName,Format('Invalid stream size %d. Lower than minimum %d - Initialized to 0',[stream.Size, GetBlockHeaderFixedSize]));
+          stream.size := 0;
+          Result := True;
+          Exit;
+        end;
+      end;
+    end;
+    // Initialize it
+    if stream.Size>GetBlockHeaderFixedSize then begin
+      SetLength(FBlockHeadersFirstBytePosition,1);
+      FBlockHeadersFirstBytePosition[0] := 0;
+    end;
+    mem := TMemoryStream.Create;
+    Try
+      iPos := 0;
+      while (iPos + GetBlockHeaderFixedSize < Stream.Size) do begin
+        Stream.Position := iPos;
+        mem.Size := 0;
+        mem.CopyFrom(Stream,GetBlockHeaderFixedSize);
+        // Analize it:
+        mem.Position := 0;
+        for i := 0 to CT_GroupBlockSize-1 do begin
+          mem.Read(bh.BlockNumber,SizeOf(bh.BlockNumber));
+          mem.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
+          mem.Read(bh.BlockSize,sizeof(bh.BlockSize));
+          if (i=0) And (iPos=0) then begin
+            FStreamFirstBlockNumber := bh.BlockNumber;
+            FStreamLastBlockNumber := bh.BlockNumber;
+            if (0<>bh.StreamBlockRelStartPos) then begin
+              errors := Format('Invalid BlockChain stream. First block start rel pos %d',[bh.StreamBlockRelStartPos]);
+              if (ReadOnly) then begin
+                Exit;
+              end else begin
+                FStreamFirstBlockNumber := 0;
+                FStreamLastBlockNumber := -1;
+                SetLength(FBlockHeadersFirstBytePosition,0);
+                stream.Size:=0; // Set size to 0, no data
+                TLog.NewLog(ltError,ClassName,Format('%s - Initialized to 0',[errors]));
+                Result := True;
+              end;
+              Exit;
+            end;
+            lastbh := bh;
+          end else begin
+            // Protocol 2: We can find blocks not saved, with all values to 0
+            if (bh.BlockNumber=0) then begin
+              // This is an "empty" block. Check that ok
+              If (bh.BlockNumber<>0) Or (bh.StreamBlockRelStartPos<>0) Or (bh.BlockSize<>0) then begin
+                errors := Format('Invalid BlockChain stream not empty block on block header. iPos=%d i=%d BlockNumber=%d relstart=%d size=%d - Last block:%d BlockNumber=%d relstart=%d size=%d',
+                  [iPos,i,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize,
+                   FStreamLastBlockNumber,
+                   lastbh.BlockNumber,lastbh.StreamBlockRelStartPos,lastbh.BlockSize]);
+                if (ReadOnly) then begin
+                  Exit;
+                end else begin
+                  TLog.NewLog(lterror,ClassName,Format('%s - Initialized to %d',[errors,FStreamFirstBlockNumber]));
+                  DoDeleteBlockChainBlocks(FStreamLastBlockNumber+1);
+                  Result := True;
+                  Exit;
+                end;
+              end;
+              // Ok, inc blocknumber
+              inc(lastbh.BlockNumber);
+            end else begin
+              if (lastbh.BlockNumber+1<>bh.BlockNumber) or
+                ((lastbh.StreamBlockRelStartPos+lastbh.BlockSize<>bh.StreamBlockRelStartPos) And (i>0)) Or
+                ((0<>bh.StreamBlockRelStartPos) And (i=0)) then begin
+                errors := Format('Invalid BlockChain stream on block header. iPos=%d i=%d BlockNumber=%d relstart=%d size=%d - Last block:%d BlockNumber=%d relstart=%d size=%d',
+                  [iPos,i,bh.BlockNumber,bh.StreamBlockRelStartPos,bh.BlockSize,FStreamLastBlockNumber,
+                   lastbh.BlockNumber,lastbh.StreamBlockRelStartPos,lastbh.BlockSize]);
+                If (ReadOnly) then begin
+                  Exit;
+                end else begin
+                  TLog.NewLog(lterror,ClassName,Format('%s - Initialized to %d',[errors,FStreamFirstBlockNumber]));
+                  DoDeleteBlockChainBlocks(FStreamLastBlockNumber+1);
+                  Result := True;
+                  Exit;
+                end;
+              end else begin
+                FStreamLastBlockNumber := bh.BlockNumber;
+                lastbh := bh;
+              end;
+            end;
+          end;
+        end;
+        iPos := iPos + GetBlockHeaderFixedSize + lastbh.StreamBlockRelStartPos + lastBh.BlockSize;
+        lastbh.StreamBlockRelStartPos:=0;
+        lastbh.BlockSize:=0;
+      end;
+      Result := True;
+    Finally
+      mem.Free;
+    End;
+  end;
+
+Var fn : TFileName;
+  fm : Word;
+  exists : Boolean;
+  bh : TBlockHeader;
+  errors : String;
+begin
+  TPCThread.ProtectEnterCriticalSection(Self,FStorageLock);
+  Try
+    if Not Assigned(FBlockChainStream) then begin
+      if FBlockChainFileName<>'' then begin
+        fn := FBlockChainFileName
+      end else begin
+        fn := GetFolder(Orphan)+PathDelim+'BlockChainStream.blocks';
+      end;
+      exists := FileExists(fn);
+      if ReadOnly then begin
+        if exists then fm := fmOpenRead+fmShareDenyNone
+        else raise Exception.Create('FileStorage not exists for open ReadOnly: '+fn);
+      end else begin
+        if exists then fm := fmOpenReadWrite+fmShareDenyWrite
+        else fm := fmCreate+fmShareDenyWrite
+      end;
+      FBlockChainStream := TFileStream.Create(fn,fm);
+      // Init stream
+      If Not InitStreamInfo(FBlockChainStream,errors) then begin
+        TLog.NewLog(lterror,ClassName,errors);
+        raise Exception.Create('Error reading File: '+fn+#10+'Errors:'+#10+errors);
+      end else begin
+        TLog.NewLog(ltInfo,ClassName,Format('Loaded blockchain file: %s with blocks from %d to %d',[fn,FStreamFirstBlockNumber,FStreamLastBlockNumber]));
+      end;
+    end;
+  Except
+    FStorageLock.Release;
+    Raise;
+  End;
+  Result := FBlockChainStream;
+end;
+
+procedure TFileStorage.SetBlockChainFile(BlockChainFileName: AnsiString);
+begin
+  ClearStream;
+  FBlockChainFileName := BlockChainFileName;
+end;
+
+procedure TFileStorage.SetDatabaseFolder(const Value: AnsiString);
+begin
+  if FDatabaseFolder=Value then exit;
+  FDatabaseFolder := Value;
+  ClearStream;
+end;
+
+procedure TFileStorage.SetOrphan(const Value: TOrphan);
+begin
+  inherited;
+  ClearStream;
+end;
+
+procedure TFileStorage.SetReadOnly(const Value: Boolean);
+begin
+  inherited;
+  ClearStream;
+end;
+
+function TFileStorage.StreamBlockRead(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
+Var p : Int64;
+  errors : AnsiString;
+  streamFirstBlock,
+  _BlockSizeC,
+  _intBlockIndex : Cardinal;
+  _Header : TBlockHeader;
+  _ops : TStream;
+  _StreamBlockHeaderStartPos : Int64;
+begin
+  Result := False;
+  If Not StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,Block,False,_Header) then exit;
+
+  // Calculating block position
+  _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+  p := (_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
+     (_Header.StreamBlockRelStartPos);
+  if Stream.Size<(p + _Header.BlockSize) then begin
+    TLog.NewLog(ltError,Classname,Format(
+      'Invalid stream size. Block %d need to be at relative %d after %d = %d BlockSize:%d (Size %d)',
+      [Block,_Header.StreamBlockRelStartPos,(_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize),p,_Header.BlockSize,Stream.Size]));
+    exit;
+  end;
+  Stream.Position := p;
+  // Read the block
+  // Reading size
+  Stream.Read(_BlockSizeC,sizeof(_BlockSizeC));
+  if ((_BlockSizeC+sizeof(_BlockSizeC))>(_Header.BlockSize)) then begin
+    TLog.NewLog(lterror,Classname,Format('Corruption at stream Block size. Block %d SizeH:%d SizeC:%d',[Block,
+      _Header.BlockSize,_BlockSizeC]));
+    exit;
+  end;
+  // Reading Block
+  _ops := TMemoryStream.Create;
+  try
+    _ops.CopyFrom(Stream,_BlockSizeC);
+    _ops.Position := 0;
+    If Not Operations.LoadBlockFromStorage(_ops,errors) then begin
+      TLog.NewLog(lterror,Classname,'Error reading OperationBlock '+inttostr(Block)+' from stream. Errors: '+errors);
+      exit;
+    end;
+    Result := true;
+  Finally
+    _ops.Free;
+  end;
+end;
+
+
+function TFileStorage.StreamBlockSave(Stream : TStream; iBlockHeaders : Integer; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
+Var p : Int64;
+  c : Cardinal;
+  _Header, _HeaderPrevious : TBlockHeader;
+  _intBlockIndex : Cardinal;
+  _ops : TStream;
+  _StreamBlockHeaderStartPos : Int64;
+  {$IFDEF HIGHLOG}s : String;{$ENDIF}
+begin
+  Result := false;
+  _Header := CT_TBlockHeader_NUL;
+  _Header.BlockNumber := Operations.OperationBlock.block;
+  if BlockHeaderFirstBlock>_Header.BlockNumber then raise Exception.Create('Dev error 20160917-3')
+  else if BlockHeaderFirstBlock<_Header.BlockNumber then begin
+    Result := StreamReadBlockHeader(Stream,iBlockHeaders,BlockHeaderFirstBlock,_Header.BlockNumber-1,True,_HeaderPrevious);
+    // If true then Stream is positioned on blockheader for current block
+    if not Result then begin
+      raise Exception.Create('Cannot found header of previous block '+inttostr(Operations.OperationBlock.block));
+    end;
+    If ((_Header.BlockNumber-BlockHeaderFirstBlock) MOD CT_GroupBlockSize)=0 then begin
+      _Header.StreamBlockRelStartPos := 0;
+    end else begin
+      _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
+    end;
+  end else begin
+    // First block of the stream
+    Result := true;
+    _Header.StreamBlockRelStartPos := 0;
+  end;
+  _ops := TMemoryStream.Create;
+  Try
+    Operations.SaveBlockToStorage(_ops);
+    _Header.BlockSize := _ops.Size;
+    // Positioning until Header Position to save Header data
+    _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
+    p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
+    _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+    {$IFDEF HIGHLOG}s := Format('Saving block header (block %d) at position %d',[_Header.BlockNumber,Stream.Position]);{$ENDIF}
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + p,false);
+    // Save Header
+    Stream.Write(_Header.BlockNumber,sizeof(_Header.BlockNumber));
+    Stream.Write(_Header.StreamBlockRelStartPos,sizeof(_Header.StreamBlockRelStartPos));
+    c := _Header.BlockSize + sizeof(c);
+    Stream.Write(c,sizeof(_Header.BlockSize));
+    // Positioning until Header end
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
+    // If this is an override, force to clean Block Headers future rows
+    SetLength(FBlockHeadersFirstBytePosition,iBlockHeaders+1); // Force to clear future blocks on next Block Headers row (Bug solved on 2.1.8)
+    // And now positioning until Data:
+    GrowStreamUntilPos(Stream,_StreamBlockHeaderStartPos + GetBlockHeaderFixedSize + _Header.StreamBlockRelStartPos, false );
+    {$IFDEF HIGHLOG}
+    s := s + Format(' saving content at position %d (size %d)',[Stream.Position,_Header.BlockSize]);
+    TLog.NewLog(ltInfo,ClassName,s);
+    {$ENDIF}
+    // Save stream size
+    Stream.Write(_Header.BlockSize,sizeof(_Header.BlockSize));
+    // Save Data
+    _ops.Position := 0;
+    Stream.CopyFrom(_ops,_ops.Size);
+    // End Stream here
+    Stream.Size := Stream.Position;
+    //
+    FStreamLastBlockNumber := Operations.OperationBlock.block;
+  Finally
+    _ops.Free;
+  end;
+end;
+
+function TFileStorage.StreamReadBlockHeader(Stream: TStream;
+  iBlockHeaders : Integer; BlockHeaderFirstBlock, Block: Cardinal;
+  CanSearchBackward : Boolean;
+  var BlockHeader: TBlockHeader): Boolean;
+Var iBlock : Cardinal;
+  _iBlockHeaders : Integer;
+  _StreamBlockHeaderStartPos: Int64;
+  _BlockHeaderFirstBlock : Cardinal;
+begin
+  Result := false;
+  BlockHeader := CT_TBlockHeader_NUL;
+  if (BlockHeaderFirstBlock>Block) then raise Exception.Create('Dev error 20160917-1');
+  if (BlockHeaderFirstBlock+CT_GroupBlockSize)<Block then raise Exception.Create('Dev error 20160917-2');
+  _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[iBlockHeaders];
+  if Stream.Size< (_StreamBlockHeaderStartPos + (GetBlockHeaderFixedSize)) then begin
+    // Not log... it's normal when finding block
+    TLog.NewLog(ltError,Classname,Format('Invalid stream size %d < (%d + %d) Reading block %d',[Stream.Size,_StreamBlockHeaderStartPos,GetBlockHeaderFixedSize,Block]));
+    exit;
+  end;
+  iBlock := Block;
+  _iBlockHeaders:=iBlockHeaders;
+  _BlockHeaderFirstBlock:=BlockHeaderFirstBlock;
+  // Reading block header
+  Repeat
+    _StreamBlockHeaderStartPos:=FBlockHeadersFirstBytePosition[_iBlockHeaders];
+    Stream.Position := _StreamBlockHeaderStartPos + (CT_SizeOfBlockHeader*(iBlock-_BlockHeaderFirstBlock));
+    If Stream.Read(BlockHeader.BlockNumber,sizeof(BlockHeader.BlockNumber))<sizeof(BlockHeader.BlockNumber) then exit;
+    If Stream.Read(BlockHeader.StreamBlockRelStartPos,sizeof(BlockHeader.StreamBlockRelStartPos))<sizeof(BlockHeader.StreamBlockRelStartPos) then exit;
+    If Stream.Read(BlockHeader.BlockSize,sizeof(BlockHeader.BlockSize))<sizeof(BlockHeader.BlockSize) then exit;
+    Result := (BlockHeader.BlockNumber = iBlock);
+    If (Not Result) And (CanSearchBackward) then begin
+      If (iBlock>_BlockHeaderFirstBlock) then dec(iBlock)
+      else begin
+        // Search on previous header...
+        If (iBlockHeaders>0) then begin
+          dec(_iBlockHeaders);
+          dec(_BlockHeaderFirstBlock,CT_GroupBlockSize);
+        end else begin
+          break;
+        end;
+      end;
+    end;
+  Until Result Or (Not CanSearchBackward);
+  // Positioning
+  Stream.Position := FBlockHeadersFirstBytePosition[iBlockHeaders] + (CT_SizeOfBlockHeader*(Block+1-BlockHeaderFirstBlock));
+  If Result then begin // For Backward searching...
+    BlockHeader.BlockNumber:=Block;
+    If (_iBlockHeaders<>iBlockHeaders) then begin
+      BlockHeader.BlockSize:=0;
+      BlockHeader.StreamBlockRelStartPos:=0;
+    end;
+  end;
+end;
+
+procedure TFileStorage.UnlockBlockChainStream;
+begin
+  FStorageLock.Release;
+end;
+
+function TFileStorage.HasUpgradedToVersion2: Boolean;
+var searchRec: TSearchRec;
+begin
+  HasUpgradedToVersion2 := SysUtils.FindFirst( GetFolder(Orphan)+PathDelim+'*.safebox', faArchive, searchRec) = 0;
+  FindClose(searchRec);
+end;
+
+procedure TFileStorage.CleanupVersion1Data;
+var
+  folder : AnsiString;
+  searchRec : TSearchRec;
+begin
+  folder := GetFolder(Orphan);
+  if SysUtils.FindFirst( folder+PathDelim+'*.bank', faArchive, searchRec) = 0 then
+  repeat
+    SysUtils.DeleteFile(folder+PathDelim+searchRec.Name);
+  until FindNext(searchRec) <> 0;
+  FindClose(searchRec);
+end;
+
+end.