Browse Source

Fix: bulk change key operation now deals with fees correctly

Herman Schoenfeld 8 years ago
parent
commit
f3eaa527c7
3 changed files with 134 additions and 13 deletions
  1. 6 2
      PascalCoinWalletLazarus.lpi
  2. 34 11
      Units/Forms/UFRMOperation.pas
  3. 94 0
      Units/Utils/UCommon.pas

+ 6 - 2
PascalCoinWalletLazarus.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
 <CONFIG>
   <ProjectOptions>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="10"/>
     <PathDelim Value="\"/>
     <PathDelim Value="\"/>
     <General>
     <General>
       <Flags>
       <Flags>
@@ -38,7 +38,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="37">
+    <Units Count="38">
       <Unit0>
       <Unit0>
         <Filename Value="PascalCoinWalletLazarus.dpr"/>
         <Filename Value="PascalCoinWalletLazarus.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -214,6 +214,10 @@
         <Filename Value="Units\PascalCoin\UBaseTypes.pas"/>
         <Filename Value="Units\PascalCoin\UBaseTypes.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit36>
       </Unit36>
+      <Unit37>
+        <Filename Value="Units\Utils\UCommon.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit37>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>

+ 34 - 11
Units/Forms/UFRMOperation.pas

@@ -27,7 +27,7 @@ uses
 {$ENDIF}
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, UNode, UWalletKeys, UCrypto, Buttons, UBlockChain,
   Dialogs, StdCtrls, UNode, UWalletKeys, UCrypto, Buttons, UBlockChain,
-  UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types;
+  UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UCommon;
 
 
 Const
 Const
   CM_PC_WalletKeysChanged = WM_USER + 1;
   CM_PC_WalletKeysChanged = WM_USER + 1;
@@ -145,8 +145,7 @@ type
     FDefaultFee: Int64;
     FDefaultFee: Int64;
     FEncodedPayload : TRawBytes;
     FEncodedPayload : TRawBytes;
     FDisabled : Boolean;
     FDisabled : Boolean;
-    //
-    FSenderAccounts: TOrderedCardinalList;
+    FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     FOldOnChanged : TNotifyEvent;
     FOldOnChanged : TNotifyEvent;
     procedure SetWalletKeys(const Value: TWalletKeys);
     procedure SetWalletKeys(const Value: TWalletKeys);
     Procedure UpdateWalletKeys;
     Procedure UpdateWalletKeys;
@@ -198,22 +197,28 @@ Var errors : AnsiString;
   op : TPCOperation;
   op : TPCOperation;
   account,signerAccount,destAccount,accountToBuy : TAccount;
   account,signerAccount,destAccount,accountToBuy : TAccount;
   operation_to_string, operationstxt, auxs : String;
   operation_to_string, operationstxt, auxs : String;
-  _amount,_fee, _totalamount, _totalfee, _salePrice : Int64;
-  _lockedUntil : Cardinal;
+  _amount,_fee, _totalamount, _totalfee, _totalSignerFee, _salePrice : Int64;
+  _lockedUntil, _signer_n_ops : Cardinal;
   dooperation : Boolean;
   dooperation : Boolean;
   _newOwnerPublicKey : TECDSA_Public;
   _newOwnerPublicKey : TECDSA_Public;
   _newName : TRawBytes;
   _newName : TRawBytes;
   _newType : Word;
   _newType : Word;
-  _changeName, _changeType : Boolean;
+  _changeName, _changeType, _V2 : Boolean;
 begin
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
   ops := TOperationsHashTree.Create;
   ops := TOperationsHashTree.Create;
   Try
   Try
+    _V2 := FNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
     _totalamount := 0;
     _totalamount := 0;
     _totalfee := 0;
     _totalfee := 0;
+    _totalSignerFee := 0;
+    _signer_n_ops := 0;
     operationstxt := '';
     operationstxt := '';
     operation_to_string := '';
     operation_to_string := '';
+
+    // Loop through each sender account
+    // TODO: must process Signer account last if included in FSenderAccounts (not necessarily ordered enumeration)
     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));
@@ -227,10 +232,10 @@ begin
       wk := WalletKeys.Key[i];
       wk := WalletKeys.Key[i];
       dooperation := true;
       dooperation := true;
       // Default fee
       // Default fee
-      if account.balance>DefaultFee then _fee := DefaultFee
-      else _fee := account.balance;
-      //
+      if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
+      // Determine which operation type it is
       if PageControlOpType.ActivePage = tsTransaction then begin
       if PageControlOpType.ActivePage = tsTransaction then begin
+        {%region Operation: Transaction}
         if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
         if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
         if FSenderAccounts.Count>1 then begin
         if FSenderAccounts.Count>1 then begin
           if account.balance>0 then begin
           if account.balance>0 then begin
@@ -250,16 +255,26 @@ begin
           inc(_totalfee,_fee);
           inc(_totalfee,_fee);
         end;
         end;
         operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(destAccount.account);
         operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(destAccount.account);
+        {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
+        {%region Operation: Change Private Key}
         if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
-        If signerAccount.account<>account.account then begin
-          op := TOpChangeKeySigned.Create(signerAccount.account,signerAccount.n_operation+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+        if _V2 then begin
+          // Maintain correct signer fee distribution
+          if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
+          else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
+          else _fee := signerAccount.balance - uint64(_totalSignerFee);
+          op := TOpChangeKeySigned.Create(signerAccount.account,signerAccount.n_operation+_signer_n_ops+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+          inc(_signer_n_ops);
+          inc(_totalSignerFee, _fee);
         end else begin
         end else begin
           op := TOpChangeKey.Create(account.account,account.n_operation+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
           op := TOpChangeKey.Create(account.account,account.n_operation+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
         end;
         end;
         inc(_totalfee,_fee);
         inc(_totalfee,_fee);
         operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
         operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
+        {%endregion}
       end else if (PageControlOpType.ActivePage = tsListForSale) then begin
       end else if (PageControlOpType.ActivePage = tsListForSale) then begin
+        {%region Operation: List For Sale}
         If Not UpdateOpListForSale(account,_salePrice,destAccount,signerAccount,_newOwnerPublicKey,_lockedUntil,errors) then raise Exception.Create(errors);
         If Not UpdateOpListForSale(account,_salePrice,destAccount,signerAccount,_newOwnerPublicKey,_lockedUntil,errors) then raise Exception.Create(errors);
         // Special fee account:
         // Special fee account:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
@@ -269,22 +284,29 @@ begin
         end else if (rbListAccountForPrivateSale.Checked) then begin
         end else if (rbListAccountForPrivateSale.Checked) then begin
           op := TOpListAccountForSale.CreateListAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,wk.PrivateKey,FEncodedPayload);
           op := TOpListAccountForSale.CreateListAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,wk.PrivateKey,FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         end else raise Exception.Create('Select Sale type');
+        {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelist) then begin
       end else if (PageControlOpType.ActivePage = tsDelist) then begin
+        {%region Operation: Delist For Sale}
         if Not UpdateOpDelist(account,signerAccount,errors) then raise Exception.Create(errors);
         if Not UpdateOpDelist(account,signerAccount,errors) then raise Exception.Create(errors);
         // Special fee account:
         // Special fee account:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         else _fee := signerAccount.balance;
         op := TOpDelistAccountForSale.CreateDelistAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,wk.PrivateKey,FEncodedPayload);
         op := TOpDelistAccountForSale.CreateDelistAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,wk.PrivateKey,FEncodedPayload);
+        {%endregion}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
+        {%region Operation: Buy Account}
         if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         op := TOpBuyAccount.CreateBuy(account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
         op := TOpBuyAccount.CreateBuy(account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
           accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,wk.PrivateKey,FEncodedPayload);
           accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,wk.PrivateKey,FEncodedPayload);
+        {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
+        {%region Operation: Change Info}
         if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,errors) then raise Exception.Create(errors);
         if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,errors) then raise Exception.Create(errors);
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         else _fee := signerAccount.balance;
         op := TOpChangeAccountInfo.CreateChangeAccountInfo(signerAccount.account,signerAccount.n_operation+1,account.account,wk.PrivateKey,false,CT_TECDSA_Public_Nul,
         op := TOpChangeAccountInfo.CreateChangeAccountInfo(signerAccount.account,signerAccount.n_operation+1,account.account,wk.PrivateKey,false,CT_TECDSA_Public_Nul,
            _changeName,_newName,_changeType,_newType,_fee,FEncodedPayload);
            _changeName,_newName,_changeType,_newType,_fee,FEncodedPayload);
+        {%endregion}
       end else begin
       end else begin
         raise Exception.Create('No operation selected');
         raise Exception.Create('No operation selected');
       end;
       end;
@@ -295,6 +317,7 @@ begin
       end;
       end;
       FreeAndNil(op);
       FreeAndNil(op);
     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 (FSenderAccounts.Count>1) then begin
     if (FSenderAccounts.Count>1) then begin

+ 94 - 0
Units/Utils/UCommon.pas

@@ -0,0 +1,94 @@
+{ Copyright (c) 2017 PascalCoin Developers
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  CREDITS:
+  [2017-06-29] Herman Schoenfeld ([email protected]): Created unit, added IFF methods
+}
+
+unit UCommon;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{ Language-level tools }
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: UInt64): UInt64; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double): Double; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string; overload;
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
+
+implementation
+
+uses
+  Classes, SysUtils;
+
+{%region Language-level tools }
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: UInt64): UInt64;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double): Double;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+{%endregion}
+
+end.
+