Browse Source

Preparing 1.0.6

Changes for preventing some memory leaks and also to be nearly compiled
with Lazarus
PascalCoin 9 years ago
parent
commit
042c232270

+ 3 - 1
PascalCoinWallet.dpr

@@ -28,7 +28,9 @@ uses
   UFRMNewPrivateKeyType in 'Units\Forms\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
   UAES in 'Units\Utils\UAES.pas',
   UFRMPayloadDecoder in 'Units\Forms\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
-  UFRMNodesIp in 'Units\Forms\UFRMNodesIp.pas' {FRMNodesIp};
+  UFRMNodesIp in 'Units\Forms\UFRMNodesIp.pas' {FRMNodesIp},
+  UDBGridUtils in 'Units\Utils\UDBGridUtils.pas',
+  UTCPIP in 'Units\PascalCoin\UTCPIP.pas';
 
 {$R *.res}
 

BIN
PascalCoinWallet.res


+ 1 - 1
Units/Forms/UFRMNodesIp.pas

@@ -29,7 +29,7 @@ type
 implementation
 
 uses
-  UNetProtocol, UFRMWallet, UNode;
+  UNetProtocol, UNode, UConst;
 
 {$R *.dfm}
 

+ 1 - 1
Units/Forms/UFRMPascalCoinWalletConfig.pas

@@ -66,7 +66,7 @@ type
 
 implementation
 
-uses UConst, UAccounts, UFRMWallet, ULog, UCrypto;
+uses UConst, UAccounts, ULog, UCrypto, UMiner;
 
 {$R *.dfm}
 

+ 37 - 1
Units/Forms/UFRMWallet.dfm

@@ -381,6 +381,10 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     object tsAccountsExplorer: TTabSheet
       Caption = 'Accounts Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
         Left = 380
         Top = 66
@@ -586,6 +590,10 @@ object FRMWallet: TFRMWallet
         TabOrder = 2
         object tsAccountOperations: TTabSheet
           Caption = 'Operations of selected Account'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
             Left = 0
             Top = 0
@@ -605,6 +613,10 @@ object FRMWallet: TFRMWallet
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected accounts for massive operations'
           ImageIndex = 1
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgSelectedAccounts: TDrawGrid
             Left = 41
             Top = 31
@@ -788,6 +800,10 @@ object FRMWallet: TFRMWallet
     object tsPendingOperations: TTabSheet
       Caption = 'Pending Operations'
       ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object dgPendingOperations: TDrawGrid
         Left = 0
         Top = 86
@@ -835,6 +851,10 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
       Caption = 'BlockChain Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel2: TPanel
         Left = 0
         Top = 0
@@ -918,6 +938,10 @@ object FRMWallet: TFRMWallet
     object tsOperations: TTabSheet
       Caption = 'Operations Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel1: TPanel
         Left = 0
         Top = 0
@@ -1018,6 +1042,10 @@ object FRMWallet: TFRMWallet
     object tsLogs: TTabSheet
       Caption = 'Logs'
       ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object pnlTopLogs: TPanel
         Left = 0
         Top = 0
@@ -1047,6 +1075,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         891
         420)
@@ -1104,6 +1136,10 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       ImageIndex = 6
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         891
         420)
@@ -1332,7 +1368,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Top = 180
     Bitmap = {
-      494C010102000800000110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010102000800080110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 8 - 8
Units/Forms/UFRMWallet.pas

@@ -19,7 +19,7 @@ uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, pngimage, ExtCtrls, ComCtrls, UWalletKeys, ShlObj, ADOInt, StdCtrls,
   ULog, DB, ADODB, Grids, DBGrids, DBCGrids, UAppParams,
-  UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
+  UBlockChain, UNode, DBCtrls, UGridUtils, UDBGridUtils, UMiner, UAccounts, Menus, ImgList,
   AppEvnts, UNetProtocol, UCrypto, Buttons;
 
 Const
@@ -469,7 +469,7 @@ begin
         nc := TNetConnection(lbNetconnections.Items.Objects[i]);
         if TNetData.NetData.ConnectionExistsAndActive(nc) then begin
           FNode.SendNodeMessage(nc,m,errors);
-          memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.Client.RemoteHost+':'+nc.Client.RemotePort+' > '+m);
+          memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.ClientRemoteAddr+' > '+m);
         end;
       end;
     end;
@@ -477,7 +477,7 @@ begin
     nc := TNetConnection(lbNetconnections.Items.Objects[lbNetconnections.ItemIndex]);
     if TNetData.NetData.ConnectionExistsAndActive(nc) then begin
       FNode.SendNodeMessage(nc,m,errors);
-      memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.Client.RemoteHost+':'+nc.Client.RemotePort+' > '+m);
+      memoMessages.Lines.Add(DateTimeToStr(now)+' Sent to '+nc.ClientRemoteAddr+' > '+m);
     end;
   end;
 
@@ -1339,11 +1339,11 @@ Var s : String;
 begin
   inc(FMessagesUnreadCount);
   if Assigned(NetConnection) then begin
-    s := DateTimeToStr(now)+' Message received from '+NetConnection.Client.RemoteHost+':'+NetConnection.Client.RemotePort;
-    memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.Client.RemoteHost+':'+NetConnection.Client.RemotePort+' Length '+inttostr(Length(MessageData))+' bytes');
+    s := DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr;
+    memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr+' Length '+inttostr(Length(MessageData))+' bytes');
     memoMessages.Lines.Add('RECEIVED> '+MessageData);
     if FAppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
-      s := DateTimeToStr(now)+' Message from '+NetConnection.Client.RemoteHost+':'+NetConnection.Client.RemotePort+#10+
+      s := DateTimeToStr(now)+' Message from '+NetConnection.ClientRemoteAddr+#10+
          'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
       if TCrypto.IsHumanReadable(MessageData) then begin
          s := s + MessageData;
@@ -1612,11 +1612,11 @@ begin
         if NC.Connected then begin
           if NC is TNetServerClient then begin
             if Not NC.IsMyselfServer then begin
-              lbNetConnections.Items.AddObject(Format('Client: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]),NC);
+              lbNetConnections.Items.AddObject(Format('Client: IP:%s',[NC.ClientRemoteAddr]),NC);
             end;
           end else begin
             if Not NC.IsMyselfServer then begin
-              lbNetConnections.Items.AddObject(Format('Server: IP:%s:%s',[NC.Client.RemoteHost,NC.Client.RemotePort]),NC);
+              lbNetConnections.Items.AddObject(Format('Server: IP:%s',[NC.ClientRemoteAddr]),NC);
             end;
           end;
         end;

+ 5 - 5
Units/PascalCoin/UAccounts.pas

@@ -16,7 +16,7 @@ unit UAccounts;
 interface
 
 uses
-  Classes, UConst, Windows, UCrypto;
+  Classes, UConst, Windows, UCrypto, SyncObjs;
 
 Type
   TAccountKey = TECDSA_Public;
@@ -105,7 +105,7 @@ Type
     FTotalBalance: Int64;
     FTotalFee: Int64;
     FSafeBoxHash : TRawBytes;
-    FLock: TRTLCriticalSection; // Thread safe
+    FLock: TCriticalSection; // Thread safe
     FIsLocked : Boolean;
     Procedure SetAccount(account_number : Cardinal; newAccountkey: TAccountKey; newBalance: UInt64; newN_operation: Cardinal);
     Procedure AccountKeyListAddAccounts(Const AccountKey : TAccountKey; accounts : Array of Cardinal);
@@ -728,7 +728,7 @@ end;
 
 constructor TPCSafeBox.Create;
 begin
-  InitializeCriticalSection(FLock);
+  FLock := TCriticalSection.Create;
   FIsLocked := false;
   FBlockAccountsList := TList.Create;
   FListOfOrderedAccountKeysList := TList.Create;
@@ -744,7 +744,7 @@ begin
   end;
   FreeAndNil(FBlockAccountsList);
   FreeAndNil(FListOfOrderedAccountKeysList);
-  DeleteCriticalSection(Flock);
+  FLock.Free;
   inherited;
 end;
 
@@ -752,7 +752,7 @@ procedure TPCSafeBox.EndThreadSave;
 begin
   if Not FIsLocked then raise Exception.Create('Is not locked');
   FIsLocked := False;
-  LeaveCriticalSection(FLock);
+  FLock.Release;
 end;
 
 function TPCSafeBox.LoadFromStream(Stream : TStream; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;

+ 60 - 75
Units/PascalCoin/UBlockChain.pas

@@ -16,7 +16,7 @@
 interface
 
 uses
-  Classes, UCrypto, UAccounts, Windows, ULog, UThread;
+  Classes, UCrypto, UAccounts, Windows, ULog, UThread, SyncObjs;
 
 
 Type
@@ -249,25 +249,28 @@ Type
   private
     FOrphan: TOrphan;
     FBank : TPCBank;
+    FReadOnly: Boolean;
     procedure SetBank(const Value: TPCBank);
   protected
     procedure SetOrphan(const Value: TOrphan); virtual;
+    procedure SetReadOnly(const Value: Boolean); virtual;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
-    Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan) : Boolean; virtual; abstract;
+    Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; virtual; abstract;
     Function DoSaveBank : Boolean; virtual; abstract;
     Function DoRestoreBank(max_block : Int64) : Boolean; virtual; abstract;
-    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); virtual; abstract;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); virtual; abstract;
     Function BlockExists(Block : Cardinal) : Boolean; virtual; abstract;
   public
     Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
-    Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan) : Boolean;
-    Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan);
+    Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
+    Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Function SaveBank : Boolean;
     Function RestoreBank(max_block : Int64) : Boolean;
     Constructor Create(AOwner : TComponent); Override;
     Property Orphan : TOrphan read FOrphan write SetOrphan;
+    Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     Property Bank : TPCBank read FBank write SetBank;
     Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
   End;
@@ -284,7 +287,7 @@ Type
     FActualTargetHash: TRawBytes;
     FIsRestoringFromFile: Boolean;
     FOnLog: TPCBankLog;
-    FBankLock: TRTLCriticalSection;
+    FBankLock: TCriticalSection;
     FNotifyList : TList;
     FStorageClass: TStorageClass;
     function GetStorage: TStorage;
@@ -380,10 +383,12 @@ begin
           exit;
         end;
       end else begin
-        // Check if valid Zero block
-        if Not (AnsiSameText(TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work),CT_Zero_Block_Proof_of_work_in_Hexa)) then begin
-          errors := 'Zero block not valid, Proof of Work invalid: '+TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work)+'<>'+CT_Zero_Block_Proof_of_work_in_Hexa;
-          exit;
+        if (CT_Zero_Block_Proof_of_work_in_Hexa<>'') then begin
+          // Check if valid Zero block
+          if Not (AnsiSameText(TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work),CT_Zero_Block_Proof_of_work_in_Hexa)) then begin
+            errors := 'Zero block not valid, Proof of Work invalid: '+TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work)+'<>'+CT_Zero_Block_Proof_of_work_in_Hexa;
+            exit;
+          end;
         end;
       end;
       if (Operations.OperationBlock.compact_target <> GetActualCompactTargetHash) then begin
@@ -440,7 +445,7 @@ begin
         NewLog(Operations, lterror, 'Invalid new block '+inttostr(Operations.OperationBlock.block)+': ' + errors);
     End;
   Finally
-    LeaveCriticalSection(FBankLock);
+    FBankLock.Release;
   End;
   if Result then begin
     for i := 0 to FNotifyList.Count - 1 do begin
@@ -488,7 +493,7 @@ begin
   inherited;
   FStorage := Nil;
   FStorageClass := Nil;
-  InitializeCriticalSection(FBankLock);
+  FBankLock := TCriticalSection.Create;
   FIsRestoringFromFile := False;
   FOnLog := Nil;
   FSafeBox := TPCSafeBox.Create;
@@ -502,7 +507,7 @@ var step : String;
 begin
   Try
     step := 'Deleting critical section';
-    DeleteCriticalSection(FBankLock);
+    FBankLock.Free;
     step := 'Clear';
     Clear;
     step := 'Destroying LastBlockCache';
@@ -554,7 +559,7 @@ begin
             if Storage.LoadBlockChainBlock(Operations,BlocksCount) then begin
               if Not AddNewBlockChainBlock(Operations,newBlock,errors) then begin
                 NewLog(Operations, lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
-                Storage.DeleteBlockChainBlocks(BlocksCount,Storage.Orphan);
+                Storage.DeleteBlockChainBlocks(BlocksCount);
                 break;
               end else begin
                 Storage.SaveBank;
@@ -570,7 +575,7 @@ begin
       FIsRestoringFromFile := False;
     end;
   finally
-    LeaveCriticalSection(FBankLock);
+    FBankLock.Release;
   end;
 end;
 
@@ -602,23 +607,6 @@ begin
     FActualTargetHash := GetNewTarget(tsTeorical, tsReal,TargetFromCompact(FLastOperationBlock.compact_target));
   end;
   Result := FActualTargetHash;
-
-  exit;
-
-  if (BlocksCount <= CT_CalcNewTargetBlocksAverage) then begin
-    // Important: CT_MinCompactTarget is applied for blocks 0 until ((CT_CalcNewDifficulty*2)-1)
-    FActualTargetHash := TargetFromCompact(CT_MinCompactTarget);
-  end else begin
-    if (BlocksCount MOD CT_CalcNewTargetBlocksAverage) = 0 then begin
-      // Calc new target!
-      ts1 := SafeBox.Block(BlocksCount-1).timestamp;
-      ts2 := SafeBox.Block(BlocksCount-CT_CalcNewTargetBlocksAverage-1).timestamp;
-      tsTeorical := (CT_CalcNewTargetBlocksAverage * CT_NewLineSecondsAvg);
-      tsReal := (ts1 - ts2);
-      FActualTargetHash := GetNewTarget(tsTeorical, tsReal,TargetFromCompact(FLastOperationBlock.compact_target));
-    end;
-  end;
-  Result := FActualTargetHash;
 end;
 
 function TPCBank.GetActualTargetSecondsAverage(BackBlocks: Cardinal): Real;
@@ -751,7 +739,7 @@ begin
       // Initialize new target hash:
       FActualTargetHash := GetActualTargetHash;
     finally
-      LeaveCriticalSection(FBankLock);
+      FBankLock.Release;
     end;
     for i := 0 to FNotifyList.Count - 1 do begin
       TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
@@ -771,7 +759,7 @@ begin
       Result := Storage.LoadBlockChainBlock(Operations,Block);
     end;
   finally
-    LeaveCriticalSection(FBankLock);
+    FBankLock.Release;
   end;
 end;
 
@@ -1270,34 +1258,30 @@ begin
   Stream.Read(c, 4);
   // c = operations count
   for i := 1 to c do begin
-    bcop := Nil;
-    try
-      errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
-      if Stream.Size - Stream.Position < 4 then exit;
-      Stream.Read(OpType, 4);
-      j := IndexOfOperationClassByOpType(OpType);
-      if j >= 0 then
-        OpClass := _OperationsClass[j]
-      else
-        OpClass := Nil;
-      if Not Assigned(OpClass) then begin
-        errors := errors + ' optype not valid:' + InttoHex(OpType, 4);
-        exit;
-      end;
-      errors := errors + ' Operation:'+OpClass.ClassName;
-      bcop := OpClass.Create;
+    errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
+    if Stream.Size - Stream.Position < 4 then exit;
+    Stream.Read(OpType, 4);
+    j := IndexOfOperationClassByOpType(OpType);
+    if j >= 0 then
+      OpClass := _OperationsClass[j]
+    else
+      OpClass := Nil;
+    if Not Assigned(OpClass) then begin
+      errors := errors + ' optype not valid:' + InttoHex(OpType, 4);
+      exit;
+    end;
+    errors := 'Invalid operation ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
+    bcop := OpClass.Create;
+    Try
       if not bcop.LoadFromStream(Stream) then begin
-        bcop.Free;
         exit;
       end;
       if Not AddOperation(false,bcop, errors2) then begin
         errors := errors + ' '+errors2+' '+bcop.ToString;
-        bcop.Free;
         exit;
       end;
-    Except
+    Finally
       FreeAndNil(bcop);
-      raise ;
     end;
   end;
   // Validation control:
@@ -1640,31 +1624,19 @@ end;
 procedure TOperationsHashTree.CopyFromHashTree(Sender: TOperationsHashTree);
 Var i : Integer;
   lme, lsender : TList;
-  opsender,op : TPCOperation;
-  ms : TMemoryStream;
+  opsender : TPCOperation;
 begin
   if (Sender = Self) then begin
     exit;
   end;
-
   ClearHastThree;
   lme := FHashTreeOperations.LockList;
   lsender := Sender.FHashTreeOperations.LockList;
   try
-    ms := TMemoryStream.Create;
-    Try
-      for i := 0 to lsender.Count - 1 do begin
-        opsender := lsender[i];
-        op := TPCOperation(opsender.NewInstance);
-        ms.Size:=0;
-        opsender.SaveToStream(ms);
-        ms.Position:=0;
-        op.LoadFromStream(ms);
-        InternalAddOperationToHashTree(lme,op);
-      end;
-    Finally
-      ms.Free;
-    End;
+    for i := 0 to lsender.Count - 1 do begin
+      opsender := lsender[i];
+      InternalAddOperationToHashTree(lme,opsender);
+    end;
   finally
     FHashTreeOperations.UnlockList;
     Sender.FHashTreeOperations.UnlockList;
@@ -1681,6 +1653,7 @@ destructor TOperationsHashTree.Destroy;
 begin
   ClearHastThree;
   FreeAndNil(FHashTreeOperations);
+  SetLength(FHashTree,0);
   inherited;
 end;
 
@@ -1761,11 +1734,13 @@ constructor TStorage.Create(AOwner: TComponent);
 begin
   inherited;
   FOrphan := '';
+  FReadOnly := false;
 end;
 
-procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
+procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
 begin
-  DoDeleteBlockChainBlocks(StartingDeleteBlock,Orphan);
+  if ReadOnly then raise Exception.Create('Cannot delete blocks because is ReadOnly');
+  DoDeleteBlockChainBlocks(StartingDeleteBlock);
 end;
 
 function TStorage.LoadBlockChainBlock(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
@@ -1773,9 +1748,12 @@ begin
    Result := DoLoadBlockChain(Operations,Block);
 end;
 
-function TStorage.MoveBlockChainBlocks(StartBlock: Cardinal; const DestOrphan: TOrphan): Boolean;
+function TStorage.MoveBlockChainBlocks(StartBlock: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
 begin
-  Result := DoMoveBlockChain(StartBlock,DestOrphan);
+  if Assigned(DestStorage) then begin
+    if DestStorage.ReadOnly then raise Exception.Create('Cannot move blocks because is ReadOnly');
+  end else if ReadOnly then raise Exception.Create('Cannot move blocks from myself because is ReadOnly');
+  Result := DoMoveBlockChain(StartBlock,DestOrphan,DestStorage);
 end;
 
 function TStorage.RestoreBank(max_block: Int64): Boolean;
@@ -1788,6 +1766,7 @@ begin
   Result := true;
   if (Bank.BlocksCount MOD CT_BankToDiskEveryNBlocks)<>0 then exit; // No bank!
   Try
+    if ReadOnly then raise Exception.Create('Cannot save because is ReadOnly');
     Result := DoSaveBank;
   Except
     On E:Exception do begin
@@ -1800,6 +1779,7 @@ end;
 function TStorage.SaveBlockChainblock(Operations: TPCOperationsComp): Boolean;
 begin
   Try
+    if ReadOnly then raise Exception.Create('Cannot save because is ReadOnly');
     Result := DoSaveBlockChain(Operations);
   Except
     On E:Exception do begin
@@ -1819,6 +1799,11 @@ begin
   FOrphan := Value;
 end;
 
+procedure TStorage.SetReadOnly(const Value: Boolean);
+begin
+  FReadOnly := Value;
+end;
+
 { TPCOperation }
 
 class function TPCOperation.IsReadablePayload(const Payload: TRawBytes): Boolean;

+ 29 - 8
Units/PascalCoin/UConst.pas

@@ -23,30 +23,31 @@ Const
     '(c) Albert Molina - Genesis block at same time than BitCoin Block 424720 Hash 000000000000000001cc41ff7846264718ef0a15f97f532a98277bd5f6820b89';
 
   CT_Zero_Block_Proof_of_work_in_Hexa =
-    '00000003A29C32E84A539ADE24397D41D30116A6FAFEC17B7D9CED68A4238C92';
+    {$IFDEF PRODUCTION}'00000003A29C32E84A539ADE24397D41D30116A6FAFEC17B7D9CED68A4238C92'{$ELSE}{$IFDEF TESTNET}''{$ENDIF}{$ENDIF};
 
 
-  CT_NetServer_Port = 4004;
+  CT_NetServer_Port = {$IFDEF PRODUCTION}4004{$ELSE}{$IFDEF TESTNET}4104{$ENDIF}{$ENDIF};
   CT_AccountsPerBlock = 5;
 
-  CT_NewLineSecondsAvg: Cardinal = 300; // 60*5=300 seconds -> 5 minutes avg
+  CT_NewLineSecondsAvg: Cardinal = {$IFDEF PRODUCTION}300{$ELSE}{$IFDEF TESTNET}30{$ENDIF}{$ENDIF};
+    // 60*5=300 seconds -> 5 minutes avg
     //   -> 1 day = 86400 seconds -> 1 year = 31536000 seconds (aprox)
     //   Each year = 105120 new blocks (aprox)
     //   -> *5 accounts per block = 525600 new accounts each year (aprox)
 
   CT_FirstReward: UInt64 = 1000000; // 4 decimals... First reward = 100,0000
   CT_MinReward: UInt64 = 10000; // 4 decimals... Min reward = 1,0000
-  CT_NewLineRewardDecrease: Cardinal = 420480; // Avg 4 year
+  CT_NewLineRewardDecrease: Cardinal = {$IFDEF PRODUCTION}420480{$ELSE}{$IFDEF TESTNET}600{$ENDIF}{$ENDIF}; // Avg 4 year
 
   CT_WaitNewBlocksBeforeTransaction = 100;
 
-  CT_RecoverFoundsWaitInactiveCount = 420480;  // After 4 years... if an account has no operations, money will be a reward for a miner!
+  CT_RecoverFoundsWaitInactiveCount = {$IFDEF PRODUCTION}420480{$ELSE}{$IFDEF TESTNET}500{$ENDIF}{$ENDIF};  // After 4 years... if an account has no operations, money will be a reward for a miner!
 
   CT_MaxTransactionAmount = 1000000000000;
   CT_MaxTransactionFee = 100000000;
   CT_MaxWalletAmount = 10000000000000;
   //
-  CT_MinCompactTarget: Cardinal = $19000000; // First compact target of block 0
+  CT_MinCompactTarget: Cardinal = {$IFDEF PRODUCTION}$19000000{$ELSE}{$IFDEF TESTNET}$16000000{$ENDIF}{$ENDIF}; // First compact target of block 0
 
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
   CT_MaxBlock : Cardinal = $FFFFFFFF;
@@ -78,12 +79,32 @@ Const
   CT_Op_Changekey = $02;
   CT_Op_Recover = $03;
 
-  CT_ClientAppVersion : AnsiString = '1.0.5';
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.0.5'{$ELSE}{$IFDEF TESTNET}'TESTNET'{$ENDIF}{$ENDIF};
 
-  CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.ddns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
+  CT_Discover_IPs =  {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.ddns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us'{$ELSE}{$IFDEF TESTNET}''{$ENDIF}{$ENDIF};
 
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
 
+  // App Params
+  CT_PARAM_GridAccountsStream = 'GridAccountsStream';
+  CT_PARAM_GridAccountsPos = 'GridAccountsPos';
+  CT_PARAM_DefaultFee = 'DefaultFee';
+  CT_PARAM_InternetServerPort = 'InternetServerPort';
+  CT_PARAM_AutomaticMineWhenConnectedToNodes = 'AutomaticMineWhenConnectedToNodes';
+  CT_PARAM_MinerPrivateKeyType = 'MinerPrivateKeyType';
+  CT_PARAM_MinerPrivateKeySelectedPublicKey = 'MinerPrivateKeySelectedPublicKey';
+  CT_PARAM_SaveLogFiles = 'SaveLogFiles';
+  CT_PARAM_SaveDebugLogs = 'SaveDebugLogs';
+  CT_PARAM_ShowLogs = 'ShowLogs';
+  CT_PARAM_MinerName = 'MinerName';
+  CT_PARAM_FirstTime = 'FirstTime';
+  CT_PARAM_ShowModalMessages = 'ShowModalMessages';
+  CT_PARAM_MaxCPUs = 'MaxCPUs';
+  CT_PARAM_PeerCache = 'PeerCache';
+  CT_PARAM_TryToConnectOnlyWithThisFixedServers = 'TryToConnectOnlyWithFixedServers';
+
+
+
 implementation
 
 end.

+ 8 - 6
Units/PascalCoin/UCrypto.pas

@@ -345,7 +345,6 @@ end;
 
 class function TCrypto.DoSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
 Var PS : PAnsiChar;
-  PC : PAnsiChar;
 begin
   SetLength(Result,32);
   PS := @Result[1];
@@ -358,7 +357,6 @@ begin
   SetLength(Result,32);
   PS := @Result[1];
   SHA256(PAnsiChar(TheMessage),Length(TheMessage),PS);
-  exit;
 end;
 
 class function TCrypto.ECDSASign(Key: PEC_KEY; const digest: AnsiString): TECDSA_SIG;
@@ -626,10 +624,14 @@ Var n : TBigNum;
   ctx : PBN_CTX;
 begin
   n := TBigNum.Create(int);
-  ctx := BN_CTX_new;
-  if BN_mul(FBN,FBN,n.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
-  BN_CTX_free(ctx);
-  Result := Self;
+  Try
+    ctx := BN_CTX_new;
+    if BN_mul(FBN,FBN,n.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
+    Result := Self;
+  Finally
+    BN_CTX_free(ctx);
+    n.Free;
+  End;
 end;
 
 function TBigNum.RShift(nbits: Integer): TBigNum;

+ 4 - 4
Units/PascalCoin/UDBStorage.pas

@@ -123,8 +123,8 @@ Type
   protected
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
-    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan) : Boolean; override;
-    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); override;
+    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; override;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); override;
     Function DoSaveBank : Boolean; override;
     Function DoRestoreBank(max_block : Int64) : Boolean; override;
     Function BlockExists(Block : Cardinal) : Boolean; override;
@@ -297,7 +297,7 @@ begin
   inherited;
 end;
 
-procedure TDBStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
+procedure TDBStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
 Var ds : TADOQuery;
   whereorphan : AnsiString;
 begin
@@ -366,7 +366,7 @@ begin
   End;
 end;
 
-function TDBStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan): Boolean;
+function TDBStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
 Var whereorphan : AnsiString;
   setvalue : AnsiString;
 begin

+ 459 - 160
Units/PascalCoin/UFileStorage.pas

@@ -16,152 +16,232 @@ unit UFileStorage;
 interface
 
 uses
-  Classes, UBlockChain;
+  Classes, UBlockChain, Windows, SyncObjs;
 
 Type
+  TBlockHeader = Record
+    BlockNumber : Cardinal;
+    StreamBlockRelStartPos : Int64;
+    BlockSize : Cardinal;
+  end; // 16 bytes
+
   TFileStorage = Class(TStorage)
   private
-    FBaseDataFolder : AnsiString;
-    FIsRestoring : Boolean;
+    FStorageLock : TCriticalSection;
+    FBlockChainStream : TFileStream;
+    FStreamFirstBlockNumber : Cardinal;
+    FBlockHeadersFirstBytePosition : Array of Int64;
+    FDatabaseFolder: AnsiString;
+    Function StreamReadBlockHeader(Stream: TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block: Cardinal; var BlockHeader : TBlockHeader): Boolean;
+    Function StreamBlockRead(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
+    Function StreamBlockSave(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
     Function GetFolder(Const AOrphan : TOrphan): AnsiString;
+    Function GetBlockHeaderFirstBytePosition(Stream : TStream; Block : Cardinal; var StreamBlockHeaderStartPos : Int64; var BlockHeaderFirstBlock : Cardinal) : Boolean;
+    Function GetBlockHeaderFixedSize : Int64;
+    procedure SetDatabaseFolder(const Value: AnsiString);
+    Procedure ClearStream;
   protected
+    procedure SetReadOnly(const Value: Boolean); override;
+    procedure SetOrphan(const Value: TOrphan); override;
     Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; override;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
-    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan) : Boolean; override;
+    Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; override;
     Function DoSaveBank : Boolean; override;
     Function DoRestoreBank(max_block : Int64) : Boolean; override;
-    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal; Orphan : TOrphan); override;
+    Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); override;
     Function BlockExists(Block : Cardinal) : Boolean; override;
+    Function LockBlockChainStream : TFileStream;
+    Procedure UnlockBlockChainStream;
   public
     Constructor Create(AOwner : TComponent); Override;
-    Class Function GetBlockChainFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+    Destructor Destroy; Override;
     Class Function GetBankFileName(Const BaseDataFolder : AnsiString; block : Cardinal) : AnsiString;
+    Property DatabaseFolder : AnsiString read FDatabaseFolder write SetDatabaseFolder;
+    Procedure CopyConfiguration(Const CopyFrom : TStorage); override;
   End;
 
 implementation
 
-uses
-  SysUtils, ULog, Forms, UConst;
+Uses ULog, SysUtils, UThread;
 
 { TFileStorage }
 
-procedure _CopyFile(const FileName, DestName: string);
-var CopyBuffer   : Pointer; { buffer for copying }
-  BytesCopied  : Longint;
-  Source, Dest : Integer; { handles }
-  Destination  : TFileName; { holder for expanded destination name }
-const ChunkSize  : Longint = 8192; { copy in 8K chunks }
+Const CT_TBlockHeader_NUL : TBlockHeader = (BlockNumber:0;StreamBlockRelStartPos:0;BlockSize:0);
+
+  CT_GroupBlockSize = 1000;
+  CT_SizeOfBlockHeader = 16;
+  {
+  BlockChain file storage:
+
+  BlockHeader 0 -> From Block 0 to (CT_GroupBlockSize-1)
+    Foreach Block:
+      BlockNumber : 4 bytes
+      StreamBlockRelStartPos : 8 bytes  -> Start pos relative to End of BlockHeader
+      BlockSizeH : 4 bytes
+      -- Total size of BlockHeader: (4+8+4) * (CT_GroupBlockSize) = 16 * CT_GroupBlockSize
+    -- Note: If BlockHeader starts at pos X, it ends at pos X + (16*CT_GroupBlockSize)
+  Block 0
+    BlockSizeC: 4 bytes
+    Data: BlockSizeC bytes
+  Block 1
+    ...
+  Block CT_GroupBlockSize-1
+
+  BlockHeader 1 -> From Block CT_GroupBlockSize to ((CT_GroupBlockSize*2)-1)
+    (Same as BlockHeader 1)
+  Block CT_GroupBlockSize
+    ...
+  Block ((CT_GroupBlockSize*2)-1)
+
+  ...
+  BlockHeader X -> From (CT_GroupBlockSize*X) to ((CT_GroupBlockSize*(X+1))-1)
+  ...
+
+  }
+
+function TFileStorage.BlockExists(Block: Cardinal): Boolean;
+Var  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+  stream : TStream;
+  BlockHeader : TBlockHeader;
 begin
-  Destination := DestName;
-  GetMem(CopyBuffer, ChunkSize); { allocate the buffer }
+  Result := false;
+  stream := LockBlockChainStream;
   try
-   Source := FileOpen(FileName, fmShareDenyWrite); { open source file }
-   if (Source<0) then raise EFOpenError.CreateFmt('Error: Can''t open file!', [FileName]);
-   try
-     Dest := FileCreate(Destination); { create output file; overwrite existing }
-     if (Dest<0) then raise EFCreateError.CreateFmt('Error: Can''t create file!', [Destination]);
-     try
-       repeat
-         BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize); { read chunk }
-         if BytesCopied > 0  {if we read anything... }
-            then FileWrite(Dest, CopyBuffer^, BytesCopied); { ...write chunk }
-       until BytesCopied < ChunkSize; { until we run out of chunks }
-     finally
-       FileClose(Dest); { close the destination file }
-     end;
-   finally
-     FileClose(Source); { close the source file }
-   end;
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
+    if not StreamReadBlockHeader(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,BlockHeader) then exit;
+    Result := (BlockHeader.BlockNumber = Block) And
+        (((BlockHeader.BlockNumber MOD CT_GroupBlockSize)=0) OR (BlockHeader.StreamBlockRelStartPos>0)) And
+        (BlockHeader.BlockSize>0);
   finally
-   FreeMem(CopyBuffer, ChunkSize); { free the buffer }
+    UnlockBlockChainStream;
   end;
 end;
 
-function TFileStorage.BlockExists(Block: Cardinal): Boolean;
+procedure TFileStorage.ClearStream;
 begin
-  Result := FileExists(GetBlockChainFileName(GetFolder(Orphan),Block));
+  FreeAndNil(FBlockChainStream);
+  FStreamFirstBlockNumber := 0;
+  SetLength(FBlockHeadersFirstBytePosition,0);
+end;
+
+procedure TFileStorage.CopyConfiguration(const CopyFrom: TStorage);
+begin
+  inherited;
+  if CopyFrom is TFileStorage then begin
+    DatabaseFolder := TFileStorage(CopyFrom).DatabaseFolder;
+  end;
 end;
 
 constructor TFileStorage.Create(AOwner: TComponent);
 begin
-  FIsRestoring := false;
-  FBaseDataFolder := '';
   inherited;
+  FDatabaseFolder := '';
+  FBlockChainStream := Nil;
+  SetLength(FBlockHeadersFirstBytePosition,0);
+  FStreamFirstBlockNumber := 0;
+  FStorageLock := TCriticalSection.Create;
 end;
 
-procedure TFileStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal; Orphan: TOrphan);
-Var bfn : AnsiString;
+destructor TFileStorage.Destroy;
 begin
-  while (BlockExists(StartingDeleteBlock)) do begin
-    DeleteFile(GetBlockChainFileName(GetFolder(Orphan),StartingDeleteBlock));
-    inc(StartingDeleteBlock);
-    bfn := GetBankFileName(GetFolder(Orphan),StartingDeleteBlock);
-    if FileExists(bfn) then begin
-      DeleteFile(bfn);
+  inherited;
+  ClearStream;
+  FreeAndNil(FStorageLock);
+end;
+
+procedure TFileStorage.DoDeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
+Var stream : TStream;
+  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
+  _Header : TBlockHeader;
+  _intBlockIndex : Cardinal;
+  p : Int64;
+  Procedure GrowUntilPos(newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
+  Var b : Byte;
+  begin
+    b := 0;
+    if Not DeleteDataStartingAtCurrentPos then begin
+      Stream.Position := Stream.Size;
     end;
+    While (Stream.Position<newPos) do begin
+      Stream.Write(b,1);
+    end;
+    Stream.Position := newPos;
   end;
+begin
+  stream := LockBlockChainStream;
+  Try
+    if Not GetBlockHeaderFirstBytePosition(stream,StartingDeleteBlock,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
+    If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,StartingDeleteBlock,_Header) then exit;
+    _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
+    p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
+    // Write null data until end of header
+    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
+    // End Stream at _Header
+    Stream.Size := Stream.Position + _Header.StreamBlockRelStartPos-1;
+  Finally
+    UnlockBlockChainStream;
+  End;
 end;
 
 function TFileStorage.DoLoadBlockChain(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
-Var filename : AnsiString;
-  fs : TFileStream;
-  e : AnsiString;
+Var stream : TStream;
+  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
 begin
-  Result := false;
-  filename :=  GetBlockChainFileName(GetFolder(Orphan),Block);
-  if Not FileExists(filename) then begin
-    TLog.NewLog(lterror,Classname,'Operations ('+inttostr(Block)+') file not found: '+filename);
-    exit;
-  end;
-  fs := TFileStream.Create(filename, fmOpenRead);
-  try
-    If Operations.LoadBlockFromStream(fs, e) then result := true
-    else begin
-      TLog.NewLog(lterror,Classname,'Error reading file: '+filename+' Errors: '+e);
-    end;
-  finally
-    fs.Free;
-  end;
+  Result := False;
+  stream := LockBlockChainStream;
+  Try
+    if Not GetBlockHeaderFirstBytePosition(stream,Block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockRead(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,Operations);
+  Finally
+    UnlockBlockChainStream;
+  End;
 end;
 
-function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan): Boolean;
-Var fn,destfn,bankfilename : AnsiString;
-  i : Cardinal;
+function TFileStorage.DoMoveBlockChain(Start_Block: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
+Var db : TFileStorage;
+  i : Integer;
+  ops : TPCOperationsComp;
+  b : Cardinal;
 begin
-  if Bank.BlocksCount<Start_Block then exit;
-  TLog.NewLog(ltInfo,Classname,'Moving operations ('+inttostr(start_block)+' to '+inttostr(Bank.BlocksCount)+' from '+GetFolder('')+' to '+GetFolder(DestOrphan));
-  for i := start_block to Bank.BlocksCount - 1 do begin
-    fn := GetBlockChainFileName(GetFolder(Orphan),i);
-    destfn := GetBlockChainFileName(GetFolder(DestOrphan),i);
-    if FileExists(destfn) then TLog.NewLog(lterror,Classname,'File exists '+fn);
-    if Not FileExists(fn) then TLog.NewLog(lterror,Classname,'File not exists '+fn)
-    else _CopyFile(fn,destfn);
-    // Delete banks:
-    bankfilename := GetBankFileName(GetFolder(Orphan),i);
-    if (bankfilename<>'') then begin
-      if not DeleteFile(bankfilename) then begin
-        TLog.NewLog(lterror,Classname,'Cannot delete old bank file: '+bankfilename);
+  Try
+    if (Assigned(DestStorage)) And (DestStorage is TFileStorage) then db := TFileStorage(DestStorage)
+    else db := Nil;
+    try
+      if Not assigned(db) then begin
+        db := TFileStorage.Create(Nil);
+        db.DatabaseFolder := Self.DatabaseFolder;
+        db.Bank := Self.Bank;
+        db.Orphan := DestOrphan;
+        db.FStreamFirstBlockNumber := Start_Block;
       end;
-    end;
-  end;
-end;
-
-function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
-  function LoadOperationsFromFile(blockcount : Cardinal; Operations : TPCOperationsComp; var errors : AnsiString) : Boolean;
-  var filename : AnsiString;
-    fs : TFileStream;
-  Begin
-    Result := false;
-    filename := GetBlockChainFileName(GetFolder(Orphan),blockcount);
-    if FileExists(filename) then begin
-      fs := TFileStream.Create(filename, fmOpenRead);
+      if db is TFileStorage then TFileStorage(db).LockBlockChainStream;
       try
-          Result := Operations.LoadBlockFromStream(fs, errors);
+        ops := TPCOperationsComp.Create(Nil);
+        try
+          b := Start_Block;
+          while LoadBlockChainBlock(ops,b) do begin
+            inc(b);
+            db.SaveBlockChainBlock(ops);
+          end;
+          TLog.NewLog(ltdebug,Classname,'Moved blockchain from "'+Orphan+'" to "'+DestOrphan+'" from block '+inttostr(Start_Block)+' to '+inttostr(b-1));
+        finally
+          ops.Free;
+        end;
       finally
-        fs.Free;
+        if db is TFileStorage then TFileStorage(db).UnlockBlockChainStream;
       end;
-    end else errors := 'File operations (Block: '+inttostr(blockcount)+') not exists:'+filename;
+    Finally
+      If Not Assigned(DestStorage) then db.Free;
+    End;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,ClassName,'Error at DoMoveBlockChain: ('+E.ClassName+') '+E.Message);
+      Raise;
+    end;
   End;
+end;
+
+function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
 var
     sr: TSearchRec;
     FileAttrs,errcode: Integer;
@@ -172,47 +252,48 @@ var
     c,lastc : Cardinal;
     operations : TPCOperationsComp;
 begin
-  FileAttrs := faArchive;
-  folder := GetFolder(Orphan);
-  filename := '';
-  operations := TPCOperationsComp.Create(Nil);
-  try
-    if SysUtils.FindFirst(folder+'\bank*.bank', FileAttrs, sr) = 0 then begin
-      lastc := 0;
-      repeat
-        if (sr.Attr and FileAttrs) = sr.Attr then begin
-          auxfn := ChangeFileExt(sr.Name,'');
-          val(copy(auxfn,5,length(auxfn)),c,errcode);
-          if (errcode=0) And ((c<=max_block)) then begin
-            if LoadOperationsFromFile(c,operations,errors) then begin
-              if (filename='') then begin
-                filename := sr.Name;
-                lastc := c;
-              end else if (lastc<c) then begin
-                filename := sr.Name;
-                lastc := c;
-              end;
-            end else begin
-              TLog.NewLog(lterror,ClassName,'Found a bank in file:'+filename+' but not operations:'+errors);
+  LockBlockChainStream;
+  Try
+    FileAttrs := faArchive;
+    folder := GetFolder(Orphan);
+    filename := '';
+    operations := TPCOperationsComp.Create(Nil);
+    try
+      if SysUtils.FindFirst(folder+'\bank*.bank', FileAttrs, sr) = 0 then begin
+        lastc := 0;
+        repeat
+          if (sr.Attr and FileAttrs) = FileAttrs then begin
+            auxfn := ChangeFileExt(sr.Name,'');
+            val(copy(auxfn,5,length(auxfn)),c,errcode);
+            if (errcode=0) And ((c<=max_block)) then begin
+                if (filename='') then begin
+                  filename := sr.Name;
+                  lastc := c;
+                end else if (lastc<c) then begin
+                  filename := sr.Name;
+                  lastc := c;
+                end;
             end;
           end;
+        until FindNext(sr) <> 0;
+        FindClose(sr);
+      end;
+      if (filename<>'') then begin
+        fs := TFileStream.Create(folder+'\'+filename,fmOpenRead);
+        try
+          if not Bank.LoadBankFromStream(fs,errors) then begin
+            TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
+          end;
+        finally
+          fs.Free;
         end;
-      until FindNext(sr) <> 0;
-      FindClose(sr);
-    end;
-    if (filename<>'') then begin
-      fs := TFileStream.Create(folder+'\'+filename,fmOpenRead);
-      try
-        if not Bank.LoadBankFromStream(fs,errors) then begin
-          TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
-        end;
-      finally
-        fs.Free;
       end;
+    finally
+      operations.Free;
     end;
-  finally
-    operations.Free;
-  end;
+  Finally
+    UnlockBlockChainStream;
+  End;
 end;
 
 function TFileStorage.DoSaveBank: Boolean;
@@ -233,54 +314,272 @@ begin
 end;
 
 function TFileStorage.DoSaveBlockChain(Operations: TPCOperationsComp): Boolean;
-Var
-  fs: TFileStream;
-  bankfilename,folder: AnsiString;
+Var stream : TStream;
+  StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal;
 begin
-  folder := GetFolder(Orphan);
-  If Not ForceDirectories(folder) then exit;
-  if Not FIsRestoring then begin
-    fs := TFileStream.Create(GetBlockChainFileName(folder,Operations.OperationBlock.block), fmCreate);
-    try
-      fs.Size := 0;
-      Operations.SaveBlockToStream(false,fs);
-    finally
-      fs.Free;
+  Result := False;
+  stream := LockBlockChainStream;
+  Try
+    if (Length(FBlockHeadersFirstBytePosition)=0) then begin
+      // Is saving first block on the stream?
+      if (Stream.Size=0) then begin
+        // Yes! Positioning
+        FStreamFirstBlockNumber := Operations.OperationBlock.block;
+      end;
+      TLog.NewLog(ltdebug,Classname,Format('Saving Block %d on a newer stream, stream first position=%d',[Operations.OperationBlock.block,FStreamFirstBlockNumber]));
     end;
-  end;
-  Result := true;
+    if Not GetBlockHeaderFirstBytePosition(stream,Operations.OperationBlock.block,StreamBlockHeaderStartPos,BlockHeaderFirstBlock) then exit;
+    Result := StreamBlockSave(stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Operations);
+  Finally
+    UnlockBlockChainStream;
+  End;
   SaveBank;
 end;
 
-class function TFileStorage.GetBankFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
+class function TFileStorage.GetBankFileName(const BaseDataFolder: AnsiString;
+  block: Cardinal): AnsiString;
 Var c : Cardinal;
   folder : AnsiString;
 begin
   Result := '';
-  if (Block MOD CT_BankToDiskEveryNBlocks)<>0 then exit; // No bank!
   If not ForceDirectories(BaseDataFolder) then exit;
   Result := BaseDataFolder + '\bank'+Format('%.6d',[Block])+'.bank';
 end;
 
-class function TFileStorage.GetBlockChainFileName(const BaseDataFolder: AnsiString; block: Cardinal): AnsiString;
-Var c : Cardinal;
-  folder : AnsiString;
+function TFileStorage.GetBlockHeaderFirstBytePosition(Stream : TStream; Block: Cardinal; var StreamBlockHeaderStartPos: Int64; var BlockHeaderFirstBlock: Cardinal): Boolean;
+var iPos,start : Cardinal;
+  bh : TBlockHeader;
 begin
-  Result := '';
-  c := block DIV 1000;
-  folder := BaseDataFolder+'\block'+Format('%.4d',[c]);
-  If not ForceDirectories(folder) then exit;
-  Result :=  folder+'\op'+Format('%.6d',[block])+ '.bco';
+  Result := false;
+  if Block<FStreamFirstBlockNumber then begin
+    TLog.NewLog(lterror,Classname,Format('Block %d is lower than Stream First block %d',[Block,FStreamFirstBlockNumber]));
+    exit;
+  end;
+  iPos := (Block-FStreamFirstBlockNumber) DIV CT_GroupBlockSize;
+  if iPos>High(FBlockHeadersFirstBytePosition) then Begin
+    if Length(FBlockHeadersFirstBytePosition)>0 then begin
+      start := High(FBlockHeadersFirstBytePosition);
+    end else begin
+      // Initialize and start at 0
+      SetLength(FBlockHeadersFirstBytePosition,1);
+      FBlockHeadersFirstBytePosition[0] := 0;
+      start := 0;
+    end;
+    while (start<iPos) do begin
+      // Read last start position
+      if (Stream.Size<(FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize)) then begin
+        // This position not exists... This is a Fatal error due must find previos block!
+        TLog.NewLog(ltError,Classname,Format('Stream size %d is lower than BlockHeader[%d] position %d + BlockHeaderSize %d',
+          [Stream.size,start,FBlockHeadersFirstBytePosition[start],GetBlockHeaderFixedSize]));
+        exit;
+      end;
+      Stream.Position := FBlockHeadersFirstBytePosition[start] + GetBlockHeaderFixedSize - CT_SizeOfBlockHeader;
+      // Read last Header
+      Stream.Read(bh.BlockNumber,SizeOf(bh.BlockNumber));
+      Stream.Read(bh.StreamBlockRelStartPos,SizeOf(bh.StreamBlockRelStartPos));
+      Stream.Read(bh.BlockSize,sizeof(bh.BlockSize));
+      SetLength(FBlockHeadersFirstBytePosition,length(FBlockHeadersFirstBytePosition)+1);
+      FBlockHeadersFirstBytePosition[High(FBlockHeadersFirstBytePosition)] := Stream.Position + bh.StreamBlockRelStartPos + bh.BlockSize;
+      inc(start);
+    end;
+  End;
+  StreamBlockHeaderStartPos := FBlockHeadersFirstBytePosition[high(FBlockHeadersFirstBytePosition)];
+  BlockHeaderFirstBlock := FStreamFirstBlockNumber + (iPos * CT_GroupBlockSize);
+  Result := true;
 end;
 
+function TFileStorage.GetBlockHeaderFixedSize: Int64;
+begin
+  Result := (CT_GroupBlockSize* CT_SizeOfBlockHeader);
+end;
 
 function TFileStorage.GetFolder(const AOrphan: TOrphan): AnsiString;
 begin
-  if FBaseDataFolder = '' then begin
-    FBaseDataFolder := ExtractFileDir(Application.ExeName)+'\'+ChangeFileExt(ExtractFileName(Application.ExeName),'')+ '\BCOperations';
+  if FDatabaseFolder = '' then raise Exception.Create('No Database Folder');
+  if AOrphan<>'' then Result := FDatabaseFolder + '\'+AOrphan
+  else Result := FDatabaseFolder;
+  if not ForceDirectories(Result) then raise Exception.Create('Cannot create database folder: '+Result);
+end;
+
+function TFileStorage.LockBlockChainStream: TFileStream;
+Var fn : TFileName;
+  fm : Word;
+begin
+  TPCThread.ProtectEnterCriticalSection(Self,FStorageLock);
+  Try
+    if Not Assigned(FBlockChainStream) then begin
+      fn := GetFolder(Orphan)+'\BlockChainStream.blocks';
+      if ReadOnly then begin
+        if FileExists(fn) then fm := fmOpenRead+fmShareDenyNone
+        else raise Exception.Create('DBFileStorage not exists for open ReadOnly: '+fn);
+      end else begin
+        if FileExists(fn) then fm := fmOpenReadWrite+fmShareDenyNone  // XXXXXXXXX Sure not to use fmShareDenyWrite ???
+        else fm := fmCreate+fmShareDenyNone  // XXXXXXXXX Sure not to use fmShareDenyWrite too ???
+      end;
+      FBlockChainStream := TFileStream.Create(fn,fm);
+      // Read Headers:
+      SetLength(FBlockHeadersFirstBytePosition,0);
+    end;
+  Except
+    FStorageLock.Release;
+    Raise;
+  End;
+  Result := FBlockChainStream;
+end;
+
+procedure TFileStorage.SetDatabaseFolder(const Value: AnsiString);
+begin
+  if FDatabaseFolder=Value then exit;
+  FDatabaseFolder := Value;
+  FreeAndNil(FBlockChainStream);
+  SetLength(FBlockHeadersFirstBytePosition,0);
+end;
+
+procedure TFileStorage.SetOrphan(const Value: TOrphan);
+begin
+  inherited;
+  ClearStream;
+end;
+
+procedure TFileStorage.SetReadOnly(const Value: Boolean);
+begin
+  inherited;
+  ClearStream;
+end;
+
+function TFileStorage.StreamBlockRead(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock, Block : Cardinal; Operations : TPCOperationsComp) : Boolean;
+Var p : Int64;
+  errors : AnsiString;
+  streamFirstBlock,
+  _BlockSizeC,
+  _intBlockIndex : Cardinal;
+  _Header : TBlockHeader;
+  _ops : TStream;
+begin
+  Result := StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,_Header);
+  if Not Result then exit;
+  // Calculating block position
+  p := (StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
+     (_Header.StreamBlockRelStartPos);
+  if Stream.Size<(p + _Header.BlockSize) then begin
+    TLog.NewLog(ltError,Classname,Format(
+      'Invalid stream size. Block %d need to be at relative %d after %d = %d BlockSize:%d (Size %d)',
+      [Block,_Header.StreamBlockRelStartPos,(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize),p,_Header.BlockSize,Stream.Size]));
+    exit;
+  end;
+  Stream.Position := p;
+  // Read the block
+  // Reading size
+  Stream.Read(_BlockSizeC,sizeof(_BlockSizeC));
+  if (_BlockSizeC>(_Header.BlockSize+sizeof(_BlockSizeC))) then begin
+    TLog.NewLog(lterror,Classname,Format('Corruption at stream Block size. Block %d SizeH:%d SizeC:%d',[Block,
+      _Header.BlockSize,_BlockSizeC]));
+    exit;
+  end;
+  // Reading Block
+  _ops := TMemoryStream.Create;
+  try
+    _ops.CopyFrom(Stream,_BlockSizeC);
+    _ops.Position := 0;
+    If Not Operations.LoadBlockFromStream(_ops,errors) then begin
+      TLog.NewLog(lterror,Classname,'Error reading OperationBlock '+inttostr(Block)+' from stream. Errors: '+errors);
+      exit;
+    end;
+    Result := true;
+  Finally
+    _ops.Free;
+  end;
+end;
+
+
+function TFileStorage.StreamBlockSave(Stream : TStream; StreamBlockHeaderStartPos : Int64; BlockHeaderFirstBlock : Cardinal; Operations : TPCOperationsComp) : Boolean;
+  Procedure GrowUntilPos(newPos : Int64; DeleteDataStartingAtCurrentPos : Boolean);
+  Var b : Byte;
+  begin
+    b := 0;
+    if Not DeleteDataStartingAtCurrentPos then begin
+      Stream.Position := Stream.Size;
+    end;
+    While (Stream.Position<newPos) do begin
+      Stream.Write(b,1);
+    end;
+    Stream.Position := newPos;
+  end;
+Var p : Int64;
+  c : Cardinal;
+  _Header, _HeaderPrevious : TBlockHeader;
+  _intBlockIndex : Cardinal;
+  _ops : TStream;
+begin
+  Result := false;
+  _Header := CT_TBlockHeader_NUL;
+  _Header.BlockNumber := Operations.OperationBlock.block;
+  if BlockHeaderFirstBlock>_Header.BlockNumber then raise Exception.Create('Dev error 20160917-3')
+  else if BlockHeaderFirstBlock<_Header.BlockNumber then begin
+    Result := StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,_Header.BlockNumber-1,_HeaderPrevious);
+    if not Result then begin
+      raise Exception.Create('Cannot found header of previous block '+inttostr(Operations.OperationBlock.block));
+    end;
+    _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
+  end else begin
+    // First block of the stream
+    _Header.StreamBlockRelStartPos := 0;
+  end;
+  _ops := TMemoryStream.Create;
+  Try
+    Operations.SaveBlockToStream(false,_ops);
+    _Header.BlockSize := _ops.Size;
+    // Positioning until Header Position to save Header data
+    _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
+    p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
+    GrowUntilPos(StreamBlockHeaderStartPos + p,false);
+    // Save Header
+    Stream.Write(_Header.BlockNumber,sizeof(_Header.BlockNumber));
+    Stream.Write(_Header.StreamBlockRelStartPos,sizeof(_Header.StreamBlockRelStartPos));
+    c := _Header.BlockSize + sizeof(c);
+    Stream.Write(c,sizeof(_Header.BlockSize));
+    // Positioning until Header end
+    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
+    // And now positioning until Data:
+    GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize + _Header.StreamBlockRelStartPos, false );
+    // Save stream size
+    Stream.Write(_Header.BlockSize,sizeof(_Header.BlockSize));
+    // Save Data
+    _ops.Position := 0;
+    Stream.CopyFrom(_ops,_ops.Size);
+  Finally
+    _ops.Free;
   end;
-  if AOrphan<>'' then Result := FBaseDataFolder + '\'+AOrphan
-  else Result := FBaseDataFolder;
+end;
+
+function TFileStorage.StreamReadBlockHeader(Stream: TStream;
+  StreamBlockHeaderStartPos: Int64; BlockHeaderFirstBlock, Block: Cardinal;
+  var BlockHeader: TBlockHeader): Boolean;
+Var p : Int64;
+  errors : AnsiString;
+  streamFirstBlock : Cardinal;
+  _intBlockIndex : Cardinal;
+  _Blocks : Cardinal;
+begin
+  Result := false;
+  BlockHeader := CT_TBlockHeader_NUL;
+  if (BlockHeaderFirstBlock>Block) then raise Exception.Create('Dev error 20160917-1');
+  if (BlockHeaderFirstBlock+CT_GroupBlockSize)<Block then raise Exception.Create('Dev error 20160917-2');
+  if Stream.Size< (StreamBlockHeaderStartPos + (GetBlockHeaderFixedSize)) then begin
+    TLog.NewLog(ltError,Classname,'Invalid stream size');
+    exit;
+  end;
+  Stream.Position := StreamBlockHeaderStartPos + (CT_SizeOfBlockHeader*(Block-BlockHeaderFirstBlock));
+  // Reading block header
+  If Stream.Read(BlockHeader.BlockNumber,sizeof(BlockHeader.BlockNumber))<sizeof(BlockHeader.BlockNumber) then exit;
+  If Stream.Read(BlockHeader.StreamBlockRelStartPos,sizeof(BlockHeader.StreamBlockRelStartPos))<sizeof(BlockHeader.StreamBlockRelStartPos) then exit;
+  If Stream.Read(BlockHeader.BlockSize,sizeof(BlockHeader.BlockSize))<sizeof(BlockHeader.BlockSize) then exit;
+  Result := (BlockHeader.BlockNumber = Block);
+end;
+
+procedure TFileStorage.UnlockBlockChainStream;
+begin
+  FStorageLock.Release;
 end;
 
 end.

+ 7 - 5
Units/PascalCoin/UMiner.pas

@@ -18,6 +18,8 @@ interface
 Uses UBlockChain, Classes, SyncObjs, Windows, UAccounts, UThread;
 
 Type
+  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
+
   TMinerThread = Class;
 
   TMinerNewAccountFound = procedure(sender : TMinerThread; Operations : TPCOperationsComp) of object;
@@ -26,7 +28,7 @@ Type
   TMinerThread = Class(TPCThread)
   private
     FOperations : TPCOperationsComp;
-    FLock: TRTLCriticalSection;
+    FLock: TCriticalSection;
     FPlayCount : Int64;
     FTotalActiveTime : Int64;
     FLastStartTickCount : Cardinal;
@@ -100,7 +102,7 @@ begin
   FPaused := true;
   FPlayCount := 0;
   FAccountKey := minerAccountKey;
-  InitializeCriticalSection(FLock);
+  FLock := TCriticalSection.Create;
   FOperations := TPCOperationsComp.Create(nil);
   FOperations.Bank := Bank;
   FOperations.AccountKey := AccountKey;
@@ -145,7 +147,7 @@ begin
 
         end;
       finally
-        LeaveCriticalSection(FLock);
+        FLock.Release;
       end;
       if (winner) then begin
         Try
@@ -173,7 +175,7 @@ end;
 
 destructor TMinerThread.Destroy;
 begin
-  DeleteCriticalSection(Flock);
+  FreeAndNil(FLock);
   FreeAndNil(FOperations);
   inherited;
 end;
@@ -193,7 +195,7 @@ end;
 
 procedure TMinerThread.MinerUnLockOperations(IsNewBlock : Boolean);
 begin
-  LeaveCriticalSection(FLock);
+  FLock.Release;
   if IsNewBlock then CheckIfCanRecoverBlocks;
 end;
 

+ 62 - 190
Units/PascalCoin/UNetProtocol.pas

@@ -15,12 +15,11 @@ unit UNetProtocol;
 
 interface
 
-Uses UBlockChain, Classes, SysUtils, UAccounts, UThread, Sockets, ExtCtrls,
-  UCrypto,
-  Windows;
+Uses UBlockChain, Classes, SysUtils, UAccounts, UThread, ExtCtrls,
+  UCrypto, UTCPIP, SyncObjs, Windows;
 
 Const
-  CT_MagicNetIdentification = $0A043580; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}{$IFDEF TESTNET}$8035040A{$ENDIF}{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
   CT_MagicRequest = $0001;
   CT_MagicResponse = $0002;
   CT_MagicAutoSend = $0003;
@@ -53,8 +52,6 @@ Type
   Max size: (depends on last 4 bytes) = 22..(2^32)-1
   }
 
-  TNetTcpIpClient = TCustomIpClient;
-
   TNetTransferType = (ntp_unknown, ntp_request, ntp_response, ntp_autosend);
 
   TNetProtocolVersion = Record
@@ -225,11 +222,10 @@ Type
   private
     FTcpIpClient : TNetTcpIpClient;
     FRemoteOperationBlock : TOperationBlock;
-    FSocketError : Integer;
     FLastDataReceivedTS : Cardinal;
     FLastDataSendedTS : Cardinal;
     FClientBufferRead : TStream;
-    FNetLock : TRTLCriticalSection;
+    FNetLock : TCriticalSection;
     FIsWaitingForResponse : Boolean;
     FLastKnownTimestampDiff : Int64;
     FIsMyselfServer : Boolean;
@@ -243,13 +239,8 @@ Type
     FIsDownloadingBlocks : Boolean;
     function GetConnected: Boolean;
     procedure SetConnected(const Value: Boolean);
-    procedure TcpClient_OnError(Sender: TObject; SocketError: Integer);
     procedure TcpClient_OnConnect(Sender: TObject);
     procedure TcpClient_OnDisconnect(Sender: TObject);
-    procedure TcpClient_OnReceive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
-    procedure TcpClient_OnSend(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
-    procedure TcpClient_OnCreateHandle(Sender : TObject);
-    procedure TcpClient_OnDestroyHandle(Sender : TObject);
     Function DoSendAndWaitForResponse(operation: Word; RequestId: Integer; SendDataBuffer, ReceiveDataBuffer: TStream; MaxWaitTime : Cardinal; var HeaderData : TNetHeaderData) : Boolean;
     procedure DoProcessBuffer;
     Procedure DoProcess_Hello(HeaderData : TNetHeaderData; DataBuffer: TStream);
@@ -318,27 +309,12 @@ Type
 
   TNetServerClient = Class(TNetConnection);
 
-  TNetServer = Class(TComponent)
+  TNetServer = Class(TNetTcpIpServer)
   private
-    // Build 1.0.4 Changing FNetClients from TList to TPCThreadList
-    FNetClients : TPCThreadList;  // When a connection is established to a new client, a TNetConnection is created (p2p)
-    FTCPServer : TTcpServer;
-    FPort: Word;
-    function GetActive: Boolean;
-    procedure SetActive(const Value: Boolean);
-    procedure OnTcpServerCreateHandle(Sender : TObject);
-    procedure OnTcpServerDestroyHandle(Sender : TObject);
-    procedure OnTcpServerListening(Sender : TObject);
-    procedure OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
-    procedure OnTcpServerGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
-    procedure SetPort(const Value: Word);
   protected
-    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    Procedure OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient); override;
+    procedure SetActive(const Value: Boolean); override;
   public
-    Constructor Create(AOwner : TComponent); override;
-    Destructor Destroy; override;
-    Property Active : Boolean read GetActive write SetActive;
-    Property Port : Word read FPort Write SetPort;
   End;
 
   TThreadDiscoverConnection = Class(TPCThread)
@@ -534,7 +510,7 @@ begin
   try
     for i := 0 to l.Count - 1 do begin
       if TObject(l[i])=ObjectPointer then begin
-        LeaveCriticalSection(TNetConnection(l[i]).FNetLock);
+        TNetConnection(l[i]).FNetLock.Release;
         exit;
       end;
     end;
@@ -934,19 +910,20 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     TLog.NewLog(ltdebug,CT_LogSender,Format('GetNewBank(new_start_block:%d)',[start_block]));
     Bank := TPCBank.Create(Nil);
     try
-
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.Storage.Orphan := TNode.Node.Bank.Storage.Orphan;
+      Bank.Storage.ReadOnly := true;
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
       if start_block>=0 then begin
         // Restore a part
-        Bank.DiskRestoreFromOperations(start_block);
-        start := start_block + 1;
+        Bank.DiskRestoreFromOperations(start_block-1);
+        start := start_block;
       end else begin
         start := 0;
         start_block := 0;
       end;
       Bank.Storage.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+      Bank.Storage.ReadOnly := false;
       // Receive new blocks:
       finished := false;
       repeat
@@ -983,12 +960,17 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         start := Bank.BlocksCount;
       until (Bank.BlocksCount=Connection.FRemoteOperationBlock.block+1) Or (finished);
       if Bank.BlocksCount>TNode.Node.Bank.BlocksCount then begin
-        // I'm an orphan blockchain...
-        TLog.NewLog(ltinfo,CT_LogSender,'New valid blockchain found. My block count='+inttostr(TNode.Node.Bank.BlocksCount)+
-          ' found='+inttostr(Bank.BlocksCount)+' starting at block '+inttostr(start_block));
-        TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,Inttostr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)));
-        Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan);
-        TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+        TNode.Node.DisableNewBlocks;
+        Try
+          // I'm an orphan blockchain...
+          TLog.NewLog(ltinfo,CT_LogSender,'New valid blockchain found. My block count='+inttostr(TNode.Node.Bank.BlocksCount)+
+            ' found='+inttostr(Bank.BlocksCount)+' starting at block '+inttostr(start_block));
+          TNode.Node.Bank.Storage.MoveBlockChainBlocks(start_block,Inttostr(start_block)+'_'+FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now)),Nil);
+          Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan,TNode.Node.Bank.Storage);
+          TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
+        Finally
+          TNode.Node.EnableNewBlocks;
+        End;
       end;
     finally
       Bank.Free;
@@ -1284,83 +1266,34 @@ end;
 
 { TNetServer }
 
-constructor TNetServer.Create(AOwner: TComponent);
-begin
-  FNetClients := TPCThreadList.Create;
-  inherited;
-  FPort := CT_NetServer_Port;
-  FTCPServer := TTcpServer.Create(Self);
-  FTCPServer.LocalPort := Inttostr(CT_NetServer_Port);
-  FTCPServer.OnAccept := OnTcpServerAccept;
-  FTCPServer.OnGetThread := OnTcpServerGetThread;
-  FTCPServer.OnListening := OnTcpServerListening;
-  FTCPServer.OnCreateHandle := OnTcpServerCreateHandle;
-  FTCPServer.OnDestroyHandle := OnTcpServerDestroyHandle;
-  // New on Build 1.0.4: Prior where limited to 10... causing a queue list
-  // and CPU usage for nothing (TCPServer issue).
-  FTCPServer.ServerSocketThread.ThreadCacheSize := CT_MaxClientsConnected;
-end;
-
-destructor TNetServer.Destroy;
-begin
-  FreeAndNil(FTCPServer);
-  inherited;
-  FreeAndNil(FNetClients);
-end;
-
-function TNetServer.GetActive: Boolean;
-begin
-  Result := FTCPServer.Active;
-end;
-
-procedure TNetServer.Notification(AComponent: TComponent; Operation: TOperation);
-var i : Integer;
-  l : TList;
-begin
-  inherited;
-  if (Operation=opRemove) then begin
-    if Not Assigned(FNetClients) then exit;
-    l := FNetClients.LockList;
-    Try
-      i := l.IndexOf(AComponent);
-      if (i>=0) then begin
-        l.Delete(i);
-        TLog.NewLog(ltdebug,ClassName,'TNetConnection destroyed. Remaining: '+Inttostr(l.Count));
-      end;
-    Finally
-      FNetClients.UnlockList;
-    End;
-  end;
-end;
-
-procedure TNetServer.OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
+procedure TNetServer.OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient);
+//procedure TNetServer.OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
 Var n : TNetServerClient;
   DebugStep : String;
 begin
   DebugStep := '';
   Try
-    if Not ClientSocket.Connected then exit;
+    if Not Client.Connected then exit;
     // NOTE: I'm in a separate thread
     // While in this function the ClientSocket connection will be active, when finishes the ClientSocket will be destroyed
-    TLog.NewLog(ltInfo,Classname,'Starting ClientSocket accept '+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
-    n := TNetServerClient.Create(Self);
+    TLog.NewLog(ltInfo,Classname,'Starting ClientSocket accept '+Client.ClientRemoteAddr);
+    n := TNetServerClient.Create(Nil);
     Try
       DebugStep := 'Assigning client';
-      n.SetClient(ClientSocket);
+      n.SetClient(Client);
       TNetData.NetData.IncStatistics(1,1,0,0,0,0);
       TNetData.NetData.CleanBlackList;
       DebugStep := 'Checking blacklisted';
-      if (TNetData.NetData.IsBlackListed(ClientSocket.RemoteHost,0)) then begin
+      if (TNetData.NetData.IsBlackListed(Client.RemoteHost,0)) then begin
         // Invalid!
-        TLog.NewLog(ltinfo,Classname,'Refusing Blacklist ip: '+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
-        n.SendError(ntp_autosend,CT_NetOp_Error, 0,CT_NetError_IPBlackListed,'Your IP is blacklisted:'+ClientSocket.RemoteHost+':'+ClientSocket.RemotePort);
+        TLog.NewLog(ltinfo,Classname,'Refusing Blacklist ip: '+Client.ClientRemoteAddr);
+        n.SendError(ntp_autosend,CT_NetOp_Error, 0,CT_NetError_IPBlackListed,'Your IP is blacklisted:'+Client.ClientRemoteAddr);
         // Wait some time before close connection
         sleep(5000);
       end else begin
         DebugStep := 'Adding client';
-        FNetClients.Add(n);
         DebugStep := '   ';
-        while (n.Connected) And (FTCPServer.Active) do begin
+        while (n.Connected) And (Active) do begin
           DebugStep[1] := '1';
           n.DoProcessBuffer;
           DebugStep[1] := '2';
@@ -1375,7 +1308,7 @@ begin
         n.Connected := false;
         sleep(10);
         DebugStep := 'Assigning old client';
-        n.SetClient( TTcpClient.Create(Nil) );
+        n.SetClient( TNetTcpIpClient.Create(Nil) );
       Finally
         DebugStep := 'Freeing NetServerClient';
         n.Free;
@@ -1388,55 +1321,27 @@ begin
   End;
 end;
 
-procedure TNetServer.OnTcpServerCreateHandle(Sender: TObject);
-begin
-//  TLog.NewLog(ltdebug,Classname,'ServerCreateHandle');
-end;
-
-procedure TNetServer.OnTcpServerDestroyHandle(Sender: TObject);
-begin
-//  TLog.NewLog(ltdebug,Classname,'ServerDestroyHandle');
-end;
-
-procedure TNetServer.OnTcpServerGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
-begin
-//  TLog.NewLog(ltdebug,Classname,'ClientSocket Get Thread');
-end;
-
-procedure TNetServer.OnTcpServerListening(Sender: TObject);
-begin
-  TLog.NewLog(ltinfo,Classname,'Server listening');
-end;
-
 procedure TNetServer.SetActive(const Value: Boolean);
 begin
   if Value then begin
-    TLog.NewLog(ltinfo,Classname,'Activating server on port '+FTCPServer.LocalPort);
+    TLog.NewLog(ltinfo,Classname,'Activating server on port '+IntToStr(Port));
   end else begin
     TLog.NewLog(ltinfo,Classname,'Closing server');
   end;
-  FTCPServer.Active := Value;
-  if FTCPServer.Active then begin
+  inherited;
+  if Active then begin
     TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   end else begin
     TNetData.NetData.DisconnectClients;
   end;
 end;
 
-procedure TNetServer.SetPort(const Value: Word);
-begin
-  if FPort=Value then exit;
-  Active := false;
-  FPort := Value;
-  FTCPServer.LocalPort := Inttostr(Value);
-end;
-
 { TNetConnection }
 
 function TNetConnection.ClientRemoteAddr: AnsiString;
 begin
   If Assigned(FTcpIpClient) then begin
-    Result := Client.RemoteHost+':'+Client.RemotePort;
+    Result := FtcpIpClient.ClientRemoteAddr
   end else Result := 'NIL';
 end;
 
@@ -1458,7 +1363,7 @@ begin
 
   Client.RemoteHost := ServerIP;
   if ServerPort<=0 then ServerPort := CT_NetServer_Port;
-  Client.RemotePort := Inttostr(ServerPort);
+  Client.RemotePort := ServerPort;
   TLog.NewLog(ltDebug,Classname,'Trying to connect to a server at: '+ClientRemoteAddr);
   TNetData.NetData.NotifyNetConnectionUpdated;
   Result := Client.Connect;
@@ -1486,13 +1391,12 @@ begin
   FLastKnownTimestampDiff := 0;
   FIsWaitingForResponse := false;
   FClientBufferRead := TMemoryStream.Create;
-  InitializeCriticalSection(FNetLock);
+  FNetLock := TCriticalSection.Create;
   FLastDataReceivedTS := 0;
   FLastDataSendedTS := 0;
   FTcpIpClient := Nil;
   FRemoteOperationBlock := CT_OperationBlock_NUL;
-  FSocketError := 0;
-  SetClient( TTcpClient.Create(Nil) );
+  SetClient( TNetTcpIpClient.Create(Nil) );
   TNetData.NetData.FNetConnections.Add(Self);
   TNetData.NetData.NotifyNetConnectionUpdated;
 end;
@@ -1522,7 +1426,7 @@ begin
   Try
     TNetData.NetData.NotifyNetConnectionUpdated;
   Finally
-    DeleteCriticalSection(FNetLock);
+    FreeAndNil(FNetLock);
     FreeAndNil(FClientBufferRead);
     inherited;
     FreeAndNil(FTcpIpClient);
@@ -1548,14 +1452,14 @@ begin
   if include_in_list then begin
     l := TNetData.NetData.FBlackList.LockList;
     try
-      i := TNetData.NetData.IndexOfNetClient(l,Client.RemoteHost,StrToIntDef( Client.RemotePort,CT_NetServer_Port));
+      i := TNetData.NetData.IndexOfNetClient(l,Client.RemoteHost,Client.RemotePort);
       if i<0 then begin
         new(P);
         P^ := CT_TNodeServerAddress_NUL;
         l.Add(P);
       end else P := l[i];
       P^.ip := Client.RemoteHost;
-      P^.port := StrToIntDef( Client.RemotePort,CT_NetServer_Port);
+      P^.port := Client.RemotePort;
       P^.last_connection := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
       P^.its_myself := ItsMyself;
       P^.BlackListText := Why;
@@ -1566,7 +1470,7 @@ begin
   if ItsMyself then begin
     l := TNetData.NetData.FNodeServers.LockList;
     try
-      i := TNetData.NetData.IndexOfNetClient(l,Client.RemoteHost,StrToIntDef( Client.RemotePort,CT_NetServer_Port));
+      i := TNetData.NetData.IndexOfNetClient(l,Client.RemoteHost,Client.RemotePort);
       if i>=0 then begin
         P := l[i];
         P^.its_myself := true;
@@ -1605,7 +1509,7 @@ begin
           DebugStep := 'Is waiting for response, nothing';
         end;
       Finally
-        LeaveCriticalSection(FNetLock);
+        FNetLock.Release;
       End;
     finally
       ms.Free;
@@ -1922,7 +1826,7 @@ procedure TNetConnection.DoProcess_Hello(HeaderData: TNetHeaderData; DataBuffer:
       end;
       if showmessage then begin
         TNode.Node.NotifyNetClientMessage(Nil,'Detected a different time in an other node... check that your PC time and timezone is correct or you will be Blacklisted! '+
-          'Your time: '+TimeToStr(now)+' - '+Client.RemoteHost+':'+Client.RemotePort+' time: '+TimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(connection_ts)))+' Difference: '+inttostr(FLastKnownTimestampDiff)+' seconds. '+
+          'Your time: '+TimeToStr(now)+' - '+Client.ClientRemoteAddr+' time: '+TimeToStr(UnivDateTime2LocalDateTime( UnixToUnivDateTime(connection_ts)))+' Difference: '+inttostr(FLastKnownTimestampDiff)+' seconds. '+
           '(If this message appears on each connection, then you have a bad configured time, if not, do nothing)' );
       end;
     end else begin
@@ -2134,7 +2038,7 @@ begin
     exit;
   end;
   If Not Assigned(FTcpIpClient) then exit;
-  if Not Client.Active then exit;
+  if Not Client.Connected then exit;
   TPCThread.ProtectEnterCriticalSection(Self,FNetLock);
   Try
     was_waiting_for_response := RequestId>0;
@@ -2143,12 +2047,11 @@ begin
         FIsWaitingForResponse := true;
         Send(ntp_request,operation,0,RequestId,SendDataBuffer);
       end;
-      FSocketError := 0;
       tc := GetTickCount;
       Repeat
         if Not Client.WaitForData(100) then begin
-          If FSocketError<>0 then begin
-            TLog.NewLog(ltdebug,classname,'Broken connection by error '+Inttostr(FSocketError)+' to '+ClientRemoteAddr);
+          If Client.SocketError<>0 then begin
+            TLog.NewLog(ltdebug,classname,'Broken connection by error '+Inttostr(Client.SocketError)+' to '+ClientRemoteAddr);
             Connected := false;
             exit;
           end;
@@ -2211,7 +2114,7 @@ begin
       if was_waiting_for_response then FIsWaitingForResponse := false;
     end;
   Finally
-    LeaveCriticalSection(FNetLock);
+    FNetLock.Release;
   End;
 end;
 
@@ -2259,7 +2162,7 @@ begin
     tc := GetTickCount;
     repeat
       If not Connected then exit;
-      if Not Client.active then exit;
+      if Not Client.Connected then exit;
       last_bytes_read := 0;
       FClientBufferRead.Position := 0;
       Result := TNetData.ExtractHeaderInfo(FClientBufferRead,HeaderData,BufferData,IsValidHeaderButNeedMoreData);
@@ -2317,17 +2220,17 @@ begin
     until (Result) Or ((GetTickCount > (tc+MaxWaitMiliseconds)) And (last_bytes_read=0));
   finally
     Try
-      if (Connected) And (Client.Active) then begin
+      if (Connected) then begin
         if (Not Result) And (FClientBufferRead.Size>0) And (Not IsValidHeaderButNeedMoreData) then begin
           deletedBytes := FClientBufferRead.Size;
-          TLog.NewLog(lterror,ClassName,Format('Deleting %d bytes from TcpClient buffer of %s:%s after max %d miliseconds. Passed: %d',
-            [deletedBytes, Client.RemoteHost,Client.RemotePort,MaxWaitMiliseconds,GetTickCount-tc]));
+          TLog.NewLog(lterror,ClassName,Format('Deleting %d bytes from TcpClient buffer of %s after max %d miliseconds. Passed: %d',
+            [deletedBytes, Client.ClientRemoteAddr,MaxWaitMiliseconds,GetTickCount-tc]));
           FClientBufferRead.Size:=0;
           DisconnectInvalidClient(false,'Invalid data received in buffer ('+inttostr(deletedBytes)+' bytes)');
         end;
       end;
     Finally
-      LeaveCriticalSection(FNetLock);
+      FNetLock.Release;
     End;
   end;
   if (Result) And (HeaderData.header_type=ntp_response) then begin
@@ -2399,7 +2302,7 @@ begin
       FLastDataSendedTS := GetTickCount;
       TNetData.NetData.IncStatistics(0,0,0,0,0,Buffer.Size);
     Finally
-      LeaveCriticalSection(FNetLock);
+      FNetLock.Release;
     End;
   finally
     Buffer.Free;
@@ -2536,7 +2439,7 @@ begin
     end;
     //
     Send(NetTranferType,CT_NetOp_Hello,0,request_id,data);
-    Result := Client.Active;
+    Result := Client.Connected;
   finally
     data.Free;
   end;
@@ -2613,12 +2516,7 @@ begin
   if Assigned(FTcpIpClient) then begin
     FTcpIpClient.FreeNotification(Self);
     FTcpIpClient.OnConnect := TcpClient_OnConnect;
-    FTcpIpClient.OnCreateHandle := TcpClient_OnCreateHandle;
-    FTcpIpClient.OnDestroyHandle := TcpClient_OnDestroyHandle;
-    FTcpIpClient.OnError := TcpClient_OnError;
     FTcpIpClient.OnDisconnect := TcpClient_OnDisconnect;
-    FTcpIpClient.OnReceive := TcpClient_OnReceive;
-    FTcpIpClient.OnSend := TcpClient_OnSend;
   end;
   TNetData.NetData.NotifyNetConnectionUpdated;
 end;
@@ -2626,7 +2524,7 @@ end;
 procedure TNetConnection.SetConnected(const Value: Boolean);
 begin
   if (Value = GetConnected) then exit;
-  if Value then ConnectTo(Client.RemoteHost,StrToIntDef(Client.RemotePort,CT_NetServer_Port))
+  if Value then ConnectTo(Client.RemoteHost,Client.RemotePort)
   else Client.Disconnect;
 end;
 
@@ -2637,16 +2535,6 @@ begin
   TNetData.NetData.NotifyNetConnectionUpdated;
 end;
 
-procedure TNetConnection.TcpClient_OnCreateHandle(Sender: TObject);
-begin
-  //
-end;
-
-procedure TNetConnection.TcpClient_OnDestroyHandle(Sender: TObject);
-begin
-  //
-end;
-
 procedure TNetConnection.TcpClient_OnDisconnect(Sender: TObject);
 begin
   if self is TNetServerClient then TNetData.NetData.IncStatistics(-1,-1,0,0,0,0)
@@ -2658,29 +2546,13 @@ begin
   TNetData.NetData.NotifyNetConnectionUpdated;
 end;
 
-procedure TNetConnection.TcpClient_OnError(Sender: TObject; SocketError: Integer);
-begin
-  FSocketError := SocketError;
-  TLog.NewLog(ltdebug,Classname,'Error '+inttohex(SocketError,8)+' with connection to '+ClientRemoteAddr);
-end;
-
-procedure TNetConnection.TcpClient_OnReceive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
-begin
-  //
-end;
-
-procedure TNetConnection.TcpClient_OnSend(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
-begin
-  //
-end;
-
 { TNetClientThread }
 
 procedure TNetClientThread.BCExecute;
 Var clientIp : AnsiString;
 begin
   debugstep := 'Initiating...';
-  clientIp := FNetClient.Client.RemoteHost+':'+FNetClient.Client.RemotePort;
+  clientIp := FNetClient.ClientRemoteAddr;
   debugstep := 'Chenking terminated';
   while (Not Terminated) do begin
     debugstep := 'Check connection '+clientIp;
@@ -2946,7 +2818,7 @@ begin
   DebugStep := 'Locking NetClient if exists';
   If TNetData.NetData.ConnectionLock(Self,FNetClient) then begin
     try
-      DebugStep := 'Destroying NetClient';
+      DebugStep := 'Destroying NetClient '+FNetClient.ClientRemoteAddr;
       FreeAndNil(FNetClient);
     finally
       // Not Necessary because on Freeing then Lock is deleted.
@@ -2958,7 +2830,7 @@ end;
 constructor TNetClientDestroyThread.Create(NetClient: TNetClient);
 begin
   FNetClient := NetClient;
-  TLog.NewLog(ltdebug,Classname,'Start destroying NetClient: '+NetClient.Client.RemoteHost+':'+NetClient.Client.RemotePort);
+  TLog.NewLog(ltdebug,Classname,'Start destroying NetClient: '+NetClient.ClientRemoteAddr);
   inherited Create(true);
   FreeOnTerminate := true;
   Suspended := false;

+ 57 - 117
Units/PascalCoin/UNode.pas

@@ -26,12 +26,12 @@ unit UNode;
 interface
 
 uses
-  Classes, UBlockChain, UNetProtocol, UMiner, UAccounts, UCrypto, Windows, UThread;
+  Classes, UBlockChain, UNetProtocol, UMiner, UAccounts, UCrypto, Windows, UThread, SyncObjs;
 
 Type
   TNode = Class(TComponent)
   private
-    FLockNodeOperations : TRTLCriticalSection;
+    FLockNodeOperations : TCriticalSection;
     FNotifyList : TList;
     FBank : TPCBank;
     FOperations : TPCOperationsComp;
@@ -39,9 +39,8 @@ Type
     FMinerThreads : TPCThreadList;
     FBCBankNotify : TPCBankNotify;
     FPeerCache : AnsiString;
+    FDisabledsNewBlocksCount : Integer;
     Procedure OnBankNewBlock(Sender : TObject);
-    Procedure StartLocking(MaxWaitMilliseconds : Cardinal);
-    Procedure EndLocking;
     Procedure OnMinerThreadTerminate(Sender : TObject);
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@@ -72,6 +71,8 @@ Type
     Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
     Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
     Property PeerCache : AnsiString read FPeerCache write FPeerCache;
+    Procedure DisableNewBlocks;
+    Procedure EnableNewBlocks;
   End;
 
   TNodeNotifyEvents = Class;
@@ -157,18 +158,21 @@ Var i : Integer;
   ms : TMemoryStream;
   mtl : TList;
   netConnectionsList : TList;
+  s : String;
 begin
   Result := false;
+  if FDisabledsNewBlocksCount>0 then begin
+    TLog.NewLog(ltinfo,Classname,Format('Cannot Add new BlockChain due is adding disabled - Miner:%s Connection:%s NewBlock:%s',[Inttohex(Integer(SenderMiner),8),
+    Inttohex(Integer(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock)]));
+    exit;
+  end;
   TLog.NewLog(ltdebug,Classname,Format('AddNewBlockChain Miner:%s Connection:%s NewBlock:%s',[Inttohex(Integer(SenderMiner),8),
     Inttohex(Integer(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock)]));
-  Try
-    StartLocking(2000);
-  Except
-    On E: Exception do begin
-      TLog.NewLog(lterror,Classname,'Fatal Error at AddNewBlockChain: '+e.Message);
-      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
-    end;
-  End;
+  If Not TPCThread.TryProtectEnterCriticalSection(Self,2000,FLockNodeOperations) then begin
+    s := 'Cannot AddNewBlockChain due blocking lock operations node';
+    TLog.NewLog(lterror,Classname,s);
+    if TThread.CurrentThread.ThreadID=MainThreadID then raise Exception.Create(s) else exit;
+  end;
   try
     ms := TMemoryStream.Create;
     try
@@ -245,7 +249,7 @@ begin
       end;
     end;
   finally
-    EndLocking;
+    FLockNodeOperations.Release;
     TLog.NewLog(ltdebug,Classname,Format('Finalizing AddNewBlockChain Miner:%s Connection:%s NewBlock:%s',[Inttohex(Integer(SenderMiner),8),
       Inttohex(Integer(SenderConnection),8),TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock) ]));
   End;
@@ -276,18 +280,21 @@ Var
   e : AnsiString;
   mtl : TList;
   netConnectionsList : TList;
+  s : String;
 begin
   Result := -1;
+  if FDisabledsNewBlocksCount>0 then begin
+    errors := Format('Cannot Add Operations due is adding disabled - OpCount:%d',[Operations.OperationsCount]);
+    TLog.NewLog(ltinfo,Classname,errors);
+    exit;
+  end;
   TLog.NewLog(ltdebug,Classname,Format('AddOperations Connection:%s Operations:%d',[
     Inttohex(Integer(SenderConnection),8),Operations.OperationsCount]));
-  Try
-    StartLocking(4000);
-  Except
-    On E: Exception do begin
-      TLog.NewLog(lterror,Classname,'Fatal Error at AddOperations: '+e.Message);
-      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
-    end;
-  End;
+  if Not TPCThread.TryProtectEnterCriticalSection(Self,4000,FLockNodeOperations) then begin
+    s := 'Cannot AddOperations due blocking lock operations node';
+    TLog.NewLog(lterror,Classname,s);
+    if TThread.CurrentThread.ThreadID=MainThreadID then raise Exception.Create(s) else exit;
+  end;
   try
     Result := 0;
     errors := '';
@@ -336,9 +343,9 @@ begin
       valids_operations.Free;
     end;
   finally
-    EndLocking;
-    TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations Connection:%s Operations:%d LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
-      Inttohex(Integer(SenderConnection),8),Operations.OperationsCount,FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]));
+    FLockNodeOperations.Release;
+    TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations Connection:%s Operations:%d',[
+      Inttohex(Integer(SenderConnection),8),Operations.OperationsCount ]));
   end;
   // Notify it!
   for i := 0 to FNotifyList.Count-1 do begin
@@ -347,55 +354,13 @@ begin
 end;
 
 procedure TNode.AutoDiscoverNodes(Const ips : AnsiString);
-{  Function GetIp(var ips_string : AnsiString; var nsa : TNodeServerAddress) : Boolean;
-  Const CT_IP_CHARS = ['a'..'z','A'..'Z','0'..'9','.','-','_'];
-  var i : Integer;
-    port : AnsiString;
-  begin
-    nsa := CT_TNodeServerAddress_NUL;
-    Result := false;
-    if length(trim(ips_string))=0 then begin
-      ips_string := '';
-      exit;
-    end;
-    i := 1;
-    while (i<length(ips_string)) AND (NOT (ips_string[i] IN CT_IP_CHARS)) do inc(i);
-    if (i>1) then ips_string := copy(ips_string,i,length(ips_string));
-    //
-    i := 1;
-    while (i<=length(ips_string)) and (ips_string[i] in CT_IP_CHARS) do inc(i);
-    nsa.ip := copy(ips_string,1,i-1);
-    if (i<=length(ips_string)) and (ips_string[i]=':') then begin
-      inc(i);
-      port := '';
-      while (i<=length(ips_string)) and (ips_string[i] in ['0'..'9']) do begin
-        port := port + ips_string[i];
-        inc(i);
-      end;
-      nsa.port := StrToIntDef(port,0);
-    end;
-    ips_string := copy(ips_string,i+1,length(ips_string));
-    if nsa.port=0 then nsa.port := CT_NetServer_Port;
-    Result := (trim(nsa.ip)<>'');
-  end;}
 Var i,j : Integer;
-{  ips_string : AnsiString;
-  nsa : TNodeServerAddress;}
   nsarr : TNodeServerAddressArray;
 begin
   DecodeIpStringToNodeServerAddressArray(ips+';'+PeerCache,nsarr);
   for i := low(nsarr) to high(nsarr) do begin
     TNetData.NetData.AddServer(nsarr[i]);
   end;
-
-{  ips_string := ips+';'+PeerCache;
-  repeat
-    If GetIp(ips_string,nsa) then begin
-      TNetData.NetData.AddServer(nsa);
-    end;
-  until (ips_string='');
-  //
-}
   j := (CT_MaxServersConnected -  TNetData.NetData.ConnectionsCount(true));
   if j<=0 then exit;
   TNetData.NetData.DiscoverServers;
@@ -405,14 +370,13 @@ constructor TNode.Create(AOwner: TComponent);
 begin
   if Assigned(_Node) then raise Exception.Create('Duplicate nodes protection');
   inherited;
-  InitializeCriticalSection(FLockNodeOperations);
-  TLog.NewLog(ltdebug,Classname,Format('Initialize LockOperations LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
-    FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]));
+  FDisabledsNewBlocksCount := 0;
+  FLockNodeOperations := TCriticalSection.Create;
   FBank := TPCBank.Create(Self);
   FBCBankNotify := TPCBankNotify.Create(Self);
   FBCBankNotify.Bank := FBank;
   FBCBankNotify.OnNewBlock := OnBankNewBlock;
-  FNetServer := TNetServer.Create(Self);
+  FNetServer := TNetServer.Create;
   FMinerThreads := TPCThreadList.Create;
   FOperations := TPCOperationsComp.Create(Self);
   FOperations.bank := FBank;
@@ -490,7 +454,7 @@ begin
   TLog.NewLog(ltinfo,Classname,'Destroying Node - START');
   Try
     step := 'Deleting critical section';
-    DeleteCriticalSection(FLockNodeOperations);
+    FreeAndNil(FLockNodeOperations);
 
     step := 'Desactivating server';
     FNetServer.Active := false;
@@ -526,6 +490,17 @@ begin
   TLog.NewLog(ltinfo,Classname,'Destroying Node - END');
 end;
 
+procedure TNode.DisableNewBlocks;
+begin
+  inc(FDisabledsNewBlocksCount);
+end;
+
+procedure TNode.EnableNewBlocks;
+begin
+  if FDisabledsNewBlocksCount=0 then raise Exception.Create('Dev error 20160924-1');
+  dec(FDisabledsNewBlocksCount);
+end;
+
 class function TNode.EncodeNodeServerAddressArrayToIpString(
   const NodeServerAddressArray: TNodeServerAddressArray): AnsiString;
 var i : Integer;
@@ -540,11 +515,6 @@ begin
   end;
 end;
 
-procedure TNode.EndLocking;
-begin
-  LeaveCriticalSection(FLockNodeOperations);
-end;
-
 function TNode.IsBlockChainValid(var WhyNot : AnsiString): Boolean;
 Var unixtimediff : Integer;
 begin
@@ -650,17 +620,15 @@ function TNode.SendNodeMessage(Target: TNetConnection; TheMessage: AnsiString; v
 Var i : Integer;
   nc : TNetConnection;
   netConnectionsList : TList;
+  s : String;
 begin
-  Try
-    StartLocking(4000);
-  Except
-    On E: Exception do begin
-      TLog.NewLog(lterror,Classname,'Fatal Error at SendNodeMessage: '+e.Message);
-      if TThread.CurrentThread.ThreadID=MainThreadID then raise else exit;
-    end;
-  End;
+  Result := false;
+  if Not TPCThread.TryProtectEnterCriticalSection(Self,4000,FLockNodeOperations) then begin
+    s := 'Cannot Send node message due blocking lock operations node';
+    TLog.NewLog(lterror,Classname,s);
+    if TThread.CurrentThread.ThreadID=MainThreadID then raise Exception.Create(s) else exit;
+  end;
   try
-    Result := false;
     errors := '';
     if assigned(Target) then begin
       Target.Send_Message(TheMessage);
@@ -677,26 +645,7 @@ begin
     end;
     result := true;
   finally
-    EndLocking;
-  end;
-end;
-
-procedure TNode.StartLocking(MaxWaitMilliseconds : Cardinal);
-Var tc : Cardinal;
-  s : String;
-  IsLocked : Boolean;
-begin
-  if MaxWaitMilliseconds>60000 then MaxWaitMilliseconds := 60000;
-  tc := GetTickCount;
-  Repeat
-    IsLocked := TryEnterCriticalSection(FLockNodeOperations);
-    if Not IsLocked then sleep(1);
-  Until (IsLocked) Or (GetTickCount > (tc + MaxWaitMilliseconds));
-  if Not IsLocked then begin
-    s := Format('Cannot lock operations node - LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
-      FLockNodeOperations.LockCount,FLockNodeOperations.RecursionCount,FLockNodeOperations.LockSemaphore,IntToHex(FLockNodeOperations.OwningThread,8) ]);
-    TLog.NewLog(lterror,Classname,s);
-    raise Exception.Create(s);
+    FLockNodeOperations.Release;
   end;
 end;
 
@@ -798,10 +747,10 @@ procedure TThreadNodeNotifyNewBlock.BCExecute;
 begin
   if TNetData.NetData.ConnectionLock(Self,FNetConnection) then begin
     try
-      TLog.NewLog(ltdebug,ClassName,'Sending new block found to '+FNetConnection.Client.RemoteHost+':'+FNetConnection.Client.RemotePort);
+      TLog.NewLog(ltdebug,ClassName,'Sending new block found to '+FNetConnection.Client.ClientRemoteAddr);
       FNetConnection.Send_NewBlockFound;
       if TNode.Node.Operations.OperationsHashTree.OperationsCount>0 then begin
-         TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(TNode.Node.Operations.OperationsHashTree.OperationsCount)+' sanitized operations to '+FNetConnection.Client.RemoteHost+':'+FNetConnection.Client.RemotePort);
+         TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(TNode.Node.Operations.OperationsHashTree.OperationsCount)+' sanitized operations to '+FNetConnection.ClientRemoteAddr);
          FNetConnection.Send_AddOperations(TNode.Node.Operations.OperationsHashTree);
       end;
     finally
@@ -824,21 +773,12 @@ begin
   if TNetData.NetData.ConnectionLock(Self, FNetConnection) then begin
     try
       if FOperationsHashTree.OperationsCount<=0 then exit;
-      TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(FOperationsHashTree.OperationsCount)+' Operations to '+FNetConnection.Client.RemoteHost+':'+FNetConnection.Client.RemotePort);
+      TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(FOperationsHashTree.OperationsCount)+' Operations to '+FNetConnection.ClientRemoteAddr);
       FNetConnection.Send_AddOperations(FOperationsHashTree);
     finally
       TNetData.NetData.ConnectionUnlock(FNetConnection);
     end;
   end;
-
-{  If Not TNetData.NetData.ConnectionExistsAndActive(FNetConnection) then begin
-    TLog.NewLog(ltdebug,Classname,'Connection not active');
-    exit;
-  end;
-  if FOperationsHashTree.OperationsCount<=0 then exit;
-  TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(FOperationsHashTree.OperationsCount)+' Operations to '+FNetConnection.Client.RemoteHost+':'+FNetConnection.Client.RemotePort);
-  FNetConnection.Send_AddOperations(TNode.Node.Operations.OperationsHashTree);
-  }
 end;
 
 constructor TThreadNodeNotifyOperations.Create(NetConnection: TNetConnection;

+ 1 - 0
Units/PascalCoin/UOpTransaction.pas

@@ -222,6 +222,7 @@ begin
     trans.sign.s:='';
     Result := false;
   End;
+  SetLength(s,0);
 end;
 
 function TOpTransaction.GetOperationBufferToHash: TRawBytes;

+ 323 - 0
Units/PascalCoin/UTCPIP.pas

@@ -0,0 +1,323 @@
+unit UTCPIP;
+
+{ Copyright (c) 2016 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 Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+{$IFDEF FPC}
+  {$mode objfpc}
+{$ENDIF}
+
+{$DEFINE DelphiSockets}
+
+uses
+  {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+  Classes, Sysutils,
+  UThread,Sockets;
+
+type
+  TNetTcpIpClient = Class(TComponent)
+  private
+    {$IFDEF DelphiSockets}
+    FTcpIpClient : TCustomIpClient;
+    FOnConnect: TNotifyEvent;
+    FOnDisconnect: TNotifyEvent;
+    FSocketError: Integer;
+    {$ENDIF}
+    function GetConnected: Boolean;
+    function GetRemoteHost: AnsiString;
+    function GetRemotePort: Word;
+    procedure SetRemoteHost(const Value: AnsiString);
+    procedure SetRemotePort(const Value: Word);
+    procedure SetOnConnect(const Value: TNotifyEvent);
+    procedure SetOnDisconnect(const Value: TNotifyEvent);
+    procedure SetSocketError(const Value: Integer);
+    {$IFDEF DelphiSockets}
+    procedure TCustomIpClient_OnError(Sender: TObject; ASocketError: Integer);
+    {$ENDIF}
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Function ClientRemoteAddr : AnsiString;
+    Property RemoteHost : AnsiString read GetRemoteHost Write SetRemoteHost;
+    Property RemotePort : Word read GetRemotePort write SetRemotePort;
+    Property Connected : Boolean read GetConnected;
+    Procedure Disconnect;
+    Function Connect : Boolean;
+    //
+    Function WaitForData(WaitMilliseconds : Integer) : Boolean;
+    function ReceiveBuf(var Buf; BufSize: Integer): Integer;
+    Function SendStream(Stream : TStream) : Int64;
+    //
+    Property OnConnect : TNotifyEvent read FOnConnect write SetOnConnect;
+    Property OnDisconnect : TNotifyEvent read FOnDisconnect write SetOnDisconnect;
+    Function BytesReceived : Int64;
+    Function BytesSent : Int64;
+    Property SocketError : Integer read FSocketError write SetSocketError;
+  End;
+
+  TNetTcpIpServer = Class(TObject)
+  private
+    {$IFDEF DelphiSockets}
+    FTcpIpServer : TTcpServer;
+    FTcpIpClient : TCustomIpClient;
+    {$ENDIF}
+    FNetClients : TPCThreadList;
+    function GetActive: Boolean;
+    procedure SetPort(const Value: Word);  // When a connection is established to a new client, a TNetConnection is created (p2p)
+    {$IFDEF DelphiSockets}
+    procedure OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
+    function GetPort: Word;
+    {$ENDIF}
+  protected
+    Procedure OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient); virtual;
+    procedure SetActive(const Value: Boolean); virtual;
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    Property Active : Boolean read GetActive write SetActive;
+    Property Port : Word read GetPort Write SetPort;
+  End;
+
+
+implementation
+
+uses UConst, ULog;
+
+{ TNetTcpIpClient }
+
+function TNetTcpIpClient.BytesReceived: Int64;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.BytesReceived;
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.BytesSent: Int64;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.BytesSent;
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.ClientRemoteAddr: AnsiString;
+begin
+  If Assigned(FTcpIpClient) then begin
+    {$IFDEF DelphiSockets}
+    Result := FTcpIpClient.RemoteHost+':'+FTcpIpClient.RemotePort;
+    {$ENDIF}
+  end else Result := 'NIL';
+end;
+
+function TNetTcpIpClient.Connect: Boolean;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.Connect;
+  {$ENDIF}
+end;
+
+constructor TNetTcpIpClient.Create(AOwner : TComponent);
+begin
+  inherited;
+  FTcpIpClient := Nil;
+  FSocketError := 0;
+  {$IFDEF DelphiSockets}
+  FTcpIpClient := TTcpClient.Create(Nil);
+  FTcpIpClient.OnConnect := OnConnect;
+  FTcpIpClient.OnDisconnect := OnDisconnect;
+  FTcpIpClient.OnError := TCustomIpClient_OnError;
+  {$ENDIF}
+end;
+
+destructor TNetTcpIpClient.Destroy;
+begin
+  Disconnect;
+  inherited;
+  FreeAndNil(FTcpIpClient);
+end;
+
+procedure TNetTcpIpClient.Disconnect;
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpClient.Disconnect;
+  FTcpIpClient.OnConnect := Nil;
+  FTcpIpClient.OnDisconnect := Nil;
+  FTcpIpClient.OnError := Nil;
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.GetConnected: Boolean;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.Connected;
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.GetRemoteHost: AnsiString;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.RemoteHost;
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.GetRemotePort: Word;
+begin
+  {$IFDEF DelphiSockets}
+  Result := StrToIntDef(FTcpIpClient.RemotePort,0);
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.ReceiveBuf(var Buf; BufSize: Integer): Integer;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.ReceiveBuf(Buf,BufSize);
+  {$ENDIF}
+end;
+
+function TNetTcpIpClient.SendStream(Stream: TStream): Int64;
+Var sp : Int64;
+begin
+  {$IFDEF DelphiSockets}
+  sp := Stream.Position;
+  FTcpIpClient.SendStream(Stream);
+  Result := Stream.Position - sp;
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpClient.SetOnConnect(const Value: TNotifyEvent);
+begin
+  FOnConnect := Value;
+  {$IFDEF DelphiSockets}
+  FTcpIpClient.OnConnect := OnConnect;
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpClient.SetOnDisconnect(const Value: TNotifyEvent);
+begin
+  FOnDisconnect := Value;
+  {$IFDEF DelphiSockets}
+  FTcpIpClient.OnDisconnect := OnDisconnect;
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpClient.SetRemoteHost(const Value: AnsiString);
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpClient.RemoteHost := Value;
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpClient.SetRemotePort(const Value: Word);
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpClient.RemotePort := IntToStr(Value);
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpClient.SetSocketError(const Value: Integer);
+begin
+  FSocketError := Value;
+  if Value<>0 then
+    TLog.NewLog(ltdebug,Classname,'Error '+inttohex(SocketError,8)+' with connection to '+ClientRemoteAddr);
+end;
+
+{$IFDEF DelphiSockets}
+procedure TNetTcpIpClient.TCustomIpClient_OnError(Sender: TObject; ASocketError: Integer);
+begin
+  SocketError := ASocketError;
+end;
+{$ENDIF}
+
+function TNetTcpIpClient.WaitForData(WaitMilliseconds: Integer): Boolean;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpClient.WaitForData(WaitMilliseconds);
+  {$ENDIF}
+end;
+
+{ TNetTcpIpServer }
+
+constructor TNetTcpIpServer.Create;
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpServer := TTcpServer.Create(Nil);
+  FTcpIpServer.OnAccept := OnTcpServerAccept;
+  FTcpIpServer.ServerSocketThread.ThreadCacheSize := CT_MaxClientsConnected;
+  {$ENDIF}
+end;
+
+destructor TNetTcpIpServer.Destroy;
+begin
+  Active := false;
+  {$IFDEF DelphiSockets}
+  FreeAndNil(FTcpIpServer);
+  {$ENDIF}
+  inherited;
+  FreeAndNil(FNetClients);
+end;
+
+function TNetTcpIpServer.GetActive: Boolean;
+begin
+  {$IFDEF DelphiSockets}
+  Result := FTcpIpServer.Active;
+  {$ENDIF}
+end;
+
+function TNetTcpIpServer.GetPort: Word;
+begin
+  {$IFDEF DelphiSockets}
+  Result := StrToIntDef(FTcpIpServer.LocalPort,0);
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpServer.OnNewIncommingConnection(Sender: TObject; Client: TNetTcpIpClient);
+begin
+  //
+end;
+
+{$IFDEF DelphiSockets}
+procedure TNetTcpIpServer.OnTcpServerAccept(Sender: TObject; ClientSocket: TCustomIpClient);
+Var n : TNetTcpIpClient;
+  oldSocket : TCustomIpClient;
+begin
+  n := TNetTcpIpClient.Create(Nil);
+  Try
+    oldSocket := n.FTcpIpClient;
+    n.FTcpIpClient := ClientSocket;
+    OnNewIncommingConnection(Sender,n);
+  Finally
+    n.FTcpIpClient := oldSocket;
+    FreeAndNil(n);
+  End;
+end;
+{$ENDIF}
+
+procedure TNetTcpIpServer.SetActive(const Value: Boolean);
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpServer.Active := Value;
+  {$ENDIF}
+end;
+
+procedure TNetTcpIpServer.SetPort(const Value: Word);
+begin
+  {$IFDEF DelphiSockets}
+  FTcpIpServer.LocalPort := IntToStr(Value);
+  {$ENDIF}
+end;
+
+
+end.

+ 13 - 14
Units/PascalCoin/UThread.pas

@@ -34,8 +34,8 @@ Type
     Class function ThreadCount : Integer;
     Class function GetThread(index : Integer) : TPCThread;
     Class function TerminateAllThreads : Integer;
-    Class Procedure ProtectEnterCriticalSection(Const Sender : TObject; var Lock : TRTLCriticalSection);
-    Class Function TryProtectEnterCriticalSection(Const Sender : TObject; MaxWaitMilliseconds : Cardinal; var Lock : TRTLCriticalSection) : Boolean;
+    Class Procedure ProtectEnterCriticalSection(Const Sender : TObject; var Lock : TCriticalSection);
+    Class Function TryProtectEnterCriticalSection(Const Sender : TObject; MaxWaitMilliseconds : Cardinal; var Lock : TCriticalSection) : Boolean;
     Class Procedure ThreadsListInfo(list: TStrings);
     Property DebugStep : String read FDebugStep write FDebugStep;
   End;
@@ -43,7 +43,7 @@ Type
   TPCThreadList = class
   private
     FList: TList;
-    FLock: TRTLCriticalSection;
+    FLock: TCriticalSection;
   public
     constructor Create;
     destructor Destroy; override;
@@ -116,12 +116,12 @@ begin
   end;
 end;
 
-class procedure TPCThread.ProtectEnterCriticalSection(Const Sender : TObject; var Lock: TRTLCriticalSection);
+class procedure TPCThread.ProtectEnterCriticalSection(Const Sender : TObject; var Lock: TCriticalSection);
 begin
-  if Not TryEnterCriticalSection(Lock) then begin
+  if Not Lock.TryEnter then begin
 //    TLog.NewLog(ltdebug,Sender.Classname,Format('Locked critical section (WAIT): LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
 //      Lock.LockCount,Lock.RecursionCount,Lock.LockSemaphore,IntToHex(Lock.OwningThread,8) ]));
-    EnterCriticalSection(Lock);
+    Lock.Acquire;
 //    TLog.NewLog(ltdebug,Sender.Classname,Format('UnLocked critical section (ENTER): LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',[
 //      Lock.LockCount,Lock.RecursionCount,Lock.LockSemaphore,IntToHex(Lock.OwningThread,8) ]));
   end;
@@ -191,20 +191,19 @@ begin
 end;
 
 class function TPCThread.TryProtectEnterCriticalSection(const Sender: TObject;
-  MaxWaitMilliseconds: Cardinal; var Lock: TRTLCriticalSection): Boolean;
+  MaxWaitMilliseconds: Cardinal; var Lock: TCriticalSection): Boolean;
 Var tc : Cardinal;
   s : String;
 begin
   if MaxWaitMilliseconds>60000 then MaxWaitMilliseconds := 60000;
   tc := GetTickCount;
   Repeat
-    Result := TryEnterCriticalSection(Lock);
+    Result := Lock.TryEnter;
     if Not Result then sleep(1);
   Until (Result) Or (GetTickCount > (tc + MaxWaitMilliseconds));
   if Not Result then begin
-    s := Format('Cannot Protect a critical section by %s after %d milis - LockCount:%d RecursionCount:%d Semaphore:%d LockOwnerThread:%s',
-      [Sender.ClassName,MaxWaitMilliseconds,
-       Lock.LockCount,Lock.RecursionCount,Lock.LockSemaphore,IntToHex(Lock.OwningThread,8) ]);
+    s := Format('Cannot Protect a critical section by %s after %d milis',
+      [Sender.ClassName,MaxWaitMilliseconds]);
     TLog.NewLog(lterror,Classname,s);
   end;
 end;
@@ -233,7 +232,7 @@ end;
 
 constructor TPCThreadList.Create;
 begin
-  InitializeCriticalSection(FLock);
+  FLock := TCriticalSection.Create;
   FList := TList.Create;
 end;
 
@@ -245,7 +244,7 @@ begin
     inherited Destroy;
   finally
     UnlockList;
-    DeleteCriticalSection(FLock);
+    FLock.Free;
   end;
 end;
 
@@ -267,7 +266,7 @@ end;
 
 procedure TPCThreadList.UnlockList;
 begin
-  LeaveCriticalSection(FLock);
+  FLock.Release;
 end;
 
 initialization

+ 1486 - 0
Units/Utils/UDBGridUtils.pas

@@ -0,0 +1,1486 @@
+unit UDBGridUtils;
+
+{ Copyright (c) 2016 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 Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  ABOUT THIS UNIT:
+
+  This units works with UDBStorage.pas unit.
+  So, is to use in Windows version due UDBStorage works with Access database
+
+  }
+
+interface
+
+uses
+  Classes, Grids, Windows, UNode, UAccounts, UBlockChain, UDBStorage, DBGrids, DB, ADODB,
+  UWalletKeys, UAppParams;
+
+Type
+  // TAccountsGrid implements a visual integration of TDrawGrid
+  // to show accounts information
+  TAccountColumnType = (act_account_number,act_account_key,act_balance,act_updated,act_n_operation,act_updated_state);
+  TAccountColumn = Record
+    ColumnType : TAccountColumnType;
+    width : Integer;
+  End;
+
+  TAccountsGrid = Class(TComponent)
+  private
+    FAccountsBalance : Int64;
+    FAccountsList : TOrderedCardinalList;
+    FColumns : Array of TAccountColumn;
+    FDrawGrid : TDrawGrid;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FShowAllAccounts: Boolean;
+    FOnUpdated: TNotifyEvent;
+    FAccountsCount: Integer;
+    procedure SetDrawGrid(const Value: TDrawGrid);
+    Procedure InitGrid;
+    Procedure OnNodeNewOperation(Sender : TObject);
+    procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
+    procedure SetNode(const Value: TNode);
+    function GetNode: TNode;
+    procedure SetShowAllAccounts(const Value: Boolean);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DrawGrid : TDrawGrid read FDrawGrid write SetDrawGrid;
+    Function LockAccountsList : TOrderedCardinalList;
+    Procedure UnlockAccountsList;
+    Property Node : TNode read GetNode write SetNode;
+    Function AccountNumber(GridRow : Integer) : Int64;
+    Procedure SaveToStream(Stream : TStream);
+    Procedure LoadFromStream(Stream : TStream);
+    Property ShowAllAccounts : Boolean read FShowAllAccounts write SetShowAllAccounts;
+    Property AccountsBalance : Int64 read FAccountsBalance;
+    Property AccountsCount : Integer read FAccountsCount;
+    Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
+    Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
+  End;
+
+  TOperationsGrid = Class(TComponent)
+  private
+    FDrawGrid: TDrawGrid;
+    FAccountNumber: Int64;
+    FOperationsResume : TOperationsResumeList;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FPendingOperations: Boolean;
+    Procedure OnNodeNewOperation(Sender : TObject);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    Procedure InitGrid;
+    procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
+    procedure SetDrawGrid(const Value: TDrawGrid);
+    procedure SetAccountNumber(const Value: Int64);
+    procedure SetNode(const Value: TNode);
+    function GetNode: TNode;
+    procedure SetPendingOperations(const Value: Boolean);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DrawGrid : TDrawGrid read FDrawGrid write SetDrawGrid;
+    Property PendingOperations : Boolean read FPendingOperations write SetPendingOperations;
+    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
+    Property Node : TNode read GetNode write SetNode;
+    Procedure UpdateAccountOperations;
+    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+  End;
+
+  TOperationsDBGrid = Class(TComponent)
+  private
+    FDisableds : Integer;
+    FQrySQL : TAdoQuery;
+    FDBGrid: TDBGrid;
+    FAccountNumber: Int64;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FDataSource : TDataSource;
+    FDateEnd: TDate;
+    FBlockStart: Int64;
+    FDateStart: TDate;
+    FBlockEnd: Int64;
+    FNeedRefreshSQL : Boolean;
+    function GetNode: TNode;
+    procedure SetAccountNumber(const Value: Int64);
+    procedure SetDBGrid(const Value: TDBGrid);
+    procedure SetNode(const Value: TNode);
+    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+    procedure SetAdoConnection(const Value: TADOConnection);
+    function GetAdoConnection: TADOConnection;
+    Procedure OnQryCalcFields(DataSet: TDataSet);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    procedure SetBlockEnd(const Value: Int64);
+    procedure SetBlockStart(const Value: Int64);
+    procedure SetDateEnd(const Value: TDate);
+    procedure SetDateStart(const Value: TDate);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  published
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
+    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
+    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
+    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
+    Procedure SetBlocks(bstart,bend : Int64);
+    Property DateStart : TDate read FDateStart write SetDateStart;
+    Property DateEnd : TDate read FDateEnd write SetDateEnd;
+    Procedure SetDates(dStart,dEnd : TDate);
+    Property Node : TNode read GetNode write SetNode;
+    Procedure RefreshData;
+    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
+    Procedure Disable;
+    Procedure Enable;
+    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+  End;
+
+  TBlockChainDBGrid = Class(TComponent)
+  private
+    FDisableds : Integer;
+    FQrySQL : TAdoQuery;
+    FDBGrid: TDBGrid;
+    FNodeNotifyEvents : TNodeNotifyEvents;
+    FDataSource : TDataSource;
+    FDateEnd: TDate;
+    FBlockStart: Int64;
+    FDateStart: TDate;
+    FBlockEnd: Int64;
+    FAccountNumber: Int64;
+    FNeedRefreshSQL : Boolean;
+    function GetNode: TNode;
+    procedure SetDBGrid(const Value: TDBGrid);
+    procedure SetNode(const Value: TNode);
+    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+    procedure SetAdoConnection(const Value: TADOConnection);
+    function GetAdoConnection: TADOConnection;
+    Procedure OnQryCalcFields(DataSet: TDataSet);
+    Procedure OnNodeNewAccount(Sender : TObject);
+    procedure SetBlockEnd(const Value: Int64);
+    procedure SetBlockStart(const Value: Int64);
+    procedure SetDateEnd(const Value: TDate);
+    procedure SetDateStart(const Value: TDate);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
+  published
+  public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
+    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
+    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
+    Procedure SetBlocks(bstart,bend : Int64);
+    Property DateStart : TDate read FDateStart write SetDateStart;
+    Property DateEnd : TDate read FDateEnd write SetDateEnd;
+    Procedure SetDates(dStart,dEnd : TDate);
+    Property Node : TNode read GetNode write SetNode;
+    Procedure RefreshData;
+    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
+    Procedure Disable;
+    Procedure Enable;
+  End;
+
+implementation
+
+uses
+  Graphics, UCrypto, SysUtils, UTime, UOpTransaction, UConst,
+  UFRMPayloadDecoder, ULog;
+
+{ TAccountsGrid }
+
+Const CT_ColumnHeader : Array[TAccountColumnType] Of String =
+  ('Account N.','Key','Balance','Updated','N Oper.','State');
+
+function TAccountsGrid.AccountNumber(GridRow: Integer): Int64;
+begin
+  if GridRow<1 then Result := -1
+  else if FShowAllAccounts then begin
+    if Assigned(Node) then begin
+      Result := GridRow-1;
+    end else Result := -1;
+  end else if GridRow<=FAccountsList.Count then begin
+    Result := Integer(FAccountsList.Get(GridRow-1));
+  end else Result := -1;
+end;
+
+constructor TAccountsGrid.Create(AOwner: TComponent);
+Var i : Integer;
+begin
+  inherited;
+  FOnUpdated := Nil;
+  FAccountsBalance := 0;
+  FAccountsCount := 0;
+  FShowAllAccounts := false;
+  FAccountsList := TOrderedCardinalList.Create;
+  FDrawGrid := Nil;
+  SetLength(FColumns,4);
+  FColumns[0].ColumnType := act_account_number;
+  FColumns[0].width := 80;
+  FColumns[1].ColumnType := act_balance;
+  FColumns[1].width := 100;
+  FColumns[2].ColumnType := act_n_operation;
+  FColumns[2].width := 50;
+  FColumns[3].ColumnType := act_updated_state;
+  FColumns[3].width := 50;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
+end;
+
+destructor TAccountsGrid.Destroy;
+begin
+  FNodeNotifyEvents.Free;
+  FAccountsList.Free;
+  inherited;
+end;
+
+function TAccountsGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TAccountsGrid.InitGrid;
+Var i : Integer;
+  acc : TAccount;
+begin
+  FAccountsBalance := 0;
+  FAccountsCount := FAccountsList.Count;
+  if Not assigned(DrawGrid) then exit;
+  if FShowAllAccounts then begin
+    if Assigned(Node) then begin
+      if Node.Bank.AccountsCount<1 then DrawGrid.RowCount := 2
+      else DrawGrid.RowCount := Node.Bank.AccountsCount+1;
+      FAccountsBalance := Node.Bank.SafeBox.TotalBalance;
+    end else DrawGrid.RowCount := 2;
+  end else begin
+    if FAccountsList.Count<1 then DrawGrid.RowCount := 2
+    else DrawGrid.RowCount := FAccountsList.Count+1;
+    if Assigned(Node) then begin
+      for i := 0 to FAccountsList.Count - 1 do begin
+        acc := Node.Bank.SafeBox.Account( FAccountsList.Get(i) );
+        inc(FAccountsBalance, acc.balance);
+      end;
+    end;
+  end;
+  DrawGrid.FixedRows := 1;
+  if Length(FColumns)=0 then DrawGrid.ColCount := 1
+  else DrawGrid.ColCount := Length(FColumns);
+  DrawGrid.FixedCols := 0;
+  for i := low(FColumns) to high(FColumns) do begin
+    DrawGrid.ColWidths[i] := FColumns[i].width;
+  end;
+  FDrawGrid.DefaultRowHeight := 18;
+  DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
+    {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
+    {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
+    goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
+  FDrawGrid.Invalidate;
+  if Assigned(FOnUpdated) then FOnUpdated(Self);
+end;
+
+procedure TAccountsGrid.LoadFromStream(Stream: TStream);
+Var c,i,j : Integer;
+begin
+  if Stream.Read(c,sizeof(c))<sizeof(c) then exit;
+  if c<=0 then exit;
+  SetLength(FColumns,c);
+  for i := 0 to c - 1 do begin
+    Stream.Read(j,sizeof(j));
+    if (j>=Integer(Low(TAccountColumnType))) And (j<=Integer(High(TAccountColumnType))) then begin
+      FColumns[i].ColumnType := TAccountColumnType(j);
+    end else FColumns[i].ColumnType := act_account_number;
+    Stream.Read(FColumns[i].width,sizeof(FColumns[i].width));
+  end;
+  Stream.Read(j,sizeof(j));
+  If Assigned(FDrawGrid) then FDrawGrid.Width := j;
+  Stream.Read(j,sizeof(j));
+  If Assigned(FDrawGrid) then FDrawGrid.Height := j;
+end;
+
+function TAccountsGrid.LockAccountsList: TOrderedCardinalList;
+begin
+  Result := FAccountsList;
+end;
+
+function TAccountsGrid.MoveRowToAccount(nAccount: Cardinal): Boolean;
+Var oal : TOrderedCardinalList;
+  idx : Integer;
+begin
+  Result := false;
+  if Not Assigned(FDrawGrid) then exit;
+  if Not Assigned(Node) then exit;
+  if FDrawGrid.RowCount<=1 then exit;
+  if FShowAllAccounts then begin
+    If (FDrawGrid.RowCount>nAccount+1) And (nAccount>=0) And (nAccount<Node.Bank.AccountsCount) then begin
+      FDrawGrid.Row := nAccount+1;
+      Result := true;
+    end else begin
+      FDrawGrid.Row := FDrawGrid.RowCount-1;
+    end;
+  end else begin
+    oal := LockAccountsList;
+    try
+      If oal.Find(nAccount,idx) then begin
+        If FDrawGrid.RowCount>idx+1 then begin
+          FDrawGrid.Row := idx+1;
+          Result := true;
+        end else begin
+          FDrawGrid.Row := FDrawGrid.RowCount-1;
+        end;
+      end else begin
+        If FDrawGrid.RowCount>idx+1 then begin
+          FDrawGrid.Row := idx+1;
+        end else begin
+          FDrawGrid.Row := FDrawGrid.RowCount-1;
+        end;
+      end;
+    finally
+      UnlockAccountsList;
+    end;
+  end;
+end;
+
+procedure TAccountsGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDrawGrid) then begin
+      SetDrawGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TAccountsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
+  Function FromColorToColor(colorstart,colordest : Integer; step,totalsteps : Integer) : Integer;
+  var sr,sg,sb,dr,dg,db : Byte;
+    i : Integer;
+  begin
+    i := colorstart;
+    sr := GetRValue(i);
+    sg := GetGValue(i);
+    sb := GetBValue(i);
+    i := colordest;
+    dr := GetRValue(i);
+    dg := GetGValue(i);
+    db := GetBValue(i);
+    sr := sr + (((dr-sr) DIV totalsteps)*step);
+    sg := sg + (((dg-sg) DIV totalsteps)*step);
+    sb := sb + (((db-sb) DIV totalsteps)*step);
+    Result :=RGB(sr,sg,sb);
+  end;
+Var C : TAccountColumn;
+  s : String;
+  n_acc : Int64;
+  account : TAccount;
+  ndiff : Cardinal;
+begin
+  if Not Assigned(Node) then exit;
+
+  if (ACol>=0) AND (ACol<length(FColumns)) then begin
+    C := FColumns[ACol];
+  end else begin
+    C.ColumnType := act_account_number;
+    C.width := -1;
+  end;
+  if (ARow=0) then begin
+    // Header
+    s := CT_ColumnHeader[C.ColumnType];
+    DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter]);
+  end else begin
+    n_acc := AccountNumber(ARow);
+    if (n_acc>=0) then begin
+      if (n_acc>=Node.Bank.AccountsCount) then account := CT_Account_NUL
+      else account := Node.Operations.SafeBoxTransaction.Account(n_acc);
+      ndiff := Node.Bank.BlocksCount - account.updated_block;
+      if (gdSelected in State) then
+        If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
+        else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
+      else DrawGrid.Canvas.Brush.Color := clWindow;
+      DrawGrid.Canvas.FillRect(Rect);
+      InflateRect(Rect,-2,-1);
+      case C.ColumnType of
+        act_account_number : Begin
+          s := TAccountComp.AccountNumberToAccountTxtNumber(n_acc);
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_account_key : Begin
+          s := Tcrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountkey));
+          DrawGrid.Canvas.TextRect(Rect,s,[tfLeft,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_balance : Begin
+          if ndiff=0 then begin
+            // Pending operation... showing final balance
+            DrawGrid.Canvas.Font.Color := clBlue;
+            s := '('+TAccountComp.FormatMoney(account.balance)+')';
+          end else begin
+            s := TAccountComp.FormatMoney(account.balance);
+            if account.balance>0 then DrawGrid.Canvas.Font.Color := ClGreen
+            else if account.balance=0 then DrawGrid.Canvas.Font.Color := clGrayText
+            else DrawGrid.Canvas.Font.Color := clRed;
+          end;
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_updated : Begin
+          s := Inttostr(account.updated_block);
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_n_operation : Begin
+          s := InttoStr(account.n_operation);
+          DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+        End;
+        act_updated_state : Begin
+          if TAccountComp.IsAccountBlockedByProtocol(account.account,Node.Bank.BlocksCount) then begin
+            DrawGrid.Canvas.Brush.Color := clRed;
+            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          end else if ndiff=0 then begin
+            DrawGrid.Canvas.Brush.Color := RGB(255,128,0);
+            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          end else if ndiff<=8 then begin
+            DrawGrid.Canvas.Brush.Color := FromColorToColor(RGB(253,250,115),ColorToRGB(clGreen),ndiff-1,8-1);
+            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          end else begin
+            DrawGrid.Canvas.Brush.Color := clGreen;
+            DrawGrid.Canvas.Ellipse(Rect.Left+1,Rect.Top+1,Rect.Right-1,Rect.Bottom-1);
+          end;
+        End;
+      else
+        s := '(???)';
+        DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter,tfSingleLine]);
+      end;
+    end;
+  end;
+end;
+
+procedure TAccountsGrid.OnNodeNewOperation(Sender: TObject);
+begin
+  If Assigned(FDrawGrid) then FDrawGrid.Invalidate;
+end;
+
+procedure TAccountsGrid.SaveToStream(Stream: TStream);
+Var c,i,j : Integer;
+begin
+  c := Length(FColumns);
+  Stream.Write(c,sizeof(c));
+  for i := 0 to c - 1 do begin
+    j := Integer(FColumns[i].ColumnType);
+    Stream.Write(j,sizeof(j));
+    if Assigned(FDrawGrid) then begin
+      FColumns[i].width := FDrawGrid.ColWidths[i];
+    end;
+    Stream.Write(FColumns[i].width,sizeof(FColumns[i].width));
+  end;
+  j := FDrawGrid.Width;
+  Stream.Write(j,sizeof(j));
+  j := FDrawGrid.Height;
+  Stream.Write(j,sizeof(j));
+end;
+
+procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
+begin
+  if FDrawGrid=Value then exit;
+  FDrawGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    FDrawGrid.OnDrawCell := OnGridDrawCell;
+    InitGrid;
+  end;
+end;
+
+procedure TAccountsGrid.SetNode(const Value: TNode);
+begin
+  if GetNode=Value then exit;
+  FNodeNotifyEvents.Node := Value;
+  InitGrid;
+end;
+
+procedure TAccountsGrid.SetShowAllAccounts(const Value: Boolean);
+begin
+  if FShowAllAccounts=Value then exit;
+  FShowAllAccounts := Value;
+  InitGrid;
+end;
+
+procedure TAccountsGrid.UnlockAccountsList;
+begin
+  InitGrid;
+end;
+
+{ TOperationsGrid }
+
+constructor TOperationsGrid.Create(AOwner: TComponent);
+begin
+  FAccountNumber := 0;
+  FDrawGrid := Nil;
+  FOperationsResume := TOperationsResumeList.Create;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
+  inherited;
+end;
+
+destructor TOperationsGrid.Destroy;
+begin
+  FOperationsResume.Free;
+  FNodeNotifyEvents.Free;
+  inherited;
+end;
+
+function TOperationsGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TOperationsGrid.InitGrid;
+begin
+  if Not Assigned(FDrawGrid) then exit;
+  if FOperationsResume.Count>0 then FDrawGrid.RowCount := FOperationsResume.Count+1
+  else FDrawGrid.RowCount := 2;
+  DrawGrid.FixedRows := 1;
+  DrawGrid.DefaultDrawing := true;
+  DrawGrid.FixedCols := 0;
+  DrawGrid.ColCount := 8;
+  DrawGrid.ColWidths[0] := 110; // Time
+  DrawGrid.ColWidths[1] := 50; // Block
+  DrawGrid.ColWidths[2] := 60; // Account
+  DrawGrid.ColWidths[3] := 150; // OpType
+  DrawGrid.ColWidths[4] := 70; // Amount
+  DrawGrid.ColWidths[5] := 60; // Operation Fee
+  DrawGrid.ColWidths[6] := 70; // Balance
+  DrawGrid.ColWidths[7] := 500; // Payload
+  FDrawGrid.DefaultRowHeight := 18;
+  FDrawGrid.Invalidate;
+  DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
+    {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
+    {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
+    goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
+end;
+
+procedure TOperationsGrid.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDrawGrid) then begin
+      SetDrawGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
+Var s : String;
+  opr : TOperationResume;
+begin
+  opr := CT_TOperationResume_NUL;
+  Try
+  if (ARow=0) then begin
+    // Header
+    case ACol of
+      0 : s := 'Time';
+      1 : s := 'Block';
+      2 : s := 'Account';
+      3 : s := 'Operation';
+      4 : s := 'Amount';
+      5 : s := 'Fee';
+      6 : s := 'Balance';
+      7 : s := 'Payload';
+    else s:= '';
+    end;
+    DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter]);
+  end else begin
+    if (gdSelected in State) then
+      If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
+      else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
+    else DrawGrid.Canvas.Brush.Color := clWindow;
+    DrawGrid.Canvas.FillRect(Rect);
+    InflateRect(Rect,-2,-1);
+    if (ARow<=FOperationsResume.Count) then begin
+      opr := FOperationsResume.OperationResume[ARow-1];
+      if ACol=0 then begin
+        if opr.time=0 then s := '(Pending)'
+        else s := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime(opr.time)));
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=1 then begin
+        s := Inttostr(opr.Block);
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=2 then begin
+        s := TAccountComp.AccountNumberToAccountTxtNumber(opr.AffectedAccount);
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=3 then begin
+        s := opr.OperationTxt;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=4 then begin
+        s := TAccountComp.FormatMoney(opr.Amount);
+        if opr.Amount>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        else if opr.Amount=0 then DrawGrid.Canvas.Font.Color := clGrayText
+        else DrawGrid.Canvas.Font.Color := clRed;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=5 then begin
+        s := TAccountComp.FormatMoney(opr.Fee);
+        if opr.Fee>0 then DrawGrid.Canvas.Font.Color := ClGreen
+        else if opr.Fee=0 then DrawGrid.Canvas.Font.Color := clGrayText
+        else DrawGrid.Canvas.Font.Color := clRed;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=6 then begin
+        if opr.time=0 then begin
+          // Pending operation... showing final balance
+          DrawGrid.Canvas.Font.Color := clBlue;
+          s := '('+TAccountComp.FormatMoney(opr.Balance)+')';
+        end else begin
+          s := TAccountComp.FormatMoney(opr.Balance);
+          if opr.Balance>0 then DrawGrid.Canvas.Font.Color := ClGreen
+          else if opr.Balance=0 then DrawGrid.Canvas.Font.Color := clGrayText
+          else DrawGrid.Canvas.Font.Color := clRed;
+        end;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+      end else if ACol=7 then begin
+        s := opr.PrintablePayload;
+        DrawGrid.Canvas.TextRect(Rect,s,[tfLeft,tfVerticalCenter,tfSingleLine]);
+      end else begin
+        s := '(???)';
+        DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter,tfSingleLine]);
+      end;
+    end;
+  end;
+  Except
+    On E:Exception do begin
+      TLog.NewLog(lterror,Classname,Format('Error at OnGridDrawCell row %d col %d Block %d - %s',[ARow,ACol,opr.Block,E.Message]));
+    end;
+  End;
+end;
+
+procedure TOperationsGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  UpdateAccountOperations;
+end;
+
+procedure TOperationsGrid.OnNodeNewOperation(Sender: TObject);
+Var Op : TPCOperation;
+  l : TList;
+begin
+  if AccountNumber<0 then UpdateAccountOperations
+  else begin
+    Op := TPCOperation(Sender);
+    l := TList.Create;
+    Try
+      Op.AffectedAccounts(l);
+      if l.IndexOf(TObject(Integer(AccountNumber)))>=0 then UpdateAccountOperations;
+    Finally
+      l.Free;
+    End;
+  end;
+end;
+
+procedure TOperationsGrid.SetAccountNumber(const Value: Int64);
+begin
+  if FAccountNumber=Value then exit;
+  FAccountNumber := Value;
+  if FAccountNumber>=0 then FPendingOperations := false;
+  UpdateAccountOperations;
+end;
+
+procedure TOperationsGrid.SetDrawGrid(const Value: TDrawGrid);
+begin
+  if FDrawGrid=Value then exit;
+  FDrawGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    FDrawGrid.OnDrawCell := OnGridDrawCell;
+    InitGrid;
+  end;
+end;
+
+procedure TOperationsGrid.SetNode(const Value: TNode);
+begin
+  if GetNode=Value then exit;
+  FNodeNotifyEvents.Node := Value;
+  UpdateAccountOperations; // New Build 1.0.3
+end;
+
+procedure TOperationsGrid.SetPendingOperations(const Value: Boolean);
+begin
+  FPendingOperations := Value;
+  if FPendingOperations then  FAccountNumber := -1;
+  UpdateAccountOperations;
+end;
+
+procedure TOperationsGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
+Var i : Integer;
+  opr : TOperationResume;
+  FRM : TFRMPayloadDecoder;
+begin
+  if Not Assigned(FDrawGrid) then exit;
+  if (FDrawGrid.Row<=0) Or (FDrawGrid.Row>FOperationsResume.Count) then exit;
+  opr := FOperationsResume.OperationResume[FDrawGrid.Row-1];
+  FRM := TFRMPayloadDecoder.Create(FDrawGrid.Owner);
+  try
+    FRM.Init(opr.Block,opr.time,opr.OperationTxt,opr.OriginalPayload,WalletKeys,AppParams);
+    FRM.ShowModal;
+  finally
+    FRM.Free;
+  end;
+end;
+
+procedure TOperationsGrid.UpdateAccountOperations;
+Var list : TList;
+  i,j : Integer;
+  OPR : TOperationResume;
+  Op : TPCOperation;
+begin
+  FOperationsResume.Clear;
+  Try
+    if Not Assigned(Node) then exit;
+    if FPendingOperations then begin
+      for i := Node.Operations.Count - 1 downto 0 do begin
+        Op := Node.Operations.OperationsHashTree.GetOperation(i);
+        If TDBStorage.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
+          OPR.Block := Node.Operations.OperationBlock.block;
+          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
+          FOperationsResume.Add(OPR);
+        end;
+      end;
+    end else begin
+      if AccountNumber<0 then begin
+        list := TList.Create;
+        try
+          for i := 0 to Node.Operations.Count-1 do begin
+            Op := Node.Operations.Operation[i];
+            If TDBStorage.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
+              OPR.Block := Node.Operations.OperationBlock.block;
+              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
+              FOperationsResume.Add(OPR);
+            end;
+          end;
+        finally
+          list.Free;
+        end;
+      end else begin
+        list := TList.Create;
+        Try
+          Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
+          for i := list.Count - 1 downto 0 do begin
+            Op := Node.Operations.OperationsHashTree.GetOperation(Integer(list[i]));
+            If TDBStorage.OperationToOperationResume(Op,AccountNumber,OPR) then begin
+              OPR.Block := Node.Operations.OperationBlock.block;
+              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
+              FOperationsResume.Add(OPR);
+            end;
+          end;
+        Finally
+          list.Free;
+        End;
+        if Node.Bank.Storage is TDBStorage then begin
+          TDBStorage(Node.Bank.Storage).GetOperationsFromAccount(FOperationsResume,AccountNumber,0,200);
+        end;
+      end;
+    end;
+  Finally
+    InitGrid;
+  End;
+end;
+
+{ TOperationsDBGrid }
+
+Type TAuxDBGrid = Class(TDBGrid);
+
+constructor TOperationsDBGrid.Create(AOwner: TComponent);
+Var i : Integer;
+  fld : TField;
+begin
+  inherited;
+  FNeedRefreshSQL := false;
+  FDisableds := 0;
+  FDBGrid := Nil;
+  FQrySQL := TADOQuery.Create(Self);
+  FQrySQL.OnCalcFields := OnQryCalcFields;
+  FqrySQL.CursorLocation := clUseClient;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FDataSource := TDataSource.Create(Self);
+  FDataSource.DataSet := FQrySQL;
+  FAccountNumber := -1; // all
+  FBlockStart := -1;
+  FBlockEnd := -1;
+  FDateStart := 0;
+  FDateEnd := 0;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_block;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  //
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_s_timestamp;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  //
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_optype;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_optype_op;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_account;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_other_account;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TDateTimeField.Create(FQrySQL);
+  fld.FieldName := 'datetime';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'op_account';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 100;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'operation';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 100;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'payload_txt';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 600;
+  //
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_s_amount;
+  fld.DataSet := FQrySQL;
+  fld := TLargeintField.Create(FQrySQL);
+  fld.FieldName := 'amount';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_s_fee;
+  fld.DataSet := FQrySQL;
+  fld := TLargeintField.Create(FQrySQL);
+  fld.FieldName := 'fee';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_s_balance;
+  fld.DataSet := FQrySQL;
+  fld := TLargeintField.Create(FQrySQL);
+  fld.FieldName := 'balance';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_Operations_rawpayload;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 255;
+end;
+
+destructor TOperationsDBGrid.Destroy;
+begin
+  FNodeNotifyEvents.Free;
+  FDataSource.Free;
+  FQrySQL.Free;
+  inherited;
+end;
+
+procedure TOperationsDBGrid.Disable;
+begin
+  inc(FDisableds);
+end;
+
+procedure TOperationsDBGrid.Enable;
+begin
+  dec(FDisableds);
+  if FDisableds>0 then exit;
+  FDisableds := 0;
+  if FNeedRefreshSQL then RefreshData;
+end;
+
+function TOperationsDBGrid.GetAdoConnection: TADOConnection;
+begin
+  Result := FQrySQL.Connection;
+end;
+
+function TOperationsDBGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TOperationsDBGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=opRemove then begin
+    if (AComponent=FDBGrid) then begin
+      SetDBGrid(Nil);
+    end;
+  end;
+end;
+
+procedure TOperationsDBGrid.OnGridDrawColumnCell(Sender: TObject;
+  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+Var c : TCanvas;
+  s : String;
+  r : TRect;
+begin
+  c := TDBGrid(Sender).Canvas;
+  r := Rect;
+  if (gdSelected in State) then
+    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
+    else c.Brush.Color := clGradientInactiveCaption
+  else c.Brush.Color := clWindow;
+  c.FillRect(Rect);
+  if SameText(Column.FieldName,'amount') Or
+     SameText(Column.FieldName,'fee') Or
+     SameText(Column.FieldName,'balance')
+      then begin
+    c.FillRect(Rect);
+    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
+    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
+    else c.Font.Color := clRed;
+    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
+    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+  end else begin
+    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
+  end;
+
+  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
+     not (gdPressed in State) then
+  begin
+    InflateRect(r, 1, 1);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
+  end;
+  //
+end;
+
+procedure TOperationsDBGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.OnQryCalcFields(DataSet: TDataSet);
+Var fld : TField;
+  raw : TRawBytes;
+  s : AnsiString;
+begin
+  fld := DataSet.FieldByName('datetime');
+  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime( StrToIntDef( DataSet.FieldByName(CT_TblFld_Operations_s_timestamp).AsString, 0)));
+  fld := DataSet.FieldByName('op_account');
+  fld.AsString := TAccountComp.AccountNumberToAccountTxtNumber(DataSet.FieldByName(CT_TblFld_Operations_account).AsInteger);
+  fld := DataSet.FieldByName('payload_txt');
+  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_Operations_rawpayload),raw);
+  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
+    fld.AsString := s;
+  end else begin
+    fld.AsString := TCrypto.ToHexaString(raw);
+  end;
+  //
+  fld := DataSet.FieldByName('amount');
+  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_amount).AsString,0);
+  fld := DataSet.FieldByName('balance');
+  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_balance).AsString,0);
+  fld := DataSet.FieldByName('fee');
+  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_fee).AsString,0);
+
+  fld := DataSet.FieldByName('operation');
+          case dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger of
+            0 : fld.AsString := 'Blockchain reward';
+            CT_Op_Transaction : begin
+              if dataset.FieldByName(CT_TblFld_Operations_optype_op).AsInteger=0 then begin
+                fld.AsString := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end else begin
+                fld.AsString := 'Transaction Received from '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
+              end;
+            end;
+            CT_Op_Changekey : Begin
+              fld.AsString := 'Change Key';
+            End;
+            CT_Op_Recover : begin
+              fld.AsString := 'Recover founds';
+            end;
+          else
+            fld.AsString := 'Unknown OpType:'+Inttostr(dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger);
+          end;
+end;
+
+procedure TOperationsDBGrid.RefreshData;
+Var sql : AnsiString;
+begin
+  if FDisableds>0 then begin
+    FNeedRefreshSQL := true;
+    exit;
+  end;
+  FNeedRefreshSQL := false;
+  sql := TDBStorage.GetOperationsFromAccountSQL(AccountNumber,BlockStart,BlockEnd,DateStart,DateEnd, 0,50000);
+  If FQrySQL.Connection=Nil then exit;
+  if Node=Nil then exit;
+  if FDBGrid=Nil then exit;
+
+  FQrySQL.DisableControls;
+  try
+    FQrySQL.Close;
+    FQrySQL.SQL.Text := sql;
+    FQrySQL.Open;
+  finally
+    FQrySQL.EnableControls;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetAccountNumber(const Value: Int64);
+begin
+  if FAccountNumber=Value then exit;
+  FAccountNumber := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetAdoConnection(const Value: TADOConnection);
+begin
+  FQrySQL.Connection := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlockEnd(const Value: Int64);
+begin
+  if FBlockEnd=Value then exit;
+  FBlockEnd := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlocks(bstart, bend: Int64);
+begin
+  FBlockStart := bstart;
+  FBlockEnd := bend;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetBlockStart(const Value: Int64);
+begin
+  if FBlockStart=Value then exit;
+  FBlockStart := Value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDateEnd(const Value: TDate);
+Var d,h,m,y : word;
+begin
+  if FDateEnd=Value then exit;
+  decodedate(value,y,m,d);
+  FDateEnd := EncodeDate(y,m,d);
+  if FDateStart>=FDateEnd then FDateStart := FDateEnd;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDates(dStart, dEnd: TDate);
+begin
+  Disable;
+  try
+    DateStart := dStart;
+    DateEnd := dEnd;
+  finally
+    Enable;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetDateStart(const Value: TDate);
+Var d,h,m,y : word;
+begin
+  if FDateStart=Value then exit;
+  decodedate(value,y,m,d);
+  FDateStart := EncodeDate(y,m,d);
+  if FDateStart>=FDateEnd then FDateEnd := FDateStart;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.SetDBGrid(const Value: TDBGrid);
+  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
+  Var c : TColumn;
+  begin
+    c := DBgrid.Columns.Add;
+    c.FieldName := fieldname;
+    c.Title.Caption := displayname;
+    c.Width := colwidth;
+    c.Title.Font.Style := [fsBold];
+    c.Title.Alignment := taCenter;
+  end;
+begin
+  if FDBGrid=Value then exit;
+  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
+  FDBGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    Value.DataSource := FDataSource;
+    Value.ReadOnly := true;
+    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
+    FDBGrid.DefaultDrawing := false;
+    FDBGrid.Columns.Clear;
+    AddColumn(CT_TblFld_Operations_block,'Block',50);
+    AddColumn('datetime','Date/Time',120);
+    AddColumn('op_account','Account',70);
+    AddColumn('operation','Operation',150);
+    AddColumn('amount','Amount',80);
+    AddColumn('fee','Fee',60);
+    AddColumn('balance','Balance',80);
+    AddColumn('payload_txt','Payload',280);
+    RefreshData;
+  end;
+end;
+
+procedure TOperationsDBGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node:= value;
+  RefreshData;
+end;
+
+procedure TOperationsDBGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams: TAppParams);
+Var FRM : TFRMPayloadDecoder;
+  raw : TRawBytes;
+begin
+  if Not Assigned(FDBGrid) Or Not Assigned(FQrySQL) then exit;
+  If FQrySQL.Eof then exit;
+  FRM := TFRMPayloadDecoder.Create(FDBGrid.Owner);
+  try
+    TDBStorage.DBStringFieldToRaw(FQrySQL.FieldByName(CT_TblFld_Operations_rawpayload),raw);
+    FRM.Init(FQrySQL.FieldByName(CT_TblFld_Operations_block).AsInteger,
+      StrToIntDef( FQrySQL.FieldByName(CT_TblFld_Operations_s_timestamp).AsString,0),
+      FQrySQL.FieldByName('operation').AsString,
+      raw,WalletKeys,AppParams);
+    FRM.ShowModal;
+  finally
+    FRM.Free;
+  end;
+end;
+
+{ TBlockChainDBGrid }
+
+constructor TBlockChainDBGrid.Create(AOwner: TComponent);
+Var fld : TField;
+begin
+  inherited;
+  FNeedRefreshSQL := False;
+  FBlockStart := -1;
+  FBlockEnd := -1;
+  FDateEnd := 0;
+  FDateStart := 0;
+  FDisableds := 0;
+  FQrySQL := TADOQuery.Create(Self);
+  FQrySQL.OnCalcFields := OnQryCalcFields;
+  FqrySQL.CursorLocation := clUseClient;
+  FDBGrid := Nil;
+  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
+  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
+  FDataSource := TDataSource.Create(Self);
+  FDataSource.DataSet := FQrySQL;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_block;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_s_timestamp;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_s_reward;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_s_fee;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_s_compact_target;
+  fld.DataSet := FQrySQL;
+  fld.Visible := true;
+  fld := TIntegerField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_operations_count;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  fld := TDateTimeField.Create(FQrySQL);
+  fld.FieldName := 'datetime';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_proof_of_work;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  TStringField(fld).Size := 64;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_safe_box_hash;
+  fld.DataSet := FQrySQL;
+  fld.Visible := false;
+  TStringField(fld).Size := 64;
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'target';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 8;
+
+  fld := TLargeintField.Create(FQrySQL);
+  fld.FieldName := 'reward';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+
+  fld := TLargeintField.Create(FQrySQL);
+  fld.FieldName := 'fee';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := CT_TblFld_BlockChain_rawpayload;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 255;
+
+  fld := TStringField.Create(FQrySQL);
+  fld.FieldName := 'payload_decoded';
+  fld.Calculated := true;
+  fld.FieldKind := fkCalculated;
+  fld.DataSet := FQrySQL;
+  TStringField(fld).Size := 600;
+end;
+
+destructor TBlockChainDBGrid.Destroy;
+begin
+  SetDBGrid(Nil);
+  FNodeNotifyEvents.Free;
+  FDataSource.Free;
+  FQrySQL.Free;
+  inherited;
+end;
+
+procedure TBlockChainDBGrid.Disable;
+begin
+  Inc(FDisableds);
+end;
+
+procedure TBlockChainDBGrid.Enable;
+begin
+  if FDisableds<0 then exit;
+  Dec(FDisableds);
+  if FNeedRefreshSQL then RefreshData;
+end;
+
+function TBlockChainDBGrid.GetAdoConnection: TADOConnection;
+begin
+  Result := FQrySQL.Connection;
+end;
+
+function TBlockChainDBGrid.GetNode: TNode;
+begin
+  Result := FNodeNotifyEvents.Node;
+end;
+
+procedure TBlockChainDBGrid.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation=OpRemove then begin
+    if AComponent=FDBGrid then FDBGrid:=Nil;
+  end;
+end;
+
+procedure TBlockChainDBGrid.OnGridDrawColumnCell(Sender: TObject;
+  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
+Var c : TCanvas;
+  s : String;
+  r : TRect;
+begin
+  c := TDBGrid(Sender).Canvas;
+  r := Rect;
+  if (gdSelected in State) then
+    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
+    else c.Brush.Color := clGradientInactiveCaption
+  else c.Brush.Color := clWindow;
+  c.FillRect(Rect);
+  if SameText(Column.FieldName,'reward') Or
+     SameText(Column.FieldName,'fee')
+      then begin
+    c.FillRect(Rect);
+    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
+    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
+    else c.Font.Color := clRed;
+    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
+    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
+  end else begin
+    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
+  end;
+
+  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
+     not (gdPressed in State) then
+  begin
+    InflateRect(r, 1, 1);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
+    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
+  end;
+  //
+end;
+
+procedure TBlockChainDBGrid.OnNodeNewAccount(Sender: TObject);
+begin
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.OnQryCalcFields(DataSet: TDataSet);
+Var fld : TField;
+  raw : TRawBytes;
+  s : AnsiString;
+begin
+  fld := DataSet.FieldByName('datetime');
+  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime(StrToIntDef( DataSet.FieldByName(CT_TblFld_BlockChain_s_timestamp).AsString,0)));
+  fld := DataSet.FieldByName('target');
+  fld.AsString :=  IntToHex(StrToIntDef( DataSet.FieldByName(CT_TblFld_BlockChain_s_compact_target).AsString,0),8);
+  DataSet.FieldByName('reward').AsLargeInt := StrToIntDef(DataSet.FieldByName(CT_TblFld_BlockChain_s_reward).AsString,0);
+  DataSet.FieldByName('fee').AsLargeInt := StrToIntDef(DataSet.FieldByName(CT_TblFld_BlockChain_s_fee).AsString,0);
+  fld := DataSet.FieldByName('payload_decoded');
+  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_BlockChain_rawpayload),raw);
+  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
+    fld.AsString := s;
+  end else begin
+    fld.AsString := TCrypto.ToHexaString(raw);
+  end;
+end;
+
+procedure TBlockChainDBGrid.RefreshData;
+Var sql : AnsiString;
+begin
+  if FDisableds>0 then begin
+    FNeedRefreshSQL := true;
+    exit;
+  end;
+  FNeedRefreshSQL := false;
+  sql := TDBStorage.GetBlockChainSQL(BlockStart,BlockEnd,DateStart,DateEnd, 0,300);
+  If FQrySQL.Connection=Nil then exit;
+  if Node=Nil then exit;
+  if FDBGrid=Nil then exit;
+
+  FQrySQL.DisableControls;
+  try
+    FQrySQL.Close;
+    FQrySQL.SQL.Text := sql;
+    FQrySQL.Open;
+  finally
+    FQrySQL.EnableControls;
+  end;
+end;
+
+procedure TBlockChainDBGrid.SetAdoConnection(const Value: TADOConnection);
+begin
+  FQrySQL.Connection := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetBlockEnd(const Value: Int64);
+begin
+  if FBlockEnd=Value then exit;
+  FBlockEnd := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetBlocks(bstart, bend: Int64);
+begin
+  disable;
+  Try
+    BlockStart := bstart;
+    BlockEnd := bEnd;
+  Finally
+    Enable;
+  End;
+end;
+
+procedure TBlockChainDBGrid.SetBlockStart(const Value: Int64);
+begin
+  if FBlockStart=Value then exit;
+  FBlockStart:=Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDateEnd(const Value: TDate);
+begin
+  if FDateEnd=Value then exit;
+  FDateEnd := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDates(dStart, dEnd: TDate);
+begin
+  Disable;
+  Try
+    DateStart := dStart;
+    DateEnd := dEnd;
+  Finally
+    Enable;
+  End;
+end;
+
+procedure TBlockChainDBGrid.SetDateStart(const Value: TDate);
+begin
+  if FDateStart=Value then exit;
+  FDateStart := Value;
+  RefreshData;
+end;
+
+procedure TBlockChainDBGrid.SetDBGrid(const Value: TDBGrid);
+  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
+  Var c : TColumn;
+  begin
+    c := DBgrid.Columns.Add;
+    c.FieldName := fieldname;
+    c.Title.Caption := displayname;
+    c.Width := colwidth;
+    c.Title.Font.Style := [fsBold];
+    c.Title.Alignment := taCenter;
+  end;
+begin
+  if FDBGrid=Value then exit;
+  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
+  FDBGrid := Value;
+  if Assigned(Value) then begin
+    Value.FreeNotification(self);
+    Value.DataSource := FDataSource;
+    Value.ReadOnly := true;
+    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
+    FDBGrid.DefaultDrawing := false;
+    FDBGrid.Columns.Clear;
+    AddColumn(CT_TblFld_BlockChain_block,'Block',60);
+    AddColumn('datetime','Date/Time',120);
+    AddColumn(CT_TblFld_BlockChain_operations_count,'Ops.',30);
+    AddColumn('reward','Reward',80);
+    AddColumn('fee','Fee',60);
+    AddColumn('target','Target',60);
+    AddColumn('payload_decoded','Miner Payload',150);
+    AddColumn(CT_TblFld_BlockChain_proof_of_work,'Proof of Work',400);
+    AddColumn(CT_TblFld_BlockChain_safe_box_hash,'Safe Box Hash',400);
+    RefreshData;
+  end;
+end;
+
+procedure TBlockChainDBGrid.SetNode(const Value: TNode);
+begin
+  FNodeNotifyEvents.Node := Value;
+  RefreshData;
+end;
+
+end.

+ 1 - 1
Units/Utils/UFolderHelper.pas

@@ -39,10 +39,10 @@ Type TFileVersionInfo = record
   strict private
     class function GetFolder(const aCSIDL: Integer): string; static;
     class function GetAppDataFolder : string; static;
-    class function GetUserDataFolder : string; static;
   public
     class function GetPascalCoinDataFolder : string; static;
     class Function GetTFileVersionInfo(Const FileName : String) : TFileVersionInfo; static;
+    class function GetUserDataFolder : string; static;
   end;
 
 implementation

+ 2 - 1091
Units/Utils/UGridUtils.pas

@@ -16,8 +16,8 @@ unit UGridUtils;
 interface
 
 uses
-  Classes, Grids, Windows, UNode, UAccounts, UBlockChain, UDBStorage, DBGrids, DB, ADODB,
-  UWalletKeys, UAppParams;
+  Classes, Grids, Windows, UNode, UAccounts, UBlockChain,
+  UWalletKeys;
 
 Type
   // TAccountsGrid implements a visual integration of TDrawGrid
@@ -64,128 +64,6 @@ Type
     Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
   End;
 
-  TOperationsGrid = Class(TComponent)
-  private
-    FDrawGrid: TDrawGrid;
-    FAccountNumber: Int64;
-    FOperationsResume : TOperationsResumeList;
-    FNodeNotifyEvents : TNodeNotifyEvents;
-    FPendingOperations: Boolean;
-    Procedure OnNodeNewOperation(Sender : TObject);
-    Procedure OnNodeNewAccount(Sender : TObject);
-    Procedure InitGrid;
-    procedure OnGridDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
-    procedure SetDrawGrid(const Value: TDrawGrid);
-    procedure SetAccountNumber(const Value: Int64);
-    procedure SetNode(const Value: TNode);
-    function GetNode: TNode;
-    procedure SetPendingOperations(const Value: Boolean);
-  protected
-    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
-  public
-    Constructor Create(AOwner : TComponent); override;
-    Destructor Destroy; override;
-    Property DrawGrid : TDrawGrid read FDrawGrid write SetDrawGrid;
-    Property PendingOperations : Boolean read FPendingOperations write SetPendingOperations;
-    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
-    Property Node : TNode read GetNode write SetNode;
-    Procedure UpdateAccountOperations;
-    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
-  End;
-
-  TOperationsDBGrid = Class(TComponent)
-  private
-    FDisableds : Integer;
-    FQrySQL : TAdoQuery;
-    FDBGrid: TDBGrid;
-    FAccountNumber: Int64;
-    FNodeNotifyEvents : TNodeNotifyEvents;
-    FDataSource : TDataSource;
-    FDateEnd: TDate;
-    FBlockStart: Int64;
-    FDateStart: TDate;
-    FBlockEnd: Int64;
-    FNeedRefreshSQL : Boolean;
-    function GetNode: TNode;
-    procedure SetAccountNumber(const Value: Int64);
-    procedure SetDBGrid(const Value: TDBGrid);
-    procedure SetNode(const Value: TNode);
-    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
-    procedure SetAdoConnection(const Value: TADOConnection);
-    function GetAdoConnection: TADOConnection;
-    Procedure OnQryCalcFields(DataSet: TDataSet);
-    Procedure OnNodeNewAccount(Sender : TObject);
-    procedure SetBlockEnd(const Value: Int64);
-    procedure SetBlockStart(const Value: Int64);
-    procedure SetDateEnd(const Value: TDate);
-    procedure SetDateStart(const Value: TDate);
-  protected
-    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
-  published
-  public
-    Constructor Create(AOwner : TComponent); override;
-    Destructor Destroy; override;
-    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
-    Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
-    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
-    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
-    Procedure SetBlocks(bstart,bend : Int64);
-    Property DateStart : TDate read FDateStart write SetDateStart;
-    Property DateEnd : TDate read FDateEnd write SetDateEnd;
-    Procedure SetDates(dStart,dEnd : TDate);
-    Property Node : TNode read GetNode write SetNode;
-    Procedure RefreshData;
-    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
-    Procedure Disable;
-    Procedure Enable;
-    Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
-  End;
-
-  TBlockChainDBGrid = Class(TComponent)
-  private
-    FDisableds : Integer;
-    FQrySQL : TAdoQuery;
-    FDBGrid: TDBGrid;
-    FNodeNotifyEvents : TNodeNotifyEvents;
-    FDataSource : TDataSource;
-    FDateEnd: TDate;
-    FBlockStart: Int64;
-    FDateStart: TDate;
-    FBlockEnd: Int64;
-    FAccountNumber: Int64;
-    FNeedRefreshSQL : Boolean;
-    function GetNode: TNode;
-    procedure SetDBGrid(const Value: TDBGrid);
-    procedure SetNode(const Value: TNode);
-    Procedure OnGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
-    procedure SetAdoConnection(const Value: TADOConnection);
-    function GetAdoConnection: TADOConnection;
-    Procedure OnQryCalcFields(DataSet: TDataSet);
-    Procedure OnNodeNewAccount(Sender : TObject);
-    procedure SetBlockEnd(const Value: Int64);
-    procedure SetBlockStart(const Value: Int64);
-    procedure SetDateEnd(const Value: TDate);
-    procedure SetDateStart(const Value: TDate);
-  protected
-    procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
-  published
-  public
-    Constructor Create(AOwner : TComponent); override;
-    Destructor Destroy; override;
-    Property DBGrid : TDBGrid read FDBGrid write SetDBGrid;
-    Property BlockStart : Int64 read FBlockStart write SetBlockStart;
-    Property BlockEnd : Int64 read FBlockEnd write SetBlockEnd;
-    Procedure SetBlocks(bstart,bend : Int64);
-    Property DateStart : TDate read FDateStart write SetDateStart;
-    Property DateEnd : TDate read FDateEnd write SetDateEnd;
-    Procedure SetDates(dStart,dEnd : TDate);
-    Property Node : TNode read GetNode write SetNode;
-    Procedure RefreshData;
-    Property AdoConnection : TADOConnection read GetAdoConnection write SetAdoConnection;
-    Procedure Disable;
-    Procedure Enable;
-  End;
-
 implementation
 
 uses
@@ -511,971 +389,4 @@ begin
   InitGrid;
 end;
 
-{ TOperationsGrid }
-
-constructor TOperationsGrid.Create(AOwner: TComponent);
-begin
-  FAccountNumber := 0;
-  FDrawGrid := Nil;
-  FOperationsResume := TOperationsResumeList.Create;
-  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
-  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
-  FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
-  inherited;
-end;
-
-destructor TOperationsGrid.Destroy;
-begin
-  FOperationsResume.Free;
-  FNodeNotifyEvents.Free;
-  inherited;
-end;
-
-function TOperationsGrid.GetNode: TNode;
-begin
-  Result := FNodeNotifyEvents.Node;
-end;
-
-procedure TOperationsGrid.InitGrid;
-begin
-  if Not Assigned(FDrawGrid) then exit;
-  if FOperationsResume.Count>0 then FDrawGrid.RowCount := FOperationsResume.Count+1
-  else FDrawGrid.RowCount := 2;
-  DrawGrid.FixedRows := 1;
-  DrawGrid.DefaultDrawing := true;
-  DrawGrid.FixedCols := 0;
-  DrawGrid.ColCount := 8;
-  DrawGrid.ColWidths[0] := 110; // Time
-  DrawGrid.ColWidths[1] := 50; // Block
-  DrawGrid.ColWidths[2] := 60; // Account
-  DrawGrid.ColWidths[3] := 150; // OpType
-  DrawGrid.ColWidths[4] := 70; // Amount
-  DrawGrid.ColWidths[5] := 60; // Operation Fee
-  DrawGrid.ColWidths[6] := 70; // Balance
-  DrawGrid.ColWidths[7] := 500; // Payload
-  FDrawGrid.DefaultRowHeight := 18;
-  FDrawGrid.Invalidate;
-  DrawGrid.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
-    {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
-    {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
-    goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
-end;
-
-procedure TOperationsGrid.Notification(AComponent: TComponent; Operation: TOperation);
-begin
-  inherited;
-  if Operation=opRemove then begin
-    if (AComponent=FDrawGrid) then begin
-      SetDrawGrid(Nil);
-    end;
-  end;
-end;
-
-procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
-Var s : String;
-  opr : TOperationResume;
-begin
-  opr := CT_TOperationResume_NUL;
-  Try
-  if (ARow=0) then begin
-    // Header
-    case ACol of
-      0 : s := 'Time';
-      1 : s := 'Block';
-      2 : s := 'Account';
-      3 : s := 'Operation';
-      4 : s := 'Amount';
-      5 : s := 'Fee';
-      6 : s := 'Balance';
-      7 : s := 'Payload';
-    else s:= '';
-    end;
-    DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter]);
-  end else begin
-    if (gdSelected in State) then
-      If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
-      else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
-    else DrawGrid.Canvas.Brush.Color := clWindow;
-    DrawGrid.Canvas.FillRect(Rect);
-    InflateRect(Rect,-2,-1);
-    if (ARow<=FOperationsResume.Count) then begin
-      opr := FOperationsResume.OperationResume[ARow-1];
-      if ACol=0 then begin
-        if opr.time=0 then s := '(Pending)'
-        else s := DateTimeToStr(UnivDateTime2LocalDateTime(UnixToUnivDateTime(opr.time)));
-        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=1 then begin
-        s := Inttostr(opr.Block);
-        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=2 then begin
-        s := TAccountComp.AccountNumberToAccountTxtNumber(opr.AffectedAccount);
-        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=3 then begin
-        s := opr.OperationTxt;
-        DrawGrid.Canvas.TextRect(Rect,s,[tfleft,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=4 then begin
-        s := TAccountComp.FormatMoney(opr.Amount);
-        if opr.Amount>0 then DrawGrid.Canvas.Font.Color := ClGreen
-        else if opr.Amount=0 then DrawGrid.Canvas.Font.Color := clGrayText
-        else DrawGrid.Canvas.Font.Color := clRed;
-        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=5 then begin
-        s := TAccountComp.FormatMoney(opr.Fee);
-        if opr.Fee>0 then DrawGrid.Canvas.Font.Color := ClGreen
-        else if opr.Fee=0 then DrawGrid.Canvas.Font.Color := clGrayText
-        else DrawGrid.Canvas.Font.Color := clRed;
-        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=6 then begin
-        if opr.time=0 then begin
-          // Pending operation... showing final balance
-          DrawGrid.Canvas.Font.Color := clBlue;
-          s := '('+TAccountComp.FormatMoney(opr.Balance)+')';
-        end else begin
-          s := TAccountComp.FormatMoney(opr.Balance);
-          if opr.Balance>0 then DrawGrid.Canvas.Font.Color := ClGreen
-          else if opr.Balance=0 then DrawGrid.Canvas.Font.Color := clGrayText
-          else DrawGrid.Canvas.Font.Color := clRed;
-        end;
-        DrawGrid.Canvas.TextRect(Rect,s,[tfRight,tfVerticalCenter,tfSingleLine]);
-      end else if ACol=7 then begin
-        s := opr.PrintablePayload;
-        DrawGrid.Canvas.TextRect(Rect,s,[tfLeft,tfVerticalCenter,tfSingleLine]);
-      end else begin
-        s := '(???)';
-        DrawGrid.Canvas.TextRect(Rect,s,[tfCenter,tfVerticalCenter,tfSingleLine]);
-      end;
-    end;
-  end;
-  Except
-    On E:Exception do begin
-      TLog.NewLog(lterror,Classname,Format('Error at OnGridDrawCell row %d col %d Block %d - %s',[ARow,ACol,opr.Block,E.Message]));
-    end;
-  End;
-end;
-
-procedure TOperationsGrid.OnNodeNewAccount(Sender: TObject);
-begin
-  UpdateAccountOperations;
-end;
-
-procedure TOperationsGrid.OnNodeNewOperation(Sender: TObject);
-Var Op : TPCOperation;
-  l : TList;
-begin
-  if AccountNumber<0 then UpdateAccountOperations
-  else begin
-    Op := TPCOperation(Sender);
-    l := TList.Create;
-    Try
-      Op.AffectedAccounts(l);
-      if l.IndexOf(TObject(Integer(AccountNumber)))>=0 then UpdateAccountOperations;
-    Finally
-      l.Free;
-    End;
-  end;
-end;
-
-procedure TOperationsGrid.SetAccountNumber(const Value: Int64);
-begin
-  if FAccountNumber=Value then exit;
-  FAccountNumber := Value;
-  if FAccountNumber>=0 then FPendingOperations := false;
-  UpdateAccountOperations;
-end;
-
-procedure TOperationsGrid.SetDrawGrid(const Value: TDrawGrid);
-begin
-  if FDrawGrid=Value then exit;
-  FDrawGrid := Value;
-  if Assigned(Value) then begin
-    Value.FreeNotification(self);
-    FDrawGrid.OnDrawCell := OnGridDrawCell;
-    InitGrid;
-  end;
-end;
-
-procedure TOperationsGrid.SetNode(const Value: TNode);
-begin
-  if GetNode=Value then exit;
-  FNodeNotifyEvents.Node := Value;
-  UpdateAccountOperations; // New Build 1.0.3
-end;
-
-procedure TOperationsGrid.SetPendingOperations(const Value: Boolean);
-begin
-  FPendingOperations := Value;
-  if FPendingOperations then  FAccountNumber := -1;
-  UpdateAccountOperations;
-end;
-
-procedure TOperationsGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
-Var i : Integer;
-  opr : TOperationResume;
-  FRM : TFRMPayloadDecoder;
-begin
-  if Not Assigned(FDrawGrid) then exit;
-  if (FDrawGrid.Row<=0) Or (FDrawGrid.Row>FOperationsResume.Count) then exit;
-  opr := FOperationsResume.OperationResume[FDrawGrid.Row-1];
-  FRM := TFRMPayloadDecoder.Create(FDrawGrid.Owner);
-  try
-    FRM.Init(opr.Block,opr.time,opr.OperationTxt,opr.OriginalPayload,WalletKeys,AppParams);
-    FRM.ShowModal;
-  finally
-    FRM.Free;
-  end;
-end;
-
-procedure TOperationsGrid.UpdateAccountOperations;
-Var list : TList;
-  i,j : Integer;
-  OPR : TOperationResume;
-  Op : TPCOperation;
-begin
-  FOperationsResume.Clear;
-  Try
-    if Not Assigned(Node) then exit;
-    if FPendingOperations then begin
-      for i := Node.Operations.Count - 1 downto 0 do begin
-        Op := Node.Operations.OperationsHashTree.GetOperation(i);
-        If TDBStorage.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
-          OPR.Block := Node.Operations.OperationBlock.block;
-          OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
-          FOperationsResume.Add(OPR);
-        end;
-      end;
-    end else begin
-      if AccountNumber<0 then begin
-        list := TList.Create;
-        try
-          for i := 0 to Node.Operations.Count-1 do begin
-            Op := Node.Operations.Operation[i];
-            If TDBStorage.OperationToOperationResume(Op,Op.SenderAccount,OPR) then begin
-              OPR.Block := Node.Operations.OperationBlock.block;
-              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(Op.SenderAccount).balance;
-              FOperationsResume.Add(OPR);
-            end;
-          end;
-        finally
-          list.Free;
-        end;
-      end else begin
-        list := TList.Create;
-        Try
-          Node.Operations.OperationsHashTree.GetOperationsAffectingAccount(AccountNumber,list);
-          for i := list.Count - 1 downto 0 do begin
-            Op := Node.Operations.OperationsHashTree.GetOperation(Integer(list[i]));
-            If TDBStorage.OperationToOperationResume(Op,AccountNumber,OPR) then begin
-              OPR.Block := Node.Operations.OperationBlock.block;
-              OPR.Balance := Node.Operations.SafeBoxTransaction.Account(AccountNumber).balance;
-              FOperationsResume.Add(OPR);
-            end;
-          end;
-        Finally
-          list.Free;
-        End;
-        if Node.Bank.Storage is TDBStorage then begin
-          TDBStorage(Node.Bank.Storage).GetOperationsFromAccount(FOperationsResume,AccountNumber,0,200);
-        end;
-      end;
-    end;
-  Finally
-    InitGrid;
-  End;
-end;
-
-{ TOperationsDBGrid }
-
-Type TAuxDBGrid = Class(TDBGrid);
-
-constructor TOperationsDBGrid.Create(AOwner: TComponent);
-Var i : Integer;
-  fld : TField;
-begin
-  inherited;
-  FNeedRefreshSQL := false;
-  FDisableds := 0;
-  FDBGrid := Nil;
-  FQrySQL := TADOQuery.Create(Self);
-  FQrySQL.OnCalcFields := OnQryCalcFields;
-  FqrySQL.CursorLocation := clUseClient;
-  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
-  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
-  FDataSource := TDataSource.Create(Self);
-  FDataSource.DataSet := FQrySQL;
-  FAccountNumber := -1; // all
-  FBlockStart := -1;
-  FBlockEnd := -1;
-  FDateStart := 0;
-  FDateEnd := 0;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_block;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  //
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_s_timestamp;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  //
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_optype;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_optype_op;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_account;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_other_account;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TDateTimeField.Create(FQrySQL);
-  fld.FieldName := 'datetime';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := 'op_account';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 100;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := 'operation';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 100;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := 'payload_txt';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 600;
-  //
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_s_amount;
-  fld.DataSet := FQrySQL;
-  fld := TLargeintField.Create(FQrySQL);
-  fld.FieldName := 'amount';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_s_fee;
-  fld.DataSet := FQrySQL;
-  fld := TLargeintField.Create(FQrySQL);
-  fld.FieldName := 'fee';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_s_balance;
-  fld.DataSet := FQrySQL;
-  fld := TLargeintField.Create(FQrySQL);
-  fld.FieldName := 'balance';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_Operations_rawpayload;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 255;
-end;
-
-destructor TOperationsDBGrid.Destroy;
-begin
-  FNodeNotifyEvents.Free;
-  FDataSource.Free;
-  FQrySQL.Free;
-  inherited;
-end;
-
-procedure TOperationsDBGrid.Disable;
-begin
-  inc(FDisableds);
-end;
-
-procedure TOperationsDBGrid.Enable;
-begin
-  dec(FDisableds);
-  if FDisableds>0 then exit;
-  FDisableds := 0;
-  if FNeedRefreshSQL then RefreshData;
-end;
-
-function TOperationsDBGrid.GetAdoConnection: TADOConnection;
-begin
-  Result := FQrySQL.Connection;
-end;
-
-function TOperationsDBGrid.GetNode: TNode;
-begin
-  Result := FNodeNotifyEvents.Node;
-end;
-
-procedure TOperationsDBGrid.Notification(AComponent: TComponent;
-  Operation: TOperation);
-begin
-  inherited;
-  if Operation=opRemove then begin
-    if (AComponent=FDBGrid) then begin
-      SetDBGrid(Nil);
-    end;
-  end;
-end;
-
-procedure TOperationsDBGrid.OnGridDrawColumnCell(Sender: TObject;
-  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
-Var c : TCanvas;
-  s : String;
-  r : TRect;
-begin
-  c := TDBGrid(Sender).Canvas;
-  r := Rect;
-  if (gdSelected in State) then
-    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
-    else c.Brush.Color := clGradientInactiveCaption
-  else c.Brush.Color := clWindow;
-  c.FillRect(Rect);
-  if SameText(Column.FieldName,'amount') Or
-     SameText(Column.FieldName,'fee') Or
-     SameText(Column.FieldName,'balance')
-      then begin
-    c.FillRect(Rect);
-    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
-    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
-    else c.Font.Color := clRed;
-    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
-    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
-  end else begin
-    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
-  end;
-
-  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
-     not (gdPressed in State) then
-  begin
-    InflateRect(r, 1, 1);
-    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
-    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
-  end;
-  //
-end;
-
-procedure TOperationsDBGrid.OnNodeNewAccount(Sender: TObject);
-begin
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.OnQryCalcFields(DataSet: TDataSet);
-Var fld : TField;
-  raw : TRawBytes;
-  s : AnsiString;
-begin
-  fld := DataSet.FieldByName('datetime');
-  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime( StrToIntDef( DataSet.FieldByName(CT_TblFld_Operations_s_timestamp).AsString, 0)));
-  fld := DataSet.FieldByName('op_account');
-  fld.AsString := TAccountComp.AccountNumberToAccountTxtNumber(DataSet.FieldByName(CT_TblFld_Operations_account).AsInteger);
-  fld := DataSet.FieldByName('payload_txt');
-  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_Operations_rawpayload),raw);
-  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
-    fld.AsString := s;
-  end else begin
-    fld.AsString := TCrypto.ToHexaString(raw);
-  end;
-  //
-  fld := DataSet.FieldByName('amount');
-  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_amount).AsString,0);
-  fld := DataSet.FieldByName('balance');
-  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_balance).AsString,0);
-  fld := DataSet.FieldByName('fee');
-  TLargeintField(fld).Value := StrToInt64Def(Dataset.FieldByName(CT_TblFld_Operations_s_fee).AsString,0);
-
-  fld := DataSet.FieldByName('operation');
-          case dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger of
-            0 : fld.AsString := 'Blockchain reward';
-            CT_Op_Transaction : begin
-              if dataset.FieldByName(CT_TblFld_Operations_optype_op).AsInteger=0 then begin
-                fld.AsString := 'Transaction Sent to '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
-              end else begin
-                fld.AsString := 'Transaction Received from '+TAccountComp.AccountNumberToAccountTxtNumber(dataset.FieldByName(CT_TblFld_Operations_other_account).AsLargeInt);
-              end;
-            end;
-            CT_Op_Changekey : Begin
-              fld.AsString := 'Change Key';
-            End;
-            CT_Op_Recover : begin
-              fld.AsString := 'Recover founds';
-            end;
-          else
-            fld.AsString := 'Unknown OpType:'+Inttostr(dataset.FieldByName(CT_TblFld_Operations_optype).AsInteger);
-          end;
-end;
-
-procedure TOperationsDBGrid.RefreshData;
-Var sql : AnsiString;
-begin
-  if FDisableds>0 then begin
-    FNeedRefreshSQL := true;
-    exit;
-  end;
-  FNeedRefreshSQL := false;
-  sql := TDBStorage.GetOperationsFromAccountSQL(AccountNumber,BlockStart,BlockEnd,DateStart,DateEnd, 0,50000);
-  If FQrySQL.Connection=Nil then exit;
-  if Node=Nil then exit;
-  if FDBGrid=Nil then exit;
-
-  FQrySQL.DisableControls;
-  try
-    FQrySQL.Close;
-    FQrySQL.SQL.Text := sql;
-    FQrySQL.Open;
-  finally
-    FQrySQL.EnableControls;
-  end;
-end;
-
-procedure TOperationsDBGrid.SetAccountNumber(const Value: Int64);
-begin
-  if FAccountNumber=Value then exit;
-  FAccountNumber := Value;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetAdoConnection(const Value: TADOConnection);
-begin
-  FQrySQL.Connection := Value;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetBlockEnd(const Value: Int64);
-begin
-  if FBlockEnd=Value then exit;
-  FBlockEnd := Value;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetBlocks(bstart, bend: Int64);
-begin
-  FBlockStart := bstart;
-  FBlockEnd := bend;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetBlockStart(const Value: Int64);
-begin
-  if FBlockStart=Value then exit;
-  FBlockStart := Value;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetDateEnd(const Value: TDate);
-Var d,h,m,y : word;
-begin
-  if FDateEnd=Value then exit;
-  decodedate(value,y,m,d);
-  FDateEnd := EncodeDate(y,m,d);
-  if FDateStart>=FDateEnd then FDateStart := FDateEnd;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetDates(dStart, dEnd: TDate);
-begin
-  Disable;
-  try
-    DateStart := dStart;
-    DateEnd := dEnd;
-  finally
-    Enable;
-  end;
-end;
-
-procedure TOperationsDBGrid.SetDateStart(const Value: TDate);
-Var d,h,m,y : word;
-begin
-  if FDateStart=Value then exit;
-  decodedate(value,y,m,d);
-  FDateStart := EncodeDate(y,m,d);
-  if FDateStart>=FDateEnd then FDateEnd := FDateStart;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.SetDBGrid(const Value: TDBGrid);
-  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
-  Var c : TColumn;
-  begin
-    c := DBgrid.Columns.Add;
-    c.FieldName := fieldname;
-    c.Title.Caption := displayname;
-    c.Width := colwidth;
-    c.Title.Font.Style := [fsBold];
-    c.Title.Alignment := taCenter;
-  end;
-begin
-  if FDBGrid=Value then exit;
-  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
-  FDBGrid := Value;
-  if Assigned(Value) then begin
-    Value.FreeNotification(self);
-    Value.DataSource := FDataSource;
-    Value.ReadOnly := true;
-    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
-    FDBGrid.DefaultDrawing := false;
-    FDBGrid.Columns.Clear;
-    AddColumn(CT_TblFld_Operations_block,'Block',50);
-    AddColumn('datetime','Date/Time',120);
-    AddColumn('op_account','Account',70);
-    AddColumn('operation','Operation',150);
-    AddColumn('amount','Amount',80);
-    AddColumn('fee','Fee',60);
-    AddColumn('balance','Balance',80);
-    AddColumn('payload_txt','Payload',280);
-    RefreshData;
-  end;
-end;
-
-procedure TOperationsDBGrid.SetNode(const Value: TNode);
-begin
-  FNodeNotifyEvents.Node:= value;
-  RefreshData;
-end;
-
-procedure TOperationsDBGrid.ShowModalDecoder(WalletKeys: TWalletKeys; AppParams: TAppParams);
-Var FRM : TFRMPayloadDecoder;
-  raw : TRawBytes;
-begin
-  if Not Assigned(FDBGrid) Or Not Assigned(FQrySQL) then exit;
-  If FQrySQL.Eof then exit;
-  FRM := TFRMPayloadDecoder.Create(FDBGrid.Owner);
-  try
-    TDBStorage.DBStringFieldToRaw(FQrySQL.FieldByName(CT_TblFld_Operations_rawpayload),raw);
-    FRM.Init(FQrySQL.FieldByName(CT_TblFld_Operations_block).AsInteger,
-      StrToIntDef( FQrySQL.FieldByName(CT_TblFld_Operations_s_timestamp).AsString,0),
-      FQrySQL.FieldByName('operation').AsString,
-      raw,WalletKeys,AppParams);
-    FRM.ShowModal;
-  finally
-    FRM.Free;
-  end;
-end;
-
-{ TBlockChainDBGrid }
-
-constructor TBlockChainDBGrid.Create(AOwner: TComponent);
-Var fld : TField;
-begin
-  inherited;
-  FNeedRefreshSQL := False;
-  FBlockStart := -1;
-  FBlockEnd := -1;
-  FDateEnd := 0;
-  FDateStart := 0;
-  FDisableds := 0;
-  FQrySQL := TADOQuery.Create(Self);
-  FQrySQL.OnCalcFields := OnQryCalcFields;
-  FqrySQL.CursorLocation := clUseClient;
-  FDBGrid := Nil;
-  FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
-  FNodeNotifyEvents.OnBlocksChanged := OnNodeNewAccount;
-  FDataSource := TDataSource.Create(Self);
-  FDataSource.DataSet := FQrySQL;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_block;
-  fld.DataSet := FQrySQL;
-  fld.Visible := true;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_s_timestamp;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_s_reward;
-  fld.DataSet := FQrySQL;
-  fld.Visible := true;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_s_fee;
-  fld.DataSet := FQrySQL;
-  fld.Visible := true;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_s_compact_target;
-  fld.DataSet := FQrySQL;
-  fld.Visible := true;
-  fld := TIntegerField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_operations_count;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  fld := TDateTimeField.Create(FQrySQL);
-  fld.FieldName := 'datetime';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_proof_of_work;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  TStringField(fld).Size := 64;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_safe_box_hash;
-  fld.DataSet := FQrySQL;
-  fld.Visible := false;
-  TStringField(fld).Size := 64;
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := 'target';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 8;
-
-  fld := TLargeintField.Create(FQrySQL);
-  fld.FieldName := 'reward';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-
-  fld := TLargeintField.Create(FQrySQL);
-  fld.FieldName := 'fee';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := CT_TblFld_BlockChain_rawpayload;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 255;
-
-  fld := TStringField.Create(FQrySQL);
-  fld.FieldName := 'payload_decoded';
-  fld.Calculated := true;
-  fld.FieldKind := fkCalculated;
-  fld.DataSet := FQrySQL;
-  TStringField(fld).Size := 600;
-end;
-
-destructor TBlockChainDBGrid.Destroy;
-begin
-  SetDBGrid(Nil);
-  FNodeNotifyEvents.Free;
-  FDataSource.Free;
-  FQrySQL.Free;
-  inherited;
-end;
-
-procedure TBlockChainDBGrid.Disable;
-begin
-  Inc(FDisableds);
-end;
-
-procedure TBlockChainDBGrid.Enable;
-begin
-  if FDisableds<0 then exit;
-  Dec(FDisableds);
-  if FNeedRefreshSQL then RefreshData;
-end;
-
-function TBlockChainDBGrid.GetAdoConnection: TADOConnection;
-begin
-  Result := FQrySQL.Connection;
-end;
-
-function TBlockChainDBGrid.GetNode: TNode;
-begin
-  Result := FNodeNotifyEvents.Node;
-end;
-
-procedure TBlockChainDBGrid.Notification(AComponent: TComponent;
-  Operation: TOperation);
-begin
-  inherited;
-  if Operation=OpRemove then begin
-    if AComponent=FDBGrid then FDBGrid:=Nil;
-  end;
-end;
-
-procedure TBlockChainDBGrid.OnGridDrawColumnCell(Sender: TObject;
-  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
-Var c : TCanvas;
-  s : String;
-  r : TRect;
-begin
-  c := TDBGrid(Sender).Canvas;
-  r := Rect;
-  if (gdSelected in State) then
-    If (gdFocused in State) then c.Brush.Color := clGradientActiveCaption
-    else c.Brush.Color := clGradientInactiveCaption
-  else c.Brush.Color := clWindow;
-  c.FillRect(Rect);
-  if SameText(Column.FieldName,'reward') Or
-     SameText(Column.FieldName,'fee')
-      then begin
-    c.FillRect(Rect);
-    if Column.Field.AsLargeInt>0 then c.Font.Color := ClGreen
-    else if Column.Field.AsLargeInt=0 then c.Font.Color := clGrayText
-    else c.Font.Color := clRed;
-    s := TAccountComp.FormatMoney(Column.Field.AsLargeInt);
-    c.TextRect(r,s,[tfRight,tfVerticalCenter,tfSingleLine]);
-  end else begin
-    TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
-  end;
-
-  if (gdFixed in State) and ([dgRowLines, dgColLines] * TDBGrid(Sender).Options =  [dgRowLines, dgColLines]) and
-     not (gdPressed in State) then
-  begin
-    InflateRect(r, 1, 1);
-    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
-    DrawEdge(c.Handle, r, BDR_RAISEDINNER, BF_TOPLEFT);
-  end;
-  //
-end;
-
-procedure TBlockChainDBGrid.OnNodeNewAccount(Sender: TObject);
-begin
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.OnQryCalcFields(DataSet: TDataSet);
-Var fld : TField;
-  raw : TRawBytes;
-  s : AnsiString;
-begin
-  fld := DataSet.FieldByName('datetime');
-  fld.AsDateTime :=  UnivDateTime2LocalDateTime(UnixToUnivDateTime(StrToIntDef( DataSet.FieldByName(CT_TblFld_BlockChain_s_timestamp).AsString,0)));
-  fld := DataSet.FieldByName('target');
-  fld.AsString :=  IntToHex(StrToIntDef( DataSet.FieldByName(CT_TblFld_BlockChain_s_compact_target).AsString,0),8);
-  DataSet.FieldByName('reward').AsLargeInt := StrToIntDef(DataSet.FieldByName(CT_TblFld_BlockChain_s_reward).AsString,0);
-  DataSet.FieldByName('fee').AsLargeInt := StrToIntDef(DataSet.FieldByName(CT_TblFld_BlockChain_s_fee).AsString,0);
-  fld := DataSet.FieldByName('payload_decoded');
-  TDBStorage.DBStringFieldToRaw(DataSet.FieldByName(CT_TblFld_BlockChain_rawpayload),raw);
-  If TDBStorage.DBPayloadToReadableText(raw,s) then begin
-    fld.AsString := s;
-  end else begin
-    fld.AsString := TCrypto.ToHexaString(raw);
-  end;
-end;
-
-procedure TBlockChainDBGrid.RefreshData;
-Var sql : AnsiString;
-begin
-  if FDisableds>0 then begin
-    FNeedRefreshSQL := true;
-    exit;
-  end;
-  FNeedRefreshSQL := false;
-  sql := TDBStorage.GetBlockChainSQL(BlockStart,BlockEnd,DateStart,DateEnd, 0,300);
-  If FQrySQL.Connection=Nil then exit;
-  if Node=Nil then exit;
-  if FDBGrid=Nil then exit;
-
-  FQrySQL.DisableControls;
-  try
-    FQrySQL.Close;
-    FQrySQL.SQL.Text := sql;
-    FQrySQL.Open;
-  finally
-    FQrySQL.EnableControls;
-  end;
-end;
-
-procedure TBlockChainDBGrid.SetAdoConnection(const Value: TADOConnection);
-begin
-  FQrySQL.Connection := Value;
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.SetBlockEnd(const Value: Int64);
-begin
-  if FBlockEnd=Value then exit;
-  FBlockEnd := Value;
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.SetBlocks(bstart, bend: Int64);
-begin
-  disable;
-  Try
-    BlockStart := bstart;
-    BlockEnd := bEnd;
-  Finally
-    Enable;
-  End;
-end;
-
-procedure TBlockChainDBGrid.SetBlockStart(const Value: Int64);
-begin
-  if FBlockStart=Value then exit;
-  FBlockStart:=Value;
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.SetDateEnd(const Value: TDate);
-begin
-  if FDateEnd=Value then exit;
-  FDateEnd := Value;
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.SetDates(dStart, dEnd: TDate);
-begin
-  Disable;
-  Try
-    DateStart := dStart;
-    DateEnd := dEnd;
-  Finally
-    Enable;
-  End;
-end;
-
-procedure TBlockChainDBGrid.SetDateStart(const Value: TDate);
-begin
-  if FDateStart=Value then exit;
-  FDateStart := Value;
-  RefreshData;
-end;
-
-procedure TBlockChainDBGrid.SetDBGrid(const Value: TDBGrid);
-  Procedure AddColumn(fieldname,displayname : String; colwidth : integer);
-  Var c : TColumn;
-  begin
-    c := DBgrid.Columns.Add;
-    c.FieldName := fieldname;
-    c.Title.Caption := displayname;
-    c.Width := colwidth;
-    c.Title.Font.Style := [fsBold];
-    c.Title.Alignment := taCenter;
-  end;
-begin
-  if FDBGrid=Value then exit;
-  if Assigned(FDBGrid) then FDBGrid.DataSource := Nil;
-  FDBGrid := Value;
-  if Assigned(Value) then begin
-    Value.FreeNotification(self);
-    Value.DataSource := FDataSource;
-    Value.ReadOnly := true;
-    FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
-    FDBGrid.DefaultDrawing := false;
-    FDBGrid.Columns.Clear;
-    AddColumn(CT_TblFld_BlockChain_block,'Block',60);
-    AddColumn('datetime','Date/Time',120);
-    AddColumn(CT_TblFld_BlockChain_operations_count,'Ops.',30);
-    AddColumn('reward','Reward',80);
-    AddColumn('fee','Fee',60);
-    AddColumn('target','Target',60);
-    AddColumn('payload_decoded','Miner Payload',150);
-    AddColumn(CT_TblFld_BlockChain_proof_of_work,'Proof of Work',400);
-    AddColumn(CT_TblFld_BlockChain_safe_box_hash,'Safe Box Hash',400);
-    RefreshData;
-  end;
-end;
-
-procedure TBlockChainDBGrid.SetNode(const Value: TNode);
-begin
-  FNodeNotifyEvents.Node := Value;
-  RefreshData;
-end;
-
 end.