Browse Source

Improve PCAbstractMem adding BTree structures

TPCAbstractMemListAccountNames = Class(TAbstractMemBTreeData<TAccountNameInfo>)
TAccountsUsingThisKey = Class(TAbstractMemBTree)
New file version and more improvements
Pascal Coin 4 years ago
parent
commit
9450307f8f

+ 79 - 36
src/core/UAccounts.pas

@@ -30,7 +30,9 @@ uses
   UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFDEF USE_ABSTRACTMEM}
   UPCAbstractMem, UPCAbstractMemAccountKeys,
+  {$ELSE}
   {$ENDIF}
+  UPCAccountsOrdenations,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 Type
@@ -216,6 +218,7 @@ Type
     FOrderedByName : TOrderedRawList;
     // OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
     FOrderedAccountKeysList : TSafeboxPubKeysAndAccounts;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
     {$ENDIF}
     FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
     //
@@ -245,6 +248,7 @@ Type
     procedure SearchBlockWhenOnSeparatedChain(blockNumber : Cardinal; out blockAccount : TBlockAccount);
     function GetAggregatedHashrate: TBigNum;
     function GetOrderedAccountKeysList: TSafeboxPubKeysAndAccounts;
+    function GetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   protected
     FTotalFee: Int64;
     Procedure UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo; const newName : TRawBytes; newType : Word;
@@ -322,6 +326,7 @@ Type
     class Function CopyAbstractMemToSafeBoxStream(ASource : TPCAbstractMem; ADestStream : TStream; AFromBlock, AToBlock : Cardinal; var AErrors : String) : Boolean;
     property PCAbstractMem : TPCAbstractMem read FPCAbstractMem;
     {$ENDIF}
+    Function AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
   End;
 
 
@@ -2408,6 +2413,15 @@ begin
   end;
 end;
 
+function TPCSafeBox.AccountsOrderedByUpdatedBlock: TAccountsOrderedByUpdatedBlock;
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  Result := FPCAbstractMem.AccountsOrderedByUpdatedBlock;
+  {$ELSE}
+  Result := FAccountsOrderedByUpdatedBlock;
+  {$ENDIF}
+end;
+
 function TPCSafeBox.GetBlock(block_number: Cardinal): TBlockAccount;
 begin
   StartThreadSafe;
@@ -2823,6 +2837,7 @@ begin
   FBlockAccountsList := TList<Pointer>.Create;
   FAggregatedHashrate := TBigNum.Create(0);
   FOrderedByName := TOrderedRawList.Create;
+  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create(GetAccount);
   {$ENDIF}
   FListOfOrderedAccountKeysList := TList<TOrderedAccountKeysList>.Create;
   FCurrentProtocol := CT_PROTOCOL_1;
@@ -2866,6 +2881,10 @@ begin
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FSubChains);
+  {$IFnDEF USE_ABSTRACTMEM}
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  {$ENDIF}
+
   If Assigned(FPreviousSafeBox) then begin
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox := Nil;
@@ -2968,7 +2987,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Start deleting:
     For i:=0 to DeletedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
-      FPreviousSafebox.FPCAbstractMem.AccountsNames.Remove(DeletedNamesList.Get(i).ToString);
+      FPreviousSafebox.FPCAbstractMem.AccountsNames.DeleteAccountName(DeletedNamesList.Get(i).ToString);
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Remove(DeletedNamesList.Get(i));
       {$ENDIF}
@@ -2976,7 +2995,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Finally adding
     For i:=0 to AddedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
-      FPreviousSafebox.FPCAbstractMem.AccountsNames.Add(AddedNamesList.Get(i).ToString,AddedNamesList.GetTag(i));
+      FPreviousSafebox.FPCAbstractMem.AccountsNames.AddNameAndNumber(AddedNamesList.Get(i).ToString,AddedNamesList.GetTag(i));
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Add(AddedNamesList.Get(i),AddedNamesList.GetTag(i));
       {$ENDIF}
@@ -3111,19 +3130,23 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
 
    Procedure UndoAddedDeletedNames(AddedNamesList,DeletedNamesList : TOrderedRawList);
    Var i,j : Integer;
+     {$IFDEF USE_ABSTRACTMEM}
+     Laninfo : TAccountNameInfo;
+     {$ELSE}
+     {$ENDIF}
    Begin
      // Start adding
      For i:=0 to AddedNamesList.Count-1 do begin
        // It was added, so we MUST FIND on current names list
        {$IFDEF USE_ABSTRACTMEM}
-       If Not FPCAbstractMem.AccountsNames.FindByName(AddedNamesList.Get(i).ToString,j) then begin
+       If Not FPCAbstractMem.AccountsNames.FindByName(AddedNamesList.Get(i).ToString,Laninfo) then begin
          // ERROR: It has been added, why we not found???
          If DeletedNamesList.Find(AddedNamesList.Get(i),j) then begin
          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 begin
-         FPCAbstractMem.AccountsNames.Delete(j);
+         FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
        end;
        {$ELSE}
        If Not FOrderedByName.Find(AddedNamesList.Get(i),j) then begin
@@ -3139,15 +3162,14 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
      For i:=0 to DeletedNamesList.Count-1 do begin
        {$IFDEF USE_ABSTRACTMEM}
        // It has been deleted, we MUST NOT FIND on current names list
-       If FPCAbstractMem.AccountsNames.FindByName(DeletedNamesList.Get(i).ToString,j) then begin
-         // It has been deleted... now is found
-         If (FPCAbstractMem.AccountsNames.Item[j].accountNumber<>DeletedNamesList.GetTag(i)) then begin
+       If FPCAbstractMem.AccountsNames.FindByName(DeletedNamesList.Get(i).ToString,Laninfo) then begin
+         if Laninfo.accountNumber<>DeletedNamesList.GetTag(i) then begin
            // ERROR: It has been deleted, why is found with another account???
-           TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-2 Name %s found at account:%d <> saved account:%d',[DeletedNamesList.Get(i).ToPrintable,DeletedNamesList.GetTag(i),FPCAbstractMem.AccountsNames.Item[j].accountNumber]));
+           TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-2 Name %s found at account:%d <> saved account:%d',[DeletedNamesList.Get(i).ToPrintable,DeletedNamesList.GetTag(i),Laninfo.accountNumber]));
          end;
        end;
        // Add with Info of previous account with name (saved at Tag value)
-       FPCAbstractMem.AccountsNames.Add(DeletedNamesList.Get(i).ToString,DeletedNamesList.GetTag(i));
+       FPCAbstractMem.AccountsNames.AddNameAndNumber(DeletedNamesList.Get(i).ToString,DeletedNamesList.GetTag(i));
        {$ELSE}
        // It has been deleted, we MUST NOT FIND on current names list
        If FOrderedByName.Find(DeletedNamesList.Get(i),j) then begin
@@ -3412,7 +3434,7 @@ begin
       if sbHeader.blocksCount<FPCAbstractMem.BlocksCount then begin
         FPCAbstractMem.EraseData;
       end else begin
-        FPCAbstractMem.AccountsNames.Clear;
+        FPCAbstractMem.AccountsNames.EraseTree;
       end;
       AggregatedHashrate.Value := 0;
       {$ELSE}
@@ -3514,11 +3536,11 @@ begin
               Exit;
             end;
             {$IFDEF USE_ABSTRACTMEM}
-            if FPCAbstractMem.AccountsNames.IndexOf( LBlock.accounts[iacc].name.ToString )>=0 then begin
+            if FPCAbstractMem.AccountsNames.FindByName(LBlock.accounts[iacc].name.ToString ) then begin
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               Exit;
             end;
-            FPCAbstractMem.AccountsNames.Add(LBlock.accounts[iacc].name.ToString,LBlock.accounts[iacc].account);
+            FPCAbstractMem.AccountsNames.AddNameAndNumber(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+'"';
@@ -3613,6 +3635,12 @@ begin
         // BufferBlocksHash fill with data
         j := (length(LBlock.block_hash)*(iBlock));
         BufferBlocksHash.Replace( j, LBlock.block_hash[0], 32 );
+        for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
+          FAccountsOrderedByUpdatedBlock.Update(
+            LBlock.accounts[j].account,
+            LBlock.accounts[j].updated_on_block_active_mode,
+            LBlock.accounts[j].updated_on_block_active_mode);
+        end;
         {$ENDIF}
         for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
           AccountKeyListAddAccounts(LBlock.accounts[j].accountInfo.accountKey,[LBlock.accounts[j].account]);
@@ -4536,6 +4564,12 @@ begin
   end;
 end;
 
+function TPCSafeBox.GetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := Account(AAccountNumber);
+  Result := True;
+end;
+
 function TPCSafeBox.GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
 begin
   Result := TPascalCoinProtocol.TargetToCompact(GetActualTargetHash(protocolVersion),protocolVersion);
@@ -4549,10 +4583,13 @@ end;
 function TPCSafeBox.FindAccountByName(const aName: TRawBytes): Integer;
 Var i,j,k : Integer;
   Psnapshot : PSafeboxSnapshot;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ENDIF}
 begin
   {$IFDEF USE_ABSTRACTMEM}
-  i := FPCAbstractMem.AccountsNames.IndexOf(aName.ToString);
-  if i>=0 then Result := FPCAbstractMem.AccountsNames.Item[i].accountNumber
+  if FPCAbstractMem.AccountsNames.FindByName(aName.ToString,Laninfo) then
+    Result := Laninfo.accountNumber
   {$ELSE}
   i := FOrderedByName.IndexOf(aName);
   if i>=0 then Result := FOrderedByName.GetTag(i)
@@ -4607,26 +4644,24 @@ end;
 
 function TPCSafeBox.FindAccountsStartingByName(const AStartName: TRawBytes;
   const ARawList: TOrderedRawList; const AMax: Integer = 0): Integer;
-var LIndex : Integer;
+var
   LRaw : TRawBytes;
-  LStartNameString : String;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ELSE}
+  LIndex : Integer;
+  {$ENDIF}
 begin
   ARawList.Clear;
   StartThreadSafe;
   try
     {$IFDEF USE_ABSTRACTMEM}
-    if FPCAbstractMem.AccountsNames.FindByName(AStartName.ToString,LIndex) then begin
-      LRaw.FromString(FPCAbstractMem.AccountsNames.Item[LIndex].accountName);
-      ARawList.Add( LRaw, FPCAbstractMem.AccountsNames.Item[LIndex].accountNumber );
-      inc(LIndex);
-    end;
-    LStartNameString := AStartName.ToString;
-    while (LIndex<FPCAbstractMem.AccountsNames.Count) and (FPCAbstractMem.AccountsNames.Item[LIndex].accountName.StartsWith( LStartNameString ) )
-      and ((AMax<=0) or (AMax>ARawList.Count)) // AMax <=0 inifinte results
-      do begin
-      LRaw.FromString( FPCAbstractMem.AccountsNames.Item[LIndex].accountName );
-      ARawList.Add( LRaw, FPCAbstractMem.AccountsNames.Item[LIndex].accountNumber );
-      inc(LIndex);
+    FPCAbstractMem.AccountsNames.FindByName(AStartName.ToString,Laninfo);
+    while (Laninfo.accountName.StartsWith(AStartName.ToString))
+      and ((AMax<=0) or (AMax>ARawList.Count)) do begin
+      LRaw.FromString(Laninfo.accountName);
+      ARawList.Add( LRaw, Laninfo.accountNumber );
+      if not FPCAbstractMem.AccountsNames.FindDataSuccessor(Laninfo,Laninfo) then Break;
     end;
     {$ELSE}
     if FOrderedByName.Find(AStartName,LIndex) then begin
@@ -4703,6 +4738,8 @@ Var iBlock : Cardinal;
   blockAccount : TBlockAccount;
   {$IFnDEF USE_ABSTRACTMEM}
   Pblock : PBlockAccount;
+  {$ELSE}
+  Laninfo : TAccountNameInfo;
   {$ENDIF}
 begin
   iBlock := account_number DIV CT_AccountsPerBlock;
@@ -4716,6 +4753,11 @@ begin
   end else begin
     Pblock := FBlockAccountsList.Items[iBlock];
   end;
+  FAccountsOrderedByUpdatedBlock.Update(
+    account_number,
+    blockAccount.accounts[iAccount].updated_on_block_active_mode,
+    newUpdated_block_active_mode
+   );
   {$ENDIF}
 
   if (NOT TAccountComp.EqualAccountKeys(blockAccount.accounts[iAccount].accountInfo.accountKey,newAccountInfo.accountKey)) then begin
@@ -4746,16 +4788,15 @@ begin
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
 
         {$IFDEF USE_ABSTRACTMEM}
-        i := FPCAbstractMem.AccountsNames.IndexOf(blockAccount.accounts[iAccount].name.ToString);
-        if i<0 then begin
+        if Not FPCAbstractMem.AccountsNames.FindByName(blockAccount.accounts[iAccount].name.ToString,Laninfo) then begin
           If (Not Assigned(FPreviousSafeBox)) then begin
             TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-1 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete on account '+IntToStr(account_number));
           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)+' '+FPCAbstractMem.AccountsNames.Item[i].accountName);
+          If (Laninfo.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(Laninfo.accountNumber)+' '+Laninfo.accountName);
           end;
-          FPCAbstractMem.AccountsNames.Delete(i);
+          FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
         end;
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
@@ -4793,9 +4834,11 @@ begin
       blockAccount.accounts[iAccount].name:=newName;
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
         {$IFDEF USE_ABSTRACTMEM}
-        i := FPCAbstractMem.AccountsNames.IndexOf(blockAccount.accounts[iAccount].name.ToString);
-        if i>=0 then TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(FPCAbstractMem.AccountsNames.Item[i].accountNumber));
-        FPCAbstractMem.AccountsNames.Add(blockAccount.accounts[iAccount].name.ToString,account_number);
+        if FPCAbstractMem.AccountsNames.FindByName(blockAccount.accounts[iAccount].name.ToString,Laninfo) then begin
+          TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(Laninfo.accountNumber));
+          FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
+        end;
+        FPCAbstractMem.AccountsNames.AddNameAndNumber(blockAccount.accounts[iAccount].name.ToString,account_number);
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
         if i>=0 then TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(FOrderedByName.GetTag(i)));

+ 157 - 233
src/core/UPCAbstractMem.pas

@@ -8,10 +8,12 @@ interface
 
 uses Classes, SysUtils, SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
-  UAbstractBTree, UThread,
+  UAbstractBTree, UThread, UAbstractMemBTree,
   UAVLCache, ULog, UCrypto,
   UPCAbstractMemAccountKeys,
   UPCDataTypes, UBaseTypes, UConst, UPCSafeBoxRootHash, UOrderedList,
+  UPCAccountsOrdenations,
+  UPCAbstractMemAccounts,
 {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 type
@@ -34,8 +36,6 @@ type
     procedure SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes); override;
   end;
 
-  TPCAbstractMemListAccounts = class;
-
   TAccountNameInfo = record
     accountName: string;
     accountNumber: cardinal;
@@ -43,30 +43,22 @@ type
 
   { TPCAbstractMemListAccountNames }
 
-  TPCAbstractMemListAccountNames = class(TAbstractMemOrderedTList<TAccountNameInfo>)
+  TPCAbstractMemListAccountNames = Class(TAbstractMemBTreeData<TAccountNameInfo>)
   private
     FPCAbstractMem: TPCAbstractMem;
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
+    procedure SaveTo(const AItem: TAccountNameInfo; var ABytes: TBytes);
+    function FindByName(const AName : String; out AAbstractMemPosition : TAbstractMemPosition) : Boolean; overload;
   protected
-    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;
+    function LoadData(const APosition : TAbstractMemPosition) : TAccountNameInfo; override;
+    function SaveData(const AData : TAccountNameInfo) : TAMZone; 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;
+    function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+    // Special
+    procedure AddNameAndNumber(const AName : String; AAccountNumber : Cardinal);
+    function FindByName(const AName : String) : Boolean; overload;
+    function FindByName(const AName : String; out ANameInfo : TAccountNameInfo) : Boolean; overload;
+    function DeleteAccountName(const AName : String) : Boolean;
   end;
 
   { TPCAbstractMemBytesBuffer32Safebox }
@@ -128,6 +120,7 @@ type
 
     FSavingOldGridCache : Boolean;
     FSavingOldDefaultCacheDataBlocksSize : Integer;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
 
     function IsChecking : Boolean;
     procedure DoCheck;
@@ -145,6 +138,7 @@ type
     procedure SetSavingNewSafeboxMode(const Value: Boolean);
   protected
     procedure UpgradeAbstractMemVersion(const ACurrentHeaderVersion : Integer);
+    function DoGetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   public
     constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
     class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
@@ -185,6 +179,7 @@ type
     Property MaxAccountsCache : Integer read GetMaxAccountsCache write SetMaxAccountsCache;
     Property MaxAccountKeysCache : Integer read GetMaxAccountKeysCache write SetMaxAccountKeysCache;
     Property SavingNewSafeboxMode : Boolean read FSavingNewSafeboxMode write SetSavingNewSafeboxMode;
+    Property AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock read FAccountsOrderedByUpdatedBlock;
   end;
 
 implementation
@@ -192,8 +187,8 @@ implementation
 uses UAccounts;
 
 const
-  CT_PCAbstractMem_FileVersion = CT_PROTOCOL_5;
-  CT_PCAbstractMem_HeaderVersion = 1;
+  CT_PCAbstractMem_FileVersion = 100;
+  CT_PCAbstractMem_HeaderVersion = 2;
 
 function _AccountCache_Comparision(const Left, Right: TAccountCache.PAVLCacheMemData): Integer;
 begin
@@ -260,145 +255,6 @@ begin
   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');
-    // 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;
-end;
-
-procedure TPCAbstractMemListAccounts.SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes);
-var LStream : TStream;
-  LPointer : TAbstractMemPosition;
-  w : Word;
-  LPrevious : TAccount;
-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;
-  end;
-
-  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 );
-
-    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;
@@ -437,6 +293,11 @@ begin
   DoInit(LIsNew);
 end;
 
+function _TComparison_TAccountNameInfo(const ALeft, ARight : TAccountNameInfo) : Integer;
+begin
+  Result := CompareText(ALeft.accountName,ARight.accountName);
+end;
+
 function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
 const
   CT_HEADER_MIN_SIZE = 100;
@@ -451,6 +312,7 @@ const
   [28..31] 4 bytes: LZoneAccountKeys.position
   [32..35] 4 bytes: FZoneAggregatedHashrate.position
   [36..39] 4 bytes: LZoneBuffersBlockHash
+  [40..43] 4 bytes: LZoneAccountsOrderedByUpdatedBlock.position
   ...
   [96..99] 4 bytes: Header version
   }
@@ -458,7 +320,8 @@ var LZone,
   LZoneBlocks,
   LZoneAccounts,
   LZoneAccountsNames,
-  LZoneAccountKeys : TAMZone;
+  LZoneAccountKeys,
+  LZoneAccountsOrderedByUpdatedBlock : TAMZone;
   LZoneBuffersBlockHash : TAbstractMemPosition;
   LHeader, LBuffer, LBigNum : TBytes;
   LIsGood : Boolean;
@@ -472,6 +335,7 @@ begin
   FreeAndNil(FAccountsNames);
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
   //
   Result := False;
   AIsNewStructure := True;
@@ -482,6 +346,7 @@ begin
   LZoneAccountKeys.Clear;
   FZoneAggregatedHashrate.Clear;
   LZoneBuffersBlockHash := 0;
+  LZoneAccountsOrderedByUpdatedBlock.Clear;
 
   if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
     // Check if header is valid:
@@ -504,11 +369,21 @@ begin
         Move(LHeader[28], LZoneAccountKeys.position, 4);
         Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
         LZoneBuffersBlockHash := LZone.position + 36;
+        Move(LHeader[40], LZoneAccountsOrderedByUpdatedBlock.position, 4);
+        //
         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;
+          //
+          if (LZoneAccountsOrderedByUpdatedBlock.position=0) then begin
+            if (Not FAbstractMem.ReadOnly) then begin
+              LZoneAccountsOrderedByUpdatedBlock := FAbstractMem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+              Move(LZoneAccountsOrderedByUpdatedBlock.position,LHeader[40],4);
+              FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+            end;
+          end;
         end;
       end;
     end;
@@ -530,6 +405,8 @@ begin
     LZoneAccountKeys := FAbstractMem.New( 100 );
     FZoneAggregatedHashrate := FAbstractMem.New(100); // Note: Enough big to store a BigNum
     LZoneBuffersBlockHash := LZone.position+36;
+    LZoneAccountsOrderedByUpdatedBlock := FAbstractMem.New(
+      TAbstractMemBTree.MinAbstractMemInitialPositionSize);
 
     Move(LZoneBlocks.position,       LHeader[16],4);
     Move(LZoneAccounts.position,     LHeader[20],4);
@@ -537,6 +414,7 @@ begin
     Move(LZoneAccountKeys.position,  LHeader[28],4);
     Move(FZoneAggregatedHashrate.position,LHeader[32],4);
     LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
+    Move(LZoneAccountsOrderedByUpdatedBlock, LHeader[40],4);
     Move(LHeaderVersion,             LHeader[96],4);
 
     FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
@@ -549,12 +427,12 @@ begin
   FBlocks.FPCAbstractMem := Self;
 
   FAccounts := TPCAbstractMemListAccounts.Create( FAbstractMem, LZoneAccounts, 100000, Self.UseCacheOnAbstractMemLists);
-  FAccounts.FPCAbstractMem := Self;
 
-  FAccountsNames := TPCAbstractMemListAccountNames.Create( FAbstractMem, LZoneAccountsNames, 5000 , False, Self.UseCacheOnAbstractMemLists);
+  FAccountsNames := TPCAbstractMemListAccountNames.Create( FAbstractMem, LZoneAccountsNames, False, 31, _TComparison_TAccountNameInfo);
   FAccountsNames.FPCAbstractMem := Self;
 
   FAccountKeys := TPCAbstractMemAccountKeys.Create( FAbstractMem, LZoneAccountKeys.position, Self.UseCacheOnAbstractMemLists);
+  FAccounts.AccountKeys := FAccountKeys;
 
   // Read AggregatedHashrate
   SetLength(LBuffer,100);
@@ -564,6 +442,11 @@ begin
   end;
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
 
+  if (LZoneAccountsOrderedByUpdatedBlock.position<>0) then begin
+    FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create(FAbstractMem,LZoneAccountsOrderedByUpdatedBlock,DoGetAccount);
+  end;
+  FAccounts.AccountsOrderedByUpdatedBlock := FAccountsOrderedByUpdatedBlock;
+
   FAccountCache.Clear;
 
   if (Not AIsNewStructure) And (Not FAbstractMem.ReadOnly) And (LHeaderVersion<CT_PCAbstractMem_HeaderVersion) then begin
@@ -676,6 +559,12 @@ begin
   End;
 end;
 
+function TPCAbstractMem.DoGetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := GetAccount(AAccountNumber);
+  Result := True;
+end;
+
 procedure TPCAbstractMem.FlushCache;
 var LBigNum : TBytes;
   Ltc : TTickCount;
@@ -684,7 +573,6 @@ begin
   Ltc := TPlatform.GetTickCount;
   FBlocks.FlushCache;
   FAccounts.FlushCache;
-  FAccountsNames.FlushCache;
   FAccountKeys.FlushCache;
   FBufferBlocksHash.Flush;
   LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
@@ -743,13 +631,13 @@ 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);
+    FAccounts.Item[ AAccount.account ] := AAccount;
   end;
   // Update cache
+  FAccountCache.Remove(AAccount);
   FAccountCache.Add(AAccount);
 end;
 
@@ -906,22 +794,6 @@ var LFirstTC, LTC : TTickCount;
 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;
 
@@ -1003,25 +875,25 @@ begin
   Result.Clear;
   Result.account := AAccountNumber;
   if Not FAccountCache.Find(Result,Result) then begin
-    Result := FAccounts.GetItem( AAccountNumber );
+    Result := FAccounts.Item[ 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;
+{ TPCAbstractMemListAccountNames }
 
-function TPCAbstractMemListAccountNames.IndexOf(const AName: String): Integer;
-var LFind : TAccountNameInfo;
+function TPCAbstractMemListAccountNames.LoadData(const APosition: TAbstractMemPosition): TAccountNameInfo;
+var LZone : TAMZone;
+  LBytes : TBytes;
 begin
-  LFind.accountName := AName;
-  LFind.accountNumber := 0;
-  if Not Find(LFind,Result) then Result := -1;
+  if Not FPCAbstractMem.AbstractMem.GetUsedZoneInfo( APosition, False, LZone) then
+    raise EAbstractMemTList.Create(Format('%s.LoadData Inconsistency error used zone info not found at pos %d',[Self.ClassName,APosition]));
+  SetLength(LBytes,LZone.size);
+  if FPCAbstractMem.AbstractMem.Read(LZone.position, LBytes[0], Length(LBytes) )<>Length(LBytes) then
+    raise EAbstractMemTList.Create(Format('%s.LoadData Inconsistency error cannot read %d bytes at pos %d',[Self.ClassName,LZone.size,APosition]));
+  LoadFrom(LBytes,Result);
 end;
 
 procedure TPCAbstractMemListAccountNames.LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
@@ -1032,14 +904,31 @@ begin
   Move(ABytes[LTmp.GetSerializedLength],AItem.accountNumber,4);
 end;
 
-procedure TPCAbstractMemListAccountNames.Remove(const AName: String);
-var i : Integer;
+function TPCAbstractMemListAccountNames.NodeDataToString(const AData: TAbstractMemPosition): String;
+var Lani : TAccountNameInfo;
+begin
+  Lani := LoadData(AData);
+  Result:= Format('AccountNameInfo: Account:%d Name(%d):%d',[Lani.accountNumber, Length(Lani.accountName), Lani.accountName]);
+end;
+
+function TPCAbstractMemListAccountNames.DeleteAccountName(const AName: String) : Boolean;
+var  Lani : TAccountNameInfo;
+begin
+  Lani.accountName := AName;
+  Lani.accountNumber := 0;
+  Result := DeleteData(Lani);
+end;
+
+function TPCAbstractMemListAccountNames.SaveData(const AData: TAccountNameInfo): TAMZone;
+var LBytes : TBytes;
 begin
-  i := IndexOf(AName);
-  if i>=0 then Delete(i);
+  SetLength(LBytes,0);
+  SaveTo(AData,LBytes);
+  Result := FPCAbstractMem.AbstractMem.New(Length(LBytes));
+  FPCAbstractMem.AbstractMem.Write(Result.position,LBytes[0],Length(LBytes));
 end;
 
-procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes);
+procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; var ABytes: TBytes);
 var LStream : TStream;
   LTmp : TBytes;
 begin
@@ -1055,36 +944,46 @@ begin
   End;
 end;
 
-procedure TPCAbstractMemListAccountNames.Add(const AName: String; AAccountNumber: Cardinal);
-var LItem : TAccountNameInfo;
-  i : Integer;
+procedure TPCAbstractMemListAccountNames.AddNameAndNumber(const AName: String; AAccountNumber: Cardinal);
+var Lani : TAccountNameInfo;
+  Lposition : TAbstractMemPosition;
 begin
-  LItem.accountName := AName;
-  LItem.accountNumber := AAccountNumber;
-  i := inherited Add(LItem);
-  if (i<0) then begin
-    i := IndexOf(AName);
-    if (i<0) then
+  Lani.accountName := AName;
+  Lani.accountNumber := AAccountNumber;
+  if Not AddData(Lani) then begin
+    if Not FindData(Lani,Lposition) 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]));
+      Lani.accountNumber,Lani.accountName]));
   end;
 end;
 
-function TPCAbstractMemListAccountNames.Compare(const ALeft, ARight: TAccountNameInfo): integer;
-Var LBytesLeft,LBytesRight : TBytes;
+function TPCAbstractMemListAccountNames.FindByName(const AName: String): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  Result := FindByName(AName,Lpos);
+end;
+
+function TPCAbstractMemListAccountNames.FindByName(const AName: String; out ANameInfo: TAccountNameInfo): Boolean;
+var Lpos : TAbstractMemPosition;
 begin
-  LBytesLeft.FromString(ALeft.accountName);
-  LBytesRight.FromString(ARight.accountName);
-  Result := TBaseType.BinStrComp(LBytesLeft,LBytesRight);
+  if FindByName(AName,Lpos) then begin
+    ANameInfo := LoadData(Lpos);
+    Result := True;
+  end else begin
+    if Lpos<>0 then begin
+      ANameInfo := LoadData(Lpos);
+    end;
+    Result := False;
+  end;
 end;
 
-function TPCAbstractMemListAccountNames.FindByName(const AName: String; out AIndex: Integer): Boolean;
-var LFind : TAccountNameInfo;
+function TPCAbstractMemListAccountNames.FindByName(const AName: String; out AAbstractMemPosition: TAbstractMemPosition): Boolean;
+var Lani : TAccountNameInfo;
 begin
-  LFind.accountName := AName;
-  LFind.accountNumber := 0;
-  Result := Find(LFind,AIndex);
+  Lani.accountName := AName;
+  Lani.accountNumber := 0;
+  Result := FindData(Lani,AAbstractMemPosition);
 end;
 
 { TPCAbstractMemListBlocks }
@@ -1166,15 +1065,18 @@ procedure TPCAbstractMemCheckThread.BCExecute;
     inc(FErrorsCount);
     TLog.NewLog(ltError,ClassName,'CheckConsistency: '+AError);
   end;
-var iBlock, i, iAccName : Integer;
+var iBlock, i : Integer;
   LAccount : TAccount;
   LBlockAccount : TBlockAccount;
+  LHighestOperationBlock : TOperationBlockExt;
   LOrdered : TOrderedList<Integer>;
   LOrderedNames : TOrderedList<String>;
-  LAccountNameInfo : TAccountNameInfo;
   LTC, LTCInitial : TTickCount;
   LAggregatedHashrate, LBlockHashRate : TBigNum;
+  LBuff1,LBuff2 : TRawBytes;
+  Laninfo : TAccountNameInfo;
 begin
+  LBlockAccount := CT_BlockAccount_NUL;
   iBlock :=0;
   LOrdered := TOrderedList<Integer>.Create(False,TComparison_Integer);
   LOrderedNames := TOrderedList<String>.Create(False,TComparison_String);
@@ -1182,6 +1084,7 @@ begin
   Try
     LTC := TPlatform.GetTickCount;
     LTCInitial := LTC;
+    LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
     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]) );
@@ -1192,6 +1095,7 @@ begin
         LOrdered.Clear;
         LOrderedNames.Clear;
         LAggregatedHashrate.Value := 0;
+        LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
       end;
 
       LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
@@ -1202,19 +1106,30 @@ 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
+          if Not FPCAbstractMem.AccountsNames.FindByName(LAccount.name.ToString,Laninfo) 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]));
+            if Laninfo.accountNumber<>LAccount.account then begin
+              _error(Format('Account %d name %s found at list but links to %d',[LAccount.account,LAccount.name.ToString,Laninfo.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;
+        if LAccount.GetLastUpdatedBlock>=FPCAbstractMem.BlocksCount then begin
+          _error(Format('Account Updated %d > %d - %s',[LAccount.GetLastUpdatedBlock,FPCAbstractMem.BlocksCount,TAccountComp.AccountToTxt(LAccount)]));
+        end;
+
+      end;
+      LBuff1 := TPCSafeBox.CalcBlockHash(LBlockAccount,LHighestOperationBlock.operationBlock.protocol_version);
+      If Not (LBuff1.IsEqualTo(LBlockAccount.block_hash)) then begin
+        _error(Format('Blockaccount hash for %d are not equals: calculated %s <> saved %s',[LBlockAccount.blockchainInfo.block,LBuff1.ToHexaString,LBlockAccount.block_hash.ToHexaString]));
+      end;
+      LBuff2 := FPCAbstractMem.FBufferBlocksHash.Capture((iBlock*32),32);
+      if Not LBuff1.IsEqualTo(LBuff2) then begin
+        _error(Format('Blockaccount hash for %d are not equals: %s <> %s',[LBlockAccount.blockchainInfo.block,LBuff1.ToHexaString,LBuff2.ToHexaString]));
       end;
 
       LBlockHashRate := TBigNum.TargetToHashRate( LBlockAccount.blockchainInfo.compact_target );
@@ -1231,12 +1146,21 @@ begin
       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;
+    FPCAbstractMem.FBufferBlocksHash.SafeBoxHashCalcType := sbh_Single_Sha256;
+    FPCAbstractMem.FBufferBlocksHash.SafeBoxHashCalcType := sbh_Merkle_Root_Hash;
+    LBuff1 := FPCAbstractMem.FBufferBlocksHash.GetSafeBoxHash;
+    FErrors.Add(Format('Last Block %d - SBH %s - Next SBH: %s',[LBlockAccount.blockchainInfo.block,LBlockAccount.blockchainInfo.initial_safe_box_hash.ToHexaString,LBuff1.ToHexaString]));
+    //
+    i := 0;
+    if FPCAbstractMem.AccountsNames.FindDataLowest(Laninfo) then begin
+      repeat
+        inc(i);
+        if LOrdered.IndexOf(Laninfo.accountNumber ) < 0 then begin
+          _error(Format('Account name %s at index %d/%d not found in search',[Laninfo.accountName, i,FPCAbstractMem.AccountsNames.Count]));
+        end;
+      until Not FPCAbstractMem.AccountsNames.FindDataSuccessor(Laninfo,Laninfo);
     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;

+ 25 - 48
src/core/UPCAbstractMemAccountKeys.pas

@@ -10,7 +10,7 @@ uses Classes, SysUtils,
   SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList,
   UAbstractBTree, UAbstractAVLTree,
-  UPCDataTypes, UBaseTypes, UAVLCache,
+  UPCDataTypes, UBaseTypes, UAVLCache, UAbstractMemBTree,
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 type
@@ -31,16 +31,16 @@ type
 
   { TAccountsUsingThisKey }
 
-  TAccountsUsingThisKey = Class(TAbstractMemOrderedTList<Cardinal>)
+  TAccountsUsingThisKey = Class(TAbstractMemBTree)
+    // AbstractMem position will be considered a Account Number
   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;
+    procedure DisposeData(var AData : TAbstractMemPosition); override;
+    function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+  public
+    function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
   public
     Constructor Create(AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; AUseCache : Boolean); reintroduce;
-    Function Add(const AItem : Cardinal) : Integer; reintroduce;
-    procedure Delete(index : Integer); reintroduce;
+    function Get(Index : Integer) : Cardinal;
   End;
 
   TAccountKeyByPosition = record
@@ -269,8 +269,10 @@ begin
   try
   Lautk := GetAccountsUsingThisKey(AAccountKey);
   if Assigned(Lautk) then begin
-    for i:=0 to Lautk.Count-1 do begin
-      AList.Add( Lautk.GetItem(i) );
+    if Lautk.FindLowest(i) then begin
+      repeat
+        AList.Add(i);
+      until Not Lautk.FindSuccessor(i,i);
     end;
   end;
   finally
@@ -414,7 +416,6 @@ function TPCAbstractMemAccountKeys.GetPositionOfKeyAndRemoveAccount(
   const AAccountNumber: Cardinal): TAbstractMemPosition;
 var LNode : TAbstractMemAccountKeyNode;
   LZone : TAMZone;
-  i : Integer;
   Lacckutk : TAccountsUsingThisKey;
   LAccKeyByPos : TAccountKeyByPosition;
 begin
@@ -445,10 +446,7 @@ begin
   end;
 
   if Assigned(LAccKeyByPos.accountsUsingThisKey) then begin
-    i := LAccKeyByPos.accountsUsingThisKey.IndexOf( AAccountNumber );
-    if i>=0 then begin
-      LAccKeyByPos.accountsUsingThisKey.Delete( i );
-    end;
+    LAccKeyByPos.accountsUsingThisKey.Delete( AAccountNumber );
   end;
   finally
     FAccountKeysLock.Release;
@@ -524,53 +522,32 @@ 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);
+constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone; AUseCache : Boolean);
 begin
-  SetLength(ABytes,4);
-  Move(AItem,ABytes[0],4);
-  raise Exception.Create('INCONSISTENT 20200324-1 NEVER CALL HERE');
+  inherited Create(AAbstractMem,AInitialZone,False, 7);
 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;
+procedure TAccountsUsingThisKey.DisposeData(var AData: TAbstractMemPosition);
 begin
-  Result := ALeft - ARight;
+  // NOTE: Nothing to do NEITHER to inherit from ancestor
 end;
 
-constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone; AUseCache : Boolean);
+function TAccountsUsingThisKey.DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer;
 begin
-  inherited Create(AAbstractMem,AInitialZone,1000,False, AUseCache);
+  Result := ALeftData - ARightData;
 end;
 
-procedure TAccountsUsingThisKey.Delete(index: Integer);
+function TAccountsUsingThisKey.Get(Index: Integer): Cardinal;
+var i : Integer;
 begin
-  FList.Delete( index );
+  if Not FindIndex(Index,i) then raise Exception.Create(Format('Accounts using this key index %d not found',[Index]));
+  Result := i;
 end;
 
-function TAccountsUsingThisKey.GetItem(index: Integer): Cardinal;
+function TAccountsUsingThisKey.NodeDataToString(const AData: TAbstractMemPosition): String;
 begin
-  Result := FList.Position[index];
+  Result := IntToStr(AData);
 end;
 
 { TPCAccountKeyByPositionCache }

+ 200 - 0
src/core/UPCAbstractMemAccounts.pas

@@ -0,0 +1,200 @@
+unit UPCAbstractMemAccounts;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+uses Classes, SysUtils, SyncObjs,
+  UPCAbstractMemAccountKeys, UPCAccountsOrdenations,
+  UAbstractMem,
+  UAbstractMemTList,
+  UPCDataTypes,
+  UBaseTypes,
+  UConst,
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+  { TPCAbstractMemListAccounts }
+
+  TPCAbstractMemListAccounts = class(TAbstractMemTList<TAccount>)
+  private
+    FAccountKeys: TPCAbstractMemAccountKeys;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+  protected
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccount); override;
+    procedure SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+  public
+    class procedure LoadAccountFromTBytes(const ABytes: TBytes; const AAccountKeys : TPCAbstractMemAccountKeys; var AItem: TAccount);
+    property AccountKeys: TPCAbstractMemAccountKeys read FAccountKeys write FAccountKeys;
+    property AccountsOrderedByUpdatedBlock: TAccountsOrderedByUpdatedBlock read FAccountsOrderedByUpdatedBlock write FAccountsOrderedByUpdatedBlock;
+  end;
+
+  EAbsctractMemAccounts = Class(Exception);
+
+implementation
+
+uses UAccounts;
+
+{ TPCAbstractMemListAccounts }
+
+class procedure TPCAbstractMemListAccounts.LoadAccountFromTBytes(
+  const ABytes: TBytes; const AAccountKeys: TPCAbstractMemAccountKeys;
+  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 EAbsctractMemAccounts.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);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.accountKey := AAccountKeys.GetKeyAtPosition( LPointer );
+          if w<>AItem.accountInfo.accountKey.EC_OpenSSL_NID then raise EAbsctractMemAccounts.Create('INCONSISTENT 20200318-2');
+        end;
+      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);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.accountKey := AAccountKeys.GetKeyAtPosition( LPointer );
+        end;
+
+        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);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.new_publicKey := AAccountKeys.GetKeyAtPosition( LPointer );
+        end;
+        if (w<>CT_AccountInfo_ForSale) then begin
+          AItem.accountInfo.hashed_secret.FromSerialized(LStream);
+        end;
+
+      End;
+      else raise EAbsctractMemAccounts.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 EAbsctractMemAccounts.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;
+end;
+
+procedure TPCAbstractMemListAccounts.LoadFrom(const ABytes: TBytes; var AItem: TAccount);
+begin
+  LoadAccountFromTBytes(ABytes,FAccountKeys,AItem);
+end;
+
+procedure TPCAbstractMemListAccounts.SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes);
+var LStream : TStream;
+  LPointer : TAbstractMemPosition;
+  w : Word;
+  LPrevious : TAccount;
+begin
+  if (Length(ABytes)>0) and (Not AIsAddingItem) then begin
+    // Capture previous values
+    LoadFrom(ABytes,LPrevious);
+    if (LPrevious.account<>AItem.account) then raise EAbsctractMemAccounts.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
+      FAccountKeys.GetPositionOfKeyAndRemoveAccount( LPrevious.accountInfo.accountKey, LPrevious.account );
+    end;
+
+    if LPrevious.updated_on_block_active_mode<>AItem.updated_on_block_active_mode then begin
+      FAccountsOrderedByUpdatedBlock.Update(AItem.account,LPrevious.updated_on_block_active_mode,AItem.updated_on_block_active_mode);
+    end;
+
+  end else begin
+    FAccountsOrderedByUpdatedBlock.Update(AItem.account,LPrevious.updated_on_block_active_mode,AItem.updated_on_block_active_mode);
+  end;
+
+  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 := 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 := 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 := 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 );
+
+    LStream.Write( AItem.account_type ,2);
+    AItem.account_data.ToSerialized( LStream );
+    AItem.account_seal.ToSerialized( LStream );
+    //
+    ABytes.FromStream( LStream );
+
+  finally
+    LStream.Free;
+  end;
+end;
+
+
+end.

+ 165 - 0
src/core/UPCAccountsOrdenations.pas

@@ -0,0 +1,165 @@
+unit UPCAccountsOrdenations;
+
+{ Copyright (c) 2016-2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+{$I ./../config.inc}
+
+uses Classes, SysUtils,
+  UAbstractMem,
+  UAbstractMemBTree,
+  UAbstractBTree,
+  UPCDataTypes, UBaseTypes,
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+
+  TCallReturnAccount = function(AAccountNumber : Integer; var AAccount : TAccount) : Boolean of object;
+
+  TAccountsOrderedByUpdatedBlock = Class
+  private
+    type
+      TAccounstByUpdatedBlockBTree = Class({$IFDEF USE_ABSTRACTMEM}TAbstractMemBTree{$ELSE}TMemoryBTree<Integer>{$ENDIF})
+      protected
+        FCallReturnAccount : TCallReturnAccount;
+        FSearching_AccountNumber : Integer;
+        FSearching_UpdatedBlock : Integer;
+        function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+      public
+        function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+      End;
+  private
+    var
+    FBTree : TAccounstByUpdatedBlockBTree;
+    {$IFDEF USE_ABSTRACTMEM}
+    FAbstractMem : TAbstractMem;
+    {$ENDIF}
+  public
+    constructor Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; {$ENDIF}ACallReturnAccount : TCallReturnAccount);
+    destructor Destroy; override;
+    function First(var AAccountNumber : Integer) : Boolean;
+    function Next(var AAccountNumber : Integer) : Boolean;
+    function Count : Integer;
+    function Update(const AAccountNumber, AOldUpdatedBlock, ANewUpdatedBlock : Integer) : Boolean;
+  End;
+
+implementation
+
+Uses UPCAbstractMemAccounts;
+
+{ TAccountsOrderedByUpdatedBlock }
+
+function TAccountsOrderedByUpdatedBlock.Count: Integer;
+begin
+  Result := FBTree.Count;
+end;
+
+constructor TAccountsOrderedByUpdatedBlock.Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; {$ENDIF}ACallReturnAccount : TCallReturnAccount);
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  FAbstractMem := AAbstractMem;
+  FBTree := TAccounstByUpdatedBlockBTree.Create(FAbstractMem,AInitialZone,False,31);
+  {$ELSE}
+  FBTree := TAccounstByUpdatedBlockBTree.Create(Nil,False,31);
+  {$ENDIF}
+  FBTree.FSearching_AccountNumber := -1;
+  FBTree.FSearching_UpdatedBlock := 0;
+  FBTree.FCallReturnAccount := ACallReturnAccount;
+end;
+
+destructor TAccountsOrderedByUpdatedBlock.Destroy;
+begin
+  FBTree.Free;
+  inherited;
+end;
+
+function TAccountsOrderedByUpdatedBlock.First(var AAccountNumber : Integer) : Boolean;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := -1;
+    Result := FBTree.FindLowest(AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+end;
+
+function TAccountsOrderedByUpdatedBlock.Next(var AAccountNumber : Integer): Boolean;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := -1;
+    Result := FBTree.FindSuccessor(AAccountNumber,AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+end;
+
+function TAccountsOrderedByUpdatedBlock.Update(const AAccountNumber, AOldUpdatedBlock, ANewUpdatedBlock: Integer): Boolean;
+var
+  Lampos : TAbstractMemPosition;
+  Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LAccount : TAccount;
+  LPosition : TAbstractMemPosition;
+  LiPos : Integer;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := AAccountNumber;
+    FBTree.FSearching_UpdatedBlock := AOldUpdatedBlock;
+    FBTree.Delete(AAccountNumber);
+    FBTree.FSearching_UpdatedBlock := ANewUpdatedBlock;
+    FBTree.Add(AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+  Result := True;
+end;
+
+{ TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree }
+
+function TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree.DoCompareData(
+  const ALeftData, ARightData: TAbstractMemPosition): Integer;
+var LLeftAccount, LRightAccount : TAccount;
+begin
+  if (ALeftData = ARightData) then Exit(0);
+
+  FCallReturnAccount(ARightData,LRightAccount);
+  if ((FSearching_AccountNumber>=0) And (ALeftData=FSearching_AccountNumber)) then begin
+    Result := FSearching_UpdatedBlock - LRightAccount.updated_on_block_active_mode;
+  end else begin
+    FCallReturnAccount(ALeftData,LLeftAccount);
+    Result := LLeftAccount.updated_on_block_active_mode - LRightAccount.updated_on_block_active_mode;
+  end;
+  if Result=0 then Result := ALeftData - ARightData;
+end;
+
+function TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree.NodeDataToString(
+  const AData: TAbstractMemPosition): String;
+var LAccount : TAccount;
+begin
+  if FCallReturnAccount(AData,LAccount) then begin
+    Result := Format('(Acc:%d Upd:%d)',[LAccount.account,LAccount.updated_on_block_active_mode]);
+  end else Result := Format('(Pos:%d not found)',[AData]);
+end;
+
+end.

+ 76 - 0
src/core/UPCTemporalAbstractMem.pas

@@ -0,0 +1,76 @@
+unit UPCTemporalAbstractMem;
+
+{ Copyright (c) 2016-2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+uses
+  Classes, {$IFnDEF FPC}Windows,{$ENDIF} SysUtils,
+  UPCTemporalFileStream,
+  UAbstractMem, UFileMem;
+
+Type
+  { TPCTemporalAbstractMem }
+
+  TPCTemporalAbstractMem = Class({$IFDEF USE_ABSTRACTMEM}TFileMem{$ELSE}TMem{$ENDIF})
+  private
+    {$IFDEF USE_ABSTRACTMEM}
+    FTemporalFileName : String;
+    {$ENDIF}
+  protected
+  public
+    Constructor Create; reintroduce;
+    Destructor Destroy; override;
+  End;
+
+implementation
+
+Uses {$IFDEF HIGHLOG}ULog, {$ENDIF} UNode;
+
+{ TPCTemporalFileStream }
+
+constructor TPCTemporalAbstractMem.Create;
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  FTemporalFileName := TPCTemporalFileStream.GetTemporalFileName('ABSTRACTMEM');
+  {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Creating a new Temporal AbstractMem file: %s',[FTemporalFileName]));{$ENDIF}
+  inherited Create(FTemporalFileName,False);
+  {$ELSE}
+  inherited Create(0,False);
+  {$ENDIF}
+end;
+
+destructor TPCTemporalAbstractMem.Destroy;
+{$IFDEF HIGHLOG}var LSize : Integer;{$ENDIF}
+begin
+  {$IFDEF HIGHLOG}LSize := Size;{$ENDIF}
+  inherited Destroy;
+  {$IFDEF USE_ABSTRACTMEM}
+  if FTemporalFileName<>'' then begin
+    {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Deleting a Temporal AbstractMem file (%d bytes): %s',[LSize, FTemporalFileName]));{$ENDIF}
+    DeleteFile(FTemporalFileName);
+  end;
+  {$ENDIF}
+end;
+
+end.