Parcourir la source

Add: Seperated ChangeKey Logic from wizard.

Ugochukwu Mmaduekwe il y a 7 ans
Parent
commit
5a1f864906

+ 299 - 22
src/core.utils/UCoreUtils.pas

@@ -60,18 +60,21 @@ type
 
   TCoreTool = class
   public
-    class function GetSignerCandidates(ANumOps: integer; ASingleOperationFee: Int64; const ACandidates: array of TAccount): TArray<TAccount>; static;
+    class function GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>; static;
   end;
 
   { TOperationsManager }
 
   TOperationsManager = class
   private
-    class function SendPASCFinalizeAndDisplayMessage(AOperationsTxt: string; const AOperationToString: string; ANoOfOperations: Integer; ATotalAmount, ATotalFee: int64; 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;
+    class function SendPASCFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; ATotalAmount, ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean; static;
+    class function UpdateSendPASCPayload(const ASenderAccount, ADestinationAccount: TAccount; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
+
+    class function ChangeKeyFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; APublicKey: TAccountKey; ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean; static;
+    class function UpdateChangeKeyPayload(const ASenderAccount: TAccount; const APublicKey: TAccountKey; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
   public
-    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 function ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; const ADestinationAccount, ASignerAccount: TAccount; AAmount, AFee: int64; const ASendPASCMode: TWIZOperationsModel.TSendPASCMode; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
+    class function ExecuteChangeKey(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount: TAccount; APublicKey: TAccountKey; AFee: int64; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
     class procedure ExecuteEnlistAccountForSale(); static;
   end;
 
@@ -123,22 +126,20 @@ uses
 
 { TOperationsManager }
 
-class function TOperationsManager.SendPASCFinalizeAndDisplayMessage(
-  AOperationsTxt: string; const AOperationToString: string;
-  ANoOfOperations: Integer; ATotalAmount, ATotalFee: int64;
-  AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean;
+class function TOperationsManager.SendPASCFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; ATotalAmount, ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean;
 var
-  auxs: string;
+  LAuxs, LOperationsTxt: string;
   i: integer;
 begin
+  LOperationsTxt := AOperationsTxt;
   if (ANoOfOperations > 1) then
   begin
-    auxs := 'Total amount that dest will receive: ' + TAccountComp.FormatMoney(
+    LAuxs := '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) +
+      ' operations?' + #10 + 'Operation: ' + LOperationsTxt + #10 +
+      LAuxs + '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
@@ -155,10 +156,10 @@ begin
   i := TNode.Node.AddOperations(nil, AOperationsHashTree, nil, AErrorMessage);
   if (i = AOperationsHashTree.OperationsCount) then
   begin
-    AOperationsTxt := 'Successfully executed ' + IntToStr(i) +
+    LOperationsTxt := 'Successfully executed ' + IntToStr(i) +
       ' operations!' + #10 + #10 + AOperationToString;
     if i > 1 then
-      ShowMessage(AOperationsTxt)
+      ShowMessage(LOperationsTxt)
     else
     begin
       Application.MessageBox(
@@ -169,16 +170,16 @@ begin
   end
   else if (i > 0) then
   begin
-    AOperationsTxt := 'One or more of your operations has not been executed:' +
+    LOperationsTxt := 'One or more of your operations has not been executed:' +
       #10 + 'Errors:' + #10 + AErrorMessage + #10 + #10 +
       'Total successfully executed operations: ' + IntToStr(i);
-    ShowMessage(AOperationsTxt);
+    ShowMessage(LOperationsTxt);
   end
   else
     Result := False;
 end;
 
-class function TOperationsManager.UpdateSendPASCPayload(ASenderAccount, ADestinationAccount: TAccount; const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
+class function TOperationsManager.UpdateSendPASCPayload(const 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;
@@ -248,7 +249,128 @@ begin
   end;
 end;
 
-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;
+class function TOperationsManager.ChangeKeyFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; APublicKey: TAccountKey; ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean;
+var
+  LAuxs, LOperationsTxt: string;
+  i: integer;
+begin
+  LOperationsTxt := AOperationsTxt;
+  if (ANoOfOperations > 1) then
+  begin
+    LAuxs := '';
+    if Application.MessageBox(
+      PChar('Execute ' + IntToStr(ANoOfOperations) +
+      ' operations?' + #10 + 'Operation: ' + LOperationsTxt + #10 +
+      LAuxs + '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
+    LOperationsTxt := 'Successfully executed ' + IntToStr(i) +
+      ' operations!' + #10 + #10 + AOperationToString;
+    if i > 1 then
+      ShowMessage(LOperationsTxt)
+    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
+    LOperationsTxt := 'One or more of your operations has not been executed:' +
+      #10 + 'Errors:' + #10 + AErrorMessage + #10 + #10 +
+      'Total successfully executed operations: ' + IntToStr(i);
+    ShowMessage(LOperationsTxt);
+  end
+  else
+    Result := False;
+end;
+
+class function TOperationsManager.UpdateChangeKeyPayload(const ASenderAccount: TAccount; const APublicKey: TAccountKey; 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 public key
+        AEncodedPayloadBytes := ECIESEncrypt(APublicKey, 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;
+
+      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;
+
+class function TOperationsManager.ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; const 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;
@@ -385,8 +507,164 @@ begin
 
 end;
 
-class procedure TOperationsManager.ExecuteChangeKey();
+class function TOperationsManager.ExecuteChangeKey(
+  const ASelectedAccounts: TArray<TAccount>; const ASignerAccount: TAccount;
+  APublicKey: TAccountKey; AFee: int64;
+  const APayloadEncryptionMode: TWIZOperationsModel.TPayloadEncryptionMode;
+  const APayloadContent, APayloadEncryptionPassword: string;
+  var AErrorMessage: string): boolean;
+var
+  LWalletKey: TWalletKey;
+  LWalletKeys: TWalletKeys;
+  LNode: TNode;
+  LPCOperation: TPCOperation;
+  LOperationsHashTree: TOperationsHashTree;
+  LTotalSignerFee, LFee: int64;
+  LIsV2: boolean;
+  LOperationsTxt, LOperationToString: string;
+  LIdx, LAccountIdx, LSignerNoOfOperations: integer;
+  LCurrentAccount, LSignerAccount: TAccount;
+  LPayloadEncodedBytes: TRawBytes;
+  label loop_start;
 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
+    LIsV2 := LNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
+    LTotalSignerFee := 0;
+    LSignerNoOfOperations := 0;
+    LOperationsTxt := '';
+    LOperationToString := '';
+    for LAccountIdx := Low(ASelectedAccounts) to High(ASelectedAccounts) do
+    begin
+      loop_start:
+        LPCOperation := nil; // reset LPCOperation to Nil
+      LCurrentAccount := ASelectedAccounts[LAccountIdx];
+
+      if LNode.Bank.SafeBox.CurrentProtocol >= 1 then
+      begin
+        // Signer:
+        LSignerAccount := ASignerAccount;
+        if (TAccountComp.IsAccountLocked(LSignerAccount.accountInfo,
+          LNode.Bank.BlocksCount)) then
+        begin
+          AErrorMessage := Format('Signer Account "%s" is Locked Until Block %u', [LSignerAccount.AccountString, LSignerAccount.accountInfo.locked_until_block]);
+          Exit(False);
+        end;
+        if (not TAccountComp.EqualAccountKeys(
+          LSignerAccount.accountInfo.accountKey, LCurrentAccount.accountInfo.accountKey)) then
+        begin
+          AErrorMessage := Format('Signer Account %s is Not The Owner Of Account %s', [LSignerAccount.AccountString, LCurrentAccount.AccountString]);
+          Exit(False);
+        end;
+      end
+      else
+        LSignerAccount := LCurrentAccount;
+
+      if (TAccountComp.EqualAccountKeys(LCurrentAccount.accountInfo.accountKey,
+        APublicKey)) then
+      begin
+        AErrorMessage := 'New Key is Same as Current Key';
+        Exit(False);
+      end;
+
+      if not UpdateChangeKeyPayload(LCurrentAccount, APublicKey, 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;
+
+      if LIsV2 then
+      begin
+        // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+        if (LAccountIdx < Length(ASelectedAccounts) - 1) and
+          (LCurrentAccount.account = LSignerAccount.account) then
+        begin
+          TArrayTool<TAccount>.Swap(ASelectedAccounts, LAccountIdx,
+            Length(ASelectedAccounts) - 1); // ensure signer account processed last
+          goto loop_start; // TODO: remove ugly hack with refactoring!
+        end;
+
+        // Maintain correct signer fee distribution
+        if Uint64(LTotalSignerFee) >= LSignerAccount.balance then
+          LFee := 0
+        else if LSignerAccount.balance - uint64(LTotalSignerFee) >
+          UInt64(AFee) then
+          LFee := AFee
+        else
+          LFee := LSignerAccount.balance - UInt64(LTotalSignerFee);
+        LPCOperation := TOpChangeKeySigned.Create(LSignerAccount.account,
+          LsignerAccount.n_operation + LSignerNoOfOperations + 1, LCurrentAccount.account,
+          LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
+      end
+      else
+        LPCOperation := TOpChangeKey.Create(LCurrentAccount.account, LCurrentAccount.n_operation +
+          1, LCurrentAccount.account, LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
+
+      try
+        Inc(LSignerNoOfOperations);
+        Inc(LTotalSignerFee, LFee);
+        LOperationsTxt := Format('Change Key to "%s"', [TAccountComp.GetECInfoTxt(APublicKey.EC_OpenSSL_NID)]);
+        if Assigned(LPCOperation) then
+        begin
+          LOperationsHashTree.AddOperationToHashTree(LPCOperation);
+          if LOperationToString <> '' then
+            LOperationToString := LOperationToString + #10;
+          LOperationToString := LOperationToString + LPCOperation.ToString;
+        end;
+      finally
+        FreeAndNil(LPCOperation);
+      end;
+
+    end;
+
+    if (LOperationsHashTree.OperationsCount = 0) then
+    begin
+      AErrorMessage := 'No Valid Operation to Execute';
+      Exit(False);
+    end;
+
+    Exit(TOperationsManager.ChangeKeyFinalizeAndDisplayMessage(LOperationsTxt, LOperationToString, LSignerNoOfOperations, APublicKey, LTotalSignerFee, LOperationsHashTree, AErrorMessage));
+  finally
+    LOperationsHashTree.Free;
+  end;
 
 end;
 
@@ -397,8 +675,7 @@ end;
 
 { TCoreTool }
 
-class function TCoreTool.GetSignerCandidates(ANumOps: integer;
-  ASingleOperationFee: Int64; const ACandidates: array of TAccount): TArray<TAccount>;
+class function TCoreTool.GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>;
 var
   i, PoorSenderCount: integer;
   Fee, maxSignerFee, minSignerFee: int64;

+ 12 - 365
src/gui/wizards/operations/UWIZChangeKey.pas

@@ -22,11 +22,6 @@ type
   { TWIZChangeKeyWizard }
 
   TWIZChangeKeyWizard = class(TWizard<TWIZOperationsModel>)
-  private
-    function UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
-    function UpdateOperationOptions(var errors: string): boolean;
-    function UpdateOpChangeKey(const TargetAccount: TAccount; var SignerAccount: TAccount; var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
-    procedure WIZChangeKeyOwnership();
   public
     constructor Create(AOwner: TComponent); override;
     function DetermineHasNext: boolean; override;
@@ -38,363 +33,13 @@ type
 implementation
 
 uses
-  UBlockChain,
-  UOpTransaction,
-  UNode,
-  UConst,
-  UWallet,
-  UECIES,
-  UAES,
+  UCoreUtils,
   UWIZChangeKey_ConfirmAccount,
   UWIZChangeKey_SelectOption,
   UWIZChangeKey_Confirmation;
 
 { TWIZChangeKeyWizard }
 
-function TWIZChangeKeyWizard.UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
-var
-  valid: boolean;
-  payload_encrypted, payload_u: string;
-  account: TAccount;
-  public_key: TAccountKey;
-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
-
-        case Model.ChangeKey.ChangeKeyMode of
-          akaTransferAccountOwnership:
-            public_key := Model.TransferAccount.AccountKey;
-
-          akaChangeAccountPrivateKey:
-            public_key := Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey;
-
-        end;
-
-        errors := 'Public key: ' + 'Error encrypting';
-
-        if Model.TransferAccount.AccountKey.EC_OpenSSL_NID <>
-          CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then
-        begin
-          payload_encrypted := ECIESEncrypt(public_key, payload_u);
-          valid := payload_encrypted <> '';
-        end
-        else
-        begin
-          valid := False;
-          errors := 'Selected private key is not valid to encode';
-          exit;
-        end;
-      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;
-
-function TWIZChangeKeyWizard.UpdateOperationOptions(var errors: string): boolean;
-var
-  iAcc, iWallet: integer;
-  sender_account, signer_account: TAccount;
-  publicKey: TAccountKey;
-  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 := UpdateOpChangeKey(Model.Account.SelectedAccounts[0], signer_account,
-    publicKey, errors);
-  UpdatePayload(sender_account, e);
-end;
-
-function TWIZChangeKeyWizard.UpdateOpChangeKey(const TargetAccount: TAccount; var SignerAccount: TAccount; var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
-
-begin
-  Result := False;
-  errors := '';
-  try
-
-    case Model.ChangeKey.ChangeKeyMode of
-      akaTransferAccountOwnership:
-        NewPublicKey := Model.TransferAccount.AccountKey;
-
-      akaChangeAccountPrivateKey:
-        NewPublicKey := Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey;
-
-    end;
-
-    if TNode.Node.Bank.SafeBox.CurrentProtocol >= 1 then
-    begin
-      // Signer:
-      SignerAccount := Model.Signer.SignerAccount;
-      if (TAccountComp.IsAccountLocked(SignerAccount.accountInfo,
-        TNode.Node.Bank.BlocksCount)) then
-      begin
-        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
-          SignerAccount.account) + ' is locked until block ' + IntToStr(
-          SignerAccount.accountInfo.locked_until_block);
-        exit;
-      end;
-      if (not TAccountComp.EqualAccountKeys(
-        SignerAccount.accountInfo.accountKey, TargetAccount.accountInfo.accountKey)) then
-      begin
-        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
-          SignerAccount.account) + ' is not owner of account ' +
-          TAccountComp.AccountNumberToAccountTxtNumber(TargetAccount.account);
-        exit;
-      end;
-    end
-    else
-      SignerAccount := TargetAccount;
-
-    if (TAccountComp.EqualAccountKeys(TargetAccount.accountInfo.accountKey,
-      NewPublicKey)) then
-    begin
-      errors := 'New key is same as current key';
-      exit;
-    end;
-
-  finally
-    Result := errors = '';
-  end;
-end;
-
-procedure TWIZChangeKeyWizard.WIZChangeKeyOwnership();
-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, signerAccount: TAccount;
-  _newOwnerPublicKey: TECDSA_Public;
-label
-  loop_start;
-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
-      loop_start:
-        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.DefaultFee) then
-        _fee := Model.Fee.DefaultFee
-      else
-        _fee := account.balance;
-
-      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(Model.Account.SelectedAccounts) - 1) and
-          (account.account = signerAccount.account) then
-        begin
-          TArrayTool<TAccount>.Swap(Model.Account.SelectedAccounts, iAcc,
-            Length(Model.Account.SelectedAccounts) - 1); // ensure signer account processed last
-          // TArrayTool_internal<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1);
-          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(Model.Fee.DefaultFee) then
-          _fee := Model.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, Model.Payload.EncodedBytes);
-        Inc(_signer_n_ops);
-        Inc(_totalSignerFee, _fee);
-      end
-      else
-        op := TOpChangeKey.Create(account.account, account.n_operation +
-          1, account.account, wk.PrivateKey, _newOwnerPublicKey, _fee, Model.Payload.EncodedBytes);
-      Inc(_totalfee, _fee);
-      operationstxt :=
-        'Change private key to ' + TAccountComp.GetECInfoTxt(
-        _newOwnerPublicKey.EC_OpenSSL_NID);
-
-      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 := '';
-      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)
-      else
-      begin
-        Application.MessageBox(
-          PChar('Successfully executed ' + IntToStr(i) + ' operations!' +
-          #10 + #10 + operation_to_string),
-          PChar(Application.Title), MB_OK + MB_ICONINFORMATION);
-      end;
-
-    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);
-
-      ShowMessage(operationstxt);
-    end
-    else
-      raise Exception.Create(errors);
-
-
-  finally
-    ops.Free;
-  end;
-
-end;
-
 constructor TWIZChangeKeyWizard.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner,
@@ -419,18 +64,20 @@ begin
 end;
 
 function TWIZChangeKeyWizard.FinishRequested(out message: ansistring): boolean;
+var
+  LPublicKey: TAccountKey;
 begin
   // Execute the Change Key Action here
-  try
-    Result := True;
-    WIZChangeKeyOwnership();
-  except
-    On E: Exception do
-    begin
-      Result := False;
-      message := E.ToString;
-    end;
+  case Model.ChangeKey.ChangeKeyMode of
+    akaTransferAccountOwnership:
+      LPublicKey := Model.TransferAccount.AccountKey;
+
+    akaChangeAccountPrivateKey:
+      LPublicKey := Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey;
+
   end;
+
+  Result := TOperationsManager.ExecuteChangeKey(Model.Account.SelectedAccounts, Model.Signer.SignerAccount, LPublicKey, Model.Fee.SingleOperationFee, Model.Payload.Mode, IIF(Model.Payload.HasPayload, Model.Payload.Content, ''), Model.Payload.Password, message);
 end;
 
 function TWIZChangeKeyWizard.CancelRequested(out message: ansistring): boolean;

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

@@ -7,6 +7,7 @@ object WIZChangeKey_EnterKey: TWIZChangeKey_EnterKey
   Caption = 'ChangeKey_EnterKey'
   ClientHeight = 291
   ClientWidth = 510
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gbNewPublicKey: TGroupBox
     Left = 4

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

@@ -88,7 +88,7 @@ var
   i: integer;
 begin
   Result := True;
-  if not TAccountComp.AccountKeyFromImport(mmoNewPrivateKey.Lines.Text,
+  if not TAccountComp.AccountKeyFromImport(Trim(mmoNewPrivateKey.Lines.Text),
     tempAccountKey, message) then
   begin
     Result := False;