Browse Source

Multithread on loading blocks/operations on gui-classic

PascalCoin 6 years ago
parent
commit
0b3889d223
2 changed files with 316 additions and 185 deletions
  1. 25 2
      src/core/UNode.pas
  2. 291 183
      src/gui-classic/UGridUtils.pas

+ 25 - 2
src/core/UNode.pas

@@ -91,7 +91,8 @@ Type
     //
     Procedure NotifyBlocksChanged;
     //
-    procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0);
+    procedure GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
+    procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
     Function FindOperation(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : Boolean;
     Function FindOperationExt(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : TSearchOperationResult;
     Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOperationResult;
@@ -808,7 +809,8 @@ begin
   end;
 end;
 
-procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock : Cardinal = 0);
+procedure TNode.GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal;
+  MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock: Cardinal);
   // Optimization:
   // For better performance, will only include at "OperationsResume" values betweeen "startOperation" and "endOperation"
 
@@ -824,6 +826,9 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
     acc_0_miner_reward, acc_4_dev_reward : Int64;
     acc_4_for_dev : Boolean;
   begin
+    if Assigned(AOwnerThread) then begin
+      if AOwnerThread.terminated then Exit;
+    end;
     if (act_depth<=0) then exit;
     opc := TPCOperationsComp.Create(Nil);
     Try
@@ -833,6 +838,9 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
         while (last_block_number>block_number) And (act_depth>0)
           And (block_number >= (account_number DIV CT_AccountsPerBlock))
           And (nOpsCounter <= EndOperation) do begin
+          if Assigned(AOwnerThread) then begin
+            if AOwnerThread.terminated then Exit;
+          end;
           found_in_block := False;
           last_block_number := block_number;
           l.Clear;
@@ -926,6 +934,21 @@ begin
   end;
 end;
 
+procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock : Cardinal = 0);
+var LOpList : TList<TOperationResume>;
+  i : Integer;
+begin
+  LOpList := TList<TOperationResume>.Create;
+  try
+    GetStoredOperationsFromAccount(Nil,LOpList,account_number,MaxDepth,StartOperation,EndOperation,SearchBackwardsStartingAtBlock);
+    for i := 0 to LOpList.Count-1 do begin
+      OperationsResume.Add(LOpList[i]);
+    end;
+  finally
+    LOpList.Free;
+  end;
+end;
+
 function TNode.FindNOperation(block, account, n_operation: Cardinal;
   var OpResume: TOperationResume): TSearchOperationResult;
   // Note: block = 0 search in all blocks. If Block>0 must match a valid block with operation with this account

+ 291 - 183
src/gui-classic/UGridUtils.pas

@@ -30,7 +30,7 @@ uses
 {$ELSE}
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
-  Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams,
+  Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread,
   UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
@@ -83,6 +83,17 @@ Type
     Function SelectedAccounts(accounts : TOrderedCardinalList) : Integer;
   End;
 
+  TOperationsGrid = Class;
+
+  TOperationsGridUpdateThread = Class(TPCThread)
+    FOperationsGrid : TOperationsGrid;
+    procedure DoUpdateOperationsGrid(ANode : TNode; var AList : TList<TOperationResume>);
+  protected
+    procedure BCExecute; override;
+  public
+    constructor Create(AOperationsGrid : TOperationsGrid);
+  End;
+
   TOperationsGrid = Class(TComponent)
   private
     FDrawGrid: TDrawGrid;
@@ -93,6 +104,7 @@ Type
     FBlockStart: Int64;
     FBlockEnd: Int64;
     FMustShowAlwaysAnAccount: Boolean;
+    FOperationsGridUpdateThread : TOperationsGridUpdateThread;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
@@ -151,7 +163,19 @@ Type
     TimeAverage25 : Real;
     TimeAverage10 : Real;
   End;
-  TBlockChainDataArray = Array of TBlockChainData;
+
+  TBlockChainGrid = Class;
+
+  TBlockChainGridUpdateThread = Class(TPCThread)
+    FBlockChainGrid : TBlockChainGrid;
+    FBlockStart, FBlockEnd : Int64;
+    procedure DoUpdateBlockChainGrid(ANode : TNode; var AList : TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
+  protected
+    procedure BCExecute; override;
+  public
+    constructor Create(ABlockChainGrid : TBlockChainGrid);
+  End;
+
 
   { TBlockChainGrid }
 
@@ -159,7 +183,7 @@ Type
 
   TBlockChainGrid = Class(TComponent)
   private
-    FBlockChainDataArray : TBlockChainDataArray;
+    FBlockChainDataList : TList<TBlockChainData>;
     FBlockStart: Int64;
     FHashRateAs: TShowHashRateAs;
     FMaxBlocks: Integer;
@@ -168,6 +192,7 @@ Type
     FNodeNotifyEvents : TNodeNotifyEvents;
     FHashRateAverageBlocksCount: Integer;
     FShowTimeAverageColumns: Boolean;
+    FBlockChainGridUpdateThread : TBlockChainGridUpdateThread;
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
     procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
@@ -448,9 +473,7 @@ begin
     C.ColumnType := act_account_number;
     C.width := -1;
   end;
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   if (ARow=0) then begin
     // Header
     s := CT_ColumnHeader[C.ColumnType];
@@ -621,6 +644,127 @@ begin
   InitGrid;
 end;
 
+{ TOperationsGridUpdateThread }
+
+procedure TOperationsGridUpdateThread.BCExecute;
+var list : TList<TOperationResume>;
+  i : Integer;
+begin
+  list := TList<TOperationResume>.Create;
+  try
+    DoUpdateOperationsGrid(FOperationsGrid.Node,list);
+    if (Not Terminated) then begin
+      FOperationsGrid.FOperationsResume.Clear;
+      for i := 0 to list.Count-1 do begin
+        FOperationsGrid.FOperationsResume.Add(list[i]);
+      end;
+      FOperationsGrid.InitGrid;
+    end;
+  finally
+    list.Free;
+  end;
+end;
+
+constructor TOperationsGridUpdateThread.Create(AOperationsGrid: TOperationsGrid);
+begin
+  FOperationsGrid := AOperationsGrid;
+  inherited Create(True);
+  FreeOnTerminate := False;
+  Suspended := False;
+end;
+
+procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(ANode: TNode; var AList: TList<TOperationResume>);
+Var list : TList<Cardinal>;
+  i,j : Integer;
+  OPR : TOperationResume;
+  Op : TPCOperation;
+  opc : TPCOperationsComp;
+  bstart,bend : int64;
+  LOperationsResume : TOperationsResumeList;
+begin
+  if Not Assigned(ANode) then exit;
+  AList.Clear;
+  Try
+    if (FOperationsGrid.MustShowAlwaysAnAccount) And (FOperationsGrid.AccountNumber<0) then exit;
+
+    if FOperationsGrid.FPendingOperations then begin
+      for i := ANode.Operations.Count - 1 downto 0 do begin
+        Op := ANode.Operations.OperationsHashTree.GetOperation(i);
+        If TPCOperation.OperationToOperationResume(0,Op,True,Op.SignerAccount,OPR) then begin
+          OPR.NOpInsideBlock := i;
+          OPR.Block := ANode.Bank.BlocksCount;
+          OPR.Balance := ANode.Operations.SafeBoxTransaction.Account(Op.SignerAccount).balance;
+          AList.Add(OPR);
+        end;
+      end;
+    end else begin
+      if FOperationsGrid.AccountNumber<0 then begin
+        opc := TPCOperationsComp.Create(Nil);
+        try
+          opc.bank := ANode.Bank;
+          If FOperationsGrid.FBlockEnd<0 then begin
+            If ANode.Bank.BlocksCount>0 then bend := ANode.Bank.BlocksCount-1
+            else bend := 0;
+          end else bend := FOperationsGrid.FBlockEnd;
+          if FOperationsGrid.FBlockStart<0 then begin
+            if (bend > 300) then bstart := bend - 300
+            else bstart := 0;
+          end else bstart:= FOperationsGrid.FBlockStart;
+          If bstart<0 then bstart := 0;
+          if bend>=ANode.Bank.BlocksCount then bend:=ANode.Bank.BlocksCount;
+          while (bstart<=bend) and (Not Terminated) do begin
+            opr := CT_TOperationResume_NUL;
+            if (ANode.Bank.Storage.LoadBlockChainBlock(opc,bend)) then begin
+              // Reward operation
+              OPR := CT_TOperationResume_NUL;
+              OPR.valid := true;
+              OPR.Block := bend;
+              OPR.time := opc.OperationBlock.timestamp;
+              OPR.AffectedAccount := bend * CT_AccountsPerBlock;
+              OPR.Amount := opc.OperationBlock.reward;
+              OPR.Fee := opc.OperationBlock.fee;
+              OPR.Balance := OPR.Amount+OPR.Fee;
+              OPR.OperationTxt := 'Blockchain reward';
+              AList.Add(OPR);
+              // Reverse operations inside a block
+              for i := opc.Count - 1 downto 0 do begin
+                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],True,opc.Operation[i].SignerAccount,opr) then begin
+                  opr.NOpInsideBlock := i;
+                  opr.Block := bend;
+                  opr.time := opc.OperationBlock.timestamp;
+                  AList.Add(opr);
+                end;
+              end;
+            end else break;
+            dec(bend);
+          end;
+        finally
+          opc.Free;
+        end;
+
+      end else begin
+        list := TList<Cardinal>.Create;
+        Try
+          ANode.Operations.OperationsHashTree.GetOperationsAffectingAccount(FOperationsGrid.AccountNumber,list);
+          for i := list.Count - 1 downto 0 do begin
+            Op := ANode.Operations.OperationsHashTree.GetOperation((list[i]));
+            If TPCOperation.OperationToOperationResume(0,Op,False,FOperationsGrid.AccountNumber,OPR) then begin
+              OPR.NOpInsideBlock := i;
+              OPR.Block := ANode.Operations.OperationBlock.block;
+              OPR.Balance := ANode.Operations.SafeBoxTransaction.Account(FOperationsGrid.AccountNumber).balance;
+              AList.Add(OPR);
+            end;
+          end;
+        Finally
+          list.Free;
+        End;
+        ANode.GetStoredOperationsFromAccount(Self,AList,FOperationsGrid.AccountNumber,100,0,5000);
+      end;
+    end;
+  Finally
+  End;
+end;
+
 { TOperationsGrid }
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
@@ -635,11 +779,17 @@ begin
   FBlockStart := -1;
   FBlockEnd := -1;
   FPendingOperations := false;
+  FOperationsGridUpdateThread := Nil;
   inherited;
 end;
 
 destructor TOperationsGrid.Destroy;
 begin
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
   FOperationsResume.Free;
   FNodeNotifyEvents.Free;
   inherited;
@@ -689,9 +839,7 @@ procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; R
 Var s : String;
   opr : TOperationResume;
 begin
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   opr := CT_TOperationResume_NUL;
   Try
   if (ARow=0) then begin
@@ -855,6 +1003,11 @@ end;
 procedure TOperationsGrid.SetNode(const Value: TNode);
 begin
   if GetNode=Value then exit;
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
   FNodeNotifyEvents.Node := Value;
   UpdateAccountOperations; // New Build 1.0.3
 end;
@@ -897,95 +1050,126 @@ begin
 end;
 
 procedure TOperationsGrid.UpdateAccountOperations;
-Var list : TList<Cardinal>;
-  i,j : Integer;
-  OPR : TOperationResume;
-  Op : TPCOperation;
-  opc : TPCOperationsComp;
-  bstart,bend : int64;
 begin
-  FOperationsResume.Clear;
-  Try
-    if Not Assigned(Node) then exit;
-    if (MustShowAlwaysAnAccount) And (AccountNumber<0) then exit;
+  if Not Assigned(Node) then exit;
+  If Assigned(FOperationsGridUpdateThread) then begin
+    FOperationsGridUpdateThread.Terminate;
+    FOperationsGridUpdateThread.WaitFor;
+    FreeAndNil(FOperationsGridUpdateThread);
+  end;
+  FOperationsGridUpdateThread := TOperationsGridUpdateThread.Create(Self);
+end;
 
-    if FPendingOperations then begin
-      for i := Node.Operations.Count - 1 downto 0 do begin
-        Op := Node.Operations.OperationsHashTree.GetOperation(i);
-        If TPCOperation.OperationToOperationResume(0,Op,True,Op.SignerAccount,OPR) then begin
-          OPR.NOpInsideBlock := i;
-          OPR.Block := Node.Bank.BlocksCount;
-          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SignerAccount).balance;
-          FOperationsResume.Add(OPR);
-        end;
+{ TBlockChainGridUpdateThread }
+
+procedure TBlockChainGridUpdateThread.BCExecute;
+var Llist : TList<TBlockChainData>;
+  i : Integer;
+  LBlockStart, LBlockEnd : Integer;
+begin
+  if (Not Assigned(FBlockChainGrid.Node)) Or (Terminated) then Exit;
+
+  if (FBlockChainGrid.FBlockStart>FBlockChainGrid.FBlockEnd) And (FBlockChainGrid.FBlockStart>=0) then FBlockChainGrid.FBlockEnd := -1;
+  if (FBlockChainGrid.FBlockEnd>=0) And (FBlockChainGrid.FBlockEnd<FBlockChainGrid.FBlockStart) then FBlockChainGrid.FBlockStart:=-1;
+
+  if FBlockChainGrid.FBlockStart>(FBlockChainGrid.FNodeNotifyEvents.Node.Bank.BlocksCount-1) then FBlockChainGrid.FBlockStart := -1;
+  if (FBlockChainGrid.FBlockEnd>=0) And (FBlockChainGrid.FBlockEnd<FBlockChainGrid.Node.Bank.BlocksCount) then begin
+    LBlockEnd := FBlockChainGrid.FBlockEnd
+  end else begin
+    if (FBlockChainGrid.FBlockStart>=0) And (FBlockChainGrid.FBlockStart+FBlockChainGrid.MaxBlocks<=FBlockChainGrid.Node.Bank.BlocksCount) then LBlockEnd := FBlockChainGrid.FBlockStart + FBlockChainGrid.MaxBlocks - 1
+    else LBlockEnd := FBlockChainGrid.Node.Bank.BlocksCount-1;
+  end;
+
+  if (FBlockChainGrid.FBlockStart>=0) And (FBlockChainGrid.FBlockStart<FBlockChainGrid.Node.Bank.BlocksCount) then LBlockStart := FBlockChainGrid.FBlockStart
+  else begin
+    if LBlockEnd>FBlockChainGrid.MaxBlocks then LBlockStart := LBlockEnd - FBlockChainGrid.MaxBlocks + 1
+    else LBlockStart := 0;
+  end;
+
+
+  Llist := TList<TBlockChainData>.Create;
+  try
+    DoUpdateBlockChainGrid(FBlockChainGrid.Node,Llist,LBlockStart,LBlockEnd);
+    if (Not Terminated) then begin
+      FBlockChainGrid.FBlockChainDataList.clear;
+      for i := 0 to Llist.Count-1 do begin
+        FBlockChainGrid.FBlockChainDataList.Add(Llist[i]);
       end;
-    end else begin
-      if AccountNumber<0 then begin
-        opc := TPCOperationsComp.Create(Nil);
-        try
-          opc.bank := Node.Bank;
-          If FBlockEnd<0 then begin
-            If Node.Bank.BlocksCount>0 then bend := Node.Bank.BlocksCount-1
-            else bend := 0;
-          end else bend := FBlockEnd;
-          if FBlockStart<0 then begin
-            if (bend > 300) then bstart := bend - 300
-            else bstart := 0;
-          end else bstart:= FBlockStart;
-          If bstart<0 then bstart := 0;
-          if bend>=Node.Bank.BlocksCount then bend:=Node.Bank.BlocksCount;
-          while (bstart<=bend) do begin
-            opr := CT_TOperationResume_NUL;
-            if (Node.Bank.Storage.LoadBlockChainBlock(opc,bend)) then begin
-              // Reward operation
-              OPR := CT_TOperationResume_NUL;
-              OPR.valid := true;
-              OPR.Block := bend;
-              OPR.time := opc.OperationBlock.timestamp;
-              OPR.AffectedAccount := bend * CT_AccountsPerBlock;
-              OPR.Amount := opc.OperationBlock.reward;
-              OPR.Fee := opc.OperationBlock.fee;
-              OPR.Balance := OPR.Amount+OPR.Fee;
-              OPR.OperationTxt := 'Blockchain reward';
-              FOperationsResume.Add(OPR);
-              // Reverse operations inside a block
-              for i := opc.Count - 1 downto 0 do begin
-                if TPCOperation.OperationToOperationResume(bend,opc.Operation[i],True,opc.Operation[i].SignerAccount,opr) then begin
-                  opr.NOpInsideBlock := i;
-                  opr.Block := bend;
-                  opr.time := opc.OperationBlock.timestamp;
-                  FOperationsResume.Add(opr);
-                end;
-              end;
-            end else break;
-            dec(bend);
-          end;
-        finally
-          opc.Free;
-        end;
+      if Assigned(FBlockChainGrid.DrawGrid) then begin
+        if Llist.Count>0 then FBlockChainGrid.DrawGrid.RowCount := Llist.Count+1
+        else FBlockChainGrid.DrawGrid.RowCount := 2;
+        FBlockChainGrid.FDrawGrid.Invalidate;
+      end;
+    end;
+  finally
+    Llist.Free;
+  end;
+end;
 
-      end else begin
-        list := TList<Cardinal>.Create;
-        Try
-          Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
-          for i := list.Count - 1 downto 0 do begin
-            Op := Node.Operations.OperationsHashTree.GetOperation((list[i]));
-            If TPCOperation.OperationToOperationResume(0,Op,False,AccountNumber,OPR) then begin
-              OPR.NOpInsideBlock := i;
-              OPR.Block := Node.Operations.OperationBlock.block;
-              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
-              FOperationsResume.Add(OPR);
-            end;
-          end;
-        Finally
-          list.Free;
-        End;
-        Node.GetStoredOperationsFromAccount(FOperationsResume,AccountNumber,100,0,5000);
+constructor TBlockChainGridUpdateThread.Create(ABlockChainGrid : TBlockChainGrid);
+begin
+  FBlockChainGrid := ABlockChainGrid;
+  inherited Create(True);
+  FreeOnTerminate := False;
+  Suspended := False;
+end;
+
+procedure TBlockChainGridUpdateThread.DoUpdateBlockChainGrid(ANode: TNode; var AList: TList<TBlockChainData>; ABlockStart, ABlockEnd : Int64);
+Var opc : TPCOperationsComp;
+  bcd : TBlockChainData;
+  opb : TOperationBlock;
+  bn : TBigNum;
+begin
+  opc := TPCOperationsComp.Create(Nil);
+  try
+    opc.bank := ANode.Bank;
+    while (ABlockStart<=ABlockEnd) and (Not Terminated) do begin
+      bcd := CT_TBlockChainData_NUL;
+      opb := ANode.Bank.SafeBox.Block(ABlockEnd).blockchainInfo;
+      bcd.Block:=opb.block;
+      bcd.Timestamp := opb.timestamp;
+      bcd.BlockProtocolVersion := opb.protocol_version;
+      bcd.BlockProtocolAvailable := opb.protocol_available;
+      bcd.Reward := opb.reward;
+      bcd.Fee := opb.fee;
+      bcd.Target := opb.compact_target;
+      bn := ANode.Bank.SafeBox.CalcBlockHashRateInHs(bcd.Block,FBlockChainGrid.HashRateAverageBlocksCount);
+      try
+        bcd.HashRateHs := bn.Value;
+        bcd.HashRateKhs := bn.Divide(1000).Value;
+      finally
+        bn.Free;
+      end;
+      bn := TBigNum.TargetToHashRate(opb.compact_target);
+      Try
+        bcd.HashRateTargetHs := bn.Value / (CT_NewLineSecondsAvg);
+        bcd.HashRateTargetKhs := bn.Divide(1000).Divide(CT_NewLineSecondsAvg).Value;
+      finally
+        bn.Free;
+      end;
+      bcd.MinerPayload := opb.block_payload;
+      bcd.PoW := opb.proof_of_work;
+      bcd.SafeBoxHash := opb.initial_safe_box_hash;
+      bcd.AccumulatedWork := ANode.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
+      if (Not Terminated) then begin
+        If (ANode.Bank.LoadOperations(opc,ABlockEnd)) then begin
+          bcd.OperationsCount := opc.Count;
+          bcd.Volume := opc.OperationsHashTree.TotalAmount + opc.OperationsHashTree.TotalFee;
+        end;
+        bcd.TimeAverage200:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,200);
+        bcd.TimeAverage150:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,150);
+        bcd.TimeAverage100:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,100);
+        bcd.TimeAverage75:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,75);
+        bcd.TimeAverage50:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,50);
+        bcd.TimeAverage25:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,25);
+        bcd.TimeAverage10:=ANode.Bank.GetTargetSecondsAverage(bcd.Block,10);
+        AList.Add(bcd);
+        if (ABlockEnd>0) then dec(ABlockEnd) else Break;
       end;
     end;
-  Finally
-    InitGrid;
-  End;
+  finally
+    opc.Free;
+  end;
 end;
 
 { TBlockChainGrid }
@@ -1000,16 +1184,23 @@ begin
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
   FHashRateAverageBlocksCount := 50;
-  SetLength(FBlockChainDataArray,0);
+  FBlockChainDataList := TList<TBlockChainData>.Create;
   FShowTimeAverageColumns:=False;
   FHashRateAs:={$IFDEF PRODUCTION}hr_Giga{$ELSE}hr_Mega{$ENDIF};
+  FBlockChainGridUpdateThread := Nil;
 end;
 
 destructor TBlockChainGrid.Destroy;
 begin
+  If Assigned(FBlockChainGridUpdateThread) then begin
+    FBlockChainGridUpdateThread.Terminate;
+    FBlockChainGridUpdateThread.WaitFor;
+    FreeAndNil(FBlockChainGridUpdateThread);
+  end;
   FNodeNotifyEvents.OnBlocksChanged := Nil;
   FNodeNotifyEvents.Node := Nil;
   FreeAndNil(FNodeNotifyEvents);
+  FreeAndNil(FBlockChainDataList);
   inherited;
 end;
 
@@ -1072,9 +1263,7 @@ Var s : String;
   deviation : Real;
   hr_base : Int64;
 begin
-  {.$IFDEF FPC}
   DrawGrid.Canvas.Font.Color:=clBlack;
-  {.$ENDIF}
   if (ARow=0) then begin
     // Header
     case ACol of
@@ -1113,8 +1302,8 @@ begin
     else DrawGrid.Canvas.Brush.Color := clWindow;
     DrawGrid.Canvas.FillRect(Rect);
     InflateRect(Rect,-2,-1);
-    if ((ARow-1)<=High(FBlockChainDataArray)) then begin
-      bcd := FBlockChainDataArray[ARow-1];
+    if ((ARow-1)<FBlockChainDataList.Count) then begin
+      bcd := FBlockChainDataList[ARow-1];
       if ACol=0 then begin
         s := IntToStr(bcd.Block);
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter]);
@@ -1133,7 +1322,7 @@ begin
       end else if ACol=3 then begin
         if bcd.Volume>=0 then begin
           s := TAccountComp.FormatMoney(bcd.Volume);
-          if FBlockChainDataArray[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
+          if FBlockChainDataList[ARow-1].Volume>0 then DrawGrid.Canvas.Font.Color := ClGreen
           else DrawGrid.Canvas.Font.Color := clGrayText;
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         end else begin
@@ -1143,7 +1332,7 @@ begin
         end;
       end else if ACol=4 then begin
         s := TAccountComp.FormatMoney(bcd.Reward);
-        if FBlockChainDataArray[ARow-1].Reward>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        if FBlockChainDataList[ARow-1].Reward>0 then DrawGrid.Canvas.Font.Color := ClGreen
         else DrawGrid.Canvas.Font.Color := clGrayText;
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
       end else if ACol=5 then begin
@@ -1285,94 +1474,13 @@ end;
 
 
 procedure TBlockChainGrid.UpdateBlockChainGrid;
-Var nstart,nend : Cardinal;
-  opc : TPCOperationsComp;
-  bcd : TBlockChainData;
-  i : Integer;
-  opb : TOperationBlock;
-  bn : TBigNum;
 begin
-  if (FBlockStart>FBlockEnd) And (FBlockStart>=0) then FBlockEnd := -1;
-  if (FBlockEnd>=0) And (FBlockEnd<FBlockStart) then FBlockStart:=-1;
-
-  if Not Assigned(FNodeNotifyEvents.Node) then exit;
-
-  if FBlockStart>(FNodeNotifyEvents.Node.Bank.BlocksCount-1) then FBlockStart := -1;
-
-  try
-    if Node.Bank.BlocksCount<=0 then begin
-      SetLength(FBlockChainDataArray,0);
-      exit;
-    end;
-    if (FBlockEnd>=0) And (FBlockEnd<Node.Bank.BlocksCount) then begin
-      nend := FBlockEnd
-    end else begin
-      if (FBlockStart>=0) And (FBlockStart+MaxBlocks<=Node.Bank.BlocksCount) then nend := FBlockStart + MaxBlocks - 1
-      else nend := Node.Bank.BlocksCount-1;
-    end;
-
-    if (FBlockStart>=0) And (FBlockStart<Node.Bank.BlocksCount) then nstart := FBlockStart
-    else begin
-      if nend>MaxBlocks then nstart := nend - MaxBlocks + 1
-      else nstart := 0;
-    end;
-    SetLength(FBlockChainDataArray,nend - nstart +1);
-    opc := TPCOperationsComp.Create(Nil);
-    try
-      opc.bank := Node.Bank;
-      while (nstart<=nend) do begin
-        i := length(FBlockChainDataArray) - (nend-nstart+1);
-        bcd := CT_TBlockChainData_NUL;
-        opb := Node.Bank.SafeBox.Block(nend).blockchainInfo;
-        bcd.Block:=opb.block;
-        bcd.Timestamp := opb.timestamp;
-        bcd.BlockProtocolVersion := opb.protocol_version;
-        bcd.BlockProtocolAvailable := opb.protocol_available;
-        bcd.Reward := opb.reward;
-        bcd.Fee := opb.fee;
-        bcd.Target := opb.compact_target;
-        bn := Node.Bank.SafeBox.CalcBlockHashRateInHs(bcd.Block,HashRateAverageBlocksCount);
-        try
-          bcd.HashRateHs := bn.Value;
-          bcd.HashRateKhs := bn.Divide(1000).Value;
-        finally
-          bn.Free;
-        end;
-        bn := TBigNum.TargetToHashRate(opb.compact_target);
-        Try
-          bcd.HashRateTargetHs := bn.Value / (CT_NewLineSecondsAvg);
-          bcd.HashRateTargetKhs := bn.Divide(1000).Divide(CT_NewLineSecondsAvg).Value;
-        finally
-          bn.Free;
-        end;
-        bcd.MinerPayload := opb.block_payload;
-        bcd.PoW := opb.proof_of_work;
-        bcd.SafeBoxHash := opb.initial_safe_box_hash;
-        bcd.AccumulatedWork := Node.Bank.SafeBox.Block(bcd.Block).AccumulatedWork;
-        if (Node.Bank.LoadOperations(opc,nend)) then begin
-          bcd.OperationsCount := opc.Count;
-          bcd.Volume := opc.OperationsHashTree.TotalAmount + opc.OperationsHashTree.TotalFee;
-        end;
-        bcd.TimeAverage200:=Node.Bank.GetTargetSecondsAverage(bcd.Block,200);
-        bcd.TimeAverage150:=Node.Bank.GetTargetSecondsAverage(bcd.Block,150);
-        bcd.TimeAverage100:=Node.Bank.GetTargetSecondsAverage(bcd.Block,100);
-        bcd.TimeAverage75:=Node.Bank.GetTargetSecondsAverage(bcd.Block,75);
-        bcd.TimeAverage50:=Node.Bank.GetTargetSecondsAverage(bcd.Block,50);
-        bcd.TimeAverage25:=Node.Bank.GetTargetSecondsAverage(bcd.Block,25);
-        bcd.TimeAverage10:=Node.Bank.GetTargetSecondsAverage(bcd.Block,10);
-        FBlockChainDataArray[i] := bcd;
-        if (nend>0) then dec(nend) else break;
-      end;
-    finally
-      opc.Free;
-    end;
-  finally
-    if Assigned(FDrawGrid) then begin
-      if Length(FBlockChainDataArray)>0 then FDrawGrid.RowCount := length(FBlockChainDataArray)+1
-      else FDrawGrid.RowCount := 2;
-      FDrawGrid.Invalidate;
-    end;
+  If Assigned(FBlockChainGridUpdateThread) then begin
+    FBlockChainGridUpdateThread.Terminate;
+    FBlockChainGridUpdateThread.WaitFor;
+    FreeAndNil(FBlockChainGridUpdateThread);
   end;
+  FBlockChainGridUpdateThread := TBlockChainGridUpdateThread.Create(Self);
 end;
 
 end.