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
 
 uses
-  Classes, SysUtils, UAccounts, UThread, UBaseTypes,
+  Classes, SysUtils, UAccounts, UThread, UBaseTypes, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 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!
     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 IsEqualTo(const ACompareTo : TRawBytes) : Boolean;
+    //
     procedure FromStream(AStream : TStream); 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;
 
 
@@ -91,8 +101,10 @@ Type
     function Compare(ABytesBuffer : TBytesBuffer) : Integer;
     procedure SetLength(ANewLength : Integer);
     function Memory : Pointer;
+    function MemoryLength : Integer;
     procedure Clear;
     procedure CopyFrom(ABytesBuffer : TBytesBuffer);
+    function Capture(AStartPos, ALength : Integer) : TBytes;
   end;
 
 
@@ -195,11 +207,94 @@ begin
   end else Result := '';
 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);
 begin
   System.SetLength(Self,ALength);
   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;
 
 procedure TRawBytesHelper.FromStream(AStream: TStream);
@@ -230,6 +325,13 @@ begin
   Result := Length(Self)=0;
 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;
 Var i : Integer;
   rbs : RawByteString;
@@ -553,6 +655,18 @@ begin
   Result := Replace(Length,buffer);
 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;
 begin
   System.SetLength(FBytes,0);
@@ -616,6 +730,11 @@ begin
   Result := addr(FBytes[0]);
 end;
 
+function TBytesBuffer.MemoryLength: Integer;
+begin
+  Result := System.Length(FBytes);
+end;
+
 procedure TBytesBuffer.NotifyUpdated(AStartPos, ACountBytes: Integer);
 begin
   //

+ 73 - 8
src/core/UBlockChain.pas

@@ -16,6 +16,8 @@ unit UBlockChain;
   THIS LICENSE HEADER MUST NOT BE REMOVED.
 }
 
+{$I ./../config.inc}
+
 {$IFDEF FPC}
   {$MODE Delphi}
 {$ENDIF}
@@ -25,8 +27,8 @@ interface
 uses
   Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   {$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;
 
+
+  TCheckPointStruct = {$IFDEF USE_ABSTRACTMEM}TPCAbstractMem{$ELSE}TStream{$ENDIF};
+
   { TStorage }
 
   TStorage = Class(TComponent)
@@ -494,7 +499,7 @@ Type
     function GetFirstBlockNumber: Int64; virtual; abstract;
     function GetLastBlockNumber: Int64; 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 DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
@@ -513,10 +518,10 @@ Type
     Property FirstBlock : Int64 read GetFirstBlockNumber;
     Property LastBlock : Int64 read GetLastBlockNumber;
     Function Initialize : Boolean;
-    Function CreateSafeBoxStream(blockCount : Cardinal) : TStream;
+    Function OpenSafeBoxCheckpoint(blockCount : Cardinal) : TCheckPointStruct;
     Function HasUpgradedToVersion2 : Boolean; virtual; abstract;
     Procedure CleanupVersion1Data; virtual; abstract;
-    Procedure EraseStorage;
+    Procedure EraseStorage; // Erase Blockchain storage
     Procedure SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Procedure LoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
     Function BlockExists(Block : Cardinal) : Boolean;
@@ -549,6 +554,7 @@ Type
     function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
     function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): 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;
     Procedure Clear;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
@@ -929,6 +935,7 @@ Var
   LTmpPCOperationsComp : TPCOperationsComp;
   i,j, LProgressBlock, LProgressEndBlock, LOpsInBlocks : Integer;
   LSafeboxTransaction : TPCSafeBoxTransaction;
+  LTempSafebox : TPCSafeBox;
 begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
@@ -945,6 +952,7 @@ begin
       Storage.Initialize;
       If (max_block<Storage.LastBlock) then n := max_block
       else n := Storage.LastBlock;
+
       Storage.RestoreBank(n,restoreProgressNotify);
       // Restore last blockchain
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
@@ -955,6 +963,12 @@ begin
           FLastOperationBlock := FLastBlockCache.OperationBlock;
         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);
       LBlocks := TList<TPCOperationsComp>.Create;
       try
@@ -1032,7 +1046,12 @@ begin
     finally
       FIsRestoringFromFile := False;
       FUpgradingToV2 := false;
+      for i := 0 to FNotifyList.Count - 1 do begin
+        TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
+      end;
     end;
+
+
   finally
     FBankLock.Release;
   end;
@@ -1168,7 +1187,7 @@ begin
   Result := FStorage;
 end;
 
-function TPCBank.IsReady(Var CurrentProcess: String): Boolean;
+function TPCBank.IsReady(var CurrentProcess: String): Boolean;
 begin
   Result := false;
   CurrentProcess := '';
@@ -1180,6 +1199,52 @@ begin
   end else Result := true;
 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;
 Var LastReadBlock : TBlockAccount;
   i : Integer;
@@ -2894,9 +2959,9 @@ begin
   Result := DoInitialize;
 end;
 
-function TStorage.CreateSafeBoxStream(blockCount: Cardinal): TStream;
+function TStorage.OpenSafeBoxCheckpoint(blockCount: Cardinal): TCheckPointStruct;
 begin
-  Result := DoCreateSafeBoxStream(blockCount);
+  Result := DoOpenSafeBoxCheckpoint(blockCount);
 end;
 
 procedure TStorage.EraseStorage;

+ 103 - 1
src/core/UChunk.pas

@@ -36,10 +36,12 @@ uses
   {$ELSE}
   zlib,
   {$ENDIF}
-  UAccounts, ULog, UConst, UCrypto, UBaseTypes;
+  UAccounts, ULog, UConst, UCrypto, UBaseTypes, UPCDataTypes;
 
 type
 
+  EPCChunk = Class(Exception);
+
   { TPCChunk }
 
   TPCChunk = Class
@@ -49,8 +51,108 @@ type
     class function LoadSafeBoxFromChunk(Chunk, DestStream : TStream; var safeBoxHeader : TPCSafeBoxHeader; var errors : String) : Boolean;
   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
 
+{ 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 }
 
 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
 
-uses
-  Classes, {$IFnDEF FPC}Windows,{$ENDIF} UBlockChain, SyncObjs, UThread, UAccounts, UCrypto;
 {$I ./../config.inc}
 
+uses
+  Classes, {$IFnDEF FPC}Windows,{$ENDIF} UBlockChain, SyncObjs, UThread, UAccounts, UCrypto, UPCDataTypes;
+
+
 Type
   TBlockHeader = Record
     BlockNumber : Cardinal;
@@ -74,7 +76,7 @@ Type
     function GetFirstBlockNumber: Int64; override;
     function GetLastBlockNumber: Int64; override;
     function DoInitialize : Boolean; override;
-    Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; override;
+    Function DoOpenSafeBoxCheckpoint(blockCount : Cardinal) : TCheckPointStruct; override;
     Procedure DoEraseStorage; override;
     Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
     Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); override;
@@ -92,12 +94,18 @@ Type
 
 implementation
 
-Uses ULog, SysUtils, UConst;
-
+Uses ULog, SysUtils, UBaseTypes,
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem,
+  {$ENDIF}
+  UConst;
 { TFileStorage }
 
 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_SizeOfBlockHeader = 16;
   {
@@ -270,14 +278,18 @@ begin
   End;
 end;
 
-function TFileStorage.DoCreateSafeBoxStream(blockCount: Cardinal): TStream;
+function TFileStorage.DoOpenSafeBoxCheckpoint(blockCount: Cardinal): TCheckPointStruct;
 var fn : TFilename;
   err : AnsiString;
 begin
   Result := Nil;
   fn := GetSafeboxCheckpointingFileName(GetFolder(Orphan),blockCount);
   If (fn<>'') and (FileExists(fn)) then begin
+    {$IFDEF USE_ABSTRACTMEM}
+    Result := TPCAbstractMem.Create(fn,True);
+    {$ELSE}
     Result := TFileStream.Create(fn,fmOpenRead+fmShareDenyWrite);
+    {$ENDIF}
   end;
   If Not Assigned(Result) then begin
     err := 'Cannot load SafeBoxStream (block:'+IntToStr(blockCount)+') file:'+fn;
@@ -378,7 +390,7 @@ function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan:
   begin
     FileAttrs := faArchive;
     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
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
@@ -452,28 +464,66 @@ var
     sr: TSearchRec;
     FileAttrs: Integer;
     folder : AnsiString;
-    filename,auxfn : AnsiString;
+    Lfilename,auxfn : AnsiString;
     fs : TFileStream;
     ms : TMemoryStream;
     errors : String;
-    blockscount : Cardinal;
+    LBlockscount : Cardinal;
     sbHeader, goodSbHeader : TPCSafeBoxHeader;
+    {$IFDEF USE_ABSTRACTMEM}
+    LTempBlocksCount : Integer;
+    LSafeboxFileName : String;
+    {$ELSE}
+    {$ENDIF}
 begin
   LockBlockChainStream;
   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;
     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
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           auxfn := folder+PathDelim+sr.Name;
           If LoadBankFileInfo(auxfn,sbHeader) then begin
-            if (((max_block<0) Or (sbHeader.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
-              filename := auxfn;
-              blockscount := sbHeader.blocksCount;
+              Lfilename := auxfn;
+              LBlockscount := sbHeader.blocksCount;
               goodSbHeader := sbHeader;
             end;
           end;
@@ -481,14 +531,14 @@ begin
       until FindNext(sr) <> 0;
       FindClose(sr);
     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
         fs.Position := 0;
         if LowMemoryUsage 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 else begin
           ms := TMemoryStream.Create;
@@ -496,7 +546,7 @@ begin
             ms.CopyFrom(fs,0);
             ms.Position := 0;
             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;
           Finally
             ms.Free;
@@ -515,11 +565,15 @@ function TFileStorage.DoSaveBank: Boolean;
 var fs: TFileStream;
     bankfilename,aux_newfilename: AnsiString;
     ms : TMemoryStream;
+  LTC : TTickCount;
 begin
   Result := true;
   bankfilename := GetSafeboxCheckpointingFileName(GetFolder(Orphan),Bank.BlocksCount);
   if (bankfilename<>'') then begin
-    TLog.NewLog(ltInfo,ClassName,'Saving Safebox blocks:'+IntToStr(Bank.BlocksCount)+' file:'+bankfilename);
+    LTC := TPlatform.GetTickCount;
+    {$IFDEF USE_ABSTRACTMEM}
+    Bank.SafeBox.SaveCheckpointing(bankfilename);
+    {$ELSE}
     fs := TFileStream.Create(bankfilename,fmCreate);
     try
       fs.Size := 0;
@@ -539,9 +593,11 @@ begin
     finally
       fs.Free;
     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
     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
         {$IFDEF FPC}
         DoCopyFile(bankfilename,aux_newfilename);
@@ -589,9 +645,9 @@ begin
   If not ForceDirectories(BaseDataFolder) then exit;
   if TPCSafeBox.MustSafeBoxBeSaved(block) then begin
     // 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
-    Result := BaseDataFolder + PathDelim+'checkpoint_'+inttostr(block)+'.safebox';
+    Result := BaseDataFolder + PathDelim+'checkpoint_'+inttostr(block)+CT_Safebox_Extension;
   end;
 end;
 
@@ -1082,7 +1138,7 @@ end;
 function TFileStorage.HasUpgradedToVersion2: Boolean;
 var searchRec: TSearchRec;
 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);
 end;
 

+ 123 - 105
src/core/UNetProtocol.pas

@@ -16,6 +16,8 @@ unit UNetProtocol;
   THIS LICENSE HEADER MUST NOT BE REMOVED.
 }
 
+{$I ./../config.inc}
+
 {$IFDEF FPC}
   {$MODE Delphi}
 {$ENDIF}
@@ -31,12 +33,12 @@ Uses
 {$ENDIF}
   UBlockChain, Classes, SysUtils, UAccounts, UThread,
   UCrypto, UTCPIP, SyncObjs, UBaseTypes, UCommon, UPCOrderedLists,
+  UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults
   {$ELSE}Generics.Collections,Generics.Defaults{$ENDIF},
+  {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
   UNetProtection;
 
-{$I ./../config.inc}
-
 Const
   CT_MagicRequest = $0001;
   CT_MagicResponse = $0002;
@@ -1844,105 +1846,75 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     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;
-    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;
-    LFirstSafebox : Boolean;
   Begin
     Result := False;
-    LFirstSafebox := TNode.Node.Bank.SafeBox.BlocksCount = 0;
-    safeboxStream.Size:=0;
-    safeboxStream.Position:=0;
+    ASafeboxChunks.Clear;
     // 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;
     end;
     // 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;
     end;
-    SetLength(chunks,0);
-    try
       // 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',
-          [_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);
           Exit;
         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;
-          chunk1.Size := 0;
-          chunk1.CopyFrom(safeboxStream,0);
         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;
-      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;
-  var receiveData : TStream;
-    op : TOperationBlock;
+  var LChunks : TPCSafeboxChunks;
+    LSafeboxLastOperationBlock : TOperationBlock;
     errors : String;
     request_id : Cardinal;
   Begin
     Result := False;
-    receiveData := TPCTemporalFileStream.Create('SAFEBOX_');
+    LChunks := TPCSafeboxChunks.Create;
     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
       TNode.Node.DisableNewBlocks;
       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!');
             If Not IsMyBlockchainValid then begin
               TNode.Node.Bank.Storage.EraseStorage;
@@ -1958,14 +1930,14 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         TNode.Node.EnableNewBlocks;
       end;
     finally
-      receiveData.Free;
+      LChunks.Free;
     end;
   end;
 
   procedure DownloadNewBlockchain(start_block : Int64; IsMyBlockChainOk : Boolean);
-  var safeboxStream : TStream;
+  var LChunks : TPCSafeboxChunks;
     newTmpBank : TPCBank;
-    safebox_last_operation_block : TOperationBlock;
+    LSafeboxLastOperationBlock : TOperationBlock;
     opComp : TPCOperationsComp;
     errors : String;
     blocksList : TList<TPCOperationsComp>;
@@ -1981,10 +1953,12 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     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]));
       // 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);
         try
           newTmpBank.StorageClass := TNode.Node.Bank.StorageClass;
@@ -1993,7 +1967,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
           newTmpBank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
           newTmpBank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
           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;
             try
               TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
@@ -2001,8 +1975,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
               // Receive at least 1 new block
               blocksList := TList<TPCOperationsComp>.Create;
               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;
                 end;
                 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.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);
             Finally
               TNode.Node.EnableNewBlocks;
@@ -2037,12 +2011,11 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
             Connection.DisconnectInvalidClient(false,'Cannot load from stream! '+errors);
             exit;
           end;
-
         finally
           newTmpBank.Free;
         end;
       Finally
-        safeboxStream.Free;
+        LChunks.Free;
       End;
     end else begin
       if IsMyBlockChainOk then begin
@@ -3114,10 +3087,14 @@ procedure TNetConnection.DoProcess_GetSafeBox_Request(HeaderData: TNetHeaderData
 Var _blockcount : Cardinal;
     _safeboxHash : TRawBytes;
     _from,_to : Cardinal;
+  {$IFDEF USE_ABSTRACTMEM}
+  Labstracmem : TPCAbstractMem;
+  {$ELSE}
+  sbHeader : TPCSafeBoxHeader;
+  {$ENDIF}
   sbStream : TStream;
   responseStream : TStream;
   antPos : Int64;
-  sbHeader : TPCSafeBoxHeader;
   errors : String;
 begin
   {
@@ -3142,16 +3119,54 @@ begin
     Exit;
   end;
   //
-  sbStream := TNode.Node.Bank.Storage.CreateSafeBoxStream(_blockcount);
+
+  responseStream := TMemoryStream.Create;
   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
       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;
       end;
       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
         DisconnectInvalidClient(false,Format('Invalid safeboxhash on GetSafeBox request (Real:%s > Requested:%s)',[TCrypto.ToHexaString(sbHeader.safeBoxHash),TCrypto.ToHexaString(_safeboxHash)]));
         exit;
@@ -3162,14 +3177,15 @@ begin
         TLog.NewLog(ltError,Classname,'Error saving chunk: '+errors);
         exit;
       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
-      responseStream.Free;
+      FreeAndNil(sbStream);
     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
-    FreeAndNil(sbStream);
+    responseStream.Free;
   end;
 end;
 
@@ -3318,8 +3334,10 @@ begin
         opht.Free;
       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
     dataSend.Free;
     dataReceived.Free;
@@ -3329,14 +3347,14 @@ end;
 procedure TNetConnection.DoProcess_GetPubkeyAccounts_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 Const CT_Max_Accounts_per_call = 1000;
 var responseStream, accountsStream : TMemoryStream;
-  start,max,iPubKey : Integer;
+  start,max : Integer;
   c, nAccounts : Cardinal;
   acc : TAccount;
   DoDisconnect : Boolean;
   errors : String;
   pubKey : TAccountKey;
-  sbakl : TOrderedAccountKeysList;
-  ocl : TOrderedCardinalList;
+  sbakl : TSafeboxPubKeysAndAccounts;
+  ocl : TAccountsNumbersList;
 begin
   {
   This call is used to obtain Accounts used by a Public key
@@ -3381,9 +3399,8 @@ begin
     nAccounts := 0;
     sbakl := TNode.Node.Bank.SafeBox.OrderedAccountKeysList;
     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
           acc := TNode.Node.GetMempoolAccount(ocl.Get(start));
           if (HeaderData.protocol.protocol_available>9) then
@@ -4901,7 +4918,8 @@ begin
       i := 0;
       if (candidates.Count>1) then i := Random(candidates.Count); // i = 0..count-1
       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;
   finally
     LMaxAggregatedHashrate.Free;

+ 2 - 1
src/core/UNode.pas

@@ -35,7 +35,7 @@ interface
 
 uses
   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;
 
 {$I ./../config.inc}
@@ -874,6 +874,7 @@ end;
 class function TNode.NodeVersion: String;
 begin
   Result := CT_ClientAppVersion
+    {$IFDEF USE_ABSTRACTMEM}+'am'{$ENDIF}
     {$IFDEF LINUX}+'L'{$ELSE}+'W'{$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}

+ 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
 
 uses
-  Classes, SysUtils, UBaseTypes;
+  Classes, SysUtils, UBaseTypes, UConst;
 
 type
 
@@ -38,6 +38,15 @@ type
      EC_OpenSSL_NID : Word;
      x: 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;
 
   { TECDSA_Public_Raw is a TECDSA_Public stored in a single TRawBytes
@@ -59,8 +68,269 @@ type
   end;
   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
 
+{ 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 }
 
 function TECDSA_Public_Helper.ToRaw(var OECDSA_Public_Raw: TECDSA_Public_Raw): Boolean;
@@ -117,5 +387,56 @@ begin
   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.
 

+ 1 - 1
src/core/UPCOperationsBlockValidator.pas

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

+ 7 - 6
src/core/UPCRPCFindAccounts.pas

@@ -39,6 +39,7 @@ Type
 
 implementation
 
+uses UPCDataTypes;
 
 { TRPCFindAccounts }
 
@@ -142,8 +143,8 @@ var
   LAccountNumber : Integer;
   LRaw : TRawBytes;
   LSearchByPubkey : Boolean;
-  LStart, LMax,
-  iPubKey : Integer;
+  LStart, LMax : Integer;
+  LAccountsNumbersList : TAccountsNumbersList;
   LAccount : TAccount;
   i : Integer;
   LErrors : String;
@@ -232,8 +233,8 @@ begin
       Exit;
     end;
     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
       Result := True;
       Exit;
@@ -272,8 +273,8 @@ begin
     // Search by type-forSale-balance
     for i := LStart to ASender.Node.Bank.AccountsCount - 1 do 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
         LAccount := ASender.Node.GetMempoolAccount(i);
       end;

+ 1 - 1
src/core/UPCRPCOpData.pas

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

+ 7 - 0
src/core/UPCSafeBoxRootHash.pas

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

+ 1 - 1
src/core/UPCTNetDataExtraMessages.pas

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

+ 23 - 0
src/core/UPCTemporalFileStream.pas

@@ -36,6 +36,7 @@ Type
   public
     Constructor Create(const AInitialName : String); reintroduce;
     Destructor Destroy; override;
+    class function GetTemporalFileName(const AInitialName : String) : String;
   End;
 
 implementation
@@ -78,4 +79,26 @@ begin
   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.

+ 1 - 1
src/core/UPoolMining.pas

@@ -31,7 +31,7 @@ Uses
   {LCLIntf, LCLType, LMessages,}
 {$ENDIF}
   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};
 
 Const

+ 17 - 16
src/core/URPC.pas

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

+ 9 - 2
src/core/upcdaemon.pas

@@ -26,7 +26,7 @@ uses
   Classes, SysUtils, daemonapp,
   SyncObjs, UOpenSSL, UCrypto, UNode, UFileStorage, UFolderHelper, UWallet, UConst, ULog, UNetProtocol,
   IniFiles, UBaseTypes,
-  UThread, URPC, UPoolMining, UAccounts;
+  UThread, URPC, UPoolMining, UAccounts, UPCDataTypes;
 
 Const
   CT_INI_SECTION_GLOBAL = 'GLOBAL';
@@ -56,6 +56,7 @@ Type
     FMaxBlockToRead: Int64;
     FLastNodesCacheUpdatedTS : TTickCount;
     procedure OnNetDataReceivedHelloMessage(Sender : TObject);
+    procedure OnInitSafeboxProgressNotify(sender : TObject; const message : String; curPos, totalCount : Int64);
   protected
     Procedure BCExecute; override;
   public
@@ -122,6 +123,12 @@ begin
   TNode.Node.PeerCache := s;
 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;
 var
   FNode : TNode;
@@ -249,7 +256,7 @@ begin
         TNetData.NetData.OnReceivedHelloMessage:=@OnNetDataReceivedHelloMessage;
         FNode.PeerCache:=  FIniFile.ReadString(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_PEERCACHE,'');
         // Reading database
-        FNode.InitSafeboxAndOperations(MaxBlockToRead);
+        FNode.InitSafeboxAndOperations(MaxBlockToRead,@OnInitSafeboxProgressNotify);
         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.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