Browse Source

Build 1.0.5

PascalCoin 9 years ago
parent
commit
15a09985d0

BIN
PascalCoinWallet.res


+ 8 - 0
README.md

@@ -38,6 +38,14 @@ If you like it, consider a donation using BitCoin:
 
 
 History:
 History:
 
 
+Build 1.0.5.0 - 2016-09-21
+--------------------------
+- Massive operations, selecting multiple accounts
+- Filter accounts by balance
+- Correct operations explorer order of operations for each block (descending order)
+- Minor changes
+
+
 Build 1.0.4.0 - 2016-09-16
 Build 1.0.4.0 - 2016-09-16
 --------------------------
 --------------------------
 - IMPORTANT: Introducing net protocol changes: Must update!
 - IMPORTANT: Introducing net protocol changes: Must update!

+ 8 - 0
README.txt

@@ -38,6 +38,14 @@ If you like it, consider a donation using BitCoin:
 
 
 History:
 History:
 
 
+Build 1.0.5.0 - 2016-09-21
+--------------------------
+- Massive operations, selecting multiple accounts
+- Filter accounts by balance
+- Correct operations explorer order of operations for each block (descending order)
+- Minor changes
+
+
 Build 1.0.4.0 - 2016-09-16
 Build 1.0.4.0 - 2016-09-16
 --------------------------
 --------------------------
 - IMPORTANT: Introducing net protocol changes: Must update!
 - IMPORTANT: Introducing net protocol changes: Must update!

+ 56 - 29
Units/Forms/UFRMOperation.dfm

@@ -5,7 +5,7 @@ object FRMOperation: TFRMOperation
   BorderIcons = [biSystemMenu]
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
   BorderStyle = bsSingle
   Caption = 'New Operation'
   Caption = 'New Operation'
-  ClientHeight = 443
+  ClientHeight = 474
   ClientWidth = 608
   ClientWidth = 608
   Color = clBtnFace
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
   Font.Charset = DEFAULT_CHARSET
@@ -19,15 +19,15 @@ object FRMOperation: TFRMOperation
   OnDestroy = FormDestroy
   OnDestroy = FormDestroy
   PixelsPerInch = 96
   PixelsPerInch = 96
   TextHeight = 13
   TextHeight = 13
-  object Label1: TLabel
-    Left = 20
+  object lblAccountCaption: TLabel
+    Left = 25
     Top = 20
     Top = 20
     Width = 43
     Width = 43
     Height = 13
     Height = 13
     Caption = 'Account:'
     Caption = 'Account:'
   end
   end
   object lblAccountBalance: TLabel
   object lblAccountBalance: TLabel
-    Left = 225
+    Left = 432
     Top = 15
     Top = 15
     Width = 149
     Width = 149
     Height = 19
     Height = 19
@@ -40,15 +40,22 @@ object FRMOperation: TFRMOperation
     ParentFont = False
     ParentFont = False
   end
   end
   object Label2: TLabel
   object Label2: TLabel
-    Left = 180
+    Left = 387
     Top = 20
     Top = 20
     Width = 41
     Width = 41
     Height = 13
     Height = 13
     Caption = 'Balance:'
     Caption = 'Balance:'
   end
   end
+  object lblAccountsCount: TLabel
+    Left = 387
+    Top = 58
+    Width = 18
+    Height = 13
+    Caption = 'XXX'
+  end
   object bbExecute: TBitBtn
   object bbExecute: TBitBtn
-    Left = 295
-    Top = 386
+    Left = 300
+    Top = 411
     Width = 130
     Width = 130
     Height = 31
     Height = 31
     Caption = 'Execute (F12)'
     Caption = 'Execute (F12)'
@@ -76,8 +83,8 @@ object FRMOperation: TFRMOperation
     OnClick = actExecuteExecute
     OnClick = actExecuteExecute
   end
   end
   object bbCancel: TBitBtn
   object bbCancel: TBitBtn
-    Left = 460
-    Top = 386
+    Left = 465
+    Top = 411
     Width = 116
     Width = 116
     Height = 31
     Height = 31
     DoubleBuffered = True
     DoubleBuffered = True
@@ -85,25 +92,9 @@ object FRMOperation: TFRMOperation
     ParentDoubleBuffered = False
     ParentDoubleBuffered = False
     TabOrder = 3
     TabOrder = 3
   end
   end
-  object ebSenderAccount: TEdit
-    Left = 69
-    Top = 12
-    Width = 97
-    Height = 27
-    Font.Charset = DEFAULT_CHARSET
-    Font.Color = clBlack
-    Font.Height = -16
-    Font.Name = 'Tahoma'
-    Font.Style = [fsBold]
-    ParentFont = False
-    TabOrder = 0
-    Text = 'ebSenderAccount'
-    OnExit = ebSenderAccountExit
-    OnKeyPress = ebSenderAccountKeyPress
-  end
   object PageControl: TPageControl
   object PageControl: TPageControl
-    Left = 20
-    Top = 50
+    Left = 25
+    Top = 75
     Width = 556
     Width = 556
     Height = 321
     Height = 321
     ActivePage = tsOperation
     ActivePage = tsOperation
@@ -536,9 +527,45 @@ object FRMOperation: TFRMOperation
       end
       end
     end
     end
   end
   end
+  object memoAccounts: TMemo
+    Left = 74
+    Top = 12
+    Width = 307
+    Height = 59
+    Ctl3D = False
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    Lines.Strings = (
+      '123456-11 (0.0000); 123456-11 (0.0000); '
+      '123456-11 (0.0000); 123456-11 (0.0000); ')
+    ParentCtl3D = False
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssVertical
+    TabOrder = 4
+  end
+  object ebSenderAccount: TEdit
+    Left = 74
+    Top = 12
+    Width = 97
+    Height = 27
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clBlack
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentFont = False
+    TabOrder = 0
+    Text = 'ebSenderAccount'
+    OnExit = ebSenderAccountExit
+    OnKeyPress = ebSenderAccountKeyPress
+  end
   object ActionList1: TActionList
   object ActionList1: TActionList
-    Left = 135
-    Top = 325
+    Left = 140
+    Top = 350
     object actExecute: TAction
     object actExecute: TAction
       Caption = 'Execute (F12)'
       Caption = 'Execute (F12)'
       ShortCut = 123
       ShortCut = 123

+ 239 - 126
Units/Forms/UFRMOperation.pas

@@ -22,7 +22,7 @@ uses
 
 
 type
 type
   TFRMOperation = class(TForm)
   TFRMOperation = class(TForm)
-    Label1: TLabel;
+    lblAccountCaption: TLabel;
     bbExecute: TBitBtn;
     bbExecute: TBitBtn;
     bbCancel: TBitBtn;
     bbCancel: TBitBtn;
     lblAccountBalance: TLabel;
     lblAccountBalance: TLabel;
@@ -64,6 +64,8 @@ type
     tsGlobalError: TTabSheet;
     tsGlobalError: TTabSheet;
     lblGlobalErrors: TLabel;
     lblGlobalErrors: TLabel;
     bbPassword: TBitBtn;
     bbPassword: TBitBtn;
+    memoAccounts: TMemo;
+    lblAccountsCount: TLabel;
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure rbTransactionClick(Sender: TObject);
     procedure rbTransactionClick(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -80,11 +82,8 @@ type
     procedure bbPasswordClick(Sender: TObject);
     procedure bbPasswordClick(Sender: TObject);
     procedure ebDestAccountExit(Sender: TObject);
     procedure ebDestAccountExit(Sender: TObject);
   private
   private
-    FSenderAccount: Cardinal;
     FNode : TNode;
     FNode : TNode;
     FWalletKeys: TWalletKeys;
     FWalletKeys: TWalletKeys;
-    FPAccount : PAccount;
-    FOperation : TPCOperation;
     FFee: Int64;
     FFee: Int64;
     FEncodedPayload : TRawBytes;
     FEncodedPayload : TRawBytes;
     FDisabled : Boolean;
     FDisabled : Boolean;
@@ -92,16 +91,17 @@ type
     FTxDestAccount : Cardinal;
     FTxDestAccount : Cardinal;
     FTxAmount : Int64;
     FTxAmount : Int64;
     FNewAccountPublicKey : TAccountKey;
     FNewAccountPublicKey : TAccountKey;
-    procedure SetSenderAccount(const Value: Cardinal);
+    FSenderAccounts: TOrderedCardinalList;
     procedure SetWalletKeys(const Value: TWalletKeys);
     procedure SetWalletKeys(const Value: TWalletKeys);
     { Private declarations }
     { Private declarations }
+    Procedure UpdateAccountsInfo;
     Function UpdateOperationOptions(var errors : AnsiString) : Boolean;
     Function UpdateOperationOptions(var errors : AnsiString) : Boolean;
-    Function UpdatePayload(var errors : AnsiString) : Boolean;
-    Function GetSenderAcccount: PAccount;
+    Function UpdatePayload(Const SenderAccount : TAccount; var errors : AnsiString) : Boolean;
     procedure SetFee(const Value: Int64);
     procedure SetFee(const Value: Int64);
+    Procedure OnSenderAccountsChanged(Sender : TObject);
   public
   public
     { Public declarations }
     { Public declarations }
-    Property SenderAccount : Cardinal read FSenderAccount write SetSenderAccount;
+    Property SenderAccounts : TOrderedCardinalList read FSenderAccounts;
     Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
     Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
     Property Fee : Int64 read FFee write SetFee;
     Property Fee : Int64 read FFee write SetFee;
   end;
   end;
@@ -118,62 +118,115 @@ uses
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 Var errors : AnsiString;
 Var errors : AnsiString;
   P : PAccount;
   P : PAccount;
-  i : Integer;
+  i,iAcc : Integer;
   wk : TWalletKey;
   wk : TWalletKey;
-  npk : TECPrivateKey;
-  FRM : TFRMNewPrivateKeyType;
+  ops : TOperationsHashTree;
+  op : TPCOperation;
+  account : TAccount;
+  operation_to_string, operationstxt, auxs : String;
+  _amount,_fee, _totalamount, _totalfee : Int64;
+  dooperation : Boolean;
 begin
 begin
-  P := GetSenderAcccount;
-  if Not Assigned(P) then raise Exception.Create('Invalid sender account');
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
-  i := WalletKeys.IndexOfAccountKey(P^.accountkey);
-  if (i<0) then begin
-    Raise Exception.Create('Sender account private key not found in Wallet');
-  end;
-  wk := WalletKeys.Key[i];
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
-  If Not UpdatePayload(errors) then raise Exception.Create(errors);
-  //
-  FreeAndNil(FOperation);
-  if rbTransaction.Checked then begin
-    FOperation := TOpTransaction.Create(SenderAccount,P^.n_operation+1,FTxDestAccount,wk.PrivateKey,FTxAmount,fee,FEncodedPayload);
-  end else if rbChangeKey.Checked then begin
-    i := Integer(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
-    if i=-1 then begin
-      // New key
-      FRM := TFRMNewPrivateKeyType.Create(Self);
-      try
-        FRM.WalletKeys := WalletKeys;
-        if FRM.ShowModal=MrOk then begin
-          npk := FRM.GeneratedPrivateKey;
-          FNewAccountPublicKey := npk.PublicKey;
+  ops := TOperationsHashTree.Create;
+  Try
+    _totalamount := 0;
+    _totalfee := 0;
+    operationstxt := '';
+    operation_to_string := '';
+    for iAcc := 0 to FSenderAccounts.Count - 1 do begin
+      op := Nil;
+      account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
+      If Not UpdatePayload(account, errors) then
+        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
+      i := WalletKeys.IndexOfAccountKey(account.accountkey);
+      if i<0 then begin
+        Raise Exception.Create('Sender account private key not found in Wallet');
+      end;
+
+      wk := WalletKeys.Key[i];
+      dooperation := true;
+      //
+      if rbTransaction.Checked then begin
+        // Amount
+        _amount := 0;
+        _fee := 0;
+        if FSenderAccounts.Count>1 then begin
+          if account.balance>0 then begin
+            if account.balance>fee then begin
+              _amount := account.balance - fee;
+              _fee := fee;
+            end else begin
+              _amount := account.balance;
+              _fee := 0;
+            end;
+          end else dooperation := false;
         end else begin
         end else begin
-          raise Exception.Create('No new key generated');
+          _amount := FTxAmount;
+          _fee := fee;
         end;
         end;
-      finally
-        FRM.Free;
+        if dooperation then begin
+          op := TOpTransaction.Create(account.account,account.n_operation+1,FTxDestAccount,wk.PrivateKey,_amount,_fee,FEncodedPayload);
+          inc(_totalamount,_amount);
+          inc(_totalfee,_fee);
+        end;
+        operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(FTxDestAccount);
+      end else if rbChangeKey.Checked then begin
+        i := Integer(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+        if (i<0) Or (i>=WalletKeys.Count) then raise Exception.Create('Invalid selected key');
+        FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+        if account.balance>fee then _fee := fee
+        else _fee := 0;
+        op := TOpChangeKey.Create(account.account,account.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,_fee,FEncodedPayload);
+        inc(_totalfee,_fee);
+        operationstxt := 'Change private key to '+wk.Name;
+      end else if rbTransferToANewOwner.Checked then begin
+        if account.balance>fee then _fee := fee
+        else _fee := 0;
+        op := TOpChangeKey.Create(account.account,account.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,_fee,FEncodedPayload);
+        operationstxt := 'Transfer to a new owner with key type '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
+        inc(_totalfee,_fee);
+      end else begin
+        raise Exception.Create('No operation selected');
+      end;
+      if Assigned(op) And (dooperation) then begin
+        ops.AddOperationToHashTree(op);
+        if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
+        operation_to_string := operation_to_string + op.ToString;
       end;
       end;
-      // Note: Regenerate Payload because we have created a new Private Key...
-      If Not UpdatePayload(errors) then raise Exception.Create(errors);
+      FreeAndNil(op);
+    end;
+    if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
+
+    if (FSenderAccounts.Count>1) then begin
+      if rbTransaction.Checked then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
+      else auxs:='';
+      if Application.MessageBox(PChar('Execute '+Inttostr(FSenderAccounts.Count)+' operations?'+#10+
+        'Operation: '+operationstxt+#10+
+        auxs+
+        'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end else begin
     end else begin
-      FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+operation_to_string+#10+#10+'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end;
     end;
-    FOperation := TOpChangeKey.Create(SenderAccount,P^.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,fee,FEncodedPayload);
-  end else if rbTransferToANewOwner.Checked then begin
-    FOperation := TOpChangeKey.Create(SenderAccount,P^.n_operation+1,wk.PrivateKey,FNewAccountPublicKey,fee,FEncodedPayload);
-  end else begin
-    raise Exception.Create('No operation selected');
-  end;
-  if Application.MessageBox(PChar('Execute this operation:'+#10+#10+FOperation.ToString+#10+#10+'Note: This operation will be transmitted to the network!'),
-     PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
-
-  // Execute operation
-  If FNode.AddOperation(nil,FOperation,errors) then begin
-    Application.MessageBox(PChar('Operation executed:'+#10+FOperation.ToString),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
-    ModalResult := MrOk;
-  end else begin
-    raise Exception.Create(errors);
-  end;
+    i := FNode.AddOperations(nil,ops,errors);
+    if (i=ops.OperationsCount) then begin
+      Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+      ModalResult := MrOk;
+    end else if (i>0) then begin
+      Application.MessageBox(PChar('One or more of your operations has not been executed:'+#10+
+        'Errors:'+#10+
+        errors+#10+#10+
+        'Total successfully executed operations: '+inttostr(i)),PChar(Application.Title),MB_OK+MB_ICONWARNING);
+      ModalResult := MrOk;
+    end else begin
+      raise Exception.Create(errors);
+    end;
+  Finally
+    ops.Free;
+  End;
 end;
 end;
 
 
 procedure TFRMOperation.bbKeysClick(Sender: TObject);
 procedure TFRMOperation.bbKeysClick(Sender: TObject);
@@ -202,6 +255,7 @@ begin
       if Not InputQuery('Wallet password','Enter wallet password',s) then exit;
       if Not InputQuery('Wallet password','Enter wallet password',s) then exit;
       FWalletKeys.WalletPassword := s;
       FWalletKeys.WalletPassword := s;
     Until FWalletKeys.IsValidPassword;
     Until FWalletKeys.IsValidPassword;
+    SetWalletKeys(WalletKeys);
     UpdateOperationOptions(errors);
     UpdateOperationOptions(errors);
   end;
   end;
 end;
 end;
@@ -244,7 +298,9 @@ begin
   FDisabled := true;
   FDisabled := true;
   try
   try
     ebFee.Text := TAccountComp.FormatMoney(Fee);
     ebFee.Text := TAccountComp.FormatMoney(Fee);
-    ebAmount.Text := TAccountComp.FormatMoney(FTxAmount);
+    if SenderAccounts.Count<=1 then begin
+      ebAmount.Text := TAccountComp.FormatMoney(FTxAmount);
+    end;
   finally
   finally
     FDisabled := l;
     FDisabled := l;
   end;
   end;
@@ -261,10 +317,20 @@ procedure TFRMOperation.ebSenderAccountExit(Sender: TObject);
 Var an : Cardinal;
 Var an : Cardinal;
 begin
 begin
   If TAccountComp.AccountTxtNumberToAccountNumber(ebSenderAccount.Text,an) then begin
   If TAccountComp.AccountTxtNumberToAccountNumber(ebSenderAccount.Text,an) then begin
-    SenderAccount := an;
+    SenderAccounts.Disable;
+    try
+      SenderAccounts.Clear;
+      SenderAccounts.Add(an);
+    finally
+      SenderAccounts.Enable;
+    end;
     ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
     ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
   end else begin
   end else begin
-    ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccount);
+    if SenderAccounts.Count=1 then begin
+      ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
+    end else begin
+      ebSenderAccount.Text := '';
+    end;
   end;
   end;
 end;
 end;
 
 
@@ -275,10 +341,10 @@ end;
 
 
 procedure TFRMOperation.FormCreate(Sender: TObject);
 procedure TFRMOperation.FormCreate(Sender: TObject);
 begin
 begin
+  FSenderAccounts := TOrderedCardinalList.Create;
+  FSenderAccounts.OnListChanged := OnSenderAccountsChanged;
   FDisabled := true;
   FDisabled := true;
   FNode := TNode.Node;
   FNode := TNode.Node;
-  FOperation := Nil;
-  FPAccount := Nil;
   ebDestAccount.Text := '';
   ebDestAccount.Text := '';
   ebAmount.Text := TAccountComp.FormatMoney(0);
   ebAmount.Text := TAccountComp.FormatMoney(0);
   ebEncryptPassword.Text := '';
   ebEncryptPassword.Text := '';
@@ -291,36 +357,39 @@ begin
   lblNewOwnerErrors.Caption := '';
   lblNewOwnerErrors.Caption := '';
   FTxDestAccount := 0;
   FTxDestAccount := 0;
   FTxAmount := 0;
   FTxAmount := 0;
-  FSenderAccount := MaxInt;
   FNewAccountPublicKey := CT_Account_NUL.accountkey;
   FNewAccountPublicKey := CT_Account_NUL.accountkey;
   FDisabled := false;
   FDisabled := false;
   lblAccountBalance.Caption := '';
   lblAccountBalance.Caption := '';
+  memoAccounts.Lines.Clear;
 end;
 end;
 
 
 procedure TFRMOperation.FormDestroy(Sender: TObject);
 procedure TFRMOperation.FormDestroy(Sender: TObject);
 begin
 begin
-  if Assigned(FPAccount) then Dispose(FPAccount);
-  FreeAndNil(FOperation);
+  FreeAndNil(FSenderAccounts);
 end;
 end;
 
 
-function TFRMOperation.GetSenderAcccount: PAccount;
+procedure TFRMOperation.memoPayloadClick(Sender: TObject);
+Var errors : AnsiString;
 begin
 begin
-  if Not Assigned(FPAccount) then begin
-    if (Assigned(FNode)) And (FSenderAccount>=0) And (FSenderAccount<FNode.Bank.AccountsCount) then begin
-      New(FPAccount);
-      FPAccount^ := FNode.Operations.SafeBoxTransaction.Account(FSenderAccount);
-    end;
+  if SenderAccounts.Count>0 then begin
+    UpdatePayload(TNode.Node.Bank.SafeBox.Account(SenderAccounts.Get(0)),errors);
   end;
   end;
-  Result := FPAccount;
-  if Assigned(FPAccount) then begin
-    lblAccountBalance.Caption := 'Balance: '+TAccountComp.FormatMoney(FPAccount.balance);
-  end else lblAccountBalance.Caption := '';
 end;
 end;
 
 
-procedure TFRMOperation.memoPayloadClick(Sender: TObject);
+procedure TFRMOperation.OnSenderAccountsChanged(Sender: TObject);
 Var errors : AnsiString;
 Var errors : AnsiString;
 begin
 begin
-  UpdatePayload(errors);
+  if SenderAccounts.Count>1 then begin
+    ebAmount.Text := 'ALL BALANCE';
+    ebAmount.font.Style := [fsBold];
+    ebAmount.ReadOnly := true;
+  end else begin
+    ebAmount.Text := TAccountComp.FormatMoney(0);
+    ebAmount.ReadOnly := false;
+    ebAmount.Enabled := true;
+  end;
+  UpdateAccountsInfo;
+  UpdateOperationOptions(errors);
 end;
 end;
 
 
 procedure TFRMOperation.rbTransactionClick(Sender: TObject);
 procedure TFRMOperation.rbTransactionClick(Sender: TObject);
@@ -343,21 +412,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFRMOperation.SetSenderAccount(const Value: Cardinal);
-begin
-  if FSenderAccount=Value then exit;
-  if Value>TNode.Node.Bank.AccountsCount-1 then FSenderAccount := 0
-  else FSenderAccount := Value;
-
-  if Assigned(FPAccount) then begin
-    Dispose(FPAccount);
-    FPAccount := Nil;
-  end;
-  ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(Value);
-  //
-  rbTransactionClick(Nil);
-end;
-
 procedure TFRMOperation.SetWalletKeys(const Value: TWalletKeys);
 procedure TFRMOperation.SetWalletKeys(const Value: TWalletKeys);
 Var i : Integer;
 Var i : Integer;
   wk : TWalletKey;
   wk : TWalletKey;
@@ -385,15 +439,61 @@ begin
   memoPayloadClick(Nil);
   memoPayloadClick(Nil);
 end;
 end;
 
 
-function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
-Var P : PAccount;
+procedure TFRMOperation.UpdateAccountsInfo;
+Var ld : Boolean;
   i : Integer;
   i : Integer;
+  balance : int64;
+  acc : TAccount;
+  accountstext : String;
+begin
+  ld := FDisabled;
+  FDisabled := true;
+  Try
+    lblAccountCaption.Caption := 'Account';
+    lblAccountsCount.Visible := false;
+    lblAccountsCount.caption := inttostr(senderAccounts.Count)+' accounts';
+    balance := 0;
+    if SenderAccounts.Count<=0 then begin
+      ebSenderAccount.Text := '';
+      memoAccounts.Visible := false;
+      ebSenderAccount.Visible := true;
+    end else if SenderAccounts.Count=1 then begin
+      ebSenderAccount.Text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
+      memoAccounts.Visible := false;
+      ebSenderAccount.Visible := true;
+      balance := TNode.Node.Operations.SafeBoxTransaction.Account(SenderAccounts.Get(0)).balance;
+    end else begin
+      // Multiple sender accounts
+      lblAccountCaption.Caption := 'Accounts';
+      lblAccountsCount.Visible := true;
+      ebSenderAccount.Visible := false;
+      accountstext := '';
+      for i := 0 to SenderAccounts.Count - 1 do begin
+         acc := TNode.Node.Operations.SafeBoxTransaction.Account(SenderAccounts.Get(i));
+         balance := balance + acc.balance;
+         if (accountstext<>'') then accountstext:=accountstext+'; ';
+         accountstext := accountstext+TAccountComp.AccountNumberToAccountTxtNumber(acc.account)+' ('+TAccountComp.FormatMoney(acc.balance)+')';
+      end;
+      memoAccounts.Lines.Text := accountstext;
+      memoAccounts.Visible := true;
+    end;
+    ebSenderAccount.Enabled := ebSenderAccount.Visible;
+    lblAccountBalance.Caption := TAccountComp.FormatMoney(balance);
+  Finally
+    FDisabled := ld;
+  End;
+end;
+
+function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
+Var
+  iWallet,iAcc : Integer;
   wk : TWalletKey;
   wk : TWalletKey;
   e : AnsiString;
   e : AnsiString;
+  sender_account : TAccount;
 begin
 begin
   Result := false;
   Result := false;
+  sender_account := CT_Account_NUL;
   errors := '';
   errors := '';
-  FreeAndNil(FOperation);
   rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
   rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
   lblDestAccount.Enabled := rbTransaction.Checked;
   lblDestAccount.Enabled := rbTransaction.Checked;
   lblAmount.Enabled := rbTransaction.Checked;
   lblAmount.Enabled := rbTransaction.Checked;
@@ -402,36 +502,39 @@ begin
   lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
   lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
   try
   try
     Try
     Try
-      P := GetSenderAcccount;
       bbPassword.Visible := false;
       bbPassword.Visible := false;
       bbPassword.Enabled := false;
       bbPassword.Enabled := false;
-      if Not Assigned(P) then begin
-        errors := 'Invalid sender account';
-        lblGlobalErrors.Caption := errors;
-        exit;
-      end;
       if Not Assigned(WalletKeys) then begin
       if Not Assigned(WalletKeys) then begin
         errors := 'No wallet keys';
         errors := 'No wallet keys';
         lblGlobalErrors.Caption := errors;
         lblGlobalErrors.Caption := errors;
         exit;
         exit;
       end;
       end;
-      i := WalletKeys.IndexOfAccountKey(P^.accountkey);
-      if (i<0) then begin
-        errors := 'Private key not found in wallet... You cannot operate with this account';
+      if SenderAccounts.Count=0 then begin
+        errors := 'No sender account';
         lblGlobalErrors.Caption := errors;
         lblGlobalErrors.Caption := errors;
         exit;
         exit;
-      end;
-      wk := WalletKeys.Key[i];
-      if not assigned(wk.PrivateKey) then begin
-        if wk.CryptedKey<>'' then begin
-          errors := 'Wallet is password protected. Need password';
-          bbPassword.Visible := true;
-          bbPassword.Enabled := true;
-        end else begin
-          errors := 'Private key not found in wallet, only public key. You cannot operate with this account';
+      end else begin
+        for iAcc := 0 to SenderAccounts.Count - 1 do begin
+          sender_account := TNode.Node.Bank.SafeBox.Account(SenderAccounts.Get(iAcc));
+          iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountkey);
+          if (iWallet<0) then begin
+            errors := 'Private key of account '+TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account)+' not found in wallet';
+            lblGlobalErrors.Caption := errors;
+            exit;
+          end;
+          wk := WalletKeys.Key[iWallet];
+          if not assigned(wk.PrivateKey) then begin
+            if wk.CryptedKey<>'' then begin
+              errors := 'Wallet is password protected. Need password';
+              bbPassword.Visible := true;
+              bbPassword.Enabled := true;
+            end else begin
+              errors := 'Only public key of account '+TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account)+' found in wallet. You cannot operate with this account';
+            end;
+            lblGlobalErrors.Caption := errors;
+            exit;
+          end;
         end;
         end;
-        lblGlobalErrors.Caption := errors;
-        exit;
       end;
       end;
       lblGlobalErrors.Caption := '';
       lblGlobalErrors.Caption := '';
     Finally
     Finally
@@ -452,7 +555,6 @@ begin
       rbEncryptedWithOldEC.Checked := false;
       rbEncryptedWithOldEC.Checked := false;
       rbEncryptedWithEC.Caption := 'Encrypted with dest. account public key';
       rbEncryptedWithEC.Caption := 'Encrypted with dest. account public key';
       ebDestAccount.ParentFont := true;
       ebDestAccount.ParentFont := true;
-      ebAmount.ParentFont := true;
       ebFee.ParentFont := true;
       ebFee.ParentFont := true;
       cbNewPrivateKey.Font.Color := clGrayText;
       cbNewPrivateKey.Font.Color := clGrayText;
       ebNewPublicKey.Font.Color := clGrayText;
       ebNewPublicKey.Font.Color := clGrayText;
@@ -461,20 +563,28 @@ begin
         lblTransactionErrors.Caption := errors;
         lblTransactionErrors.Caption := errors;
         exit;
         exit;
       end;
       end;
-      if not TAccountComp.TxtToMoney(ebAmount.Text,FTxAmount) then begin
-        errors := 'Invalid amount ('+ebAmount.Text+')';
-        lblTransactionErrors.Caption := errors;
-        exit;
+      if SenderAccounts.Count>1 then begin
+        // If multisender then amount is ALL balance
+        ebAmount.Font.Color := clNavy;
+      end else begin
+        ebAmount.ParentFont := true;
+        if not TAccountComp.TxtToMoney(ebAmount.Text,FTxAmount) then begin
+          errors := 'Invalid amount ('+ebAmount.Text+')';
+          lblTransactionErrors.Caption := errors;
+          exit;
+        end;
       end;
       end;
       if not TAccountComp.TxtToMoney(ebFee.Text,FFee) then begin
       if not TAccountComp.TxtToMoney(ebFee.Text,FFee) then begin
         errors := 'Invalid fee ('+ebFee.Text+')';
         errors := 'Invalid fee ('+ebFee.Text+')';
         lblTransactionErrors.Caption := errors;
         lblTransactionErrors.Caption := errors;
         exit;
         exit;
       end;
       end;
-      if (P^.balance<(FTxAmount+FFee)) then begin
-        errors := 'Insufficient funds';
-        lblTransactionErrors.Caption := errors;
-        exit;
+      if (SenderAccounts.Count=1) then begin
+        if (sender_account.balance<(FTxAmount+FFee)) then begin
+          errors := 'Insufficient funds';
+          lblTransactionErrors.Caption := errors;
+          exit;
+        end;
       end;
       end;
 
 
       lblTransactionErrors.Caption := '';
       lblTransactionErrors.Caption := '';
@@ -513,9 +623,12 @@ begin
       cbNewPrivateKey.Font.Color := clGrayText;
       cbNewPrivateKey.Font.Color := clGrayText;
       ebNewPublicKey.ParentFont := true;
       ebNewPublicKey.ParentFont := true;
       If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,FNewAccountPublicKey,errors) then begin
       If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,FNewAccountPublicKey,errors) then begin
-//        errors := 'Invalid new owner public key';
         lblNewOwnerErrors.Caption := errors;
         lblNewOwnerErrors.Caption := errors;
+        lblNewOwnerErrors.Font.Color := clRed;
         exit;
         exit;
+      end else begin
+        lblNewOwnerErrors.Caption := 'New key type: '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
+        lblNewOwnerErrors.Font.Color := clGreen;
       end;
       end;
       Result := true;
       Result := true;
     end else begin
     end else begin
@@ -535,10 +648,10 @@ begin
 
 
   end;
   end;
   //
   //
-  UpdatePayload(e);
+  UpdatePayload(sender_account, e);
 end;
 end;
 
 
-function TFRMOperation.UpdatePayload(var errors : AnsiString) : Boolean;
+function TFRMOperation.UpdatePayload(Const SenderAccount : TAccount; var errors : AnsiString) : Boolean;
 Var payload_u : AnsiString;
 Var payload_u : AnsiString;
   payload_encrypted : TRawBytes;
   payload_encrypted : TRawBytes;
   account : TAccount;
   account : TAccount;
@@ -559,7 +672,7 @@ begin
     end;
     end;
     if (rbEncryptedWithOldEC.Checked) then begin
     if (rbEncryptedWithOldEC.Checked) then begin
       errors := 'Error encrypting';
       errors := 'Error encrypting';
-      account := FNode.Node.Operations.SafeBoxTransaction.Account(SenderAccount);
+      account := FNode.Node.Operations.SafeBoxTransaction.Account(SenderAccount.account);
       payload_encrypted := ECIESEncrypt(account.accountkey,payload_u);
       payload_encrypted := ECIESEncrypt(account.accountkey,payload_u);
       valid := payload_encrypted<>'';
       valid := payload_encrypted<>'';
     end else if (rbEncryptedWithEC.Checked) then begin
     end else if (rbEncryptedWithEC.Checked) then begin

+ 342 - 29
Units/Forms/UFRMWallet.dfm

@@ -423,28 +423,53 @@ object FRMWallet: TFRMWallet
         end
         end
         object ebFindAccountNumber: TEdit
         object ebFindAccountNumber: TEdit
           Left = 87
           Left = 87
-          Top = 32
+          Top = 33
           Width = 83
           Width = 83
           Height = 21
           Height = 21
-          TabOrder = 2
+          TabOrder = 3
           OnChange = ebFindAccountNumberChange
           OnChange = ebFindAccountNumberChange
           OnExit = ebFindAccountNumberExit
           OnExit = ebFindAccountNumberExit
         end
         end
-      end
-      object dgAccountOperations: TDrawGrid
-        Left = 383
-        Top = 66
-        Width = 508
-        Height = 354
-        Align = alClient
-        TabOrder = 1
-        OnDblClick = MiDecodePayloadClick
-        RowHeights = (
-          24
-          24
-          24
-          24
-          24)
+        object bbChangeKeyName: TBitBtn
+          Left = 685
+          Top = 5
+          Width = 126
+          Height = 25
+          Caption = 'Change Key name'
+          DoubleBuffered = True
+          ParentDoubleBuffered = False
+          TabOrder = 2
+          OnClick = bbChangeKeyNameClick
+        end
+        object cbFilterAccounts: TCheckBox
+          Left = 260
+          Top = 35
+          Width = 151
+          Height = 17
+          Caption = 'Filter accounts by balance'
+          TabOrder = 4
+          OnClick = cbFilterAccountsClick
+        end
+        object ebFilterAccountByBalanceMin: TEdit
+          Left = 412
+          Top = 33
+          Width = 83
+          Height = 21
+          Hint = 'Min balance'
+          TabOrder = 5
+          OnExit = ebFilterAccountByBalanceMinExit
+          OnKeyPress = ebFilterAccountByBalanceMinKeyPress
+        end
+        object ebFilterAccountByBalanceMax: TEdit
+          Left = 503
+          Top = 33
+          Width = 83
+          Height = 21
+          Hint = 'Max balance'
+          TabOrder = 6
+          OnExit = ebFilterAccountByBalanceMinExit
+          OnKeyPress = ebFilterAccountByBalanceMinKeyPress
+        end
       end
       end
       object pnlAccounts: TPanel
       object pnlAccounts: TPanel
         Left = 0
         Left = 0
@@ -453,7 +478,7 @@ object FRMWallet: TFRMWallet
         Height = 354
         Height = 354
         Align = alLeft
         Align = alLeft
         BevelOuter = bvNone
         BevelOuter = bvNone
-        TabOrder = 2
+        TabOrder = 1
         object dgAccounts: TDrawGrid
         object dgAccounts: TDrawGrid
           Left = 0
           Left = 0
           Top = 0
           Top = 0
@@ -479,6 +504,9 @@ object FRMWallet: TFRMWallet
           Align = alBottom
           Align = alBottom
           BevelOuter = bvNone
           BevelOuter = bvNone
           TabOrder = 1
           TabOrder = 1
+          DesignSize = (
+            380
+            34)
           object Label17: TLabel
           object Label17: TLabel
             Left = 5
             Left = 5
             Top = 10
             Top = 10
@@ -507,6 +535,253 @@ object FRMWallet: TFRMWallet
             Height = 13
             Height = 13
             Caption = '000'
             Caption = '000'
           end
           end
+          object bbAccountsRefresh: TBitBtn
+            Left = 302
+            Top = 6
+            Width = 75
+            Height = 25
+            Anchors = [akTop, akRight]
+            Caption = 'Refresh'
+            DoubleBuffered = True
+            Glyph.Data = {
+              36030000424D3603000000000000360000002800000010000000100000000100
+              18000000000000030000120B0000120B00000000000000000000FF00FFFF00FF
+              C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2A6
+              A4C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEFCFBFEFCFBFEFCFBFEFCFBFE
+              FCFBFEFCFBFEFCFBFEFCFBFEFCFBFEFCFBC2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FEFCFBFEFCFBFEFCFBFEFCFBD8EBD6018A02018A02D8EBD6FEFCFBFEFC
+              FBC2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEFBF7FEFBF7018A02D8EAD201
+              8A02D8EAD2D8EAD2018A02FEFBF7FEFBF7C2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FEF9F4FEF9F4018A02018A02D8E8D0FEF9F4FEF9F4D8E8D0FEF9F4FEF9
+              F4C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEF7F0FEF7F0018A02018A0201
+              8A02FEF7F0FEF7F0FEF7F0FEF7F0FEF7F0C2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FEF5ECFEF5ECFEF5ECFEF5ECFEF5EC018A02018A02018A02FEF5ECFEF5
+              ECC2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FEF3E9FEF3E9D8E3C7FEF3E9FE
+              F3E9D8E3C7018A02018A02FEF3E9FEF3E9C2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FFF1E5FFF1E5018A02D9E2C3D9E2C3018A02D9E2C3018A02FFF1E5FFF1
+              E5C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FFF0E2FFF0E2D9E1C1018A0201
+              8A02D9E1C1DDCFC2DDCFC2DDCFC2DDCFC2C2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FFEEDEFFEEDEFFEEDEFFEEDEFFEEDEFFEEDEC5B5A9C3B4A8C2B3A7C1B2
+              A6C2A6A4FF00FFFF00FFFF00FFFF00FFC2A6A4FFECDAFFECDAFFECDAFFECDAFF
+              ECDAFFECDAB0A296B0A296B0A296B0A296C2A6A4FF00FFFF00FFFF00FFFF00FF
+              C2A6A4FFEAD7FFEAD7FFEAD7FFEAD7FFEAD7C9B9ACFBF8F4FBF8F4E6DAD9C2A6
+              A4FF00FFFF00FFFF00FFFF00FFFF00FFC2A6A4FFE8D3FFE8D3FFE8D3FFE8D3FF
+              E8D3C9B9ACFBF8F4DFCEC7C2A6A4FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              C2A6A4FFE6D0FFE6D0FFE6D0FFE6D0FFE6D0C9B9ACDFCEC7C2A6A4FF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FFC2A6A4C2A6A4C2A6A4C2A6A4C2A6A4C2
+              A6A4C2A6A4C2A6A4FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+            ParentDoubleBuffered = False
+            TabOrder = 0
+            OnClick = bbAccountsRefreshClick
+          end
+        end
+      end
+      object pcAccountsOptions: TPageControl
+        Left = 383
+        Top = 66
+        Width = 508
+        Height = 354
+        ActivePage = tsMultiSelectAccounts
+        Align = alClient
+        TabOrder = 2
+        object tsAccountOperations: TTabSheet
+          Caption = 'Operations of selected Account'
+          object dgAccountOperations: TDrawGrid
+            Left = 0
+            Top = 0
+            Width = 500
+            Height = 326
+            Align = alClient
+            TabOrder = 0
+            OnDblClick = MiDecodePayloadClick
+            RowHeights = (
+              24
+              24
+              24
+              24
+              24)
+          end
+        end
+        object tsMultiSelectAccounts: TTabSheet
+          Caption = 'Selected accounts for massive operations'
+          ImageIndex = 1
+          object dgSelectedAccounts: TDrawGrid
+            Left = 41
+            Top = 31
+            Width = 320
+            Height = 269
+            Align = alLeft
+            TabOrder = 0
+          end
+          object pnlSelectedAccountsTop: TPanel
+            Left = 0
+            Top = 0
+            Width = 500
+            Height = 31
+            Align = alTop
+            BevelOuter = bvNone
+            Font.Charset = DEFAULT_CHARSET
+            Font.Color = clWindowText
+            Font.Height = -13
+            Font.Name = 'Tahoma'
+            Font.Style = [fsBold]
+            ParentFont = False
+            TabOrder = 1
+            object Label15: TLabel
+              Left = 41
+              Top = 4
+              Width = 361
+              Height = 16
+              Caption = 'Select multiple accounts to execute massive operations'
+            end
+          end
+          object pnlSelectedAccountsBottom: TPanel
+            Left = 0
+            Top = 300
+            Width = 500
+            Height = 26
+            Align = alBottom
+            BevelOuter = bvNone
+            TabOrder = 2
+            object Label20: TLabel
+              Left = 41
+              Top = 6
+              Width = 48
+              Height = 13
+              Caption = 'Accounts:'
+            end
+            object lblSelectedAccountsCount: TLabel
+              Left = 96
+              Top = 6
+              Width = 18
+              Height = 13
+              Caption = '000'
+            end
+            object Label22: TLabel
+              Left = 156
+              Top = 6
+              Width = 88
+              Height = 13
+              Caption = 'Accounts Balance:'
+            end
+            object lblSelectedAccountsBalance: TLabel
+              Left = 250
+              Top = 6
+              Width = 18
+              Height = 13
+              Caption = '000'
+            end
+          end
+          object pnlSelectedAccountsLeft: TPanel
+            Left = 0
+            Top = 31
+            Width = 41
+            Height = 269
+            Align = alLeft
+            BevelOuter = bvNone
+            TabOrder = 3
+            object sbSelectedAccountsAdd: TSpeedButton
+              Left = 2
+              Top = 0
+              Width = 33
+              Height = 31
+              Caption = '>'
+              OnClick = sbSelectedAccountsAddClick
+            end
+            object sbSelectedAccountsAddAll: TSpeedButton
+              Left = 2
+              Top = 37
+              Width = 33
+              Height = 31
+              Caption = '>>'
+              OnClick = sbSelectedAccountsAddAllClick
+            end
+            object sbSelectedAccountsDel: TSpeedButton
+              Left = 2
+              Top = 74
+              Width = 33
+              Height = 31
+              Caption = '<'
+              OnClick = sbSelectedAccountsDelClick
+            end
+            object sbSelectedAccountsDelAll: TSpeedButton
+              Left = 2
+              Top = 111
+              Width = 33
+              Height = 31
+              Caption = '<<'
+              OnClick = sbSelectedAccountsDelAllClick
+            end
+          end
+          object bbSelectedAccountsOperation: TBitBtn
+            Left = 367
+            Top = 31
+            Width = 75
+            Height = 61
+            Caption = 'Operations'
+            DoubleBuffered = True
+            Glyph.Data = {
+              F6060000424DF606000000000000360000002800000018000000180000000100
+              180000000000C0060000120B0000120B00000000000000000000FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFFF00FFFF00FF019ACF019ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C8518FF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FF0D9FD18BD4EE6BD3F845C0ED28B0E0019ACF01
+              9ACF019ACF019ACFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF0C85180C8518
+              FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF069CD076C8E5A9E9FE6DD8
+              FF75DBFF77DCFF77DBFF63D1F930B3E3029BD0019ACF019ACF019ACF019ACFFF
+              00FF0C85181399220C8518FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACF
+              34AFD9BCE9F86ED8FF6FD8FE70D8FE70D8FE71D8FF0C85180C85180C85180C85
+              180C85180C85180C85180C85181DAC31139A220C8518FF00FFFF00FFFF00FFFF
+              00FFFF00FF019ACF1FA9D68FD3EB97E4FF6FD9FE71D9FE71D9FE71D9FE0C8518
+              57E38851DD7E4AD77443D0693BC95E34C1522BBA4725B33C1EAE33149B230C85
+              18FF00FFFF00FFFF00FFFF00FF019ACF31B1DC49B7DEBDEEFB71DDFE77DEFE77
+              DEFE77DEFE0C85185EE89059E48953DE804CD87645D16C3DCA6035C2542DBB49
+              26B53F1FAF35149B250C8518FF00FFFF00FFFF00FF019ACF52C2E71DA7D5ADE2
+              F38FE8FF7CE2FE7CE3FE7CE3FE0C851861EB955FE9925AE58B54DF824DD97846
+              D26D3ECB6237C4562FBD4C27B64021B037159B250C8518FF00FFFF00FF019ACF
+              60CAEF1FA8D85EC1E1C2E6ED8ACEE08FCFE18ECFE10C851861EB9561EB955FEA
+              935CE58D56E0844FDB7A48D47040CD6538C65931BF4D1DA3320C8518FF00FFFF
+              00FFFF00FF019ACF65CFF53EB7E52CA9D4C5EFF8ACF3FEA5F2FFA5F2FF0C8518
+              61EB9561EB9561EB9561EB945CE68E57E18650DC7C49D57242CE6727AD410C85
+              18FF00FFFF00FFFF00FFFF00FF019ACF69D1F855C4F32A9CC673CBE7D6FEFDB1
+              FBFDB2FBFD0C85180C85180C85180C85180C85180C85180C85180C851852DD7F
+              32B6500C851898FAFF019ACFFF00FFFF00FFFF00FF019ACF77D5FC5CC8FB748E
+              A224A8D5B9E7F3D5F5F9D5F6F9D6F6FADCFAFBCDFDFCB9FCFCAFFAFCB0FAFCB1
+              FAFC0C85183ABE5C0C85189FFCFFA4FFFF43C1E2019ACFFF00FFFF00FF019ACF
+              8BDBFF5FCDFFB7898973C3DD18A2D218A2D216A2D215A1D21AA4D391D7EBEBFE
+              FDDBFDFCC5FBFBC2FBFB0C85180C851883E4F3B6FDFFBAFFFFB5FCFD019ACFFF
+              00FFFF00FF019ACF99E2FF67D3FFB88989FEF5ECFDF3EBF0EFEAE5EBE8D6E5E6
+              A4D2E025A6D34DB9DDE5F8FBF5FDFCEBFCFB0C8518C4FBFF9CE4F2DAFEFFD9FE
+              FFE3FFFFADE9F5019ACFFF00FF019ACF9FE9FF70DCFFB88989FEF3E9FFF2E6FE
+              F3E9FEF3E9FEF3E9FEF3E9D4E4E439ADD422A5D49DD8ECF1F9FBEEEFEFE9FDFF
+              CEEEF7F8FFFFF7FFFFFEFFFFE9F9FD019ACFFF00FF019ACFA7EFFF76E5FFB889
+              89FFF2E5FFF0E2FFF2E5FFF2E5FFF2E5FFF2E5FFF2E5EAEBE38EC9DA44B0D501
+              9ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACF019ACFFF00FF019ACF
+              ABF6FF7EEDFFB88989FFF0E2FFEFDFFFF0E2FFF0E2FFF0E2FFF0E2FFF0E2FEEE
+              E0FBECDEFAEBDEF6E6D9B8898993F7FF019ACFFF00FFFF00FFFF00FFFF00FFFF
+              00FFFF00FF019ACFC7FFFF82F5FFB88989FFEEDFFFECDBFFEEDFFFEEDFFFEEDF
+              FFEEDFF9E8D9DECCC1D9CABDCFBDB4C8B3ACB88989B5FFFF019ACFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FF019ACFA4E0F0A0FDFFB88989FFECDBFFEBD8FF
+              ECDBFFECDBFFECDBFFECDBF5E2D2C4ABA7C2A8A5BBA39FC2AFA9B88989019ACF
+              FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF019ACFECFFFFB889
+              89FFEBD8FFEAD5FFEBD8FFEBD8FFEBD8FFEBD8FFEBD8D9C8C5FEFEFDFEF6EFDE
+              C9C0B88989FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FF019ACFB88989FFE9D5FFE8D3FFE9D5FFE9D5FFE9D5FFE9D5FFE9D5C6AD
+              A9FEF8F2E8D4CACD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFB88989FFE7D1FFE7D0FFE7D1FFE7D1FFE7D1
+              FFE7D1E7CEBFD3BFB9E8D5CCCD9999FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB88989FFE6CFFFE6CFFF
+              E6CFFFE6CFFFE6CFFFE6CFD5BBB2E0CCC5CD9999FF00FFFF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB889
+              89B88989B88989B88989B88989B88989B88989B88989B88989FF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF}
+            Layout = blGlyphTop
+            ParentDoubleBuffered = False
+            TabOrder = 4
+            OnClick = bbSelectedAccountsOperationClick
+          end
         end
         end
       end
       end
     end
     end
@@ -971,16 +1246,6 @@ object FRMWallet: TFRMWallet
     Top = 160
     Top = 160
     object miProject: TMenuItem
     object miProject: TMenuItem
       Caption = 'Project'
       Caption = 'Project'
-      object miNewOperation: TMenuItem
-        Caption = 'New Operation'
-        ShortCut = 120
-        OnClick = miNewOperationClick
-      end
-      object MiDecodePayload: TMenuItem
-        Caption = 'Decode Payload'
-        ShortCut = 113
-        OnClick = MiDecodePayloadClick
-      end
       object miPrivatekeys: TMenuItem
       object miPrivatekeys: TMenuItem
         Caption = 'Private keys'
         Caption = 'Private keys'
         ShortCut = 16464
         ShortCut = 16464
@@ -1006,6 +1271,54 @@ object FRMWallet: TFRMWallet
         OnClick = MiCloseClick
         OnClick = MiCloseClick
       end
       end
     end
     end
+    object MiOperations: TMenuItem
+      Caption = 'Operations'
+      object miNewOperation: TMenuItem
+        Caption = 'New single Operation'
+        ShortCut = 120
+        OnClick = miNewOperationClick
+      end
+      object MiDecodePayload: TMenuItem
+        Caption = 'Decode Payload'
+        ShortCut = 113
+        OnClick = MiDecodePayloadClick
+      end
+      object MiFindaccount: TMenuItem
+        Caption = 'Find account'
+        ShortCut = 16454
+        OnClick = MiFindaccountClick
+      end
+      object N2: TMenuItem
+        Caption = '-'
+      end
+      object MiAddaccounttoSelected: TMenuItem
+        Caption = 'Add account to selected'
+        ShortCut = 117
+        OnClick = MiAddaccounttoSelectedClick
+      end
+      object MiRemoveaccountfromselected: TMenuItem
+        Caption = 'Remove account from selected'
+        ShortCut = 118
+        OnClick = MiRemoveaccountfromselectedClick
+      end
+      object MiMultiaccountoperation: TMenuItem
+        Caption = 'Multi account operation'
+        OnClick = MiMultiaccountoperationClick
+      end
+      object N3: TMenuItem
+        Caption = '-'
+      end
+      object MiFindpreviousaccountwithhighbalance: TMenuItem
+        Caption = 'Find previous account with high balance'
+        ShortCut = 16498
+        OnClick = MiFindpreviousaccountwithhighbalanceClick
+      end
+      object MiFindnextaccountwithhighbalance: TMenuItem
+        Caption = 'Find next account with high balance'
+        ShortCut = 114
+        OnClick = MiFindnextaccountwithhighbalanceClick
+      end
+    end
     object miAbout: TMenuItem
     object miAbout: TMenuItem
       Caption = 'About'
       Caption = 'About'
       object miAboutPascalCoin: TMenuItem
       object miAboutPascalCoin: TMenuItem
@@ -1019,7 +1332,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Left = 105
     Top = 180
     Top = 180
     Bitmap = {
     Bitmap = {
-      494C010102000800EC0010003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010102000800000110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 364 - 31
Units/Forms/UFRMWallet.pas

@@ -20,7 +20,7 @@ uses
   Dialogs, pngimage, ExtCtrls, ComCtrls, UWalletKeys, ShlObj, ADOInt, StdCtrls,
   Dialogs, pngimage, ExtCtrls, ComCtrls, UWalletKeys, ShlObj, ADOInt, StdCtrls,
   ULog, DB, ADODB, Grids, DBGrids, DBCGrids, UAppParams,
   ULog, DB, ADODB, Grids, DBGrids, DBCGrids, UAppParams,
   UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
   UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
-  AppEvnts, UNetProtocol, UCrypto;
+  AppEvnts, UNetProtocol, UCrypto, Buttons;
 
 
 Const
 Const
   CT_PARAM_GridAccountsStream = 'GridAccountsStream';
   CT_PARAM_GridAccountsStream = 'GridAccountsStream';
@@ -64,7 +64,6 @@ type
     pnlMyAccountsTop: TPanel;
     pnlMyAccountsTop: TPanel;
     dgAccounts: TDrawGrid;
     dgAccounts: TDrawGrid;
     cbMyPrivateKeys: TComboBox;
     cbMyPrivateKeys: TComboBox;
-    dgAccountOperations: TDrawGrid;
     Splitter1: TSplitter;
     Splitter1: TSplitter;
     MainMenu: TMainMenu;
     MainMenu: TMainMenu;
     miProject: TMenuItem;
     miProject: TMenuItem;
@@ -150,6 +149,38 @@ type
     ebFindAccountNumber: TEdit;
     ebFindAccountNumber: TEdit;
     Label18: TLabel;
     Label18: TLabel;
     IPnodes1: TMenuItem;
     IPnodes1: TMenuItem;
+    bbChangeKeyName: TBitBtn;
+    pcAccountsOptions: TPageControl;
+    tsAccountOperations: TTabSheet;
+    dgAccountOperations: TDrawGrid;
+    tsMultiSelectAccounts: TTabSheet;
+    dgSelectedAccounts: TDrawGrid;
+    pnlSelectedAccountsTop: TPanel;
+    pnlSelectedAccountsBottom: TPanel;
+    pnlSelectedAccountsLeft: TPanel;
+    sbSelectedAccountsAdd: TSpeedButton;
+    sbSelectedAccountsAddAll: TSpeedButton;
+    sbSelectedAccountsDel: TSpeedButton;
+    sbSelectedAccountsDelAll: TSpeedButton;
+    Label20: TLabel;
+    lblSelectedAccountsCount: TLabel;
+    Label22: TLabel;
+    lblSelectedAccountsBalance: TLabel;
+    bbSelectedAccountsOperation: TBitBtn;
+    Label15: TLabel;
+    MiOperations: TMenuItem;
+    MiAddaccounttoSelected: TMenuItem;
+    MiRemoveaccountfromselected: TMenuItem;
+    N2: TMenuItem;
+    MiMultiaccountoperation: TMenuItem;
+    N3: TMenuItem;
+    MiFindnextaccountwithhighbalance: TMenuItem;
+    MiFindpreviousaccountwithhighbalance: TMenuItem;
+    MiFindaccount: TMenuItem;
+    cbFilterAccounts: TCheckBox;
+    ebFilterAccountByBalanceMin: TEdit;
+    ebFilterAccountByBalanceMax: TEdit;
+    bbAccountsRefresh: TBitBtn;
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure TimerUpdateStatusTimer(Sender: TObject);
     procedure TimerUpdateStatusTimer(Sender: TObject);
@@ -180,6 +211,23 @@ type
     procedure ebFindAccountNumberChange(Sender: TObject);
     procedure ebFindAccountNumberChange(Sender: TObject);
     procedure ebFindAccountNumberExit(Sender: TObject);
     procedure ebFindAccountNumberExit(Sender: TObject);
     procedure IPnodes1Click(Sender: TObject);
     procedure IPnodes1Click(Sender: TObject);
+    procedure bbChangeKeyNameClick(Sender: TObject);
+    procedure sbSelectedAccountsAddClick(Sender: TObject);
+    procedure sbSelectedAccountsAddAllClick(Sender: TObject);
+    procedure sbSelectedAccountsDelClick(Sender: TObject);
+    procedure sbSelectedAccountsDelAllClick(Sender: TObject);
+    procedure bbSelectedAccountsOperationClick(Sender: TObject);
+    procedure MiAddaccounttoSelectedClick(Sender: TObject);
+    procedure MiRemoveaccountfromselectedClick(Sender: TObject);
+    procedure MiMultiaccountoperationClick(Sender: TObject);
+    procedure MiFindnextaccountwithhighbalanceClick(Sender: TObject);
+    procedure MiFindpreviousaccountwithhighbalanceClick(Sender: TObject);
+    procedure MiFindaccountClick(Sender: TObject);
+    procedure bbAccountsRefreshClick(Sender: TObject);
+    procedure ebFilterAccountByBalanceMinExit(Sender: TObject);
+    procedure ebFilterAccountByBalanceMinKeyPress(Sender: TObject;
+      var Key: Char);
+    procedure cbFilterAccountsClick(Sender: TObject);
   private
   private
     FMinersBlocksFound: Integer;
     FMinersBlocksFound: Integer;
     procedure SetMinersBlocksFound(const Value: Integer);
     procedure SetMinersBlocksFound(const Value: Integer);
@@ -194,6 +242,7 @@ type
     FAppParams : TAppParams;
     FAppParams : TAppParams;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FAccountsGrid : TAccountsGrid;
     FAccountsGrid : TAccountsGrid;
+    FSelectedAccountsGrid : TAccountsGrid;
     FOperationsGrid : TOperationsGrid;
     FOperationsGrid : TOperationsGrid;
     FPendingOperationsGrid : TOperationsGrid;
     FPendingOperationsGrid : TOperationsGrid;
     FOrderedAccountsKeyList : TOrderedAccountKeysList;
     FOrderedAccountsKeyList : TOrderedAccountKeysList;
@@ -202,6 +251,8 @@ type
     FMinerPrivateKeyType : TMinerPrivateKey;
     FMinerPrivateKeyType : TMinerPrivateKey;
     FUpdating : Boolean;
     FUpdating : Boolean;
     FMessagesUnreadCount : Integer;
     FMessagesUnreadCount : Integer;
+    FMinAccountBalance : Int64;
+    FMaxAccountBalance : Int64;
     Procedure CheckMining;
     Procedure CheckMining;
     Procedure OnNewAccount(Sender : TObject);
     Procedure OnNewAccount(Sender : TObject);
     Procedure OnReceivedHelloMessage(Sender : TObject);
     Procedure OnReceivedHelloMessage(Sender : TObject);
@@ -213,8 +264,9 @@ type
     procedure OnNetNodeServersUpdated(Sender : TObject);
     procedure OnNetNodeServersUpdated(Sender : TObject);
     procedure OnNetBlackListUpdated(Sender : TObject);
     procedure OnNetBlackListUpdated(Sender : TObject);
     Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
     Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
+    Procedure OnSelectedAccountsGridUpdated(Sender : TObject);
     Procedure UpdateConnectionStatus;
     Procedure UpdateConnectionStatus;
-    Procedure UpdateAccounts;
+    Procedure UpdateAccounts(RefreshData : Boolean);
     Procedure UpdateBlockChainState;
     Procedure UpdateBlockChainState;
     Procedure UpdatePrivateKeys;
     Procedure UpdatePrivateKeys;
     Procedure UpdateOperations;
     Procedure UpdateOperations;
@@ -226,6 +278,8 @@ type
     procedure Activate; override;
     procedure Activate; override;
     Function ForceMining : Boolean; virtual;
     Function ForceMining : Boolean; virtual;
     Function GetAccountKeyForMiner : TAccountKey;
     Function GetAccountKeyForMiner : TAccountKey;
+    Procedure DoUpdateAccounts;
+    Function DoUpdateAccountsFilter : Boolean;
   public
   public
     { Public declarations }
     { Public declarations }
     Property WalletKeys : TWalletKeys read FWalletKeys;
     Property WalletKeys : TWalletKeys read FWalletKeys;
@@ -257,7 +311,7 @@ begin
   TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
   TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.NetServer.Active := true;
   TNode.Node.NetServer.Active := true;
-  Synchronize( FRMWallet.UpdateAccounts );
+  Synchronize( FRMWallet.DoUpdateAccounts );
 end;
 end;
 
 
 { TFRMWallet }
 { TFRMWallet }
@@ -295,6 +349,7 @@ begin
     TDBStorage(FNode.Bank.Storage).AccessFileName := TFolderHelper.GetPascalCoinDataFolder+'\pascalcoinB03.mdb';
     TDBStorage(FNode.Bank.Storage).AccessFileName := TFolderHelper.GetPascalCoinDataFolder+'\pascalcoinB03.mdb';
     // Init Grid
     // Init Grid
     FAccountsGrid.Node := FNode;
     FAccountsGrid.Node := FNode;
+    FSelectedAccountsGrid.Node := FNode;
     FWalletKeys.OnChanged := OnWalletChanged;
     FWalletKeys.OnChanged := OnWalletChanged;
     FOperationsGrid.Node := FNode;
     FOperationsGrid.Node := FNode;
     FPendingOperationsGrid.Node := FNode;
     FPendingOperationsGrid.Node := FNode;
@@ -324,7 +379,7 @@ begin
     end;
     end;
   end;
   end;
   UpdatePrivateKeys;
   UpdatePrivateKeys;
-  UpdateAccounts;
+  UpdateAccounts(false);
   if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
   if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
     FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
     FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
     miAboutPascalCoinClick(Nil);
     miAboutPascalCoinClick(Nil);
@@ -341,6 +396,46 @@ begin
   TrayIcon.ShowBalloonHint;
   TrayIcon.ShowBalloonHint;
 end;
 end;
 
 
+procedure TFRMWallet.bbAccountsRefreshClick(Sender: TObject);
+begin
+  UpdateAccounts(true);
+end;
+
+procedure TFRMWallet.bbChangeKeyNameClick(Sender: TObject);
+var i : Integer;
+  name : String;
+begin
+  if (cbMyPrivateKeys.ItemIndex<0) then  exit;
+  i := Integer(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
+  if (i<0) Or (i>=FWalletKeys.Count) then raise Exception.Create('Must select a Key');
+  name := FWalletKeys.Key[i].Name;
+  if InputQuery('Change Key name','Input new name',name) then begin
+    FWalletKeys.SetName(i,name);
+  end;
+  UpdatePrivateKeys;
+end;
+
+procedure TFRMWallet.bbSelectedAccountsOperationClick(Sender: TObject);
+var l : TOrderedCardinalList;
+begin
+  CheckIsReady;
+  if FSelectedAccountsGrid.AccountsCount<=0 then raise Exception.Create('Must select at least 1 account');
+  With TFRMOperation.Create(Self) do
+  Try
+    l := FSelectedAccountsGrid.LockAccountsList;
+    try
+      SenderAccounts.CopyFrom(l);
+    finally
+      FSelectedAccountsGrid.UnlockAccountsList;
+    end;
+    Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    WalletKeys := FWalletKeys;
+    ShowModal;
+  Finally
+    Free;
+  End;
+end;
+
 procedure TFRMWallet.bbSendAMessageClick(Sender: TObject);
 procedure TFRMWallet.bbSendAMessageClick(Sender: TObject);
 Var basem,m : String;
 Var basem,m : String;
   them, errors : AnsiString;
   them, errors : AnsiString;
@@ -419,10 +514,15 @@ end;
 procedure TFRMWallet.cbExploreMyAccountsClick(Sender: TObject);
 procedure TFRMWallet.cbExploreMyAccountsClick(Sender: TObject);
 begin
 begin
   cbMyPrivateKeys.Enabled := cbExploreMyAccounts.Checked;
   cbMyPrivateKeys.Enabled := cbExploreMyAccounts.Checked;
-  UpdateAccounts;
+  UpdateAccounts(true);
   UpdateOperations;
   UpdateOperations;
 end;
 end;
 
 
+procedure TFRMWallet.cbFilterAccountsClick(Sender: TObject);
+begin
+  If not DoUpdateAccountsFilter then UpdateAccounts(true);
+end;
+
 procedure TFRMWallet.cbFilterOperationsByDateClick(Sender: TObject);
 procedure TFRMWallet.cbFilterOperationsByDateClick(Sender: TObject);
 begin
 begin
   dtpFilterOperationsDateStart.Enabled := cbFilterOperationsByDate.Checked;
   dtpFilterOperationsDateStart.Enabled := cbFilterOperationsByDate.Checked;
@@ -432,7 +532,7 @@ end;
 
 
 procedure TFRMWallet.cbMyPrivateKeysChange(Sender: TObject);
 procedure TFRMWallet.cbMyPrivateKeysChange(Sender: TObject);
 begin
 begin
-  UpdateAccounts;
+  UpdateAccounts(true);
 end;
 end;
 
 
 procedure TFRMWallet.CheckIsReady;
 procedure TFRMWallet.CheckIsReady;
@@ -521,6 +621,45 @@ begin
   SaveAppParams;
   SaveAppParams;
 end;
 end;
 
 
+procedure TFRMWallet.DoUpdateAccounts;
+begin
+  UpdateAccounts(true);
+end;
+
+Function TFRMWallet.DoUpdateAccountsFilter : Boolean;
+Var m,bmin,bmax:Int64;
+  doupd : Boolean;
+begin
+  if FUpdating then exit;
+  FUpdating := true;
+  Try
+    If Not TAccountComp.TxtToMoney(ebFilterAccountByBalanceMin.Text,bmin) then bmin := 0;
+    If not TAccountComp.TxtToMoney(ebFilterAccountByBalanceMax.Text,bmax) then bmax := CT_MaxWalletAmount;
+    if (bmax<bmin) or (bmax=0) then bmax := CT_MaxWalletAmount;
+    if bmin>bmax then bmin := 0;
+    doupd := (bmin<>FMinAccountBalance) Or (bmax<>FMaxAccountBalance);
+    FMinAccountBalance := bmin;
+    FMaxAccountBalance := bmax;
+    if bmin>0 then
+      ebFilterAccountByBalanceMin.Text:=TAccountComp.FormatMoney(bmin)
+    else ebFilterAccountByBalanceMin.Text := '';
+    if bmax<CT_MaxWalletAmount then
+      ebFilterAccountByBalanceMax.Text := TAccountComp.FormatMoney(bmax)
+    else ebFilterAccountByBalanceMax.Text := '';
+    if cbFilterAccounts.Checked then begin
+      ebFilterAccountByBalanceMin.ParentFont := true;
+      ebFilterAccountByBalanceMax.ParentFont := true;
+    end else begin
+      ebFilterAccountByBalanceMin.font.Color := clDkGray;
+      ebFilterAccountByBalanceMax.font.Color := clDkGray;
+    end;
+  Finally
+    FUpdating := false;
+  End;
+  if doupd then UpdateAccounts(true);
+  Result := doupd;
+end;
+
 procedure TFRMWallet.ebBlockChainBlockStartExit(Sender: TObject);
 procedure TFRMWallet.ebBlockChainBlockStartExit(Sender: TObject);
 var i64 : Int64;
 var i64 : Int64;
 begin
 begin
@@ -558,6 +697,17 @@ begin
   if key=#13 then  ebBlockChainBlockStartExit(Nil);
   if key=#13 then  ebBlockChainBlockStartExit(Nil);
 end;
 end;
 
 
+procedure TFRMWallet.ebFilterAccountByBalanceMinExit(Sender: TObject);
+begin
+  DoUpdateAccountsFilter;
+end;
+
+procedure TFRMWallet.ebFilterAccountByBalanceMinKeyPress(Sender: TObject;
+  var Key: Char);
+begin
+  if key=#13 then DoUpdateAccountsFilter;
+end;
+
 procedure TFRMWallet.ebFilterOperationsAccountExit(Sender: TObject);
 procedure TFRMWallet.ebFilterOperationsAccountExit(Sender: TObject);
 Var acc : Cardinal;
 Var acc : Cardinal;
   i64 : Int64;
   i64 : Int64;
@@ -640,6 +790,8 @@ Var i : Integer;
 begin
 begin
   if CPUCount>1 then FMaxCPUs := CPUCount-1
   if CPUCount>1 then FMaxCPUs := CPUCount-1
   else FMaxCPUs := 1;
   else FMaxCPUs := 1;
+  FMinAccountBalance := 0;
+  FMaxAccountBalance := CT_MaxWalletAmount;
   FMessagesUnreadCount := 0;
   FMessagesUnreadCount := 0;
   lblReceivedMessages.Visible := false;
   lblReceivedMessages.Visible := false;
   memoNetConnections.Lines.Clear;
   memoNetConnections.Lines.Clear;
@@ -667,6 +819,9 @@ begin
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid.DrawGrid := dgAccounts;
   FAccountsGrid.DrawGrid := dgAccounts;
+  FSelectedAccountsGrid := TAccountsGrid.Create(Self);
+  FSelectedAccountsGrid.DrawGrid := dgSelectedAccounts;
+  FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
   FOperationsGrid := TOperationsGrid.Create(Self);
   FOperationsGrid := TOperationsGrid.Create(Self);
   FOperationsGrid.DrawGrid := dgAccountOperations;
   FOperationsGrid.DrawGrid := dgAccountOperations;
   FPendingOperationsGrid := TOperationsGrid.Create(Self);
   FPendingOperationsGrid := TOperationsGrid.Create(Self);
@@ -682,6 +837,7 @@ begin
   UpdateConnectionStatus;
   UpdateConnectionStatus;
   ebFilterOperationsAccount.Text := '';
   ebFilterOperationsAccount.Text := '';
   PageControl.ActivePage := tsAccountsExplorer;
   PageControl.ActivePage := tsAccountsExplorer;
+  pcAccountsOptions.ActivePage := tsAccountOperations;
   dtpFilterOperationsDateStart.Date := Now;
   dtpFilterOperationsDateStart.Date := Now;
   dtpFilterOperationsDateEnd.Date := Now;
   dtpFilterOperationsDateEnd.Date := Now;
   ebFilterOperationsAccount.Text := '';
   ebFilterOperationsAccount.Text := '';
@@ -718,6 +874,7 @@ begin
   FOperationsGrid.Node := Nil;
   FOperationsGrid.Node := Nil;
   FPendingOperationsGrid.Node := Nil;
   FPendingOperationsGrid.Node := Nil;
   FAccountsGrid.Node := Nil;
   FAccountsGrid.Node := Nil;
+  FSelectedAccountsGrid.Node := Nil;
   TNetData.NetData.OnReceivedHelloMessage := Nil;
   TNetData.NetData.OnReceivedHelloMessage := Nil;
   TNetData.NetData.OnStatisticsChanged := Nil;
   TNetData.NetData.OnStatisticsChanged := Nil;
   TNetData.NetData.OnNetConnectionsUpdated := Nil;
   TNetData.NetData.OnNetConnectionsUpdated := Nil;
@@ -859,6 +1016,14 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFRMWallet.MiAddaccounttoSelectedClick(Sender: TObject);
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  pcAccountsOptions.ActivePage := tsMultiSelectAccounts;
+  sbSelectedAccountsAddClick(Sender);
+end;
+
 procedure TFRMWallet.MiCloseClick(Sender: TObject);
 procedure TFRMWallet.MiCloseClick(Sender: TObject);
 begin
 begin
   Close;
   Close;
@@ -875,12 +1040,68 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFRMWallet.MiFindaccountClick(Sender: TObject);
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  ebFindAccountNumber.SetFocus;
+end;
+
+procedure TFRMWallet.MiFindnextaccountwithhighbalanceClick(Sender: TObject);
+Var an  : Cardinal;
+  an64 : Int64;
+  start : TAccount;
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  an64 := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  if an64<0 then an := 0
+  else an := an64;
+  If an>=FNode.Bank.SafeBox.AccountsCount then exit;
+  start := FNode.Bank.SafeBox.Account(an);
+  while (an<FNode.Bank.SafeBox.AccountsCount)  do begin
+    if FNode.Bank.SafeBox.Account(an).balance>start.balance then break
+    else inc(an);
+  end;
+  if (an<FNode.Bank.SafeBox.AccountsCount) then FAccountsGrid.MoveRowToAccount(an)
+  else raise Exception.Create('Not found any account higher than '+TAccountComp.AccountNumberToAccountTxtNumber(start.account)+' with balance higher than '+
+    TAccountComp.FormatMoney(start.balance));
+end;
+
+procedure TFRMWallet.MiFindpreviousaccountwithhighbalanceClick(Sender: TObject);
+Var an  : Cardinal;
+  an64 : Int64;
+  start : TAccount;
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  an64 := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  if an64<0 then an := FNode.Bank.SafeBox.AccountsCount-1
+  else an := an64;
+  If an>=FNode.Bank.SafeBox.AccountsCount then exit;
+  start := FNode.Bank.SafeBox.Account(an);
+  while (an>0)  do begin
+    if FNode.Bank.SafeBox.Account(an).balance>start.balance then break
+    else dec(an);
+  end;
+  if (FNode.Bank.SafeBox.Account(an).balance>start.balance) then FAccountsGrid.MoveRowToAccount(an)
+  else raise Exception.Create('Not found any account lower than '+TAccountComp.AccountNumberToAccountTxtNumber(start.account)+' with balance higher than '+
+    TAccountComp.FormatMoney(start.balance));
+end;
+
+procedure TFRMWallet.MiMultiaccountoperationClick(Sender: TObject);
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  pcAccountsOptions.ActivePage := tsMultiSelectAccounts;
+  bbSelectedAccountsOperationClick(Sender);
+end;
+
 procedure TFRMWallet.miNewOperationClick(Sender: TObject);
 procedure TFRMWallet.miNewOperationClick(Sender: TObject);
 begin
 begin
   CheckIsReady;
   CheckIsReady;
   With TFRMOperation.Create(Self) do
   With TFRMOperation.Create(Self) do
   Try
   Try
-    SenderAccount := FAccountsGrid.AccountNumber(dgAccounts.Row);
+    SenderAccounts.Add( FAccountsGrid.AccountNumber(dgAccounts.Row) );
     Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     WalletKeys := FWalletKeys;
     WalletKeys := FWalletKeys;
     ShowModal;
     ShowModal;
@@ -917,6 +1138,14 @@ begin
   End;
   End;
 end;
 end;
 
 
+procedure TFRMWallet.MiRemoveaccountfromselectedClick(Sender: TObject);
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  pcAccountsOptions.ActivePage := tsMultiSelectAccounts;
+  sbSelectedAccountsDelClick(Sender);
+end;
+
 procedure TFRMWallet.OnMinerNewBlockFound(sender: TMinerThread; Operations: TPCOperationsComp);
 procedure TFRMWallet.OnMinerNewBlockFound(sender: TMinerThread; Operations: TPCOperationsComp);
 begin
 begin
   MinersBlocksFound := MinersBlocksFound+1;
   MinersBlocksFound := MinersBlocksFound+1;
@@ -1083,7 +1312,7 @@ end;
 
 
 procedure TFRMWallet.OnNewAccount(Sender: TObject);
 procedure TFRMWallet.OnNewAccount(Sender: TObject);
 begin
 begin
-  UpdateAccounts;
+  UpdateAccounts(false);
   UpdateBlockChainState;
   UpdateBlockChainState;
 end;
 end;
 
 
@@ -1149,6 +1378,12 @@ begin
   TNode.Node.PeerCache := s;
   TNode.Node.PeerCache := s;
 end;
 end;
 
 
+procedure TFRMWallet.OnSelectedAccountsGridUpdated(Sender: TObject);
+begin
+  lblSelectedAccountsCount.Caption := Inttostr(FSelectedAccountsGrid.AccountsCount);
+  lblSelectedAccountsBalance.Caption := TAccountComp.FormatMoney( FSelectedAccountsGrid.AccountsBalance );
+end;
+
 procedure TFRMWallet.OnWalletChanged(Sender: TObject);
 procedure TFRMWallet.OnWalletChanged(Sender: TObject);
 begin
 begin
   UpdatePrivateKeys;
   UpdatePrivateKeys;
@@ -1160,7 +1395,11 @@ begin
   if PageControl.ActivePage=tsAccountsExplorer then begin
   if PageControl.ActivePage=tsAccountsExplorer then begin
     FAccountsGrid.Node := FNode;
     FAccountsGrid.Node := FNode;
     MiDecodePayload.Enabled := true;
     MiDecodePayload.Enabled := true;
-  end else FAccountsGrid.Node := Nil;
+    FSelectedAccountsGrid.Node := FNode;
+  end else begin
+    FAccountsGrid.Node := Nil;
+    FSelectedAccountsGrid.Node := Nil;
+  end;
   if PageControl.ActivePage=tsPendingOperations then begin
   if PageControl.ActivePage=tsPendingOperations then begin
     FPendingOperationsGrid.Node := FNode;
     FPendingOperationsGrid.Node := FNode;
     MiDecodePayload.Enabled := true;
     MiDecodePayload.Enabled := true;
@@ -1194,6 +1433,68 @@ begin
   End;
   End;
 end;
 end;
 
 
+procedure TFRMWallet.sbSelectedAccountsAddAllClick(Sender: TObject);
+Var lsource,ltarget : TOrderedCardinalList;
+  i : Integer;
+begin
+  lsource := FAccountsGrid.LockAccountsList;
+  Try
+    ltarget := FSelectedAccountsGrid.LockAccountsList;
+    Try
+      for i := 0 to lsource.Count-1 do begin
+        if FWalletKeys.IndexOfAccountKey(FNode.Bank.SafeBox.Account(lsource.Get(i)).accountkey)<0 then raise Exception.Create(Format('You cannot operate with account %d because private key not found in your wallet',[lsource.Get(i)]));
+        ltarget.Add(lsource.Get(i));
+      end;
+    Finally
+      FSelectedAccountsGrid.UnlockAccountsList;
+    End;
+  Finally
+    FAccountsGrid.UnlockAccountsList;
+  End;
+end;
+
+procedure TFRMWallet.sbSelectedAccountsAddClick(Sender: TObject);
+Var l : TOrderedCardinalList;
+  an : Int64;
+begin
+  an := FAccountsGrid.AccountNumber(dgAccounts.Row);
+  if (an<0) then raise Exception.Create('No account selected');
+  if FWalletKeys.IndexOfAccountKey(FNode.Bank.SafeBox.Account(an).accountkey)<0 then
+    raise Exception.Create(Format('You cannot add %s account because private key not found in your wallet.'#10+#10+'You''re not the owner!',
+      [TAccountComp.AccountNumberToAccountTxtNumber(an)]));
+  // Add
+  l := FSelectedAccountsGrid.LockAccountsList;
+  Try
+    l.Add( an );
+  Finally
+    FSelectedAccountsGrid.UnlockAccountsList;
+  End;
+end;
+
+procedure TFRMWallet.sbSelectedAccountsDelAllClick(Sender: TObject);
+Var l : TOrderedCardinalList;
+begin
+  l := FSelectedAccountsGrid.LockAccountsList;
+  try
+    l.Clear;
+  finally
+    FSelectedAccountsGrid.UnlockAccountsList;
+  end;
+end;
+
+procedure TFRMWallet.sbSelectedAccountsDelClick(Sender: TObject);
+Var an : Int64;
+  l : TOrderedCardinalList;
+begin
+  l := FSelectedAccountsGrid.LockAccountsList;
+  try
+    an := FSelectedAccountsGrid.AccountNumber(dgSelectedAccounts.Row);
+    if an>=0 then l.Remove(an);
+  finally
+    FSelectedAccountsGrid.UnlockAccountsList;
+  end;
+end;
+
 procedure TFRMWallet.SetMinersBlocksFound(const Value: Integer);
 procedure TFRMWallet.SetMinersBlocksFound(const Value: Integer);
 begin
 begin
   FMinersBlocksFound := Value;
   FMinersBlocksFound := Value;
@@ -1224,37 +1525,63 @@ begin
   Application.BringToFront();
   Application.BringToFront();
 end;
 end;
 
 
-procedure TFRMWallet.UpdateAccounts;
+procedure TFRMWallet.UpdateAccounts(RefreshData : Boolean);
 Var accl : TOrderedCardinalList;
 Var accl : TOrderedCardinalList;
   l : TList;
   l : TList;
   i,j,k : Integer;
   i,j,k : Integer;
+  c  : Cardinal;
+  applyfilter : Boolean;
+  acc : TAccount;
 begin
 begin
   If Not Assigned(FOrderedAccountsKeyList) Then exit;
   If Not Assigned(FOrderedAccountsKeyList) Then exit;
-  FAccountsGrid.ShowAllAccounts := Not cbExploreMyAccounts.Checked;
-  if cbExploreMyAccounts.Checked then begin
+  if Not RefreshData then begin
+    dgAccounts.Invalidate;
+    exit;
+  end;
+  applyfilter := (cbFilterAccounts.Checked) and ((FMinAccountBalance>0) Or (FMaxAccountBalance<CT_MaxWalletAmount));
+  FAccountsGrid.ShowAllAccounts := (Not cbExploreMyAccounts.Checked) And (not applyfilter);
+  if Not FAccountsGrid.ShowAllAccounts then begin
     accl := FAccountsGrid.LockAccountsList;
     accl := FAccountsGrid.LockAccountsList;
     Try
     Try
       accl.Clear;
       accl.Clear;
-      if cbMyPrivateKeys.ItemIndex<0 then exit;
-      if cbMyPrivateKeys.ItemIndex=0 then begin
-        // All keys in the wallet
-        for i := 0 to FWalletKeys.Count - 1 do begin
-          j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
-          if (j>=0) then begin
-            l := FOrderedAccountsKeyList.AccountKeyList[j];
-            for k := 0 to l.Count - 1 do begin
-              accl.Add(Cardinal(l[k]));
+      if cbExploreMyAccounts.Checked then begin
+        if cbMyPrivateKeys.ItemIndex<0 then exit;
+        if cbMyPrivateKeys.ItemIndex=0 then begin
+          // All keys in the wallet
+          for i := 0 to FWalletKeys.Count - 1 do begin
+            j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+            if (j>=0) then begin
+              l := FOrderedAccountsKeyList.AccountKeyList[j];
+              for k := 0 to l.Count - 1 do begin
+                if applyfilter then begin
+                  acc := FNode.Bank.SafeBox.Account(Cardinal(l[k]));
+                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                end else accl.Add(Cardinal(l[k]));
+              end;
+            end;
+          end;
+        end else begin
+          i := Integer(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
+          if (i>=0) And (i<FWalletKeys.Count) then begin
+            j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+            if (j>=0) then begin
+              l := FOrderedAccountsKeyList.AccountKeyList[j];
+              for k := 0 to l.Count - 1 do begin
+                if applyfilter then begin
+                  acc := FNode.Bank.SafeBox.Account(Cardinal(l[k]));
+                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                end else accl.Add(Cardinal(l[k]));
+              end;
             end;
             end;
           end;
           end;
         end;
         end;
       end else begin
       end else begin
-        i := cbMyPrivateKeys.ItemIndex-1;
-        j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
-        if (j>=0) then begin
-          l := FOrderedAccountsKeyList.AccountKeyList[j];
-          for k := 0 to l.Count - 1 do begin
-            accl.Add(Cardinal(l[k]));
-          end;
+        // There is a filter... check every account...
+        c := 0;
+        while (c<FNode.Bank.SafeBox.AccountsCount) do begin
+          acc := FNode.Bank.SafeBox.Account(c);
+          if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+          inc(c);
         end;
         end;
       end;
       end;
     Finally
     Finally
@@ -1264,6 +1591,7 @@ begin
   end else begin
   end else begin
     lblAccountsCount.Caption := inttostr(FNode.Bank.AccountsCount);
     lblAccountsCount.Caption := inttostr(FNode.Bank.AccountsCount);
   end;
   end;
+  bbChangeKeyName.Enabled := cbExploreMyAccounts.Checked;
   // Show Totals:
   // Show Totals:
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   UpdateOperations;
   UpdateOperations;
@@ -1465,11 +1793,12 @@ begin
   If (Not Assigned(FOrderedAccountsKeyList)) And (Assigned(FNode)) Then begin
   If (Not Assigned(FOrderedAccountsKeyList)) And (Assigned(FNode)) Then begin
     FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(FNode.Bank.SafeBox,false);
     FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(FNode.Bank.SafeBox,false);
   end;
   end;
-  last_i := cbMyPrivateKeys.ItemIndex;
+  if (cbMyPrivateKeys.ItemIndex>=0) then last_i := Integer(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex])
+  else last_i := -1;
   cbMyPrivateKeys.items.BeginUpdate;
   cbMyPrivateKeys.items.BeginUpdate;
   Try
   Try
     cbMyPrivateKeys.Items.Clear;
     cbMyPrivateKeys.Items.Clear;
-    cbMyPrivateKeys.Items.AddObject('(All my private keys)',TObject(-1));
+//    cbMyPrivateKeys.Items.AddObject('(All my private keys)',TObject(-1));
     For i:=0 to FWalletKeys.Count-1 do begin
     For i:=0 to FWalletKeys.Count-1 do begin
       wk := FWalletKeys.Key[i];
       wk := FWalletKeys.Key[i];
       if assigned(FOrderedAccountsKeyList) then begin
       if assigned(FOrderedAccountsKeyList) then begin
@@ -1483,9 +1812,13 @@ begin
       if Not Assigned(wk.PrivateKey) then s := s + '(*)';
       if Not Assigned(wk.PrivateKey) then s := s + '(*)';
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
     end;
     end;
+    cbMyPrivateKeys.Sorted := true;
+    cbMyPrivateKeys.Sorted := false;
+    cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));
   Finally
   Finally
     cbMyPrivateKeys.Items.EndUpdate;
     cbMyPrivateKeys.Items.EndUpdate;
   End;
   End;
+  last_i := cbMyPrivateKeys.Items.IndexOfObject(TObject(last_i));
   if last_i<0 then last_i := 0;
   if last_i<0 then last_i := 0;
   if cbMyPrivateKeys.Items.Count>last_i then cbMyPrivateKeys.ItemIndex := last_i
   if cbMyPrivateKeys.Items.Count>last_i then cbMyPrivateKeys.ItemIndex := last_i
   else if cbMyPrivateKeys.Items.Count>=0 then cbMyPrivateKeys.ItemIndex := 0;
   else if cbMyPrivateKeys.Items.Count>=0 then cbMyPrivateKeys.ItemIndex := 0;

+ 134 - 2
Units/PascalCoin/UAccounts.pas

@@ -180,6 +180,28 @@ Type
     class Function ReadAnsiString(Stream: TStream; var value: AnsiString): Integer;
     class Function ReadAnsiString(Stream: TStream; var value: AnsiString): Integer;
   End;
   End;
 
 
+  TOrderedCardinalList = Class
+  private
+    FOrderedList : TList;
+    FDisabledsCount : Integer;
+    FModifiedWhileDisabled : Boolean;
+    FOnListChanged: TNotifyEvent;
+    Procedure NotifyChanged;
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    Function Add(Value : Cardinal) : Integer;
+    Procedure Remove(Value : Cardinal);
+    Procedure Clear;
+    Function Get(index : Integer) : Cardinal;
+    Function Count : Integer;
+    Function Find(const Value: Cardinal; var Index: Integer): Boolean;
+    Procedure Disable;
+    Procedure Enable;
+    Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
+    Procedure CopyFrom(Sender : TOrderedCardinalList);
+  End;
+
 Const
 Const
   CT_Account_NUL : TAccount = (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0);
   CT_Account_NUL : TAccount = (account:0;accountkey:(EC_OpenSSL_NID:0;x:'';y:'');balance:0;updated_block:0;n_operation:0);
   CT_BlockAccount_NUL : TBlockAccount = (
   CT_BlockAccount_NUL : TBlockAccount = (
@@ -452,7 +474,7 @@ end;
 
 
 class function TAccountComp.FormatMoney(Money: Int64): AnsiString;
 class function TAccountComp.FormatMoney(Money: Int64): AnsiString;
 begin
 begin
-  Result := FormatFloat('0.0000',(Money/10000));
+  Result := FormatFloat('#,###0.0000',(Money/10000));
 end;
 end;
 
 
 class function TAccountComp.GetECInfoTxt(const EC_OpenSSL_NID: Word): AnsiString;
 class function TAccountComp.GetECInfoTxt(const EC_OpenSSL_NID: Word): AnsiString;
@@ -530,7 +552,7 @@ begin
     exit;
     exit;
   end;
   end;
   try
   try
-    s := StringReplace(moneytxt,ThousandSeparator,DecimalSeparator,[rfReplaceAll]);
+    s := StringReplace(moneytxt,ThousandSeparator,'',[rfReplaceAll]);
     money := Round( StrToFloat(s)*10000 );
     money := Round( StrToFloat(s)*10000 );
     Result := true;
     Result := true;
   Except
   Except
@@ -1321,4 +1343,114 @@ begin
   Dispose(P);
   Dispose(P);
 end;
 end;
 
 
+{ TOrderedCardinalList }
+
+function TOrderedCardinalList.Add(Value: Cardinal): Integer;
+begin
+  if Find(Value,Result) then exit
+  else begin
+    FOrderedList.Insert(Result,TObject(Value));
+    NotifyChanged;
+  end;
+end;
+
+procedure TOrderedCardinalList.Clear;
+begin
+  FOrderedList.Clear;
+  NotifyChanged;
+end;
+
+procedure TOrderedCardinalList.CopyFrom(Sender: TOrderedCardinalList);
+Var i : Integer;
+begin
+  if Self=Sender then exit;
+  Disable;
+  Try
+    Clear;
+    for I := 0 to Sender.Count - 1 do begin
+      Add(Sender.Get(i));
+    end;
+  Finally
+    Enable;
+  End;
+end;
+
+function TOrderedCardinalList.Count: Integer;
+begin
+  Result := FOrderedList.Count;
+end;
+
+constructor TOrderedCardinalList.Create;
+begin
+  FOrderedList := TList.Create;
+  FDisabledsCount := 0;
+  FModifiedWhileDisabled := false;
+end;
+
+destructor TOrderedCardinalList.Destroy;
+begin
+  FOrderedList.Free;
+  inherited;
+end;
+
+procedure TOrderedCardinalList.Disable;
+begin
+  inc(FDisabledsCount);
+end;
+
+procedure TOrderedCardinalList.Enable;
+begin
+  if FDisabledsCount<=0 then raise Exception.Create('Dev error. Invalid disabled counter');
+  dec(FDisabledsCount);
+  if (FDisabledsCount=0) And (FModifiedWhileDisabled) then NotifyChanged;
+end;
+
+function TOrderedCardinalList.Find(const Value: Cardinal; var Index: Integer): Boolean;
+var L, H, I: Integer;
+  C : Int64;
+begin
+  Result := False;
+  L := 0;
+  H := FOrderedList.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := Int64(FOrderedList[I]) - Int64(Value);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function TOrderedCardinalList.Get(index: Integer): Cardinal;
+begin
+  Result := Cardinal(FOrderedList[index]);
+end;
+
+procedure TOrderedCardinalList.NotifyChanged;
+begin
+  if FDisabledsCount>0 then begin
+    FModifiedWhileDisabled := true;
+    exit;
+  end;
+  FModifiedWhileDisabled := false;
+  if Assigned(FOnListChanged) then FOnListChanged(Self);
+end;
+
+procedure TOrderedCardinalList.Remove(Value: Cardinal);
+Var i : Integer;
+begin
+  if Find(Value,i) then begin
+    FOrderedList.Delete(i);
+    NotifyChanged;
+  end;
+end;
+
 end.
 end.

+ 1 - 1
Units/PascalCoin/UBlockChain.pas

@@ -565,7 +565,7 @@ begin
       finally
       finally
         Operations.Free;
         Operations.Free;
       end;
       end;
-      NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan);
+      NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
     finally
     finally
       FIsRestoringFromFile := False;
       FIsRestoringFromFile := False;
     end;
     end;

+ 1 - 1
Units/PascalCoin/UConst.pas

@@ -78,7 +78,7 @@ Const
   CT_Op_Changekey = $02;
   CT_Op_Changekey = $02;
   CT_Op_Recover = $03;
   CT_Op_Recover = $03;
 
 
-  CT_ClientAppVersion : AnsiString = '1.0.4';
+  CT_ClientAppVersion : AnsiString = '1.0.5';
 
 
   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 =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.ddns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
 
 

+ 2 - 2
Units/PascalCoin/UDBStorage.pas

@@ -700,7 +700,7 @@ begin
   sql := 'SELECT '+sqltop+' * '+
   sql := 'SELECT '+sqltop+' * '+
     ' FROM '+CT_TblName_Operations+
     ' FROM '+CT_TblName_Operations+
     ' WHERE ('+CT_TblFld_Operations_account+'='+inttostr(account_number)+') AND '+whereorphan+
     ' WHERE ('+CT_TblFld_Operations_account+'='+inttostr(account_number)+') AND '+whereorphan+
-    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_optype+' DESC';
+    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_nopblock+' DESC, '+CT_TblFld_Operations_optype_op+' DESC';
   ms := TMemoryStream.Create;
   ms := TMemoryStream.Create;
     ds := TADOQuery.Create(Self);
     ds := TADOQuery.Create(Self);
     try
     try
@@ -777,7 +777,7 @@ begin
   Result := 'SELECT '+sqltop+' * '+
   Result := 'SELECT '+sqltop+' * '+
     ' FROM '+CT_TblName_Operations+
     ' FROM '+CT_TblName_Operations+
     ' '+where+
     ' '+where+
-    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_optype+' DESC';
+    ' ORDER BY '+CT_TblFld_Operations_block+' DESC, '+CT_TblFld_Operations_nopblock+' DESC, '+CT_TblFld_Operations_optype_op+' DESC';
 end;
 end;
 
 
 class function TDBStorage.OperationToOperationResume(Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;
 class function TDBStorage.OperationToOperationResume(Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean;

+ 40 - 16
Units/PascalCoin/UNetProtocol.pas

@@ -240,6 +240,7 @@ Type
     FNetProtocolVersion: TNetProtocolVersion;
     FNetProtocolVersion: TNetProtocolVersion;
     FAlertedForNewProtocolAvailable : Boolean;
     FAlertedForNewProtocolAvailable : Boolean;
     FHasReceivedData : Boolean;
     FHasReceivedData : Boolean;
+    FIsDownloadingBlocks : Boolean;
     function GetConnected: Boolean;
     function GetConnected: Boolean;
     procedure SetConnected(const Value: Boolean);
     procedure SetConnected(const Value: Boolean);
     procedure TcpClient_OnError(Sender: TObject; SocketError: Integer);
     procedure TcpClient_OnError(Sender: TObject; SocketError: Integer);
@@ -997,7 +998,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
 var rid : Cardinal;
 var rid : Cardinal;
   bufferdata : TMemoryStream;
   bufferdata : TMemoryStream;
   headerdata : TNetHeaderData;
   headerdata : TNetHeaderData;
-  op : TOperationBlock;
+  my_op, client_op : TOperationBlock;
 begin
 begin
   // Protection against discovering servers...
   // Protection against discovering servers...
   if FIsDiscoveringServers then begin
   if FIsDiscoveringServers then begin
@@ -1021,25 +1022,27 @@ begin
       ' with OperationBlock:'+TPCOperationsComp.OperationBlockToText(Connection.FRemoteOperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')');
       ' with OperationBlock:'+TPCOperationsComp.OperationBlockToText(Connection.FRemoteOperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')');
     // NOTE: FRemoteOperationBlock.block >= TNode.Node.Bank.BlocksCount
     // NOTE: FRemoteOperationBlock.block >= TNode.Node.Bank.BlocksCount
     // First capture same block than me (TNode.Node.Bank.BlocksCount-1) to check if i'm an orphan block...
     // First capture same block than me (TNode.Node.Bank.BlocksCount-1) to check if i'm an orphan block...
-    If Not Do_GetOperationBlock(TNode.Node.Bank.BlocksCount-1,op) then begin
-      TLog.NewLog(lterror,CT_LogSender,'Cannot receive my block ('+inttostr(TNode.Node.Bank.BlocksCount)+')... Invalid client. Disconnecting');
-      Connection.DisconnectInvalidClient(false,'Cannot receive my block ('+inttostr(TNode.Node.Bank.BlocksCount)+')... Invalid client. Disconnecting');
+    my_op := TNode.Node.Bank.LastOperationBlock;
+    If Not Do_GetOperationBlock(my_op.block,client_op) then begin
+      TLog.NewLog(lterror,CT_LogSender,'Cannot receive information about my block ('+inttostr(my_op.block)+')... Invalid client. Disconnecting');
+      Connection.DisconnectInvalidClient(false,'Cannot receive information about my block ('+inttostr(my_op.block)+')... Invalid client. Disconnecting');
       exit;
       exit;
     end;
     end;
-    if op.proof_of_work<>TNode.Node.Bank.LastOperationBlock.proof_of_work then begin
-      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is incorrect... received: '+TPCOperationsComp.OperationBlockToText(op)+' My: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock));
-      if Not FindLastSameBlockByOperationsBlock(0,op.block,op) then begin
+
+    if (NOT TPCOperationsComp.EqualsOperationBlock(my_op,client_op)) then begin
+      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is incorrect... received: '+TPCOperationsComp.OperationBlockToText(client_op)+' My: '+TPCOperationsComp.OperationBlockToText(my_op));
+      if Not FindLastSameBlockByOperationsBlock(0,client_op.block,client_op) then begin
         TLog.NewLog(ltinfo,CT_LogSender,'No found base block to start process... Receiving ALL');
         TLog.NewLog(ltinfo,CT_LogSender,'No found base block to start process... Receiving ALL');
         GetNewBank(-1);
         GetNewBank(-1);
       end else begin
       end else begin
-        TLog.NewLog(ltinfo,CT_LogSender,'Found base new block: '+TPCOperationsComp.OperationBlockToText(op));
+        TLog.NewLog(ltinfo,CT_LogSender,'Found base new block: '+TPCOperationsComp.OperationBlockToText(client_op));
         // Move operations to orphan folder... (temporal... waiting for a confirmation)
         // Move operations to orphan folder... (temporal... waiting for a confirmation)
-        GetNewBank(op.block);
+        GetNewBank(client_op.block);
       end;
       end;
     end else begin
     end else begin
-      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is ok! Need to download new blocks starting at '+inttostr(TNode.Node.Bank.BlocksCount));
+      TLog.NewLog(ltinfo,CT_LogSender,'My blockchain is ok! Need to download new blocks starting at '+inttostr(my_op.block+1));
       // High to new value:
       // High to new value:
-      Connection.Send_GetBlocks(TNode.Node.Bank.BlocksCount,0,rid);
+      Connection.Send_GetBlocks(my_op.block+1,0,rid);
     end;
     end;
   Finally
   Finally
     TLog.NewLog(ltdebug,CT_LogSender,'Finalizing');
     TLog.NewLog(ltdebug,CT_LogSender,'Finalizing');
@@ -1470,6 +1473,7 @@ end;
 constructor TNetConnection.Create(AOwner: TComponent);
 constructor TNetConnection.Create(AOwner: TComponent);
 begin
 begin
   inherited;
   inherited;
+  FIsDownloadingBlocks := false;
   FHasReceivedData := false;
   FHasReceivedData := false;
   FNetProtocolVersion.protocol_version := 0; // 0 = unknown
   FNetProtocolVersion.protocol_version := 0; // 0 = unknown
   FNetProtocolVersion.protocol_available := 0;
   FNetProtocolVersion.protocol_available := 0;
@@ -1531,6 +1535,7 @@ Var P : PNodeServerAddress;
   i : Integer;
   i : Integer;
   include_in_list : Boolean;
   include_in_list : Boolean;
 begin
 begin
+  FIsDownloadingBlocks := false;
   if ItsMyself then begin
   if ItsMyself then begin
     TLog.NewLog(ltInfo,Classname,'Disconecting myself '+ClientRemoteAddr+' > '+Why)
     TLog.NewLog(ltInfo,Classname,'Disconecting myself '+ClientRemoteAddr+' > '+Why)
   end else begin
   end else begin
@@ -1687,6 +1692,7 @@ Var b,b_start,b_end:Cardinal;
     c : Cardinal;
     c : Cardinal;
   errors : AnsiString;
   errors : AnsiString;
   DoDisconnect : Boolean;
   DoDisconnect : Boolean;
+  posquantity : Int64;
 begin
 begin
   DoDisconnect := true;
   DoDisconnect := true;
   try
   try
@@ -1714,14 +1720,23 @@ begin
        op := TPCOperationsComp.Create(TNetData.NetData.bank);
        op := TPCOperationsComp.Create(TNetData.NetData.bank);
        try
        try
          c := b_end - b_start + 1;
          c := b_end - b_start + 1;
+         posquantity := db.position;
          db.Write(c,4);
          db.Write(c,4);
+         c := 0;
          for b := b_start to b_end do begin
          for b := b_start to b_end do begin
+           inc(c);
            If TNetData.NetData.bank.LoadOperations(op,b) then begin
            If TNetData.NetData.bank.LoadOperations(op,b) then begin
              op.SaveBlockToStream(false,db);
              op.SaveBlockToStream(false,db);
            end else begin
            end else begin
              SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,'Operations of block:'+inttostr(b)+' not found');
              SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,'Operations of block:'+inttostr(b)+' not found');
              exit;
              exit;
            end;
            end;
+           // Build 1.0.5 To prevent high data over net in response (Max 2 Mb of data)
+           if (db.size>(1024*1024*2)) then begin
+             // Stop
+             db.position := posquantity;
+             db.Write(c,4);
+           end;
          end;
          end;
          Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
          Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
        finally
        finally
@@ -1785,10 +1800,12 @@ begin
           end;
           end;
         end else begin
         end else begin
           // Receiving an unexpected operationblock
           // Receiving an unexpected operationblock
-          TLog.NewLog(lterror,classname,'ReceivedGetOperations an unexpected operationblock: '+TPCOperationsComp.OperationBlockToText(op.OperationBlock));
+          TLog.NewLog(lterror,classname,'Received a distinct block, finalizing: '+TPCOperationsComp.OperationBlockToText(op.OperationBlock));
+          FIsDownloadingBlocks := false;
           exit;
           exit;
         end;
         end;
       end;
       end;
+      FIsDownloadingBlocks := false;
       if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
       if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
         Send_GetBlocks(TNode.Node.Bank.BlocksCount,50,i);
         Send_GetBlocks(TNode.Node.Bank.BlocksCount,50,i);
       end;
       end;
@@ -2441,12 +2458,14 @@ begin
       if FRemoteOperationBlock.block>0 then c2 := FRemoteOperationBlock.block
       if FRemoteOperationBlock.block>0 then c2 := FRemoteOperationBlock.block
       else c2 := c1+100;
       else c2 := c1+100;
     end else c2 := c1+quantity-1;
     end else c2 := c1+quantity-1;
-    if (FRemoteOperationBlock.block>0) And (c2>FRemoteOperationBlock.block) then c2 := FRemoteOperationBlock.block;
+    // Build 1.0.5 to allow download all:
+    // Deleted: if (FRemoteOperationBlock.block>0) And (c2>FRemoteOperationBlock.block) then c2 := FRemoteOperationBlock.block;
     data.Write(c1,4);
     data.Write(c1,4);
     data.Write(c2,4);
     data.Write(c2,4);
     request_id := TNetData.NetData.NewRequestId;
     request_id := TNetData.NetData.NewRequestId;
     TNetData.NetData.RegisterRequest(Self,CT_NetOp_GetBlocks,request_id);
     TNetData.NetData.RegisterRequest(Self,CT_NetOp_GetBlocks,request_id);
     TLog.NewLog(ltdebug,ClassName,Format('Send GET BLOCKS start:%d quantity:%d (from:%d to %d)',[StartAddress,quantity,StartAddress,quantity+StartAddress]));
     TLog.NewLog(ltdebug,ClassName,Format('Send GET BLOCKS start:%d quantity:%d (from:%d to %d)',[StartAddress,quantity,StartAddress,quantity+StartAddress]));
+    FIsDownloadingBlocks := quantity>1;
     Send(ntp_request,CT_NetOp_GetBlocks,0,request_id,data);
     Send(ntp_request,CT_NetOp_GetBlocks,0,request_id,data);
     Result := Connected;
     Result := Connected;
   finally
   finally
@@ -2832,6 +2851,7 @@ Var i : Integer;
   candidates : TList;
   candidates : TList;
   lop : TOperationBlock;
   lop : TOperationBlock;
   netConnectionsList : TList;
   netConnectionsList : TList;
+  nc : TNetConnection;
 begin
 begin
   // Search better candidates:
   // Search better candidates:
   candidates := TList.Create;
   candidates := TList.Create;
@@ -2841,12 +2861,16 @@ begin
     Try
     Try
       for i := 0 to netConnectionsList.Count - 1 do begin
       for i := 0 to netConnectionsList.Count - 1 do begin
         TNetData.NetData.FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
         TNetData.NetData.FMaxRemoteOperationBlock := CT_OperationBlock_NUL;
-        if (TNetConnection(netConnectionsList[i]).FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount) And
-           (TNetConnection(netConnectionsList[i]).FRemoteOperationBlock.block>=lop.block)
+        nc := TNetConnection(netConnectionsList[i]);
+        if (nc.FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount) And
+           (nc.FRemoteOperationBlock.block>=lop.block)
            then begin
            then begin
-           candidates.Add(TNetConnection(netConnectionsList[i]));
+           candidates.Add(nc);
            lop := TNetConnection(netConnectionsList[i]).FRemoteOperationBlock;
            lop := TNetConnection(netConnectionsList[i]).FRemoteOperationBlock;
         end;
         end;
+        //
+        if nc.FIsDownloadingBlocks then exit;
+        //
       end;
       end;
     Finally
     Finally
       TNetData.NetData.ConnectionsUnlock;
       TNetData.NetData.ConnectionsUnlock;

+ 3 - 14
Units/PascalCoin/UNode.pas

@@ -294,13 +294,15 @@ begin
     valids_operations := TOperationsHashTree.Create;
     valids_operations := TOperationsHashTree.Create;
     try
     try
       for j := 0 to Operations.OperationsCount-1 do begin
       for j := 0 to Operations.OperationsCount-1 do begin
-        TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),Operations.OperationsCount,Operations.GetOperation(j).ToString]));
         if (FOperations.AddOperation(true,Operations.GetOperation(j),e)) then begin
         if (FOperations.AddOperation(true,Operations.GetOperation(j),e)) then begin
           inc(Result);
           inc(Result);
           valids_operations.AddOperationToHashTree(Operations.GetOperation(j));
           valids_operations.AddOperationToHashTree(Operations.GetOperation(j));
+          TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),Operations.OperationsCount,Operations.GetOperation(j).ToString]));
         end else begin
         end else begin
           if (errors<>'') then errors := errors+' ';
           if (errors<>'') then errors := errors+' ';
           errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(Operations.OperationsCount)+':'+e;
           errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(Operations.OperationsCount)+':'+e;
+          TLog.NewLog(ltdebug,Classname,Format('AddOperation failed %d/%d: %s  - Error:%s',
+            [(j+1),Operations.OperationsCount,Operations.GetOperation(j).ToString,e]));
         end;
         end;
       end;
       end;
       if Result=0 then exit;
       if Result=0 then exit;
@@ -806,19 +808,6 @@ begin
       TNetData.NetData.ConnectionUnlock(FNetConnection);
       TNetData.NetData.ConnectionUnlock(FNetConnection);
     end;
     end;
   end;
   end;
-  {
-  If Not TNetData.NetData.ConnectionExistsAndActive(FNetConnection) then begin
-    TLog.NewLog(ltdebug,Classname,'Connection not active');
-    exit;
-  end;
-  FNetConnection.WhenPossible_Send_NewBlockFound;
-  TLog.NewLog(ltdebug,ClassName,'Sending new block found to '+FNetConnection.Client.RemoteHost+':'+FNetConnection.Client.RemotePort);
-  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);
-     FNetConnection.Send_AddOperations(TNode.Node.Operations.OperationsHashTree);
-  end;
-  }
 end;
 end;
 
 
 constructor TThreadNodeNotifyNewBlock.Create(NetConnection: TNetConnection);
 constructor TThreadNodeNotifyNewBlock.Create(NetConnection: TNetConnection);

+ 2 - 2
Units/PascalCoin/UThread.pas

@@ -77,7 +77,7 @@ begin
   FDebugStep := '';
   FDebugStep := '';
   _threads.Add(Self);
   _threads.Add(Self);
   try
   try
-//    TLog.NewLog(ltdebug,Classname,'Starting Thread');
+    TLog.NewLog(ltdebug,Classname,'Starting Thread');
     Try
     Try
       Try
       Try
         BCExecute;
         BCExecute;
@@ -88,7 +88,7 @@ begin
         end;
         end;
       End;
       End;
     Finally
     Finally
-//      TLog.NewLog(ltdebug,Classname,'Finalizing Thread');
+      TLog.NewLog(ltdebug,Classname,'Finalizing Thread');
     End;
     End;
   finally
   finally
     if (Assigned(_threads)) then begin
     if (Assigned(_threads)) then begin

+ 15 - 85
Units/Utils/UGridUtils.pas

@@ -28,20 +28,6 @@ Type
     width : Integer;
     width : Integer;
   End;
   End;
 
 
-  TOrderedCardinalList = Class
-  private
-    FOrderedList : TList;
-  public
-    Constructor Create;
-    Destructor Destroy; override;
-    Function Add(Value : Cardinal) : Integer;
-    Procedure Remove(Value : Cardinal);
-    Procedure Clear;
-    Function Get(index : Integer) : Cardinal;
-    Function Count : Integer;
-    Function Find(const Value: Cardinal; var Index: Integer): Boolean;
-  End;
-
   TAccountsGrid = Class(TComponent)
   TAccountsGrid = Class(TComponent)
   private
   private
     FAccountsBalance : Int64;
     FAccountsBalance : Int64;
@@ -50,6 +36,8 @@ Type
     FDrawGrid : TDrawGrid;
     FDrawGrid : TDrawGrid;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FShowAllAccounts: Boolean;
     FShowAllAccounts: Boolean;
+    FOnUpdated: TNotifyEvent;
+    FAccountsCount: Integer;
     procedure SetDrawGrid(const Value: TDrawGrid);
     procedure SetDrawGrid(const Value: TDrawGrid);
     Procedure InitGrid;
     Procedure InitGrid;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewOperation(Sender : TObject);
@@ -71,7 +59,9 @@ Type
     Procedure LoadFromStream(Stream : TStream);
     Procedure LoadFromStream(Stream : TStream);
     Property ShowAllAccounts : Boolean read FShowAllAccounts write SetShowAllAccounts;
     Property ShowAllAccounts : Boolean read FShowAllAccounts write SetShowAllAccounts;
     Property AccountsBalance : Int64 read FAccountsBalance;
     Property AccountsBalance : Int64 read FAccountsBalance;
+    Property AccountsCount : Integer read FAccountsCount;
     Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
     Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
+    Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
   End;
   End;
 
 
   TOperationsGrid = Class(TComponent)
   TOperationsGrid = Class(TComponent)
@@ -223,7 +213,9 @@ constructor TAccountsGrid.Create(AOwner: TComponent);
 Var i : Integer;
 Var i : Integer;
 begin
 begin
   inherited;
   inherited;
+  FOnUpdated := Nil;
   FAccountsBalance := 0;
   FAccountsBalance := 0;
+  FAccountsCount := 0;
   FShowAllAccounts := false;
   FShowAllAccounts := false;
   FAccountsList := TOrderedCardinalList.Create;
   FAccountsList := TOrderedCardinalList.Create;
   FDrawGrid := Nil;
   FDrawGrid := Nil;
@@ -257,6 +249,7 @@ Var i : Integer;
   acc : TAccount;
   acc : TAccount;
 begin
 begin
   FAccountsBalance := 0;
   FAccountsBalance := 0;
+  FAccountsCount := FAccountsList.Count;
   if Not assigned(DrawGrid) then exit;
   if Not assigned(DrawGrid) then exit;
   if FShowAllAccounts then begin
   if FShowAllAccounts then begin
     if Assigned(Node) then begin
     if Assigned(Node) then begin
@@ -287,6 +280,7 @@ begin
     {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
     {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
     goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
     goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
   FDrawGrid.Invalidate;
   FDrawGrid.Invalidate;
+  if Assigned(FOnUpdated) then FOnUpdated(Self);
 end;
 end;
 
 
 procedure TAccountsGrid.LoadFromStream(Stream: TStream);
 procedure TAccountsGrid.LoadFromStream(Stream: TStream);
@@ -500,6 +494,7 @@ end;
 
 
 procedure TAccountsGrid.SetNode(const Value: TNode);
 procedure TAccountsGrid.SetNode(const Value: TNode);
 begin
 begin
+  if GetNode=Value then exit;
   FNodeNotifyEvents.Node := Value;
   FNodeNotifyEvents.Node := Value;
   InitGrid;
   InitGrid;
 end;
 end;
@@ -516,72 +511,6 @@ begin
   InitGrid;
   InitGrid;
 end;
 end;
 
 
-{ TOrderedCardinalList }
-
-function TOrderedCardinalList.Add(Value: Cardinal): Integer;
-begin
-  if Find(Value,Result) then exit
-  else begin
-    FOrderedList.Insert(Result,TObject(Value));
-  end;
-end;
-
-procedure TOrderedCardinalList.Clear;
-begin
-  FOrderedList.Clear;
-end;
-
-function TOrderedCardinalList.Count: Integer;
-begin
-  Result := FOrderedList.Count;
-end;
-
-constructor TOrderedCardinalList.Create;
-begin
-  FOrderedList := TList.Create;
-end;
-
-destructor TOrderedCardinalList.Destroy;
-begin
-  FOrderedList.Free;
-  inherited;
-end;
-
-function TOrderedCardinalList.Find(const Value: Cardinal; var Index: Integer): Boolean;
-var L, H, I: Integer;
-  C : Int64;
-begin
-  Result := False;
-  L := 0;
-  H := FOrderedList.Count - 1;
-  while L <= H do
-  begin
-    I := (L + H) shr 1;
-    C := Int64(FOrderedList[I]) - Int64(Value);
-    if C < 0 then L := I + 1 else
-    begin
-      H := I - 1;
-      if C = 0 then
-      begin
-        Result := True;
-        L := I;
-      end;
-    end;
-  end;
-  Index := L;
-end;
-
-function TOrderedCardinalList.Get(index: Integer): Cardinal;
-begin
-  Result := Cardinal(FOrderedList[index]);
-end;
-
-procedure TOrderedCardinalList.Remove(Value: Cardinal);
-Var i : Integer;
-begin
-  if Find(Value,i) then FOrderedList.Delete(i);
-end;
-
 { TOperationsGrid }
 { TOperationsGrid }
 
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
 constructor TOperationsGrid.Create(AOwner: TComponent);
@@ -767,6 +696,7 @@ end;
 
 
 procedure TOperationsGrid.SetNode(const Value: TNode);
 procedure TOperationsGrid.SetNode(const Value: TNode);
 begin
 begin
+  if GetNode=Value then exit;
   FNodeNotifyEvents.Node := Value;
   FNodeNotifyEvents.Node := Value;
   UpdateAccountOperations; // New Build 1.0.3
   UpdateAccountOperations; // New Build 1.0.3
 end;
 end;
@@ -1529,15 +1459,15 @@ begin
     FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
     FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
     FDBGrid.DefaultDrawing := false;
     FDBGrid.DefaultDrawing := false;
     FDBGrid.Columns.Clear;
     FDBGrid.Columns.Clear;
-    AddColumn(CT_TblFld_BlockChain_block,'Block',50);
+    AddColumn(CT_TblFld_BlockChain_block,'Block',60);
     AddColumn('datetime','Date/Time',120);
     AddColumn('datetime','Date/Time',120);
-    AddColumn(CT_TblFld_BlockChain_operations_count,'Ops.',50);
+    AddColumn(CT_TblFld_BlockChain_operations_count,'Ops.',30);
     AddColumn('reward','Reward',80);
     AddColumn('reward','Reward',80);
     AddColumn('fee','Fee',60);
     AddColumn('fee','Fee',60);
-    AddColumn('target','Target',65);
-    AddColumn(CT_TblFld_BlockChain_safe_box_hash,'Safe Box Hash',350);
+    AddColumn('target','Target',60);
     AddColumn('payload_decoded','Miner Payload',150);
     AddColumn('payload_decoded','Miner Payload',150);
-    AddColumn(CT_TblFld_BlockChain_proof_of_work,'Proof of Work',350);
+    AddColumn(CT_TblFld_BlockChain_proof_of_work,'Proof of Work',400);
+    AddColumn(CT_TblFld_BlockChain_safe_box_hash,'Safe Box Hash',400);
     RefreshData;
     RefreshData;
   end;
   end;
 end;
 end;