Ver Fonte

Merge upstream

Herman Schoenfeld há 4 anos atrás
pai
commit
fd8fbd5a84

+ 8 - 1
CHANGELOG.md

@@ -1,8 +1,10 @@
 # Changelog
 
 ## Build 5.4 - (PENDING RELEASE)
+- CURRENT 5.4.Beta2
 - Added usage of AbstractMem library to allow build a PascalCoin version using virtual memory and efficient caching mechanism
-  - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file
+  - Use AbstractMem library v1.2
+  - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file (Enabled by default)
 - Changes to `pascalcoin_daemon.ini` file:
   - Added "DATAFOLDER" configuration option at pascalcoin_daemon.ini file (daemon/service) in order to allow customize data folder
   - Added "ABSTRACTMEM_MAX_CACHE_MB" to customize Maximum megabytes in memory as a cache
@@ -15,6 +17,11 @@
     - "start","end","max" : Based on block number and max returns values (max by default=100)
     - "enc_pubkey" or "b58_pubkey" : If provided will return blocks where pubkey equal to provided
     - "payload", "payloadsearchtype" : Same workaround than "name" and "namesearchtype" on "findaccounts" method  
+  - New method "save-safebox-stream" : Will save a Safebox file in Stream format
+    - "filename" : String (optional)
+  - New method "save-safebox-abstractmem" : Will save a Safebox AbstractMem file of actual state
+    - "filename" : String (optional)
+  - New method "abstractmem-stats" : Testing purposes only
 - Fixed bugs:
   - Fixed bugs on "pascalcoin_daemon" (daemon on Linux / Service on Windows) that produced crash on windows and some invalid finalization on Linux
   - Fixed minor bugs

+ 2 - 2
src/config.inc

@@ -68,11 +68,11 @@
   {$DEFINE USE_ABSTRACTMEM}
 
   // Activate GNUGETTEXT library
-  {$DEFINE USE_GNUGETTEXT}
+  {.$DEFINE USE_GNUGETTEXT}
   
   // Activate usage of TPCTemporalFileStream instead of TBytes in order to minimize mem usage
   // This also fixes issue #207 High memory usage on FreePascal compiler
-  {$DEFINE USE_BIGBLOCKS_MEM_ON_DISK}
+  {.$DEFINE USE_BIGBLOCKS_MEM_ON_DISK}
 
 { ********************************************************************
   Don't touch more code, it will addapt based on your preferences

+ 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)));

+ 4 - 4
src/core/UConst.pas

@@ -130,7 +130,7 @@ Const
   CT_Protocol_Upgrade_v6_MinBlock = {$IFDEF PRODUCTION}999999999{$ELSE}999999999{$ENDIF}; // TODO: ALLOW V6 activate setting a valid "min block" value
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000004{$ENDIF};
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000005{$ENDIF};
 
   CT_NetProtocol_Version: Word = 10;
   // IMPORTANT NOTE!!!
@@ -141,9 +141,9 @@ Const
 
   CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
 
-  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.4'{$ENDIF}; //
+  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET'{$ENDIF}; //
 
-  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET_5.Beta.4'{$ENDIF}; //
+  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET'{$ENDIF}; //
 
   CT_PseudoOp_Reward = $0;
   // Value of Operations type in Protocol 1
@@ -198,7 +198,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4.Beta'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4.beta'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4.Beta2'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4.Beta2'{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};

+ 158 - 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
@@ -645,6 +528,7 @@ begin
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
   FreeAndNil(FAggregatedHashrate);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
   if (FFileName<>'') And (FAbstractMem is TMem) And (Not FAbstractMem.ReadOnly) then begin
     LFile := TFileStream.Create(FFileName,fmCreate);
     try
@@ -676,6 +560,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 +574,6 @@ begin
   Ltc := TPlatform.GetTickCount;
   FBlocks.FlushCache;
   FAccounts.FlushCache;
-  FAccountsNames.FlushCache;
   FAccountKeys.FlushCache;
   FBufferBlocksHash.Flush;
   LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
@@ -743,13 +632,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 +795,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 +876,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 +905,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 +945,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 +1066,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 +1085,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 +1096,7 @@ begin
         LOrdered.Clear;
         LOrderedNames.Clear;
         LAggregatedHashrate.Value := 0;
+        LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
       end;
 
       LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
@@ -1202,19 +1107,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 +1147,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.

+ 180 - 0
src/core/UPCRPCFileUtils.pas

@@ -0,0 +1,180 @@
+unit UPCRPCFileUtils;
+
+{ Copyright (c) 2020 by PascalCoin developers, orignal code 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, SysUtils,
+  UJSONFunctions, URPC, UCrypto, ULog,
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem, UPCAbstractMemAccountKeys,
+  {$ENDIF}
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UBlockChain, UPCOrderedLists;
+
+
+Type
+
+  { TRPCFileUtils }
+
+  TRPCFileUtils = Class
+  private
+  public
+    class function SaveAsSafeboxStream(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function GenerateNewAbstractMemSafebox(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function AbstractMemStats(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+
+  End;
+
+implementation
+
+uses UPCDataTypes, UFileStorage, UNode;
+
+{ TRPCFileUtils }
+
+class function TRPCFileUtils.GenerateNewAbstractMemSafebox(
+  const ASender: TRPCProcess; const AMethodName: String; AInputParams,
+  AJSONResponse: TPCJSONObject; var AErrorNum: Integer;
+  var AErrorDesc: String): Boolean;
+{$IFDEF USE_ABSTRACTMEM}
+var
+  LFileName : String;
+{$ENDIF}
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+{$IFDEF USE_ABSTRACTMEM}
+  LFileName := AInputParams.AsString('filename', '').Trim;
+  if (LFileName='') then begin
+    LFileName := TFileStorage.GetSafeboxCheckpointingFileName(TFileStorage(TNode.Node.Bank.Storage).DatabaseFolder,TNode.Node.Bank.BlocksCount);
+  end;
+  TNode.Node.Bank.SafeBox.SaveCheckpointing(LFileName);
+  AJSONResponse.GetAsObject('result').GetAsVariant('filename').Value := LFileName;
+  AErrorNum := 0;
+  AErrorDesc := '';
+  Result := True;
+{$ELSE}
+  AErrorNum := CT_RPC_ErrNum_NotImplemented;
+  AErrorDesc := 'AbstractMem library is not available in this build';
+  Result := False;
+{$ENDIF}
+end;
+
+class function TRPCFileUtils.AbstractMemStats(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LStrings, LReport : TStrings;
+   LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount : Integer;
+   i, nMax : Integer;
+   Lobj : TPCJSONObject;
+   Larray : TPCJSONArray;
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+{$IFDEF USE_ABSTRACTMEM}
+  LStrings := TStringList.Create;
+  Try
+    if AInputParams.GetAsVariant('report').AsBoolean(False) then LReport := LStrings
+    else LReport := Nil;
+    Lobj := AJSONResponse.GetAsObject('result').GetAsObject('abstractmem');
+    if TNode.Node.Bank.SafeBox.PCAbstractMem.AbstractMem.CheckConsistency(LReport, LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount) then begin
+      Lobj.GetAsVariant('checkconsistency').Value := True;
+    end else begin
+      Lobj.GetAsVariant('checkconsistency').Value := False;
+    end;
+    Lobj.GetAsVariant('total_used_size').Value := LTotalUsedSize;
+    Lobj.GetAsVariant('total_used_blocks_count').Value := LTotalUsedBlocksCount;
+    Lobj.GetAsVariant('total_leaks_size').Value := LTotalLeaksSize;
+    Lobj.GetAsVariant('total_leaks_blocks_count').Value := LTotalLeaksBlocksCount;
+
+    if Assigned(LReport) then begin
+      Larray := Lobj.GetAsArray('report');
+      i := AInputParams.GetAsVariant('report_start').AsInteger(0);
+      nMax := AInputParams.GetAsVariant('report_max').AsInteger(100);
+      while (nMax>0) and (i>=0) and (i<LStrings.Count-1) do begin
+        Larray.GetAsVariant(Larray.Count).Value := LStrings[i];
+        inc(i);
+        dec(nMax);
+      end;
+    end;
+    Result := True;
+  Finally
+    LStrings.Free;
+  end;
+{$ELSE}
+  AErrorNum := CT_RPC_ErrNum_NotImplemented;
+  AErrorDesc := 'AbstractMem library is not available in this build';
+  Result := False;
+{$ENDIF}
+  //
+end;
+
+class function TRPCFileUtils.SaveAsSafeboxStream(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+var LFileName : String;
+  LFs : TFileStream;
+  LStart,LEnd : Integer;
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+
+  LFileName := AInputParams.AsString('filename', '').Trim;
+  if (LFileName='') then begin
+    LFileName := TFileStorage.GetSafeboxCheckpointingFileName(TFileStorage(TNode.Node.Bank.Storage).DatabaseFolder,TNode.Node.Bank.BlocksCount);
+    LFileName := ChangeFileExt(LFileName,'.safebox');
+  end;
+  LFs := TFileStream.Create(LFileName,fmCreate);
+  try
+    LFs.Size := 0;
+    LFs.Position := 0;
+    LStart := 0;
+    LEnd := TNode.Node.Bank.BlocksCount-1;
+    TNode.Node.Bank.SafeBox.SaveSafeBoxToAStream(LFs,LStart,LEnd);
+  finally
+    LFs.Free;
+  end;
+  AJSONResponse.GetAsObject('result').GetAsVariant('filename').Value := LFileName;
+  AJSONResponse.GetAsObject('result').GetAsVariant('start').Value := LStart;
+  AJSONResponse.GetAsObject('result').GetAsVariant('end').Value := LEnd;
+  AErrorNum := 0;
+  AErrorDesc := '';
+  Result := True;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('save-safebox-stream',TRPCFileUtils.SaveAsSafeboxStream);
+  TRPCProcess.RegisterProcessMethod('save-safebox-abstractmem',TRPCFileUtils.GenerateNewAbstractMemSafebox);
+  TRPCProcess.RegisterProcessMethod('abstractmem-stats',TRPCFileUtils.AbstractMemStats);
+finalization
+  TRPCProcess.UnregisterProcessMethod('save-safebox-stream');
+  TRPCProcess.UnregisterProcessMethod('save-safebox-abstractmem');
+  TRPCProcess.UnregisterProcessMethod('abstractmem-stats');
+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.

+ 2 - 1
src/core/URPC.pas

@@ -155,7 +155,7 @@ implementation
 
 Uses  {$IFNDEF FPC}windows,{$ENDIF}
   SysUtils, Synautil,
-  UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks;
+  UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
 
 Type
   TRegisteredRPCProcessMethod = Record
@@ -3547,6 +3547,7 @@ begin
     GetResultObject.GetAsObject('netstats').GetAsVariant('tservers').Value:=TNetData.NetData.NetStatistics.TotalServersConnections;
     GetResultObject.GetAsObject('netstats').GetAsVariant('breceived').Value:=TNetData.NetData.NetStatistics.BytesReceived;
     GetResultObject.GetAsObject('netstats').GetAsVariant('bsend').Value:=TNetData.NetData.NetStatistics.BytesSend;
+    GetResultObject.GetAsObject('netstats').GetAsVariant('ips').Value:=TNetData.NetData.IpInfos.Count;
     {$IFDEF Use_OpenSSL}
     GetResultObject.GetAsVariant('openssl').Value := IntToHex(OpenSSLVersion,8);
     {$ENDIF}

+ 2 - 0
src/gui-classic/UFRMWallet.pas

@@ -1099,7 +1099,9 @@ begin
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Connect/Disconnect';
   mi.OnClick:=Test_ConnectDisconnect;
+  {$IFnDEF FPC}
   mi.ShortCut := TextToShortCut('CTRL+D');
+  {$ENDIF}
   miAbout.Add(mi);
 
   mi := TMenuItem.Create(MainMenu);

+ 2 - 1
src/libraries/abstractmem/ConfigAbstractMem.inc

@@ -49,7 +49,8 @@
   - Fixed bug on CacheMem when replacing initial position of buffer
 
   Version 1.2 - Jan 2021
-  - Added TAbstractBTree - Standard B-Tree implementation for use on AbstractMem Library
+  - Added TAbstractBTree - Standard B-Tree implementation for use on AbstractMem Library - Multithread protected
+  - Added TAbstractMemBTreeData<TData> that implements a generic <TData> implementation for TAbstractBTree on TAbstractMem
   - Added ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION compiler directive to prevent circular structures on Tree nodes
 
 }

+ 398 - 125
src/libraries/abstractmem/UAbstractBTree.pas

@@ -34,6 +34,7 @@ interface
 
 uses
   Classes, SysUtils,
+  SyncObjs,
   // NOTE ABOUT FREEPASCAL (2020-03-10)
   // Current version 3.0.4 does not contain valid support for Generics, using Generics from this:
   // https://github.com/PascalCoinDev/PascalCoin/tree/master/src/libraries/generics.collections
@@ -74,16 +75,21 @@ type
     FAllowDuplicates: Boolean;
     FOrder: Integer;
     FCircularProtection : Boolean;
-    procedure SplitAfterInsert(var ANode : TAbstractBTreeNode);
+    procedure SplitAfterInsert(var ANode : TAbstractBTreeNode; const ACircularProtectionList : TOrderedList<TIdentify>);
     procedure MoveRange(var ASourceNode, ADestNode : TAbstractBTreeNode; AFromSource, ACount, AToDest : Integer);
     procedure MoveRangeBetweenSiblings(var ASourceNode, ADestNode : TAbstractBTreeNode);
     procedure BTreeNodeToString(const ANode : TAbstractBTreeNode; ALevel, ALevelIndex : Integer; const AStrings : TStrings);
     procedure CheckConsistencyEx(const ANode: TAbstractBTreeNode; AIsGoingDown : Boolean; AParentDataIndexLeft,AParentDataIndexRight : Integer; ADatas: TList<TData>; AIdents: TOrderedList<TIdentify>; ACurrentLevel : Integer; var ALevels, ANodesCount, AItemsCount : Integer);
-    function FindPrecessorExt(var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
-    function FindSuccessorExt(var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
+    function FindPrecessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
+    function FindSuccessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
     procedure EraseTreeExt(var ANode : TAbstractBTreeNode);
+    function FindExt(const AData: TData; const ACircularProtectionList : TOrderedList<TIdentify>; out ANode : TAbstractBTreeNode; out iPos : Integer): Boolean;
+    function FindLowestNodeExt(const ACircularProtectionList : TOrderedList<TIdentify>): TAbstractBTreeNode;
+    function FindHighestNodeExt(const ACircularProtectionList : TOrderedList<TIdentify>): TAbstractBTreeNode;
   protected
     FCount: integer;
+    FAbstractBTreeLock : TCriticalSection;
+    FIsFindingProcess : Boolean;
     function GetRoot: TAbstractBTreeNode; virtual; abstract;
     procedure SetRoot(var Value: TAbstractBTreeNode); virtual; abstract;
 
@@ -117,10 +123,13 @@ type
     function FindLowest(out ALowest : TData) : Boolean;
     function FindHighestNode: TAbstractBTreeNode;
     function FindHighest(out AHighest : TData) : Boolean;
+    function FindIndex(AIndex : Integer; out AData : TData) : Boolean;
+    function FillList(AStartIndex, ACount : Integer; const AList : TList<TData>) : Integer;
     function Add(const AData: TData) : Boolean;
     function Delete(const AData: TData) : Boolean;
     function NodeDataToString(const AData : TData) : String; virtual;
     constructor Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
+    destructor Destroy; override;
     property OnCompareIdentifyMethod: TComparison<TIdentify> read FOnCompareIdentify;
     property OnCompareDataMethod: TComparison<TData> read FOnCompareData;
     function BTreeToString : String;
@@ -132,6 +141,8 @@ type
     procedure CheckConsistency; virtual;
     property Height : Integer read GetHeight;
     property CircularProtection : Boolean read FCircularProtection write FCircularProtection;
+    procedure Lock;
+    procedure Unlock;
   End;
 
   TMemoryBTree<TData> = Class( TAbstractBTree<Integer,TData> )
@@ -165,14 +176,6 @@ type
     destructor Destroy; override;
   End;
 
-  TIntegerBTree = Class( TMemoryBTree<Integer> )
-  private
-  protected
-  public
-    constructor Create(AAllowDuplicates : Boolean; AOrder : Integer);
-    function NodeDataToString(const AData : Integer) : String; override;
-  End;
-
 implementation
 
 { TAbstractBTree<TIdentify, TData> }
@@ -180,32 +183,47 @@ implementation
 function TAbstractBTree<TIdentify, TData>.Add(const AData: TData): Boolean;
 var Lnode  : TAbstractBTreeNode;
   iDataPos : Integer;
+  LCircularProtectionList : TOrderedList<TIdentify>;
 begin
-  if (Find(AData,Lnode,iDataPos)) then begin
-    if (Not FAllowDuplicates) then Exit(False);
-    // Follow childs until leaf node
-    while (Not Lnode.IsLeaf) do begin
-      Lnode := GetNode(Lnode.childs[iDataPos]); // Insert at right position
-      if (BinarySearch(AData,Lnode.data,iDataPos)) then begin
-        //
+  FAbstractBTreeLock.Acquire;
+  Try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      if (FindExt(AData,LCircularProtectionList,Lnode,iDataPos)) then begin
+        if (Not FAllowDuplicates) then Exit(False);
+        // Follow childs until leaf node
+        while (Not Lnode.IsLeaf) do begin
+          Lnode := GetNode(Lnode.childs[iDataPos]); // Insert at right position
+          if (BinarySearch(AData,Lnode.data,iDataPos)) then begin
+            //
+          end;
+        end;
+      end else if (IsNil(Lnode.identify)) then begin
+        Lnode := NewNode;
+        SetRoot(Lnode);
       end;
-    end;
-  end else if (IsNil(Lnode.identify)) then begin
-    Lnode := NewNode;
-    SetRoot(Lnode);
-  end;
-  Assert(Lnode.IsLeaf,'Node must be a leaf');
-  // Lnode is a leaf and iDataPos is position to insert
-  Lnode.InsertData(Adata,iDataPos);
-  SaveNode(Lnode);
-  if Lnode.Count>MaxItemsPerNode then begin
-    // Split and up
-    SplitAfterInsert(Lnode);
-  end;
-  Result := True;
-  if (FCount>=0) then begin
-    SetCount(FCount+1);
-  end;
+      Assert(Lnode.IsLeaf,'Node must be a leaf');
+      // Lnode is a leaf and iDataPos is position to insert
+      Lnode.InsertData(Adata,iDataPos);
+      SaveNode(Lnode);
+      if Lnode.Count>MaxItemsPerNode then begin
+        // Split and up
+        if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+        SplitAfterInsert(Lnode,LCircularProtectionList);
+      end;
+      Result := True;
+      if (FCount>=0) then begin
+        SetCount(FCount+1);
+      end;
+    Finally
+      if Assigned(LCircularProtectionList) then
+        LCircularProtectionList.Free;
+    End;
+  Finally
+    FAbstractBTreeLock.Release;
+  End;
 end;
 
 function TAbstractBTree<TIdentify, TData>.AreEquals(const AIdentify1, AIdentify2: TIdentify): Boolean;
@@ -257,11 +275,13 @@ var Lsl : TStrings;
   Lnode : TAbstractBTreeNode;
 begin
   Lsl := TStringList.Create;
+  FAbstractBTreeLock.Acquire;
   try
     Lnode := GetRoot;
     if Not IsNil(Lnode.identify) then BTreeNodeToString(Lnode,0,0,Lsl);
     Result := Lsl.Text;
   finally
+    FAbstractBTreeLock.Release;
     Lsl.Free;
   end;
 end;
@@ -275,6 +295,7 @@ var
 begin
   FIdents := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
   FDatas := TList<TData>.Create;
+  FAbstractBTreeLock.Acquire;
   try
     Llevels := 0;
     LnodesCount := 0;
@@ -288,6 +309,7 @@ begin
     end;
     CheckConsistencyFinalized(FDatas,FIdents,Llevels,LnodesCount,LItemsCount);
   finally
+    FAbstractBTreeLock.Release;
     FDatas.Free;
     FIdents.Free;
   end;
@@ -377,12 +399,14 @@ end;
 
 constructor TAbstractBTree<TIdentify, TData>.Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
 begin
+  FIsFindingProcess := False;
+  FAbstractBTreeLock := TCriticalSection.Create;
   FOnCompareIdentify := AOnCompareIdentifyMethod;
   FOnCompareData := AOnCompareDataMethod;
   FAllowDuplicates := AAllowDuplicates;
   FOrder := AOrder;
   if FOrder<3 then FOrder := 3 // Minimum order for a BTree is 3. Order = Max childs
-  else if FOrder>32 then FOrder := 32; // Maximum order will be established to 32
+  else if FOrder>255 then FOrder := 255; // Maximum order will be established to 255
   FCount := -1;                 // -1 Means there is no control
   {$IFDEF ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION}
   FCircularProtection := True;
@@ -396,22 +420,35 @@ var Lnode, Lparent, Lparentparent : TAbstractBTreeNode;
   iPos, iPosParent, iPosParentParent, j : Integer;
   LmovingUp : Boolean;
   Lleft, Lright : TAbstractBTreeNode;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  if Not Find(AData,Lnode,iPos) then Exit(False);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
 
-  Assert(FCount<>0,'Cannot Delete when FCount = 0');
+    if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
 
-  if (FCount>0) then begin
-    SetCount(FCount-1);
-  end;
+    Assert(FCount<>0,'Cannot Delete when FCount = 0');
 
-  LmovingUp := False;
+    if (FCount>0) then begin
+      SetCount(FCount-1);
+    end;
 
-  if (Lnode.IsLeaf) then begin
-    Lnode.DeleteData(iPos);
-  end;
+    LmovingUp := False;
+
+    if (Lnode.IsLeaf) then begin
+      Lnode.DeleteData(iPos);
+    end;
+
+    if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
 
   repeat
+    if Assigned(LCircularProtectionList) then begin
+      if LCircularProtectionList.Add(Lnode.identify)<0 then raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure at Find for T='+ToString(LNode)+ ' deleting '+NodeDataToString(AData));
+    end;
     if (Lnode.IsLeaf) or (LmovingUp) then begin
       if (IsNil(Lnode.parent)) and (Length(Lnode.childs)=1) then begin
         // child will be root
@@ -578,7 +615,13 @@ begin
       //
       // Search Indorder predecessor:
       Lleft := GetNode(Lnode.childs[iPos]);
-      while (Not Lleft.IsLeaf) do Lleft := GetNode(Lleft.childs[Lleft.Count]);
+      while (Not Lleft.IsLeaf) do begin
+        if Assigned(LCircularProtectionList) then begin
+          if LCircularProtectionList.Add(Lleft.childs[Lleft.Count])<0 then
+            raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure searching for inorder precessor at '+ToString(Lleft)+' deleting '+NodeDataToString(AData));
+        end;
+        Lleft := GetNode(Lleft.childs[Lleft.Count]);
+      end;
       if (Lleft.Count>MinItemsPerNode) then begin
         // Inorder predecessor
         Lnode.data[iPos] := Lleft.data[Lleft.Count-1];
@@ -589,7 +632,13 @@ begin
       end;
       // Search Indorder successor:
       Lright := GetNode(Lnode.childs[iPos+1]);
-      while (Not Lright.IsLeaf) do Lright := GetNode(Lright.childs[0]);
+      while (Not Lright.IsLeaf) do begin
+        if Assigned(LCircularProtectionList) then begin
+          if LCircularProtectionList.Add(Lright.childs[0])<0 then
+            raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure searching for inorder successor at '+ToString(Lright)+' deleting '+NodeDataToString(AData));
+        end;
+        Lright := GetNode(Lright.childs[0]);
+      end;
       if (Lright.Count>MinItemsPerNode) then begin
         // Inorder successor
         Lnode.data[iPos] := Lright.data[0];
@@ -642,8 +691,24 @@ begin
 
     end;
 
-    LmovingUp := True;
+    if (Not LmovingUp) then begin
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      LmovingUp := True;
+    end;
   until (False);
+    finally
+      if Assigned(LCircularProtectionList) then
+        LCircularProtectionList.Free;
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+destructor TAbstractBTree<TIdentify, TData>.Destroy;
+begin
+  FAbstractBTreeLock.Free;
+  inherited;
 end;
 
 procedure TAbstractBTree<TIdentify, TData>.DisposeData(var AData: TData);
@@ -659,11 +724,16 @@ end;
 procedure TAbstractBTree<TIdentify, TData>.EraseTree;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := GetRoot;
-  if Not IsNil(Lnode.identify) then EraseTreeExt(Lnode);
-  ClearNode(Lnode);
-  if Fcount>0 then SetCount(0);
-  SetRoot(Lnode);
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := GetRoot;
+    if Not IsNil(Lnode.identify) then EraseTreeExt(Lnode);
+    ClearNode(Lnode);
+    if Fcount>0 then SetCount(0);
+    SetRoot(Lnode);
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 procedure TAbstractBTree<TIdentify, TData>.EraseTreeExt(var ANode: TAbstractBTreeNode);
@@ -683,27 +753,62 @@ begin
   ClearNode(ANode);
 end;
 
-function TAbstractBTree<TIdentify, TData>.Find(const AData: TData; out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
-var LCircularPreviousSearchProtection : TNoDuplicateData<TIdentify>;
+function TAbstractBTree<TIdentify, TData>.FillList(AStartIndex, ACount: Integer; const AList: TList<TData>): Integer;
+var Lnode : TAbstractBTreeNode;
+  iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  if FCircularProtection then begin
-    LCircularPreviousSearchProtection := TNoDuplicateData<TIdentify>.Create(FOnCompareIdentify);
-  end else LCircularPreviousSearchProtection := Nil;
+  Assert((AStartIndex>=0) and (ACount>=0),Format('Invalid start %d or count %d',[AStartIndex,ACount]));
+  Result := 0;
+  FAbstractBTreeLock.Acquire;
   try
-    ANode := GetRoot;
-    iPos := 0;
-    repeat
-      if FCircularProtection then begin
-        if Not LCircularPreviousSearchProtection.Add(ANode.identify) then raise EAbstractBTree.Create('Circular T structure at Find for T='+ToString(ANode)+ ' searching for '+NodeDataToString(AData));
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
+      if (ACount<=0) or (AStartIndex<0) then Exit;
+      if (FCount>=0) And (FCount-1 < AStartIndex) then Exit;
+
+      Lnode := FindLowestNodeExt(LCircularProtectionList);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      if Lnode.Count<=0 then Exit;
+      //
+      Dec(AStartIndex);
+      iPos := 0;
+      while (AStartIndex>=0) do begin
+        if Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos) then Exit;
+        Dec(AStartIndex);
       end;
-      if (BinarySearch(AData,ANode.data,iPos)) then Exit(True)
-      else if (Not ANode.IsLeaf) then ANode := GetNode( ANode.childs[ iPos ] )
-      else Exit(False);
-    until False;
+      if Not ( (AStartIndex=-1) and (iPos < Lnode.Count) and (iPos>=0) ) then Exit;
+      // Lnode.data[iPos] = Start position
+      repeat
+        AList.Add(Lnode.data[iPos]);
+        Dec(ACount);
+        inc(Result);
+      until (ACount<0) or (Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos));
+    finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    end;
   finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractBTree<TIdentify, TData>.Find(const AData: TData; out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
+var LCircularProtectionList: TOrderedList<TIdentify>;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
     if FCircularProtection then begin
-      LCircularPreviousSearchProtection.Free;
-    end;
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := FindExt(AData,LCircularProtectionList,ANode,iPos);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
   end;
 end;
 
@@ -715,59 +820,178 @@ begin
   raise EAbstractBTree.Create(Format('Child not found at %s',[ToString(AParent)]));
 end;
 
+function TAbstractBTree<TIdentify, TData>.FindExt(const AData: TData; const ACircularProtectionList: TOrderedList<TIdentify>;
+  out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
+begin
+  Assert(Not FIsFindingProcess,'Is finding process');
+  FIsFindingProcess := True;
+  Try
+    ANode := GetRoot;
+    iPos := 0;
+    repeat
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.identify)<0 then raise EAbstractBTree.Create(ClassName+'.Find Circular T structure at Find for T='+ToString(ANode)+ ' searching for '+NodeDataToString(AData));
+      end;
+      if (BinarySearch(AData,ANode.data,iPos)) then Exit(True)
+      else if (Not ANode.IsLeaf) then ANode := GetNode( ANode.childs[ iPos ] )
+      else Exit(False);
+    until False;
+  Finally
+    FIsFindingProcess := False;
+  End;
+end;
+
 function TAbstractBTree<TIdentify, TData>.FindHighest(out AHighest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := FindHighestNode;
-  if Lnode.Count>0 then begin
-     AHighest := Lnode.data[Lnode.Count-1];
-     Result := True;
-  end else Result := False;
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := FindHighestNode;
+    if Lnode.Count>0 then begin
+       AHighest := Lnode.data[Lnode.Count-1];
+       Result := True;
+    end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindHighestNode: TAbstractBTreeNode;
 begin
-  Result := GetRoot;
-  while (Not Result.IsLeaf) do Result := GetNode(Result.childs[Result.Count]);
+  Result := FindHighestNodeExt(Nil);
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindHighestNodeExt(
+  const ACircularProtectionList: TOrderedList<TIdentify>): TAbstractBTreeNode;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    Result := GetRoot;
+    while (Not Result.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(Result.childs[Result.Count])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindHighestNode Circular T structure for T='+ToString(Result));
+      end;
+      Result := GetNode(Result.childs[Result.Count]);
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindIndex(AIndex: Integer; out AData: TData): Boolean;
+var Lnode : TAbstractBTreeNode;
+  iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
+      Lnode := FindLowestNodeExt(LCircularProtectionList);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      if Lnode.Count<=0 then Exit(False);
+      //
+      Dec(AIndex);
+      iPos := 0;
+      while (AIndex>=0) do begin
+        if Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos) then Exit(False);
+        Dec(AIndex);
+      end;
+      if (AIndex=-1) and (iPos < Lnode.Count) and (iPos>=0) then begin
+        Result := True;
+        AData := Lnode.data[iPos];
+      end else Result := False;
+    finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindLowest(out ALowest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := FindLowestNode;
-  if Lnode.Count>0 then begin
-    ALowest := Lnode.data[0];
-    Result := True;
-  end else Result := False;
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := FindLowestNode;
+    if Lnode.Count>0 then begin
+      ALowest := Lnode.data[0];
+      Result := True;
+    end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindLowestNode: TAbstractBTreeNode;
 begin
-  Result := GetRoot;
-  while (Not Result.IsLeaf) do Result := GetNode(Result.childs[0]);
+  Result := FindLowestNodeExt(Nil);
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindLowestNodeExt(
+  const ACircularProtectionList: TOrderedList<TIdentify>): TAbstractBTreeNode;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    Result := GetRoot;
+    while (Not Result.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(Result.childs[0])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindLowestNode Circular T structure for T='+ToString(Result));
+      end;
+      Result := GetNode(Result.childs[0]);
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindPrecessor(const AData : TData; out APrecessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  Result := False;
-  if Not Find(AData,Lnode,iPos) then Exit(False);
-  repeat
-    Result := FindPrecessorExt(Lnode,iPos);
-    if Result then begin
-      APrecessor := Lnode.data[iPos];
-    end;
-  until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,APrecessor)>0);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := False;
+      if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      repeat
+        Result := FindPrecessorExt(LCircularProtectionList,Lnode,iPos);
+        if Result then begin
+          APrecessor := Lnode.data[iPos];
+        end;
+      until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,APrecessor)>0);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
-function TAbstractBTree<TIdentify, TData>.FindPrecessorExt(var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
+function TAbstractBTree<TIdentify, TData>.FindPrecessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
   Result := False;
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos]);
-    while (Not ANode.IsLeaf) do ANode := GetNode(ANode.childs[ANode.Count]);
+    while (Not ANode.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.childs[ANode.Count])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindPrecessor Circular T structure at Find for T='+ToString(ANode));
+      end;
+      ANode := GetNode(ANode.childs[ANode.Count]);
+    end;
     iPos := ANode.Count-1;
     Exit(True);
   end else begin
@@ -784,10 +1008,21 @@ begin
         Exit(True);
       end else begin
         // Search parents until parent iPos>0
-        while (iPos=0) and (Not IsNil(Lparent.parent)) do begin
-          ANode := Lparent;
-          Lparent := GetNode(ANode.parent);
-          iPos := FindChildPos(ANode.identify,Lparent);
+        if Assigned(ACircularProtectionList) then begin
+          Lsecondary := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+        end else Lsecondary := Nil;
+        try
+          while (iPos=0) and (Not IsNil(Lparent.parent)) do begin
+            ANode := Lparent;
+            if Assigned(Lsecondary) then begin
+              if Lsecondary.Add(ANode.parent)<0 then
+                raise EAbstractBTree.Create(ClassName+'.FindPrecessor Circular T structure at Find for parent of T='+ToString(ANode));
+            end;
+            Lparent := GetNode(ANode.parent);
+            iPos := FindChildPos(ANode.identify,Lparent);
+          end;
+        finally
+          if Assigned(Lsecondary) then Lsecondary.Free;
         end;
         if iPos>0 then begin
           Dec(iPos);
@@ -802,25 +1037,46 @@ end;
 function TAbstractBTree<TIdentify, TData>.FindSuccessor(const AData : TData; out ASuccessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  Result := False;
-  if Not Find(AData,Lnode,iPos) then Exit(False);
-  repeat
-    Result := FindSuccessorExt(Lnode,iPos);
-    if Result then begin
-      ASuccessor := Lnode.data[iPos];
-    end;
-  until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,ASuccessor)<0);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := False;
+      if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      repeat
+        Result := FindSuccessorExt(LCircularProtectionList,Lnode,iPos);
+        if Result then begin
+          ASuccessor := Lnode.data[iPos];
+        end;
+      until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,ASuccessor)<0);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
-function TAbstractBTree<TIdentify, TData>.FindSuccessorExt(var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
+function TAbstractBTree<TIdentify, TData>.FindSuccessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
   Result := False;
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos+1]);
     iPos := 0;
-    while (Not ANode.IsLeaf) do ANode := GetNode(ANode.childs[0]);
+    while (Not ANode.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.childs[0])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindSuccessor Circular T structure at Find for T='+ToString(ANode));
+      end;
+      ANode := GetNode(ANode.childs[0]);
+    end;
     Exit(True);
   end else begin
     if iPos+1<ANode.Count then begin
@@ -835,10 +1091,21 @@ begin
         Exit(True);
       end else begin
         // Search parents until parent iPos>0
-        while (iPos=Lparent.Count) and (Not IsNil(Lparent.parent)) do begin
-          ANode := Lparent;
-          Lparent := GetNode(ANode.parent);
-          iPos := FindChildPos(ANode.identify,Lparent);
+        if Assigned(ACircularProtectionList) then begin
+          Lsecondary := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+        end else Lsecondary := Nil;
+        try
+          while (iPos=Lparent.Count) and (Not IsNil(Lparent.parent)) do begin
+            ANode := Lparent;
+            if Assigned(Lsecondary) then begin
+              if Lsecondary.Add(ANode.parent)<0 then
+                raise EAbstractBTree.Create(ClassName+'.FindSuccessor Circular T structure at Find for parent of T='+ToString(ANode));
+            end;
+            Lparent := GetNode(ANode.parent);
+            iPos := FindChildPos(ANode.identify,Lparent);
+          end;
+        finally
+          if Assigned(Lsecondary) then Lsecondary.Free;
         end;
         if iPos<Lparent.Count then begin
           ANode := Lparent;
@@ -857,6 +1124,8 @@ end;
 function TAbstractBTree<TIdentify, TData>.GetHeight: Integer;
 var Lnode : TAbstractBTreeNode;
 begin
+  FAbstractBTreeLock.Acquire;
+  try
   Lnode := GetRoot;
   if (Lnode.Count=0) or (IsNil(Lnode.identify)) then Exit(0);
   Result := 1;
@@ -864,6 +1133,14 @@ begin
     Lnode := GetNode(Lnode.childs[0]);
     inc(Result);
   end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+procedure TAbstractBTree<TIdentify, TData>.Lock;
+begin
+  FAbstractBTreeLock.Acquire;
 end;
 
 function TAbstractBTree<TIdentify, TData>.MaxChildrenPerNode: Integer;
@@ -966,7 +1243,7 @@ begin
   FCount := ANewCount;
 end;
 
-procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode);
+procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode; const ACircularProtectionList : TOrderedList<TIdentify>);
 var iDataInsertPos : Integer;
   LnewNode, Lup : TAbstractBTreeNode;
 begin
@@ -978,6 +1255,9 @@ begin
     // Lup will be a new root
     Lup := NewNode;
   end else begin
+    if Assigned(ACircularProtectionList) then begin
+      if ACircularProtectionList.Add(ANode.parent)<0 then raise EAbstractBTree.Create(ClassName+'.SplitAfterInsert Circular T structure at Find for parent of T='+ToString(ANode));
+    end;
     Lup := GetNode(ANode.parent);
   end;
   if Lup.Count=0 then begin
@@ -1001,7 +1281,7 @@ begin
   // Remove data&child
   ANode.DeleteData(MinItemsPerNode);
   SaveNode(ANode);
-  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup);
+  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup,ACircularProtectionList);
 end;
 
 function TAbstractBTree<TIdentify, TData>.ToString(const ANode: TAbstractBTreeNode): String;
@@ -1015,6 +1295,11 @@ begin
   Result := '['+Result+']';
 end;
 
+procedure TAbstractBTree<TIdentify, TData>.Unlock;
+begin
+  FAbstractBTreeLock.Release;
+end;
+
 { TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode }
 
 function TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode.Count: Integer;
@@ -1204,18 +1489,6 @@ begin
   Froot := Value.identify;
 end;
 
-{ TIntegerBTree }
-
-constructor TIntegerBTree.Create(AAllowDuplicates: Boolean; AOrder: Integer);
-begin
-  inherited Create(TComparison_Integer,AAllowDuplicates,AOrder);
-end;
-
-function TIntegerBTree.NodeDataToString(const AData: Integer): String;
-begin
-  Result := AData.ToString;
-end;
-
 { TNoDuplicateData<TData> }
 
 function TNoDuplicateData<TData>.Add(const AData: TData): Boolean;

+ 210 - 108
src/libraries/abstractmem/UAbstractMemBTree.pas

@@ -57,7 +57,9 @@ type
     FInitialZone : TAMZone;
     FrootPosition : TAbstractMemPosition;
     procedure SaveHeader;
-    function GetNodeSize : Integer;
+    Procedure CheckInitialized;
+    procedure LoadNodeHeader(const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+    procedure SaveNodeHeader(const ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
   protected
     FAbstractMem : TAbstractMem;
     function GetRoot: TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
@@ -82,6 +84,7 @@ type
     function GetNode(AIdentify : TAbstractMemPosition) : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
     class function MinAbstractMemInitialPositionSize : Integer;
     property AbstractMem : TAbstractMem read FAbstractMem;
+    property Count;
   End;
 
   TAbstractMemBTreeData<TData> = Class(TAbstractMemBTree)
@@ -101,6 +104,10 @@ type
     function AddData(const AData: TData) : Boolean;
     function FindData(const AData: TData; var APosition : TAbstractMemPosition) : Boolean;
     function DeleteData(const AData: TData) : Boolean;
+    function FindDataPrecessor(const AData : TData; var APrecessor : TData) : Boolean;
+    function FindDataSuccessor(const AData : TData; var ASuccessor : TData) : Boolean;
+    function FindDataLowest(out ALowest : TData) : Boolean;
+    function FindDataHighest(out AHighest : TData) : Boolean;
   End;
 
 
@@ -109,19 +116,35 @@ implementation
 
 { TAbstractMemBTree<TData> }
 
-constructor TAbstractMemBTree.Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates: Boolean;  AOrder: Integer);
+procedure TAbstractMemBTree.CheckInitialized;
+begin
+  if (FInitialZone.position=0) then raise EAbstractMemBTree.Create(Format('%s initial position not initialized',[ClassName]));
+end;
+
+constructor TAbstractMemBTree.Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates: Boolean; AOrder: Integer);
 var LBuff : TBytes;
  i : Integer;
  LOrder : Integer;
-
 begin
   FAbstractMem := AAbstractMem;
   FrootPosition := 0;
+
   inherited Create(TComparison_Integer,TComparison_Integer,AAllowDuplicates,AOrder);
   FCount := 0;
   //
-  if Not FAbstractMem.GetUsedZoneInfo(AInitialZone.position,False,FInitialZone) then raise EAbstractMemBTree.Create('Cannot capture zone info for initialize');
-  if (FInitialZone.size<MinAbstractMemInitialPositionSize) then raise EAbstractMemBTree.Create(Format('Invalid size %d for initialize',[FInitialZone.size]));
+  if Not FAbstractMem.GetUsedZoneInfo(AInitialZone.position,False,FInitialZone) then begin
+    if FAbstractMem.ReadOnly then begin
+      // Is not initialized and is Read Only
+      FInitialZone.Clear;
+      Exit;
+    end;
+    raise EAbstractMemBTree.Create('Cannot capture zone info for initialize');
+  end else begin
+    if FInitialZone.position=0 then Exit;
+  end;
+  if (FInitialZone.size<MinAbstractMemInitialPositionSize) then begin
+    raise EAbstractMemBTree.Create(Format('Invalid size %d for initialize',[FInitialZone.size]));
+  end;
   SetLength(LBuff,CT_MIN_INITIAL_POSITION_SIZE);
   FAbstractMem.Read(FInitialZone.position,LBuff[0],Length(LBuff));
   try
@@ -134,21 +157,8 @@ begin
     LOrder := 0;
     Move(LBuff[12],LOrder,4);
     if LOrder<>Order then raise EAbstractMemBTree.Create(Format('Invalid Order %d expected %d',[LOrder,Order]));
-    if ( Not ((FrootPosition=0) and (FCount=0))) then raise EAbstractMemBTree.Create(Format('Invalid initial root %d vs count %d',[FrootPosition,FCount]));
+    if (((FrootPosition=0) and (FCount>0))) then raise EAbstractMemBTree.Create(Format('Invalid initial root %d vs count %d',[FrootPosition,FCount]));
   finally
-    if FrootPosition<=0 then begin
-      FrootPosition := 0;
-      FCount := 0;
-      for i := 0 to CT_AbstractMemBTree_Magic.Length-1 do begin
-        LBuff[i] := Byte(Ord(CT_AbstractMemBTree_Magic.Chars[i]));
-      end;
-      Move(FrootPosition,LBuff[4],4);
-      Move(FCount,LBuff[8],4);
-      LOrder := Order;
-      Move(LOrder,LBuff[12],4);
-      FAbstractMem.Write(FInitialZone.position,LBuff[0],16);
-      SaveHeader;
-    end;
   end;
 end;
 
@@ -166,76 +176,47 @@ begin
 end;
 
 procedure TAbstractMemBTree.DisposeNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
+var LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
 begin
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
   FAbstractMem.Dispose( ANode.identify );
   ClearNode(ANode);
+  Assert(((LChildsCount=0) and (LChildsPosition=0))
+     or ((LChildsCount<>0) and (LChildsPosition<>0)),Format('Invalid Childs count %d and position %d',[LChildsCount,LChildsPosition]));
+  if LChildsCount>0 then begin
+    FAbstractMem.Dispose( LChildsPosition );
+  end;
 end;
 
 function TAbstractMemBTree.GetNode(AIdentify: TAbstractMemPosition): TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
 var LBuff : TBytes;
-  LStream : TStream;
-  LByte : Byte;
-  i, LItemsCount, LChildsCount : Integer;
+  i, LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
 begin
-  // For each node:
-  // Size = (4+2+2)+(4*MaxItemsPerNode)+(4*MaxChildrenPerNode) = GetNodeSize
-  // 4 Bytes [0..3] : Parent
-  // 1 Byte  [4] : Used items (0..32)
-  // 1 Byte  [5] : Used childs (0 (leaf) or Used Items+1)
-  // 2 Bytes [6..7] : 0 (unusued)
-  // For each item:
-  //   4 Bytes : data (AbstractMemPosition or Data using 4 bytes)
-  // For each children:
-  //   4 Bytes : Children AbstractMem position
-  ClearNode(Result);
-  Result.identify := AIdentify;
-  SetLength(LBuff, GetNodeSize );
-  FAbstractMem.Read(AIdentify,LBuff[0],Length(LBuff));
-  LStream := TMemoryStream.Create;
-  try
-    LStream.Write(LBuff[0],Length(LBuff));
-    LStream.Position := 0;
-    //
-    LStream.Read(Result.parent,4); // Read parent position
-    LStream.Read(LByte,1);
-    LItemsCount := LByte;
-    LStream.Read(LByte,1);
-    LChildsCount := LByte;
-    LStream.Read(LByte,1);
-    Assert(LByte=0);
-    LStream.Read(LByte,1);
-    Assert(LByte=0);
-    if ((LItemsCount=0) and (Result.parent=0) and (LChildsCount=0)) then begin
-      // root without data
-    end else begin
-      if (Result.parent=0) then begin
-        if ((LItemsCount<1) or (LItemsCount>MaxItemsPerNode)) then
-          raise EAbstractMemBTree.Create(Format('Root Node items %d not in range [%d..%d]',[LItemsCount,MinItemsPerNode,MaxItemsPerNode]));
-      end else begin
-        if ((LItemsCount<MinItemsPerNode) or (LItemsCount>MaxItemsPerNode)) then
-          raise EAbstractMemBTree.Create(Format('Node items %d not in range [%d..%d]',[LItemsCount,MinItemsPerNode,MaxItemsPerNode]));
-      end;
-      if ((LChildsCount<>0) and (LChildsCount<>(LItemsCount+1))) then
-        raise EAbstractMemBTree.Create(Format('Node childrens %d not %d+1 in range [%d..%d]',[LChildsCount,LItemsCount,MinChildrenPerNode,MaxChildrenPerNode]));
-    end;
-    // Read items
-    SetLength(Result.data,LItemsCount);
+  LoadNodeHeader(AIdentify,Result,LChildsCount,LChildsPosition);
+  if LChildsCount>0 then begin
     SetLength(Result.childs,LChildsCount);
-    for i := 0 to LItemsCount-1 do begin
-      LStream.Read(Result.data[i],4);
-    end;
-    // Read childrens
+    SetLength(LBuff,(LChildsCount*4));
+    FAbstractMem.Read(LChildsPosition,LBuff[0],Length(LBuff));
     for i := 0 to LChildsCount-1 do begin
-      LStream.Read(Result.childs[i],4);
+      Move(LBuff[i*4],Result.childs[i],4);
     end;
-  finally
-    LStream.Free;
   end;
-end;
-
-function TAbstractMemBTree.GetNodeSize: Integer;
-begin
-  Result := 8 + (4 * MaxItemsPerNode) + (4 * MaxChildrenPerNode);
+  if ((Result.Count=0) and (Result.parent=0) and (LChildsCount=0)) then begin
+    // root without data
+  end else begin
+    if (Result.parent=0) then begin
+      if ((Result.Count<1) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Root Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end else begin
+      if ((Result.Count<MinItemsPerNode) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end;
+    if ((LChildsCount<>0) and (LChildsCount<>(Result.Count+1))) then
+      raise EAbstractMemBTree.Create(Format('Node childrens %d not %d+1 in range [%d..%d]',[LChildsCount,Result.Count,MinChildrenPerNode,MaxChildrenPerNode]));
+  end;
 end;
 
 function TAbstractMemBTree.GetRoot: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
@@ -250,6 +231,43 @@ begin
   Result := AIdentify=0;
 end;
 
+procedure TAbstractMemBTree.LoadNodeHeader(
+  const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount : Integer;
+begin
+  // Node is stored in zone 2 positions:
+  //
+  // Zone 1: Header
+  //   Size = (4+2+2+4) + (4*MaxItemsPerNode)
+  // 4 Bytes [0..3] : Parent
+  // 1 Byte  [4]    : Used items (0..255)
+  // 1 Byte  [5]    : Used childs (0 (leaf) or Used Items+1)
+  // 2 Bytes [6..7] : 0 (unusued)
+  // 4 Bytes [8..11]: Zone 2 position ( If is a leaf must be 0 )
+  // For each item:
+  //   4 Bytes : data (AbstractMemPosition or Data using 4 bytes)
+  //
+  // Zone 2: OPTIONAL Only if NOT a leaf
+  // For each children:
+  //   4 Bytes : Children AbstractMem position
+  //
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FAbstractMem.Read(APosition,LBuff[0],Length(LBuff));
+  ClearNode(ANode);
+  LItemsCount := 0;
+  AChildsCount := 0;
+  ANode.identify := APosition;
+  Move(LBuff[0],ANode.parent,4);
+  Move(LBuff[4],LItemsCount,1);
+  Move(LBuff[5],AChildsCount,1);
+  Move(LBuff[8],AChildsPosition,4);
+  SetLength(ANode.data,LItemsCount);
+  for i := 0 to LItemsCount-1 do begin
+    Move(LBuff[12 + (i*4)], ANode.data[i], 4);
+  end;
+end;
+
 class function TAbstractMemBTree.MinAbstractMemInitialPositionSize: Integer;
 begin
   Result := CT_MIN_INITIAL_POSITION_SIZE;
@@ -257,8 +275,10 @@ end;
 
 function TAbstractMemBTree.NewNode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
 begin
+  CheckInitialized;
   ClearNode(Result);
-  Result.identify := FAbstractMem.New(GetNodeSize).position;
+  Result.identify := FAbstractMem.New( 8 + (4 * MaxItemsPerNode) + 4 ).position;
+  SaveNodeHeader(Result,0);
 end;
 
 procedure TAbstractMemBTree.SaveHeader;
@@ -266,6 +286,7 @@ var LBuff : TBytes;
  i : Integer;
  LOrder : Integer;
 begin
+  CheckInitialized;
   SetLength(LBuff,16);
   for i := 0 to CT_AbstractMemBTree_Magic.Length-1 do begin
     LBuff[i] := Byte(Ord(CT_AbstractMemBTree_Magic.Chars[i]));
@@ -279,40 +300,56 @@ end;
 
 procedure TAbstractMemBTree.SaveNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
 var LBuff : TBytes;
-  LStream : TStream;
-  LByte : Byte;
-  i, LItemsCount, LChildsCount : Integer;
+  i, LChildsCount : Integer;
+  LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsPosition : TAbstractMemPosition;
+  LZone : TAMZone;
 begin
+  CheckInitialized;
   if ((ANode.Count)>MaxItemsPerNode) or (Length(ANode.childs)>MaxChildrenPerNode) then begin
-    // Protection agains saving temporal Node info with extra datas or childs
+    // Protection against saving temporal Node info with extra datas or childs
     Exit;
   end;
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
+  //
+  if (LChildsCount>0) And (ANode.IsLeaf) then begin
+    // Node wasn't a leaf previously
+    Assert(LChildsPosition<>0,'Old childs position<>0');
+    FAbstractMem.Dispose(LChildsPosition);
+  end else if (LChildsCount=0) And (Not ANode.IsLeaf) then begin
+    // Node was a leaf previously, now not
+    LZone := FAbstractMem.New( MaxChildrenPerNode * 4 );
+    LChildsPosition := LZone.position;
+  end;
+  LChildsCount := Length(ANode.childs);
+  //
+  SaveNodeHeader(ANode,LChildsPosition);
+  //
+  SetLength(LBuff, MaxChildrenPerNode * 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  for i := 0 to LChildsCount-1 do begin
+    Move(ANode.childs[i],LBuff[i*4],4);
+  end;
+  FAbstractMem.Write(LChildsPosition,LBuff[0],LChildsCount*4);
+end;
 
-  // See GetNode info
-  LStream := TMemoryStream.Create;
-  try
-    LStream.Write(ANode.parent,4);
-    LItemsCount := Length(ANode.data);
-    LStream.Write(LItemsCount,1);
-    LChildsCount := Length(ANode.childs);
-    LStream.Write(LChildsCount,1);
-    LByte := 0;
-    LStream.Write(LByte,1);
-    LStream.Write(LByte,1);
-    for i := 0 to LItemsCount-1 do begin
-      LStream.Write(ANode.data[i],4)
-    end;
-    // Read childrens
-    for i := 0 to LChildsCount-1 do begin
-      LStream.Write(ANode.childs[i],4);
-    end;
-    SetLength(LBuff,LStream.Size);
-    LStream.Position := 0;
-    LStream.Read(LBuff[0],LStream.Size);
-    FAbstractMem.Write(ANode.identify,LBuff[0],Length(LBuff));
-  finally
-    LStream.Free;
+procedure TAbstractMemBTree.SaveNodeHeader(
+  const ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount, LChildsCount : Integer;
+begin
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  Move(ANode.parent,LBuff[0],4);
+  LItemsCount := ANode.Count;
+  Move(LItemsCount,LBuff[4],1);
+  LChildsCount := Length(ANode.childs);
+  Move(LChildsCount,LBuff[5],1);
+  Move(AChildsPosition,LBuff[8],4);
+  for i := 0 to LItemsCount-1 do begin
+    Move(ANode.data[i], LBuff[12 + (i*4)], 4);
   end;
+  FAbstractMem.Write(ANode.identify,LBuff[0],Length(LBuff));
 end;
 
 procedure TAbstractMemBTree.SetCount(const ANewCount: Integer);
@@ -329,6 +366,7 @@ end;
 
 procedure TAbstractMemBTree.SetRoot(var Value: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
 begin
+  CheckInitialized;
   inherited;
   FrootPosition := Value.identify;
   SaveHeader;
@@ -416,15 +454,79 @@ function TAbstractMemBTreeData<TData>.FindData(const AData: TData;
 var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
   LiPosNode : Integer;
 begin
-  // NOTE: This is not multithread protected
+  FAbstractBTreeLock.Acquire;
+  try
   FSearchTarget := AData;
+  ClearNode(Lnode);
   if Find(1,Lnode,LiPosNode) then begin
     APosition := Lnode.data[LiPosNode];
     Result := True;
   end else begin
-    APosition := 0;
+    // if Node exists will set APosition of previous value, otherwise will set 0
+    if Lnode.Count>LiPosNode then APosition := Lnode.data[LiPosNode]
+    else if Lnode.Count>0 then APosition := Lnode.data[Lnode.Count-1]
+    else APosition := 0;
     Result := False;
   end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataHighest(out AHighest: TData): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  if FindHighest(Lpos) then begin
+    Result := True;
+    AHighest := LoadData(Lpos);
+  end else Result := False;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataLowest(out ALowest: TData): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  if FindLowest(Lpos) then begin
+    Result := True;
+    ALowest := LoadData(Lpos);
+  end else Result := False;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataPrecessor(const AData: TData; var APrecessor: TData): Boolean;
+var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LiPosNode : Integer;
+  Lpos : TAbstractMemPosition;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+  FSearchTarget := AData;
+  if Find(1,Lnode,LiPosNode) then begin
+    if FindPrecessor(Lnode.data[LiPosNode],Lpos) then begin
+      Result := True;
+      APrecessor := LoadData(Lpos);
+    end else Result := False;
+  end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataSuccessor(const AData: TData; var ASuccessor: TData): Boolean;
+var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LiPosNode : Integer;
+  Lpos : TAbstractMemPosition;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+  FSearchTarget := AData;
+  if Find(1,Lnode,LiPosNode) then begin
+    if FindSuccessor(Lnode.data[LiPosNode],Lpos) then begin
+      Result := True;
+      ASuccessor := LoadData(Lpos);
+    end else Result := False;
+  end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 initialization

+ 24 - 4
src/libraries/abstractmem/tests/src/UAbstractBTree.Tests.pas

@@ -16,6 +16,16 @@ uses
    UAbstractBTree, UOrderedList;
 
 type
+
+  TIntegerBTree = Class( TMemoryBTree<Integer> )
+  private
+  protected
+  public
+    constructor Create(AAllowDuplicates : Boolean; AOrder : Integer);
+    function NodeDataToString(const AData : Integer) : String; override;
+  End;
+
+
    TestTAbstractBTree = class(TTestCase)
    strict private
    public
@@ -37,11 +47,20 @@ type
 
 implementation
 
-function TComparison_XX_Integer(const ALeft, ARight: Integer): Integer;
+{ TIntegerBTree }
+
+constructor TIntegerBTree.Create(AAllowDuplicates: Boolean; AOrder: Integer);
 begin
-  Result := ALeft - ARight;
+  inherited Create(TComparison_Integer,AAllowDuplicates,AOrder);
 end;
 
+function TIntegerBTree.NodeDataToString(const AData: Integer): String;
+begin
+  Result := AData.ToString;
+end;
+
+{ TestTAbstractBTree }
+
 procedure TestTAbstractBTree.SetUp;
 begin
 end;
@@ -65,6 +84,7 @@ begin
   nDeletes := 0;
   Lbt := TIntegerBTree.Create(True,AOrder);
   try
+    Lbt.CircularProtection := (AOrder MOD 2)=0;
     repeat
       inc(nRounds);
       intValue := Random(AOrder * 100);
@@ -184,6 +204,7 @@ begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(False,Lorder);
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       intValue :=valMin;
       Lregs := 0;
@@ -226,6 +247,7 @@ begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(True,Lorder);
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       intValue :=valMin;
       Lregs := 0;
@@ -314,7 +336,6 @@ begin
       end;
       LCurrentTree := Lbt.BTreeToString;
       Lbt.CheckConsistency;
-      if LLastTree = '' then Beep;
     finally
       Lbt.Free;
     end;
@@ -369,7 +390,6 @@ begin
         intValue := Random(intValue)+1;
         DoDelete(intValue);
       end;
-      if LLastTree = '' then Beep;
     finally
       Lbt.Free;
     end;

+ 52 - 6
src/libraries/abstractmem/tests/src/UAbstractMemBTree.Tests.pas

@@ -14,7 +14,7 @@ uses
    TestFramework,
    {$ENDIF}
    {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults,{$ELSE}Generics.Collections,Generics.Defaults,{$ENDIF}
-   UAbstractMem,
+   UAbstractMem, UAbstractBTree.Tests,
    UAbstractBTree, UOrderedList, UAbstractMemBTree;
 
 type
@@ -34,7 +34,6 @@ type
      function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
    End;
 
-
    TestTAbstractMemBTree = class(TTestCase)
    strict private
    public
@@ -137,7 +136,7 @@ procedure TestTAbstractMemBTree.TestInfinite_Integer(AOrder : Integer; AAllowDup
 var Lbt : TAbstractMemBTreeExampleInteger;
   Lbts : TAbstractMemBTreeExampleString;
   Lzone : TAMZone;
-  intValue, nRounds, nAdds, nDeletes, i : Integer;
+  intValue, nRounds, nAdds, nDeletes, i, j : Integer;
   Lnode : TIntegerBTree.TAbstractBTreeNode;
   Lmem : TAbstractMem;
   LCurr : String;
@@ -153,6 +152,7 @@ begin
     nAdds := 0;
     nDeletes := 0;
     Lzone := Lmem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    try
     Lbt := TAbstractMemBTreeExampleInteger.Create(Lmem,Lzone,AAllowDuplicates,AOrder);
     try
       repeat
@@ -189,12 +189,23 @@ begin
         intValue := Random(AOrder * 100);
         Assert(Lbt.Add(intValue),Format('Cannot re-use %d/%d and add %d',[i,AOrder,intValue]));
         Lbt.CheckConsistency;
+        Assert(Lbt.FindIndex(i-1,j),Format('Cannot find %d on index %d on order %d',[intValue,i-1,AOrder]));
+        Assert(Not Lbt.FindIndex(i,j),Format('Found %d on index %d on order %d',[j,i-1,AOrder]));
       end;
+    finally
+      Lbt.Free;
+    end;
+    Lbt := TAbstractMemBTreeExampleInteger.Create(Lmem,Lzone,AAllowDuplicates,AOrder);
+    try
+      Lbt.CheckConsistency;
       Lbt.EraseTree;
+      Lbt.CheckConsistency;
     finally
       Lbt.Free;
     end;
-    Lmem.Dispose(Lzone);
+    finally
+      Lmem.Dispose(Lzone);
+    end;
     DoCheckAbstractMem(Lmem,0);
   Finally
     Lmem.Free;
@@ -221,6 +232,7 @@ begin
     nAdds := 0;
     nDeletes := 0;
     Lzone := Lmem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    try
     Lbt := TAbstractMemBTreeExampleString.Create(Lmem,Lzone,AAllowDuplicates,AOrder,TComparison_String);
     try
       repeat
@@ -255,15 +267,49 @@ begin
       Lbt.CheckConsistency;
       // Try to re-use
       for i := 1 to AOrder do begin
-        intValue := Random(AOrder * 100);
+        intValue := i;
         Assert(Lbt.AddData(intValue.ToString),Format('Cannot re-use %d/%d and add %d',[i,AOrder,intValue]));
         Lbt.CheckConsistency;
       end;
+    finally
+      Lbt.Free;
+    end;
+    Lbt := TAbstractMemBTreeExampleString.Create(Lmem,Lzone,AAllowDuplicates,AOrder,TComparison_String);
+    try
+      Lbt.CheckConsistency;
+      LCurr := Lbt.BTreeToString;
+      // SUCCESSOR
+      Assert(Lbt.FindDataLowest(LCurrData),'Not found Lowest');
+      Assert(LcurrData='1','Not valid lowest');
+      for i := 1 to AOrder do begin
+        Assert(i.ToString=LcurrData,Format('Not valid successor %d %s',[i,LcurrData]));
+        if i<AOrder then begin
+          Assert(Lbt.FindDataSuccessor(LcurrData,LCurrData),Format('Not found successor %d %s',[i,LcurrData]));
+        end else begin
+          Assert(Not Lbt.FindDataSuccessor(LCurrData,LCurrData),Format('Not valid last successor %s',[LCurrData]));
+        end;
+      end;
+      // PRECESSOR
+      Assert(Lbt.FindDataHighest(LCurrData),'Not found Highest');
+      Assert(LcurrData=IntToStr(AOrder),'Not valid highest');
+      for i := AOrder downto 1 do begin
+        Assert(i.ToString=LcurrData,Format('Not valid precessor %d %s',[i,LcurrData]));
+        if i>1 then begin
+          Assert(Lbt.FindDataPrecessor(LcurrData,LCurrData),Format('Not found precessor %d %s',[i,LcurrData]));
+        end else begin
+          Assert(Not Lbt.FindDataPrecessor(LCurrData,LCurrData),Format('Not valid last precessor %s',[LCurrData]));
+        end;
+      end;
+      Lbt.EraseTree;
+      Assert(Lbt.Count=0,'Not erased tree count 0');
+      Lbt.CheckConsistency;
       Lbt.EraseTree;
     finally
       Lbt.Free;
     end;
-    Lmem.Dispose(Lzone);
+    finally
+      Lmem.Dispose(Lzone);
+    end;
     DoCheckAbstractMem(Lmem,0);
   Finally
     Lmem.Free;

+ 1 - 1
src/pascalcoin_daemon.lpi

@@ -53,7 +53,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;libraries\synapse;libraries\pascalcoin;libraries\generics.collections;libraries\sphere10;libraries\hashlib4pascal;libraries\paszlib;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\abstractmem;libraries\regex"/>
+      <OtherUnitFiles Value="core;libraries\synapse;libraries\pascalcoin;libraries\sphere10;libraries\hashlib4pascal;libraries\paszlib;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\abstractmem;libraries\regex"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 14 - 6
src/pascalcoin_miner.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <General>
       <Flags>
@@ -9,7 +9,7 @@
         <MainUnitHasTitleStatement Value="False"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>      
+      <MainUnit Value="0"/>
       <UseAppBundle Value="False"/>
       <ResourceType Value="res"/>
     </General>
@@ -24,9 +24,16 @@
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
         <CommandLineParams Value="-c 1 -s -n TEST"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <CommandLineParams Value="-c 1 -s -n TEST"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
     <RequiredPackages Count="1">
       <Item1>
@@ -36,7 +43,8 @@
     <Units Count="1">
       <Unit0>
         <Filename Value="pascalcoin_miner.pp"/>
-        <IsPartOfProject Value="True"/>        
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="PascalCoinMiner"/>
       </Unit0>
     </Units>
   </ProjectOptions>
@@ -44,11 +52,11 @@
     <Version Value="11"/>
     <PathDelim Value="\"/>
     <Target>
-      <Filename Value="PascalCoinMiner"/>
+      <Filename Value="PascalCoinMineX"/>
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\sphere10;libraries\hashlib4pascal;libraries\generics.collections;libraries\pascalcoin;libraries\paszlib"/>
+      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\sphere10;libraries\hashlib4pascal;libraries\pascalcoin;libraries\paszlib;libraries\regex;libraries\abstractmem;libraries\cryptolib4pascal;libraries\simplebaselib4pascal"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 1 - 1
src/pascalcoin_miner.pp

@@ -57,7 +57,7 @@ type
   end;
 
 Const
-  CT_MINER_VERSION = {$IFDEF PRODUCTION}'5.1'{$ELSE}{$IFDEF TESTNET}'5.1 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
+  CT_MINER_VERSION = {$IFDEF PRODUCTION}'5.2'{$ELSE}{$IFDEF TESTNET}'5.2 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
   CT_Line_DeviceStatus = 3;
   CT_Line_ConnectionStatus = 4;
   CT_Line_MinerValues = 7;

+ 7 - 1
src/pascalcoin_wallet_classic.lpi

@@ -232,7 +232,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;gui-classic;libraries\synapse;libraries\sphere10;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\hashlib4pascal;libraries\generics.collections;libraries\pascalcoin;libraries\paszlib;libraries\gnugettext;libraries\abstractmem"/>
+      <OtherUnitFiles Value="core;gui-classic;libraries\synapse;libraries\sphere10;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\hashlib4pascal;libraries\pascalcoin;libraries\paszlib;libraries\gnugettext;libraries\abstractmem;libraries\regex"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Parsing>
@@ -240,8 +240,14 @@
         <SyntaxMode Value="Delphi"/>
       </SyntaxOptions>
     </Parsing>
+    <CodeGeneration>
+      <Optimizations>
+        <OptimizationLevel Value="2"/>
+      </Optimizations>
+    </CodeGeneration>
     <Linking>
       <Debugging>
+        <GenerateDebugInfo Value="False"/>
         <DebugInfoType Value="dsDwarf2Set"/>
       </Debugging>
       <Options>