Quellcode durchsuchen

Add: Seperated SendPASC Logic from wizard.

Ugochukwu Mmaduekwe vor 7 Jahren
Ursprung
Commit
9720deef90

+ 495 - 184
src/core.utils/UCoreUtils.pas

@@ -9,6 +9,7 @@ unit UCoreUtils;
 
   Acknowledgements:
     Herman Schoenfeld <[email protected]>: unit creator
+    Ugochukwu Mmaduekwe - added TOperationsManager Class
 }
 
 {$mode delphi}
@@ -16,225 +17,534 @@ unit UCoreUtils;
 interface
 
 uses
-  Classes, SysUtils, UCrypto, UAccounts, UBlockChain, UNode, UCommon, UNetProtocol,
-  Generics.Collections, Generics.Defaults, UCoreObjects;
+  Classes, SysUtils, UCrypto, UAccounts, UBlockChain, UOpTransaction, UNode, UCommon, UNetProtocol,
+  Generics.Collections, Generics.Defaults, UCoreObjects, UWIZModels, Forms, Dialogs, LCLType;
 
 type
 
-{ TAccountComparer }
+  { TAccountComparer }
 
-TAccountComparer = class (TComparer<TAccount>)
-  function Compare(constref ALeft, ARight: T): Integer; override;
-  class function DoCompare(constref ALeft, ARight : TAccount) : Integer; inline;
-end;
+  TAccountComparer = class(TComparer<TAccount>)
+    function Compare(constref ALeft, ARight: T): integer; override;
+    class function DoCompare(constref ALeft, ARight: TAccount): integer; inline;
+  end;
 
-{ TAccountEqualityComparer }
+  { TAccountEqualityComparer }
 
-TAccountEqualityComparer = class(TEqualityComparer<TAccount>)
+  TAccountEqualityComparer = class(TEqualityComparer<TAccount>)
   public
-    function Equals(constref ALeft, ARight: TAccount): Boolean; override;
+    function Equals(constref ALeft, ARight: TAccount): boolean; override;
     function GetHashCode(constref AValue: TAccount): UInt32; override;
-    class function AreEqual(constref ALeft, ARight: TAccount): Boolean;
+    class function AreEqual(constref ALeft, ARight: TAccount): boolean;
     class function CalcHashCode(constref AValue: TAccount): UInt32;
-end;
+  end;
 
-{ TAccountKeyComparer }
+  { TAccountKeyComparer }
 
-TAccountKeyComparer = class (TComparer<TAccountKey>)
-  function Compare(constref ALeft, ARight: T): Integer; override;
-  class function DoCompare(constref ALeft, ARight : TAccountKey) : Integer; inline;
-end;
+  TAccountKeyComparer = class(TComparer<TAccountKey>)
+    function Compare(constref ALeft, ARight: T): integer; override;
+    class function DoCompare(constref ALeft, ARight: TAccountKey): integer; inline;
+  end;
 
-{ TAccountKeyEqualityComparer }
+  { TAccountKeyEqualityComparer }
 
-TAccountKeyEqualityComparer = class(TEqualityComparer<TAccountKey>)
+  TAccountKeyEqualityComparer = class(TEqualityComparer<TAccountKey>)
   public
-    function Equals(constref ALeft, ARight: TAccountKey): Boolean; override;
+    function Equals(constref ALeft, ARight: TAccountKey): boolean; override;
     function GetHashCode(constref AValue: TAccountKey): UInt32; override;
-    class function AreEqual(constref ALeft, ARight: TAccountKey): Boolean;
+    class function AreEqual(constref ALeft, ARight: TAccountKey): boolean;
     class function CalcHashCode(constref AValue: TAccountKey): UInt32;
-end;
+  end;
 
-{ TCoreTool }
+  { TCoreTool }
 
-TCoreTool = class
-public
-  class function GetSignerCandidates(ANumOps : Integer; const ACandidates : array of TAccount) : TArray<TAccount>; static;
-end;
+  TCoreTool = class
+  public
+    class function GetSignerCandidates(ANumOps: integer; ASingleOperationFee: Int64; const ACandidates: array of TAccount): TArray<TAccount>; static;
+  end;
 
-{ TSafeBoxHelper }
 
-TSafeBoxHelper = class helper for TPCSafeBox
+  { TCoreTool }
+
+  { TOperationsManager }
+
+  TOperationsManager = class
   private
-    function GetKeysSummaryInternal(UseFilter: boolean; const AFilterKeys : array of TAccountKey; FetchAccounts : boolean = false) : TUserSummary;
+    class function FinalizeAndDisplayMessage(AOperationsTxt: string; const AOperationToString: string; ANoOfOperations: Integer; ATotalAmount, ATotalFee: int64; ANoOfAccounts: integer; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean; static;
+    class function UpdateSendPASCPayload(ASenderAccount, ADestinationAccount: TAccount; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
   public
-    function GetModifiedAccounts(const AAccounts : array of TAccount) : TArray<TAccount>;
-    function GetKeySummary(const AKey : TAccountKey; FetchAccounts : boolean = false) : TKeySummary;
-    function GetUserSummary(const AKeys : array of TAccountKey; FetchAccounts : boolean = false) : TUserSummary;
-    function GetSummaryAllKeys : TUserSummary;
-end;
+    class function ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; ADestinationAccount, ASignerAccount: TAccount; AAmount, AFee: int64; const ASendPASCMode: TWIZOperationsModel.TSendPASCMode; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
+    class procedure ExecuteChangeKey(); static;
+    class procedure ExecuteEnlistAccountForSale(); static;
+  end;
 
-{ TNodeHelper }
+  { TSafeBoxHelper }
+
+  TSafeBoxHelper = class helper for TPCSafeBox
+  private
+    function GetKeysSummaryInternal(UseFilter: boolean; const AFilterKeys: array of TAccountKey; FetchAccounts: boolean = False): TUserSummary;
+  public
+    function GetModifiedAccounts(const AAccounts: array of TAccount): TArray<TAccount>;
+    function GetKeySummary(const AKey: TAccountKey; FetchAccounts: boolean = False): TKeySummary;
+    function GetUserSummary(const AKeys: array of TAccountKey; FetchAccounts: boolean = False): TUserSummary;
+    function GetSummaryAllKeys: TUserSummary;
+  end;
+
+  { TNodeHelper }
+
+  TNodeHelper = class helper for TNode
+    function HasBestKnownBlockchainTip: boolean;
+  end;
+
+  { TAccountHelper }
+
+  TAccountHelper = record helper for TAccount
+    function GetAccountString : AnsiString;
+    function GetDisplayString : AnsiString;
+    function GetInfoText(const ABank : TPCBank) : utf8string;
+    property AccountString : AnsiString read GetAccountString;
+    property DisplayString : AnsiString read GetDisplayString;
+  end;
+
+  { TOperationResumeHelper }
+
+  TOperationResumeHelper = record helper for TOperationResume
+    function GetPrintableOPHASH : AnsiString;
+    function GetInfoText(const ABank : TPCBank) : utf8string;
+  end;
+
+  { TTimeSpanHelper }
 
-TNodeHelper = class helper for TNode
-  function HasBestKnownBlockchainTip : boolean;
+  TTimeSpanHelper = record helper for TTimeSpan
+    function TotalBlockCount : Integer;
+  end;
+
+implementation
+
+uses
+  UMemory, UConst, UWallet, UECIES, UAES;
+
+{ TOperationsManager }
+
+class function TOperationsManager.FinalizeAndDisplayMessage(
+  AOperationsTxt: string; const AOperationToString: string;
+  ANoOfOperations: Integer; ATotalAmount, ATotalFee: int64;
+  ANoOfAccounts: integer; AOperationsHashTree: TOperationsHashTree;
+  var AErrorMessage: string): boolean;
+var
+  auxs: string;
+  i: integer;
+begin
+  if (ANoOfAccounts > 1) then
+  begin
+    auxs := 'Total amount that dest will receive: ' + TAccountComp.FormatMoney(
+      ATotalAmount) + #10;
+    if Application.MessageBox(
+      PChar('Execute ' + IntToStr(ANoOfOperations) +
+      ' operations?' + #10 + 'Operation: ' + AOperationsTxt + #10 +
+      auxs + 'Total fee: ' + TAccountComp.FormatMoney(ATotalFee) +
+      #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
+  if Application.MessageBox(PChar('Execute this operation:' +
+    #10 + #10 + AOperationToString + #10 + #10 +
+    'Note: This operation will be transmitted to the network!'),
+    PChar(Application.Title), MB_YESNO + MB_ICONINFORMATION + MB_DEFBUTTON2) <>
+    idYes then
+    Exit;
+  Result := True;
+  i := TNode.Node.AddOperations(nil, AOperationsHashTree, nil, AErrorMessage);
+  if (i = AOperationsHashTree.OperationsCount) then
+  begin
+    AOperationsTxt := 'Successfully executed ' + IntToStr(i) +
+      ' operations!' + #10 + #10 + AOperationToString;
+    if i > 1 then
+      ShowMessage(AOperationsTxt)
+    else
+    begin
+      Application.MessageBox(
+        PChar('Successfully executed ' + IntToStr(i) + ' operations!' +
+        #10 + #10 + AOperationToString),
+        PChar(Application.Title), MB_OK + MB_ICONINFORMATION);
+    end;
+  end
+  else if (i > 0) then
+  begin
+    AOperationsTxt := 'One or more of your operations has not been executed:' +
+      #10 + 'Errors:' + #10 + AErrorMessage + #10 + #10 +
+      'Total successfully executed operations: ' + IntToStr(i);
+    ShowMessage(AOperationsTxt);
+  end
+  else
+    Result := False;
 end;
 
-{ TAccountHelper }
+class function TOperationsManager.UpdateSendPASCPayload(ASenderAccount, ADestinationAccount: TAccount; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
+var
+  LValid: boolean;
+  LWorkingAccount: TAccount;
+begin
+
+  if (APayloadContent = '') then
+    Exit(True);
+
+  LValid := False;
+  AErrorMessage := 'An Error Occured During Payload Encryption.';
+
+  try
+
+    case APayloadEncryptionMode of
+
+      akaEncryptWithSender:
+      begin
+        // Use sender account public key
+        LWorkingAccount := ASenderAccount;
+        AEncodedPayloadBytes := ECIESEncrypt(LWorkingAccount.accountInfo.accountKey, APayloadContent);
+        LValid := AEncodedPayloadBytes <> '';
+      end;
+
+      akaEncryptWithReceiver:
+      begin
+        // With destination account public key
+        LWorkingAccount := ADestinationAccount;
+        AEncodedPayloadBytes := ECIESEncrypt(LWorkingAccount.accountInfo.accountKey, APayloadContent);
+        LValid := AEncodedPayloadBytes <> '';
+      end;
+
+      akaEncryptWithPassword:
+      begin
+        // With defined password
+        if APayloadEncryptionPassword = '' then
+        begin
+          AErrorMessage := 'Payload Encryption Password Cannot be Empty with the Chosen Option : "Encrypt With Password."';
+          Exit(False);
+        end;
+        AEncodedPayloadBytes := TAESComp.EVP_Encrypt_AES256(
+          APayloadContent, APayloadEncryptionPassword);
+        LValid := AEncodedPayloadBytes <> '';
+      end;
 
-TAccountHelper = record helper for TAccount
-  function GetAccountString : AnsiString;
-  function GetDisplayString : AnsiString;
-  function GetInfoText(const ABank : TPCBank) : utf8string;
-  property AccountString : AnsiString read GetAccountString;
-  property DisplayString : AnsiString read GetDisplayString;
+      akaNotEncrypt:
+      begin
+        // no encryption
+        AEncodedPayloadBytes := APayloadContent;
+        LValid := True;
+      end
+
+      else
+      begin
+        AErrorMessage := 'Unknown Encryption Selected';
+        Exit(False);
+      end;
+    end;
+
+  finally
+    if LValid then
+      if Length(AEncodedPayloadBytes) > CT_MaxPayloadSize then
+      begin
+        LValid := False;
+        AErrorMessage := Format('Payload Size is %d Which is Bigger Than %d', [Length(AEncodedPayloadBytes), CT_MaxPayloadSize]);
+      end;
+    Result := LValid;
+  end;
 end;
 
-{ TOperationResumeHelper }
+class function TOperationsManager.ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; ADestinationAccount, ASignerAccount: TAccount; AAmount, AFee: int64; const ASendPASCMode: TWIZOperationsModel.TSendPASCMode; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
+var
+  LWalletKey: TWalletKey;
+  LWalletKeys: TWalletKeys;
+  LNode: TNode;
+  LPCOperation: TPCOperation;
+  LOperationsHashTree: TOperationsHashTree;
+  LTotalAmount, LTotalFee, LAmount, LFee: int64;
+  LDoOperation: boolean;
+  LOperationsTxt, LOperationToString: string;
+  LIdx, LAccountIdx, LNoOfOperations: integer;
+  LCurrentAccount: TAccount;
+  LPayloadEncodedBytes: TRawBytes;
+begin
+  if Length(ASelectedAccounts) = 0 then
+  begin
+    AErrorMessage := 'No Sender Account Found';
+    Exit(False);
+  end;
+
+  LWalletKeys := TWallet.Keys;
+  LNode := TNode.Node;
+
+  if not Assigned(LWalletKeys) then
+  begin
+    AErrorMessage := 'No Wallet Keys Found';
+    Exit(False);
+  end;
+
+  if not Assigned(LNode) then
+  begin
+    AErrorMessage := 'No Node Found';
+    Exit(False);
+  end;
+
+  LOperationsHashTree := TOperationsHashTree.Create;
+  try
+    LTotalAmount := 0;
+    LTotalFee := 0;
+    LNoOfOperations := 0;
+    LOperationsTxt := '';
+    LOperationToString := '';
+    for LAccountIdx := Low(ASelectedAccounts) to High(ASelectedAccounts) do
+    begin
+      LPCOperation := nil; // reset LPCOperation to Nil
+      LCurrentAccount := ASelectedAccounts[LAccountIdx];
+
+      if LCurrentAccount.account = ADestinationAccount.account then
+      begin
+        AErrorMessage := Format('Sender "%s" and Destination "%s" Accounts are the Same', [LCurrentAccount.AccountString, ADestinationAccount.AccountString]);
+        Exit(False);
+      end;
+
+      if not UpdateSendPASCPayload(LCurrentAccount, ADestinationAccount, APayloadEncryptionMode, APayloadContent, LPayloadEncodedBytes, APayloadEncryptionPassword, AErrorMessage) then
+      begin
+        AErrorMessage := Format('Error Encoding Payload Of Sender Account "%s. ", Specific Error Is "%s"', [LCurrentAccount.AccountString, AErrorMessage]);
+        Exit(False);
+      end;
+
+      LIdx := LWalletKeys.IndexOfAccountKey(LCurrentAccount.accountInfo.accountKey);
+      if LIdx < 0 then
+      begin
+        AErrorMessage := Format('Sender Account "%s" Private Key Not Found In Wallet', [LCurrentAccount.AccountString]);
+        Exit(False);
+      end;
+      LWalletKey := LWalletKeys.Key[LIdx];
+
+      if not Assigned(LWalletKey.PrivateKey) then
+      begin
+        if LWalletKey.HasPrivateKey then
+          AErrorMessage := 'Wallet is Password Protected. Please Unlock Before You Proceed.'
+        else
+          AErrorMessage := Format('Only Public Key of Account %s Was Found in Wallet. You Cannot Operate This Account', [LCurrentAccount.AccountString]);
+        Exit(False);
+      end;
+
+      LDoOperation := True;
+
+      if LCurrentAccount.balance > 0 then
+        case ASendPASCMode of
+          akaAllBalance:
+          begin
+            LAmount := LCurrentAccount.balance - AFee;
+            LFee := AFee;
+          end;
+
+          akaSpecifiedAmount:
+            if LCurrentAccount.balance >= UInt64(AAmount + AFee) then
+            begin
+              LAmount := AAmount;
+              LFee := AFee;
+            end
+            else
+            begin
+              AErrorMessage := Format('Insufficient Funds in "%s". %s < (%s + %s = %s)', [LCurrentAccount.AccountString, TAccountComp.FormatMoney(LCurrentAccount.balance), TAccountComp.FormatMoney(AAmount), TAccountComp.FormatMoney(AFee), TAccountComp.FormatMoney(AAmount + AFee)]);
+              Exit(False);
+            end;
+        end
+      else
+        LDoOperation := False;
+
+      if LDoOperation then
+      begin
+        LPCOperation := TOpTransaction.CreateTransaction(
+          LCurrentAccount.account, LCurrentAccount.n_operation + 1, ADestinationAccount.account, LWalletKey.PrivateKey, LAmount, LFee, LPayloadEncodedBytes);
+        try
+          Inc(LTotalAmount, LAmount);
+          Inc(LTotalFee, LFee);
+          Inc(LNoOfOperations);
+          LOperationsTxt := Format('Transaction to "%s"', [ADestinationAccount.AccountString]);
+
+          if Assigned(LPCOperation) then
+          begin
+            LOperationsHashTree.AddOperationToHashTree(LPCOperation);
+            if LOperationToString <> '' then
+              LOperationToString := LOperationToString + #10;
+            LOperationToString := LOperationToString + LPCOperation.ToString;
+          end;
+        finally
+          FreeAndNil(LPCOperation);
+        end;
+      end;
+
+    end;
+    if (LOperationsHashTree.OperationsCount = 0) then
+    begin
+      AErrorMessage := 'No Valid Operation to Execute';
+      Exit(False);
+    end;
+    // showmessage here
+    Exit(TOperationsManager.FinalizeAndDisplayMessage(LOperationsTxt, LOperationToString, LNoOfOperations, LTotalAmount, LTotalFee, Length(ASelectedAccounts), LOperationsHashTree, AErrorMessage));
+  finally
+    LOperationsHashTree.Free;
+  end;
 
-TOperationResumeHelper = record helper for TOperationResume
-  function GetPrintableOPHASH : AnsiString;
-  function GetInfoText(const ABank : TPCBank) : utf8string;
 end;
 
-{ TTimeSpanHelper }
+class procedure TOperationsManager.ExecuteChangeKey();
+begin
 
-TTimeSpanHelper = record helper for TTimeSpan
-  function TotalBlockCount : Integer;
 end;
 
-implementation
+class procedure TOperationsManager.ExecuteEnlistAccountForSale();
+begin
 
-uses
-  UMemory, UConst;
+end;
 
 { TCoreTool }
 
-class function TCoreTool.GetSignerCandidates(ANumOps : Integer; const ACandidates : array of TAccount) : TArray<TAccount>;
+class function TCoreTool.GetSignerCandidates(ANumOps: integer;
+  ASingleOperationFee: Int64; const ACandidates: array of TAccount): TArray<TAccount>;
 var
-  i: Integer;
-  Fee: Int64;
+  i, PoorSenderCount: integer;
+  Fee, maxSignerFee, minSignerFee: int64;
   acc: TAccount;
 begin
   //make deep copy of accounts!!! Very Important
   Result := TArrayTool<TAccount>.Copy(ACandidates);
-  Fee := ANumOps * CT_MOLINA;
+  Fee := ASingleOperationFee;
+  PoorSenderCount := 0;
+  for i := Low(Result) to High(Result) do
+  begin
+    acc := Result[i];
+    if (acc.Balance < Fee) then
+      Inc(PoorSenderCount);
+  end;
+
+  maxSignerFee := ANumOps * Fee;
+  minSignerFee := maxSignerFee - (PoorSenderCount * Fee);
+
   for i := High(Result) downto Low(Result) do
-    begin
-      acc := Result[i];
-      if not (acc.Balance >= Fee) then
-      begin
-        TArrayTool<TAccount>.RemoveAt(Result, i);
-      end;
-    end;
+  begin
+    acc := Result[i];
+    if not (acc.Balance >= maxSignerFee) then
+      TArrayTool<TAccount>.RemoveAt(Result, i);
+  end;
 end;
 
 { TNodeHelper }
 
-function TNodeHelper.HasBestKnownBlockchainTip : boolean;
+function TNodeHelper.HasBestKnownBlockchainTip: boolean;
 var
-  LReady : boolean;
-  LMsg : AnsiString;
+  LReady: boolean;
+  LMsg: ansistring;
 begin
   LReady := Self.Bank.IsReady(LMsg);
-  if LReady AND TNetData.NetData.IsGettingNewBlockChainFromClient then
+  if LReady and TNetData.NetData.IsGettingNewBlockChainFromClient then
     Result := Self.Bank.BlocksCount = TNetData.NetData.MaxRemoteOperationBlock.block;
 end;
 
 { TSafeBoxHelper }
 
-function TSafeBoxHelper.GetModifiedAccounts(const AAccounts : array of TAccount) : TArray<TAccount>;
+function TSafeBoxHelper.GetModifiedAccounts(const AAccounts: array of TAccount): TArray<TAccount>;
 var
-  i : integer;
-  LChanged : TList<TAccount>;
-  LAcc : TAccount;
-  GC : TDisposables;
+  i: integer;
+  LChanged: TList<TAccount>;
+  LAcc: TAccount;
+  GC: TDisposables;
 begin
-  LChanged := GC.AddObject( TList<TAccount>.Create ) as TList<TAccount>;
-  for i := Low(AAccounts) to High(AAccounts) do begin
+  LChanged := GC.AddObject(TList<TAccount>.Create) as TList<TAccount>;
+  for i := Low(AAccounts) to High(AAccounts) do
+  begin
     LAcc := Self.Account(AAccounts[i].account);
-    if (LAcc.n_Operation <> AAccounts[i].n_operation) OR (LAcc.Balance <> AAccounts[i].balance) then
+    if (LAcc.n_Operation <> AAccounts[i].n_operation) or (LAcc.Balance <> AAccounts[i].balance) then
       LChanged.Add(LAcc);
   end;
   Result := LChanged.ToArray;
 end;
 
-function TSafeBoxHelper.GetKeySummary(const AKey : TAccountKey; FetchAccounts : boolean = false) : TKeySummary;
+function TSafeBoxHelper.GetKeySummary(const AKey: TAccountKey; FetchAccounts: boolean = False): TKeySummary;
 var
-  AKeysResult : TUserSummary;
+  AKeysResult: TUserSummary;
 begin
   AKeysResult := GetUserSummary([AKey], FetchAccounts);
-  if Length(AKeysResult.Items) = 0 then begin
+  if Length(AKeysResult.Items) = 0 then
+  begin
     Result := CT_KeySummary_Nil;
     exit;
   end;
   Result := AKeysResult.Items[0];
 end;
 
-function TSafeBoxHelper.GetUserSummary(const AKeys : array of TAccountKey; FetchAccounts : boolean = false) : TUserSummary;
+function TSafeBoxHelper.GetUserSummary(const AKeys: array of TAccountKey; FetchAccounts: boolean = False): TUserSummary;
 begin
   Result := GetKeysSummaryInternal(True, AKeys, FetchAccounts);
 end;
 
-function TSafeBoxHelper.GetSummaryAllKeys : TUserSummary;
+function TSafeBoxHelper.GetSummaryAllKeys: TUserSummary;
 begin
   Result := GetKeysSummaryInternal(False, [], False);
 end;
 
-function TSafeBoxHelper.GetKeysSummaryInternal(UseFilter: boolean; const AFilterKeys : array of TAccountKey; FetchAccounts : boolean = false) : TUserSummary;
+function TSafeBoxHelper.GetKeysSummaryInternal(UseFilter: boolean; const AFilterKeys: array of TAccountKey; FetchAccounts: boolean = False): TUserSummary;
 type
   __TList_TAccount = TList<TAccount>;
   __TPair_TAccountKey_TList_TAccount = TPair<TAccountKey, __TList_TAccount>;
   __TObjectDictionary_TAccountKey_TList_TAccount = TObjectDictionary<TAccountKey, __TList_TAccount>;
 var
-  i,j : integer;
-  LAcc : TAccount;
-  LAccs : TSortedHashSet<TAccount>;
-  LKey : TAccountKey;
-  LValue : __TList_TAccount;
-  safeBox : TPCSafeBox;
-  GC : TDisposables;
-  LKeys : __TObjectDictionary_TAccountKey_TList_TAccount;
-  LPair : __TPair_TAccountKey_TList_TAccount;
+  i, j: integer;
+  LAcc: TAccount;
+  LAccs: TSortedHashSet<TAccount>;
+  LKey: TAccountKey;
+  LValue: __TList_TAccount;
+  safeBox: TPCSafeBox;
+  GC: TDisposables;
+  LKeys: __TObjectDictionary_TAccountKey_TList_TAccount;
+  LPair: __TPair_TAccountKey_TList_TAccount;
 begin
   // Setup local dictionary key -> account[]
-  LAccs := GC.AddObject( TSortedHashSet<TAccount>.Create( TAccountComparer.Create, TAccountEqualityComparer.Create ) ) as TSortedHashSet<TAccount>;
-  LKeys := GC.AddObject( __TObjectDictionary_TAccountKey_TList_TAccount.Create([doOwnsValues], TAccountKeyEqualityComparer.Create )) as __TObjectDictionary_TAccountKey_TList_TAccount;
+  LAccs := GC.AddObject(TSortedHashSet<TAccount>.Create(TAccountComparer.Create, TAccountEqualityComparer.Create)) as TSortedHashSet<TAccount>;
+  LKeys := GC.AddObject(__TObjectDictionary_TAccountKey_TList_TAccount.Create([doOwnsValues], TAccountKeyEqualityComparer.Create)) as __TObjectDictionary_TAccountKey_TList_TAccount;
 
-  if UseFilter then begin
+  if UseFilter then
+  begin
     for i := Low(AFilterKeys) to High(AFilterKeys) do
       LKeys.Add(AFilterKeys[i], __TList_TAccount.Create);
 
-    for i := 0 to Self.AccountsCount - 1 do begin
+    for i := 0 to Self.AccountsCount - 1 do
+    begin
       LAcc := Self.Account(i);
       if LKeys.TryGetValue(LAcc.accountInfo.accountKey, LValue) then
         LValue.Add(LAcc);
-    end
+    end;
   end
   else
-  for i := 0 to Self.AccountsCount - 1 do begin
-    LAcc := Self.Account(i);
-    if NOT LKeys.TryGetValue(LAcc.accountInfo.accountKey, LValue) then begin
-      LValue := __TList_TAccount.Create;
-      LKeys.Add(LAcc.accountInfo.accountKey, LValue);
-     end;
-     LValue.Add(LAcc);
-  end;
+    for i := 0 to Self.AccountsCount - 1 do
+    begin
+      LAcc := Self.Account(i);
+      if not LKeys.TryGetValue(LAcc.accountInfo.accountKey, LValue) then
+      begin
+        LValue := __TList_TAccount.Create;
+        LKeys.Add(LAcc.accountInfo.accountKey, LValue);
+      end;
+      LValue.Add(LAcc);
+    end;
 
   // Build the results
   SetLength(Result.Items, LKeys.Count);
   i := 0;
-  for LPair in LKeys do begin
+  for LPair in LKeys do
+  begin
     Result.Items[i].Key := CT_AccountInfo_NUL.accountKey;
-    Result.Items[i].TotalPASA:=0;
-    Result.Items[i].TotalPASC:=0;
+    Result.Items[i].TotalPASA := 0;
+    Result.Items[i].TotalPASC := 0;
     SetLength(Result.Items[i].Accounts, 0);
-    for j := 0 to LPair.Value.Count - 1 do begin
+    for j := 0 to LPair.Value.Count - 1 do
+    begin
       LAcc := LPair.Value[j];
       Inc(Result.Items[i].TotalPASA);
       Inc(Result.Items[i].TotalPASC, LAcc.balance);
     end;
-    if FetchAccounts then begin
+    if FetchAccounts then
+    begin
       Result.Items[i].Accounts := LPair.Value.ToArray;
       LAccs.AddRange(Result.Items[i].Accounts);
     end;
@@ -251,19 +561,19 @@ end;
 
 { TAccountComparer }
 
-function TAccountComparer.Compare(constref ALeft, ARight: TAccount): Integer;
+function TAccountComparer.Compare(constref ALeft, ARight: TAccount): integer;
 begin
   Result := TAccountComparer.DoCompare(ALeft, ARight);
 end;
 
-class function TAccountComparer.DoCompare(constref ALeft, ARight : TAccount) : Integer;
+class function TAccountComparer.DoCompare(constref ALeft, ARight: TAccount): integer;
 begin
   Result := TCompare.UInt64(ALeft.account, ARight.account);
 end;
 
 { TAccountEqualityComparer }
 
-function TAccountEqualityComparer.Equals(constref ALeft, ARight: TAccount): Boolean;
+function TAccountEqualityComparer.Equals(constref ALeft, ARight: TAccount): boolean;
 begin
   Result := TAccountEqualityComparer.AreEqual(ALeft, ARight);
 end;
@@ -273,13 +583,13 @@ begin
   Result := TAccountEqualityComparer.CalcHashCode(AValue);
 end;
 
-class function TAccountEqualityComparer.AreEqual(constref ALeft, ARight: TAccount): Boolean;
+class function TAccountEqualityComparer.AreEqual(constref ALeft, ARight: TAccount): boolean;
 begin
   Result :=
-    (ALeft.account = ARight.account) AND
-    (ALeft.balance = ARight.balance) AND
-    (ALeft.updated_block = ARight.updated_block) AND
-    (ALeft.n_operation = ARight.n_operation) AND
+    (ALeft.account = ARight.account) and
+    (ALeft.balance = ARight.balance) and
+    (ALeft.updated_block = ARight.updated_block) and
+    (ALeft.n_operation = ARight.n_operation) and
     TAccountKeyEqualityComparer.AreEqual(ALeft.accountInfo.accountKey, ARight.accountInfo.accountKey);
 end;
 
@@ -290,12 +600,12 @@ end;
 
 { TAccountKeyComparer }
 
-function TAccountKeyComparer.Compare(constref ALeft, ARight: T): Integer;
+function TAccountKeyComparer.Compare(constref ALeft, ARight: T): integer;
 begin
   Result := TAccountKeyComparer.DoCompare(ALeft, ARight);
 end;
 
-class function TAccountKeyComparer.DoCompare(constref ALeft, ARight : TAccountKey) : Integer;
+class function TAccountKeyComparer.DoCompare(constref ALeft, ARight: TAccountKey): integer;
 begin
   Result := BinStrComp(ALeft.x, ARight.x);
   if Result = 0 then
@@ -304,7 +614,7 @@ end;
 
 { TAccountKeyEqualityComparer }
 
-function TAccountKeyEqualityComparer.Equals(constref ALeft, ARight: TAccountKey): Boolean;
+function TAccountKeyEqualityComparer.Equals(constref ALeft, ARight: TAccountKey): boolean;
 begin
   Result := TAccountKeyEqualityComparer.AreEqual(ALeft, ARight);
 end;
@@ -314,111 +624,112 @@ begin
   Result := TAccountKeyEqualityComparer.CalcHashCode(AValue);
 end;
 
-class function TAccountKeyEqualityComparer.AreEqual(constref ALeft, ARight: TAccountKey): Boolean;
+class function TAccountKeyEqualityComparer.AreEqual(constref ALeft, ARight: TAccountKey): boolean;
 begin
   Result := TAccountKeyComparer.DoCompare(ALeft, ARight) = 0;
 end;
 
 class function TAccountKeyEqualityComparer.CalcHashCode(constref AValue: TAccountKey): UInt32;
 begin
-  Result := TEqualityComparer<AnsiString>.Default.GetHashCode(IntToStr(AValue.EC_OpenSSL_NID) + AValue.x + AValue.y  );
+  Result := TEqualityComparer<ansistring>.Default.GetHashCode(IntToStr(AValue.EC_OpenSSL_NID) + AValue.x + AValue.y);
 end;
 
 { TAccountHelper }
 
-function TAccountHelper.GetAccountString : AnsiString;
+function TAccountHelper.GetAccountString: ansistring;
 begin
   Result := TAccountComp.AccountNumberToAccountTxtNumber(Self.account);
 end;
 
-function TAccountHelper.GetDisplayString : AnsiString;
+function TAccountHelper.GetDisplayString: ansistring;
 begin
   Result := GetAccountString;
-  if Self.name <> '' then
-    Result := Result + ': ' + Self.name;
+  if Self.Name <> '' then
+    Result := Result + ': ' + Self.Name;
 end;
 
-function TAccountHelper.GetInfoText(const ABank : TPCBank) : utf8string;
+function TAccountHelper.GetInfoText(const ABank: TPCBank): utf8string;
 var
-  builder : TStrings;
-  GC : TDisposables;
+  builder: TStrings;
+  GC: TDisposables;
 begin
   builder := GC.AddObject(TStringList.Create) as TStrings;
-  builder.Append(Format('Account: %s %s Type:%d',[TAccountComp.AccountNumberToAccountTxtNumber(self.account), IIF(Self.name<>'','Name: '+Self.name,'') ,Self.account_type]));
-   builder.Append('');
-   builder.Append(Format('Current balance: %s',[TAccountComp.FormatMoney(Self.balance)]));
-   builder.Append('');
-   builder.Append(Format('Updated on block: %d  (%d blocks ago)',[Self.updated_block, ABank.BlocksCount-Self.updated_block]));
-   builder.Append(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(Self.accountInfo.accountKey.EC_OpenSSL_NID)]));
-   builder.Append(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(Self.accountInfo.accountKey)]));
-  if TAccountComp.IsAccountForSale(Self.accountInfo) then begin
-     builder.Append('');
-     builder.Append('** Account is for sale: **');
-     builder.Append(Format('Price: %s',[TAccountComp.FormatMoney(Self.accountInfo.price)]));
-     builder.Append(Format('Seller account (where to pay): %s',[TAccountComp.AccountNumberToAccountTxtNumber(Self.accountInfo.account_to_pay)]));
-    if TAccountComp.IsAccountForSaleAcceptingTransactions(Self.accountInfo) then begin
-       builder.Append('');
-       builder.Append('** Private sale **');
-       builder.Append(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(Self.accountInfo.new_publicKey)]));
-       builder.Append('');
-      if TAccountComp.IsAccountLocked(Self.accountInfo, ABank.BlocksCount) then begin
-         builder.Append(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
-          [Self.accountInfo.locked_until_block, ABank.BlocksCount,Self.accountInfo.locked_until_block - ABank.BlocksCount]));
-      end else begin
-         builder.Append(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
+  builder.Append(Format('Account: %s %s Type:%d', [TAccountComp.AccountNumberToAccountTxtNumber(self.account), IIF(Self.Name <> '', 'Name: ' + Self.Name, ''), Self.account_type]));
+  builder.Append('');
+  builder.Append(Format('Current balance: %s', [TAccountComp.FormatMoney(Self.balance)]));
+  builder.Append('');
+  builder.Append(Format('Updated on block: %d  (%d blocks ago)', [Self.updated_block, ABank.BlocksCount - Self.updated_block]));
+  builder.Append(Format('Public key type: %s', [TAccountComp.GetECInfoTxt(Self.accountInfo.accountKey.EC_OpenSSL_NID)]));
+  builder.Append(Format('Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.accountKey)]));
+  if TAccountComp.IsAccountForSale(Self.accountInfo) then
+  begin
+    builder.Append('');
+    builder.Append('** Account is for sale: **');
+    builder.Append(Format('Price: %s', [TAccountComp.FormatMoney(Self.accountInfo.price)]));
+    builder.Append(Format('Seller account (where to pay): %s', [TAccountComp.AccountNumberToAccountTxtNumber(Self.accountInfo.account_to_pay)]));
+    if TAccountComp.IsAccountForSaleAcceptingTransactions(Self.accountInfo) then
+    begin
+      builder.Append('');
+      builder.Append('** Private sale **');
+      builder.Append(Format('New Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.new_publicKey)]));
+      builder.Append('');
+      if TAccountComp.IsAccountLocked(Self.accountInfo, ABank.BlocksCount) then
+        builder.Append(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
+          [Self.accountInfo.locked_until_block, ABank.BlocksCount, Self.accountInfo.locked_until_block - ABank.BlocksCount]))
+      else
+        builder.Append(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
           [Self.accountInfo.locked_until_block, ABank.BlocksCount]));
-      end;
     end;
   end;
-  Result :=  builder.Text;
+  Result := builder.Text;
 end;
 
 { TOperationResumeHelper }
 
-function TOperationResumeHelper.GetPrintableOPHASH : AnsiString;
+function TOperationResumeHelper.GetPrintableOPHASH: ansistring;
 begin
   Result := TCrypto.ToHexaString(Self.OperationHash);
 end;
 
-function TOperationResumeHelper.GetInfoText(const ABank : TPCBank) : utf8string;
+function TOperationResumeHelper.GetInfoText(const ABank: TPCBank): utf8string;
 var
-  builder : TStrings;
-  GC : TDisposables;
+  builder: TStrings;
+  GC: TDisposables;
 begin
-  If (not Self.valid) then exit;
+  if (not Self.valid) then
+    exit;
   builder := GC.AddObject(TStringList.Create) as TStrings;
-  If Self.Block < ABank.BlocksCount then
-    if (Self.NOpInsideBlock>=0) then begin
-      builder.Add(Format('Block: %d/%d',[Self.Block,Self.NOpInsideBlock]))
-    end else begin
-      builder.Add(Format('Block: %d',[Self.Block]))
+  if Self.Block < ABank.BlocksCount then
+    if (Self.NOpInsideBlock >= 0) then
+      builder.Add(Format('Block: %d/%d', [Self.Block, Self.NOpInsideBlock]))
+    else
+    begin
+      builder.Add(Format('Block: %d', [Self.Block]));
     end
-  else builder.Add('** Pending operation not included on blockchain **');
-  builder.Add(Format('%s',[Self.OperationTxt]));
-  builder.Add(Format('OpType:%d Subtype:%d',[Self.OpType,Self.OpSubtype]));
-  builder.Add(Format('Operation Hash (ophash): %s',[TCrypto.ToHexaString(Self.OperationHash)]));
-  If (Self.OperationHash_OLD<>'') then begin
-    builder.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(Self.OperationHash_OLD)]));
-  end;
-  if (Self.OriginalPayload<>'') then begin
-    builder.Add(Format('Payload length:%d',[length(Self.OriginalPayload)]));
-    If Self.PrintablePayload<>'' then begin
-      builder.Add(Format('Payload (human): %s',[Self.PrintablePayload]));
-    end;
-    builder.Add(Format('Payload (Hexadecimal): %s',[TCrypto.ToHexaString(Self.OriginalPayload)]));
-  end;
-  If Self.Balance>=0 then begin
-    builder.Add(Format('Final balance: %s',[TAccountComp.FormatMoney(Self.Balance)]));
+  else
+    builder.Add('** Pending operation not included on blockchain **');
+  builder.Add(Format('%s', [Self.OperationTxt]));
+  builder.Add(Format('OpType:%d Subtype:%d', [Self.OpType, Self.OpSubtype]));
+  builder.Add(Format('Operation Hash (ophash): %s', [TCrypto.ToHexaString(Self.OperationHash)]));
+  if (Self.OperationHash_OLD <> '') then
+    builder.Add(Format('Old Operation Hash (old_ophash): %s', [TCrypto.ToHexaString(Self.OperationHash_OLD)]));
+  if (Self.OriginalPayload <> '') then
+  begin
+    builder.Add(Format('Payload length:%d', [length(Self.OriginalPayload)]));
+    if Self.PrintablePayload <> '' then
+      builder.Add(Format('Payload (human): %s', [Self.PrintablePayload]));
+    builder.Add(Format('Payload (Hexadecimal): %s', [TCrypto.ToHexaString(Self.OriginalPayload)]));
   end;
+  if Self.Balance >= 0 then
+    builder.Add(Format('Final balance: %s', [TAccountComp.FormatMoney(Self.Balance)]));
   Result := builder.Text;
 end;
 
 { TTimeSpanHelper }
 
-function TTimeSpanHelper.TotalBlockCount : Integer;
+function TTimeSpanHelper.TotalBlockCount: integer;
 begin
-  Result := Round( Self.TotalSeconds / CT_NewLineSecondsAvg );
+  Result := Round(Self.TotalSeconds / CT_NewLineSecondsAvg);
 end;
 
 end.
-

+ 0 - 1
src/gui/UCTRLWallet.lfm

@@ -10,7 +10,6 @@ object CTRLWallet: TCTRLWallet
   ClientWidth = 1151
   OnCreate = FormCreate
   OnResize = FormResize
-  LCLVersion = '1.8.2.0'
   Visible = False
   object PairSplitter1: TPairSplitter
     Left = 0

+ 12 - 11
src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.lfm

@@ -1,26 +1,27 @@
 object WIZChangeKey_ConfirmAccount: TWIZChangeKey_ConfirmAccount
   Left = 0
-  Height = 299
+  Height = 261
   Top = 0
   Width = 429
   Caption = 'ChangeKey_ConfirmAccount'
-  ClientHeight = 299
+  ClientHeight = 261
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gpChangeKey: TGroupBox
     Left = 4
-    Height = 294
+    Height = 256
     Top = 4
     Width = 420
     Anchors = [akTop, akLeft, akRight, akBottom]
     Caption = 'Confirm Accounts'
-    ClientHeight = 274
+    ClientHeight = 236
     ClientWidth = 416
     TabOrder = 0
     object paGrid: TPanel
       Left = 8
-      Height = 216
-      Top = 16
+      Height = 192
+      Top = 32
       Width = 400
       Anchors = [akTop, akLeft, akRight, akBottom]
       BevelOuter = bvNone
@@ -28,12 +29,12 @@ object WIZChangeKey_ConfirmAccount: TWIZChangeKey_ConfirmAccount
       TabOrder = 0
     end
     object lblTotalBalances: TLabel
-      Left = 184
+      Left = 192
       Height = 15
-      Top = 248
+      Top = 8
       Width = 116
       Alignment = taRightJustify
-      Anchors = [akRight, akBottom]
+      Anchors = [akTop, akRight]
       AutoSize = False
       Caption = 'Total Balance: '
       ParentColor = False
@@ -41,9 +42,9 @@ object WIZChangeKey_ConfirmAccount: TWIZChangeKey_ConfirmAccount
     object lblTotalBalanceValue: TLabel
       Left = 312
       Height = 15
-      Top = 248
+      Top = 8
       Width = 96
-      Anchors = [akRight, akBottom]
+      Anchors = [akTop, akRight]
       AutoSize = False
       Caption = 'Total Balance'
       Font.Color = clGreen

+ 1 - 1
src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.pas

@@ -119,7 +119,7 @@ function TWIZChangeKey_ConfirmAccount.Validate(out message: ansistring): boolean
 begin
   Result := True;
   // get signer accounts from selected accounts
-  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Account.SelectedAccounts);
+  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Fee.SingleOperationFee, Model.Account.SelectedAccounts);
 
   if Length(Model.Signer.SignerCandidates) < 1 then
   begin

+ 1 - 0
src/gui/wizards/operations/UWIZEnlistAccountForSale_ConfirmAccount.lfm

@@ -6,6 +6,7 @@ object WIZEnlistAccountForSale_ConfirmAccount: TWIZEnlistAccountForSale_ConfirmA
   Caption = 'WIZEnlistAccountForSale_ConfirmAccount'
   ClientHeight = 274
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object grpEnlistAccount: TGroupBox
     Left = 4

+ 1 - 1
src/gui/wizards/operations/UWIZEnlistAccountForSale_ConfirmAccount.pas

@@ -107,7 +107,7 @@ begin
     end;
 
   // get signer accounts from selected accounts
-  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Account.SelectedAccounts);
+  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Fee.SingleOperationFee, Model.Account.SelectedAccounts);
 
   if Length(Model.Signer.SignerCandidates) < 1 then
   begin

+ 1 - 0
src/gui/wizards/operations/UWIZOperationFee_Custom.lfm

@@ -7,6 +7,7 @@ object WIZOperationFee_Custom: TWIZOperationFee_Custom
   Caption = 'WIZOperationFee_Custom'
   ClientHeight = 253
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gbTransactionFee: TGroupBox
     Left = 14

+ 9 - 15
src/gui/wizards/operations/UWIZOperationFee_Custom.pas

@@ -38,7 +38,6 @@ type
 
 
 
-
   public
     procedure OnPresent; override;
     procedure OnNext; override;
@@ -51,7 +50,7 @@ implementation
 {$R *.lfm}
 
 uses
-  UAccounts, UUserInterface, USettings;
+  UAccounts, UCoreUtils, UUserInterface, USettings;
 
 { TWIZOperationFee_Custom }
 
@@ -76,8 +75,6 @@ end;
 
 procedure TWIZOperationFee_Custom.OnNext;
 begin
-  //TAccountComp.TxtToMoney(Trim(fseFee.ValueToStr(fseFee.Value)),
-  //  Model.Fee.SingleOperationFee);
   if Model.Payload.HasPayload then
     UpdatePath(ptInject, [TWIZOperationPayload_Encryption])
   else if Length(Model.Account.SelectedAccounts) > 1 then
@@ -106,27 +103,24 @@ begin
 
   Model.Fee.SingleOperationFee := opfee;
 
-   if Length(Model.Account.SelectedAccounts) > 1 then
-  begin
+  if Length(Model.Account.SelectedAccounts) > 1 then
     if not (Model.Fee.SingleOperationFee > 0) then
     begin
       message := 'zero fee only allowed for single operations.';
       Result := False;
       Exit;
     end;
-  end;
 
 
-  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  // get signer accounts from selected accounts
+  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Fee.SingleOperationFee, Model.Account.SelectedAccounts);
+
+  if Length(Model.Signer.SignerCandidates) < 1 then
   begin
-    acc := Model.Account.SelectedAccounts[i];
-    if acc.balance < Model.Fee.SingleOperationFee then
-    begin
-      message := 'Insufficient funds for fees in one or more accounts';
-      Result := False;
-      Exit;
-    end;
+    Result := False;
+    message := 'no valid signer account was found with the current requirements.';
   end;
+
 end;
 
 end.

+ 1 - 0
src/gui/wizards/operations/UWIZOperationPayload_Content.lfm

@@ -7,6 +7,7 @@ object WIZOperationPayload_Content: TWIZOperationPayload_Content
   Caption = 'Form1'
   ClientHeight = 253
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object grpPayload: TGroupBox
     Left = 8

+ 15 - 2
src/gui/wizards/operations/UWIZOperationPayload_Content.pas

@@ -32,6 +32,7 @@ type
   public
     procedure OnPresent; override;
     procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
   end;
 
 
@@ -40,7 +41,7 @@ implementation
 {$R *.lfm}
 
 uses
-  UAccounts, UUserInterface;
+  UAccounts, UConst, UUserInterface;
 
 { TWIZOperationPayload_Content }
 
@@ -54,10 +55,22 @@ begin
   Model.Payload.Content := mmoPayload.Lines.Text;
   if Length(Model.Account.SelectedAccounts) > 1 then
     UpdatePath(ptInject, [TWIZOperationSigner_Select])
-  else begin
+  else
+  begin
     Model.Signer.SignerAccount := Model.Account.SelectedAccounts[0];
     Model.Signer.OperationSigningMode := akaPrimary;
   end;
 end;
 
+function TWIZOperationPayload_Content.Validate(out message: ansistring): boolean;
+begin
+  Result := True;
+  if Length(mmoPayload.Lines.Text) > CT_MaxPayloadSize then
+  begin
+    message := 'Payload is Larger than Max Payload Size "255"';
+    Result := False;
+    Exit;
+  end;
+end;
+
 end.

+ 3 - 336
src/gui/wizards/operations/UWIZSendPASC.pas

@@ -15,18 +15,13 @@ unit UWIZSendPASC;
 interface
 
 uses
-  Classes, SysUtils, Forms, Dialogs, UCrypto, UWizard, UAccounts, LCLType, UWIZModels;
+  Classes, SysUtils, Forms, Dialogs, UWizard, UCommon, UWIZModels;
 
 type
 
   { TWIZSendPASCWizard }
 
   TWIZSendPASCWizard = class(TWizard<TWIZOperationsModel>)
-  private
-    function UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
-    function UpdateOperationOptions(var errors: string): boolean;
-    function UpdateOpTransaction(const SenderAccount: TAccount; var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
-    procedure SendPASC();
   public
     constructor Create(AOwner: TComponent); override;
     function DetermineHasNext: boolean; override;
@@ -38,13 +33,7 @@ type
 implementation
 
 uses
-  UBlockChain,
-  UOpTransaction,
-  UNode,
-  UConst,
-  UWallet,
-  UECIES,
-  UAES,
+  UCoreUtils,
   UWIZSendPASC_ConfirmSender,
   UWIZSendPASC_EnterRecipient,
   UWIZSendPASC_EnterQuantity,
@@ -52,319 +41,6 @@ uses
 
 { TWIZSendPASCWizard }
 
-function TWIZSendPASCWizard.UpdateOperationOptions(var errors: string): boolean;
-var
-  iAcc, iWallet: integer;
-  sender_account, dest_account: TAccount;
-  wk: TWalletKey;
-  e: string;
-  amount: int64;
-begin
-  Result := False;
-  errors := '';
-  if not Assigned(TWallet.Keys) then
-  begin
-    errors := 'No wallet keys';
-    Exit;
-  end;
-
-  if Length(Model.Account.SelectedAccounts) = 0 then
-  begin
-    errors := 'No sender account';
-    Exit;
-  end
-  else
-    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
-    begin
-      sender_account := Model.Account.SelectedAccounts[iAcc];
-      iWallet := TWallet.Keys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
-      if (iWallet < 0) then
-      begin
-        errors := 'Private key of account ' +
-          TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
-          ' not found in wallet';
-        Exit;
-      end;
-      wk := TWallet.Keys.Key[iWallet];
-      if not assigned(wk.PrivateKey) then
-      begin
-        if wk.CryptedKey <> '' then
-          errors := 'Wallet is password protected. Need password'// TODO: handle unlocking of encrypted wallet here
-
-        else
-          errors := 'Only public key of account ' +
-            TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
-            ' found in wallet. You cannot operate with this account';
-        Exit;
-      end;
-    end;
-
-  Result := UpdateOpTransaction(Model.Account.SelectedAccounts[0], dest_account, amount, errors);
-  UpdatePayload(sender_account, e);
-end;
-
-function TWIZSendPASCWizard.UpdateOpTransaction(const SenderAccount: TAccount; var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
-var
-  c: cardinal;
-  i: integer;
-begin
-  Result := False;
-  errors := '';
-
-  DestAccount := Model.SendPASC.DestinationAccount;
-
-  case Model.SendPASC.SendPASCMode of
-    akaAllBalance:
-      amount := 0;// ALL BALANCE
-
-
-    akaSpecifiedAmount:
-      amount := Model.SendPASC.SingleAmountToSend;
-  end;
-
-  if DestAccount.account = SenderAccount.account then
-  begin
-    errors := 'Sender and dest account are the same';
-    Exit;
-  end;
-
-  case Model.SendPASC.SendPASCMode of
-    akaSpecifiedAmount:
-      if (SenderAccount.balance < (amount + Model.Fee.SingleOperationFee)) then
-      begin
-        errors := 'Insufficient funds';
-        Exit;
-      end;
-  end;
-
-  Result := True;
-end;
-
-procedure TWIZSendPASCWizard.SendPASC();
-var
-  _V2, dooperation: boolean;
-  iAcc, i: integer;
-  _totalamount, _totalfee, _totalSignerFee, _amount, _fee: int64;
-  _signer_n_ops: cardinal;
-  operationstxt, operation_to_string, errors, auxs: string;
-  wk: TWalletKey;
-  ops: TOperationsHashTree;
-  op: TPCOperation;
-  account, destAccount: TAccount;
-begin
-  if not Assigned(TWallet.Keys) then
-    raise Exception.Create('No wallet keys');
-  if not UpdateOperationOptions(errors) then
-    raise Exception.Create(errors);
-  ops := TOperationsHashTree.Create;
-  try
-    _V2 := TNode.Node.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
-    _totalamount := 0;
-    _totalfee := 0;
-    _totalSignerFee := 0;
-    _signer_n_ops := 0;
-    operationstxt := '';
-    operation_to_string := '';
-    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
-    begin
-      op := nil;
-      account := Model.Account.SelectedAccounts[iAcc];
-      if not UpdatePayload(account, errors) then
-        raise Exception.Create('Error encoding payload of sender account ' +
-          TAccountComp.AccountNumberToAccountTxtNumber(account.account) + ': ' + errors);
-      i := TWallet.Keys.IndexOfAccountKey(account.accountInfo.accountKey);
-      if i < 0 then
-        raise Exception.Create('Sender account private key not found in Wallet');
-
-      wk := TWallet.Keys.Key[i];
-      dooperation := True;
-      // Default fee
-      if account.balance > uint64(Model.Fee.SingleOperationFee) then
-        _fee := Model.Fee.SingleOperationFee
-      else
-        _fee := account.balance;
-
-      if not UpdateOpTransaction(account, destAccount, _amount, errors) then
-        raise Exception.Create(errors);
-
-      if account.balance > 0 then
-      begin
-        if account.balance > Model.Fee.SingleOperationFee then
-          case Model.SendPASC.SendPASCMode of
-            akaAllBalance:
-            begin
-              _amount := account.balance - Model.Fee.SingleOperationFee;
-              _fee := Model.Fee.SingleOperationFee;
-            end;
-          end
-        else
-        begin
-          _amount := account.balance;
-          _fee := 0;
-        end;
-      end
-      else
-        dooperation := False;
-
-      if dooperation then
-      begin
-        op := TOpTransaction.CreateTransaction(
-          account.account, account.n_operation + 1, destAccount.account,
-          wk.PrivateKey, _amount, _fee, Model.Payload.EncodedBytes);
-        Inc(_totalamount, _amount);
-        Inc(_totalfee, _fee);
-      end;
-      operationstxt := 'Transaction to ' + TAccountComp.AccountNumberToAccountTxtNumber(
-        destAccount.account);
-
-      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;
-      FreeAndNil(op);
-    end;
-
-    if (ops.OperationsCount = 0) then
-      raise Exception.Create('No valid operation to execute');
-
-    if (Length(Model.Account.SelectedAccounts) > 1) then
-    begin
-      auxs := 'Total amount that dest will receive: ' + TAccountComp.FormatMoney(
-        _totalamount) + #10;
-      if Application.MessageBox(
-        PChar('Execute ' + IntToStr(Length(Model.Account.SelectedAccounts)) +
-        ' 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
-    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;
-    i := TNode.Node.AddOperations(nil, ops, nil, errors);
-    if (i = ops.OperationsCount) then
-    begin
-      operationstxt := 'Successfully executed ' + IntToStr(i) +
-        ' operations!' + #10 + #10 + operation_to_string;
-      if i > 1 then
-        ShowMessage(operationstxt)//  with TFRMMemoText.Create(Self) do
-      //    try
-      //      InitData(Application.Title, operationstxt);
-      //      ShowModal;
-      //    finally
-      //      Free;
-      //    end;
-
-      else
-      begin
-        Application.MessageBox(
-          PChar('Successfully executed ' + IntToStr(i) + ' operations!' +
-          #10 + #10 + operation_to_string),
-          PChar(Application.Title), MB_OK + MB_ICONINFORMATION);
-      end;
-      // ModalResult := mrOk;
-    end
-    else if (i > 0) then
-    begin
-      operationstxt := 'One or more of your operations has not been executed:' +
-        #10 + 'Errors:' + #10 + errors + #10 + #10 +
-        'Total successfully executed operations: ' + IntToStr(i);
-      //with TFRMMemoText.Create(Self) do
-      //  try
-      //    InitData(Application.Title, operationstxt);
-      //    ShowModal;
-      //  finally
-      //    Free;
-      //  end;
-      //ModalResult := mrOk;
-      ShowMessage(operationstxt);
-    end
-    else
-      raise Exception.Create(errors);
-
-  finally
-    ops.Free;
-  end;
-end;
-
-function TWIZSendPASCWizard.UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
-var
-  valid: boolean;
-  payload_encrypted, payload_u: string;
-  account: TAccount;
-begin
-  valid := False;
-  payload_encrypted := '';
-  Model.Payload.EncodedBytes := '';
-  errors := 'Unknown error';
-  payload_u := Model.Payload.Content;
-
-  try
-    if (payload_u = '') then
-    begin
-      valid := True;
-      Exit;
-    end;
-    case Model.Payload.Mode of
-
-      akaEncryptWithSender:
-      begin
-        // Use sender
-        errors := 'Error encrypting';
-        account := SenderAccount;
-        payload_encrypted := ECIESEncrypt(account.accountInfo.accountKey, payload_u);
-        valid := payload_encrypted <> '';
-      end;
-
-      akaEncryptWithReceiver:
-      begin
-        // With dest public key
-        errors := 'Error encrypting';
-        account := Model.SendPASC.DestinationAccount;
-        payload_encrypted := ECIESEncrypt(account.accountInfo.accountKey, payload_u);
-        valid := payload_encrypted <> '';
-      end;
-
-      akaEncryptWithPassword:
-      begin
-        payload_encrypted := TAESComp.EVP_Encrypt_AES256(
-          payload_u, Model.Payload.Password);
-        valid := payload_encrypted <> '';
-      end;
-
-      akaNotEncrypt:
-      begin
-        payload_encrypted := payload_u;
-        valid := True;
-      end
-
-      else
-        raise Exception.Create('Invalid Encryption Selection');
-    end;
-
-  finally
-    if valid then
-      if length(payload_encrypted) > CT_MaxPayloadSize then
-      begin
-        valid := False;
-        errors := 'Payload size is bigger than ' + IntToStr(CT_MaxPayloadSize) +
-          ' (' + IntToStr(length(payload_encrypted)) + ')';
-      end;
-    Model.Payload.EncodedBytes := payload_encrypted;
-    Result := valid;
-  end;
-
-end;
-
 constructor TWIZSendPASCWizard.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner,
@@ -392,16 +68,7 @@ end;
 function TWIZSendPASCWizard.FinishRequested(out message: ansistring): boolean;
 begin
   // Execute the PASC Sending here
-  try
-    Result := True;
-    SendPASC();
-  except
-    On E: Exception do
-    begin
-      Result := False;
-      message := E.ToString;
-    end;
-  end;
+  Result := TOperationsManager.ExecuteSendPASC(Model.Account.SelectedAccounts, Model.SendPASC.DestinationAccount, Model.Signer.SignerAccount, Model.SendPASC.SingleAmountToSend, Model.Fee.SingleOperationFee, Model.SendPASC.SendPASCMode, Model.Payload.Mode, IIF(Model.Payload.HasPayload, Model.Payload.Content, ''), Model.Payload.Password, message);
 end;
 
 function TWIZSendPASCWizard.CancelRequested(out message: ansistring): boolean;

+ 0 - 15
src/gui/wizards/operations/UWIZSendPASC_ConfirmSender.pas

@@ -31,7 +31,6 @@ type
   public
     procedure OnPresent; override;
     procedure OnNext; override;
-    function Validate(out message: ansistring): boolean; override;
   end;
 
 
@@ -100,20 +99,6 @@ begin
   UpdatePath(ptInject, [TWIZSendPASC_EnterRecipient]);
 end;
 
-function TWIZSendPASC_ConfirmSender.Validate(out message: ansistring): boolean;
-begin
-  Result := True;
-  // get signer accounts from selected accounts
-  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Account.SelectedAccounts);
-
-  if Length(Model.Signer.SignerCandidates) < 1 then
-  begin
-    Result := False;
-    message := 'no valid signer account was found.';
-  end;
-
-end;
-
 { TAccountSenderDataSource }
 
 procedure TAccountSenderDataSource.FetchAll(const AContainer: TList<TAccount>);

+ 0 - 1
src/gui/wizards/operations/UWIZSendPASC_EnterQuantity.lfm

@@ -7,7 +7,6 @@ object WIZSendPASC_EnterQuantity: TWIZSendPASC_EnterQuantity
   Caption = 'WIZSendPASC_EnterQuantity'
   ClientHeight = 253
   ClientWidth = 429
-  LCLVersion = '1.8.2.0'
   Visible = False
   object gbQuantity: TGroupBox
     Left = 16

+ 0 - 2
src/gui/wizards/operations/UWIZSendPASC_EnterQuantity.pas

@@ -90,8 +90,6 @@ begin
 end;
 
 procedure TWIZSendPASC_EnterQuantity.OnNext;
-var
-  amount: int64;
 begin
   Model.Payload.HasPayload := chkAttachPayload.Checked;
   if chkallfunds.Checked then

+ 0 - 1
src/gui/wizards/operations/UWIZSendPASC_EnterRecipient.lfm

@@ -7,7 +7,6 @@ object WIZSendPASC_EnterRecipient: TWIZSendPASC_EnterRecipient
   Caption = 'WIZSendPASC_EnterRecipient'
   ClientHeight = 253
   ClientWidth = 429
-  LCLVersion = '1.8.2.0'
   Visible = False
   object gbRecipient: TGroupBox
     Left = 16