Browse Source

Build 1.5.6

PascalCoin 8 years ago
parent
commit
0000c33018

BIN
PascalCoinWallet.res


+ 14 - 0
README.md

@@ -34,6 +34,20 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 
 ## History:  
 ## History:  
 
 
+### Build 1.5.6.0 - 2017-05-03
+- Allow multiselect accounts (GUI wallet)
+- Priority for operations with fee:
+  - Allow miner server (pools) to select what to mine using pascalcoin_daemon.ini file (daemon only)
+    - RPC_SERVERMINER_MAX_OPERATIONS_PER_BLOCK: Max of operations that can be included in a block (Default 5000, min value 1000)
+    - RPC_SERVERMINER_MAX_ZERO_FEE_OPERATIONS: Max of operations with 0 fee that can be included in a block (Default 2000, min value 400)
+  - Operations with fee have always preference over 0 fee operations (will be mined first)
+  - If operations with fee fills all the buffer, then no zero fee operation will be included
+- Fixed bug on receiving big blocks and slow net connection to prevent never synchronize
+- Fixed bug on miner server that produces "invalid operations hash" error on valid solutions with 0 operations
+- Fixed bug on file storage
+- Fixed minor bugs
+
+
 ### Build 1.5.5.0 - 2017-04-11
 ### Build 1.5.5.0 - 2017-04-11
 - Corrected fee result on RPC calls as a negative number on "Change key" operations
 - Corrected fee result on RPC calls as a negative number on "Change key" operations
 - Corrected PASCURRENCY value to be limited as a 4 decimal digits on RPC calls
 - Corrected PASCURRENCY value to be limited as a 4 decimal digits on RPC calls

+ 14 - 0
README.txt

@@ -34,6 +34,20 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 
 ## History:  
 ## History:  
 
 
+### Build 1.5.6.0 - 2017-05-03
+- Allow multiselect accounts (GUI wallet)
+- Priority for operations with fee:
+  - Allow miner server (pools) to select what to mine using pascalcoin_daemon.ini file (daemon only)
+    - RPC_SERVERMINER_MAX_OPERATIONS_PER_BLOCK: Max of operations that can be included in a block (Default 5000, min value 1000)
+    - RPC_SERVERMINER_MAX_ZERO_FEE_OPERATIONS: Max of operations with 0 fee that can be included in a block (Default 2000, min value 400)
+  - Operations with fee have always preference over 0 fee operations (will be mined first)
+  - If operations with fee fills all the buffer, then no zero fee operation will be included
+- Fixed bug on receiving big blocks and slow net connection to prevent never synchronize
+- Fixed bug on miner server that produces "invalid operations hash" error on valid solutions with 0 operations
+- Fixed bug on file storage
+- Fixed minor bugs
+
+
 ### Build 1.5.5.0 - 2017-04-11
 ### Build 1.5.5.0 - 2017-04-11
 - Corrected fee result on RPC calls as a negative number on "Change key" operations
 - Corrected fee result on RPC calls as a negative number on "Change key" operations
 - Corrected PASCURRENCY value to be limited as a 4 decimal digits on RPC calls
 - Corrected PASCURRENCY value to be limited as a 4 decimal digits on RPC calls

+ 3 - 3
Units/Forms/UFRMOperation.dfm

@@ -223,12 +223,12 @@ object FRMOperation: TFRMOperation
         end
         end
         object ebAmount: TEdit
         object ebAmount: TEdit
           Left = 298
           Left = 298
-          Top = 39
+          Top = 40
           Width = 86
           Width = 86
           Height = 21
           Height = 21
           TabOrder = 2
           TabOrder = 2
           Text = 'Edit1'
           Text = 'Edit1'
-          OnChange = ebDestAccountChange
+          OnChange = ebAmountChange
           OnClick = rbTransactionClick
           OnClick = rbTransactionClick
           OnExit = ebFeeExit
           OnExit = ebFeeExit
         end
         end
@@ -269,7 +269,7 @@ object FRMOperation: TFRMOperation
           Height = 21
           Height = 21
           TabOrder = 3
           TabOrder = 3
           Text = 'ebFee'
           Text = 'ebFee'
-          OnChange = ebDestAccountChange
+          OnChange = ebFeeChange
           OnClick = rbTransactionClick
           OnClick = rbTransactionClick
           OnExit = ebFeeExit
           OnExit = ebFeeExit
         end
         end

+ 2 - 2
Units/Forms/UFRMOperation.lfm

@@ -1,7 +1,7 @@
 object FRMOperation: TFRMOperation
 object FRMOperation: TFRMOperation
-  Left = 628
+  Left = 700
   Height = 474
   Height = 474
-  Top = 283
+  Top = 248
   Width = 608
   Width = 608
   BorderIcons = [biSystemMenu]
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
   BorderStyle = bsSingle

+ 48 - 14
Units/Forms/UFRMOperation.pas

@@ -144,7 +144,7 @@ uses
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 Var errors : AnsiString;
 Var errors : AnsiString;
   P : PAccount;
   P : PAccount;
-  i,iAcc : Integer;
+  i,iAcc, nZeroFee : Integer;
   wk : TWalletKey;
   wk : TWalletKey;
   ops : TOperationsHashTree;
   ops : TOperationsHashTree;
   op : TPCOperation;
   op : TPCOperation;
@@ -161,6 +161,7 @@ begin
     _totalfee := 0;
     _totalfee := 0;
     operationstxt := '';
     operationstxt := '';
     operation_to_string := '';
     operation_to_string := '';
+    nZeroFee := 0;
     for iAcc := 0 to FSenderAccounts.Count - 1 do begin
     for iAcc := 0 to FSenderAccounts.Count - 1 do begin
       op := Nil;
       op := Nil;
       account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
       account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
@@ -204,20 +205,25 @@ begin
         FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
         FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
         if account.balance>fee then _fee := fee
         if account.balance>fee then _fee := fee
         else _fee := 0;
         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;
+        if Not TAccountComp.Equal(wk.AccountKey,FNewAccountPublicKey) then begin
+          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;
       end else if rbTransferToANewOwner.Checked then begin
       end else if rbTransferToANewOwner.Checked then begin
         if account.balance>fee then _fee := fee
         if account.balance>fee then _fee := fee
         else _fee := 0;
         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);
+        if Not TAccountComp.Equal(wk.AccountKey,FNewAccountPublicKey) then begin
+          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;
       end else begin
       end else begin
         raise Exception.Create('No operation selected');
         raise Exception.Create('No operation selected');
       end;
       end;
       if Assigned(op) And (dooperation) then begin
       if Assigned(op) And (dooperation) then begin
         ops.AddOperationToHashTree(op);
         ops.AddOperationToHashTree(op);
+        if (op.OperationFee<=0) then inc(nZeroFee);
         if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
         if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
         operation_to_string := operation_to_string + op.ToString;
         operation_to_string := operation_to_string + op.ToString;
       end;
       end;
@@ -225,6 +231,12 @@ begin
     end;
     end;
     if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
     if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
 
 
+    if (nZeroFee>0) then begin
+      if Application.MessageBox(PChar('WARNING!'+#10+#10+'You are going to execute '+Inttostr(nZeroFee)+' operations without fee (fee = 0)'+#10+#10+
+        'Are you sure?'+#10+#10+'(Operations without fee have lower priority)'),PChar(Application.Title),MB_YESNO+MB_ICONWARNING+MB_DEFBUTTON2)<>IdYes then exit;
+    end;
+
+
     if (FSenderAccounts.Count>1) then begin
     if (FSenderAccounts.Count>1) then begin
       if rbTransaction.Checked then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
       if rbTransaction.Checked then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
       else auxs:='';
       else auxs:='';
@@ -242,7 +254,7 @@ begin
       Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
       Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
       ModalResult := MrOk;
       ModalResult := MrOk;
     end else if (i>0) then begin
     end else if (i>0) then begin
-      Application.MessageBox(PChar('One or more of your operations has not been executed:'+#10+
+      Application.MessageBox(PChar('One or more of your operations have not been executed:'+#10+
         'Errors:'+#10+
         'Errors:'+#10+
         errors+#10+#10+
         errors+#10+#10+
         'Total successfully executed operations: '+inttostr(i)),PChar(Application.Title),MB_OK+MB_ICONWARNING);
         'Total successfully executed operations: '+inttostr(i)),PChar(Application.Title),MB_OK+MB_ICONWARNING);
@@ -546,7 +558,7 @@ end;
 
 
 function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
 function TFRMOperation.UpdateOperationOptions(var errors : AnsiString) : Boolean;
 Var
 Var
-  iWallet,iAcc : Integer;
+  iWallet,iAcc,i : Integer;
   wk : TWalletKey;
   wk : TWalletKey;
   e : AnsiString;
   e : AnsiString;
   sender_account : TAccount;
   sender_account : TAccount;
@@ -557,7 +569,7 @@ begin
   rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
   rbEncryptedWithOldEC.Enabled := rbChangeKey.Checked;
   lblDestAccount.Enabled := rbTransaction.Checked;
   lblDestAccount.Enabled := rbTransaction.Checked;
   lblAmount.Enabled := rbTransaction.Checked;
   lblAmount.Enabled := rbTransaction.Checked;
-  lblFee.Enabled := rbTransaction.Checked;
+  lblFee.Enabled := true;
   lblNewPrivateKey.Enabled := rbChangeKey.Checked;
   lblNewPrivateKey.Enabled := rbChangeKey.Checked;
   lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
   lblNewOwnerPublicKey.Enabled := rbTransferToANewOwner.Checked;
   try
   try
@@ -665,7 +677,7 @@ begin
       rbEncryptedWithEC.Caption := 'Encrypted with new public key';
       rbEncryptedWithEC.Caption := 'Encrypted with new public key';
       ebDestAccount.Font.Color := clGrayText;
       ebDestAccount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
-      ebFee.Font.Color := clGrayText;
+      //ebFee.Font.Color := clGrayText;
       cbNewPrivateKey.ParentFont := true;
       cbNewPrivateKey.ParentFont := true;
       ebNewPublicKey.Font.Color := clGrayText;
       ebNewPublicKey.Font.Color := clGrayText;
       //
       //
@@ -673,7 +685,21 @@ begin
         errors := 'Must select a new private key';
         errors := 'Must select a new private key';
         lblChangeKeyErrors.Caption := errors;
         lblChangeKeyErrors.Caption := errors;
         exit;
         exit;
-
+      end;
+      i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+      if (i<0) Or (i>=WalletKeys.Count) then begin
+        errors := 'Invalid selected key';
+        lblChangeKeyErrors.Caption := errors;
+        exit;
+      end;
+      FNewAccountPublicKey := WalletKeys.Key[i].AccountKey;
+      if (SenderAccounts.Count=1) then begin
+        if (TAccountComp.Equal(sender_account.accountkey,FNewAccountPublicKey)) then begin
+          errors := 'Same public key';
+          lblNewOwnerErrors.Caption := errors;
+          lblNewOwnerErrors.Font.Color := clRed;
+          exit;
+        end;
       end;
       end;
       lblChangeKeyErrors.Caption := '';
       lblChangeKeyErrors.Caption := '';
       Result := true;
       Result := true;
@@ -686,7 +712,7 @@ begin
       lblNewOwnerErrors.Caption := '';
       lblNewOwnerErrors.Caption := '';
       ebDestAccount.Font.Color := clGrayText;
       ebDestAccount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
-      ebFee.Font.Color := clGrayText;
+      //ebFee.Font.Color := clGrayText;
       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
@@ -694,6 +720,14 @@ begin
         lblNewOwnerErrors.Font.Color := clRed;
         lblNewOwnerErrors.Font.Color := clRed;
         exit;
         exit;
       end else begin
       end else begin
+        if (SenderAccounts.Count=1) then begin
+          if (TAccountComp.Equal(sender_account.accountkey,FNewAccountPublicKey)) then begin
+            errors := 'Same public key';
+            lblNewOwnerErrors.Caption := errors;
+            lblNewOwnerErrors.Font.Color := clRed;
+            exit;
+          end;
+        end;
         lblNewOwnerErrors.Caption := 'New key type: '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
         lblNewOwnerErrors.Caption := 'New key type: '+TAccountComp.GetECInfoTxt(FNewAccountPublicKey.EC_OpenSSL_NID);
         lblNewOwnerErrors.Font.Color := clGreen;
         lblNewOwnerErrors.Font.Color := clGreen;
       end;
       end;
@@ -704,7 +738,7 @@ begin
       rbTransferToANewOwner.ParentFont := true;
       rbTransferToANewOwner.ParentFont := true;
       ebDestAccount.Font.Color := clGrayText;
       ebDestAccount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
       ebAmount.Font.Color := clGrayText;
-      ebFee.Font.Color := clGrayText;
+      //ebFee.Font.Color := clGrayText;
       cbNewPrivateKey.Font.Color := clGrayText;
       cbNewPrivateKey.Font.Color := clGrayText;
       lblTransactionErrors.Caption := '';
       lblTransactionErrors.Caption := '';
       lblChangeKeyErrors.Caption := '';
       lblChangeKeyErrors.Caption := '';

+ 18 - 2
Units/Forms/UFRMWallet.dfm

@@ -374,6 +374,10 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
     object tsMyAccounts: TTabSheet
       Caption = 'Accounts Explorer'
       Caption = 'Accounts Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
       object Splitter1: TSplitter
         Left = 380
         Left = 380
         Top = 66
         Top = 66
@@ -571,11 +575,15 @@ object FRMWallet: TFRMWallet
         Top = 66
         Top = 66
         Width = 458
         Width = 458
         Height = 338
         Height = 338
-        ActivePage = tsAccountOperations
+        ActivePage = tsMultiSelectAccounts
         Align = alClient
         Align = alClient
         TabOrder = 2
         TabOrder = 2
         object tsAccountOperations: TTabSheet
         object tsAccountOperations: TTabSheet
           Caption = 'Operations of selected Account'
           Caption = 'Operations of selected Account'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
           object dgAccountOperations: TDrawGrid
             Left = 0
             Left = 0
             Top = 0
             Top = 0
@@ -970,6 +978,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       Caption = 'Node Stats'
       ImageIndex = 3
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         841
         841
         404)
         404)
@@ -1035,6 +1047,10 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       Caption = 'Messages'
       ImageIndex = 6
       ImageIndex = 6
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         841
         841
         404)
         404)
@@ -1269,7 +1285,7 @@ object FRMWallet: TFRMWallet
     Left = 105
     Left = 105
     Top = 180
     Top = 180
     Bitmap = {
     Bitmap = {
-      494C010102000800EC0110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010102000800F40110003000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000
       0000000000000000002A292929D60B0B0BF4111111EE0000006B000000000000

+ 17 - 3
Units/Forms/UFRMWallet.pas

@@ -784,6 +784,7 @@ begin
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid.DrawGrid := dgAccounts;
   FAccountsGrid.DrawGrid := dgAccounts;
+  FAccountsGrid.AllowMultiSelect := true;
   FSelectedAccountsGrid := TAccountsGrid.Create(Self);
   FSelectedAccountsGrid := TAccountsGrid.Create(Self);
   FSelectedAccountsGrid.DrawGrid := dgSelectedAccounts;
   FSelectedAccountsGrid.DrawGrid := dgSelectedAccounts;
   FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
   FSelectedAccountsGrid.OnUpdated := OnSelectedAccountsGridUpdated;
@@ -1073,11 +1074,18 @@ begin
 end;
 end;
 
 
 procedure TFRMWallet.miNewOperationClick(Sender: TObject);
 procedure TFRMWallet.miNewOperationClick(Sender: TObject);
+var l : TOrderedCardinalList;
 begin
 begin
   CheckIsReady;
   CheckIsReady;
   With TFRMOperation.Create(Self) do
   With TFRMOperation.Create(Self) do
   Try
   Try
-    SenderAccounts.Add( FAccountsGrid.AccountNumber(dgAccounts.Row) );
+    l := TOrderedCardinalList.Create;
+    try
+      If FAccountsGrid.SelectedAccounts(l)<1 then raise Exception.Create('No row selected');
+      SenderAccounts.CopyFrom(l);
+    finally
+      l.Free;
+    end;
     Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     Fee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
     WalletKeys := FWalletKeys;
     WalletKeys := FWalletKeys;
     ShowModal;
     ShowModal;
@@ -1390,8 +1398,9 @@ begin
 end;
 end;
 
 
 procedure TFRMWallet.sbSelectedAccountsAddClick(Sender: TObject);
 procedure TFRMWallet.sbSelectedAccountsAddClick(Sender: TObject);
-Var l : TOrderedCardinalList;
+Var l, selected : TOrderedCardinalList;
   an : Int64;
   an : Int64;
+  i : Integer;
 begin
 begin
   an := FAccountsGrid.AccountNumber(dgAccounts.Row);
   an := FAccountsGrid.AccountNumber(dgAccounts.Row);
   if (an<0) then raise Exception.Create('No account selected');
   if (an<0) then raise Exception.Create('No account selected');
@@ -1400,10 +1409,15 @@ begin
       [TAccountComp.AccountNumberToAccountTxtNumber(an)]));
       [TAccountComp.AccountNumberToAccountTxtNumber(an)]));
   // Add
   // Add
   l := FSelectedAccountsGrid.LockAccountsList;
   l := FSelectedAccountsGrid.LockAccountsList;
+  selected := TOrderedCardinalList.Create;
   Try
   Try
-    l.Add( an );
+    FAccountsGrid.SelectedAccounts(selected);
+    for i := 0 to selected.Count - 1 do begin
+      l.Add(selected.Get(i));
+    end;
   Finally
   Finally
     FSelectedAccountsGrid.UnlockAccountsList;
     FSelectedAccountsGrid.UnlockAccountsList;
+    selected.Free;
   End;
   End;
 end;
 end;
 
 

+ 1 - 1
Units/PascalCoin/UBlockChain.pas

@@ -1470,11 +1470,11 @@ begin
           TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString);
           TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString);
         end;
         end;
       end;
       end;
-      FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
     Finally
     Finally
       aux2 := FOperationsHashTree;
       aux2 := FOperationsHashTree;
       FOperationsHashTree := aux;
       FOperationsHashTree := aux;
       aux2.Free;
       aux2.Free;
+      FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
     End;
     End;
   Finally
   Finally
     CalcProofOfWork(true,FOperationBlock.proof_of_work);
     CalcProofOfWork(true,FOperationBlock.proof_of_work);

+ 4 - 1
Units/PascalCoin/UConst.pas

@@ -100,12 +100,15 @@ Const
   CT_Op_Changekey = $02;
   CT_Op_Changekey = $02;
   CT_Op_Recover = $03;
   CT_Op_Recover = $03;
 
 
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.5.5'{$ELSE}{$IFDEF TESTNET}'TESTNET 1.5.5'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'1.5.6'{$ELSE}{$IFDEF TESTNET}'TESTNET 1.5.6'{$ELSE}{$ENDIF}{$ENDIF};
 
 
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin2.ddns.net;pascalcoin1.dynamic-dns.net;pascalcoin1.dns1.us';
 
 
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
 
 
+  CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}5{$ELSE}{$ENDIF}{$ENDIF};
+  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}5000{$ELSE}{$IFDEF TESTNET}10{$ELSE}{$ENDIF}{$ENDIF};
+
   // App Params
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStream';
   CT_PARAM_GridAccountsStream = 'GridAccountsStream';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';

+ 7 - 9
Units/PascalCoin/UFileStorage.pas

@@ -193,10 +193,11 @@ begin
     If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,StartingDeleteBlock,_Header) then exit;
     If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,StartingDeleteBlock,_Header) then exit;
     _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
     _intBlockIndex := (_Header.BlockNumber-BlockHeaderFirstBlock);
     p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
     p := Int64(_intBlockIndex) * Int64(CT_SizeOfBlockHeader);
+    Stream.Position:=p;
     // Write null data until end of header
     // Write null data until end of header
     GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
     GrowUntilPos(StreamBlockHeaderStartPos + GetBlockHeaderFixedSize,true);
     // End Stream at _Header
     // End Stream at _Header
-    Stream.Size := Stream.Position + _Header.StreamBlockRelStartPos-1;
+    Stream.Size := Stream.Position + _Header.StreamBlockRelStartPos;
   Finally
   Finally
     UnlockBlockChainStream;
     UnlockBlockChainStream;
   End;
   End;
@@ -610,8 +611,9 @@ Var p : Int64;
   _Header : TBlockHeader;
   _Header : TBlockHeader;
   _ops : TStream;
   _ops : TStream;
 begin
 begin
-  Result := StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,_Header);
-  if Not Result then exit;
+  Result := False;
+  If Not StreamReadBlockHeader(Stream,StreamBlockHeaderStartPos,BlockHeaderFirstBlock,Block,_Header) then exit;
+
   // Calculating block position
   // Calculating block position
   p := (StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
   p := (StreamBlockHeaderStartPos + GetBlockHeaderFixedSize) +
      (_Header.StreamBlockRelStartPos);
      (_Header.StreamBlockRelStartPos);
@@ -625,7 +627,7 @@ begin
   // Read the block
   // Read the block
   // Reading size
   // Reading size
   Stream.Read(_BlockSizeC,sizeof(_BlockSizeC));
   Stream.Read(_BlockSizeC,sizeof(_BlockSizeC));
-  if (_BlockSizeC>(_Header.BlockSize+sizeof(_BlockSizeC))) then begin
+  if ((_BlockSizeC+sizeof(_BlockSizeC))>(_Header.BlockSize)) then begin
     TLog.NewLog(lterror,Classname,Format('Corruption at stream Block size. Block %d SizeH:%d SizeC:%d',[Block,
     TLog.NewLog(lterror,Classname,Format('Corruption at stream Block size. Block %d SizeH:%d SizeC:%d',[Block,
       _Header.BlockSize,_BlockSizeC]));
       _Header.BlockSize,_BlockSizeC]));
     exit;
     exit;
@@ -682,6 +684,7 @@ begin
     _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
     _Header.StreamBlockRelStartPos := _HeaderPrevious.StreamBlockRelStartPos + _HeaderPrevious.BlockSize;
   end else begin
   end else begin
     // First block of the stream
     // First block of the stream
+    Result := true;
     _Header.StreamBlockRelStartPos := 0;
     _Header.StreamBlockRelStartPos := 0;
   end;
   end;
   _ops := TMemoryStream.Create;
   _ops := TMemoryStream.Create;
@@ -715,11 +718,6 @@ end;
 function TFileStorage.StreamReadBlockHeader(Stream: TStream;
 function TFileStorage.StreamReadBlockHeader(Stream: TStream;
   StreamBlockHeaderStartPos: Int64; BlockHeaderFirstBlock, Block: Cardinal;
   StreamBlockHeaderStartPos: Int64; BlockHeaderFirstBlock, Block: Cardinal;
   var BlockHeader: TBlockHeader): Boolean;
   var BlockHeader: TBlockHeader): Boolean;
-Var p : Int64;
-  errors : AnsiString;
-  streamFirstBlock : Cardinal;
-  _intBlockIndex : Cardinal;
-  _Blocks : Cardinal;
 begin
 begin
   Result := false;
   Result := false;
   BlockHeader := CT_TBlockHeader_NUL;
   BlockHeader := CT_TBlockHeader_NUL;

+ 12 - 4
Units/PascalCoin/UNetProtocol.pas

@@ -872,14 +872,18 @@ begin
     buffer.Read(HeaderData.protocol.protocol_version,2);
     buffer.Read(HeaderData.protocol.protocol_version,2);
     buffer.Read(HeaderData.protocol.protocol_available,2);
     buffer.Read(HeaderData.protocol.protocol_available,2);
     buffer.Read(c,4);
     buffer.Read(c,4);
+    HeaderData.buffer_data_length := c;
     DataBuffer.Size := 0;
     DataBuffer.Size := 0;
     if buffer.Size - buffer.Position < c then begin
     if buffer.Size - buffer.Position < c then begin
       IsValidHeaderButNeedMoreData := true;
       IsValidHeaderButNeedMoreData := true;
+      {$IFDEF HIGHLOG}
+      TLog.NewLog(ltdebug,className,Format('Need more data! Buffer size (%d) - position (%d) < %d - Header info: %s',
+        [buffer.Size,buffer.Position,c,HeaderDataToText(HeaderData)]));
+      {$ENDIF}
       exit;
       exit;
     end;
     end;
     DataBuffer.CopyFrom(buffer,c);
     DataBuffer.CopyFrom(buffer,c);
     DataBuffer.Position := 0;
     DataBuffer.Position := 0;
-    HeaderData.buffer_data_length := c;
     //
     //
     if HeaderData.header_type=ntp_response then begin
     if HeaderData.header_type=ntp_response then begin
       HeaderData.is_error := HeaderData.error_code<>0;
       HeaderData.is_error := HeaderData.error_code<>0;
@@ -1094,7 +1098,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       repeat
       repeat
         BlocksList := TList.Create;
         BlocksList := TList.Create;
         try
         try
-          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,5000,false,BlocksList);
+          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,90000,false,BlocksList);
           i := 0;
           i := 0;
           while (i<BlocksList.Count) And (Not finished) do begin
           while (i<BlocksList.Count) And (Not finished) do begin
             OpComp := TPCOperationsComp(BlocksList[i]);
             OpComp := TPCOperationsComp(BlocksList[i]);
@@ -1145,7 +1149,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       end else begin
       end else begin
         if (Not IsAScam) And (Connection.FRemoteAccumulatedWork > TNode.Node.Bank.SafeBox.WorkSum) then begin
         if (Not IsAScam) And (Connection.FRemoteAccumulatedWork > TNode.Node.Bank.SafeBox.WorkSum) then begin
           // Possible scammer!
           // Possible scammer!
-          Connection.DisconnectInvalidClient(false,Format('Possible scammer! Says blocks:%d Work:% - Obtained blocks:%d work:%d',
+          Connection.DisconnectInvalidClient(false,Format('Possible scammer! Says blocks:%d Work:%d - Obtained blocks:%d work:%d',
             [Connection.FRemoteOperationBlock.block+1,Connection.FRemoteAccumulatedWork,
             [Connection.FRemoteOperationBlock.block+1,Connection.FRemoteAccumulatedWork,
              Bank.BlocksCount,Bank.SafeBox.WorkSum]));
              Bank.BlocksCount,Bank.SafeBox.WorkSum]));
         end;
         end;
@@ -2478,6 +2482,7 @@ begin
           end;
           end;
         end;
         end;
       end else begin
       end else begin
+        sleep(1);
         if Not Client.WaitForData(100) then begin
         if Not Client.WaitForData(100) then begin
           exit;
           exit;
         end;
         end;
@@ -2506,10 +2511,13 @@ begin
       if (Connected) then begin
       if (Connected) then begin
         if (Not Result) And (FClientBufferRead.Size>0) And (Not IsValidHeaderButNeedMoreData) then begin
         if (Not Result) And (FClientBufferRead.Size>0) And (Not IsValidHeaderButNeedMoreData) then begin
           deletedBytes := FClientBufferRead.Size;
           deletedBytes := FClientBufferRead.Size;
-          TLog.NewLog(lterror,ClassName,Format('Deleting %d bytes from TcpClient buffer of %s after max %d miliseconds. Passed: %d',
+          TLog.NewLog(lterror,ClassName,Format('Deleting %d bytes from TcpClient buffer of %s after max %d miliseconds. Elapsed: %d',
             [deletedBytes, Client.ClientRemoteAddr,MaxWaitMiliseconds,GetTickCount-tc]));
             [deletedBytes, Client.ClientRemoteAddr,MaxWaitMiliseconds,GetTickCount-tc]));
           FClientBufferRead.Size:=0;
           FClientBufferRead.Size:=0;
           DisconnectInvalidClient(false,'Invalid data received in buffer ('+inttostr(deletedBytes)+' bytes)');
           DisconnectInvalidClient(false,'Invalid data received in buffer ('+inttostr(deletedBytes)+' bytes)');
+        end else if (IsValidHeaderButNeedMoreData) then begin
+          TLog.NewLog(ltDebug,ClassName,Format('Not enough data received - Received %d bytes from TcpClient buffer of %s after max %d miliseconds. Elapsed: %d - HeaderData: %s',
+            [FClientBufferRead.Size, Client.ClientRemoteAddr,MaxWaitMiliseconds,GetTickCount-tc,TNetData.HeaderDataToText(HeaderData)]));
         end;
         end;
       end;
       end;
     Finally
     Finally

+ 125 - 3
Units/PascalCoin/UPoolMining.pas

@@ -126,6 +126,9 @@ Type
     FOnMiningServerNewBlockFound: TNotifyEvent;
     FOnMiningServerNewBlockFound: TNotifyEvent;
     FPoolJobs : TPCThreadList;
     FPoolJobs : TPCThreadList;
     FPoolThread : TPoolMiningServerThread;
     FPoolThread : TPoolMiningServerThread;
+    FMinerOperations : TPCOperationsComp;
+    FMaxOperationsPerBlock: Integer;
+    FMax0FeeOperationsPerBlock: Integer;
     Procedure DoProcessJSON(json : TPCJSONObject; ResponseMethod : String; Client : TJSONRPCTcpIpClient);
     Procedure DoProcessJSON(json : TPCJSONObject; ResponseMethod : String; Client : TJSONRPCTcpIpClient);
     Procedure OnNodeNewBlock(Sender : TObject);
     Procedure OnNodeNewBlock(Sender : TObject);
     Procedure OnNodeOperationsChanged(Sender : TObject);
     Procedure OnNodeOperationsChanged(Sender : TObject);
@@ -135,6 +138,9 @@ Type
     Procedure ClearPoolJobs;
     Procedure ClearPoolJobs;
     Procedure CaptureNewJobAndSendToMiners;
     Procedure CaptureNewJobAndSendToMiners;
     Procedure SendJobToMiner(Operations : TPCOperationsComp; Client : TJSONRPCTcpIpClient; IsResponse : Boolean; idResponse : Variant);
     Procedure SendJobToMiner(Operations : TPCOperationsComp; Client : TJSONRPCTcpIpClient; IsResponse : Boolean; idResponse : Variant);
+    Procedure FillMinerOperations;
+    procedure SetMax0FeeOperationsPerBlock(const Value: Integer);
+    procedure SetMaxOperationsPerBlock(const Value: Integer);
   protected
   protected
     Procedure OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient); override;
     Procedure OnNewIncommingConnection(Sender : TObject; Client : TNetTcpIpClient); override;
     procedure SetActive(const Value: Boolean); override;
     procedure SetActive(const Value: Boolean); override;
@@ -147,6 +153,8 @@ Type
     Property ClientsCount : Integer read FClientsCount;
     Property ClientsCount : Integer read FClientsCount;
     Property ClientsWins : Integer read FClientsWins;
     Property ClientsWins : Integer read FClientsWins;
     Property OnMiningServerNewBlockFound : TNotifyEvent read FOnMiningServerNewBlockFound write FOnMiningServerNewBlockFound;
     Property OnMiningServerNewBlockFound : TNotifyEvent read FOnMiningServerNewBlockFound write FOnMiningServerNewBlockFound;
+    Property Max0FeeOperationsPerBlock : Integer read FMax0FeeOperationsPerBlock write SetMax0FeeOperationsPerBlock;
+    Property MaxOperationsPerBlock : Integer read FMaxOperationsPerBlock write SetMaxOperationsPerBlock;
   End;
   End;
 
 
 Function TBytesToString(Const bytes : TBytes):AnsiString;
 Function TBytesToString(Const bytes : TBytes):AnsiString;
@@ -515,6 +523,7 @@ Var P, PToDelete : PPoolJob;
   OpB : TOperationBlock;
   OpB : TOperationBlock;
 begin
 begin
   if Not Active then exit;
   if Not Active then exit;
+  if FClientsCount<=0 then exit;
   doAdd := false;
   doAdd := false;
   P := Nil;
   P := Nil;
   l := FPoolJobs.LockList;
   l := FPoolJobs.LockList;
@@ -536,13 +545,17 @@ begin
       if (P^.SentMinTimestamp<FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp) then begin
       if (P^.SentMinTimestamp<FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp) then begin
         P^.SentMinTimestamp := FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp;
         P^.SentMinTimestamp := FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp;
       end;
       end;
+      FillMinerOperations;
       P^.OperationsComp := TPCOperationsComp.Create(Nil);
       P^.OperationsComp := TPCOperationsComp.Create(Nil);
-      P^.OperationsComp.CopyFrom(FNodeNotifyEvents.Node.Operations);
+      P^.OperationsComp.CopyFrom(FMinerOperations);
       P^.OperationsComp.AccountKey := FMinerAccountKey;
       P^.OperationsComp.AccountKey := FMinerAccountKey;
       P^.OperationsComp.BlockPayload := FMinerPayload;
       P^.OperationsComp.BlockPayload := FMinerPayload;
       P^.OperationsComp.timestamp := P^.SentMinTimestamp; // Best practices 1.5.3
       P^.OperationsComp.timestamp := P^.SentMinTimestamp; // Best practices 1.5.3
       OpB := P^.OperationsComp.OperationBlock;
       OpB := P^.OperationsComp.OperationBlock;
       if (OpB.block<>0) And (OpB.block <> (FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.block+1)) then begin
       if (OpB.block<>0) And (OpB.block <> (FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.block+1)) then begin
+        TLog.NewLog(ltError,ClassName,'ERROR DEV 20170228-1 '+TPCOperationsComp.OperationBlockToText(OpB)+'<>'+TPCOperationsComp.OperationBlockToText(FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock));
+        P^.OperationsComp.Free;
+        Dispose(P);
         raise Exception.Create('ERROR DEV 20170228-1');
         raise Exception.Create('ERROR DEV 20170228-1');
       end;
       end;
       i := l.Add(P);
       i := l.Add(P);
@@ -610,10 +623,13 @@ begin
   FNodeNotifyEvents.OnBlocksChanged := OnNodeNewBlock;
   FNodeNotifyEvents.OnBlocksChanged := OnNodeNewBlock;
   FNodeNotifyEvents.OnOperationsChanged := OnNodeOperationsChanged;
   FNodeNotifyEvents.OnOperationsChanged := OnNodeOperationsChanged;
   FNodeNotifyEvents.Node := TNode.Node;
   FNodeNotifyEvents.Node := TNode.Node;
+  FMinerOperations := TPCOperationsComp.Create(FNodeNotifyEvents.Node.Bank);
   FMinerAccountKey := CT_TECDSA_Public_Nul;
   FMinerAccountKey := CT_TECDSA_Public_Nul;
   FMinerPayload := '';
   FMinerPayload := '';
   FPoolJobs := TPCThreadList.Create('TPoolMiningServer_PoolJobs');
   FPoolJobs := TPCThreadList.Create('TPoolMiningServer_PoolJobs');
   FPoolThread := TPoolMiningServerThread.Create(Self);
   FPoolThread := TPoolMiningServerThread.Create(Self);
+  FMax0FeeOperationsPerBlock := CT_MAX_0_fee_operations_per_block_by_miner;
+  FMaxOperationsPerBlock := CT_MAX_Operations_per_block_by_miner;
 end;
 end;
 
 
 destructor TPoolMiningServer.Destroy;
 destructor TPoolMiningServer.Destroy;
@@ -624,6 +640,7 @@ begin
   FNodeNotifyEvents.Node := Nil;
   FNodeNotifyEvents.Node := Nil;
   FNodeNotifyEvents.OnBlocksChanged := Nil;
   FNodeNotifyEvents.OnBlocksChanged := Nil;
   FNodeNotifyEvents.OnOperationsChanged := Nil;
   FNodeNotifyEvents.OnOperationsChanged := Nil;
+  FreeAndNil(FMinerOperations);
   FreeAndNil(FNodeNotifyEvents);
   FreeAndNil(FNodeNotifyEvents);
   ClearPoolJobs;
   ClearPoolJobs;
   FreeAndNil(FPoolJobs);
   FreeAndNil(FPoolJobs);
@@ -684,6 +701,76 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TPoolMiningServer.FillMinerOperations;
+var tree : TOperationsHashTree;
+  Procedure DoAdd(Const Op : TPCOperation; checkDuplicate : Boolean);
+  Begin
+    if checkDuplicate then begin
+      if tree.IndexOfOperation(Op)>=0 then exit;
+    end;
+    tree.AddOperationToHashTree(Op);
+  End;
+Var i,j : Integer;
+  MasterOp : TPCOperationsComp;
+  op : TPCOperation;
+  Var errors : AnsiString;
+begin
+  MasterOp := FNodeNotifyEvents.Node.Operations;
+  MasterOp.Lock;
+  Try
+    FMinerOperations.Lock;
+    Try
+      tree := TOperationsHashTree.Create;
+      try
+        if (Not (TPCOperationsComp.EqualsOperationBlock(FMinerOperations.OperationBlock,MasterOp.OperationBlock))) then begin
+          FMinerOperations.Clear(true);
+          if MasterOp.Count>=0 then begin
+            // First round: Select with fee > 0
+            i := 0;
+            while (tree.OperationsCount<MaxOperationsPerBlock) And (i<MasterOp.OperationsHashTree.OperationsCount) do begin
+              op := MasterOp.OperationsHashTree.GetOperation(i);
+              if op.OperationFee>0 then begin
+                DoAdd(op,false);
+              end;
+              inc(i);
+            end;
+            // Second round: Allow fee = 0
+            j := 0;
+            i := 0;
+            while (tree.OperationsCount<MaxOperationsPerBlock) And (i<MasterOp.OperationsHashTree.OperationsCount) And (j<Max0FeeOperationsPerBlock) do begin
+              op := MasterOp.OperationsHashTree.GetOperation(i);
+              if op.OperationFee=0 then begin
+                DoAdd(op,false);
+                inc(j);
+              end;
+              inc(i);
+            end;
+            // Add operations:
+            i := FMinerOperations.AddOperations(tree,errors);
+            if (i<>tree.OperationsCount) Or (i<>MasterOp.OperationsHashTree.OperationsCount) then begin
+              TLog.NewLog(ltDebug,ClassName,Format('Cannot add all operations! Master:%d Selected:%d Added:%d - Errors: %s',
+                [MasterOp.OperationsHashTree.OperationsCount,tree.OperationsCount,i,errors]));
+            end;
+          end else begin
+            FMinerOperations.CopyFrom(MasterOp);
+          end;
+          //
+          TLog.NewLog(ltInfo,ClassName,Format('New miner operations:%d Hash:%s %s',
+            [FMinerOperations.OperationsHashTree.OperationsCount,TCrypto.ToHexaString(FMinerOperations.OperationsHashTree.HashTree),TCrypto.ToHexaString(FMinerOperations.OperationBlock.operations_hash)]));
+        end else begin
+          TLog.NewLog(ltDebug,ClassName,Format('No need to change Miner buffer. Operations:%d',[FMinerOperations.OperationsHashTree.OperationsCount]));
+        end;
+      finally
+        tree.Free;
+      end;
+    Finally
+      FMinerOperations.Unlock;
+    end;
+  Finally
+    MasterOp.Unlock;
+  End;
+end;
+
 function TPoolMiningServer.MinerSubmit(Client: TJSONRPCTcpIpClient; params: TPCJSONObject; const id : Variant): Boolean;
 function TPoolMiningServer.MinerSubmit(Client: TJSONRPCTcpIpClient; params: TPCJSONObject; const id : Variant): Boolean;
 Var s : String;
 Var s : String;
   nbOperations : TPCOperationsComp;
   nbOperations : TPCOperationsComp;
@@ -826,6 +913,7 @@ Var P : PPoolJob;
   i,nJobs : Integer;
   i,nJobs : Integer;
   l : TList;
   l : TList;
 begin
 begin
+  if FClientsCount<=0 then exit;
   if (Not Assigned(Operations)) then begin
   if (Not Assigned(Operations)) then begin
     P := Nil;
     P := Nil;
     l := FPoolJobs.LockList;
     l := FPoolJobs.LockList;
@@ -839,12 +927,16 @@ begin
         if (P^.SentMinTimestamp<FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp) then begin
         if (P^.SentMinTimestamp<FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp) then begin
           P^.SentMinTimestamp := FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp;
           P^.SentMinTimestamp := FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.timestamp;
         end;
         end;
+        FillMinerOperations;
         P^.OperationsComp := TPCOperationsComp.Create(Nil);
         P^.OperationsComp := TPCOperationsComp.Create(Nil);
-        P^.OperationsComp.CopyFrom(FNodeNotifyEvents.Node.Operations);
+        P^.OperationsComp.CopyFrom(FMinerOperations);
         P^.OperationsComp.AccountKey := FMinerAccountKey;
         P^.OperationsComp.AccountKey := FMinerAccountKey;
         P^.OperationsComp.BlockPayload := FMinerPayload;
         P^.OperationsComp.BlockPayload := FMinerPayload;
         P^.OperationsComp.timestamp := P^.SentMinTimestamp; // Best practices 1.5.3
         P^.OperationsComp.timestamp := P^.SentMinTimestamp; // Best practices 1.5.3
         if (P^.OperationsComp.OperationBlock.block<>0) And (P^.OperationsComp.OperationBlock.block <> (FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.block+1)) then begin
         if (P^.OperationsComp.OperationBlock.block<>0) And (P^.OperationsComp.OperationBlock.block <> (FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock.block+1)) then begin
+          TLog.NewLog(ltError,ClassName,'ERROR DEV 20170228-2 '+TPCOperationsComp.OperationBlockToText(P^.OperationsComp.OperationBlock)+'<>'+TPCOperationsComp.OperationBlockToText(FNodeNotifyEvents.Node.Bank.LastBlockFound.OperationBlock));
+          P^.OperationsComp.Free;
+          Dispose(P);
           raise Exception.Create('ERROR DEV 20170228-2');
           raise Exception.Create('ERROR DEV 20170228-2');
         end;
         end;
         l.Add(P);
         l.Add(P);
@@ -901,6 +993,30 @@ begin
 end;
 end;
 
 
 
 
+procedure TPoolMiningServer.SetMax0FeeOperationsPerBlock(const Value: Integer);
+begin
+  if FMax0FeeOperationsPerBlock = Value then exit;
+  if (Value<(CT_MAX_0_fee_operations_per_block_by_miner DIV 5)) Or (Value<1) then begin
+    FMax0FeeOperationsPerBlock := (CT_MAX_0_fee_operations_per_block_by_miner DIV 5); // To prevent no 0 fee...
+    if FMax0FeeOperationsPerBlock<1 then FMax0FeeOperationsPerBlock := 1; // For Testnet or low constant values...
+    TLog.NewLog(ltError,ClassName,Format('Invalid max zero fee operations per block value %d, set to %d',[Value,FMax0FeeOperationsPerBlock]));
+  end else FMax0FeeOperationsPerBlock := Value;
+  TLog.NewLog(ltInfo,ClassName,Format('Updated max zero fee operations per block to %d',[FMax0FeeOperationsPerBlock]));
+  CaptureNewJobAndSendToMiners;
+end;
+
+procedure TPoolMiningServer.SetMaxOperationsPerBlock(const Value: Integer);
+begin
+  if FMaxOperationsPerBlock = Value then exit;
+  if (Value<(CT_MAX_Operations_per_block_by_miner DIV 5)) Or (Value<1) then begin
+    FMaxOperationsPerBlock := (CT_MAX_Operations_per_block_by_miner DIV 5); // To prevent very small blocks...
+    if FMaxOperationsPerBlock<1 then FMaxOperationsPerBlock := 1; // For Testnet or low constant values...
+    TLog.NewLog(ltError,ClassName,Format('Invalid max operations per block value %d, set to %d',[Value,FMaxOperationsPerBlock]));
+  end else FMaxOperationsPerBlock := Value;
+  TLog.NewLog(ltInfo,ClassName,Format('Updated max operations per block to %d',[FMaxOperationsPerBlock]));
+  CaptureNewJobAndSendToMiners;
+end;
+
 procedure TPoolMiningServer.SetMinerAccountKey(const Value: TAccountKey);
 procedure TPoolMiningServer.SetMinerAccountKey(const Value: TAccountKey);
 begin
 begin
   if TAccountComp.Equal(FMinerAccountKey,Value) then exit;
   if TAccountComp.Equal(FMinerAccountKey,Value) then exit;
@@ -1130,7 +1246,13 @@ begin
     Sleep(100);
     Sleep(100);
     inc(i);
     inc(i);
     if (not terminated) And ((i mod 10)=0) then begin
     if (not terminated) And ((i mod 10)=0) then begin
-      FPoolMiningServer.CaptureNewJobAndSendToMiners;
+      Try
+        FPoolMiningServer.CaptureNewJobAndSendToMiners;
+      Except
+        On E:Exception do begin
+          TLog.NewLog(ltError,ClassName,'Error ('+E.ClassName+') Capturing job for miners: '+E.Message );
+        end;
+      End;
     end;
     end;
   Until terminated;
   Until terminated;
 end;
 end;

+ 13 - 3
Units/PascalCoin/UTCPIP.pas

@@ -54,6 +54,7 @@ type
     FRemotePort : Word;
     FRemotePort : Word;
     FBytesReceived, FBytesSent : Int64;
     FBytesReceived, FBytesSent : Int64;
     FLock : TPCCriticalSection;
     FLock : TPCCriticalSection;
+    FSendBufferLock : TPCCriticalSection;
     {$ENDIF}
     {$ENDIF}
     FOnConnect: TNotifyEvent;
     FOnConnect: TNotifyEvent;
     FOnDisconnect: TNotifyEvent;
     FOnDisconnect: TNotifyEvent;
@@ -282,6 +283,7 @@ begin
   {$ENDIF}
   {$ENDIF}
   {$IFDEF Synapse}
   {$IFDEF Synapse}
   FLock := TPCCriticalSection.Create('TNetTcpIpClient_Lock');
   FLock := TPCCriticalSection.Create('TNetTcpIpClient_Lock');
+  FSendBufferLock := TPCCriticalSection.Create('TNetTcpIpClient_SendBufferLock');
   FTcpBlockSocket := TTCPBlockSocket.Create;
   FTcpBlockSocket := TTCPBlockSocket.Create;
   FTcpBlockSocket.OnAfterConnect := OnConnect;
   FTcpBlockSocket.OnAfterConnect := OnConnect;
   FTcpBlockSocket.SocksTimeout := 5000; //Build 1.5.0 was 10000;
   FTcpBlockSocket.SocksTimeout := 5000; //Build 1.5.0 was 10000;
@@ -298,6 +300,7 @@ destructor TNetTcpIpClient.Destroy;
 begin
 begin
   Disconnect;
   Disconnect;
   {$IFDEF Synapse}  // Memory leak on 1.5.0
   {$IFDEF Synapse}  // Memory leak on 1.5.0
+  FreeAndNil(FSendBufferLock);
   FreeAndNil(FLock);
   FreeAndNil(FLock);
   {$ENDIF}
   {$ENDIF}
   inherited;
   inherited;
@@ -431,6 +434,7 @@ end;
 
 
 function TNetTcpIpClient.SendStream(Stream: TStream): Int64;
 function TNetTcpIpClient.SendStream(Stream: TStream): Int64;
 Var sp : Int64;
 Var sp : Int64;
+  unlocked : Boolean;
 begin
 begin
   sp := Stream.Position;
   sp := Stream.Position;
   {$IFDEF DelphiSockets}
   {$IFDEF DelphiSockets}
@@ -440,13 +444,17 @@ begin
   {$ENDIF}
   {$ENDIF}
   {$IFDEF Synapse}
   {$IFDEF Synapse}
   Result := 0;
   Result := 0;
-  FLock.Acquire;
+  unlocked := false;
+  // In order to allow a big stream sending, will cut up in small blocks
+  FSendBufferLock.Acquire;
   Try
   Try
     Try
     Try
       FTcpBlockSocket.SendStreamRaw(Stream);
       FTcpBlockSocket.SendStreamRaw(Stream);
       if FTcpBlockSocket.LastError<>0 then begin
       if FTcpBlockSocket.LastError<>0 then begin
         TLog.NewLog(ltDebug,ClassName,'Closing connection from '+ClientRemoteAddr+' (Sending error): '+Inttostr(FTcpBlockSocket.LastError)+' '+FTcpBlockSocket.GetErrorDescEx);
         TLog.NewLog(ltDebug,ClassName,'Closing connection from '+ClientRemoteAddr+' (Sending error): '+Inttostr(FTcpBlockSocket.LastError)+' '+FTcpBlockSocket.GetErrorDescEx);
         Result := -1;
         Result := -1;
+        unlocked := true;
+        FSendBufferLock.Release;
         Disconnect;
         Disconnect;
       end else begin
       end else begin
         Result := Stream.Position - sp;
         Result := Stream.Position - sp;
@@ -456,12 +464,14 @@ begin
       On E:Exception do begin
       On E:Exception do begin
         SocketError := FTcpBlockSocket.LastError;
         SocketError := FTcpBlockSocket.LastError;
         TLog.NewLog(lterror,ClassName,'Exception sending stream to '+ClientRemoteAddr+': '+FTcpBlockSocket.GetErrorDescEx);
         TLog.NewLog(lterror,ClassName,'Exception sending stream to '+ClientRemoteAddr+': '+FTcpBlockSocket.GetErrorDescEx);
+        unlocked := true;
+        FSendBufferLock.Release;
         Disconnect;
         Disconnect;
       end;
       end;
     End;
     End;
   Finally
   Finally
-    FLock.Release;
-  End;
+    If not unlocked then FSendBufferLock.Release;
+  end;
   {$ENDIF}
   {$ENDIF}
   if Result>0 then FLastCommunicationTime := Now;
   if Result>0 then FLastCommunicationTime := Now;
 end;
 end;

+ 4 - 0
Units/PascalCoin/upcdaemon.pas

@@ -35,6 +35,8 @@ Const
   CT_INI_IDENT_MINER_B58_PUBLICKEY = 'RPC_SERVERMINER_B58_PUBKEY';
   CT_INI_IDENT_MINER_B58_PUBLICKEY = 'RPC_SERVERMINER_B58_PUBKEY';
   CT_INI_IDENT_MINER_NAME = 'RPC_SERVERMINER_NAME';
   CT_INI_IDENT_MINER_NAME = 'RPC_SERVERMINER_NAME';
   CT_INI_IDENT_MINER_MAX_CONNECTIONS = 'RPC_SERVERMINER_MAX_CONNECTIONS';
   CT_INI_IDENT_MINER_MAX_CONNECTIONS = 'RPC_SERVERMINER_MAX_CONNECTIONS';
+  CT_INI_IDENT_MINER_MAX_OPERATIONS_PER_BLOCK = 'RPC_SERVERMINER_MAX_OPERATIONS_PER_BLOCK';
+  CT_INI_IDENT_MINER_MAX_ZERO_FEE_OPERATIONS  = 'RPC_SERVERMINER_MAX_ZERO_FEE_OPERATIONS';
 
 
 Type
 Type
   { TPCDaemonThread }
   { TPCDaemonThread }
@@ -172,6 +174,8 @@ var
       FMinerServer.Port:=port;
       FMinerServer.Port:=port;
       FMinerServer.Active:=True;
       FMinerServer.Active:=True;
       FMinerServer.MaxConnections:=maxconnections;
       FMinerServer.MaxConnections:=maxconnections;
+      FMinerServer.MaxOperationsPerBlock := FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINER_MAX_OPERATIONS_PER_BLOCK,CT_MAX_Operations_per_block_by_miner);
+      FMinerServer.Max0FeeOperationsPerBlock := FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINER_MAX_ZERO_FEE_OPERATIONS,CT_MAX_0_fee_operations_per_block_by_miner);
     end else begin
     end else begin
       TLog.NewLog(ltinfo,ClassName,'RPC Miner Server NOT ACTIVE (Ini file is '+CT_INI_IDENT_RPC_SERVERMINER_PORT+'=0)');
       TLog.NewLog(ltinfo,ClassName,'RPC Miner Server NOT ACTIVE (Ini file is '+CT_INI_IDENT_RPC_SERVERMINER_PORT+'=0)');
     end;
     end;

+ 32 - 0
Units/Utils/UGridUtils.pas

@@ -47,6 +47,7 @@ Type
     FShowAllAccounts: Boolean;
     FShowAllAccounts: Boolean;
     FOnUpdated: TNotifyEvent;
     FOnUpdated: TNotifyEvent;
     FAccountsCount: Integer;
     FAccountsCount: Integer;
+    FAllowMultiSelect: Boolean;
     procedure SetDrawGrid(const Value: TDrawGrid);
     procedure SetDrawGrid(const Value: TDrawGrid);
     Procedure InitGrid;
     Procedure InitGrid;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewOperation(Sender : TObject);
@@ -54,6 +55,7 @@ Type
     procedure SetNode(const Value: TNode);
     procedure SetNode(const Value: TNode);
     function GetNode: TNode;
     function GetNode: TNode;
     procedure SetShowAllAccounts(const Value: Boolean);
     procedure SetShowAllAccounts(const Value: Boolean);
+    procedure SetAllowMultiSelect(const Value: Boolean);
   protected
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
     procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   public
   public
@@ -71,6 +73,8 @@ Type
     Property AccountsCount : Integer read FAccountsCount;
     Property AccountsCount : Integer read FAccountsCount;
     Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
     Function MoveRowToAccount(nAccount : Cardinal) : Boolean;
     Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     Property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
+    Property AllowMultiSelect : Boolean read FAllowMultiSelect write SetAllowMultiSelect;
+    Function SelectedAccounts(accounts : TOrderedCardinalList) : Integer;
   End;
   End;
 
 
   TOperationsGrid = Class(TComponent)
   TOperationsGrid = Class(TComponent)
@@ -195,6 +199,7 @@ constructor TAccountsGrid.Create(AOwner: TComponent);
 Var i : Integer;
 Var i : Integer;
 begin
 begin
   inherited;
   inherited;
+  FAllowMultiSelect := false;
   FOnUpdated := Nil;
   FOnUpdated := Nil;
   FAccountsBalance := 0;
   FAccountsBalance := 0;
   FAccountsCount := 0;
   FAccountsCount := 0;
@@ -261,6 +266,7 @@ begin
     {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
     {goRangeSelect, }goDrawFocusSelected, {goRowSizing, }goColSizing, {goRowMoving,}
     {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
     {goColMoving, goEditing, }goTabs, goRowSelect, {goAlwaysShowEditor,}
     goThumbTracking{$IFnDEF FPC}, goFixedColClick, goFixedRowClick, goFixedHotTrack{$ENDIF}];
     goThumbTracking{$IFnDEF FPC}, goFixedColClick, goFixedRowClick, goFixedHotTrack{$ENDIF}];
+  if FAllowMultiSelect then DrawGrid.Options := DrawGrid.Options + [goRangeSelect];
   FDrawGrid.Invalidate;
   FDrawGrid.Invalidate;
   if Assigned(FOnUpdated) then FOnUpdated(Self);
   if Assigned(FOnUpdated) then FOnUpdated(Self);
 end;
 end;
@@ -502,6 +508,32 @@ begin
   Stream.Write(j,sizeof(j));
   Stream.Write(j,sizeof(j));
 end;
 end;
 
 
+function TAccountsGrid.SelectedAccounts(accounts: TOrderedCardinalList): Integer;
+var i64 : Int64;
+  i : Integer;
+begin
+  accounts.Clear;
+  Result := 0;
+  if not assigned(FDrawGrid) then exit;
+  if FAllowMultiSelect then begin
+    for i := FDrawGrid.Selection.Top to FDrawGrid.Selection.Bottom do begin
+      i64 := AccountNumber(i);
+      if i64>=0 then accounts.Add(i64);
+    end;
+  end;
+  If accounts.Count=0 then begin
+    i64 := AccountNumber(DrawGrid.Row);
+    if i64>=0 then accounts.Add(i64);
+  end;
+  Result := accounts.Count;
+end;
+
+procedure TAccountsGrid.SetAllowMultiSelect(const Value: Boolean);
+begin
+  FAllowMultiSelect := Value;
+  InitGrid;
+end;
+
 procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
 procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
 begin
 begin
   if FDrawGrid=Value then exit;
   if FDrawGrid=Value then exit;

+ 7 - 0
pascalcoin_daemon.ini

@@ -30,3 +30,10 @@ RPC_SERVERMINER_B58_PUBKEY=
 ;RPC_SERVERMINER_MAX_CONNECTIONS : Integer
 ;RPC_SERVERMINER_MAX_CONNECTIONS : Integer
 ;Max connections that RPC Miner server can accept
 ;Max connections that RPC Miner server can accept
 RPC_SERVERMINER_MAX_CONNECTIONS=1000
 RPC_SERVERMINER_MAX_CONNECTIONS=1000
+;RPC_SERVERMINER_MAX_OPERATIONS_PER_BLOCK : Integer
+;Max operations included per block
+RPC_SERVERMINER_MAX_OPERATIONS_PER_BLOCK=5000
+;RPC_SERVERMINER_MAX_ZERO_FEE_OPERATIONS : Integer
+;Max operations without fee that can be included in a block
+;Note: Operations with fee>0 are processed first (have more priority)
+RPC_SERVERMINER_MAX_ZERO_FEE_OPERATIONS=4000