Browse Source

Fix: bug when including signer account inside bulk operations

Herman Schoenfeld 7 years ago
parent
commit
c03e57c5e6
3 changed files with 56 additions and 10 deletions
  1. 18 7
      Units/Forms/UFRMOperation.pas
  2. 11 2
      Units/PascalCoin/UAccounts.pas
  3. 27 1
      Units/Utils/UCommon.pas

+ 18 - 7
Units/Forms/UFRMOperation.pas

@@ -203,7 +203,9 @@ Var errors : AnsiString;
   _newOwnerPublicKey : TECDSA_Public;
   _newName : TRawBytes;
   _newType : Word;
-  _changeName, _changeType, _V2 : Boolean;
+  _changeName, _changeType, _V2, _executeSigner  : Boolean;
+  _senderAccounts : array of Cardinal;
+label loop_start;
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
@@ -217,11 +219,14 @@ begin
     operationstxt := '';
     operation_to_string := '';
 
+    // Compile FSenderAccounts into a reorderable array
+    _senderAccounts := FSenderAccounts.ToArray;
+
     // 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 Length(_senderAccounts) - 1 do begin
+loop_start:
       op := Nil;
-      account := FNode.Operations.SafeBoxTransaction.Account(FSenderAccounts.Get(iAcc));
+      account := FNode.Operations.SafeBoxTransaction.Account(_senderAccounts[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.accountInfo.accountKey);
@@ -237,7 +242,7 @@ begin
       if PageControlOpType.ActivePage = tsTransaction then begin
         {%region Operation: Transaction}
         if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
-        if FSenderAccounts.Count>1 then begin
+        if Length(_senderAccounts) > 1 then begin
           if account.balance>0 then begin
             if account.balance>DefaultFee then begin
               _amount := account.balance - DefaultFee;
@@ -260,6 +265,12 @@ begin
         {%region Operation: Change Private Key}
         if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if _V2 then begin
+          // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+          if (iAcc < Length(_senderAccounts) - 1) AND (account.account = signerAccount.account) then begin
+            TArrayTool<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1); // ensure signer account processed last
+            goto loop_start; // TODO: remove ugly hack with refactoring!
+          end;
+
           // Maintain correct signer fee distribution
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
@@ -320,10 +331,10 @@ begin
 
     if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
 
-    if (FSenderAccounts.Count>1) then begin
+    if (Length(_senderAccounts)>1) then begin
       if PageControlOpType.ActivePage = tsTransaction then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
       else auxs:='';
-      if Application.MessageBox(PChar('Execute '+Inttostr(FSenderAccounts.Count)+' operations?'+#10+
+      if Application.MessageBox(PChar('Execute '+Inttostr(Length(_senderAccounts))+' operations?'+#10+
         'Operation: '+operationstxt+#10+
         auxs+
         'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),

+ 11 - 2
Units/PascalCoin/UAccounts.pas

@@ -142,6 +142,7 @@ Type
     accumulatedWork : UInt64; // Accumulated work (previous + target) this value can be calculated.
   end;
 
+  TCardinalsArray = Array of Cardinal;
 
   { Estimated TAccount size:
     4 + 200 (max aprox) + 8 + 4 + 4 = 220 max aprox
@@ -169,6 +170,7 @@ Type
     Procedure Enable;
     Property OnListChanged : TNotifyEvent read FOnListChanged write FOnListChanged;
     Procedure CopyFrom(Sender : TOrderedCardinalList);
+    Function ToArray : TCardinalsArray;
   End;
 
   TPCSafeBox = Class;
@@ -229,8 +231,6 @@ Type
   // happens only when a new BlockChain is included. After this, a new "SafeBoxHash"
   // is created, so each SafeBox has a unique SafeBoxHash
 
-  TCardinalsArray = Array of Cardinal;
-
   { TPCSafeBox }
 
   TPCSafeBox = Class
@@ -3182,6 +3182,15 @@ begin
   end;
 end;
 
+Function TOrderedCardinalList.ToArray : TCardinalsArray;
+var i : integer;
+begin
+  SetLength(Result, self.Count);
+  for i := 0 to self.Count - 1 do
+    Result[i] := Self.Get(i);
+end;
+
+
 { TOrderedRawList }
 
 Type TRawListData = Record

+ 27 - 1
Units/Utils/UCommon.pas

@@ -13,12 +13,14 @@
 
 unit UCommon;
 
+
 {$IFDEF FPC}
   {$MODE Delphi}
 {$ENDIF}
 
 interface
 
+
 { Converts a string to hexidecimal format }
 function String2Hex(const Buffer: AnsiString): AnsiString;
 
@@ -34,6 +36,14 @@ function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double)
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
 
+type
+  { TArrayTool }
+  TArrayTool<T> = class
+    public
+      class procedure Swap(var Values : array of T; Item1Index, Item2Index : SizeInt);
+    end;
+
+
 implementation
 
 uses
@@ -48,7 +58,6 @@ begin
     Result := LowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
 end;
 
-
 function BinStrComp(const Str1, Str2: AnsiString): integer;
 var Str1Len, Str2Len, i : Integer;
 begin
@@ -130,5 +139,22 @@ begin
 end;
 {%endregion}
 
+{%region TArrayTool }
+
+class procedure TArrayTool<T>.Swap(var Values : array of T; Item1Index, Item2Index : SizeInt);
+var temp : T; len, recSize : SizeInt; itemSize : SizeInt;
+begin
+  len := Length(Values);
+  recSize := SizeOf(T);
+  if (Item1Index < 0) OR (Item1Index > len) then Raise Exception.Create('Invalid Parameter: Item1Index out of bounds');
+  if (Item2Index < 0) OR (Item2Index > len) then Raise Exception.Create('Invalid Parameter: Item2Index out of bounds');
+  temp := Values[Item1Index];
+  Values[Item1Index] := Values[Item2Index];
+  Values[Item2Index] := temp;
+end;
+
+{%endregion}
+
+
 end.