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:
 
+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
 --------------------------
 - 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:
 
+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
 --------------------------
 - IMPORTANT: Introducing net protocol changes: Must update!

+ 56 - 29
Units/Forms/UFRMOperation.dfm

@@ -5,7 +5,7 @@ object FRMOperation: TFRMOperation
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
   Caption = 'New Operation'
-  ClientHeight = 443
+  ClientHeight = 474
   ClientWidth = 608
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
@@ -19,15 +19,15 @@ object FRMOperation: TFRMOperation
   OnDestroy = FormDestroy
   PixelsPerInch = 96
   TextHeight = 13
-  object Label1: TLabel
-    Left = 20
+  object lblAccountCaption: TLabel
+    Left = 25
     Top = 20
     Width = 43
     Height = 13
     Caption = 'Account:'
   end
   object lblAccountBalance: TLabel
-    Left = 225
+    Left = 432
     Top = 15
     Width = 149
     Height = 19
@@ -40,15 +40,22 @@ object FRMOperation: TFRMOperation
     ParentFont = False
   end
   object Label2: TLabel
-    Left = 180
+    Left = 387
     Top = 20
     Width = 41
     Height = 13
     Caption = 'Balance:'
   end
+  object lblAccountsCount: TLabel
+    Left = 387
+    Top = 58
+    Width = 18
+    Height = 13
+    Caption = 'XXX'
+  end
   object bbExecute: TBitBtn
-    Left = 295
-    Top = 386
+    Left = 300
+    Top = 411
     Width = 130
     Height = 31
     Caption = 'Execute (F12)'
@@ -76,8 +83,8 @@ object FRMOperation: TFRMOperation
     OnClick = actExecuteExecute
   end
   object bbCancel: TBitBtn
-    Left = 460
-    Top = 386
+    Left = 465
+    Top = 411
     Width = 116
     Height = 31
     DoubleBuffered = True
@@ -85,25 +92,9 @@ object FRMOperation: TFRMOperation
     ParentDoubleBuffered = False
     TabOrder = 3
   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
-    Left = 20
-    Top = 50
+    Left = 25
+    Top = 75
     Width = 556
     Height = 321
     ActivePage = tsOperation
@@ -536,9 +527,45 @@ object FRMOperation: TFRMOperation
       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
-    Left = 135
-    Top = 325
+    Left = 140
+    Top = 350
     object actExecute: TAction
       Caption = 'Execute (F12)'
       ShortCut = 123

+ 239 - 126
Units/Forms/UFRMOperation.pas

@@ -22,7 +22,7 @@ uses
 
 type
   TFRMOperation = class(TForm)
-    Label1: TLabel;
+    lblAccountCaption: TLabel;
     bbExecute: TBitBtn;
     bbCancel: TBitBtn;
     lblAccountBalance: TLabel;
@@ -64,6 +64,8 @@ type
     tsGlobalError: TTabSheet;
     lblGlobalErrors: TLabel;
     bbPassword: TBitBtn;
+    memoAccounts: TMemo;
+    lblAccountsCount: TLabel;
     procedure FormCreate(Sender: TObject);
     procedure rbTransactionClick(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -80,11 +82,8 @@ type
     procedure bbPasswordClick(Sender: TObject);
     procedure ebDestAccountExit(Sender: TObject);
   private
-    FSenderAccount: Cardinal;
     FNode : TNode;
     FWalletKeys: TWalletKeys;
-    FPAccount : PAccount;
-    FOperation : TPCOperation;
     FFee: Int64;
     FEncodedPayload : TRawBytes;
     FDisabled : Boolean;
@@ -92,16 +91,17 @@ type
     FTxDestAccount : Cardinal;
     FTxAmount : Int64;
     FNewAccountPublicKey : TAccountKey;
-    procedure SetSenderAccount(const Value: Cardinal);
+    FSenderAccounts: TOrderedCardinalList;
     procedure SetWalletKeys(const Value: TWalletKeys);
     { Private declarations }
+    Procedure UpdateAccountsInfo;
     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 OnSenderAccountsChanged(Sender : TObject);
   public
     { Public declarations }
-    Property SenderAccount : Cardinal read FSenderAccount write SetSenderAccount;
+    Property SenderAccounts : TOrderedCardinalList read FSenderAccounts;
     Property WalletKeys : TWalletKeys read FWalletKeys write SetWalletKeys;
     Property Fee : Int64 read FFee write SetFee;
   end;
@@ -118,62 +118,115 @@ uses
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 Var errors : AnsiString;
   P : PAccount;
-  i : Integer;
+  i,iAcc : Integer;
   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
-  P := GetSenderAcccount;
-  if Not Assigned(P) then raise Exception.Create('Invalid sender account');
   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 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
-          raise Exception.Create('No new key generated');
+          _amount := FTxAmount;
+          _fee := fee;
         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;
-      // 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
-      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;
-    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;
 
 procedure TFRMOperation.bbKeysClick(Sender: TObject);
@@ -202,6 +255,7 @@ begin
       if Not InputQuery('Wallet password','Enter wallet password',s) then exit;
       FWalletKeys.WalletPassword := s;
     Until FWalletKeys.IsValidPassword;
+    SetWalletKeys(WalletKeys);
     UpdateOperationOptions(errors);
   end;
 end;
@@ -244,7 +298,9 @@ begin
   FDisabled := true;
   try
     ebFee.Text := TAccountComp.FormatMoney(Fee);
-    ebAmount.Text := TAccountComp.FormatMoney(FTxAmount);
+    if SenderAccounts.Count<=1 then begin
+      ebAmount.Text := TAccountComp.FormatMoney(FTxAmount);
+    end;
   finally
     FDisabled := l;
   end;
@@ -261,10 +317,20 @@ procedure TFRMOperation.ebSenderAccountExit(Sender: TObject);
 Var an : Cardinal;
 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);
   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;
 
@@ -275,10 +341,10 @@ end;
 
 procedure TFRMOperation.FormCreate(Sender: TObject);
 begin
+  FSenderAccounts := TOrderedCardinalList.Create;
+  FSenderAccounts.OnListChanged := OnSenderAccountsChanged;
   FDisabled := true;
   FNode := TNode.Node;
-  FOperation := Nil;
-  FPAccount := Nil;
   ebDestAccount.Text := '';
   ebAmount.Text := TAccountComp.FormatMoney(0);
   ebEncryptPassword.Text := '';
@@ -291,36 +357,39 @@ begin
   lblNewOwnerErrors.Caption := '';
   FTxDestAccount := 0;
   FTxAmount := 0;
-  FSenderAccount := MaxInt;
   FNewAccountPublicKey := CT_Account_NUL.accountkey;
   FDisabled := false;
   lblAccountBalance.Caption := '';
+  memoAccounts.Lines.Clear;
 end;
 
 procedure TFRMOperation.FormDestroy(Sender: TObject);
 begin
-  if Assigned(FPAccount) then Dispose(FPAccount);
-  FreeAndNil(FOperation);
+  FreeAndNil(FSenderAccounts);
 end;
 
-function TFRMOperation.GetSenderAcccount: PAccount;
+procedure TFRMOperation.memoPayloadClick(Sender: TObject);
+Var errors : AnsiString;
 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;
-  Result := FPAccount;
-  if Assigned(FPAccount) then begin
-    lblAccountBalance.Caption := 'Balance: '+TAccountComp.FormatMoney(FPAccount.balance);
-  end else lblAccountBalance.Caption := '';
 end;
 
-procedure TFRMOperation.memoPayloadClick(Sender: TObject);
+procedure TFRMOperation.OnSenderAccountsChanged(Sender: TObject);
 Var errors : AnsiString;
 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;
 
 procedure TFRMOperation.rbTransactionClick(Sender: TObject);
@@ -343,21 +412,6 @@ begin
   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);
 Var i : Integer;
   wk : TWalletKey;
@@ -385,15 +439,61 @@ begin
   memoPayloadClick(Nil);
 end;
 
-function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
-Var P : PAccount;
+procedure TFRMOperation.UpdateAccountsInfo;
+Var ld : Boolean;
   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;
   e : AnsiString;
+  sender_account : TAccount;
 begin
   Result := false;
+  sender_account := CT_Account_NUL;
   errors := '';
-  FreeAndNil(FOperation);
   rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
   lblDestAccount.Enabled := rbTransaction.Checked;
   lblAmount.Enabled := rbTransaction.Checked;
@@ -402,36 +502,39 @@ begin
   lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
   try
     Try
-      P := GetSenderAcccount;
       bbPassword.Visible := 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
         errors := 'No wallet keys';
         lblGlobalErrors.Caption := errors;
         exit;
       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;
         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;
-        lblGlobalErrors.Caption := errors;
-        exit;
       end;
       lblGlobalErrors.Caption := '';
     Finally
@@ -452,7 +555,6 @@ begin
       rbEncryptedWithOldEC.Checked := false;
       rbEncryptedWithEC.Caption := 'Encrypted with dest. account public key';
       ebDestAccount.ParentFont := true;
-      ebAmount.ParentFont := true;
       ebFee.ParentFont := true;
       cbNewPrivateKey.Font.Color := clGrayText;
       ebNewPublicKey.Font.Color := clGrayText;
@@ -461,20 +563,28 @@ begin
         lblTransactionErrors.Caption := errors;
         exit;
       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;
       if not TAccountComp.TxtToMoney(ebFee.Text,FFee) then begin
         errors := 'Invalid fee ('+ebFee.Text+')';
         lblTransactionErrors.Caption := errors;
         exit;
       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;
 
       lblTransactionErrors.Caption := '';
@@ -513,9 +623,12 @@ begin
       cbNewPrivateKey.Font.Color := clGrayText;
       ebNewPublicKey.ParentFont := true;
       If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,FNewAccountPublicKey,errors) then begin
-//        errors := 'Invalid new owner public key';
         lblNewOwnerErrors.Caption := errors;
+        lblNewOwnerErrors.Font.Color := clRed;
         exit;
+      end else begin
+        lblNewOwnerErrors.Caption := 'New key type: '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
+        lblNewOwnerErrors.Font.Color := clGreen;
       end;
       Result := true;
     end else begin
@@ -535,10 +648,10 @@ begin
 
   end;
   //
-  UpdatePayload(e);
+  UpdatePayload(sender_account, e);
 end;
 
-function TFRMOperation.UpdatePayload(var errors : AnsiString) : Boolean;
+function TFRMOperation.UpdatePayload(Const SenderAccount : TAccount; var errors : AnsiString) : Boolean;
 Var payload_u : AnsiString;
   payload_encrypted : TRawBytes;
   account : TAccount;
@@ -559,7 +672,7 @@ begin
     end;
     if (rbEncryptedWithOldEC.Checked) then begin
       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);
       valid := payload_encrypted<>'';
     end else if (rbEncryptedWithEC.Checked) then begin

+ 342 - 29
Units/Forms/UFRMWallet.dfm

@@ -423,28 +423,53 @@ object FRMWallet: TFRMWallet
         end
         object ebFindAccountNumber: TEdit
           Left = 87
-          Top = 32
+          Top = 33
           Width = 83
           Height = 21
-          TabOrder = 2
+          TabOrder = 3
           OnChange = ebFindAccountNumberChange
           OnExit = ebFindAccountNumberExit
         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
       object pnlAccounts: TPanel
         Left = 0
@@ -453,7 +478,7 @@ object FRMWallet: TFRMWallet
         Height = 354
         Align = alLeft
         BevelOuter = bvNone
-        TabOrder = 2
+        TabOrder = 1
         object dgAccounts: TDrawGrid
           Left = 0
           Top = 0
@@ -479,6 +504,9 @@ object FRMWallet: TFRMWallet
           Align = alBottom
           BevelOuter = bvNone
           TabOrder = 1
+          DesignSize = (
+            380
+            34)
           object Label17: TLabel
             Left = 5
             Top = 10
@@ -507,6 +535,253 @@ object FRMWallet: TFRMWallet
             Height = 13
             Caption = '000'
           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
@@ -971,16 +1246,6 @@ object FRMWallet: TFRMWallet
     Top = 160
     object miProject: TMenuItem
       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
         Caption = 'Private keys'
         ShortCut = 16464
@@ -1006,6 +1271,54 @@ object FRMWallet: TFRMWallet
         OnClick = MiCloseClick
       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
       Caption = 'About'
       object miAboutPascalCoin: TMenuItem
@@ -1019,7 +1332,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Top = 180
     Bitmap = {
-      494C010102000800EC0010003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010102000800000110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 364 - 31
Units/Forms/UFRMWallet.pas

@@ -20,7 +20,7 @@ uses
   Dialogs, pngimage, ExtCtrls, ComCtrls, UWalletKeys, ShlObj, ADOInt, StdCtrls,
   ULog, DB, ADODB, Grids, DBGrids, DBCGrids, UAppParams,
   UBlockChain, UNode, DBCtrls, UGridUtils, UMiner, UAccounts, Menus, ImgList,
-  AppEvnts, UNetProtocol, UCrypto;
+  AppEvnts, UNetProtocol, UCrypto, Buttons;
 
 Const
   CT_PARAM_GridAccountsStream = 'GridAccountsStream';
@@ -64,7 +64,6 @@ type
     pnlMyAccountsTop: TPanel;
     dgAccounts: TDrawGrid;
     cbMyPrivateKeys: TComboBox;
-    dgAccountOperations: TDrawGrid;
     Splitter1: TSplitter;
     MainMenu: TMainMenu;
     miProject: TMenuItem;
@@ -150,6 +149,38 @@ type
     ebFindAccountNumber: TEdit;
     Label18: TLabel;
     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 FormDestroy(Sender: TObject);
     procedure TimerUpdateStatusTimer(Sender: TObject);
@@ -180,6 +211,23 @@ type
     procedure ebFindAccountNumberChange(Sender: TObject);
     procedure ebFindAccountNumberExit(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
     FMinersBlocksFound: Integer;
     procedure SetMinersBlocksFound(const Value: Integer);
@@ -194,6 +242,7 @@ type
     FAppParams : TAppParams;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FAccountsGrid : TAccountsGrid;
+    FSelectedAccountsGrid : TAccountsGrid;
     FOperationsGrid : TOperationsGrid;
     FPendingOperationsGrid : TOperationsGrid;
     FOrderedAccountsKeyList : TOrderedAccountKeysList;
@@ -202,6 +251,8 @@ type
     FMinerPrivateKeyType : TMinerPrivateKey;
     FUpdating : Boolean;
     FMessagesUnreadCount : Integer;
+    FMinAccountBalance : Int64;
+    FMaxAccountBalance : Int64;
     Procedure CheckMining;
     Procedure OnNewAccount(Sender : TObject);
     Procedure OnReceivedHelloMessage(Sender : TObject);
@@ -213,8 +264,9 @@ type
     procedure OnNetNodeServersUpdated(Sender : TObject);
     procedure OnNetBlackListUpdated(Sender : TObject);
     Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
+    Procedure OnSelectedAccountsGridUpdated(Sender : TObject);
     Procedure UpdateConnectionStatus;
-    Procedure UpdateAccounts;
+    Procedure UpdateAccounts(RefreshData : Boolean);
     Procedure UpdateBlockChainState;
     Procedure UpdatePrivateKeys;
     Procedure UpdateOperations;
@@ -226,6 +278,8 @@ type
     procedure Activate; override;
     Function ForceMining : Boolean; virtual;
     Function GetAccountKeyForMiner : TAccountKey;
+    Procedure DoUpdateAccounts;
+    Function DoUpdateAccountsFilter : Boolean;
   public
     { Public declarations }
     Property WalletKeys : TWalletKeys read FWalletKeys;
@@ -257,7 +311,7 @@ begin
   TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.NetServer.Active := true;
-  Synchronize( FRMWallet.UpdateAccounts );
+  Synchronize( FRMWallet.DoUpdateAccounts );
 end;
 
 { TFRMWallet }
@@ -295,6 +349,7 @@ begin
     TDBStorage(FNode.Bank.Storage).AccessFileName := TFolderHelper.GetPascalCoinDataFolder+'\pascalcoinB03.mdb';
     // Init Grid
     FAccountsGrid.Node := FNode;
+    FSelectedAccountsGrid.Node := FNode;
     FWalletKeys.OnChanged := OnWalletChanged;
     FOperationsGrid.Node := FNode;
     FPendingOperationsGrid.Node := FNode;
@@ -324,7 +379,7 @@ begin
     end;
   end;
   UpdatePrivateKeys;
-  UpdateAccounts;
+  UpdateAccounts(false);
   if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
     FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
     miAboutPascalCoinClick(Nil);
@@ -341,6 +396,46 @@ begin
   TrayIcon.ShowBalloonHint;
 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);
 Var basem,m : String;
   them, errors : AnsiString;
@@ -419,10 +514,15 @@ end;
 procedure TFRMWallet.cbExploreMyAccountsClick(Sender: TObject);
 begin
   cbMyPrivateKeys.Enabled := cbExploreMyAccounts.Checked;
-  UpdateAccounts;
+  UpdateAccounts(true);
   UpdateOperations;
 end;
 
+procedure TFRMWallet.cbFilterAccountsClick(Sender: TObject);
+begin
+  If not DoUpdateAccountsFilter then UpdateAccounts(true);
+end;
+
 procedure TFRMWallet.cbFilterOperationsByDateClick(Sender: TObject);
 begin
   dtpFilterOperationsDateStart.Enabled := cbFilterOperationsByDate.Checked;
@@ -432,7 +532,7 @@ end;
 
 procedure TFRMWallet.cbMyPrivateKeysChange(Sender: TObject);
 begin
-  UpdateAccounts;
+  UpdateAccounts(true);
 end;
 
 procedure TFRMWallet.CheckIsReady;
@@ -521,6 +621,45 @@ begin
   SaveAppParams;
 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);
 var i64 : Int64;
 begin
@@ -558,6 +697,17 @@ begin
   if key=#13 then  ebBlockChainBlockStartExit(Nil);
 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);
 Var acc : Cardinal;
   i64 : Int64;
@@ -640,6 +790,8 @@ Var i : Integer;
 begin
   if CPUCount>1 then FMaxCPUs := CPUCount-1
   else FMaxCPUs := 1;
+  FMinAccountBalance := 0;
+  FMaxAccountBalance := CT_MaxWalletAmount;
   FMessagesUnreadCount := 0;
   lblReceivedMessages.Visible := false;
   memoNetConnections.Lines.Clear;
@@ -667,6 +819,9 @@ begin
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid.DrawGrid := dgAccounts;
+  FSelectedAccountsGrid := TAccountsGrid.Create(Self);
+  FSelectedAccountsGrid.DrawGrid := dgSelectedAccounts;
+  FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
   FOperationsGrid := TOperationsGrid.Create(Self);
   FOperationsGrid.DrawGrid := dgAccountOperations;
   FPendingOperationsGrid := TOperationsGrid.Create(Self);
@@ -682,6 +837,7 @@ begin
   UpdateConnectionStatus;
   ebFilterOperationsAccount.Text := '';
   PageControl.ActivePage := tsAccountsExplorer;
+  pcAccountsOptions.ActivePage := tsAccountOperations;
   dtpFilterOperationsDateStart.Date := Now;
   dtpFilterOperationsDateEnd.Date := Now;
   ebFilterOperationsAccount.Text := '';
@@ -718,6 +874,7 @@ begin
   FOperationsGrid.Node := Nil;
   FPendingOperationsGrid.Node := Nil;
   FAccountsGrid.Node := Nil;
+  FSelectedAccountsGrid.Node := Nil;
   TNetData.NetData.OnReceivedHelloMessage := Nil;
   TNetData.NetData.OnStatisticsChanged := Nil;
   TNetData.NetData.OnNetConnectionsUpdated := Nil;
@@ -859,6 +1016,14 @@ begin
   end;
 end;
 
+procedure TFRMWallet.MiAddaccounttoSelectedClick(Sender: TObject);
+begin
+  PageControl.ActivePage := tsAccountsExplorer;
+  PageControlChange(Nil);
+  pcAccountsOptions.ActivePage := tsMultiSelectAccounts;
+  sbSelectedAccountsAddClick(Sender);
+end;
+
 procedure TFRMWallet.MiCloseClick(Sender: TObject);
 begin
   Close;
@@ -875,12 +1040,68 @@ begin
   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);
 begin
   CheckIsReady;
   With TFRMOperation.Create(Self) do
   Try
-    SenderAccount := FAccountsGrid.AccountNumber(dgAccounts.Row);
+    SenderAccounts.Add( FAccountsGrid.AccountNumber(dgAccounts.Row) );
     Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     WalletKeys := FWalletKeys;
     ShowModal;
@@ -917,6 +1138,14 @@ begin
   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);
 begin
   MinersBlocksFound := MinersBlocksFound+1;
@@ -1083,7 +1312,7 @@ end;
 
 procedure TFRMWallet.OnNewAccount(Sender: TObject);
 begin
-  UpdateAccounts;
+  UpdateAccounts(false);
   UpdateBlockChainState;
 end;
 
@@ -1149,6 +1378,12 @@ begin
   TNode.Node.PeerCache := s;
 end;
 
+procedure TFRMWallet.OnSelectedAccountsGridUpdated(Sender: TObject);
+begin
+  lblSelectedAccountsCount.Caption := Inttostr(FSelectedAccountsGrid.AccountsCount);
+  lblSelectedAccountsBalance.Caption := TAccountComp.FormatMoney( FSelectedAccountsGrid.AccountsBalance );
+end;
+
 procedure TFRMWallet.OnWalletChanged(Sender: TObject);
 begin
   UpdatePrivateKeys;
@@ -1160,7 +1395,11 @@ begin
   if PageControl.ActivePage=tsAccountsExplorer then begin
     FAccountsGrid.Node := FNode;
     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
     FPendingOperationsGrid.Node := FNode;
     MiDecodePayload.Enabled := true;
@@ -1194,6 +1433,68 @@ begin
   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);
 begin
   FMinersBlocksFound := Value;
@@ -1224,37 +1525,63 @@ begin
   Application.BringToFront();
 end;
 
-procedure TFRMWallet.UpdateAccounts;
+procedure TFRMWallet.UpdateAccounts(RefreshData : Boolean);
 Var accl : TOrderedCardinalList;
   l : TList;
   i,j,k : Integer;
+  c  : Cardinal;
+  applyfilter : Boolean;
+  acc : TAccount;
 begin
   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;
     Try
       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 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;
     Finally
@@ -1264,6 +1591,7 @@ begin
   end else begin
     lblAccountsCount.Caption := inttostr(FNode.Bank.AccountsCount);
   end;
+  bbChangeKeyName.Enabled := cbExploreMyAccounts.Checked;
   // Show Totals:
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   UpdateOperations;
@@ -1465,11 +1793,12 @@ begin
   If (Not Assigned(FOrderedAccountsKeyList)) And (Assigned(FNode)) Then begin
     FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(FNode.Bank.SafeBox,false);
   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;
   Try
     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
       wk := FWalletKeys.Key[i];
       if assigned(FOrderedAccountsKeyList) then begin
@@ -1483,9 +1812,13 @@ begin
       if Not Assigned(wk.PrivateKey) then s := s + '(*)';
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
     end;
+    cbMyPrivateKeys.Sorted := true;
+    cbMyPrivateKeys.Sorted := false;
+    cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));
   Finally
     cbMyPrivateKeys.Items.EndUpdate;
   End;
+  last_i := cbMyPrivateKeys.Items.IndexOfObject(TObject(last_i));
   if last_i<0 then last_i := 0;
   if cbMyPrivateKeys.Items.Count>last_i then cbMyPrivateKeys.ItemIndex := last_i
   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;
   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
   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 = (
@@ -452,7 +474,7 @@ end;
 
 class function TAccountComp.FormatMoney(Money: Int64): AnsiString;
 begin
-  Result := FormatFloat('0.0000',(Money/10000));
+  Result := FormatFloat('#,###0.0000',(Money/10000));
 end;
 
 class function TAccountComp.GetECInfoTxt(const EC_OpenSSL_NID: Word): AnsiString;
@@ -530,7 +552,7 @@ begin
     exit;
   end;
   try
-    s := StringReplace(moneytxt,ThousandSeparator,DecimalSeparator,[rfReplaceAll]);
+    s := StringReplace(moneytxt,ThousandSeparator,'',[rfReplaceAll]);
     money := Round( StrToFloat(s)*10000 );
     Result := true;
   Except
@@ -1321,4 +1343,114 @@ begin
   Dispose(P);
 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.

+ 1 - 1
Units/PascalCoin/UBlockChain.pas

@@ -565,7 +565,7 @@ begin
       finally
         Operations.Free;
       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
       FIsRestoringFromFile := False;
     end;

+ 1 - 1
Units/PascalCoin/UConst.pas

@@ -78,7 +78,7 @@ Const
   CT_Op_Changekey = $02;
   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';
 

+ 2 - 2
Units/PascalCoin/UDBStorage.pas

@@ -700,7 +700,7 @@ begin
   sql := 'SELECT '+sqltop+' * '+
     ' FROM '+CT_TblName_Operations+
     ' 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;
     ds := TADOQuery.Create(Self);
     try
@@ -777,7 +777,7 @@ begin
   Result := 'SELECT '+sqltop+' * '+
     ' FROM '+CT_TblName_Operations+
     ' '+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;
 
 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;
     FAlertedForNewProtocolAvailable : Boolean;
     FHasReceivedData : Boolean;
+    FIsDownloadingBlocks : Boolean;
     function GetConnected: Boolean;
     procedure SetConnected(const Value: Boolean);
     procedure TcpClient_OnError(Sender: TObject; SocketError: Integer);
@@ -997,7 +998,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
 var rid : Cardinal;
   bufferdata : TMemoryStream;
   headerdata : TNetHeaderData;
-  op : TOperationBlock;
+  my_op, client_op : TOperationBlock;
 begin
   // Protection against discovering servers...
   if FIsDiscoveringServers then begin
@@ -1021,25 +1022,27 @@ begin
       ' with OperationBlock:'+TPCOperationsComp.OperationBlockToText(Connection.FRemoteOperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')');
     // 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...
-    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;
     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');
         GetNewBank(-1);
       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)
-        GetNewBank(op.block);
+        GetNewBank(client_op.block);
       end;
     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:
-      Connection.Send_GetBlocks(TNode.Node.Bank.BlocksCount,0,rid);
+      Connection.Send_GetBlocks(my_op.block+1,0,rid);
     end;
   Finally
     TLog.NewLog(ltdebug,CT_LogSender,'Finalizing');
@@ -1470,6 +1473,7 @@ end;
 constructor TNetConnection.Create(AOwner: TComponent);
 begin
   inherited;
+  FIsDownloadingBlocks := false;
   FHasReceivedData := false;
   FNetProtocolVersion.protocol_version := 0; // 0 = unknown
   FNetProtocolVersion.protocol_available := 0;
@@ -1531,6 +1535,7 @@ Var P : PNodeServerAddress;
   i : Integer;
   include_in_list : Boolean;
 begin
+  FIsDownloadingBlocks := false;
   if ItsMyself then begin
     TLog.NewLog(ltInfo,Classname,'Disconecting myself '+ClientRemoteAddr+' > '+Why)
   end else begin
@@ -1687,6 +1692,7 @@ Var b,b_start,b_end:Cardinal;
     c : Cardinal;
   errors : AnsiString;
   DoDisconnect : Boolean;
+  posquantity : Int64;
 begin
   DoDisconnect := true;
   try
@@ -1714,14 +1720,23 @@ begin
        op := TPCOperationsComp.Create(TNetData.NetData.bank);
        try
          c := b_end - b_start + 1;
+         posquantity := db.position;
          db.Write(c,4);
+         c := 0;
          for b := b_start to b_end do begin
+           inc(c);
            If TNetData.NetData.bank.LoadOperations(op,b) then begin
              op.SaveBlockToStream(false,db);
            end else begin
              SendError(ntp_response,HeaderData.operation,HeaderData.request_id,CT_NetError_InternalServerError,'Operations of block:'+inttostr(b)+' not found');
              exit;
            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;
          Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,db);
        finally
@@ -1785,10 +1800,12 @@ begin
           end;
         end else begin
           // 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;
         end;
       end;
+      FIsDownloadingBlocks := false;
       if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
         Send_GetBlocks(TNode.Node.Bank.BlocksCount,50,i);
       end;
@@ -2441,12 +2458,14 @@ begin
       if FRemoteOperationBlock.block>0 then c2 := FRemoteOperationBlock.block
       else c2 := c1+100;
     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(c2,4);
     request_id := TNetData.NetData.NewRequestId;
     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]));
+    FIsDownloadingBlocks := quantity>1;
     Send(ntp_request,CT_NetOp_GetBlocks,0,request_id,data);
     Result := Connected;
   finally
@@ -2832,6 +2851,7 @@ Var i : Integer;
   candidates : TList;
   lop : TOperationBlock;
   netConnectionsList : TList;
+  nc : TNetConnection;
 begin
   // Search better candidates:
   candidates := TList.Create;
@@ -2841,12 +2861,16 @@ begin
     Try
       for i := 0 to netConnectionsList.Count - 1 do begin
         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
-           candidates.Add(TNetConnection(netConnectionsList[i]));
+           candidates.Add(nc);
            lop := TNetConnection(netConnectionsList[i]).FRemoteOperationBlock;
         end;
+        //
+        if nc.FIsDownloadingBlocks then exit;
+        //
       end;
     Finally
       TNetData.NetData.ConnectionsUnlock;

+ 3 - 14
Units/PascalCoin/UNode.pas

@@ -294,13 +294,15 @@ begin
     valids_operations := TOperationsHashTree.Create;
     try
       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
           inc(Result);
           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
           if (errors<>'') then errors := errors+' ';
           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;
       if Result=0 then exit;
@@ -806,19 +808,6 @@ begin
       TNetData.NetData.ConnectionUnlock(FNetConnection);
     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;
 
 constructor TThreadNodeNotifyNewBlock.Create(NetConnection: TNetConnection);

+ 2 - 2
Units/PascalCoin/UThread.pas

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

+ 15 - 85
Units/Utils/UGridUtils.pas

@@ -28,20 +28,6 @@ Type
     width : Integer;
   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)
   private
     FAccountsBalance : Int64;
@@ -50,6 +36,8 @@ Type
     FDrawGrid : TDrawGrid;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FShowAllAccounts: Boolean;
+    FOnUpdated: TNotifyEvent;
+    FAccountsCount: Integer;
     procedure SetDrawGrid(const Value: TDrawGrid);
     Procedure InitGrid;
     Procedure OnNodeNewOperation(Sender : TObject);
@@ -71,7 +59,9 @@ Type
     Procedure LoadFromStream(Stream : TStream);
     Property ShowAllAccounts : Boolean read FShowAllAccounts write SetShowAllAccounts;
     Property AccountsBalance : Int64 read FAccountsBalance;
+    Property AccountsCount : Integer read FAccountsCount;
     Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
+    Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
   End;
 
   TOperationsGrid = Class(TComponent)
@@ -223,7 +213,9 @@ constructor TAccountsGrid.Create(AOwner: TComponent);
 Var i : Integer;
 begin
   inherited;
+  FOnUpdated := Nil;
   FAccountsBalance := 0;
+  FAccountsCount := 0;
   FShowAllAccounts := false;
   FAccountsList := TOrderedCardinalList.Create;
   FDrawGrid := Nil;
@@ -257,6 +249,7 @@ Var i : Integer;
   acc : TAccount;
 begin
   FAccountsBalance := 0;
+  FAccountsCount := FAccountsList.Count;
   if Not assigned(DrawGrid) then exit;
   if FShowAllAccounts then begin
     if Assigned(Node) then begin
@@ -287,6 +280,7 @@ begin
     {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
     goThumbTracking, goFixedColClick, goFixedRowClick, goFixedHotTrack];
   FDrawGrid.Invalidate;
+  if Assigned(FOnUpdated) then FOnUpdated(Self);
 end;
 
 procedure TAccountsGrid.LoadFromStream(Stream: TStream);
@@ -500,6 +494,7 @@ end;
 
 procedure TAccountsGrid.SetNode(const Value: TNode);
 begin
+  if GetNode=Value then exit;
   FNodeNotifyEvents.Node := Value;
   InitGrid;
 end;
@@ -516,72 +511,6 @@ begin
   InitGrid;
 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 }
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
@@ -767,6 +696,7 @@ end;
 
 procedure TOperationsGrid.SetNode(const Value: TNode);
 begin
+  if GetNode=Value then exit;
   FNodeNotifyEvents.Node := Value;
   UpdateAccountOperations; // New Build 1.0.3
 end;
@@ -1529,15 +1459,15 @@ begin
     FDBGrid.OnDrawColumnCell := OnGridDrawColumnCell;
     FDBGrid.DefaultDrawing := false;
     FDBGrid.Columns.Clear;
-    AddColumn(CT_TblFld_BlockChain_block,'Block',50);
+    AddColumn(CT_TblFld_BlockChain_block,'Block',60);
     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('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(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;
   end;
 end;