Browse Source

USE_ABSTRACTMEM - Compiler directive

Added USE_ABSTRACTMEM compiler directive to activate usage of ABSTRAMEM library and caching or file saving mechanism
PascalCoin 5 years ago
parent
commit
689f8c1b4f

+ 1 - 1
src/core/UAccountKeyStorage.pas

@@ -7,7 +7,7 @@ unit UAccountKeyStorage;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, UAccounts, UThread, UBaseTypes,
+  Classes, SysUtils, UAccounts, UThread, UBaseTypes, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type

File diff suppressed because it is too large
+ 281 - 190
src/core/UAccounts.pas


+ 120 - 1
src/core/UBaseTypes.pas

@@ -62,8 +62,18 @@ Type
     procedure FromString(const AValue : String); // Will store a RAW bytes assuming each char of the string is a byte -> ALERT: Do not use when the String contains chars encoded with multibyte character set!
     procedure FromString(const AValue : String); // Will store a RAW bytes assuming each char of the string is a byte -> ALERT: Do not use when the String contains chars encoded with multibyte character set!
     function Add(const ARawValue : TRawBytes) : TRawBytes; // Will concat a new RawBytes value to current value
     function Add(const ARawValue : TRawBytes) : TRawBytes; // Will concat a new RawBytes value to current value
     function IsEmpty : Boolean; // Will return TRUE when Length = 0
     function IsEmpty : Boolean; // Will return TRUE when Length = 0
+    function IsEqualTo(const ACompareTo : TRawBytes) : Boolean;
+    //
     procedure FromStream(AStream : TStream); overload;
     procedure FromStream(AStream : TStream); overload;
     procedure FromStream(AStream : TStream; AStartPos, ALength : Integer); overload;
     procedure FromStream(AStream : TStream; AStartPos, ALength : Integer); overload;
+    //
+    procedure SaveInsideTBytes(var ADestToWrite: TBytes; var AStartPosition: integer);    // AStartPosition will update to next position
+    function LoadFromTBytes(const ASource: TBytes; var AStartPosition: integer): boolean; // AStartPosition will update to next position
+    function ToSerialized : TBytes; overload;
+    procedure ToSerialized(const AStream : TStream); overload;
+    function FromSerialized(const ASerialized : TBytes; ACheckLength : Integer = 0) : Boolean; overload;
+    function FromSerialized(const AStream : TStream; ACheckLength : Integer = 0) : Integer; overload;
+    function GetSerializedLength: integer; // 2 + Length
   end;
   end;
 
 
 
 
@@ -91,8 +101,10 @@ Type
     function Compare(ABytesBuffer : TBytesBuffer) : Integer;
     function Compare(ABytesBuffer : TBytesBuffer) : Integer;
     procedure SetLength(ANewLength : Integer);
     procedure SetLength(ANewLength : Integer);
     function Memory : Pointer;
     function Memory : Pointer;
+    function MemoryLength : Integer;
     procedure Clear;
     procedure Clear;
     procedure CopyFrom(ABytesBuffer : TBytesBuffer);
     procedure CopyFrom(ABytesBuffer : TBytesBuffer);
+    function Capture(AStartPos, ALength : Integer) : TBytes;
   end;
   end;
 
 
 
 
@@ -195,11 +207,94 @@ begin
   end else Result := '';
   end else Result := '';
 end;
 end;
 
 
+function TRawBytesHelper.FromSerialized(const ASerialized: TBytes; ACheckLength : Integer = 0): Boolean;
+var Lsize: integer;
+begin
+  if (Length(ASerialized)<2) then Exit(False);
+  Lsize := 0;
+  Move(ASerialized[0],Lsize,2);
+  if (2 + Lsize > Length(ASerialized)) then Exit(False);
+  SetLength(Self,Lsize);
+  Move(ASerialized[2],Self[0],Lsize);
+  Result := True;
+end;
+
+function TRawBytesHelper.FromSerialized(const AStream: TStream; ACheckLength : Integer = 0): Integer;
+Var w: Word;
+begin
+  if AStream.Size - AStream.Position < 2 then begin
+    SetLength(Self,0);
+    Result := -1;
+    Exit;
+  end;
+  AStream.Read(w, 2);
+  if (AStream.Size - AStream.Position < w) OR ((ACheckLength > 0) AND (w <> ACheckLength)) then begin
+    AStream.Position := AStream.Position - 2; // Go back!
+    SetLength(Self,0);
+    Result := -1;
+    Exit;
+  end;
+  SetLength(Self, w);
+  if (w>0) then begin
+    AStream.ReadBuffer(Self[0], w);
+  end;
+  Result := w+2;
+end;
+
 procedure TRawBytesHelper.FromStream(AStream: TStream; AStartPos, ALength: Integer);
 procedure TRawBytesHelper.FromStream(AStream: TStream; AStartPos, ALength: Integer);
 begin
 begin
   System.SetLength(Self,ALength);
   System.SetLength(Self,ALength);
   AStream.Position := AStartPos;
   AStream.Position := AStartPos;
-  AStream.Read(Self,ALength);
+  AStream.Read(Self[0],ALength);
+end;
+
+procedure TRawBytesHelper.SaveInsideTBytes(var ADestToWrite: TBytes; var AStartPosition: integer);
+var Lsize: integer;
+begin
+  if (AStartPosition + Length(Self) + 2) > Length(ADestToWrite) then begin
+    SetLength(ADestToWrite,AStartPosition + Length(Self) + 2);
+  end;
+  Lsize := Length(Self);
+  Move(Lsize, ADestToWrite[AStartPosition], 2);
+  Move(Self[0], ADestToWrite[AStartPosition + 2], Lsize);
+  Inc(AStartPosition, 2 + Lsize);
+end;
+
+function TRawBytesHelper.LoadFromTBytes(const ASource: TBytes; var AStartPosition: integer): boolean;
+var Lsize: integer;
+begin
+  Lsize := 0;
+  if (AStartPosition + 2 > Length(ASource)) then Exit(False);
+  Move(ASource[AStartPosition],Lsize,2);
+  if (AStartPosition + 2 + Lsize > Length(ASource)) then Exit(False);
+  SetLength(Self,Lsize);
+  Move(ASource[AStartPosition + 2],Self[0],Lsize);
+  inc(AStartPosition, 2 + Lsize);
+  Result := True;
+end;
+
+function TRawBytesHelper.ToSerialized: TBytes;
+var Lsize: integer;
+begin
+  LSize := Length(Self);
+  if LSize>65536 then raise Exception.Create('Cannot serialize TBytes due high length '+IntToStr(Length(Self)));
+  SetLength(Result, LSize + 2);
+  Move(Lsize, Result[0], 2);
+  Move(Self[0], Result[2], Lsize);
+end;
+
+procedure TRawBytesHelper.ToSerialized(const AStream: TStream);
+var LSize : Integer;
+begin
+  LSize := Length(Self);
+  if LSize>65536 then raise Exception.Create('Cannot serialize TBytes due high length '+IntToStr(Length(Self)));
+  AStream.Write(LSize,2);
+  AStream.Write(Self[0],LSize);
+end;
+
+function TRawBytesHelper.GetSerializedLength: integer;
+begin
+  Result := 2 + Length(Self);
 end;
 end;
 
 
 procedure TRawBytesHelper.FromStream(AStream: TStream);
 procedure TRawBytesHelper.FromStream(AStream: TStream);
@@ -230,6 +325,13 @@ begin
   Result := Length(Self)=0;
   Result := Length(Self)=0;
 end;
 end;
 
 
+function TRawBytesHelper.IsEqualTo(const ACompareTo: TRawBytes): Boolean;
+begin
+  if (Length(Self)=Length(ACompareTo)) and (Length(Self)>0) then
+    Result := (CompareMem(@Self[0],@ACompareTo[0],Length(Self)))
+  else Result := False;
+end;
+
 function TRawBytesHelper.ToHexaString: String;
 function TRawBytesHelper.ToHexaString: String;
 Var i : Integer;
 Var i : Integer;
   rbs : RawByteString;
   rbs : RawByteString;
@@ -553,6 +655,18 @@ begin
   Result := Replace(Length,buffer);
   Result := Replace(Length,buffer);
 end;
 end;
 
 
+function TBytesBuffer.Capture(AStartPos, ALength: Integer): TBytes;
+var LLength : Integer;
+begin
+  if AStartPos+ALength <= Self.Length then LLength := ALength
+  else LLength := Self.Length - AStartPos;
+  if (LLength<0) or (LLength>Self.Length) or (AStartPos<0) or (ALength<0) then raise Exception.Create(Format('Invalid Capture start %d length %d for a %d buffer',
+    [AStartPos,ALength,Self.Length]));
+  System.SetLength(Result,LLength);
+  if LLength>0 then
+    Move(FBytes[AStartPos],Result[0],LLength);
+end;
+
 procedure TBytesBuffer.Clear;
 procedure TBytesBuffer.Clear;
 begin
 begin
   System.SetLength(FBytes,0);
   System.SetLength(FBytes,0);
@@ -616,6 +730,11 @@ begin
   Result := addr(FBytes[0]);
   Result := addr(FBytes[0]);
 end;
 end;
 
 
+function TBytesBuffer.MemoryLength: Integer;
+begin
+  Result := System.Length(FBytes);
+end;
+
 procedure TBytesBuffer.NotifyUpdated(AStartPos, ACountBytes: Integer);
 procedure TBytesBuffer.NotifyUpdated(AStartPos, ACountBytes: Integer);
 begin
 begin
   //
   //

+ 73 - 8
src/core/UBlockChain.pas

@@ -16,6 +16,8 @@ unit UBlockChain;
   THIS LICENSE HEADER MUST NOT BE REMOVED.
   THIS LICENSE HEADER MUST NOT BE REMOVED.
 }
 }
 
 
+{$I ./../config.inc}
+
 {$IFDEF FPC}
 {$IFDEF FPC}
   {$MODE Delphi}
   {$MODE Delphi}
 {$ENDIF}
 {$ENDIF}
@@ -25,8 +27,8 @@ interface
 uses
 uses
   Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes;
-{$I ./../config.inc}
+  {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
+  UPCDataTypes, UChunk;
 
 
 {
 {
 
 
@@ -472,6 +474,9 @@ Type
 
 
   TOrphan = RawByteString;
   TOrphan = RawByteString;
 
 
+
+  TCheckPointStruct = {$IFDEF USE_ABSTRACTMEM}TPCAbstractMem{$ELSE}TStream{$ENDIF};
+
   { TStorage }
   { TStorage }
 
 
   TStorage = Class(TComponent)
   TStorage = Class(TComponent)
@@ -494,7 +499,7 @@ Type
     function GetFirstBlockNumber: Int64; virtual; abstract;
     function GetFirstBlockNumber: Int64; virtual; abstract;
     function GetLastBlockNumber: Int64; virtual; abstract;
     function GetLastBlockNumber: Int64; virtual; abstract;
     function DoInitialize:Boolean; virtual; abstract;
     function DoInitialize:Boolean; virtual; abstract;
-    Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; virtual; abstract;
+    Function DoOpenSafeBoxCheckpoint(blockCount : Cardinal) : TCheckPointStruct; virtual; abstract;
     Procedure DoEraseStorage; virtual; abstract;
     Procedure DoEraseStorage; virtual; abstract;
     Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
     Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
@@ -513,10 +518,10 @@ Type
     Property FirstBlock : Int64 read GetFirstBlockNumber;
     Property FirstBlock : Int64 read GetFirstBlockNumber;
     Property LastBlock : Int64 read GetLastBlockNumber;
     Property LastBlock : Int64 read GetLastBlockNumber;
     Function Initialize : Boolean;
     Function Initialize : Boolean;
-    Function CreateSafeBoxStream(blockCount : Cardinal) : TStream;
+    Function OpenSafeBoxCheckpoint(blockCount : Cardinal) : TCheckPointStruct;
     Function HasUpgradedToVersion2 : Boolean; virtual; abstract;
     Function HasUpgradedToVersion2 : Boolean; virtual; abstract;
     Procedure CleanupVersion1Data; virtual; abstract;
     Procedure CleanupVersion1Data; virtual; abstract;
-    Procedure EraseStorage;
+    Procedure EraseStorage; // Erase Blockchain storage
     Procedure SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Procedure SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Procedure LoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Procedure LoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Function BlockExists(Block : Cardinal) : Boolean;
     Function BlockExists(Block : Cardinal) : Boolean;
@@ -549,6 +554,7 @@ Type
     function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
     function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
     function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): Real;
     function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): Real;
     function GetTargetSecondsMedian(AFromBlock: Cardinal; ABackBlocks : Integer): Real;
     function GetTargetSecondsMedian(AFromBlock: Cardinal; ABackBlocks : Integer): Real;
+    function LoadBankFromChunks(AChunks : TPCSafeboxChunks; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors : String) : Boolean;
     function LoadBankFromStream(Stream : TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors : String) : Boolean;
     function LoadBankFromStream(Stream : TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors : String) : Boolean;
     Procedure Clear;
     Procedure Clear;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
@@ -929,6 +935,7 @@ Var
   LTmpPCOperationsComp : TPCOperationsComp;
   LTmpPCOperationsComp : TPCOperationsComp;
   i,j, LProgressBlock, LProgressEndBlock, LOpsInBlocks : Integer;
   i,j, LProgressBlock, LProgressEndBlock, LOpsInBlocks : Integer;
   LSafeboxTransaction : TPCSafeBoxTransaction;
   LSafeboxTransaction : TPCSafeBoxTransaction;
+  LTempSafebox : TPCSafeBox;
 begin
 begin
   if FIsRestoringFromFile then begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
@@ -945,6 +952,7 @@ begin
       Storage.Initialize;
       Storage.Initialize;
       If (max_block<Storage.LastBlock) then n := max_block
       If (max_block<Storage.LastBlock) then n := max_block
       else n := Storage.LastBlock;
       else n := Storage.LastBlock;
+
       Storage.RestoreBank(n,restoreProgressNotify);
       Storage.RestoreBank(n,restoreProgressNotify);
       // Restore last blockchain
       // Restore last blockchain
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
@@ -955,6 +963,12 @@ begin
           FLastOperationBlock := FLastBlockCache.OperationBlock;
           FLastOperationBlock := FLastBlockCache.OperationBlock;
         end;
         end;
       end;
       end;
+      If SafeBox.BlocksCount>0 then FLastOperationBlock := SafeBox.GetBlockInfo(SafeBox.BlocksCount-1)
+      else begin
+        FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
+        FLastOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Genesis hash
+      end;
+
       NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') BlockCount: '+inttostr(BlocksCount)+' Orphan: ' +Storage.Orphan);
       NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') BlockCount: '+inttostr(BlocksCount)+' Orphan: ' +Storage.Orphan);
       LBlocks := TList<TPCOperationsComp>.Create;
       LBlocks := TList<TPCOperationsComp>.Create;
       try
       try
@@ -1032,7 +1046,12 @@ begin
     finally
     finally
       FIsRestoringFromFile := False;
       FIsRestoringFromFile := False;
       FUpgradingToV2 := false;
       FUpgradingToV2 := false;
+      for i := 0 to FNotifyList.Count - 1 do begin
+        TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
+      end;
     end;
     end;
+
+
   finally
   finally
     FBankLock.Release;
     FBankLock.Release;
   end;
   end;
@@ -1168,7 +1187,7 @@ begin
   Result := FStorage;
   Result := FStorage;
 end;
 end;
 
 
-function TPCBank.IsReady(Var CurrentProcess: String): Boolean;
+function TPCBank.IsReady(var CurrentProcess: String): Boolean;
 begin
 begin
   Result := false;
   Result := false;
   CurrentProcess := '';
   CurrentProcess := '';
@@ -1180,6 +1199,52 @@ begin
   end else Result := true;
   end else Result := true;
 end;
 end;
 
 
+function TPCBank.LoadBankFromChunks(AChunks : TPCSafeboxChunks;
+  checkSafeboxHash: TRawBytes; previousCheckedSafebox: TPCSafebox;
+  progressNotify: TProgressNotify; var errors: String): Boolean;
+Var LastReadBlock : TBlockAccount;
+  i : Integer;
+  LMemStream : TStream;
+begin
+  Result := False;
+  Try
+    if Not AChunks.IsComplete then begin
+      errors := 'AChunks is not complete';
+      Exit;
+    end;
+    LMemStream := TMemoryStream.Create;
+    try
+      for i := 0 to AChunks.Count-1 do begin
+        LMemStream.Size:=0;
+        LMemStream.Position := 0;
+        LMemStream.CopyFrom( AChunks.GetSafeboxChunk(i), 0 );
+        LMemStream.Position := 0;
+          if Not Safebox.LoadSafeBoxChunkFromStream(LMemStream,True,checkSafeboxHash,progressNotify,previousCheckedSafebox,LastReadBlock,errors) then begin
+          errors := Format('Error at chunk %d/%d ',[i+1,AChunks.Count])+errors;
+          Exit;
+        end;
+      end;
+    finally
+      LMemStream.Free;
+    end;
+    Result := True;
+    TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
+    try
+      If SafeBox.BlocksCount>0 then FLastOperationBlock := SafeBox.GetBlockInfo(SafeBox.BlocksCount-1)
+      else begin
+        FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
+        FLastOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Genesis hash
+      end;
+    finally
+      FBankLock.Release;
+    end;
+    for i := 0 to FNotifyList.Count - 1 do begin
+      TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
+    end;
+  finally
+  end;
+end;
+
 function TPCBank.LoadBankFromStream(Stream: TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors: String): Boolean;
 function TPCBank.LoadBankFromStream(Stream: TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors: String): Boolean;
 Var LastReadBlock : TBlockAccount;
 Var LastReadBlock : TBlockAccount;
   i : Integer;
   i : Integer;
@@ -2894,9 +2959,9 @@ begin
   Result := DoInitialize;
   Result := DoInitialize;
 end;
 end;
 
 
-function TStorage.CreateSafeBoxStream(blockCount: Cardinal): TStream;
+function TStorage.OpenSafeBoxCheckpoint(blockCount: Cardinal): TCheckPointStruct;
 begin
 begin
-  Result := DoCreateSafeBoxStream(blockCount);
+  Result := DoOpenSafeBoxCheckpoint(blockCount);
 end;
 end;
 
 
 procedure TStorage.EraseStorage;
 procedure TStorage.EraseStorage;

+ 103 - 1
src/core/UChunk.pas

@@ -36,10 +36,12 @@ uses
   {$ELSE}
   {$ELSE}
   zlib,
   zlib,
   {$ENDIF}
   {$ENDIF}
-  UAccounts, ULog, UConst, UCrypto, UBaseTypes;
+  UAccounts, ULog, UConst, UCrypto, UBaseTypes, UPCDataTypes;
 
 
 type
 type
 
 
+  EPCChunk = Class(Exception);
+
   { TPCChunk }
   { TPCChunk }
 
 
   TPCChunk = Class
   TPCChunk = Class
@@ -49,8 +51,108 @@ type
     class function LoadSafeBoxFromChunk(Chunk, DestStream : TStream; var safeBoxHeader : TPCSafeBoxHeader; var errors : String) : Boolean;
     class function LoadSafeBoxFromChunk(Chunk, DestStream : TStream; var safeBoxHeader : TPCSafeBoxHeader; var errors : String) : Boolean;
   end;
   end;
 
 
+  { TPCSafeboxChunks }
+
+  TPCSafeboxChunks = Class
+  private
+    FChunks : Array of TStream;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    procedure Clear;
+    function Count : Integer;
+    procedure AddChunk(ASafeboxStreamChunk : TStream);
+    function GetSafeboxChunk(index : Integer) : TStream;
+    function GetSafeboxChunkHeader(index : Integer) : TPCSafeBoxHeader;
+    function IsComplete : Boolean;
+    function GetSafeboxHeader : TPCSafeBoxHeader;
+  end;
+
 implementation
 implementation
 
 
+{ TPCSafeboxChunks }
+
+constructor TPCSafeboxChunks.Create;
+begin
+  SetLength(FChunks,0);
+end;
+
+destructor TPCSafeboxChunks.Destroy;
+begin
+  Clear;
+  inherited Destroy;
+end;
+
+procedure TPCSafeboxChunks.Clear;
+var i : Integer;
+begin
+  For i:=0 to Count-1 do begin
+    FChunks[i].Free;
+  end;
+  SetLength(FChunks,0);
+end;
+
+function TPCSafeboxChunks.Count: Integer;
+begin
+  Result := Length(FChunks);
+end;
+
+procedure TPCSafeboxChunks.AddChunk(ASafeboxStreamChunk: TStream);
+var LLastHeader, LsbHeader : TPCSafeBoxHeader;
+begin
+  If Not TPCSafeBox.LoadSafeBoxStreamHeader(ASafeboxStreamChunk,LsbHeader) then begin
+    Raise EPCChunk.Create('SafeBoxStream is not a valid SafeBox to add!');
+  end;
+  if (Count>0) then begin
+    LLastHeader := GetSafeboxChunkHeader(Count-1);
+    if (LsbHeader.ContainsFirstBlock)
+      or (LsbHeader.startBlock<>LLastHeader.endBlock+1)
+      or (LLastHeader.ContainsLastBlock)
+      or (LsbHeader.protocol<>LLastHeader.protocol)
+      or (LsbHeader.blocksCount<>LLastHeader.blocksCount)
+      or (Not LsbHeader.safeBoxHash.IsEqualTo( LLastHeader.safeBoxHash ))
+      then begin
+      raise EPCChunk.Create(Format('Cannot add %s at (%d) %s',[LsbHeader.ToString,Length(FChunks),LLastHeader.ToString]));
+    end;
+  end else if (Not LsbHeader.ContainsFirstBlock) then begin
+    raise EPCChunk.Create(Format('Cannot add %s',[LsbHeader.ToString]));
+  end;
+  //
+  SetLength(FChunks,Length(FChunks)+1);
+  FChunks[High(FChunks)] := ASafeboxStreamChunk;
+end;
+
+function TPCSafeboxChunks.GetSafeboxChunk(index: Integer): TStream;
+begin
+  if (index<0) or (index>=Count) then raise EPCChunk.Create(Format('Invalid index %d of %d',[index,Length(FChunks)]));
+  Result := FChunks[index];
+  Result.Position := 0;
+end;
+
+function TPCSafeboxChunks.GetSafeboxChunkHeader(index: Integer): TPCSafeBoxHeader;
+begin
+  If Not TPCSafeBox.LoadSafeBoxStreamHeader(GetSafeboxChunk(index),Result) then begin
+    Raise EPCChunk.Create(Format('Cannot capture header index %d of %d',[index,Length(FChunks)]));
+  end;
+end;
+
+function TPCSafeboxChunks.IsComplete: Boolean;
+var LsbHeader : TPCSafeBoxHeader;
+begin
+  if Count=0 then Result := False
+  else begin
+    LsbHeader := GetSafeboxChunkHeader(Count-1);
+    Result := LsbHeader.ContainsLastBlock;
+  end;
+end;
+
+function TPCSafeboxChunks.GetSafeboxHeader: TPCSafeBoxHeader;
+begin
+  if Not IsComplete then Raise EPCChunk.Create(Format('Chunks are not complete %d',[Length(FChunks)]));
+  Result := GetSafeboxChunkHeader(Count-1);
+  Result.startBlock := 0;
+end;
+
 { TPCChunk }
 { TPCChunk }
 
 
 class function TPCChunk.SaveSafeBoxChunkFromSafeBox(SafeBoxStream, DestStream : TStream; fromBlock, toBlock: Cardinal; var errors : String) : Boolean;
 class function TPCChunk.SaveSafeBoxChunkFromSafeBox(SafeBoxStream, DestStream : TStream; fromBlock, toBlock: Cardinal; var errors : String) : Boolean;

+ 80 - 24
src/core/UFileStorage.pas

@@ -22,10 +22,12 @@ unit UFileStorage;
 
 
 interface
 interface
 
 
-uses
-  Classes, {$IFnDEF FPC}Windows,{$ENDIF} UBlockChain, SyncObjs, UThread, UAccounts, UCrypto;
 {$I ./../config.inc}
 {$I ./../config.inc}
 
 
+uses
+  Classes, {$IFnDEF FPC}Windows,{$ENDIF} UBlockChain, SyncObjs, UThread, UAccounts, UCrypto, UPCDataTypes;
+
+
 Type
 Type
   TBlockHeader = Record
   TBlockHeader = Record
     BlockNumber : Cardinal;
     BlockNumber : Cardinal;
@@ -74,7 +76,7 @@ Type
     function GetFirstBlockNumber: Int64; override;
     function GetFirstBlockNumber: Int64; override;
     function GetLastBlockNumber: Int64; override;
     function GetLastBlockNumber: Int64; override;
     function DoInitialize : Boolean; override;
     function DoInitialize : Boolean; override;
-    Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; override;
+    Function DoOpenSafeBoxCheckpoint(blockCount : Cardinal) : TCheckPointStruct; override;
     Procedure DoEraseStorage; override;
     Procedure DoEraseStorage; override;
     Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
     Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
@@ -92,12 +94,18 @@ Type
 
 
 implementation
 implementation
 
 
-Uses ULog, SysUtils, UConst;
-
+Uses ULog, SysUtils, UBaseTypes,
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem,
+  {$ENDIF}
+  UConst;
 { TFileStorage }
 { TFileStorage }
 
 
 Const CT_TBlockHeader_NUL : TBlockHeader = (BlockNumber:0;StreamBlockRelStartPos:0;BlockSize:0);
 Const CT_TBlockHeader_NUL : TBlockHeader = (BlockNumber:0;StreamBlockRelStartPos:0;BlockSize:0);
 
 
+
+  CT_Safebox_Extension = {$IFDEF USE_ABSTRACTMEM}'.am_safebox'{$ELSE}'.safebox'{$ENDIF};
+
   CT_GroupBlockSize = 1000;
   CT_GroupBlockSize = 1000;
   CT_SizeOfBlockHeader = 16;
   CT_SizeOfBlockHeader = 16;
   {
   {
@@ -270,14 +278,18 @@ begin
   End;
   End;
 end;
 end;
 
 
-function TFileStorage.DoCreateSafeBoxStream(blockCount: Cardinal): TStream;
+function TFileStorage.DoOpenSafeBoxCheckpoint(blockCount: Cardinal): TCheckPointStruct;
 var fn : TFilename;
 var fn : TFilename;
   err : AnsiString;
   err : AnsiString;
 begin
 begin
   Result := Nil;
   Result := Nil;
   fn := GetSafeboxCheckpointingFileName(GetFolder(Orphan),blockCount);
   fn := GetSafeboxCheckpointingFileName(GetFolder(Orphan),blockCount);
   If (fn<>'') and (FileExists(fn)) then begin
   If (fn<>'') and (FileExists(fn)) then begin
+    {$IFDEF USE_ABSTRACTMEM}
+    Result := TPCAbstractMem.Create(fn,True);
+    {$ELSE}
     Result := TFileStream.Create(fn,fmOpenRead+fmShareDenyWrite);
     Result := TFileStream.Create(fn,fmOpenRead+fmShareDenyWrite);
+    {$ENDIF}
   end;
   end;
   If Not Assigned(Result) then begin
   If Not Assigned(Result) then begin
     err := 'Cannot load SafeBoxStream (block:'+IntToStr(blockCount)+') file:'+fn;
     err := 'Cannot load SafeBoxStream (block:'+IntToStr(blockCount)+') file:'+fn;
@@ -378,7 +390,7 @@ function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan:
   begin
   begin
     FileAttrs := faArchive;
     FileAttrs := faArchive;
     folder := GetFolder(Orphan);
     folder := GetFolder(Orphan);
-    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
+    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
       repeat
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
           sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
@@ -452,28 +464,66 @@ var
     sr: TSearchRec;
     sr: TSearchRec;
     FileAttrs: Integer;
     FileAttrs: Integer;
     folder : AnsiString;
     folder : AnsiString;
-    filename,auxfn : AnsiString;
+    Lfilename,auxfn : AnsiString;
     fs : TFileStream;
     fs : TFileStream;
     ms : TMemoryStream;
     ms : TMemoryStream;
     errors : String;
     errors : String;
-    blockscount : Cardinal;
+    LBlockscount : Cardinal;
     sbHeader, goodSbHeader : TPCSafeBoxHeader;
     sbHeader, goodSbHeader : TPCSafeBoxHeader;
+    {$IFDEF USE_ABSTRACTMEM}
+    LTempBlocksCount : Integer;
+    LSafeboxFileName : String;
+    {$ELSE}
+    {$ENDIF}
 begin
 begin
   LockBlockChainStream;
   LockBlockChainStream;
   Try
   Try
+    {$IFDEF USE_ABSTRACTMEM}
+    Lfilename := '';
+    LSafeboxFileName := GetFolder(Orphan)+PathDelim+'safebox'+CT_Safebox_Extension;
+    if TPCAbstractMem.AnalyzeFile(LSafeboxFileName,LTempBlocksCount) then begin
+      LBlockscount := LTempBlocksCount;
+    end else begin
+      LBlockscount := 0;
+    end;
+    //
     FileAttrs := faArchive;
     FileAttrs := faArchive;
     folder := GetFolder(Orphan);
     folder := GetFolder(Orphan);
-    filename := '';
-    blockscount := 0;
+    if SysUtils.FindFirst(folder+PathDelim+'*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
+      repeat
+        if (sr.Attr and FileAttrs) = FileAttrs then begin
+          auxfn := folder+PathDelim+sr.Name;
+          if TPCAbstractMem.AnalyzeFile(auxfn,LTempBlocksCount) then begin
+            if (((max_block<0) Or (LTempBlocksCount<=max_block)) AND (LTempBlocksCount>LBlockscount)) then begin
+              Lfilename := auxfn;
+              LBlockscount := LTempBlocksCount;
+            end;
+          end;
+        end;
+      until FindNext(sr) <> 0;
+      FindClose(sr);
+    end;
+    if (Lfilename='') then begin
+      Bank.SafeBox.SetSafeboxFileName(LSafeboxFileName);
+    end else begin
+      Bank.SafeBox.SetSafeboxFileName(Lfilename);
+      Bank.SafeBox.UpdateSafeboxFileName(LSafeboxFileName);
+    end;
+    {$ELSE}
+    LBlockscount := 0;
+    {$ENDIF}
+    FileAttrs := faArchive;
+    folder := GetFolder(Orphan);
+    Lfilename := '';
     if SysUtils.FindFirst(folder+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
     if SysUtils.FindFirst(folder+PathDelim+'*.safebox', FileAttrs, sr) = 0 then begin
       repeat
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           auxfn := folder+PathDelim+sr.Name;
           auxfn := folder+PathDelim+sr.Name;
           If LoadBankFileInfo(auxfn,sbHeader) then begin
           If LoadBankFileInfo(auxfn,sbHeader) then begin
-            if (((max_block<0) Or (sbHeader.endBlock<=max_block)) AND (sbHeader.blocksCount>blockscount)) And
+            if (((max_block<0) Or (sbHeader.endBlock<=max_block)) AND (sbHeader.blocksCount>LBlockscount)) And
               (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
               (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
-              filename := auxfn;
-              blockscount := sbHeader.blocksCount;
+              Lfilename := auxfn;
+              LBlockscount := sbHeader.blocksCount;
               goodSbHeader := sbHeader;
               goodSbHeader := sbHeader;
             end;
             end;
           end;
           end;
@@ -481,14 +531,14 @@ begin
       until FindNext(sr) <> 0;
       until FindNext(sr) <> 0;
       FindClose(sr);
       FindClose(sr);
     end;
     end;
-    if (filename<>'') then begin
-      TLog.NewLog(ltinfo,Self.ClassName,'Loading SafeBox protocol:'+IntToStr(goodSbHeader.protocol)+' with '+inttostr(blockscount)+' blocks from file '+filename);
-      fs := TFileStream.Create(filename,fmOpenRead);
+    if (Lfilename<>'') then begin
+      TLog.NewLog(ltinfo,Self.ClassName,'Loading SafeBox protocol:'+IntToStr(goodSbHeader.protocol)+' with '+inttostr(LBlockscount)+' blocks from file '+Lfilename+' LowMemoryUsage:'+LowMemoryUsage.ToString(True));
+      fs := TFileStream.Create(Lfilename,fmOpenRead);
       try
       try
         fs.Position := 0;
         fs.Position := 0;
         if LowMemoryUsage then begin
         if LowMemoryUsage then begin
           if not Bank.LoadBankFromStream(fs,False,Nil,Nil,restoreProgressNotify,errors) then begin
           if not Bank.LoadBankFromStream(fs,False,Nil,Nil,restoreProgressNotify,errors) then begin
-            TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
+            TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+Lfilename+ ' Error: '+errors);
           end;
           end;
         end else begin
         end else begin
           ms := TMemoryStream.Create;
           ms := TMemoryStream.Create;
@@ -496,7 +546,7 @@ begin
             ms.CopyFrom(fs,0);
             ms.CopyFrom(fs,0);
             ms.Position := 0;
             ms.Position := 0;
             if not Bank.LoadBankFromStream(ms,False,Nil,Nil,restoreProgressNotify,errors) then begin
             if not Bank.LoadBankFromStream(ms,False,Nil,Nil,restoreProgressNotify,errors) then begin
-              TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
+              TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+Lfilename+ ' Error: '+errors);
             end;
             end;
           Finally
           Finally
             ms.Free;
             ms.Free;
@@ -515,11 +565,15 @@ function TFileStorage.DoSaveBank: Boolean;
 var fs: TFileStream;
 var fs: TFileStream;
     bankfilename,aux_newfilename: AnsiString;
     bankfilename,aux_newfilename: AnsiString;
     ms : TMemoryStream;
     ms : TMemoryStream;
+  LTC : TTickCount;
 begin
 begin
   Result := true;
   Result := true;
   bankfilename := GetSafeboxCheckpointingFileName(GetFolder(Orphan),Bank.BlocksCount);
   bankfilename := GetSafeboxCheckpointingFileName(GetFolder(Orphan),Bank.BlocksCount);
   if (bankfilename<>'') then begin
   if (bankfilename<>'') then begin
-    TLog.NewLog(ltInfo,ClassName,'Saving Safebox blocks:'+IntToStr(Bank.BlocksCount)+' file:'+bankfilename);
+    LTC := TPlatform.GetTickCount;
+    {$IFDEF USE_ABSTRACTMEM}
+    Bank.SafeBox.SaveCheckpointing(bankfilename);
+    {$ELSE}
     fs := TFileStream.Create(bankfilename,fmCreate);
     fs := TFileStream.Create(bankfilename,fmCreate);
     try
     try
       fs.Size := 0;
       fs.Size := 0;
@@ -539,9 +593,11 @@ begin
     finally
     finally
       fs.Free;
       fs.Free;
     end;
     end;
+    {$ENDIF}
+    TLog.NewLog(ltInfo,ClassName,Format('Saving Safebox blocks:%d file:%s in %.2n seconds',[Bank.BlocksCount,bankfilename,TPlatform.GetElapsedMilliseconds(LTC)/1000]));
     // Save a copy each 10000 blocks (aprox 1 month) only when not an orphan
     // 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
     if (Orphan='') And ((Bank.BlocksCount MOD (CT_BankToDiskEveryNBlocks*100))=0) then begin
-      aux_newfilename := GetFolder('') + PathDelim+'checkpoint_'+ inttostr(Bank.BlocksCount)+'.safebox';
+      aux_newfilename := GetFolder('') + PathDelim+'checkpoint_'+ inttostr(Bank.BlocksCount)+CT_Safebox_Extension;
       try
       try
         {$IFDEF FPC}
         {$IFDEF FPC}
         DoCopyFile(bankfilename,aux_newfilename);
         DoCopyFile(bankfilename,aux_newfilename);
@@ -589,9 +645,9 @@ begin
   If not ForceDirectories(BaseDataFolder) then exit;
   If not ForceDirectories(BaseDataFolder) then exit;
   if TPCSafeBox.MustSafeBoxBeSaved(block) then begin
   if TPCSafeBox.MustSafeBoxBeSaved(block) then begin
     // We will store checkpointing
     // We will store checkpointing
-    Result := BaseDataFolder + PathDelim+'checkpoint'+ inttostr((block DIV CT_BankToDiskEveryNBlocks) MOD CT_SafeboxsToStore)+'.safebox';
+    Result := BaseDataFolder + PathDelim+'checkpoint'+ inttostr((block DIV CT_BankToDiskEveryNBlocks) MOD CT_SafeboxsToStore)+CT_Safebox_Extension;
   end else begin
   end else begin
-    Result := BaseDataFolder + PathDelim+'checkpoint_'+inttostr(block)+'.safebox';
+    Result := BaseDataFolder + PathDelim+'checkpoint_'+inttostr(block)+CT_Safebox_Extension;
   end;
   end;
 end;
 end;
 
 
@@ -1082,7 +1138,7 @@ end;
 function TFileStorage.HasUpgradedToVersion2: Boolean;
 function TFileStorage.HasUpgradedToVersion2: Boolean;
 var searchRec: TSearchRec;
 var searchRec: TSearchRec;
 begin
 begin
-  HasUpgradedToVersion2 := SysUtils.FindFirst( GetFolder(Orphan)+PathDelim+'*.safebox', faArchive, searchRec) = 0;
+  HasUpgradedToVersion2 := SysUtils.FindFirst( GetFolder(Orphan)+PathDelim+'*'+CT_Safebox_Extension, faArchive, searchRec) = 0;
   FindClose(searchRec);
   FindClose(searchRec);
 end;
 end;
 
 

+ 123 - 105
src/core/UNetProtocol.pas

@@ -16,6 +16,8 @@ unit UNetProtocol;
   THIS LICENSE HEADER MUST NOT BE REMOVED.
   THIS LICENSE HEADER MUST NOT BE REMOVED.
 }
 }
 
 
+{$I ./../config.inc}
+
 {$IFDEF FPC}
 {$IFDEF FPC}
   {$MODE Delphi}
   {$MODE Delphi}
 {$ENDIF}
 {$ENDIF}
@@ -31,12 +33,12 @@ Uses
 {$ENDIF}
 {$ENDIF}
   UBlockChain, Classes, SysUtils, UAccounts, UThread,
   UBlockChain, Classes, SysUtils, UAccounts, UThread,
   UCrypto, UTCPIP, SyncObjs, UBaseTypes, UCommon, UPCOrderedLists,
   UCrypto, UTCPIP, SyncObjs, UBaseTypes, UCommon, UPCOrderedLists,
+  UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults
   {$ELSE}Generics.Collections,Generics.Defaults{$ENDIF},
   {$ELSE}Generics.Collections,Generics.Defaults{$ENDIF},
+  {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
   UNetProtection;
   UNetProtection;
 
 
-{$I ./../config.inc}
-
 Const
 Const
   CT_MagicRequest = $0001;
   CT_MagicRequest = $0001;
   CT_MagicResponse = $0002;
   CT_MagicResponse = $0002;
@@ -1844,105 +1846,75 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     end;
     end;
   end;
   end;
 
 
-  Type TSafeBoxChunkData = Record
+  Function DownloadSafeboxChunks(ASafeboxChunks : TPCSafeboxChunks; var ASafeboxLastOperationBlock : TOperationBlock; var errors : String) : Boolean;
+  var LDownloadedSafeboxBlocksCount, request_id : Cardinal;
+    LreceivedChunk : TStream;
     safeBoxHeader : TPCSafeBoxHeader;
     safeBoxHeader : TPCSafeBoxHeader;
-    chunkStream : TStream;
-  end;
-
-  Function DownloadSafeboxStream(safeboxStream : TStream; var safebox_last_operation_block : TOperationBlock) : Boolean;
-  var _blockcount, request_id : Cardinal;
-    chunks : Array of TSafeBoxChunkData;
-    receiveChunk, chunk1 : TStream;
-    safeBoxHeader : TPCSafeBoxHeader;
-    errors : String;
+    //errors : String;
     i : Integer;
     i : Integer;
-    LFirstSafebox : Boolean;
   Begin
   Begin
     Result := False;
     Result := False;
-    LFirstSafebox := TNode.Node.Bank.SafeBox.BlocksCount = 0;
-    safeboxStream.Size:=0;
-    safeboxStream.Position:=0;
+    ASafeboxChunks.Clear;
     // Will try to download penultimate saved safebox
     // Will try to download penultimate saved safebox
-    _blockcount := ((Connection.FRemoteOperationBlock.block DIV CT_BankToDiskEveryNBlocks)-1) * CT_BankToDiskEveryNBlocks;
-    If not Do_GetOperationBlock(_blockcount,5000,safebox_last_operation_block) then begin
-      Connection.DisconnectInvalidClient(false,Format('Cannot obtain operation block %d for downloading safebox',[_blockcount]));
+    LDownloadedSafeboxBlocksCount := ((Connection.FRemoteOperationBlock.block DIV CT_BankToDiskEveryNBlocks)-1) * CT_BankToDiskEveryNBlocks;
+
+    If not Do_GetOperationBlock(LDownloadedSafeboxBlocksCount,5000,ASafeboxLastOperationBlock) then begin
+      Connection.DisconnectInvalidClient(false,Format('Cannot obtain operation block %d for downloading safebox',[LDownloadedSafeboxBlocksCount]));
       exit;
       exit;
     end;
     end;
     // New Build 2.1.7 - Check valid operationblock
     // New Build 2.1.7 - Check valid operationblock
-    If Not TPCSafeBox.IsValidOperationBlock(safebox_last_operation_block,errors) then begin
-      Connection.DisconnectInvalidClient(false,'Invalid operation block at DownloadSafeBox '+TPCOperationsComp.OperationBlockToText(safebox_last_operation_block)+' errors: '+errors);
+    If Not TPCSafeBox.IsValidOperationBlock(ASafeboxLastOperationBlock,errors) then begin
+      Connection.DisconnectInvalidClient(false,'Invalid operation block at DownloadSafeBox '+TPCOperationsComp.OperationBlockToText(ASafeboxLastOperationBlock)+' errors: '+errors);
       Exit;
       Exit;
     end;
     end;
-    SetLength(chunks,0);
-    try
       // Will obtain chunks of 10000 blocks each -> Note: Maximum is CT_MAX_SAFEBOXCHUNK_BLOCKS
       // Will obtain chunks of 10000 blocks each -> Note: Maximum is CT_MAX_SAFEBOXCHUNK_BLOCKS
-      for i:=0 to ((_blockcount-1) DIV 10000) do begin // Bug v3.0.1 and minors
+      for i:=0 to ((LDownloadedSafeboxBlocksCount-1) DIV 10000) do begin // Bug v3.0.1 and minors
         FNewBlockChainFromClientStatus := Format('Receiving new safebox with %d blocks (step %d/%d) from %s',
         FNewBlockChainFromClientStatus := Format('Receiving new safebox with %d blocks (step %d/%d) from %s',
-          [_blockcount,i+1,((_blockcount-1) DIV 10000)+1,Connection.ClientRemoteAddr]);
-        if LFirstSafebox then receiveChunk := TMemoryStream.Create
-        else receiveChunk := TPCTemporalFileStream.Create('CHUNK_'+IntToStr(i)+'_');
-        if (Not DownloadSafeBoxChunk(_blockcount,safebox_last_operation_block.initial_safe_box_hash,(i*10000),((i+1)*10000)-1,receiveChunk,safeBoxHeader,errors)) then begin
-          receiveChunk.Free;
+          [LDownloadedSafeboxBlocksCount,i+1,((LDownloadedSafeboxBlocksCount-1) DIV 10000)+1,Connection.ClientRemoteAddr]);
+        LreceivedChunk := TPCTemporalFileStream.Create(Format('CHUNK_%.3d_',[i]));
+        if (Not DownloadSafeBoxChunk(LDownloadedSafeboxBlocksCount,ASafeboxLastOperationBlock.initial_safe_box_hash,(i*10000),((i+1)*10000)-1,LreceivedChunk,safeBoxHeader,errors)) then begin
+          LreceivedChunk.Free;
           TLog.NewLog(ltError,CT_LogSender,errors);
           TLog.NewLog(ltError,CT_LogSender,errors);
           Exit;
           Exit;
         end;
         end;
-        SetLength(chunks,length(chunks)+1);
-        chunks[High(chunks)].safeBoxHeader := safeBoxHeader;
-        chunks[High(chunks)].chunkStream := receiveChunk;
-      end;
-      TLog.NewLog(ltDebug,CT_LogSender,Format('Concatening %d chunks',[Length(chunks)]));
-      // Will concat safeboxs:
-      chunk1 := TPCTemporalFileStream.Create('CONCAT_CHUNKS_');
-      try
-        if (length(chunks)=1) then begin
-          safeboxStream.CopyFrom(chunks[0].chunkStream,0);
-        end else begin
-          chunk1.CopyFrom(chunks[0].chunkStream,0);
-        end;
-        for i:=1 to high(chunks) do begin
-          FNewBlockChainFromClientStatus := Format('Concatening downloaded safebox (step %d/%d) from %s',
-            [i,High(chunks),Connection.ClientRemoteAddr]);
-          TLog.NewLog(ltDebug,CT_LogSender,Format('Concatening chunk %d/%d',[i,High(chunks)]));
-          safeboxStream.Size:=0;
-          safeboxStream.Position := 0; // Added caused by FPC 3.0.4 bug that does not update position auto when setting size=0 at a TFileStream
-          chunk1.Position:=0;
-          chunks[i].chunkStream.Position:=0;
-          If Not TPCSafeBox.ConcatSafeBoxStream(chunk1,chunks[i].chunkStream,safeboxStream,errors) then begin
-            TLog.NewLog(ltError,CT_LogSender,errors);
-            exit;
+        try
+          LreceivedChunk.Position := 0;
+          ASafeboxChunks.AddChunk( LreceivedChunk );
+        Except
+          On E:Exception do begin
+            errors:= Format('(%s) %s',[E.ClassName,E.Message]);
+            Result := false;
+            LreceivedChunk.Free;
+            Exit;
           end;
           end;
-          chunk1.Size := 0;
-          chunk1.CopyFrom(safeboxStream,0);
         end;
         end;
-        FNewBlockChainFromClientStatus := Format('Downloaded safebox with %d chunks from %s',[High(chunks),Connection.ClientRemoteAddr]);
-      finally
-        chunk1.Free;
-      end;
-    finally
-      for i:=0 to high(chunks) do begin
-        chunks[i].chunkStream.Free;
       end;
       end;
-      SetLength(chunks,0);
-    end;
-    Result := True;
-  End;
+
+      if Not ASafeboxChunks.IsComplete then begin
+        errors := 'Safebox Chunks is not complete!';
+        Exit;
+      end else Result := True;
+  end;
+
 
 
   Function DownloadSafeBox(IsMyBlockchainValid : Boolean) : Boolean;
   Function DownloadSafeBox(IsMyBlockchainValid : Boolean) : Boolean;
-  var receiveData : TStream;
-    op : TOperationBlock;
+  var LChunks : TPCSafeboxChunks;
+    LSafeboxLastOperationBlock : TOperationBlock;
     errors : String;
     errors : String;
     request_id : Cardinal;
     request_id : Cardinal;
   Begin
   Begin
     Result := False;
     Result := False;
-    receiveData := TPCTemporalFileStream.Create('SAFEBOX_');
+    LChunks := TPCSafeboxChunks.Create;
     try
     try
-      if Not DownloadSafeboxStream(receiveData,op) then Exit;
+      if Not DownloadSafeboxChunks( LChunks, LSafeboxLastOperationBlock, errors ) then begin
+        TLog.NewLog(lterror,CT_LogSender,'Cannot DownloadSafeBox: '+errors);
+        Exit;
+      end;
       // Now receiveData is the ALL safebox
       // Now receiveData is the ALL safebox
       TNode.Node.DisableNewBlocks;
       TNode.Node.DisableNewBlocks;
       try
       try
-          FNewBlockChainFromClientStatus := Format('Received new safebox with %d blocks from %s',[op.block+1,Connection.ClientRemoteAddr]);
-          receiveData.Position:=0;
-          If TNode.Node.Bank.LoadBankFromStream(receiveData,True,op.initial_safe_box_hash,TNode.Node.Bank.SafeBox,OnReadingNewSafeboxProgressNotify,errors) then begin
+          FNewBlockChainFromClientStatus := Format('Received new safebox with %d blocks from %s',[LSafeboxLastOperationBlock.block+1,Connection.ClientRemoteAddr]);
+          If TNode.Node.Bank.LoadBankFromChunks(LChunks,LSafeboxLastOperationBlock.initial_safe_box_hash,TNode.Node.Bank.SafeBox,OnReadingNewSafeboxProgressNotify,errors) then begin
             TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
             TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
             If Not IsMyBlockchainValid then begin
             If Not IsMyBlockchainValid then begin
               TNode.Node.Bank.Storage.EraseStorage;
               TNode.Node.Bank.Storage.EraseStorage;
@@ -1958,14 +1930,14 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         TNode.Node.EnableNewBlocks;
         TNode.Node.EnableNewBlocks;
       end;
       end;
     finally
     finally
-      receiveData.Free;
+      LChunks.Free;
     end;
     end;
   end;
   end;
 
 
   procedure DownloadNewBlockchain(start_block : Int64; IsMyBlockChainOk : Boolean);
   procedure DownloadNewBlockchain(start_block : Int64; IsMyBlockChainOk : Boolean);
-  var safeboxStream : TStream;
+  var LChunks : TPCSafeboxChunks;
     newTmpBank : TPCBank;
     newTmpBank : TPCBank;
-    safebox_last_operation_block : TOperationBlock;
+    LSafeboxLastOperationBlock : TOperationBlock;
     opComp : TPCOperationsComp;
     opComp : TPCOperationsComp;
     errors : String;
     errors : String;
     blocksList : TList<TPCOperationsComp>;
     blocksList : TList<TPCOperationsComp>;
@@ -1981,10 +1953,12 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     if (download_new_safebox) then begin
     if (download_new_safebox) then begin
       TLog.NewLog(ltinfo,ClassName,Format('Will download new safebox. My blocks:%d Remote blocks:%d Equal Block:%d (MaxFutureBlocksToDownloadNewSafebox:%d)',[TNode.Node.Bank.BlocksCount,Connection.RemoteOperationBlock.block+1,start_block-1,MinFutureBlocksToDownloadNewSafebox]));
       TLog.NewLog(ltinfo,ClassName,Format('Will download new safebox. My blocks:%d Remote blocks:%d Equal Block:%d (MaxFutureBlocksToDownloadNewSafebox:%d)',[TNode.Node.Bank.BlocksCount,Connection.RemoteOperationBlock.block+1,start_block-1,MinFutureBlocksToDownloadNewSafebox]));
       // Will try to download safebox
       // Will try to download safebox
-      safeboxStream := TPCTemporalFileStream.Create('NEW_SAFEBOX_');
-      Try
-        if Not DownloadSafeboxStream(safeboxStream,safebox_last_operation_block) then Exit;
-        safeboxStream.Position := 0;
+      LChunks := TPCSafeboxChunks.Create;
+      try
+        if Not DownloadSafeboxChunks( LChunks, LSafeboxLastOperationBlock, errors ) then begin
+          TLog.NewLog(lterror,CT_LogSender,'Cannot DownloadNewBlockchain: '+errors);
+          Exit;
+        end;
         newTmpBank := TPCBank.Create(Nil);
         newTmpBank := TPCBank.Create(Nil);
         try
         try
           newTmpBank.StorageClass := TNode.Node.Bank.StorageClass;
           newTmpBank.StorageClass := TNode.Node.Bank.StorageClass;
@@ -1993,7 +1967,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
           newTmpBank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
           newTmpBank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
           newTmpBank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
           newTmpBank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
           newTmpBank.Storage.ReadOnly := false;
           newTmpBank.Storage.ReadOnly := false;
-          if newTmpBank.LoadBankFromStream(safeboxStream,True,safebox_last_operation_block.initial_safe_box_hash,TNode.Node.Bank.SafeBox,OnReadingNewSafeboxProgressNotify,errors) then begin
+          If newTmpBank.LoadBankFromChunks(LChunks,LSafeboxLastOperationBlock.initial_safe_box_hash,TNode.Node.Bank.SafeBox,OnReadingNewSafeboxProgressNotify,errors) then begin
             TNode.Node.DisableNewBlocks;
             TNode.Node.DisableNewBlocks;
             try
             try
               TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
               TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
@@ -2001,8 +1975,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
               // Receive at least 1 new block
               // Receive at least 1 new block
               blocksList := TList<TPCOperationsComp>.Create;
               blocksList := TList<TPCOperationsComp>.Create;
               try
               try
-                if Not Do_GetOperationsBlock(newTmpBank,safebox_last_operation_block.block,safebox_last_operation_block.block+10,20000,False,blocksList) then begin
-                  TLog.NewLog(ltError,ClassName,Format('Cannot receive at least 1 new block:%d',[safebox_last_operation_block.block]));
+                if Not Do_GetOperationsBlock(newTmpBank,LSafeboxLastOperationBlock.block,LSafeboxLastOperationBlock.block+10,20000,False,blocksList) then begin
+                  TLog.NewLog(ltError,ClassName,Format('Cannot receive at least 1 new block:%d',[LSafeboxLastOperationBlock.block]));
                   Exit;
                   Exit;
                 end;
                 end;
                 for i:=0 to blocksList.Count-1 do begin
                 for i:=0 to blocksList.Count-1 do begin
@@ -2025,7 +1999,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
               TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,IntToStr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)),Nil);
               TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,IntToStr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)),Nil);
               TNode.Node.Bank.Storage.DeleteBlockChainBlocks(start_block);
               TNode.Node.Bank.Storage.DeleteBlockChainBlocks(start_block);
 
 
-              newTmpBank.Storage.MoveBlockChainBlocks(safebox_last_operation_block.block,'',TNode.Node.Bank.Storage);
+              newTmpBank.Storage.MoveBlockChainBlocks(LSafeboxLastOperationBlock.block,'',TNode.Node.Bank.Storage);
               TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
               TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
             Finally
             Finally
               TNode.Node.EnableNewBlocks;
               TNode.Node.EnableNewBlocks;
@@ -2037,12 +2011,11 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
             Connection.DisconnectInvalidClient(false,'Cannot load from stream! '+errors);
             Connection.DisconnectInvalidClient(false,'Cannot load from stream! '+errors);
             exit;
             exit;
           end;
           end;
-
         finally
         finally
           newTmpBank.Free;
           newTmpBank.Free;
         end;
         end;
       Finally
       Finally
-        safeboxStream.Free;
+        LChunks.Free;
       End;
       End;
     end else begin
     end else begin
       if IsMyBlockChainOk then begin
       if IsMyBlockChainOk then begin
@@ -3114,10 +3087,14 @@ procedure TNetConnection.DoProcess_GetSafeBox_Request(HeaderData: TNetHeaderData
 Var _blockcount : Cardinal;
 Var _blockcount : Cardinal;
     _safeboxHash : TRawBytes;
     _safeboxHash : TRawBytes;
     _from,_to : Cardinal;
     _from,_to : Cardinal;
+  {$IFDEF USE_ABSTRACTMEM}
+  Labstracmem : TPCAbstractMem;
+  {$ELSE}
+  sbHeader : TPCSafeBoxHeader;
+  {$ENDIF}
   sbStream : TStream;
   sbStream : TStream;
   responseStream : TStream;
   responseStream : TStream;
   antPos : Int64;
   antPos : Int64;
-  sbHeader : TPCSafeBoxHeader;
   errors : String;
   errors : String;
 begin
 begin
   {
   {
@@ -3142,16 +3119,54 @@ begin
     Exit;
     Exit;
   end;
   end;
   //
   //
-  sbStream := TNode.Node.Bank.Storage.CreateSafeBoxStream(_blockcount);
+
+  responseStream := TMemoryStream.Create;
   try
   try
-    responseStream := TMemoryStream.Create;
+    {$IFDEF USE_ABSTRACTMEM}
+    Labstracmem := TNode.Node.Bank.Storage.OpenSafeBoxCheckpoint(_blockcount);
+    try
+      If Not Assigned(Labstracmem) then begin
+        SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Safebox stream file for block %d not found',[_blockcount]));
+        exit;
+      end;
+      If Not TBaseType.Equals(Labstracmem.BufferBlocksHash.GetSafeBoxHash,_safeboxHash) then begin
+        DisconnectInvalidClient(false,Format('Invalid safeboxhash on GetSafeBox request (Real:%s > Requested:%s)',[TCrypto.ToHexaString(Labstracmem.BufferBlocksHash.GetSafeBoxHash),TCrypto.ToHexaString(_safeboxHash)]));
+        exit;
+      end;
+
+
+      sbStream := TMemoryStream.Create;
+      try
+        if Not TPCSafeBox.CopyAbstractMemToSafeBoxStream(Labstracmem,sbStream,_from,_to,errors) then begin
+          SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Invalid Safebox stream for block %d',[_blockcount]));
+          TLog.NewLog(ltError,Classname,'Error CopyAbstractMemToSafeBoxStream: '+errors);
+          exit;
+        end;
+
+        // Response:
+        sbStream.Position:=0;
+        If not TPCChunk.SaveSafeBoxChunkFromSafeBox(sbStream,responseStream,_from,_to,errors) then begin
+          TLog.NewLog(ltError,Classname,'Error saving chunk: '+errors);
+          exit;
+        end;
+      finally
+        sbStream.Free;
+      end;
+    finally
+      FreeAndNil(Labstracmem);
+    end;
+    {$ELSE}
+    sbStream := TNode.Node.Bank.Storage.CreateSafeBoxStream(_blockcount);
     try
     try
       If Not Assigned(sbStream) then begin
       If Not Assigned(sbStream) then begin
-        SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Safebox for block %d not found',[_blockcount]));
+        SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Safebox stream file for block %d not found',[_blockcount]));
         exit;
         exit;
       end;
       end;
       antPos := sbStream.Position;
       antPos := sbStream.Position;
-      TPCSafeBox.LoadSafeBoxStreamHeader(sbStream,sbHeader);
+      If Not TPCSafeBox.LoadSafeBoxStreamHeader(sbStream,sbHeader) then begin
+        SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Invalid Safebox stream for block %d',[_blockcount]));
+        exit;
+      end;
       If Not TBaseType.Equals(sbHeader.safeBoxHash,_safeboxHash) then begin
       If Not TBaseType.Equals(sbHeader.safeBoxHash,_safeboxHash) then begin
         DisconnectInvalidClient(false,Format('Invalid safeboxhash on GetSafeBox request (Real:%s > Requested:%s)',[TCrypto.ToHexaString(sbHeader.safeBoxHash),TCrypto.ToHexaString(_safeboxHash)]));
         DisconnectInvalidClient(false,Format('Invalid safeboxhash on GetSafeBox request (Real:%s > Requested:%s)',[TCrypto.ToHexaString(sbHeader.safeBoxHash),TCrypto.ToHexaString(_safeboxHash)]));
         exit;
         exit;
@@ -3162,14 +3177,15 @@ begin
         TLog.NewLog(ltError,Classname,'Error saving chunk: '+errors);
         TLog.NewLog(ltError,Classname,'Error saving chunk: '+errors);
         exit;
         exit;
       end;
       end;
-      // Sending
-      Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,responseStream);
-      TLog.NewLog(ltInfo,ClassName,Format('Sending Safebox(%d) chunk[%d..%d] to %s Bytes:%d',[_blockcount,_from,_to,ClientRemoteAddr,responseStream.Size]));
     finally
     finally
-      responseStream.Free;
+      FreeAndNil(sbStream);
     end;
     end;
+    {$ENDIF}
+    // Sending
+    Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,responseStream);
+    TLog.NewLog(ltInfo,ClassName,Format('Sending Safebox(%d) chunk[%d..%d] to %s Bytes:%d',[_blockcount,_from,_to,ClientRemoteAddr,responseStream.Size]));
   finally
   finally
-    FreeAndNil(sbStream);
+    responseStream.Free;
   end;
   end;
 end;
 end;
 
 
@@ -3318,8 +3334,10 @@ begin
         opht.Free;
         opht.Free;
       end;
       end;
     end;
     end;
-    TLog.NewLog(ltInfo,Classname,Format('Processed GetPendingOperations to %s obtaining %d (available %d) operations and added %d to Node',
-      [Self.ClientRemoteAddr,cTotal,cTotalByOther,cAddedOperations]));
+    if cAddedOperations>0 then begin
+      TLog.NewLog(ltInfo,Classname,Format('Processed GetPendingOperations to %s obtaining %d (available %d) operations and added %d to Node',
+        [Self.ClientRemoteAddr,cTotal,cTotalByOther,cAddedOperations]));
+    end;
   finally
   finally
     dataSend.Free;
     dataSend.Free;
     dataReceived.Free;
     dataReceived.Free;
@@ -3329,14 +3347,14 @@ end;
 procedure TNetConnection.DoProcess_GetPubkeyAccounts_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 procedure TNetConnection.DoProcess_GetPubkeyAccounts_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 Const CT_Max_Accounts_per_call = 1000;
 Const CT_Max_Accounts_per_call = 1000;
 var responseStream, accountsStream : TMemoryStream;
 var responseStream, accountsStream : TMemoryStream;
-  start,max,iPubKey : Integer;
+  start,max : Integer;
   c, nAccounts : Cardinal;
   c, nAccounts : Cardinal;
   acc : TAccount;
   acc : TAccount;
   DoDisconnect : Boolean;
   DoDisconnect : Boolean;
   errors : String;
   errors : String;
   pubKey : TAccountKey;
   pubKey : TAccountKey;
-  sbakl : TOrderedAccountKeysList;
-  ocl : TOrderedCardinalList;
+  sbakl : TSafeboxPubKeysAndAccounts;
+  ocl : TAccountsNumbersList;
 begin
 begin
   {
   {
   This call is used to obtain Accounts used by a Public key
   This call is used to obtain Accounts used by a Public key
@@ -3381,9 +3399,8 @@ begin
     nAccounts := 0;
     nAccounts := 0;
     sbakl := TNode.Node.Bank.SafeBox.OrderedAccountKeysList;
     sbakl := TNode.Node.Bank.SafeBox.OrderedAccountKeysList;
     if Assigned(sbakl) then begin
     if Assigned(sbakl) then begin
-      iPubKey := sbakl.IndexOfAccountKey(pubKey);
-      if (iPubKey>=0) then begin
-        ocl := sbakl.AccountKeyList[iPubKey];
+      ocl := sbakl.GetAccountsUsingThisKey(pubKey);
+      if Assigned(ocl) then begin
         while (start<ocl.Count) And (max>0) do begin
         while (start<ocl.Count) And (max>0) do begin
           acc := TNode.Node.GetMempoolAccount(ocl.Get(start));
           acc := TNode.Node.GetMempoolAccount(ocl.Get(start));
           if (HeaderData.protocol.protocol_available>9) then
           if (HeaderData.protocol.protocol_available>9) then
@@ -4901,7 +4918,8 @@ begin
       i := 0;
       i := 0;
       if (candidates.Count>1) then i := Random(candidates.Count); // i = 0..count-1
       if (candidates.Count>1) then i := Random(candidates.Count); // i = 0..count-1
       nc := TNetConnection(candidates[i]);
       nc := TNetConnection(candidates[i]);
-      TNetData.NetData.GetNewBlockChainFromClient(nc,Format('Candidate block: %d Aggregated: %d %s',[nc.FRemoteOperationBlock.block,nc.FRemoteAccumulatedWork,nc.FRemoteAggregatedHashrate.ToDecimal]));
+      TNetData.NetData.GetNewBlockChainFromClient(nc,Format('Candidate block: %d Aggregated: %d %s (My %d %s)',[nc.FRemoteOperationBlock.block,nc.FRemoteAccumulatedWork,nc.FRemoteAggregatedHashrate.ToDecimal,
+        TNode.Node.Bank.SafeBox.WorkSum,TNode.Node.Bank.SafeBox.AggregatedHashrate.ToDecimal]));
     end;
     end;
   finally
   finally
     LMaxAggregatedHashrate.Free;
     LMaxAggregatedHashrate.Free;

+ 2 - 1
src/core/UNode.pas

@@ -35,7 +35,7 @@ interface
 
 
 uses
 uses
   Classes, SysUtils,
   Classes, SysUtils,
-  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes,
   UBlockChain, UNetProtocol, UAccounts, UCrypto, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
   UBlockChain, UNetProtocol, UAccounts, UCrypto, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
 
 
 {$I ./../config.inc}
 {$I ./../config.inc}
@@ -874,6 +874,7 @@ end;
 class function TNode.NodeVersion: String;
 class function TNode.NodeVersion: String;
 begin
 begin
   Result := CT_ClientAppVersion
   Result := CT_ClientAppVersion
+    {$IFDEF USE_ABSTRACTMEM}+'am'{$ENDIF}
     {$IFDEF LINUX}+'L'{$ELSE}+'W'{$ENDIF}
     {$IFDEF LINUX}+'L'{$ELSE}+'W'{$ENDIF}
     {$IFDEF FPC}{$IFDEF LCL}+'l'{$ELSE}+'f'{$ENDIF}{$ENDIF}
     {$IFDEF FPC}{$IFDEF LCL}+'l'{$ELSE}+'f'{$ENDIF}{$ENDIF}
     {$IFDEF FPC}{$IFDEF CPU32}+'32b'{$ELSE}+'64b'{$ENDIF}{$ELSE}{$IFDEF CPU32BITS}+'32b'{$ELSE}+'64b'{$ENDIF}{$ENDIF}
     {$IFDEF FPC}{$IFDEF CPU32}+'32b'{$ELSE}+'64b'{$ENDIF}{$ELSE}{$IFDEF CPU32BITS}+'32b'{$ELSE}+'64b'{$ENDIF}{$ENDIF}

+ 1092 - 0
src/core/UPCAbstractMem.pas

@@ -0,0 +1,1092 @@
+unit UPCAbstractMem;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+uses Classes, SysUtils, SyncObjs,
+  UAbstractMem, UFileMem, UAbstractMemTList,
+  UAbstractBTree, UThread,
+  UAVLCache, ULog, UCrypto,
+  UPCAbstractMemAccountKeys,
+  UPCDataTypes, UBaseTypes, UConst, UPCSafeBoxRootHash, UOrderedList,
+{$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+  EPCAbstractMem = class(Exception);
+
+  TPCAbstractMem = class;
+
+  TOperationBlockExt = record
+    operationBlock : TOperationBlock;
+    accumulatedWork : UInt64;
+  end;
+
+  TPCAbstractMemListBlocks = class(TAbstractMemTList<TOperationBlockExt>)
+  private
+    FPCAbstractMem: TPCAbstractMem;
+  protected
+    function ToString(const AItem: TOperationBlockExt): string; override;
+
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TOperationBlockExt); override;
+    procedure SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+  end;
+
+  TPCAbstractMemListAccounts = class;
+
+  TAccountNameInfo = record
+    accountName: string;
+    accountNumber: cardinal;
+  end;
+
+  { TPCAbstractMemListAccountNames }
+
+  TPCAbstractMemListAccountNames = class(TAbstractMemOrderedTList<TAccountNameInfo>)
+  private
+    FPCAbstractMem: TPCAbstractMem;
+  protected
+    FAccounts: TPCAbstractMemListAccounts;
+    function ToString(const AItem: TAccountNameInfo): string; override;
+
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo); override;
+    procedure SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+    function Compare(const ALeft, ARight: TAccountNameInfo): integer; override;
+  public
+    function IndexOf(const AName : String) : Integer;
+    procedure Remove(const AName : String);
+    procedure Add(const AName : String; AAccountNumber : Cardinal);
+    function FindByName(const AName : String; out AIndex : Integer) : Boolean;
+  end;
+
+  { TPCAbstractMemListAccounts }
+
+  TPCAbstractMemListAccounts = class(TAbstractMemTList<TAccount>)
+  private
+    FPCAbstractMem: TPCAbstractMem;
+  protected
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccount); override;
+    procedure SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+  end;
+
+  { TPCAbstractMemBytesBuffer32Safebox }
+
+  TPCAbstractMemBytesBuffer32Safebox = Class(TBytesBuffer32Safebox)
+  private
+    FAbstractMem: TAbstractMem;
+    FSaveBufferPosition : TAbstractMemPosition;
+  protected
+  public
+    Constructor Create(AAbstractMem : TAbstractMem; APosition : TAbstractMemPosition; ACurrBlocksCount : Integer);
+    procedure Flush;
+  end;
+
+  TPCAbstractMemCheckThread = Class(TPCThread)
+    FPCAbstractMem : TPCAbstractMem;
+    FErrorsCount : Integer;
+    FErrors : TStrings;
+    FMustRestart : Boolean;
+  protected
+    procedure BCExecute; override;
+  public
+    Constructor Create(APCAbstractMem : TPCAbstractMem);
+    Destructor Destroy;
+    procedure Restart;
+  End;
+
+  TAccountCache = Class(TAVLCache<TAccount>)
+  End;
+
+  TPCAbstractMem = class
+  private
+    FFileName : String;
+    FAbstractMem: TAbstractMem;
+    FCheckingThread : TPCAbstractMemCheckThread;
+    FLockAbstractMem : TPCCriticalSection;
+
+    FBlocks: TPCAbstractMemListBlocks;
+    FAccounts: TPCAbstractMemListAccounts;
+    FAccountsNames: TPCAbstractMemListAccountNames;
+    FAccountKeys: TPCAbstractMemAccountKeys;
+    FAccountCache : TAccountCache;
+    FBufferBlocksHash: TPCAbstractMemBytesBuffer32Safebox;
+    FAggregatedHashrate : TBigNum;
+    FZoneAggregatedHashrate : TAMZone;
+
+    function IsChecking : Boolean;
+    procedure DoCheck;
+
+
+    procedure AddBlockInfo(const ABlock : TOperationBlockExt);
+    procedure SetBlockInfo(const ABlock : TOperationBlockExt);
+    function DoInit(out AIsNewStructure : Boolean) : Boolean;
+
+  public
+    constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
+    class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
+    destructor Destroy; override;
+
+    function BlocksCount: integer;
+    function GetBlockInfo(ABlockNumber: cardinal): TOperationBlockExt;
+
+    procedure AddBlockAccount(const ABlockAccount : TBlockAccount);
+    procedure SetBlockAccount(const ABlockAccount : TBlockAccount);
+    function GetBlockAccount(const ABlockNumber : Integer) : TBlockAccount;
+    procedure DeleteBlockAccount(const ABlockNumber : Integer);
+
+    function AccountsCount: integer;
+    function GetAccount(AAccountNumber: cardinal): TAccount;
+    procedure SetAccount(const AAccount : TAccount);
+
+    property AccountKeys : TPCAbstractMemAccountKeys read FAccountKeys;
+    property AccountsNames: TPCAbstractMemListAccountNames read FAccountsNames;
+
+    property AbstractMem: TAbstractMem read FAbstractMem;
+    procedure FlushCache;
+    procedure CopyFrom(ASource : TPCAbstractMem);
+    //
+    property BufferBlocksHash: TPCAbstractMemBytesBuffer32Safebox read FBufferBlocksHash;
+    property AggregatedHashrate : TBigNum read FAggregatedHashrate;
+
+    function CheckConsistency(AReport : TStrings) : Boolean;
+    procedure SaveToFile(const ASaveToFileName : String);
+    procedure UpdateSafeboxFileName(const ANewSafeboxFileName : String);
+    property AccountCache : TAccountCache read FAccountCache;
+  end;
+
+implementation
+
+
+const
+  CT_PCAbstractMem_FileVersion = CT_PROTOCOL_5;
+
+function _AccountCache_Comparision(const Left, Right: TAccountCache.PAVLCacheMemData): Integer;
+begin
+  // Compare only by data.account number (not for content)
+  Result := Left^.data.account - Right^.data.account;
+end;
+
+{ TPCAbstractMemBytesBuffer32Safebox }
+
+constructor TPCAbstractMemBytesBuffer32Safebox.Create(AAbstractMem : TAbstractMem; APosition : TAbstractMemPosition; ACurrBlocksCount : Integer);
+var LZone : TAMZone;
+  LBufferBlockHashData,
+  LCachedSafeboxHash : TBytes;
+begin
+  FCachedSafeboxHash := Nil;
+  inherited Create(1000*32);
+  FAbstractMem := AAbstractMem;
+  FSaveBufferPosition:=APosition;
+  if (APosition>0) then begin
+    LZone.Clear;
+    FAbstractMem.Read(FSaveBufferPosition,LZone.position,4);
+    if FAbstractMem.GetUsedZoneInfo(LZone.position,True,LZone) then begin
+      // LZone contains info
+      // 32 bytes for FSavedSafeboxHash (as a cache)
+      // 32*ACurrBlocksCount bytes for data
+      // Minimum Size >= ((ACurrBlocksCount+1)*32)
+      if (LZone.size>=((ACurrBlocksCount+1) * 32)) then begin
+        // Valid
+        System.SetLength(LCachedSafeboxHash,32);
+        if FAbstractMem.Read(LZone.position,LCachedSafeboxHash[0],32)<>32 then Raise EPCAbstractMem.Create('Error dev 20200522-1');
+        System.SetLength(LBufferBlockHashData,ACurrBlocksCount * 32);
+        if FAbstractMem.Read(LZone.position + 32,LBufferBlockHashData[0],System.Length(LBufferBlockHashData))<>System.Length(LBufferBlockHashData) then Raise EPCAbstractMem.Create('Error dev 20200522-2');
+        Self.Clear;
+        Self.Add(LBufferBlockHashData);
+        FCachedSafeboxHash := LCachedSafeboxHash; // Set cached info
+      end;
+    end;
+  end;
+  if (Self.Length<>(ACurrBlocksCount*32)) then Raise EPCAbstractMem.Create(Format('Error dev 20200403-4 %d <> %d (%d)',[Self.Length,ACurrBlocksCount*32,ACurrBlocksCount]));
+end;
+
+procedure TPCAbstractMemBytesBuffer32Safebox.Flush;
+var LZone : TAMZone;
+begin
+  if FCachedSafeboxHash=Nil then FCachedSafeboxHash := GetSafeBoxHash;
+  LZone.Clear;
+  FAbstractMem.Read(FSaveBufferPosition,LZone.position,4);
+  if FAbstractMem.GetUsedZoneInfo(LZone.position,True,LZone) then begin
+    if ((Self.Length + 32)<=LZone.size) then begin
+      // Use same:
+      FAbstractMem.Write(LZone.position, FCachedSafeboxHash[0], 32);
+      FAbstractMem.Write(LZone.position + 32,Self.Memory^,Self.Length);
+      Exit;
+    end else begin
+      // Not enough space...
+      FAbstractMem.Dispose(LZone);
+    end;
+  end;
+  if (Self.Length>0) then begin
+    LZone := FAbstractMem.New(((Self.Length + 32) * 3) DIV 2);
+    FAbstractMem.Write(FSaveBufferPosition,LZone.position,4);
+    FAbstractMem.Write(LZone.position, FCachedSafeboxHash[0], 32);
+    FAbstractMem.Write(LZone.position + 32,Self.Memory^,Self.Length);
+  end;
+end;
+
+{ TPCAbstractMemListAccounts }
+
+procedure TPCAbstractMemListAccounts.LoadFrom(const ABytes: TBytes; var AItem: TAccount);
+var
+  LPointer: TAbstractMemPosition;
+  LStream : TStream;
+  w : Word;
+begin
+  AItem.Clear;
+  LStream := TMemoryStream.Create;
+  Try
+    LPointer := 0;
+    LStream.Write(ABytes[0],Length(ABytes));
+    LStream.Position := 0;
+
+    LStream.Read( AItem.account , 4 );
+
+    LStream.Read( w,2 );
+    if (w<>CT_PROTOCOL_5) then raise EPCAbstractMem.Create(Format('Invalid Account %d protocol %d',[AItem.account,w]));
+
+    LStream.Read( w, 2 );
+    case w of
+      CT_NID_secp256k1,CT_NID_secp384r1,CT_NID_sect283k1,CT_NID_secp521r1 : Begin
+        AItem.accountInfo.state := as_Normal;
+        LStream.Read(LPointer,4);
+        AItem.accountInfo.accountKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
+        if w<>AItem.accountInfo.accountKey.EC_OpenSSL_NID then raise EPCAbstractMem.Create('INCONSISTENT 20200318-2');
+      End;
+      CT_AccountInfo_ForSale, CT_AccountInfo_ForAccountSwap, CT_AccountInfo_ForCoinSwap : Begin
+        case w of
+          CT_AccountInfo_ForSale : AItem.accountInfo.state := as_ForSale;
+          CT_AccountInfo_ForAccountSwap : AItem.accountInfo.state := as_ForAtomicAccountSwap;
+          CT_AccountInfo_ForCoinSwap : AItem.accountInfo.state := as_ForAtomicCoinSwap;
+        end;
+        LStream.Read(LPointer,4);
+        AItem.accountInfo.accountKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
+
+        LStream.Read(AItem.accountInfo.locked_until_block,4);
+        LStream.Read(AItem.accountInfo.price,8);
+        LStream.Read(AItem.accountInfo.account_to_pay,4);
+        LStream.Read(LPointer,4);
+        AItem.accountInfo.new_publicKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
+        if (w<>CT_AccountInfo_ForSale) then begin
+          AItem.accountInfo.hashed_secret.FromSerialized(LStream);
+        end;
+
+      End;
+      else raise EPCAbstractMem.Create(Format('Unknow accountInfo type %d for account %d',[w,Aitem.account]));
+    end;
+    //
+    LStream.Read( AItem.balance , 8);
+    LStream.Read( AItem.updated_on_block_passive_mode , 4);
+    LStream.Read( AItem.updated_on_block_active_mode , 4);
+    LStream.Read( AItem.n_operation , 4);
+    AItem.name.FromSerialized( LStream );
+    LStream.Read( AItem.account_type ,2);
+    AItem.account_data.FromSerialized( LStream );
+    if AItem.account_seal.FromSerialized( LStream )<0 then raise EPCAbstractMem.Create('INCONSISTENT 20200318-4');
+  Finally
+    LStream.Free;
+  End;
+end;
+
+procedure TPCAbstractMemListAccounts.SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes);
+var LStream : TStream;
+  LPointer : TAbstractMemPosition;
+  w : Word;
+  LPrevious : TAccount;
+  LAddNewNameToAccountNamesList : Boolean;
+begin
+  if (Length(ABytes)>0) and (Not AIsAddingItem) then begin
+    // Capture previous values
+    LoadFrom(ABytes,LPrevious);
+    if (LPrevious.account<>AItem.account) then raise EPCAbstractMem.Create(Format('INCONSISTENT account number %d<>%d',[AItem.account,LPrevious.account]));
+
+    if Not LPrevious.accountInfo.accountKey.IsEqualTo( AItem.accountInfo.accountKey ) then begin
+      // Remove previous account link
+      FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndRemoveAccount( LPrevious.accountInfo.accountKey, LPrevious.account );
+    end;
+    if (Length(LPrevious.name)>0) and (Not LPrevious.name.IsEqualTo(AItem.name)) then begin
+      // Remove previous name
+      FPCAbstractMem.FAccountsNames.Remove(LPrevious.name.ToString);
+      LAddNewNameToAccountNamesList := True;
+    end else LAddNewNameToAccountNamesList := False;
+  end else LAddNewNameToAccountNamesList := True;
+
+  LStream := TMemoryStream.Create;
+  try
+    LStream.Position := 0;
+
+
+    LStream.Write( AItem.account , 4 );
+
+    w := CT_PROTOCOL_5;
+    LStream.Write( w, 2 );
+
+    w := 0;
+    case AItem.accountInfo.state of
+      as_Normal : begin
+        LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
+        LStream.Write( AItem.accountInfo.accountKey.EC_OpenSSL_NID , 2 );
+        LStream.Write( LPointer, 4);
+      end;
+      as_ForSale : w := CT_AccountInfo_ForSale;
+      as_ForAtomicAccountSwap : w := CT_AccountInfo_ForAccountSwap;
+      as_ForAtomicCoinSwap :  w := CT_AccountInfo_ForCoinSwap;
+    end;
+    if (w>0) then begin
+      LStream.Write(w,2);
+
+      LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
+      LStream.Write( LPointer, 4);
+
+      LStream.Write(AItem.accountInfo.locked_until_block,4);
+      LStream.Write(AItem.accountInfo.price,8);
+      LStream.Write(AItem.accountInfo.account_to_pay,4);
+      LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKey(AItem.accountInfo.new_publicKey,True);
+      LStream.Write(LPointer,4);
+      if (w<>CT_AccountInfo_ForSale) then begin
+        AItem.accountInfo.hashed_secret.ToSerialized(LStream);
+      end;
+    end;
+    //
+    LStream.Write( AItem.balance , 8);
+    LStream.Write( AItem.updated_on_block_passive_mode , 4);
+    LStream.Write( AItem.updated_on_block_active_mode , 4);
+    LStream.Write( AItem.n_operation , 4);
+
+    AItem.name.ToSerialized( LStream );
+    if (Length(AItem.name)>0) and (LAddNewNameToAccountNamesList) then begin
+      FPCAbstractMem.FAccountsNames.Add(AItem.name.ToString,AItem.account);
+    end;
+
+    LStream.Write( AItem.account_type ,2);
+    AItem.account_data.ToSerialized( LStream );
+    AItem.account_seal.ToSerialized( LStream );
+    //
+    ABytes.FromStream( LStream );
+
+  finally
+    LStream.Free;
+  end;
+end;
+
+{ TPCAbstractMem }
+
+function TPCAbstractMem.CheckConsistency(AReport: TStrings) : Boolean;
+begin
+end;
+
+procedure TPCAbstractMem.CopyFrom(ASource: TPCAbstractMem);
+var LIsNew : Boolean;
+begin
+  ASource.FlushCache;
+  FAbstractMem.CopyFrom(ASource.FAbstractMem);
+  DoInit(LIsNew);
+end;
+
+function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
+const
+  CT_HEADER_STRING = 'TPCAbstractMem'; // Do not localize/modify. Fixed 14 bytes!
+  {
+  Header:
+  [ 0..13] 14 bytes: Literal "TPCAbstractMem"
+  [14..15] 2 bytes: Protocol version
+  [16..19] 4 bytes: LZoneBlocks.position
+  [20..23] 4 bytes: LZoneAccounts.position
+  [24..27] 4 bytes: LZoneAccountsNames.position
+  [28..31] 4 bytes: LZoneAccountKeys.position
+  [32..35] 4 bytes: FZoneAggregatedHashrate.position
+  [36..39] 4 bytes: LZoneBuffersBlockHash
+
+
+  }
+var LZone,
+  LZoneBlocks,
+  LZoneAccounts,
+  LZoneAccountsNames,
+  LZoneAccountKeys : TAMZone;
+  LZoneBuffersBlockHash : TAbstractMemPosition;
+  LHeader, LBuffer, LBigNum : TBytes;
+  LIsGood : Boolean;
+  w : Word;
+  i : Integer;
+begin
+  // Free
+  FreeAndNil(FBlocks);
+  FreeAndNil(FAccounts);
+  FreeAndNil(FAccountsNames);
+  FreeAndNil(FAccountKeys);
+  FreeAndNil(FBufferBlocksHash);
+  //
+  Result := False;
+  AIsNewStructure := True;
+  LZone.Clear;
+  LZoneBlocks.Clear;
+  LZoneAccounts.Clear;
+  LZoneAccountsNames.Clear;
+  LZoneAccountKeys.Clear;
+  FZoneAggregatedHashrate.Clear;
+  LZoneBuffersBlockHash := 0;
+
+  if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
+    // Check if header is valid:
+    if Length(LHeader)>=100 then begin
+      LIsGood := True;
+      i := 0;
+      while (LIsGood) and (i<CT_HEADER_STRING.Length) do begin
+        LIsGood := ord(CT_HEADER_STRING.Chars[i])=LHeader[i];
+        inc(i);
+      end;
+      if i<>14 then LIsGood := False;
+      if LIsGood then begin
+        Move(LHeader[14], w, 2);
+        LIsGood := (w = CT_PCAbstractMem_FileVersion);
+      end;
+      if LIsGood then begin
+        Move(LHeader[16], LZoneBlocks.position, 4);
+        Move(LHeader[20], LZoneAccounts.position, 4);
+        Move(LHeader[24], LZoneAccountsNames.position, 4);
+        Move(LHeader[28], LZoneAccountKeys.position, 4);
+        Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
+        LZoneBuffersBlockHash := LZone.position + 36;
+        AIsNewStructure := False;
+      end;
+    end;
+  end;
+  if (Not FAbstractMem.ReadOnly) and (AIsNewStructure) then begin
+    // Initialize struct
+    FAbstractMem.ClearContent;
+    LZone := FAbstractMem.New( 100 );  // Header zone
+    SetLength(LHeader,100);
+    FillChar(LHeader[0],Length(LHeader),0);
+    //
+    LBuffer.FromString(CT_HEADER_STRING);
+    Move(LBuffer[0],LHeader[0],14);
+    w := CT_PCAbstractMem_FileVersion;
+    Move(w,LHeader[14],2);
+    LZoneBlocks := FAbstractMem.New( CT_AbstractMemTList_HeaderSize );
+    LZoneAccounts := FAbstractMem.New( CT_AbstractMemTList_HeaderSize );
+    LZoneAccountsNames := FAbstractMem.New( CT_AbstractMemTList_HeaderSize );
+    LZoneAccountKeys := FAbstractMem.New( 100 );
+    FZoneAggregatedHashrate := FAbstractMem.New(100); // Note: Enough big to store a BigNum
+    LZoneBuffersBlockHash := LZone.position+36;
+
+    Move(LZoneBlocks.position,       LHeader[16],4);
+    Move(LZoneAccounts.position,     LHeader[20],4);
+    Move(LZoneAccountsNames.position,LHeader[24],4);
+    Move(LZoneAccountKeys.position,  LHeader[28],4);
+    Move(FZoneAggregatedHashrate.position,LHeader[32],4);
+
+    FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+  end;
+  // Free
+  FreeAndNil(FBlocks);
+  //
+  FBlocks := TPCAbstractMemListBlocks.Create( FAbstractMem, LZoneBlocks, 10000 );
+  FBlocks.FPCAbstractMem := Self;
+  FAccounts := TPCAbstractMemListAccounts.Create( FAbstractMem, LZoneAccounts, 50000);
+  FAccounts.FPCAbstractMem := Self;
+  FAccountsNames := TPCAbstractMemListAccountNames.Create( FAbstractMem, LZoneAccountsNames, 5000 , False);
+  FAccountsNames.FPCAbstractMem := Self;
+  FAccountKeys := TPCAbstractMemAccountKeys.Create( FAbstractMem, LZoneAccountKeys.position );
+  // Read AggregatedHashrate
+  SetLength(LBuffer,100);
+  FAbstractMem.Read(FZoneAggregatedHashrate.position,LBuffer[0],Length(LBuffer));
+  if LBigNum.FromSerialized(LBuffer) then begin
+    FAggregatedHashrate.RawValue := LBigNum;
+  end;
+  FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
+
+  FAccountCache.Clear;
+end;
+
+constructor TPCAbstractMem.Create(const ASafeboxFileName: string; AReadOnly: boolean);
+var
+  LIsNewStructure : Boolean;
+begin
+  FCheckingThread := Nil;
+  FLockAbstractMem := TPCCriticalSection.Create(Self.ClassName);
+  FAccountCache := TAccountCache.Create(10000,_AccountCache_Comparision);
+
+  FAggregatedHashrate := TBigNum.Create(0);
+  FFileName := ASafeboxFileName;
+  if (FFileName<>'') {and (FileExists(ASafeboxFileName))} then begin
+    FAbstractMem := TFileMem.Create( ASafeboxFileName , AReadOnly)
+  end else begin
+    FAbstractMem := TMem.Create(0,AReadOnly);
+  end;
+  if FAbstractMem is TFileMem then begin
+    TFileMem(FAbstractMem).MaxCacheSize := 40 * 1024 * 1024; // 40Mb
+    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+  end;
+
+  DoInit(LIsNewStructure);
+  //
+  if ((BlocksCount>0) And (ASafeboxFileName<>'')) Or (Not LIsNewStructure) then begin
+    TLog.NewLog(ltdebug,ClassName,Format('Opened PascalCoin AbstractMem File with %d blocks %d accounts %s aggregated hashrate and buffer %d size (%d blocks) at file: %s',
+      [BlocksCount,AccountsCount,FAggregatedHashrate.ToDecimal,FBufferBlocksHash.Length,FBufferBlocksHash.Length DIV 32,ASafeboxFileName]));
+  end;
+end;
+
+destructor TPCAbstractMem.Destroy;
+var LFile : TFileStream;
+begin
+  FLockAbstractMem.Acquire;
+  try
+    if Assigned(FCheckingThread) then begin
+      FCheckingThread.Terminate;
+    end;
+  finally
+    FLockAbstractMem.Release;
+  end;
+
+  FlushCache;
+  FreeAndNil(FAccountCache);
+  FreeAndNil(FBlocks);
+  FreeAndNil(FAccounts);
+  FreeAndNil(FAccountsNames);
+  FreeAndNil(FAccountKeys);
+  FreeAndNil(FBufferBlocksHash);
+  FreeAndNil(FAggregatedHashrate);
+  if (FFileName<>'') And (FAbstractMem is TMem) And (Not FAbstractMem.ReadOnly) then begin
+    LFile := TFileStream.Create(FFileName,fmCreate);
+    try
+      LFile.Size := 0;
+      LFile.Position := 0;
+      FAbstractMem.SaveToStream(LFile);
+    finally
+      LFile.Free;
+    end;
+  end;
+  FreeAndNil(FAbstractMem);
+
+  FreeAndNil(FLockAbstractMem);
+
+  inherited Destroy;
+end;
+
+procedure TPCAbstractMem.DoCheck;
+begin
+  if IsChecking then Exit;
+  FLockAbstractMem.Acquire;
+  Try
+    if Not Assigned(FCheckingThread) then begin
+      FCheckingThread := TPCAbstractMemCheckThread.Create(Self);
+    end;
+  Finally
+    FLockAbstractMem.Release;
+  End;
+end;
+
+procedure TPCAbstractMem.FlushCache;
+var LBigNum : TBytes;
+begin
+  if FAbstractMem.ReadOnly then Exit;
+  FBlocks.FlushCache;
+  FAccounts.FlushCache;
+  FAccountsNames.FlushCache;
+  FAccountKeys.FlushCache;
+  FBufferBlocksHash.Flush;
+  LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
+  FAbstractMem.Write( FZoneAggregatedHashrate.position, LBigNum[0], Length(LBigNum) );
+  if FAbstractMem is TFileMem then begin
+    TFileMem(FAbstractMem).FlushCache;
+  end;
+end;
+
+Procedure DoCopyFile(const ASource, ADest : String);
+var LSourceFS, LDestFS : TFileStream;
+Begin
+  if Not FileExists(ASource) then Raise Exception.Create('Source file not found: '+ASource);
+  LSourceFS := TFileStream.Create(ASource,fmOpenRead+fmShareDenyNone);
+  try
+    LSourceFS.Position:=0;
+    LDestFS := TFileStream.Create(ADest,fmCreate+fmShareDenyWrite);
+    try
+      LDestFS.Size:=0;
+      LDestFS.CopyFrom(LSourceFS,LSourceFS.Size);
+    finally
+      LDestFS.Free;
+    end;
+  finally
+    LSourceFS.Free;
+  end;
+end;
+
+
+procedure TPCAbstractMem.SaveToFile(const ASaveToFileName: String);
+var LFile : TFileStream;
+begin
+  FlushCache;
+  //
+  ForceDirectories(ExtractFileDir(ASaveToFileName));
+  if FileExists(ASaveToFileName) then DeleteFile(ASaveToFileName);
+  //
+  if (FAbstractMem is TFileMem) then begin
+    DoCopyFile(TFileMem(FAbstractMem).FileName,ASaveToFileName);
+  end else begin
+    LFile := TFileStream.Create(ASaveToFileName,fmCreate);
+    try
+      LFile.Size := 0;
+      LFile.Position := 0;
+      FAbstractMem.SaveToStream(LFile);
+    finally
+      LFile.Free;
+    end;
+  end;
+end;
+
+procedure TPCAbstractMem.SetAccount(const AAccount: TAccount);
+begin
+  if (AAccount.account<0) or (AAccount.account>FAccounts.Count) then begin
+    raise EPCAbstractMem.Create(Format('Account %d not in range %d..%d',[AAccount.account,0,FAccounts.Count]));
+  end;
+  FAccountCache.Remove(AAccount);
+  if (AAccount.account = FAccounts.Count) then begin
+    FAccounts.Add(AAccount);
+  end else begin
+    FAccounts.SetItem( AAccount.account , AAccount);
+  end;
+  // Update cache
+  FAccountCache.Add(AAccount);
+end;
+
+procedure TPCAbstractMem.AddBlockAccount(const ABlockAccount: TBlockAccount);
+var i : Integer;
+  LOpBlockExt :  TOperationBlockExt;
+begin
+  LOpBlockExt.operationBlock := ABlockAccount.blockchainInfo;
+  LOpBlockExt.accumulatedWork := ABlockAccount.accumulatedWork;
+  AddBlockInfo(LOpBlockExt);
+  for i := Low(ABlockAccount.accounts) to High(ABlockAccount.accounts) do begin
+    SetAccount( ABlockAccount.accounts[i] );
+  end;
+  FBufferBlocksHash.Replace(ABlockAccount.blockchainInfo.block * 32, ABlockAccount.block_hash);
+end;
+
+procedure TPCAbstractMem.SetBlockAccount(const ABlockAccount: TBlockAccount);
+var i : Integer;
+  LOpBlockExt :  TOperationBlockExt;
+begin
+  LOpBlockExt.operationBlock := ABlockAccount.blockchainInfo;
+  LOpBlockExt.accumulatedWork := ABlockAccount.accumulatedWork;
+  SetBlockInfo(LOpBlockExt);
+  for i := Low(ABlockAccount.accounts) to High(ABlockAccount.accounts) do begin
+    SetAccount( ABlockAccount.accounts[i] );
+  end;
+  FBufferBlocksHash.Replace(ABlockAccount.blockchainInfo.block * 32, ABlockAccount.block_hash);
+end;
+
+procedure TPCAbstractMem.AddBlockInfo(const ABlock : TOperationBlockExt);
+begin
+  if (ABlock.operationBlock.block<>FBlocks.Count) then raise EPCAbstractMem.Create(Format('Cannot add blockInfo %d at pos %d',[ABlock.operationBlock.block,FBlocks.Count]));
+  SetBlockInfo(ABlock);
+end;
+
+class function TPCAbstractMem.AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount: Integer): Boolean;
+var LPCAbstractMem : TPCAbstractMem;
+begin
+  ABlocksCount := 0;
+  Result := False;
+  if Not FileExists(ASafeboxFileName) then Exit(False);
+  LPCAbstractMem := TPCAbstractMem.Create(ASafeboxFileName,True);
+  try
+    ABlocksCount := LPCAbstractMem.BlocksCount;
+    Result := (ABlocksCount>0) And (LPCAbstractMem.AccountsCount = ABlocksCount * CT_AccountsPerBlock);
+  finally
+    LPCAbstractMem.Free;
+  end;
+end;
+
+procedure TPCAbstractMem.SetBlockInfo(const ABlock: TOperationBlockExt);
+var LCount : Integer;
+begin
+  LCount := FBlocks.Count;
+  if ABlock.operationBlock.block<LCount then begin
+    FBlocks.Item[ABlock.operationBlock.block] := ABlock;
+  end else if ABlock.operationBlock.block=LCount then begin
+    FBlocks.Add( ABlock );
+  end else raise EPCAbstractMem.Create(Format('Cannot set block info %d (current %d blocks)',[ABlock.operationBlock.block,LCount]));
+end;
+
+procedure TPCAbstractMem.UpdateSafeboxFileName(const ANewSafeboxFileName: String);
+var LReadOnly, Ltmp : Boolean;
+begin
+  if SameFileName(FFileName,ANewSafeboxFileName) then Exit;
+
+  if ANewSafeboxFileName<>'' then
+    SaveToFile(ANewSafeboxFileName);
+
+  FFileName := ANewSafeboxFileName;
+  LReadOnly := FAbstractMem.ReadOnly;
+  FreeAndNil(FAbstractMem);
+
+  if (FFileName<>'') then begin
+    FAbstractMem := TFileMem.Create( FFileName , LReadOnly)
+  end else begin
+    FAbstractMem := TMem.Create(0,LReadOnly);
+  end;
+  if FAbstractMem is TFileMem then begin
+    TFileMem(FAbstractMem).MaxCacheSize := 40 * 1024 * 1024; // 40Mb
+    TFileMem(FAbstractMem).MaxCacheDataBlocks := 200000;
+  end;
+  DoInit(Ltmp);
+end;
+
+function TPCAbstractMem.BlocksCount: integer;
+begin
+  Result := FBlocks.Count;
+end;
+
+function TPCAbstractMem.GetBlockAccount(const ABlockNumber: Integer): TBlockAccount;
+var i : Integer;
+ LBlock : TOperationBlockExt;
+begin
+  Result := CT_BlockAccount_NUL;
+  LBlock := GetBlockInfo(ABlockNumber);
+  Result.blockchainInfo := LBlock.operationBlock;
+  Result.accumulatedWork := LBlock.accumulatedWork;
+
+  Result.block_hash := FBufferBlocksHash.Capture( ABlockNumber * 32, 32);
+
+  for i := Low(Result.accounts) to High(Result.accounts) do begin
+    Result.accounts[i] := GetAccount( i + (ABlockNumber * CT_AccountsPerBlock) );
+  end;
+end;
+
+procedure TPCAbstractMem.DeleteBlockAccount(const ABlockNumber: Integer);
+var i : Integer;
+  LBlAcc :  TBlockAccount;
+  LExtract : TBytes;
+begin
+  LBlAcc := GetBlockAccount(ABlockNumber);
+  FBlocks.Delete(ABlockNumber);
+  for i := Low(LBlAcc.accounts) to High(LBlAcc.accounts) do begin
+    FAccounts.Delete(LBlAcc.accounts[i].account );
+  end;
+  LExtract := FBufferBlocksHash.Capture((ABlockNumber+1)*32,32);
+  FBufferBlocksHash.Replace(ABlockNumber*32,LExtract);
+  FBufferBlocksHash.SetLength(BufferBlocksHash.Length - 32);
+end;
+
+function TPCAbstractMem.GetBlockInfo(ABlockNumber: cardinal): TOperationBlockExt;
+begin
+  Result := FBlocks.GetItem( ABlockNumber );
+end;
+
+function TPCAbstractMem.IsChecking: Boolean;
+begin
+  Result := Assigned(TPCThread.GetThreadByClass(TPCAbstractMemCheckThread,Nil));
+  FLockAbstractMem.Acquire;
+  Try
+    Result := Assigned(FCheckingThread);
+  Finally
+    FLockAbstractMem.Release;
+  End;
+end;
+
+function TPCAbstractMem.AccountsCount: integer;
+begin
+  Result := FAccounts.Count;
+end;
+
+function TPCAbstractMem.GetAccount(AAccountNumber: cardinal): TAccount;
+begin
+  Result.Clear;
+  Result.account := AAccountNumber;
+  if Not FAccountCache.Find(Result,Result) then begin
+    Result := FAccounts.GetItem( AAccountNumber );
+    // Save for future usage:
+    FAccountCache.Add(Result);
+  end;
+end;
+
+{ TPCAbstractMemListAccountNames }
+
+function TPCAbstractMemListAccountNames.ToString(const AItem: TAccountNameInfo): string;
+begin
+  Result:= Format('AccountNameInfo: Account:%d Name(%d):%d',[AItem.accountNumber, Length(AItem.accountName), AItem.accountName]);
+end;
+
+function TPCAbstractMemListAccountNames.IndexOf(const AName: String): Integer;
+var LFind : TAccountNameInfo;
+begin
+  LFind.accountName := AName;
+  LFind.accountNumber := 0;
+  if Not Find(LFind,Result) then Result := -1;
+end;
+
+procedure TPCAbstractMemListAccountNames.LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
+var LTmp : TBytes;
+begin
+  if Not LTmp.FromSerialized(ABytes) then raise EPCAbstractMem.Create('INCONSISTENT 20200318-5');
+  AItem.accountName := LTmp.ToString;
+  Move(ABytes[LTmp.GetSerializedLength],AItem.accountNumber,4);
+end;
+
+procedure TPCAbstractMemListAccountNames.Remove(const AName: String);
+var i : Integer;
+begin
+  i := IndexOf(AName);
+  if i>=0 then Delete(i);
+end;
+
+procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes);
+var LStream : TStream;
+  LTmp : TBytes;
+begin
+  LStream := TMemoryStream.Create;
+  Try
+    LTmp.FromString(AItem.accountName);
+    LTmp.ToSerialized(LStream);
+    LStream.Write(AItem.accountNumber,4);
+    //
+    ABytes.FromStream(LStream);
+  Finally
+    LStream.Free;
+  End;
+end;
+
+procedure TPCAbstractMemListAccountNames.Add(const AName: String; AAccountNumber: Cardinal);
+var LItem : TAccountNameInfo;
+  i : Integer;
+begin
+  LItem.accountName := AName;
+  LItem.accountNumber := AAccountNumber;
+  i := inherited Add(LItem);
+  if (i<0) then begin
+    i := IndexOf(AName);
+    if (i<0) then
+      raise EPCAbstractMem.Create(Format('Fatal error Cannot add account(%d) name %s',[AAccountNumber,AName]))
+    else raise EPCAbstractMem.Create(Format('Cannot add account(%d) name %s because used by %d with %s',[AAccountNumber,AName,
+      GetItem(i).accountNumber,GetItem(i).accountName]));
+  end;
+end;
+
+function TPCAbstractMemListAccountNames.Compare(const ALeft, ARight: TAccountNameInfo): integer;
+Var LBytesLeft,LBytesRight : TBytes;
+begin
+  LBytesLeft.FromString(ALeft.accountName);
+  LBytesRight.FromString(ARight.accountName);
+  Result := TBaseType.BinStrComp(LBytesLeft,LBytesRight);
+end;
+
+function TPCAbstractMemListAccountNames.FindByName(const AName: String; out AIndex: Integer): Boolean;
+var LFind : TAccountNameInfo;
+begin
+  LFind.accountName := AName;
+  LFind.accountNumber := 0;
+  Result := Find(LFind,AIndex);
+end;
+
+{ TPCAbstractMemListBlocks }
+
+procedure TPCAbstractMemListBlocks.LoadFrom(const ABytes: TBytes; var AItem: TOperationBlockExt);
+var
+  LPointer: TAbstractMemPosition;
+  LIndex: integer;
+begin
+  AItem.accumulatedWork := 0;
+  Move(ABytes[0], AItem.operationBlock.block, 4);
+  Move(ABytes[4], LPointer, 4);
+  // Load account_key
+  AItem.operationBlock.account_key := FPCAbstractMem.FAccountKeys.GetKeyAtPosition(LPointer);
+
+  Move(ABytes[8], AItem.operationBlock.reward, 8);
+  Move(ABytes[16], AItem.operationBlock.fee, 8);
+  Move(ABytes[24], AItem.operationBlock.protocol_version, 2);
+  Move(ABytes[26], AItem.operationBlock.protocol_available, 2);
+  Move(ABytes[28], AItem.operationBlock.timestamp, 4);
+  Move(ABytes[32], AItem.operationBlock.compact_target, 4);
+  Move(ABytes[36], AItem.operationBlock.nonce, 4);
+  Move(ABytes[40], AItem.accumulatedWork, 8);
+
+  LIndex := 48;
+  if not AItem.operationBlock.block_payload.LoadFromTBytes(ABytes, LIndex) then
+    raise EPCAbstractMem.Create(Format('LoadFrom Invalid 20200317-2 %d', [LIndex]));
+  if not AItem.operationBlock.initial_safe_box_hash.LoadFromTBytes(ABytes, LIndex) then
+    raise EPCAbstractMem.Create(Format('LoadFrom Invalid 20200317-3 %d', [LIndex]));
+  if not AItem.operationBlock.operations_hash.LoadFromTBytes(ABytes, LIndex) then
+    raise EPCAbstractMem.Create(Format('LoadFrom Invalid 20200317-4 %d', [LIndex]));
+  if not AItem.operationBlock.proof_of_work.LoadFromTBytes(ABytes, LIndex) then
+    raise EPCAbstractMem.Create(Format('LoadFrom Invalid 20200317-5 %d', [LIndex]));
+  if not AItem.operationBlock.previous_proof_of_work.LoadFromTBytes(ABytes, LIndex) then
+    raise EPCAbstractMem.Create(Format('LoadFrom Invalid 20200317-6 %d', [LIndex]));
+end;
+
+procedure TPCAbstractMemListBlocks.SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes);
+var
+  LPointer: TAbstractMemPosition;
+  LStream : TStream;
+begin
+  LStream := TMemoryStream.Create;
+  Try
+    LStream.Write(AItem.operationBlock.block, 4);
+    // Pointer
+    LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKey(AItem.operationBlock.account_key,True);
+    LStream.Write(LPointer, 4);
+    LStream.Write(AItem.operationBlock.reward, 8);
+    LStream.Write(AItem.operationBlock.fee, 8);
+    LStream.Write(AItem.operationBlock.protocol_version, 2);
+    LStream.Write(AItem.operationBlock.protocol_available, 2);
+    LStream.Write(AItem.operationBlock.timestamp, 4);
+    LStream.Write(AItem.operationBlock.compact_target, 4);
+    LStream.Write(AItem.operationBlock.nonce, 4);
+    LStream.Write(AItem.accumulatedWork, 8);
+    AItem.operationBlock.block_payload.ToSerialized(LStream);
+    AItem.operationBlock.initial_safe_box_hash.ToSerialized(LStream);
+    AItem.operationBlock.operations_hash.ToSerialized(LStream);
+    AItem.operationBlock.proof_of_work.ToSerialized(LStream);
+    AItem.operationBlock.previous_proof_of_work.ToSerialized(LStream);
+    ABytes.FromStream(LStream);
+  Finally
+    LStream.Free;
+  End;
+end;
+
+function TPCAbstractMemListBlocks.ToString(const AItem: TOperationBlockExt): string;
+begin
+  Result := Format('Block %d AccWork:%s', [AItem.operationBlock.block,AItem.accumulatedWork]);
+end;
+
+{ TPCAbstractMemCheckThread }
+
+procedure TPCAbstractMemCheckThread.BCExecute;
+  procedure _error(const AError : String);
+  begin
+    FErrors.Add( AError );
+    inc(FErrorsCount);
+    TLog.NewLog(ltError,ClassName,'CheckConsistency: '+AError);
+  end;
+var iBlock, i, iAccName : Integer;
+  LAccount : TAccount;
+  LBlockAccount : TBlockAccount;
+  LOrdered : TOrderedList<Integer>;
+  LOrderedNames : TOrderedList<String>;
+  LAccountNameInfo : TAccountNameInfo;
+  LTC, LTCInitial : TTickCount;
+  LAggregatedHashrate, LBlockHashRate : TBigNum;
+begin
+  iBlock :=0;
+  LOrdered := TOrderedList<Integer>.Create(False,TComparison_Integer);
+  LOrderedNames := TOrderedList<String>.Create(False,TComparison_String);
+  LAggregatedHashrate := TBigNum.Create;
+  Try
+    LTC := TPlatform.GetTickCount;
+    LTCInitial := LTC;
+    while (iBlock < FPCAbstractMem.BlocksCount) and (Not Terminated) do begin
+      if FMustRestart then begin
+        TLog.NewLog(ltdebug,ClassName,Format('Restarting check thread after %d/%d blocks',[iBlock+1,FPCAbstractMem.BlocksCount]) );
+        FMustRestart := False;
+        FErrorsCount := 0;
+        FErrors.Clear;
+        iBlock := 0;
+        LOrdered.Clear;
+        LOrderedNames.Clear;
+        LAggregatedHashrate.Value := 0;
+      end;
+
+      LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
+      for i:=Low(LBlockAccount.accounts) to high(LBlockAccount.accounts) do begin
+        //
+        LAccount := LBlockAccount.accounts[i];
+        if Length(LAccount.name)>0 then begin
+          if LOrderedNames.Add(LAccount.name.ToString)<0 then begin
+            _error(Format('Account %d name %s allready added',[LAccount.account,LAccount.name.ToString]));
+          end;
+          iAccName := FPCAbstractMem.AccountsNames.IndexOf(LAccount.name.ToString);
+          if iAccName<0 then begin
+            // ERROR
+            _error(Format('Account %d name %s not found at list',[LAccount.account,LAccount.name.ToString]));
+          end else begin
+            if FPCAbstractMem.AccountsNames.Item[iAccName].accountNumber<>LAccount.account then begin
+              _error(Format('Account %d name %s found at list at pos %d but links to %d',[LAccount.account,LAccount.name.ToString,iAccName,FPCAbstractMem.AccountsNames.Item[iAccName].accountNumber]));
+            end;
+            if (LOrdered.Add(LAccount.account)<0) then begin
+              _error(Format('Account %d (with name %s) allready added',[LAccount.account,LAccount.name.ToString]));
+            end;
+          end;
+        end;
+      end;
+
+      LBlockHashRate := TBigNum.TargetToHashRate( LBlockAccount.blockchainInfo.compact_target );
+      Try
+        LAggregatedHashrate.Add( LBlockHashRate );
+      finally
+        LBlockHashRate.Free;
+      end;
+
+      if (TPlatform.GetElapsedMilliseconds(LTC)>2000) then begin
+        LTC := TPlatform.GetTickCount;
+        TLog.NewLog(ltDebug,ClassName,Format('Checking consistency at %d/%d',[iBlock+1,FPCAbstractMem.BlocksCount]));
+      end;
+      inc(iBlock);
+    end;
+    //
+    for i := 0 to FPCAbstractMem.AccountsNames.Count-1 do begin
+      LAccountNameInfo := FPCAbstractMem.AccountsNames.Item[i];
+      if LOrdered.IndexOf( LAccountNameInfo.accountNumber ) < 0 then begin
+        _error(Format('Account name %s at index %d/%d not found in search',[LAccountNameInfo.accountName, i+1,FPCAbstractMem.AccountsNames.Count]));
+      end;
+    end;
+    if (LOrdered.Count)<>FPCAbstractMem.AccountsNames.Count then begin
+      _error(Format('Found %d accounts with names but %d on list',[LOrdered.Count,FPCAbstractMem.AccountsNames.Count]));
+    end;
+    if (LOrderedNames.Count)<>FPCAbstractMem.AccountsNames.Count then begin
+      _error(Format('Found %d accounts with names but %d on ordered names list',[FPCAbstractMem.AccountsNames.Count,LOrderedNames.Count]));
+    end;
+    //
+    if FPCAbstractMem.AggregatedHashrate.CompareTo(LAggregatedHashrate)<>0 then begin
+      _error(Format('Different AggregatedHashRate Found %s vs previous %s',[LAggregatedHashrate.ToDecimal,FPCAbstractMem.AggregatedHashrate.ToDecimal]));
+      FPCAbstractMem.AggregatedHashrate.RawValue := LAggregatedHashrate.RawValue;
+    end;
+
+  finally
+    LOrdered.Free;
+    LOrderedNames.Free;
+    LAggregatedHashrate.Free;
+  end;
+
+  TLog.NewLog(ltDebug,ClassName,Format('Finalized checking consistency at %d blocks in %.2f milis',[iBlock+1,FPCAbstractMem.BlocksCount,TPlatform.GetElapsedMilliseconds(LTCInitial)]));
+  FPCAbstractMem.CheckConsistency(FErrors);
+end;
+
+constructor TPCAbstractMemCheckThread.Create(APCAbstractMem: TPCAbstractMem);
+begin
+  FPCAbstractMem := APCAbstractMem;
+
+  FPCAbstractMem.FLockAbstractMem.Acquire;
+  try
+    FPCAbstractMem.FCheckingThread := Self;
+  finally
+    FPCAbstractMem.FLockAbstractMem.Release;
+  end;
+
+  FErrorsCount := 0;
+  FErrors := TStringList.Create;
+  FMustRestart := False;
+  FreeOnTerminate := True;
+  inherited Create(True);
+  FreeOnTerminate := True;
+  Suspended := False;
+end;
+
+destructor TPCAbstractMemCheckThread.Destroy;
+begin
+  FPCAbstractMem.FLockAbstractMem.Acquire;
+  try
+    FPCAbstractMem.FCheckingThread := Nil;
+  finally
+    FPCAbstractMem.FLockAbstractMem.Release;
+  end;
+  FErrors.Free;
+end;
+
+procedure TPCAbstractMemCheckThread.Restart;
+begin
+  FMustRestart := True;
+  TLog.NewLog(ltdebug,ClassName,Format('Callirg Restart at %d',[FPCAbstractMem.BlocksCount]) );
+end;
+
+
+end.

+ 602 - 0
src/core/UPCAbstractMemAccountKeys.pas

@@ -0,0 +1,602 @@
+unit UPCAbstractMemAccountKeys;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+uses Classes, SysUtils,
+  SyncObjs,
+  UAbstractMem, UFileMem, UAbstractMemTList,
+  UAbstractBTree,
+  UPCDataTypes, UBaseTypes, UAVLCache,
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+  TAccountsUsingThisKey = Class;
+
+  TAbstractMemAccountKeyNode = record
+    myPosition :   TAbstractMemPosition;    // Position in the AbstractMem
+    accountKey : TAccountKey;
+    accounts_using_this_key_position : TAbstractMemPosition;
+    function GetSize : Integer;
+    procedure ReadFromMem(AMyPosition : TAbstractMemPosition; AAbstractMem : TAbstractMem);
+    procedure WriteToMem(AAbstractMem : TAbstractMem);
+    procedure Clear;
+    function ToString : String;
+  end;
+
+  EPCAbstractMemAccountKeys = Class(Exception);
+
+  { TAccountsUsingThisKey }
+
+  TAccountsUsingThisKey = Class(TAbstractMemOrderedTList<Cardinal>)
+  protected
+    function GetItem(index : Integer) : Cardinal; override;
+    procedure LoadFrom(const ABytes : TBytes; var AItem : Cardinal); override;
+    procedure SaveTo(const AItem : Cardinal; AIsAddingItem : Boolean; var ABytes : TBytes); override;
+    function Compare(const ALeft, ARight : Cardinal) : Integer; override;
+  public
+    Constructor Create(AAbstractMem : TAbstractMem; const AInitialZone : TAMZone); reintroduce;
+    Function Add(const AItem : Cardinal) : Integer; reintroduce;
+    procedure Delete(index : Integer); reintroduce;
+  End;
+
+  TAccountKeyByPosition = record
+    position : TAbstractMemPosition;
+    accountKey : TAccountKey;
+    accountsUsingThisKey : TAccountsUsingThisKey;
+    procedure Clear;
+    procedure Dispose;
+  end;
+
+  TPCAccountKeyByPositionCache = Class(TAVLCache<TAccountKeyByPosition>)
+  protected
+    procedure BeforeDelete(var AData : TAccountKeyByPosition); override;
+  public
+  End;
+
+  TPCAbstractMemAccountKeys = Class(TAVLAbstractTree<TAbstractMemAccountKeyNode>)
+  private
+    FAccountKeysLock : TCriticalSection;
+    FAbstractMem : TAbstractMem;
+    FPointerToRootPosition : TAbstractMemPosition;
+    FRootPosition : TAbstractMemPosition;
+    FAccountKeyByPositionCache : TPCAccountKeyByPositionCache;
+  protected
+    function GetRoot: TAbstractMemAccountKeyNode; override;
+    procedure SetRoot(const Value: TAbstractMemAccountKeyNode); override;
+    function HasPosition(const ANode : TAbstractMemAccountKeyNode; APosition : TAVLTreePosition) : Boolean; override;
+    function GetPosition(const ANode : TAbstractMemAccountKeyNode; APosition : TAVLTreePosition) : TAbstractMemAccountKeyNode; override;
+    procedure SetPosition(var ANode : TAbstractMemAccountKeyNode; APosition : TAVLTreePosition; const ANewValue : TAbstractMemAccountKeyNode); override;
+    procedure ClearPosition(var ANode : TAbstractMemAccountKeyNode; APosition : TAVLTreePosition); override;
+    function GetBalance(const ANode : TAbstractMemAccountKeyNode) : Integer; override;
+    procedure SetBalance(var ANode : TAbstractMemAccountKeyNode; ANewBalance : Integer); override;
+    function AreEquals(const ANode1, ANode2 : TAbstractMemAccountKeyNode) : Boolean; override;
+    procedure ClearNode(var ANode : TAbstractMemAccountKeyNode); override;
+    procedure DisposeNode(var ANode : TAbstractMemAccountKeyNode); override;
+  public
+    function IsNil(const ANode : TAbstractMemAccountKeyNode) : Boolean; override;
+    function ToString(const ANode: TAbstractMemAccountKeyNode) : String; override;
+    constructor Create(AAbstractMem : TAbstractMem; APointerToRootPosition : TAbstractMemPosition); reintroduce;
+    destructor Destroy; override;
+    //
+    function GetKeyAtPosition(APosition : TAbstractMemPosition) : TAccountKey;
+    function GetPositionOfKey(const AAccountKey : TAccountKey; AAddIfNotFound : Boolean) : TAbstractMemPosition;
+    function GetPositionOfKeyAndAddAccount(const AAccountKey : TAccountKey; const AAccountNumber : Cardinal) : TAbstractMemPosition;
+    function GetPositionOfKeyAndRemoveAccount(const AAccountKey : TAccountKey; const AAccountNumber : Cardinal) : TAbstractMemPosition;
+    procedure GetAccountsUsingKey(const AAccountKey : TAccountKey; const AList : TList<Cardinal>);
+    function GetAccountsUsingThisKey(const AAccountKey : TAccountKey) : TAccountsUsingThisKey;
+    procedure FlushCache;
+  end;
+
+
+implementation
+
+type
+  TAccountsUsingThisKey_BlackHole = Class(TAccountsUsingThisKey)
+  public
+    class function GetInstance : TAccountsUsingThisKey_BlackHole;
+  End;
+
+var _TAccountsUsingThisKey_BlackHole : TAccountsUsingThisKey_BlackHole = Nil;
+    _BlackHoleAbstractMem : TMem;
+
+{ TAccountsUsingThisKey_BlackHole }
+
+class function TAccountsUsingThisKey_BlackHole.GetInstance: TAccountsUsingThisKey_BlackHole;
+var LZone : TAMZone;
+begin
+  if Not Assigned(_TAccountsUsingThisKey_BlackHole) then begin
+    if Not Assigned(_BlackHoleAbstractMem) then begin
+      _BlackHoleAbstractMem := TMem.Create(0,True);
+    end;
+    LZone.Clear;
+    _TAccountsUsingThisKey_BlackHole := TAccountsUsingThisKey_BlackHole.Create(_BlackHoleAbstractMem,LZone);
+  end;
+  Result :=  _TAccountsUsingThisKey_BlackHole;
+end;
+
+
+{ TAbstractMemAccountKeyNode }
+
+procedure TAbstractMemAccountKeyNode.Clear;
+begin
+  Self.myPosition := 0;
+  Self.accountKey.EC_OpenSSL_NID := 0;
+  Self.accountKey.x := Nil;
+  Self.accountKey.y := Nil;
+  Self.accounts_using_this_key_position := 0;
+end;
+
+function TAbstractMemAccountKeyNode.GetSize: Integer;
+begin
+  Result := accountKey.GetSerializedLength + 4 + TAbstractMemAVLTreeNodeInfoClass.GetSize;
+end;
+
+procedure TAbstractMemAccountKeyNode.ReadFromMem(AMyPosition: TAbstractMemPosition; AAbstractMem: TAbstractMem);
+var LBytes : TBytes;
+  LRawSize : Integer;
+  i : Integer;
+begin
+  Self.Clear;
+  Self.myPosition := AMyPosition;
+  inc(AMyPosition,TAbstractMemAVLTreeNodeInfoClass.GetSize);
+  // Minimum size is  4 + 2 + 2 = 8 bytes
+  i := 8;
+  SetLength(LBytes,i);
+  if AAbstractMem.Read(AMyPosition,LBytes[0],Length(LBytes))<>Length(LBytes) then raise EPCAbstractMemAccountKeys.Create(Format('Not enough data to read AccountKeyNode at %d',[AMyPosition]));
+  Move(LBytes[0],Self.accounts_using_this_key_position,4);
+  Move(LBytes[4],Self.accountKey.EC_OpenSSL_NID,2);
+  //
+  LRawSize := 0;
+  Move(LBytes[6],LRawSize,2);
+  SetLength(Self.accountKey.x,LRawSize);
+  if AAbstractMem.Read(AMyPosition + i,Self.accountKey.x[0],Length(Self.accountKey.x))<>Length(Self.accountKey.x) then raise EPCAbstractMemAccountKeys.Create(Format('Not enough data to read AccountKeyNode.x at %d',[AMyPosition]));
+  // Read next
+  inc(i,LRawSize);
+  LRawSize := 0;
+  if AAbstractMem.Read(AMyPosition + i,LRawSize,2)<>2 then raise EPCAbstractMemAccountKeys.Create(Format('Not enough data to read AccountKeyNode.Length(y) at %d',[AMyPosition]));
+  SetLength(Self.accountKey.y,LRawSize);
+  inc(i,2);
+  if AAbstractMem.Read(AMyPosition + i,Self.accountKey.y[0],Length(Self.accountKey.y))<>Length(Self.accountKey.y) then raise EPCAbstractMemAccountKeys.Create(Format('Not enough data to read AccountKeyNode.y at %d',[AMyPosition]));
+end;
+
+function TAbstractMemAccountKeyNode.ToString: String;
+begin
+  Result := Format('AccountKeyNode type %d (length x:%d y:%d) at position %d',[Self.accountKey.EC_OpenSSL_NID,Length(Self.accountKey.x),Length(Self.accountKey.y),Self.myPosition]);
+end;
+
+procedure TAbstractMemAccountKeyNode.WriteToMem(AAbstractMem: TAbstractMem);
+var LBytes : TBytes;
+  LStream : TStream;
+begin
+  LStream := TMemoryStream.Create;
+  try
+    LStream.Write(Self.accounts_using_this_key_position,4);
+    Self.accountKey.ToSerialized(LStream);
+    LBytes.FromStream(LStream);
+    AAbstractMem.Write(Self.myPosition + TAbstractMemAVLTreeNodeInfoClass.GetSize,LBytes[0],Length(LBytes));
+  finally
+    LStream.Free;
+  end;
+end;
+
+{ TPCAbstractMemAccountKeys }
+
+function _TPCAbstractMemAccountKeys_Compare(const Left, Right: TAbstractMemAccountKeyNode): Integer;
+begin
+  Result := Left.accountKey.EC_OpenSSL_NID - Right.accountKey.EC_OpenSSL_NID;
+  if (Result = 0) then begin
+    Result := TBaseType.BinStrComp(Left.accountKey.x,Right.accountKey.x);
+    if Result=0 then begin
+      Result := TBaseType.BinStrComp(Left.accountKey.y,Right.accountKey.y);
+    end;
+  end;
+
+  if (Result=0) and (Left.myPosition>0) and (Right.myPosition>0) then begin
+    // This will allow to find exactly a node when both are real (otherwise is searching for a position)
+    Result := Left.myPosition - Right.myPosition;
+  end;
+end;
+
+
+function TPCAbstractMemAccountKeys.AreEquals(const ANode1, ANode2: TAbstractMemAccountKeyNode): Boolean;
+begin
+  Result := (ANode1.accountKey.EC_OpenSSL_NID = ANode2.accountKey.EC_OpenSSL_NID)
+    and (TBaseType.Equals(ANode1.accountKey.x,ANode2.accountKey.x))
+    And (TBaseType.Equals(ANode1.accountKey.y,ANode2.accountKey.y));
+end;
+
+procedure TPCAbstractMemAccountKeys.ClearNode(var ANode: TAbstractMemAccountKeyNode);
+begin
+  ANode.Clear;
+end;
+
+procedure TPCAbstractMemAccountKeys.ClearPosition(var ANode: TAbstractMemAccountKeyNode; APosition: TAVLTreePosition);
+begin
+  TAbstractMemAVLTreeNodeInfoClass.ClearPosition(ANode.myPosition,FAbstractMem,APosition);
+end;
+
+function _AccountKeyByPositionCache_Comparision(const Left, Right: TPCAccountKeyByPositionCache.PAVLCacheMemData): Integer;
+begin
+  // Compare only by data.account number (not for content)
+  Result := Left.data.position - Right.data.position;
+end;
+
+constructor TPCAbstractMemAccountKeys.Create(AAbstractMem: TAbstractMem; APointerToRootPosition : TAbstractMemPosition);
+begin
+  FAccountKeysLock := TCriticalSection.Create;
+  FAbstractMem := AAbstractMem;
+  FPointerToRootPosition := APointerToRootPosition;
+  FRootPosition := 0;
+  // Read Root position
+  FAbstractMem.Read(FPointerToRootPosition,FRootPosition,4);
+  FAccountKeyByPositionCache := TPCAccountKeyByPositionCache.Create(5000,_AccountKeyByPositionCache_Comparision);
+  inherited Create(_TPCAbstractMemAccountKeys_Compare,False);
+end;
+
+destructor TPCAbstractMemAccountKeys.Destroy;
+begin
+  FAccountKeyByPositionCache.Free;
+  FAccountKeysLock.Free;
+  inherited;
+end;
+
+procedure TPCAbstractMemAccountKeys.DisposeNode(var ANode: TAbstractMemAccountKeyNode);
+begin
+  // Free from mem
+  FAbstractMem.Dispose(ANode.myPosition);
+end;
+
+procedure TPCAbstractMemAccountKeys.FlushCache;
+begin
+  FAccountKeyByPositionCache.Clear;
+end;
+
+procedure TPCAbstractMemAccountKeys.GetAccountsUsingKey(
+  const AAccountKey: TAccountKey; const AList: TList<Cardinal>);
+var Lautk : TAccountsUsingThisKey;
+  i : Integer;
+begin
+  AList.Clear;
+  FAccountKeysLock.Acquire;
+  try
+  Lautk := GetAccountsUsingThisKey(AAccountKey);
+  if Assigned(Lautk) then begin
+    for i:=0 to Lautk.Count-1 do begin
+      AList.Add( Lautk.GetItem(i) );
+    end;
+  end;
+  finally
+    FAccountKeysLock.Release;
+  end;
+end;
+
+function TPCAbstractMemAccountKeys.GetAccountsUsingThisKey(const AAccountKey: TAccountKey): TAccountsUsingThisKey;
+var LNode : TAbstractMemAccountKeyNode;
+  LZone : TAMZone;
+  i : Integer;
+  LP : TAccountKeyByPosition;
+begin
+  Result := TAccountsUsingThisKey_BlackHole.GetInstance;
+  FAccountKeysLock.Acquire;
+  try
+  LNode.Clear;
+  LNode.accountKey := AAccountKey;
+  LNode := Find(LNode);
+  if IsNil(LNode) then Exit;
+  LZone.Clear;
+  LZone.position := LNode.accounts_using_this_key_position;
+  // Add Account Number
+  if (LNode.accounts_using_this_key_position=0) then Exit;
+  LP.Clear;
+  LP.position := LNode.myPosition;
+  if Not FAccountKeyByPositionCache.Find(LP,LP) then begin
+    LP.Clear;
+    LP.position := LNode.myPosition;
+    LP.accountKey := AAccountKey;
+    LP.accountsUsingThisKey := TAccountsUsingThisKey.Create(FAbstractMem,LZone);
+    FAccountKeyByPositionCache.Add(LP); // Add to cache!
+  end;
+  Result := LP.accountsUsingThisKey;
+  finally
+    FAccountKeysLock.Release;
+  end;
+end;
+
+function TPCAbstractMemAccountKeys.GetBalance(const ANode: TAbstractMemAccountKeyNode): Integer;
+begin
+  Result := TAbstractMemAVLTreeNodeInfoClass.GetBalance(ANode.myPosition,FAbstractMem);
+end;
+
+function TPCAbstractMemAccountKeys.GetKeyAtPosition(APosition: TAbstractMemPosition): TAccountKey;
+var LNode : TAbstractMemAccountKeyNode;
+  LP, LPFound : TAccountKeyByPosition;
+  LAccZone : TAMZone;
+begin
+  FAccountKeysLock.Acquire;
+  try
+  LP.Clear;
+  LP.position := APosition;
+  LPFound.Clear;
+  if FAccountKeyByPositionCache.Find(LP,LPFound) then begin
+    Result := LPFound.accountKey;
+  end else begin
+    LNode.ReadFromMem(APosition,FAbstractMem);
+    Result := LNode.accountKey;
+    LP.accountKey := Result;
+    if LNode.accounts_using_this_key_position>0 then begin
+      LAccZone.Clear;
+      LAccZone.position := LNode.accounts_using_this_key_position;
+      LP.accountsUsingThisKey := TAccountsUsingThisKey.Create(FAbstractMem,LAccZone);
+    end else LP.accountsUsingThisKey := Nil;
+    FAccountKeyByPositionCache.Add(LP); // Add to cache!
+  end;
+  finally
+    FAccountKeysLock.Release;
+  end;
+end;
+
+function TPCAbstractMemAccountKeys.GetPosition(
+  const ANode: TAbstractMemAccountKeyNode;
+  APosition: TAVLTreePosition): TAbstractMemAccountKeyNode;
+var LPos : TAbstractMemPosition;
+begin
+  if ANode.myPosition>0 then begin
+    LPos := TAbstractMemAVLTreeNodeInfoClass.GetPosition(ANode.myPosition,FAbstractMem,APosition);
+  end else LPos := 0;
+  if (LPos>0) then begin
+    Result.ReadFromMem(LPos,FAbstractMem);
+  end else Result.Clear;
+end;
+
+function TPCAbstractMemAccountKeys.GetPositionOfKeyAndAddAccount(const AAccountKey: TAccountKey; const AAccountNumber: Cardinal): TAbstractMemPosition;
+var LNode : TAbstractMemAccountKeyNode;
+  LZone : TAMZone;
+  Lacckutk : TAccountsUsingThisKey;
+  LAccKeyByPos  : TAccountKeyByPosition;
+begin
+  FAccountKeysLock.Acquire;
+  try
+  LNode.Clear;
+  LNode.accountKey := AAccountKey;
+  LNode := Find(LNode);
+  if IsNil(LNode) then begin
+    // if LNode does not exists, then ADD
+    LNode.accountKey := AAccountKey;
+    LNode.accounts_using_this_key_position := 0;
+    LNode.myPosition := FAbstractMem.New( LNode.GetSize ).position;
+    LNode.WriteToMem(FAbstractMem);
+    Add(LNode);
+  end;
+
+  LAccKeyByPos.Clear;
+  LAccKeyByPos.position := LNode.myPosition;
+  if FAccountKeyByPositionCache.Find(LAccKeyByPos,LAccKeyByPos) then begin
+    if Not Assigned(LAccKeyByPos.accountsUsingThisKey) then begin
+      // We will need to add... remove from cache
+      FAccountKeyByPositionCache.Remove(LAccKeyByPos);
+      LAccKeyByPos.Clear;
+    end;
+  end else LAccKeyByPos.Clear;
+  if (LAccKeyByPos.position<=0) then begin
+    // Create
+    LAccKeyByPos.position := LNode.myPosition;
+    LAccKeyByPos.accountKey := AAccountKey;
+    LZone.Clear;
+    if (LNode.accounts_using_this_key_position=0) then begin
+      // Create
+      LZone := FAbstractMem.New( CT_AbstractMemTList_HeaderSize );
+      LNode.accounts_using_this_key_position := LZone.position;
+      LNode.WriteToMem( FAbstractMem ); // Save update:
+    end else LZone.position := LNode.accounts_using_this_key_position;
+    LAccKeyByPos.accountsUsingThisKey := TAccountsUsingThisKey.Create(FAbstractMem,LZone);
+    // Add to cache
+    FAccountKeyByPositionCache.Add( LAccKeyByPos );
+  end;
+  //
+  LAccKeyByPos.accountsUsingThisKey.Add( AAccountNumber );
+
+  Result := LNode.myPosition;
+  finally
+    FAccountKeysLock.Release;
+  end;
+end;
+
+function TPCAbstractMemAccountKeys.GetPositionOfKeyAndRemoveAccount(
+  const AAccountKey: TAccountKey;
+  const AAccountNumber: Cardinal): TAbstractMemPosition;
+var LNode : TAbstractMemAccountKeyNode;
+  LZone : TAMZone;
+  i : Integer;
+  Lacckutk : TAccountsUsingThisKey;
+  LAccKeyByPos : TAccountKeyByPosition;
+begin
+  FAccountKeysLock.Acquire;
+  try
+  LNode.Clear;
+  LNode.accountKey := AAccountKey;
+  LNode := Find(LNode);
+  if IsNil(LNode) then begin
+    Exit(0);
+  end;
+  Result := LNode.myPosition;
+  // Remove Account Number
+
+  if (LNode.accounts_using_this_key_position=0) then Exit;
+
+  LAccKeyByPos.Clear;
+  LAccKeyByPos.position := LNode.myPosition;
+  if Not FAccountKeyByPositionCache.Find(LAccKeyByPos,LAccKeyByPos) then begin
+    // Create
+    LAccKeyByPos.position := LNode.myPosition;
+    LAccKeyByPos.accountKey := AAccountKey;
+    LZone.Clear;
+    LZone.position := LNode.accounts_using_this_key_position;
+    LAccKeyByPos.accountsUsingThisKey := TAccountsUsingThisKey.Create(FAbstractMem,LZone);
+    // Add to cache
+    FAccountKeyByPositionCache.Add( LAccKeyByPos );
+  end;
+
+  if Assigned(LAccKeyByPos.accountsUsingThisKey) then begin
+    i := LAccKeyByPos.accountsUsingThisKey.IndexOf( AAccountNumber );
+    if i>=0 then begin
+      LAccKeyByPos.accountsUsingThisKey.Delete( i );
+    end;
+  end;
+  finally
+    FAccountKeysLock.Release;
+  end;
+end;
+
+function TPCAbstractMemAccountKeys.GetPositionOfKey(const AAccountKey: TAccountKey; AAddIfNotFound : Boolean): TAbstractMemPosition;
+var LNode : TAbstractMemAccountKeyNode;
+begin
+  LNode.Clear;
+  LNode.accountKey := AAccountKey;
+  LNode := Find(LNode);
+  if (IsNil(LNode) and (AAddIfNotFound)) then begin
+    // if LNode does not exists, then ADD
+    LNode.accountKey := AAccountKey;
+    LNode.accounts_using_this_key_position := 0;
+    LNode.myPosition := FAbstractMem.New( LNode.GetSize ).position;
+    LNode.WriteToMem(FAbstractMem);
+    Add(LNode);
+  end;
+  Result := LNode.myPosition;
+end;
+
+function TPCAbstractMemAccountKeys.GetRoot: TAbstractMemAccountKeyNode;
+begin
+  if FRootPosition>0 then Result.ReadFromMem( FRootPosition , FAbstractMem )
+  else Result.Clear;
+end;
+
+function TPCAbstractMemAccountKeys.HasPosition(
+  const ANode: TAbstractMemAccountKeyNode;
+  APosition: TAVLTreePosition): Boolean;
+begin
+  if (ANode.myPosition>0) then begin
+    Result := TAbstractMemAVLTreeNodeInfoClass.GetPosition(ANode.myPosition,FAbstractMem,APosition)>0;
+  end else Result := False;
+end;
+
+function TPCAbstractMemAccountKeys.IsNil(
+  const ANode: TAbstractMemAccountKeyNode): Boolean;
+begin
+  Result := ANode.myPosition=0;
+end;
+
+procedure TPCAbstractMemAccountKeys.SetBalance(
+  var ANode: TAbstractMemAccountKeyNode; ANewBalance: Integer);
+begin
+  TAbstractMemAVLTreeNodeInfoClass.SetBalance(ANode.myPosition,FAbstractMem,ANewBalance);
+end;
+
+procedure TPCAbstractMemAccountKeys.SetPosition(
+  var ANode: TAbstractMemAccountKeyNode; APosition: TAVLTreePosition;
+  const ANewValue: TAbstractMemAccountKeyNode);
+begin
+  TAbstractMemAVLTreeNodeInfoClass.SetPosition(ANode.myPosition,FAbstractMem,APosition,ANewValue.myPosition);
+end;
+
+procedure TPCAbstractMemAccountKeys.SetRoot(
+  const Value: TAbstractMemAccountKeyNode);
+begin
+  FRootPosition := Value.myPosition;
+  // Save
+  if Value.myPosition>0 then begin
+    Value.WriteToMem(FAbstractMem);
+  end;
+  FAbstractMem.Write(FPointerToRootPosition,FRootPosition,4);
+end;
+
+function TPCAbstractMemAccountKeys.ToString(const ANode: TAbstractMemAccountKeyNode): String;
+begin
+  Result := ANode.ToString;
+end;
+
+{ TAccountsUsingThisKey }
+
+procedure TAccountsUsingThisKey.LoadFrom(const ABytes: TBytes; var AItem: Cardinal);
+begin
+  Move(ABytes[0],AItem,4);
+end;
+
+procedure TAccountsUsingThisKey.SaveTo(const AItem: Cardinal; AIsAddingItem : Boolean; var ABytes: TBytes);
+begin
+  SetLength(ABytes,4);
+  Move(AItem,ABytes[0],4);
+  raise Exception.Create('INCONSISTENT 20200324-1 NEVER CALL HERE');
+end;
+
+function TAccountsUsingThisKey.Add(const AItem: Cardinal): Integer;
+var
+  LFound : Boolean;
+  LBytes : TBytes;
+  LZone : TAMZone;
+begin
+  FList.LockList;
+  try
+    LFound := Find(AItem,Result);
+    if (LFound and AllowDuplicates) or (Not LFound) then begin
+      FList.Insert( Result , AItem );
+    end else Result := -1;
+  finally
+    FList.UnlockList;
+  end;
+end;
+
+function TAccountsUsingThisKey.Compare(const ALeft, ARight: Cardinal): Integer;
+begin
+  Result := ALeft - ARight;
+end;
+
+constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone);
+begin
+  inherited Create(AAbstractMem,AInitialZone,1000,False);
+end;
+
+procedure TAccountsUsingThisKey.Delete(index: Integer);
+begin
+  FList.Delete( index );
+end;
+
+function TAccountsUsingThisKey.GetItem(index: Integer): Cardinal;
+begin
+  Result := FList.Position[index];
+end;
+
+{ TPCAccountKeyByPositionCache }
+
+procedure TPCAccountKeyByPositionCache.BeforeDelete(var AData: TAccountKeyByPosition);
+begin
+  inherited;
+  if Assigned(AData.accountsUsingThisKey) then begin
+    FreeAndNil(AData.accountsUsingThisKey);
+  end;
+end;
+
+{ TAccountKeyByPosition }
+
+procedure TAccountKeyByPosition.Clear;
+begin
+  Self.position := 0;
+  Self.accountKey := CT_AccountInfo_NUL.accountKey;
+  Self.accountsUsingThisKey := Nil;
+end;
+
+procedure TAccountKeyByPosition.Dispose;
+begin
+  FreeAndNil(Self.accountsUsingThisKey);
+end;
+
+initialization
+  _TAccountsUsingThisKey_BlackHole := Nil;
+  _BlackHoleAbstractMem := Nil;
+finalization
+  FreeAndNil(_TAccountsUsingThisKey_BlackHole);
+  FreeAndNil(_BlackHoleAbstractMem);
+end.

+ 322 - 1
src/core/UPCDataTypes.pas

@@ -23,7 +23,7 @@ unit UPCDataTypes;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, UBaseTypes;
+  Classes, SysUtils, UBaseTypes, UConst;
 
 
 type
 type
 
 
@@ -38,6 +38,15 @@ type
      EC_OpenSSL_NID : Word;
      EC_OpenSSL_NID : Word;
      x: TRawBytes;
      x: TRawBytes;
      y: TRawBytes;
      y: TRawBytes;
+     //
+     procedure Clear;
+     function GetSerializedLength : Integer;
+     function ToSerialized : TBytes; overload;
+     procedure ToSerialized(const AStream : TStream); overload;
+     function FromSerialized(const ASerialized : TBytes) : Boolean; overload;
+     function FromSerialized(const AStream : TStream) : Boolean; overload;
+     function LoadFromTBytes(const ABytes : TBytes; var AStartIndex : Integer) : Boolean;
+     function IsEqualTo(const ACompareTo : TECDSA_Public) : Boolean;
   end;
   end;
 
 
   { TECDSA_Public_Raw is a TECDSA_Public stored in a single TRawBytes
   { TECDSA_Public_Raw is a TECDSA_Public stored in a single TRawBytes
@@ -59,8 +68,269 @@ type
   end;
   end;
   PECDSA_Public = ^TECDSA_Public; // Pointer to a TECDSA_SIG
   PECDSA_Public = ^TECDSA_Public; // Pointer to a TECDSA_SIG
 
 
+
+
+  TAccountKey = TECDSA_Public;
+  PAccountKey = ^TAccountKey;
+
+  TAccountState = (as_Unknown, as_Normal, as_ForSale, as_ForAtomicAccountSwap, as_ForAtomicCoinSwap);
+
+  { TAccountInfo }
+
+  TAccountInfo = Record
+    state : TAccountState;
+    accountKey: TAccountKey;
+    // Trade info, only when state=as_ForSale
+    locked_until_block : Cardinal; // 0 = Not locked
+    price : UInt64;                // 0 = invalid price
+    account_to_pay : Cardinal;     // <> itself
+    new_publicKey : TAccountKey;
+    hashed_secret : TRawBytes;     // Hashed Secret for AtomicSwaps
+    //
+    procedure Clear;
+    function ToSerialized : TBytes;
+    function FromSerialized(const ASerialized : TBytes) : Boolean;
+    function LoadFromTBytes(const ABytes : TBytes; var AStartIndex : Integer) : Boolean;
+  end;
+
+  TOperationBlock = Record
+    block: Cardinal;
+    account_key: TAccountKey;
+    reward: UInt64;
+    fee: UInt64;
+    protocol_version: Word;     // Protocol version
+    protocol_available: Word;   // Used to upgrade protocol
+    timestamp: Cardinal;        // Timestamp creation
+    compact_target: Cardinal;   // Target in compact form
+    nonce: Cardinal;            // Random value to generate a new P-o-W
+    block_payload : TRawBytes;  // RAW Payload that a miner can include to a blockchain
+    initial_safe_box_hash: TRawBytes; // RAW Safe Box Hash value (32 bytes, it's a Sha256)
+    operations_hash: TRawBytes; // RAW sha256 (32 bytes) of Operations
+    proof_of_work: TRawBytes;   // RAW 32 bytes
+    previous_proof_of_work: TRawBytes; // RAW 32 bytes
+  end;
+
+  { TAccount }
+
+  TAccount = Record
+    account: Cardinal;        // FIXED value. Account number
+    accountInfo : TAccountInfo;
+    balance: UInt64;          // Balance, always >= 0
+    updated_on_block_passive_mode: Cardinal; // Number of block where was updated (active or passive mode)
+    updated_on_block_active_mode: Cardinal; // Number of block where was used (active mode only)
+    n_operation: Cardinal;    // count number of owner operations (when receive, this is not updated)
+    name : TRawBytes;         // Protocol 2. Unique name
+    account_type : Word;      // Protocol 2. Layer 2 use case
+    account_data : TRawBytes; // Protocol 5. PIP-0024 RAW data information
+    account_seal : TRawBytes;  // Protocol 5. PIP-0029 seal of data changes
+    procedure Clear;
+    function GetLastUpdatedBlock : Cardinal;
+  End;
+  PAccount = ^TAccount;
+
+  TBlockAccount = Record
+    blockchainInfo : TOperationBlock;
+    accounts : Array[0..CT_AccountsPerBlock-1] of TAccount;
+    block_hash: TRawBytes;   // Calculated on every block change (on create and on accounts updated)
+    accumulatedWork : UInt64; // Accumulated work (previous + target) this value can be calculated.
+  end;
+
+  { TPCSafeBoxHeader }
+
+  TPCSafeBoxHeader = Record
+    protocol : Word;
+    startBlock,
+    endBlock,
+    blocksCount : Cardinal;
+    safeBoxHash : TRawBytes;
+    function GetSavedBlocksCount : Integer;
+    function IsAChunk : Boolean;
+    function IsFullSafebox : Boolean;
+    function ContainsFirstBlock : Boolean;
+    function ContainsLastBlock : Boolean;
+    function ToString : String;
+  end;
+
+
+
+const
+  CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);hashed_secret:Nil);
+  CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil);
+  CT_BlockAccount_NUL : TBlockAccount = (
+    blockchainInfo:(block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil;previous_proof_of_work:Nil);
+    accounts:(
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block_passive_mode:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil)
+    );
+    block_hash:Nil;
+    accumulatedWork:0);
+  CT_PCSafeBoxHeader_NUL : TPCSafeBoxHeader = (protocol:0;startBlock:0;endBlock:0;blocksCount:0;safeBoxHash:Nil);
+
+
 implementation
 implementation
 
 
+{ TECDSA_Public }
+
+procedure TECDSA_Public.Clear;
+begin
+  Self.EC_OpenSSL_NID:=0;
+  Self.x := Nil;
+  Self.y := Nil;
+end;
+
+function TECDSA_Public.ToSerialized: TBytes;
+var LPos : Integer;
+begin
+  SetLength(Result,2 + 2 + Length(Self.x) + 2 + Length(Self.y));
+  Move(Self.EC_OpenSSL_NID,Result[0],2);
+  LPos := 2;
+  Self.x.SaveInsideTBytes(Result,LPos);
+  Self.y.SaveInsideTBytes(Result,LPos);
+end;
+
+function TECDSA_Public.FromSerialized(const ASerialized: TBytes): Boolean;
+var i : Integer;
+begin
+  i := 0;
+  Result := LoadFromTBytes(ASerialized,i);
+end;
+
+function TECDSA_Public.FromSerialized(const AStream: TStream): Boolean;
+begin
+  if AStream.Read(Self.EC_OpenSSL_NID,2)<>2 then Exit(False);
+  if Self.x.FromSerialized(AStream)<0 then Exit(False);
+  if Self.y.FromSerialized(AStream)<0 then Exit(False);
+  Result := True;
+end;
+
+function TECDSA_Public.GetSerializedLength: Integer;
+begin
+  Result := 2 + Self.x.GetSerializedLength + Self.y.GetSerializedLength;
+end;
+
+function TECDSA_Public.IsEqualTo(const ACompareTo: TECDSA_Public): Boolean;
+begin
+  Result := (Self.EC_OpenSSL_NID = ACompareTo.EC_OpenSSL_NID) and
+    (Self.x.IsEqualTo(ACompareTo.x)) and (Self.y.IsEqualTo(ACompareTo.y));
+end;
+
+function TECDSA_Public.LoadFromTBytes(const ABytes: TBytes; var AStartIndex: Integer): Boolean;
+begin
+  Self.Clear;
+  if (AStartIndex + 2 + 2 + 2 > Length(ABytes)) then Exit(False);
+  Move(ABytes[AStartIndex],Self.EC_OpenSSL_NID,2);
+  inc(AStartIndex,2);
+  if Not Self.x.LoadFromTBytes(ABytes,AStartIndex) then Exit(False);
+  if Not Self.y.LoadFromTBytes(ABytes,AStartIndex) then Exit(False);
+end;
+
+procedure TECDSA_Public.ToSerialized(const AStream: TStream);
+begin
+  AStream.Write(Self.EC_OpenSSL_NID,2);
+  Self.x.ToSerialized(AStream);
+  Self.y.ToSerialized(AStream);
+end;
+
+{ TAccountInfo }
+
+procedure TAccountInfo.Clear;
+begin
+  Self.state := as_Unknown;
+  Self.accountKey.Clear;
+  Self.locked_until_block := 0;
+  Self.price := 0;
+  Self.account_to_pay := 0;
+  Self.new_publicKey.Clear;
+  Self.hashed_secret := Nil;
+end;
+
+function TAccountInfo.ToSerialized: TBytes;
+var w : Word;
+  LtotalLenght : Integer;
+  Lacc_serialized, Lnew_serialized, Lsecret_serialized : TBytes;
+begin
+  Lacc_serialized := Self.accountKey.ToSerialized;
+  case Self.state of
+    as_Unknown: begin
+      Result := Nil;
+      Exit;
+    end;
+    as_Normal: Begin
+      Result := Lacc_serialized;
+      Exit;
+    End;
+    as_ForSale, as_ForAtomicAccountSwap, as_ForAtomicCoinSwap: begin
+      Lnew_serialized := Self.new_publicKey.ToSerialized;
+      Lsecret_serialized := Self.hashed_secret.ToSerialized;
+      LtotalLenght := 2 + Length(Lacc_serialized) + 4 + 8 + 4 + Length(Lnew_serialized);
+      case Self.state of
+        as_ForSale: w := CT_AccountInfo_ForSale;
+        as_ForAtomicAccountSwap: begin
+          w := CT_AccountInfo_ForAccountSwap;
+          inc(LtotalLenght,Length(Lsecret_serialized));
+        end;
+        as_ForAtomicCoinSwap: begin
+          w := CT_AccountInfo_ForCoinSwap;
+          inc(LtotalLenght,Length(Lsecret_serialized));
+        end;
+      end;
+      SetLength(Result, LtotalLenght);
+      Move(w,Result[0],2);
+      Move(Lacc_serialized[0],Result[2],Length(Lacc_serialized));
+      //
+      Move(Self.locked_until_block,Result[2+Length(Lacc_serialized)],4);
+      Move(Self.price,Result[2+Length(Lacc_serialized)+4],8);
+      Move(Self.account_to_pay,Result[2+Length(Lacc_serialized)+4+8],4);
+      //
+      Move(Lnew_serialized[0],Result[2+Length(Lacc_serialized)+4+8+4], Length(Lnew_serialized));
+      if (Self.state in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap]) then begin
+        Move(Lsecret_serialized[0],Result[2+Length(Lacc_serialized)+4+8+4+Length(Lnew_serialized)],Length(Lsecret_serialized));
+      end;
+    end;
+  end;
+end;
+
+function TAccountInfo.FromSerialized(const ASerialized: TBytes): Boolean;
+var i : Integer;
+begin
+  i := 0;
+  Result := LoadFromTBytes(ASerialized,i);
+end;
+
+function TAccountInfo.LoadFromTBytes(const ABytes: TBytes; var AStartIndex: Integer): Boolean;
+var w : Word;
+begin
+  Self.Clear;
+  if (AStartIndex + 2 > Length(ABytes)) then Exit(False);
+  Move(ABytes[AStartIndex],w,2);
+  case w of
+    CT_NID_secp256k1,CT_NID_secp384r1,CT_NID_sect283k1,CT_NID_secp521r1 : Begin
+      Self.state := as_Normal;
+      Result := Self.accountKey.LoadFromTBytes(ABytes,AStartIndex);
+    End;
+    CT_AccountInfo_ForSale, CT_AccountInfo_ForAccountSwap, CT_AccountInfo_ForCoinSwap : Begin
+      inc(AStartIndex,2);
+      if Not Self.accountKey.LoadFromTBytes(ABytes,AStartIndex) then Exit(False);
+      if (AStartIndex + 4 + 8 + 4 > Length(ABytes)) then Exit(False);
+      Move(ABytes[AStartIndex],Self.locked_until_block,4);
+      Move(ABytes[AStartIndex + 4],Self.price,8);
+      Move(ABytes[AStartIndex + 4 + 8],Self.account_to_pay,4);
+      inc(AStartIndex, 4+8+4);
+      if Not Self.new_publicKey.LoadFromTBytes(ABytes,AStartIndex) then Exit(False);
+      if Self.state in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
+        if Not Self.hashed_secret.LoadFromTBytes(ABytes,AStartIndex) then Exit(False);
+      end;
+      Result:=True;
+    End;
+  else
+    raise Exception.Create('DEVELOP ERROR 20200318-1');
+  end;
+end;
+
 { TECDSA_Public_Helper }
 { TECDSA_Public_Helper }
 
 
 function TECDSA_Public_Helper.ToRaw(var OECDSA_Public_Raw: TECDSA_Public_Raw): Boolean;
 function TECDSA_Public_Helper.ToRaw(var OECDSA_Public_Raw: TECDSA_Public_Raw): Boolean;
@@ -117,5 +387,56 @@ begin
   end;
   end;
 end;
 end;
 
 
+{ TAccount }
+
+procedure TAccount.Clear;
+begin
+  Self := CT_Account_NUL;
+end;
+
+function TAccount.GetLastUpdatedBlock: Cardinal;
+begin
+  if (Self.updated_on_block_passive_mode>Self.updated_on_block_active_mode) then Result := Self.updated_on_block_passive_mode
+  else Result := Self.updated_on_block_active_mode;
+end;
+
+{ TPCSafeBoxHeader }
+
+function TPCSafeBoxHeader.GetSavedBlocksCount: Integer;
+begin
+  Result := Self.endBlock - Self.startBlock + 1;
+end;
+
+function TPCSafeBoxHeader.IsAChunk: Boolean;
+begin
+  Result := (Self.startBlock<>0) or (Self.endBlock+1<>Self.blocksCount);
+end;
+
+function TPCSafeBoxHeader.IsFullSafebox: Boolean;
+begin
+  Result := (Self.startBlock=0) and (Self.endBlock+1=Self.blocksCount);
+end;
+
+function TPCSafeBoxHeader.ContainsFirstBlock: Boolean;
+begin
+  Result := (Self.startBlock=0);
+end;
+
+function TPCSafeBoxHeader.ContainsLastBlock: Boolean;
+begin
+  Result := (Self.endBlock+1=Self.blocksCount);
+end;
+
+function TPCSafeBoxHeader.ToString: String;
+begin
+  if IsFullSafebox then begin
+    Result := Format('Fulls SafeboxHeader from %d to %d (%d)',[Self.startBlock,Self.endBlock,Self.blocksCount])
+  end else begin
+    Result := Format('Chunk SafeboxHeader from %d to %d (%d of %d)',[Self.startBlock,Self.endBlock,Self.GetSavedBlocksCount,Self.blocksCount]);
+  end;
+end;
+
+
+
 end.
 end.
 
 

+ 1 - 1
src/core/UPCOperationsBlockValidator.pas

@@ -35,7 +35,7 @@ interface
 {$ENDIF}
 {$ENDIF}
 
 
 
 
-Uses UThread, UAccounts, UPCOrderedLists, UBlockChain, Classes,
+Uses UThread, UAccounts, UPCOrderedLists, UBlockChain, Classes, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type

+ 7 - 6
src/core/UPCRPCFindAccounts.pas

@@ -39,6 +39,7 @@ Type
 
 
 implementation
 implementation
 
 
+uses UPCDataTypes;
 
 
 { TRPCFindAccounts }
 { TRPCFindAccounts }
 
 
@@ -142,8 +143,8 @@ var
   LAccountNumber : Integer;
   LAccountNumber : Integer;
   LRaw : TRawBytes;
   LRaw : TRawBytes;
   LSearchByPubkey : Boolean;
   LSearchByPubkey : Boolean;
-  LStart, LMax,
-  iPubKey : Integer;
+  LStart, LMax : Integer;
+  LAccountsNumbersList : TAccountsNumbersList;
   LAccount : TAccount;
   LAccount : TAccount;
   i : Integer;
   i : Integer;
   LErrors : String;
   LErrors : String;
@@ -232,8 +233,8 @@ begin
       Exit;
       Exit;
     end;
     end;
     LSearchByPubkey := True;
     LSearchByPubkey := True;
-    iPubKey := ASender.Node.Bank.SafeBox.OrderedAccountKeysList.IndexOfAccountKey(LAccPubKey);
-    if (iPubKey<0) then begin
+    LAccountsNumbersList := ASender.Node.Bank.SafeBox.OrderedAccountKeysList.GetAccountsUsingThisKey(LAccPubKey);
+    if (Not Assigned(LAccountsNumbersList)) then begin
       // No account available with this pubkey, exit
       // No account available with this pubkey, exit
       Result := True;
       Result := True;
       Exit;
       Exit;
@@ -272,8 +273,8 @@ begin
     // Search by type-forSale-balance
     // Search by type-forSale-balance
     for i := LStart to ASender.Node.Bank.AccountsCount - 1 do begin
     for i := LStart to ASender.Node.Bank.AccountsCount - 1 do begin
       if (LSearchByPubkey) then begin
       if (LSearchByPubkey) then begin
-        if (i>=ASender.Node.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[iPubKey].Count) then Break;
-        LAccount := ASender.Node.GetMempoolAccount( ASender.Node.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[iPubKey].Get(i) );
+        if (i>=LAccountsNumbersList.Count) then Break;
+        LAccount := ASender.Node.GetMempoolAccount( LAccountsNumbersList.Get(i) );
       end else begin
       end else begin
         LAccount := ASender.Node.GetMempoolAccount(i);
         LAccount := ASender.Node.GetMempoolAccount(i);
       end;
       end;

+ 1 - 1
src/core/UPCRPCOpData.pas

@@ -25,7 +25,7 @@ interface
 {$I ./../config.inc}
 {$I ./../config.inc}
 
 
 Uses classes, SysUtils,
 Uses classes, SysUtils,
-  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   URPC, UCrypto, UWallet, UBlockChain, ULog;
   URPC, UCrypto, UWallet, UBlockChain, ULog;
 
 

+ 7 - 0
src/core/UPCSafeBoxRootHash.pas

@@ -115,6 +115,7 @@ type
     FSafeBoxHashCalcType: TSafeboxHashCalcType;
     FSafeBoxHashCalcType: TSafeboxHashCalcType;
     procedure SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
     procedure SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
   protected
   protected
+    FCachedSafeboxHash : TRawBytes;
     procedure NotifyUpdated(AStartPos, ACountBytes : Integer); override;
     procedure NotifyUpdated(AStartPos, ACountBytes : Integer); override;
     procedure RedoNextLevelsForMerkleRootHash;
     procedure RedoNextLevelsForMerkleRootHash;
   public
   public
@@ -379,6 +380,7 @@ end;
 
 
 constructor TBytesBuffer32Safebox.Create(ADefaultIncrement: Integer);
 constructor TBytesBuffer32Safebox.Create(ADefaultIncrement: Integer);
 begin
 begin
+  FCachedSafeboxHash := Nil;
   FNextLevelBytesBuffer := Nil;
   FNextLevelBytesBuffer := Nil;
   FSafeBoxHashCalcType := sbh_Single_Sha256;
   FSafeBoxHashCalcType := sbh_Single_Sha256;
   inherited Create(ADefaultIncrement);
   inherited Create(ADefaultIncrement);
@@ -392,6 +394,8 @@ end;
 
 
 function TBytesBuffer32Safebox.GetSafeBoxHash: TRawBytes;
 function TBytesBuffer32Safebox.GetSafeBoxHash: TRawBytes;
 begin
 begin
+  if System.Length(FCachedSafeboxHash)=32 then Exit(FCachedSafeboxHash);
+
   if (FSafeBoxHashCalcType = sbh_Single_Sha256) then begin
   if (FSafeBoxHashCalcType = sbh_Single_Sha256) then begin
     if ((Self.Length MOD 32)=0) and (Self.Length>0) then begin
     if ((Self.Length MOD 32)=0) and (Self.Length>0) then begin
       Result := TCrypto.DoSha256(Self.Memory,Self.Length);
       Result := TCrypto.DoSha256(Self.Memory,Self.Length);
@@ -409,6 +413,7 @@ begin
   end else begin
   end else begin
     Result := Nil;
     Result := Nil;
   end;
   end;
+  FCachedSafeboxHash := Result; // Save to a Cache
 end;
 end;
 
 
 procedure TBytesBuffer32Safebox.NotifyUpdated(AStartPos, ACountBytes: Integer);
 procedure TBytesBuffer32Safebox.NotifyUpdated(AStartPos, ACountBytes: Integer);
@@ -417,6 +422,7 @@ var LLevelItemIndex, LLevelItemsCount : Integer;
   LSHA256 : TRawBytes;
   LSHA256 : TRawBytes;
 begin
 begin
   inherited;
   inherited;
+  FCachedSafeboxHash := Nil; // Set my cahce to Nil
   if (FSafeBoxHashCalcType = sbh_Single_Sha256) or
   if (FSafeBoxHashCalcType = sbh_Single_Sha256) or
     ((ACountBytes<>32) or ((AStartPos MOD 32)<>0)) or (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
     ((ACountBytes<>32) or ((AStartPos MOD 32)<>0)) or (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
     FreeAndNil(FNextLevelBytesBuffer);
     FreeAndNil(FNextLevelBytesBuffer);
@@ -471,6 +477,7 @@ end;
 procedure TBytesBuffer32Safebox.SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
 procedure TBytesBuffer32Safebox.SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
 begin
 begin
   if FSafeBoxHashCalcType=Value then Exit;
   if FSafeBoxHashCalcType=Value then Exit;
+  FCachedSafeboxHash := Nil;
   FSafeBoxHashCalcType := Value;
   FSafeBoxHashCalcType := Value;
   FreeAndNil(FNextLevelBytesBuffer);
   FreeAndNil(FNextLevelBytesBuffer);
 end;
 end;

+ 1 - 1
src/core/UPCTNetDataExtraMessages.pas

@@ -33,7 +33,7 @@ interface
 {$ENDIF}
 {$ENDIF}
 
 
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
-  UWallet, UNetProtection,
+  UWallet, UNetProtection, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type

+ 23 - 0
src/core/UPCTemporalFileStream.pas

@@ -36,6 +36,7 @@ Type
   public
   public
     Constructor Create(const AInitialName : String); reintroduce;
     Constructor Create(const AInitialName : String); reintroduce;
     Destructor Destroy; override;
     Destructor Destroy; override;
+    class function GetTemporalFileName(const AInitialName : String) : String;
   End;
   End;
 
 
 implementation
 implementation
@@ -78,4 +79,26 @@ begin
   end;
   end;
 end;
 end;
 
 
+class function TPCTemporalFileStream.GetTemporalFileName(
+  const AInitialName: String): String;
+var LFolder, LTime, LFileName : String;
+  i : Integer;
+begin
+  Result:= '';
+  LFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Temp';
+  ForceDirectories(LFolder);
+  i := 0;
+  repeat
+    LTime := FormatDateTime('yyyymmddhhnnsszzz',Now);
+    if i>0 then begin
+      Sleep(1);
+      LFileName := LFolder + PathDelim + AInitialName + LTime +'_'+ IntToStr(i) + '.tmp';
+    end else begin
+      LFileName := LFolder + PathDelim + AInitialName + LTime + '.tmp';
+    end;
+    inc(i);
+  until (Not (FileExists(LFileName)) or (i>5000));
+  Result := LFileName;
+end;
+
 end.
 end.

+ 1 - 1
src/core/UPoolMining.pas

@@ -31,7 +31,7 @@ Uses
   {LCLIntf, LCLType, LMessages,}
   {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
 {$ENDIF}
   UTCPIP, SysUtils, UThread, SyncObjs, Classes, UJSONFunctions, UPCEncryption, UNode,
   UTCPIP, SysUtils, UThread, SyncObjs, Classes, UJSONFunctions, UPCEncryption, UNode,
-  UCrypto, UAccounts, UConst, UBlockChain, UBaseTypes,
+  UCrypto, UAccounts, UConst, UBlockChain, UBaseTypes, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 Const
 Const

+ 17 - 16
src/core/URPC.pas

@@ -2776,6 +2776,7 @@ Var c,c2,c3 : Cardinal;
   opr : TOperationResume;
   opr : TOperationResume;
   r1,r2 : TRawBytes;
   r1,r2 : TRawBytes;
   ocl : TOrderedCardinalList;
   ocl : TOrderedCardinalList;
+  Lanl : TAccountsNumbersList;
   jsonarr : TPCJSONArray;
   jsonarr : TPCJSONArray;
   jso : TPCJSONObject;
   jso : TPCJSONObject;
   LRPCProcessMethod : TRPCProcessMethod;
   LRPCProcessMethod : TRPCProcessMethod;
@@ -2830,12 +2831,12 @@ begin
         ErrorDesc := 'Public key not found in wallet';
         ErrorDesc := 'Public key not found in wallet';
         exit;
         exit;
       end;
       end;
-      ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+      Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
       k := params.AsInteger('max',100);
       k := params.AsInteger('max',100);
       l := params.AsInteger('start',0);
       l := params.AsInteger('start',0);
-      for j := 0 to ocl.Count - 1 do begin
+      for j := 0 to Lanl.Count - 1 do begin
         if (j>=l) then begin
         if (j>=l) then begin
-          account := FNode.GetMempoolAccount(ocl.Get(j));
+          account := FNode.GetMempoolAccount(Lanl.Get(j));
           TPascalCoinJSONComp.FillAccountObject(account,jsonarr.GetAsObject(jsonarr.Count));
           TPascalCoinJSONComp.FillAccountObject(account,jsonarr.GetAsObject(jsonarr.Count));
         end;
         end;
         if (k>0) And ((j+1)>=(k+l)) then break;
         if (k>0) And ((j+1)>=(k+l)) then break;
@@ -2846,10 +2847,10 @@ begin
       l := params.AsInteger('start',0);
       l := params.AsInteger('start',0);
       c := 0;
       c := 0;
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
-        ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
-        for j := 0 to ocl.Count - 1 do begin
+        Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+        for j := 0 to Lanl.Count - 1 do begin
           if (c>=l) then begin
           if (c>=l) then begin
-            account := FNode.GetMempoolAccount(ocl.Get(j));
+            account := FNode.GetMempoolAccount(Lanl.Get(j));
             TPascalCoinJSONComp.FillAccountObject(account,jsonarr.GetAsObject(jsonarr.Count));
             TPascalCoinJSONComp.FillAccountObject(account,jsonarr.GetAsObject(jsonarr.Count));
           end;
           end;
           inc(c);
           inc(c);
@@ -2878,15 +2879,15 @@ begin
         ErrorDesc := 'Public key not found in wallet';
         ErrorDesc := 'Public key not found in wallet';
         exit;
         exit;
       end;
       end;
-      ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
-      jsonresponse.GetAsVariant('result').value := ocl.count;
+      Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+      jsonresponse.GetAsVariant('result').value := Lanl.count;
       Result := true;
       Result := true;
     end else begin
     end else begin
       ErrorDesc := '';
       ErrorDesc := '';
       c :=0;
       c :=0;
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
-        ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
-        inc(c,ocl.count);
+        Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+        inc(c,Lanl.count);
       end;
       end;
       jsonresponse.GetAsVariant('result').value := c;
       jsonresponse.GetAsVariant('result').value := c;
       Result := true;
       Result := true;
@@ -2908,10 +2909,10 @@ begin
         ErrorDesc := 'Public key not found in wallet';
         ErrorDesc := 'Public key not found in wallet';
         exit;
         exit;
       end;
       end;
-      ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+      Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
       account.balance := 0;
       account.balance := 0;
-      for j := 0 to ocl.Count - 1 do begin
-        inc(account.balance, FNode.GetMempoolAccount(ocl.Get(j)).balance );
+      for j := 0 to Lanl.Count - 1 do begin
+        inc(account.balance, FNode.GetMempoolAccount(Lanl.Get(j)).balance );
       end;
       end;
       jsonresponse.GetAsVariant('result').value := ToJSONCurrency(account.balance);
       jsonresponse.GetAsVariant('result').value := ToJSONCurrency(account.balance);
       Result := true;
       Result := true;
@@ -2920,9 +2921,9 @@ begin
       c :=0;
       c :=0;
       account.balance := 0;
       account.balance := 0;
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
       for i:=0 to _RPCServer.WalletKeys.AccountsKeyList.Count-1 do begin
-        ocl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
-        for j := 0 to ocl.Count - 1 do begin
-          inc(account.balance, FNode.GetMempoolAccount(ocl.Get(j)).balance );
+        Lanl := _RPCServer.WalletKeys.AccountsKeyList.AccountKeyList[i];
+        for j := 0 to Lanl.Count - 1 do begin
+          inc(account.balance, FNode.GetMempoolAccount(Lanl.Get(j)).balance );
         end;
         end;
       end;
       end;
       jsonresponse.GetAsVariant('result').value := ToJSONCurrency(account.balance);
       jsonresponse.GetAsVariant('result').value := ToJSONCurrency(account.balance);

+ 9 - 2
src/core/upcdaemon.pas

@@ -26,7 +26,7 @@ uses
   Classes, SysUtils, daemonapp,
   Classes, SysUtils, daemonapp,
   SyncObjs, UOpenSSL, UCrypto, UNode, UFileStorage, UFolderHelper, UWallet, UConst, ULog, UNetProtocol,
   SyncObjs, UOpenSSL, UCrypto, UNode, UFileStorage, UFolderHelper, UWallet, UConst, ULog, UNetProtocol,
   IniFiles, UBaseTypes,
   IniFiles, UBaseTypes,
-  UThread, URPC, UPoolMining, UAccounts;
+  UThread, URPC, UPoolMining, UAccounts, UPCDataTypes;
 
 
 Const
 Const
   CT_INI_SECTION_GLOBAL = 'GLOBAL';
   CT_INI_SECTION_GLOBAL = 'GLOBAL';
@@ -56,6 +56,7 @@ Type
     FMaxBlockToRead: Int64;
     FMaxBlockToRead: Int64;
     FLastNodesCacheUpdatedTS : TTickCount;
     FLastNodesCacheUpdatedTS : TTickCount;
     procedure OnNetDataReceivedHelloMessage(Sender : TObject);
     procedure OnNetDataReceivedHelloMessage(Sender : TObject);
+    procedure OnInitSafeboxProgressNotify(sender : TObject; const message : String; curPos, totalCount : Int64);
   protected
   protected
     Procedure BCExecute; override;
     Procedure BCExecute; override;
   public
   public
@@ -122,6 +123,12 @@ begin
   TNode.Node.PeerCache := s;
   TNode.Node.PeerCache := s;
 end;
 end;
 
 
+procedure TPCDaemonThread.OnInitSafeboxProgressNotify(sender: TObject;
+  const message: String; curPos, totalCount: Int64);
+begin
+  TLog.NewLog(ltdebug,ClassName,Format('Progress (%d/%d): %s',[curPos,totalCount,message]));
+end;
+
 procedure TPCDaemonThread.BCExecute;
 procedure TPCDaemonThread.BCExecute;
 var
 var
   FNode : TNode;
   FNode : TNode;
@@ -249,7 +256,7 @@ begin
         TNetData.NetData.OnReceivedHelloMessage:=@OnNetDataReceivedHelloMessage;
         TNetData.NetData.OnReceivedHelloMessage:=@OnNetDataReceivedHelloMessage;
         FNode.PeerCache:=  FIniFile.ReadString(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_PEERCACHE,'');
         FNode.PeerCache:=  FIniFile.ReadString(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_PEERCACHE,'');
         // Reading database
         // Reading database
-        FNode.InitSafeboxAndOperations(MaxBlockToRead);
+        FNode.InitSafeboxAndOperations(MaxBlockToRead,@OnInitSafeboxProgressNotify);
         FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
         FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
         FNode.Node.NetServer.Port:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_PORT,CT_NetServer_Port);
         FNode.Node.NetServer.Port:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_PORT,CT_NetServer_Port);
         FNode.Node.NetServer.MaxConnections:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_MAX_CONNECTIONS,CT_MaxClientsConnected);
         FNode.Node.NetServer.MaxConnections:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_MAX_CONNECTIONS,CT_MaxClientsConnected);

Some files were not shown because too many files changed in this diff