Browse Source

PascalCoin AbstractMem fixed bugs from 5.4 alpha version

PascalCoin 5 years ago
parent
commit
f5c76fb8ca

+ 50 - 12
src/core/UAccounts.pas

@@ -316,6 +316,7 @@ Type
     procedure SetSafeboxFileName(ASafeboxFileName : String);
     procedure SaveCheckpointing(ACheckpointingSafeboxFileName : String);
     procedure UpdateSafeboxFileName(const ANewSafeboxFileName : String);
+    procedure ClearSafeboxfile;
     class Function CopyAbstractMemToSafeBoxStream(ASource : TPCAbstractMem; ADestStream : TStream; AFromBlock, AToBlock : Cardinal; var AErrors : String) : Boolean;
     {$ENDIF}
   End;
@@ -486,6 +487,7 @@ Var i,j : Integer;
   acc : TAccount;
   auxs : TRawBytes;
   tc : TTickCount;
+  LErrsString : TStrings;
 Begin
   tc := TPlatform.GetTickCount;
   Try
@@ -501,6 +503,15 @@ Begin
       end;
     end;
     {$IFDEF USE_ABSTRACTMEM}
+    LErrsString := TStringList.Create;
+    try
+      sb.FPCAbstractMem.CheckConsistency(LErrsString);
+      if LErrsString.Count>0 then begin
+        errors := errors + #10+ LErrsString.Text;
+      end;
+    finally
+      LErrsString.Free;
+    end;
     {$ELSE}
     // Reverse
     for i:=0 to sb.FOrderedByName.Count-1 do begin
@@ -2783,6 +2794,15 @@ begin
   FPCAbstractMem.UpdateSafeboxFileName(ANewSafeboxFileName);
 end;
 
+procedure TPCSafeBox.ClearSafeboxfile;
+var LFileName : String;
+begin
+  LFileName := FPCAbstractMem.FileName;
+  SetSafeboxFileName('');
+  DeleteFile(LFileName);
+  SetSafeboxFileName(LFileName);
+end;
+
 {$ENDIF}
 
 constructor TPCSafeBox.Create;
@@ -2859,6 +2879,9 @@ begin
   StartThreadSafe;
   Try
     Clear;
+    {$IFDEF USE_ABSTRACTMEM}
+    FPCAbstractMem.EraseData;
+    {$ENDIF}
     If Assigned(FPreviousSafeBox) then begin
       FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
       FPreviousSafeBox := Nil;
@@ -3085,7 +3108,9 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
          end else begin
            TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-1 Name %s not found at account:%d',[AddedNamesList.Get(i).ToPrintable,AddedNamesList.GetTag(i)]));
          end;
-       end else FPCAbstractMem.AccountsNames.Delete(j);
+       end else begin
+         FPCAbstractMem.AccountsNames.Delete(j);
+       end;
        {$ELSE}
        If Not FOrderedByName.Find(AddedNamesList.Get(i),j) then begin
          // ERROR: It has been added, why we not found???
@@ -3159,12 +3184,14 @@ begin
 
       // Must UNDO changes:
       UndoModifiedBlocks(Psnapshot^.oldBlocks);
+      if Psnapshot=Nil then Exit;
+
       UndoAddedDeletedNames(Psnapshot^.namesAdded,Psnapshot^.namesDeleted);
 
       // Undo Created BlockAccount
       // Undo ONLY of if not target
       {$IFDEF USE_ABSTRACTMEM}
-      FPCAbstractMem.DeleteBlockAccount(i);
+      FPCAbstractMem.DeleteBlockAccount( FPCAbstractMem.BlocksCount - 1);
       {$ELSE}
       PBlock:=FBlockAccountsList.Items[FBlockAccountsList.Count-1];
       FBlockAccountsList.Delete(FBlockAccountsList.Count-1);
@@ -3358,6 +3385,11 @@ begin
       ALastReadBlock := CT_BlockAccount_NUL;
       Clear; // Clear only when reading an entire safebox or starting at 0 block
       {$IFDEF USE_ABSTRACTMEM}
+      if sbHeader.blocksCount<FPCAbstractMem.BlocksCount then begin
+        FPCAbstractMem.EraseData;
+      end else begin
+        FPCAbstractMem.AccountsNames.Clear;
+      end;
       AggregatedHashrate.Value := 0;
       {$ELSE}
       FBlockAccountsList.Capacity := sbHeader.blockscount;
@@ -3451,17 +3483,21 @@ begin
           //
           // check valid
           If (Length(LBlock.accounts[iacc].name)>0) then begin
-            {$IFnDEF USE_ABSTRACTMEM}
-            if FOrderedByName.IndexOf(LBlock.accounts[iacc].name)>=0 then begin
+            if Not TPCSafeBox.ValidAccountName(LBlock.accounts[iacc].name,aux_errors) then begin
+              errors := errors + ' > Invalid name "'+LBlock.accounts[iacc].name.ToPrintable+'": '+aux_errors;
+              Exit;
+            end;
+            {$IFDEF USE_ABSTRACTMEM}
+            if FPCAbstractMem.AccountsNames.IndexOf( LBlock.accounts[iacc].name.ToString )>=0 then begin
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               Exit;
             end;
-            {$ENDIF}
-            if Not TPCSafeBox.ValidAccountName(LBlock.accounts[iacc].name,aux_errors) then begin
-              errors := errors + ' > Invalid name "'+LBlock.accounts[iacc].name.ToPrintable+'": '+aux_errors;
+            FPCAbstractMem.AccountsNames.Add(LBlock.accounts[iacc].name.ToString,LBlock.accounts[iacc].account);
+            {$ELSE}
+            if FOrderedByName.IndexOf(LBlock.accounts[iacc].name)>=0 then begin
+              errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               Exit;
             end;
-            {$IFnDEF USE_ABSTRACTMEM}
             FOrderedByName.Add(LBlock.accounts[iacc].name,LBlock.accounts[iacc].account);
             {$ENDIF}
           end;
@@ -3543,7 +3579,7 @@ begin
         end;
         // Add
         {$IFDEF USE_ABSTRACTMEM}
-        FPCAbstractMem.AddBlockAccount(LBlock);
+        FPCAbstractMem.SetBlockAccount(LBlock);
         {$ELSE}
         New(P);
         ToTMemBlockAccount(LBlock,P^);
@@ -4677,7 +4713,7 @@ begin
           end;
         end else begin
           If (FPCAbstractMem.AccountsNames.Item[i].accountNumber<>account_number) then begin
-            TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-3 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete at suposed account '+IntToStr(account_number)+' found at '+IntToStr(FPCAbstractMem.AccountsNames.Item[i].accountNumber));
+            TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-3 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete at suposed account '+IntToStr(account_number)+' found at '+IntToStr(FPCAbstractMem.AccountsNames.Item[i].accountNumber)+' '+FPCAbstractMem.AccountsNames.Item[i].accountName);
           end;
           FPCAbstractMem.AccountsNames.Delete(i);
         end;
@@ -4735,7 +4771,7 @@ begin
             // Is restoring to initial position, delete from deleted
             {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Adding equal to PREVIOUS (DELETING FROM DELETED) snapshot name:%s at account:%d',[blockAccount.accounts[iAccount].name,account_number]));{$ENDIF}
             FDeletedNamesSincePreviousSafebox.Delete(iDeleted);
-            FAddedNamesSincePreviousSafebox.Remove(blockAccount.accounts[iAccount].name);
+            if iAdded>=0 then FAddedNamesSincePreviousSafebox.Remove(blockAccount.accounts[iAccount].name);
           end else begin
             // Was deleted, but now adding to a new account
             {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Adding again name:%s to new account account:%d',[blockAccount.accounts[iAccount].name,account_number]));{$ENDIF}
@@ -4774,7 +4810,9 @@ begin
     FModifiedBlocksSeparatedChain.Add(blockAccount);
   end;
   {$IFDEF USE_ABSTRACTMEM}
-  FPCAbstractMem.SetAccount( blockAccount.accounts[iAccount] );
+  If Not Assigned(FPreviousSafeBox) then begin
+    FPCAbstractMem.SetAccount( blockAccount.accounts[iAccount] );
+  end;
   {$ELSE}
   If (Assigned(Pblock)) then begin
     ToTMemAccount(blockAccount.accounts[iAccount],Pblock^.accounts[iAccount]);

+ 1 - 1
src/core/UBlockChain.pas

@@ -950,7 +950,7 @@ begin
     try
       Clear;
       Storage.Initialize;
-      If (max_block<Storage.LastBlock) then n := max_block
+      If (max_block<Storage.LastBlock) or (Storage.LastBlock<0) then n := max_block
       else n := Storage.LastBlock;
 
       Storage.RestoreBank(n,restoreProgressNotify);

+ 3 - 3
src/core/UFileStorage.pas

@@ -390,7 +390,7 @@ function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan:
   begin
     FileAttrs := faArchive;
     folder := GetFolder(Orphan);
-    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
+    if SysUtils.FindFirst(GetFolder(Orphan)+PathDelim+'checkpoint*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           sourcefn := GetFolder(Orphan)+PathDelim+sr.Name;
@@ -488,8 +488,8 @@ begin
     end;
     //
     FileAttrs := faArchive;
-    folder := GetFolder(Orphan);
-    if SysUtils.FindFirst(folder+PathDelim+'*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
+    folder := GetFolder(''); /// Without Orphan folder
+    if SysUtils.FindFirst(folder+PathDelim+'checkpoint*'+CT_Safebox_Extension, FileAttrs, sr) = 0 then begin
       repeat
         if (sr.Attr and FileAttrs) = FileAttrs then begin
           auxfn := folder+PathDelim+sr.Name;

+ 44 - 3
src/core/UNetProtocol.pas

@@ -163,6 +163,7 @@ Type
     procedure CleanNodeServersList;
     Function LockList : TList<Pointer>;
     Procedure UnlockList;
+    procedure ResetConnectAttempts;
     function IsBlackListed(const ip: String): Boolean;
     function GetNodeServerAddress(const ip : String; port:Word; CanAdd : Boolean; var nodeServerAddress : TNodeServerAddress) : Boolean;
     procedure SetNodeServerAddress(const nodeServerAddress : TNodeServerAddress);
@@ -848,6 +849,25 @@ begin
   Result := FListByIp;
 end;
 
+procedure TOrderedServerAddressListTS.ResetConnectAttempts;
+Var P : PNodeServerAddress;
+  i : Integer;
+begin
+  CleanNodeServersList;
+  FCritical.Acquire;
+  Try
+    for i := FListByIp.Count - 1 downto 0 do begin
+      P := FListByIp[i];
+      P^.last_connection := 0;
+      P^.last_connection_by_server := 0;
+      P^.last_connection_by_me := 0;
+      P^.last_attempt_to_connect := 0;
+    end;
+  Finally
+    FCritical.Release;
+  End;
+end;
+
 procedure TOrderedServerAddressListTS.SecuredDeleteFromListByIp(index: Integer);
 Var P : PNodeServerAddress;
   i2 : Integer;
@@ -1627,13 +1647,24 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       Bank.Storage.Orphan := TNode.Node.Bank.Storage.Orphan;
       Bank.Storage.ReadOnly := true;
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
+
+
       if start_block>=0 then begin
         If (TNode.Node.Bank.SafeBox.HasSnapshotForBlock(start_block-1)) then begin
           // Restore from a Snapshot (New on V3) instead of restore reading from File
           Bank.SafeBox.SetToPrevious(TNode.Node.Bank.SafeBox,start_block-1);
           Bank.UpdateValuesFromSafebox;
           IsUsingSnapshot := True;
+
+          Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+          Bank.Storage.ReadOnly := false;
+
         end else begin
+          {$IFDEF USE_ABSTRACTMEM}
+          Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+          Bank.Storage.ReadOnly := false;
+          {$ENDIF}
+
           // Restore a part from disk
           Bank.DiskRestoreFromOperations(start_block-1);
           Bank.Storage.SaveBank(True);
@@ -1649,8 +1680,10 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         start_block := 0;
       end;
       start_c := start;
-      Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
-      Bank.Storage.ReadOnly := false;
+      if Bank.Storage.ReadOnly then begin
+        Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+        Bank.Storage.ReadOnly := false;
+      end;
       // Receive new blocks:
       finished := false;
       repeat
@@ -1740,6 +1773,11 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
               {$ENDIF}
             end else begin
               TLog.NewLog(ltInfo,CT_LogSender,'Restoring modified Safebox from Disk');
+
+              {$IFDEF USE_ABSTRACTMEM}
+              TNode.Node.Bank.SafeBox.ClearSafeboxfile;
+              {$ELSE}
+              {$ENDIF}
               TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
             end;
           Finally
@@ -2327,6 +2365,8 @@ begin
   NotifyConnectivityChanged;
   if FNetConnectionsActive then DiscoverServers
   else DisconnectClients;
+  TNode.Node.NetServer.Active := Value;
+  NotifyConnectivityChanged;
 end;
 
 function TNetData.UnRegisterRequest(Sender: TNetConnection; operation: Word; request_id: Cardinal): Boolean;
@@ -2436,6 +2476,7 @@ begin
   inherited;
   if Active then begin
     // TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
+    TNetData.NetData.NodeServersAddresses.ResetConnectAttempts;
   end else if TNetData.NetDataExists then begin
     TNetData.NetData.DisconnectClients;
   end;
@@ -3156,7 +3197,7 @@ begin
       FreeAndNil(Labstracmem);
     end;
     {$ELSE}
-    sbStream := TNode.Node.Bank.Storage.CreateSafeBoxStream(_blockcount);
+    sbStream := TNode.Node.Bank.Storage.OpenSafeBoxCheckpoint(_blockcount);
     try
       If Not Assigned(sbStream) then begin
         SendError(ntp_response,HeaderData.operation,CT_NetError_SafeboxNotFound,HeaderData.request_id,Format('Safebox stream file for block %d not found',[_blockcount]));

+ 121 - 33
src/core/UPCAbstractMem.pas

@@ -47,7 +47,6 @@ type
   private
     FPCAbstractMem: TPCAbstractMem;
   protected
-    FAccounts: TPCAbstractMemListAccounts;
     function ToString(const AItem: TAccountNameInfo): string; override;
 
     procedure LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo); override;
@@ -93,6 +92,7 @@ type
     Constructor Create(APCAbstractMem : TPCAbstractMem);
     Destructor Destroy;
     procedure Restart;
+    property Errors : TStrings read FErrors;
   End;
 
   TAccountCache = Class(TAVLCache<TAccount>)
@@ -121,7 +121,8 @@ type
     procedure AddBlockInfo(const ABlock : TOperationBlockExt);
     procedure SetBlockInfo(const ABlock : TOperationBlockExt);
     function DoInit(out AIsNewStructure : Boolean) : Boolean;
-
+  protected
+    procedure UpgradeAbstractMemVersion(const ACurrentHeaderVersion : Integer);
   public
     constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
     class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
@@ -153,13 +154,17 @@ type
     procedure SaveToFile(const ASaveToFileName : String);
     procedure UpdateSafeboxFileName(const ANewSafeboxFileName : String);
     property AccountCache : TAccountCache read FAccountCache;
+    property FileName : String read FFileName;
+    procedure EraseData;
   end;
 
 implementation
 
+uses UAccounts;
 
 const
   CT_PCAbstractMem_FileVersion = CT_PROTOCOL_5;
+  CT_PCAbstractMem_HeaderVersion = 1;
 
 function _AccountCache_Comparision(const Left, Right: TAccountCache.PAVLCacheMemData): Integer;
 begin
@@ -284,6 +289,10 @@ begin
     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');
+    // Force account_seal to 20 bytes
+    if Length(AItem.account_seal)<>20 then begin
+      AItem.account_seal := TBaseType.T20BytesToRawBytes( TBaseType.To20Bytes(AItem.account_seal) );
+    end;
   Finally
     LStream.Free;
   End;
@@ -294,7 +303,6 @@ var LStream : TStream;
   LPointer : TAbstractMemPosition;
   w : Word;
   LPrevious : TAccount;
-  LAddNewNameToAccountNamesList : Boolean;
 begin
   if (Length(ABytes)>0) and (Not AIsAddingItem) then begin
     // Capture previous values
@@ -305,12 +313,7 @@ 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;
+  end;
 
   LStream := TMemoryStream.Create;
   try
@@ -355,9 +358,6 @@ begin
     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 );
@@ -374,6 +374,28 @@ end;
 
 function TPCAbstractMem.CheckConsistency(AReport: TStrings) : Boolean;
 begin
+  AReport.Clear;
+  FLockAbstractMem.Acquire;
+  Try
+    if Assigned(FCheckingThread) then begin
+      FCheckingThread.Terminate;
+      FCheckingThread.WaitFor;
+      FreeAndNil(FCheckingThread);
+    end;
+
+    if Not Assigned(FCheckingThread) then begin
+      FCheckingThread := TPCAbstractMemCheckThread.Create(Self);
+    end;
+    while Not FCheckingThread.Terminated do Sleep(1);
+    AReport.Assign( FCheckingThread.Errors );
+
+    FCheckingThread.Terminate;
+    FCheckingThread.WaitFor;
+    FreeAndNil(FCheckingThread);
+  Finally
+    FLockAbstractMem.Release;
+  End;
+  Result := AReport.Count=0;
 end;
 
 procedure TPCAbstractMem.CopyFrom(ASource: TPCAbstractMem);
@@ -386,9 +408,10 @@ end;
 
 function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
 const
+  CT_HEADER_MIN_SIZE = 100;
   CT_HEADER_STRING = 'TPCAbstractMem'; // Do not localize/modify. Fixed 14 bytes!
   {
-  Header:
+  Header: 0..99 = 100 Bytes (CT_HEADER_MIN_SIZE)
   [ 0..13] 14 bytes: Literal "TPCAbstractMem"
   [14..15] 2 bytes: Protocol version
   [16..19] 4 bytes: LZoneBlocks.position
@@ -397,8 +420,8 @@ const
   [28..31] 4 bytes: LZoneAccountKeys.position
   [32..35] 4 bytes: FZoneAggregatedHashrate.position
   [36..39] 4 bytes: LZoneBuffersBlockHash
-
-
+  ...
+  [96..99] 4 bytes: Header version
   }
 var LZone,
   LZoneBlocks,
@@ -410,6 +433,7 @@ var LZone,
   LIsGood : Boolean;
   w : Word;
   i : Integer;
+  LHeaderVersion : UInt32;
 begin
   // Free
   FreeAndNil(FBlocks);
@@ -430,7 +454,7 @@ begin
 
   if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
     // Check if header is valid:
-    if Length(LHeader)>=100 then begin
+    if Length(LHeader)>=CT_HEADER_MIN_SIZE then begin
       LIsGood := True;
       i := 0;
       while (LIsGood) and (i<CT_HEADER_STRING.Length) do begin
@@ -449,14 +473,19 @@ begin
         Move(LHeader[28], LZoneAccountKeys.position, 4);
         Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
         LZoneBuffersBlockHash := LZone.position + 36;
-        AIsNewStructure := False;
+        Move(LHeader[96], LHeaderVersion, 4);
+        if (LHeaderVersion>CT_PCAbstractMem_HeaderVersion) then begin
+          TLog.NewLog(lterror,ClassName,Format('Header version readed %d is greater than expected %d',[LHeaderVersion,CT_PCAbstractMem_HeaderVersion]));
+        end else begin
+          AIsNewStructure := False;
+        end;
       end;
     end;
   end;
   if (Not FAbstractMem.ReadOnly) and (AIsNewStructure) then begin
     // Initialize struct
     FAbstractMem.ClearContent;
-    LZone := FAbstractMem.New( 100 );  // Header zone
+    LZone := FAbstractMem.New( CT_HEADER_MIN_SIZE );  // Header zone
     SetLength(LHeader,100);
     FillChar(LHeader[0],Length(LHeader),0);
     //
@@ -476,8 +505,11 @@ begin
     Move(LZoneAccountsNames.position,LHeader[24],4);
     Move(LZoneAccountKeys.position,  LHeader[28],4);
     Move(FZoneAggregatedHashrate.position,LHeader[32],4);
+    LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
+    Move(LHeaderVersion,             LHeader[96],4);
 
     FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+
   end;
   // Free
   FreeAndNil(FBlocks);
@@ -498,6 +530,24 @@ begin
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
 
   FAccountCache.Clear;
+
+  if (Not AIsNewStructure) And (Not FAbstractMem.ReadOnly) And (LHeaderVersion<CT_PCAbstractMem_HeaderVersion) then begin
+    UpgradeAbstractMemVersion( LHeaderVersion );
+    // Set for future
+    LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
+    Move(LHeaderVersion,             LHeader[96],4);
+    FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+  end;
+
+end;
+
+procedure TPCAbstractMem.EraseData;
+var
+  LIsNewStructure : Boolean;
+begin
+  FlushCache;
+  FAbstractMem.ClearContent;
+  DoInit(LIsNewStructure);
 end;
 
 constructor TPCAbstractMem.Create(const ASafeboxFileName: string; AReadOnly: boolean);
@@ -511,7 +561,7 @@ begin
   FAggregatedHashrate := TBigNum.Create(0);
   FFileName := ASafeboxFileName;
   if (FFileName<>'') {and (FileExists(ASafeboxFileName))} then begin
-    FAbstractMem := TFileMem.Create( ASafeboxFileName , AReadOnly)
+    FAbstractMem := TFileMem.Create( ASafeboxFileName , AReadOnly);
   end else begin
     FAbstractMem := TMem.Create(0,AReadOnly);
   end;
@@ -522,7 +572,7 @@ begin
 
   DoInit(LIsNewStructure);
   //
-  if ((BlocksCount>0) And (ASafeboxFileName<>'')) Or (Not LIsNewStructure) then begin
+  if (Not AReadOnly) and (((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;
@@ -560,6 +610,7 @@ begin
   end;
   FreeAndNil(FAbstractMem);
 
+
   FreeAndNil(FLockAbstractMem);
 
   inherited Destroy;
@@ -666,15 +717,28 @@ end;
 
 procedure TPCAbstractMem.SetBlockAccount(const ABlockAccount: TBlockAccount);
 var i : Integer;
-  LOpBlockExt :  TOperationBlockExt;
+  LOpBlockExt, LSavedOpBlockExt :  TOperationBlockExt;
+  LSavedAccount : TAccount;
 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);
+  if ABlockAccount.blockchainInfo.block=BlocksCount then AddBlockAccount(ABlockAccount)
+  else if ABlockAccount.blockchainInfo.block<BlocksCount then begin
+    LOpBlockExt.operationBlock := ABlockAccount.blockchainInfo;
+    LOpBlockExt.accumulatedWork := ABlockAccount.accumulatedWork;
+    LSavedOpBlockExt := GetBlockInfo( ABlockAccount.blockchainInfo.block );
+    if (Not TAccountComp.EqualOperationBlocks( LOpBlockExt.operationBlock, LSavedOpBlockExt.operationBlock ))
+       or (LOpBlockExt.accumulatedWork <> LSavedOpBlockExt.accumulatedWork) then begin
+      SetBlockInfo(LOpBlockExt);
+    end;
+    for i := Low(ABlockAccount.accounts) to High(ABlockAccount.accounts) do begin
+      if TAccountComp.AccountBlock(ABlockAccount.accounts[i].account)<>ABlockAccount.blockchainInfo.block then
+        raise EPCAbstractMem.Create(Format('Account %d is not valid for block %d',[ABlockAccount.accounts[i].account,ABlockAccount.blockchainInfo.block]));
+      LSavedAccount := GetAccount(ABlockAccount.accounts[i].account);
+      if Not TAccountComp.EqualAccounts(LSavedAccount, ABlockAccount.accounts[i]) then begin
+        SetAccount( ABlockAccount.accounts[i] );
+      end;
+    end;
+    FBufferBlocksHash.Replace(ABlockAccount.blockchainInfo.block * 32, ABlockAccount.block_hash);
+  end else raise EPCAbstractMem.Create(Format('Cannot add Block %d on %d count list',[ABlockAccount.blockchainInfo.block,BlocksCount]));
 end;
 
 procedure TPCAbstractMem.AddBlockInfo(const ABlock : TOperationBlockExt);
@@ -733,6 +797,32 @@ begin
   DoInit(Ltmp);
 end;
 
+procedure TPCAbstractMem.UpgradeAbstractMemVersion(const ACurrentHeaderVersion: Integer);
+var LFirstTC, LTC : TTickCount;
+  i : integer;
+  LAccount : TAccount;
+begin
+  LFirstTC := TPlatform.GetTickCount;
+  LTC := LFirstTC;
+  if (ACurrentHeaderVersion=0) then begin
+    // Redo AccountNames
+    TLog.NewLog(ltinfo,ClassName,Format('Upgrade AbstractMem file from %d to %d with %d Accounts and %d AccNames',[ACurrentHeaderVersion,CT_PCAbstractMem_HeaderVersion, AccountsCount, AccountsNames.Count]));
+    AccountsNames.Clear;
+    for i := 0 to AccountsCount-1 do begin
+      LAccount := GetAccount(i);
+      if Length(LAccount.name)>0 then begin
+        AccountsNames.Add( LAccount.name.ToString, LAccount.account );
+      end;
+      if TPlatform.GetElapsedMilliseconds(LTC)>5000 then begin
+        LTC := TPlatform.GetTickCount;
+        TLog.NewLog(ltdebug,ClassName,Format('Upgrading %d/%d found %d',[i,AccountsCount,AccountsNames.Count]));
+      end;
+    end;
+    TLog.NewLog(ltdebug,ClassName,Format('End upgrade found %d',[AccountsNames.Count]));
+  end;
+  TLog.NewLog(ltinfo,ClassName,Format('Finalized upgrade AbstractMem file from %d to %d in %.2f seconds',[ACurrentHeaderVersion,CT_PCAbstractMem_HeaderVersion, TPlatform.GetElapsedMilliseconds(LFirstTC)/1000]));
+end;
+
 function TPCAbstractMem.BlocksCount: integer;
 begin
   Result := FBlocks.Count;
@@ -761,7 +851,7 @@ var i : Integer;
 begin
   LBlAcc := GetBlockAccount(ABlockNumber);
   FBlocks.Delete(ABlockNumber);
-  for i := Low(LBlAcc.accounts) to High(LBlAcc.accounts) do begin
+  for i := High(LBlAcc.accounts) downto Low(LBlAcc.accounts) do begin
     FAccounts.Delete(LBlAcc.accounts[i].account );
   end;
   LExtract := FBufferBlocksHash.Capture((ABlockNumber+1)*32,32);
@@ -1047,8 +1137,7 @@ begin
     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);
+  TLog.NewLog(ltDebug,ClassName,Format('Finalized checking consistency at %d %d blocks in %.2f sec',[iBlock+1,FPCAbstractMem.BlocksCount,TPlatform.GetElapsedMilliseconds(LTCInitial)/1000]));
 end;
 
 constructor TPCAbstractMemCheckThread.Create(APCAbstractMem: TPCAbstractMem);
@@ -1065,9 +1154,8 @@ begin
   FErrorsCount := 0;
   FErrors := TStringList.Create;
   FMustRestart := False;
-  FreeOnTerminate := True;
   inherited Create(True);
-  FreeOnTerminate := True;
+  FreeOnTerminate := False;
   Suspended := False;
 end;
 

+ 6 - 1
src/core/UPCOrderedLists.pas

@@ -27,6 +27,8 @@ uses
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 Type
+  EOrderedList = Class(Exception);
+
   TCardinalsArray = Array of Cardinal;
 
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
@@ -298,6 +300,7 @@ end;
 procedure TOrderedRawList.Delete(index: Integer);
 Var P : PRawListData;
 begin
+  if (index<0) or (index>=FList.Count) then raise EOrderedList.Create(Format('Index %d out of range 0..%d',[index,FList.Count-1]));
   P := PRawListData(FList[index]);
   FList.Delete(index);
   Dispose(P);
@@ -337,11 +340,13 @@ end;
 
 function TOrderedRawList.Get(index: Integer): TRawBytes;
 begin
-  Result := PRawListData(FList[index])^.RawData;
+  if (index<0) or (index>=FList.Count) then raise EOrderedList.Create(Format('Index %d out of range 0..%d',[index,FList.Count-1]));
+  Result := Copy(PRawListData(FList[index])^.RawData);
 end;
 
 function TOrderedRawList.GetTag(index: Integer): Integer;
 begin
+  if (index<0) or (index>=FList.Count) then raise EOrderedList.Create(Format('Index %d out of range 0..%d',[index,FList.Count-1]));
   Result := PRawListData(FList[index])^.tag;
 end;
 

+ 15 - 4
src/libraries/abstractmem/UAbstractMem.pas

@@ -243,11 +243,17 @@ begin
   // Will erase ALL content creating a new null header
   if FReadOnly then raise EAbstractMem.Create('Cannot ClearContent on a ReadOnly AbstractMem');
   CheckInitialized(True);
-  //
+
+  FNextAvailablePos := CT_HeaderSize; // By Default
+
+  FMaxAvailablePos := 0;
+  IncreaseSize(0);
+
+  FHeaderInitialized := False;
+  CheckInitialized(True);
+
   LNewRoot.Clear;
   FMemLeaks.SetRoot( LNewRoot );
-  FNextAvailablePos := CT_HeaderSize;
-  SaveHeader;
 end;
 
 procedure TAbstractMem.CopyFrom(ASource: TAbstractMem);
@@ -426,7 +432,7 @@ begin
   //
   FNextAvailablePos := LTmpNextAvailablePos;
   FMaxAvailablePos := LTmpMaxAvailablePos;
-  SaveHeader;
+  if ANeedSize>0 then SaveHeader;
 end;
 
 function TAbstractMem.IsAbstractMemInfoStable: Boolean;
@@ -812,7 +818,12 @@ end;
 
 procedure TMem.DoIncreaseSize(var ANextAvailablePos, AMaxAvailablePos: Integer; ANeedSize: Integer);
 begin
+  if (ANeedSize<=0) And (AMaxAvailablePos<=0) then begin
+    SetLength(FMem,0); // Reset
+    Exit;
+  end;
   AMaxAvailablePos := Length(FMem);
+  if (AMaxAvailablePos-ANextAvailablePos+1 >= ANeedSize) then Exit;
 
   ANeedSize := (((ANeedSize-1) DIV 256)+1)*256;
 

+ 7 - 0
src/libraries/abstractmem/UFileMem.pas

@@ -141,6 +141,13 @@ end;
 procedure TFileMem.DoIncreaseSize(var ANextAvailablePos, AMaxAvailablePos: Integer; ANeedSize: Integer);
 var LBuff : TBytes;
 begin
+  if (ANeedSize<=0) And (AMaxAvailablePos<=0) then begin
+    FCache.Clear;
+    FFileStream.Seek(0,soFromEnd);
+    FFileStream.Size := 0;
+    Exit;
+  end;
+
   FFileStream.Seek(0,soFromEnd);
   // GoTo ANextAvailablePos
   if (FFileStream.Position<ANextAvailablePos) then begin