Browse Source

implemented change account private key wizard.

Ugochukwu Mmaduekwe 7 years ago
parent
commit
7fe7d9f5b0

+ 21 - 3
src/gui/UCTRLWallet.pas

@@ -9,7 +9,8 @@ interface
 uses
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,
   ExtCtrls, PairSplitter, Buttons, UVisualGrid, UCommon.UI, Generics.Collections,
   ExtCtrls, PairSplitter, Buttons, UVisualGrid, UCommon.UI, Generics.Collections,
-  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZTransferAccount;
+  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZTransferAccount,
+  UWIZChangeAccountPrivateKey;
 
 
 type
 type
 
 
@@ -383,7 +384,8 @@ end;
 //  Result := ARow.Account;
 //  Result := ARow.Account;
 //end;
 //end;
 
 
-function TCTRLWallet.GetAccounts(const AccountNumbers: TArray<cardinal>): TArray<TAccount>;
+function TCTRLWallet.GetAccounts(
+  const AccountNumbers: TArray<cardinal>): TArray<TAccount>;
 var
 var
   acc: TAccount;
   acc: TAccount;
   safeBox: TPCSafeBox;
   safeBox: TPCSafeBox;
@@ -660,8 +662,24 @@ begin
 end;
 end;
 
 
 procedure TCTRLWallet.miChangeAccountsPrivateKeyClick(Sender: TObject);
 procedure TCTRLWallet.miChangeAccountsPrivateKeyClick(Sender: TObject);
+var
+  Scoped: TDisposables;
+  wiz: TWIZChangeAccountPrivateKeyWizard;
+  model: TWIZChangeAccountPrivateKeyModel;
+  AccountNumbersWithoutChecksum: TArray<cardinal>;
 begin
 begin
-  raise ENotImplemented.Create('Not Implemented');
+  wiz := Scoped.AddObject(TWIZChangeAccountPrivateKeyWizard.Create(nil)) as
+    TWIZChangeAccountPrivateKeyWizard;
+  model := Scoped.AddObject(TWIZChangeAccountPrivateKeyModel.Create(nil)) as
+    TWIZChangeAccountPrivateKeyModel;
+
+  AccountNumbersWithoutChecksum :=
+    TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows,
+    GetAccNoWithoutChecksum);
+
+  model.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
+  model.SelectedIndex := 0;
+  wiz.Start(model);
 end;
 end;
 
 
 procedure TCTRLWallet.OnPrepareOperationsPopupMenu(Sender: TObject;
 procedure TCTRLWallet.OnPrepareOperationsPopupMenu(Sender: TObject;

+ 474 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey.pas

@@ -0,0 +1,474 @@
+unit UWIZChangeAccountPrivateKey;
+
+{$mode delphi}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Dialogs, UCrypto, UWallet, UCommon, UWizard,
+  UAccounts, LCLType;
+
+type
+
+  { TWIZChangeAccountPrivateKeyModel }
+  TWIZPayloadEncryptionMode = (akaEncryptWithOldEC, akaEncryptWithEC,
+    akaEncryptWithPassword, akaNotEncrypt);
+
+  TWIZChangeAccountPrivateKeyModel = class(TComponent)
+  public
+    DefaultFee: int64;
+    NewPublicKey, Payload, EncryptionPassword: string;
+    SelectedIndex, PrivateKeySelectedIndex: integer;
+    NewWalletKey: TWalletKey;
+    EncodedPayload: TRawBytes;
+    SignerAccount: TAccount;
+    SelectedAccounts: TArray<TAccount>;
+    PayloadEncryptionMode: TWIZPayloadEncryptionMode;
+  end;
+
+  { TWIZChangeAccountPrivateKeyWizard }
+
+  TWIZChangeAccountPrivateKeyWizard = class(TWizard<TWIZChangeAccountPrivateKeyModel>)
+  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 ChangeAccountPrivateKey();
+  public
+    constructor Create(AOwner: TComponent); override;
+    function DetermineHasNext: boolean; override;
+    function DetermineHasPrevious: boolean; override;
+    function FinishRequested(out message: ansistring): boolean; override;
+    function CancelRequested(out message: ansistring): boolean; override;
+  end;
+
+implementation
+
+uses
+  UBlockChain,
+  UOpTransaction,
+  UNode,
+  UConst,
+  UECIES,
+  UAES,
+  UWIZChangeAccountPrivateKey_Start,
+  UWIZChangeAccountPrivateKey_Confirmation;
+
+{ TWIZChangeAccountPrivateKeyWizard }
+
+function TWIZChangeAccountPrivateKeyWizard.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.EncodedPayload := '';
+  errors := 'Unknown error';
+  payload_u := Model.Payload;
+
+  try
+    if (payload_u = '') then
+    begin
+      valid := True;
+      Exit;
+    end;
+    case Model.PayloadEncryptionMode of
+
+      akaEncryptWithOldEC:
+      begin
+        // Use sender
+        errors := 'Error encrypting';
+        account := SenderAccount;
+        payload_encrypted := ECIESEncrypt(account.accountInfo.accountKey, payload_u);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaEncryptWithEC:
+      begin
+        errors := 'Error encrypting';
+
+        public_key := Model.NewWalletKey.AccountKey;
+        if public_key.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.EncryptionPassword);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaNotEncrypt:
+      begin
+        payload_encrypted := payload_u;
+        valid := True;
+      end
+
+      else
+      begin
+        raise Exception.Create('Invalid Encryption Selection');
+      end;
+    end;
+
+  finally
+    if valid then
+    begin
+      if length(payload_encrypted) > CT_MaxPayloadSize then
+      begin
+        valid := False;
+        errors := 'Payload size is bigger than ' + IntToStr(CT_MaxPayloadSize) +
+          ' (' + IntToStr(length(payload_encrypted)) + ')';
+      end;
+
+    end;
+    Model.EncodedPayload := payload_encrypted;
+    Result := valid;
+  end;
+
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.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.SelectedAccounts) = 0 then
+  begin
+    errors := 'No sender account';
+    Exit;
+  end
+  else
+  begin
+
+    for iAcc := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+    begin
+      sender_account := Model.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
+        begin
+          // TODO: handle unlocking of encrypted wallet here
+          errors := 'Wallet is password protected. Need password';
+        end
+        else
+        begin
+          errors := 'Only public key of account ' +
+            TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
+            ' found in wallet. You cannot operate with this account';
+        end;
+        Exit;
+      end;
+    end;
+  end;
+
+  Result := UpdateOpChangeKey(Model.SelectedAccounts[0], signer_account,
+    publicKey, errors);
+  UpdatePayload(sender_account, e);
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.UpdateOpChangeKey(
+  const TargetAccount: TAccount; var SignerAccount: TAccount;
+  var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
+begin
+  Result := False;
+  errors := '';
+  try
+    NewPublicKey := Model.NewWalletKey.AccountKey;
+
+    if TNode.Node.Bank.SafeBox.CurrentProtocol >= 1 then
+    begin
+      // Signer:
+      SignerAccount := Model.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
+    begin
+      SignerAccount := TargetAccount;
+    end;
+
+    if (TAccountComp.EqualAccountKeys(TargetAccount.accountInfo.accountKey,
+      NewPublicKey)) then
+    begin
+      errors := 'New public key is the same public key';
+      exit;
+    end;
+
+  finally
+    Result := errors = '';
+  end;
+end;
+
+procedure TWIZChangeAccountPrivateKeyWizard.ChangeAccountPrivateKey();
+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.SelectedAccounts) to High(Model.SelectedAccounts) do
+    begin
+      loop_start:
+        op := nil;
+      account := Model.SelectedAccounts[iAcc];
+      if not UpdatePayload(account, errors) then
+      begin
+        raise Exception.Create('Error encoding payload of sender account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(account.account) + ': ' + errors);
+      end;
+      i := TWallet.Keys.IndexOfAccountKey(account.accountInfo.accountKey);
+      if i < 0 then
+      begin
+        raise Exception.Create('Sender account private key not found in Wallet');
+      end;
+
+      wk := TWallet.Keys.Key[i];
+      dooperation := True;
+      // Default fee
+      if account.balance > uint64(Model.DefaultFee) then
+        _fee := Model.DefaultFee
+      else
+        _fee := account.balance;
+
+      if not UpdateOpChangeKey(account, signerAccount, _newOwnerPublicKey, errors) then
+      begin
+        raise Exception.Create(errors);
+      end;
+      if _V2 then
+      begin
+        // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+        if (iAcc < Length(Model.SelectedAccounts) - 1) and
+          (account.account = signerAccount.account) then
+        begin
+          TArrayTool<TAccount>.Swap(Model.SelectedAccounts, iAcc,
+            Length(Model.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.DefaultFee) then
+          _fee := Model.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.EncodedPayload);
+        Inc(_signer_n_ops);
+        Inc(_totalSignerFee, _fee);
+      end
+      else
+      begin
+        op := TOpChangeKey.Create(account.account, account.n_operation +
+          1, account.account, wk.PrivateKey, _newOwnerPublicKey, _fee,
+          Model.EncodedPayload);
+      end;
+      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.SelectedAccounts) > 1) then
+    begin
+      auxs := '';
+      if Application.MessageBox(
+        PChar('Execute ' + IntToStr(Length(Model.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
+      begin
+        Exit;
+      end;
+    end
+    else
+    begin
+      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
+      begin
+        Exit;
+      end;
+    end;
+    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
+      begin
+
+        ShowMessage(operationstxt);
+      end
+      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
+    begin
+      raise Exception.Create(errors);
+    end;
+
+
+  finally
+    ops.Free;
+  end;
+
+end;
+
+constructor TWIZChangeAccountPrivateKeyWizard.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner, [TWIZChangeAccountPrivateKey_Start,
+    TWIZChangeAccountPrivateKey_Confirmation]);
+  TitleText := 'Change Key';
+  FinishText := 'Change Key';
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.DetermineHasNext: boolean;
+begin
+  Result := not (CurrentScreen is TWIZChangeAccountPrivateKey_Confirmation);
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.DetermineHasPrevious: boolean;
+begin
+  Result := inherited DetermineHasPrevious;
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.FinishRequested(
+  out message: ansistring): boolean;
+begin
+  // Execute the Change Account Private Key Action here
+  try
+    Result := True;
+    ChangeAccountPrivateKey();
+  except
+    On E: Exception do
+    begin
+      Result := False;
+      message := E.ToString;
+    end;
+  end;
+end;
+
+function TWIZChangeAccountPrivateKeyWizard.CancelRequested(
+  out message: ansistring): boolean;
+begin
+  Result := True;
+end;
+
+end.

+ 48 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Confirmation.lfm

@@ -0,0 +1,48 @@
+object WIZChangeAccountPrivateKey_Confirmation: TWIZChangeAccountPrivateKey_Confirmation
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  Caption = 'WIZChangeAccountPrivateKey_Confirmation'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.1.0'
+  Visible = False
+  object GroupBox1: TGroupBox
+    Left = 10
+    Height = 232
+    Top = 8
+    Width = 408
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Caption = 'Confirm Transfer Transaction'
+    ClientHeight = 212
+    ClientWidth = 404
+    TabOrder = 0
+    object paGrid: TPanel
+      Left = 8
+      Height = 152
+      Top = 48
+      Width = 382
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BevelOuter = bvNone
+      Caption = 'GRID PANEL'
+      TabOrder = 0
+    end
+    object Label1: TLabel
+      Left = 8
+      Height = 15
+      Top = 16
+      Width = 84
+      Caption = 'Signer Account:'
+      ParentColor = False
+    end
+    object lblSgnAcc: TLabel
+      Left = 112
+      Height = 15
+      Top = 16
+      Width = 53
+      Caption = 'lblSgnAcc'
+      ParentColor = False
+    end
+  end
+end

+ 146 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Confirmation.pas

@@ -0,0 +1,146 @@
+unit UWIZChangeAccountPrivateKey_Confirmation;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, UVisualGrid, UCellRenderers, UCommon.Data, UWizard,
+  UWIZChangeAccountPrivateKey;
+
+type
+
+  { TWIZChangeAccountPrivateKey_Confirmation }
+
+  TWIZChangeAccountPrivateKey_Confirmation =
+    class(TWizardForm<TWIZChangeAccountPrivateKeyModel>)
+    GroupBox1: TGroupBox;
+    Label1: TLabel;
+    lblSgnAcc: TLabel;
+    paGrid: TPanel;
+  private
+    FSendersGrid: TVisualGrid;
+  public
+    procedure OnPresent; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses UAccounts, UCrypto, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+
+type
+
+  { TAccountSenderDataSource }
+
+  TAccountSenderDataSource = class(TAccountsDataSourceBase)
+  private
+    FModel: TWIZChangeAccountPrivateKeyModel;
+  protected
+    function GetColumns: TDataColumns; override;
+  public
+    property Model: TWIZChangeAccountPrivateKeyModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
+    function GetItemField(constref AItem: TAccount;
+      const ABindingName: ansistring): variant; override;
+  end;
+
+{ TWIZChangeAccountPrivateKey_Confirmation }
+
+procedure TWIZChangeAccountPrivateKey_Confirmation.OnPresent;
+var
+  Data: TAccountSenderDataSource;
+begin
+  FSendersGrid := TVisualGrid.Create(Self);
+  FSendersGrid.CanSearch := False;
+  FSendersGrid.SortMode := smMultiColumn;
+  FSendersGrid.FetchDataInThread := False;
+  FSendersGrid.AutoPageSize := True;
+  FSendersGrid.SelectionType := stNone;
+  FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone,
+    vgoAutoHidePaging];
+  with FSendersGrid.AddColumn('Account') do
+  begin
+    Binding := 'Account';
+    Filters := SORTABLE_NUMERIC_FILTER;
+    Width := 100;
+    HeaderFontStyles := [fsBold];
+    DataFontStyles := [fsBold];
+  end;
+  with FSendersGrid.AddColumn('New Private Key Name') do
+  begin
+    Binding := 'NewPrivateKey';
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+  with FSendersGrid.AddColumn('Fee') do
+  begin
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+
+  Data := TAccountSenderDataSource.Create(FSendersGrid);
+  Data.Model := Model;
+  FSendersGrid.DataSource := Data;
+  paGrid.AddControlDockCenter(FSendersGrid);
+  lblSgnAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(
+    Model.SignerAccount.account);
+end;
+
+{ TAccountSenderDataSource }
+
+function TAccountSenderDataSource.GetColumns: TDataColumns;
+begin
+  Result := TDataColumns.Create(
+    TDataColumn.From('Account'),
+    TDataColumn.From('NewPrivateKey'),
+    TDataColumn.From('Fee')
+    );
+end;
+
+function TAccountSenderDataSource.GetItemField(constref AItem: TAccount;
+  const ABindingName: ansistring): variant;
+var
+  index: integer;
+begin
+  if ABindingName = 'Account' then
+    Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account)
+  else if ABindingName = 'NewPrivateKey' then
+  begin
+    Result := IIF(Model.NewWalletKey.Name = '',
+      TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(
+      Model.NewWalletKey.AccountKey)), Model.NewWalletKey.Name);
+    if not Assigned(Model.NewWalletKey.PrivateKey) then
+    begin
+      Result := Result + '(*)';
+    end;
+  end
+  else if ABindingName = 'Fee' then
+    Result := TAccountComp.FormatMoney(Model.DefaultFee)
+  else
+    raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
+end;
+
+
+procedure TAccountSenderDataSource.FetchAll(const AContainer: TList<TAccount>);
+var
+  i: integer;
+begin
+  for i := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+  begin
+    AContainer.Add(Model.SelectedAccounts[i]);
+  end;
+end;
+
+
+end.

+ 31 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Start.lfm

@@ -0,0 +1,31 @@
+object WIZChangeAccountPrivateKey_Start: TWIZChangeAccountPrivateKey_Start
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  Caption = 'WIZChangeAccountPrivateKey_Start'
+  ClientHeight = 253
+  ClientWidth = 429
+  Visible = False
+  object grpSelectedAccount: TGroupBox
+    Left = 6
+    Height = 248
+    Top = 0
+    Width = 416
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Caption = 'Selected Accounts'
+    ClientHeight = 228
+    ClientWidth = 412
+    TabOrder = 0
+    object paGrid: TPanel
+      Left = 6
+      Height = 200
+      Top = 24
+      Width = 402
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BevelOuter = bvNone
+      Caption = 'GRID PANEL'
+      TabOrder = 0
+    end
+  end
+end

+ 110 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Start.pas

@@ -0,0 +1,110 @@
+unit UWIZChangeAccountPrivateKey_Start;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, UVisualGrid, UCommon.Data, UCellRenderers,
+  UWizard, UWIZChangeAccountPrivateKey, UWIZChangeAccountPrivateKey_Transaction, UWIZChangeAccountPrivateKey_Confirmation;
+
+type
+
+  { TWIZChangeAccountPrivateKey_Start }
+
+  TWIZChangeAccountPrivateKey_Start = class(TWizardForm<TWIZChangeAccountPrivateKeyModel>)
+    grpSelectedAccount: TGroupBox;
+    paGrid: TPanel;
+  private
+    FSendersGrid: TVisualGrid;
+  public
+    procedure OnPresent; override;
+    procedure OnNext; override;
+  end;
+
+
+
+implementation
+
+{$R *.lfm}
+
+uses UAccounts, USettings, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+
+type
+
+  { TAccountSenderDataSource }
+
+  TAccountSenderDataSource = class(TAccountsDataSourceBase)
+  private
+    FModel: TWIZChangeAccountPrivateKeyModel;
+  public
+    property Model: TWIZChangeAccountPrivateKeyModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
+  end;
+
+procedure TAccountSenderDataSource.FetchAll(const AContainer: TList<TAccount>);
+var
+  i: integer;
+begin
+  for i := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+  begin
+    AContainer.Add(Model.SelectedAccounts[i]);
+  end;
+end;
+
+{ TWIZChangeAccountPrivateKey_Start }
+
+procedure TWIZChangeAccountPrivateKey_Start.OnPresent;
+var
+  Data: TAccountSenderDataSource;
+begin
+  FSendersGrid := TVisualGrid.Create(Self);
+  FSendersGrid.CanSearch := False;
+  FSendersGrid.SortMode := smMultiColumn;
+  FSendersGrid.FetchDataInThread := False;
+  FSendersGrid.AutoPageSize := True;
+  FSendersGrid.SelectionType := stNone;
+  FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone,
+    vgoAutoHidePaging];
+  with FSendersGrid.AddColumn('Account') do
+  begin
+    StretchedToFill := True;
+    Binding := 'AccountNumber';
+    SortBinding := 'AccountNumber';
+    DisplayBinding := 'Account';
+    Width := 100;
+    HeaderFontStyles := [fsBold];
+    DataFontStyles := [fsBold];
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FSendersGrid.AddColumn('Balance') do
+  begin
+    Binding := 'BalanceDecimal';
+    SortBinding := 'Balance';
+    DisplayBinding := 'Balance';
+    Width := 100;
+    HeaderAlignment := taRightJustify;
+    DataAlignment := taRightJustify;
+    Renderer := TCellRenderers.PASC;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  Data := TAccountSenderDataSource.Create(FSendersGrid);
+  Data.Model := Model;
+  FSendersGrid.DataSource := Data;
+  paGrid.AddControlDockCenter(FSendersGrid);
+end;
+
+procedure TWIZChangeAccountPrivateKey_Start.OnNext;
+begin
+   UpdatePath(ptReplaceAllNext, [TWIZChangeAccountPrivateKey_Transaction, TWIZChangeAccountPrivateKey_Confirmation]);
+end;
+
+end.

+ 105 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Transaction.lfm

@@ -0,0 +1,105 @@
+object WIZChangeAccountPrivateKey_Transaction: TWIZChangeAccountPrivateKey_Transaction
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  ActiveControl = cbSignerAccount
+  Caption = 'WIZChangeAccountPrivateKey_Transaction'
+  ClientHeight = 253
+  ClientWidth = 429
+  Visible = False
+  object gbTransaction: TGroupBox
+    Left = 24
+    Height = 192
+    Top = 40
+    Width = 384
+    Caption = 'Transaction Details'
+    ClientHeight = 172
+    ClientWidth = 380
+    TabOrder = 0
+    object cbSignerAccount: TComboBox
+      Left = 8
+      Height = 23
+      Top = 8
+      Width = 168
+      ItemHeight = 15
+      Items.Strings = (
+        ''
+      )
+      OnChange = cbSignerAccountChange
+      TabOrder = 0
+      Text = 'Select Signer Account'
+    end
+    object lblBalance: TLabel
+      Left = 200
+      Height = 15
+      Top = 12
+      Width = 151
+      Caption = 'Please Select Signer Account'
+      Font.Color = clRed
+      ParentColor = False
+      ParentFont = False
+    end
+    object lblprivatekey: TLabel
+      Left = 8
+      Height = 15
+      Top = 48
+      Width = 85
+      Caption = 'New Private Key'
+      ParentColor = False
+    end
+    object edtOpFee: TEdit
+      Left = 8
+      Height = 23
+      Top = 144
+      Width = 360
+      TabOrder = 1
+    end
+    object lblOpFee: TLabel
+      Left = 8
+      Height = 15
+      Top = 112
+      Width = 74
+      Caption = 'Operation Fee'
+      ParentColor = False
+    end
+    object cbNewPrivateKey: TComboBox
+      Left = 8
+      Height = 23
+      Top = 74
+      Width = 168
+      ItemHeight = 15
+      OnChange = cbNewPrivateKeyChange
+      TabOrder = 2
+      Text = 'Select Private Key'
+    end
+    object lblKeyName: TLabel
+      Left = 200
+      Height = 15
+      Top = 78
+      Width = 128
+      Caption = 'Please Select Private Key'
+      Font.Color = clRed
+      ParentColor = False
+      ParentFont = False
+    end
+  end
+  object lblTotalBalances: TLabel
+    Left = 24
+    Height = 15
+    Top = 16
+    Width = 176
+    Caption = 'Selected Accounts Total Balance: '
+    ParentColor = False
+  end
+  object lblTotalBalanceValue: TLabel
+    Left = 208
+    Height = 15
+    Top = 16
+    Width = 70
+    Caption = 'Total Balance'
+    Font.Color = clGreen
+    ParentColor = False
+    ParentFont = False
+  end
+end

+ 216 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_Transaction.pas

@@ -0,0 +1,216 @@
+unit UWIZChangeAccountPrivateKey_Transaction;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, Buttons, UCommon, UCommon.Collections, UWallet,
+  UFRMAccountSelect, UNode, UWizard, UWIZChangeAccountPrivateKey,
+  UWIZChangeAccountPrivateKey_TransactionPayload,
+  UWIZChangeAccountPrivateKey_Confirmation;
+
+type
+
+  { TWIZChangeAccountPrivateKey_Transaction }
+
+  TWIZChangeAccountPrivateKey_Transaction =
+  class(TWizardForm<TWIZChangeAccountPrivateKeyModel>)
+    cbSignerAccount: TComboBox;
+    cbNewPrivateKey: TComboBox;
+    edtOpFee: TEdit;
+    gbTransaction: TGroupBox;
+    lblKeyName: TLabel;
+    lblOpFee: TLabel;
+    lblprivatekey: TLabel;
+    lblTotalBalanceValue: TLabel;
+    lblTotalBalances: TLabel;
+    lblBalance: TLabel;
+    procedure cbNewPrivateKeyChange(Sender: TObject);
+    procedure cbSignerAccountChange(Sender: TObject);
+  private
+    procedure UpdateWalletKeys();
+  public
+    procedure OnPresent; override;
+    procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses
+  UAccounts, UCrypto, UUserInterface, USettings;
+
+{ TWIZChangeAccountPrivateKey_Transaction }
+
+procedure TWIZChangeAccountPrivateKey_Transaction.cbSignerAccountChange(Sender: TObject);
+begin
+  if cbSignerAccount.ItemIndex < 1 then
+  begin
+    lblBalance.Font.Color := clRed;
+    lblBalance.Caption := 'Please Select Signer Account';
+  end
+  else
+  begin
+    lblBalance.Font.Color := clGreen;
+    lblBalance.Caption := Format('%s PASC',
+      [TAccountComp.FormatMoney(Model.SelectedAccounts[PtrInt(
+      cbSignerAccount.Items.Objects[cbSignerAccount.ItemIndex])].Balance)]);
+  end;
+end;
+
+procedure TWIZChangeAccountPrivateKey_Transaction.cbNewPrivateKeyChange(
+  Sender: TObject);
+var
+  i: integer;
+  wk: TWalletKey;
+begin
+  if cbNewPrivateKey.ItemIndex < 1 then
+  begin
+    lblKeyName.Font.Color := clRed;
+    lblKeyName.Caption := 'Please Select Private Key';
+  end
+  else
+  begin
+    lblKeyName.Font.Color := clGreen;
+    i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+    wk := TWallet.Keys.Key[i];
+    lblKeyName.Caption := Format('%s ',
+      [IIF(wk.Name = '', TCrypto.ToHexaString(
+      TAccountComp.AccountKey2RawString(wk.AccountKey)), wk.Name)]);
+  end;
+end;
+
+procedure TWIZChangeAccountPrivateKey_Transaction.UpdateWalletKeys();
+var
+  i: integer;
+  wk: TWalletKey;
+  s: string;
+begin
+  cbNewPrivateKey.items.BeginUpdate;
+  try
+    cbNewPrivateKey.Items.Clear;
+    cbNewPrivateKey.Items.Add('Select Private Key');
+    if not Assigned(TWallet.Keys) then
+    begin
+      Exit;
+    end;
+    for i := 0 to TWallet.Keys.Count - 1 do
+    begin
+      wk := TWallet.Keys.Key[i];
+      s := IIF(wk.Name = '', TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(wk.AccountKey)), wk.Name);
+      if not Assigned(wk.PrivateKey) then
+      begin
+        s := s + '(*)';
+      end;
+      cbNewPrivateKey.Items.AddObject(s, TObject(i));
+    end;
+    cbNewPrivateKey.Sorted := True;
+  finally
+    cbNewPrivateKey.Items.EndUpdate;
+  end;
+end;
+
+procedure TWIZChangeAccountPrivateKey_Transaction.OnPresent;
+
+  function GetAccNoWithChecksum(AAccountNumber: cardinal): string;
+  begin
+    Result := TAccountComp.AccountNumberToAccountTxtNumber(AAccountNumber);
+  end;
+
+var
+  acc: TAccount;
+  accNumberwithChecksum: string;
+  totalBalance: int64;
+  i: integer;
+begin
+  cbSignerAccount.Items.BeginUpdate;
+  totalBalance := 0;
+  try
+    cbSignerAccount.Items.Clear;
+    cbSignerAccount.Items.Add('Select Signer Account');
+    for i := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+    begin
+      acc := Model.SelectedAccounts[i];
+      accNumberwithChecksum := GetAccNoWithChecksum(acc.account);
+      totalBalance := totalBalance + acc.balance;
+      cbSignerAccount.Items.AddObject(accNumberwithChecksum, TObject(i));
+    end;
+  finally
+    cbSignerAccount.Items.EndUpdate;
+  end;
+  UpdateWalletKeys();
+  cbSignerAccount.ItemIndex := Model.SelectedIndex;
+  cbSignerAccountChange(Self);
+  cbNewPrivateKey.ItemIndex := Model.PrivateKeySelectedIndex;
+  cbNewPrivateKeyChange(Self);
+  lblTotalBalanceValue.Caption :=
+    Format('%s PASC', [TAccountComp.FormatMoney(totalBalance)]);
+
+  edtOpFee.Text := TAccountComp.FormatMoney(TSettings.DefaultFee);
+end;
+
+procedure TWIZChangeAccountPrivateKey_Transaction.OnNext;
+begin
+  Model.SelectedIndex := cbSignerAccount.ItemIndex;
+  Model.PrivateKeySelectedIndex := cbNewPrivateKey.ItemIndex;
+  Model.SignerAccount := Model.SelectedAccounts[PtrInt(
+    cbSignerAccount.Items.Objects[cbSignerAccount.ItemIndex])];
+  Model.NewWalletKey := TWallet.Keys.Key[PtrInt(
+    cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex])];
+
+  UpdatePath(ptReplaceAllNext, [TWIZChangeAccountPrivateKey_TransactionPayload,
+    TWIZChangeAccountPrivateKey_Confirmation]);
+end;
+
+function TWIZChangeAccountPrivateKey_Transaction.Validate(
+  out message: ansistring): boolean;
+var
+  i: integer;
+begin
+  if cbSignerAccount.ItemIndex < 1 then
+  begin
+    message := 'A signer account must be selected';
+    Result := False;
+    Exit;
+  end;
+
+  if cbNewPrivateKey.ItemIndex < 1 then
+  begin
+    message := 'A private key must be selected';
+    Result := False;
+    Exit;
+  end;
+
+  if not TAccountComp.TxtToMoney(Trim(edtOpFee.Text), Model.DefaultFee) then
+  begin
+    message := 'Invalid fee value "' + edtOpFee.Text + '"';
+    Result := False;
+    Exit;
+  end;
+
+  //Result := TAccountComp.AccountKeyFromImport(edtPublicKey.Text,
+  //  Model.AccountKey, message);
+  //for i := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+  //begin
+  //  if TAccountComp.EqualAccountKeys(Model.SelectedAccounts[i].accountInfo.accountKey,
+  //    Model.AccountKey) then
+  //  begin
+  //    Result := False;
+  //    message := 'new public key is same as selected account public key';
+  //  end;
+  //end;
+end;
+
+end.

+ 98 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_TransactionPayload.lfm

@@ -0,0 +1,98 @@
+object WIZChangeAccountPrivateKey_TransactionPayload: TWIZChangeAccountPrivateKey_TransactionPayload
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  ActiveControl = mmoPayload
+  Caption = 'Form1'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.1.0'
+  Visible = False
+  object grpPayload: TGroupBox
+    Left = 8
+    Height = 244
+    Top = 2
+    Width = 416
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Caption = 'Transaction Payload'
+    ClientHeight = 224
+    ClientWidth = 412
+    TabOrder = 0
+    object paPayload: TPanel
+      Left = 8
+      Height = 214
+      Top = 2
+      Width = 396
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BevelOuter = bvNone
+      ClientHeight = 214
+      ClientWidth = 396
+      TabOrder = 0
+      object mmoPayload: TMemo
+        Left = 0
+        Height = 70
+        Top = 136
+        Width = 386
+        TabOrder = 0
+      end
+      object Label1: TLabel
+        Left = 0
+        Height = 15
+        Top = 118
+        Width = 69
+        Caption = 'Payload Data'
+        ParentColor = False
+      end
+      object rbEncryptedWithOldEC: TRadioButton
+        Left = 3
+        Height = 19
+        Top = 0
+        Width = 176
+        Caption = 'Encrypted with old public key'
+        TabOrder = 1
+      end
+      object rbEncryptedWithEC: TRadioButton
+        Left = 3
+        Height = 19
+        Top = 24
+        Width = 181
+        Caption = 'Encrypted with new public key'
+        Checked = True
+        TabOrder = 2
+        TabStop = True
+      end
+      object rbEncryptedWithPassword: TRadioButton
+        Left = 3
+        Height = 19
+        Top = 48
+        Width = 152
+        Caption = 'Encrypted with password'
+        TabOrder = 3
+      end
+      object lblPassword: TLabel
+        Left = 3
+        Height = 15
+        Top = 73
+        Width = 53
+        Caption = 'Password:'
+        ParentColor = False
+      end
+      object rbNotEncrypted: TRadioButton
+        Left = 3
+        Height = 19
+        Top = 94
+        Width = 181
+        Caption = 'Don''t encrypt (public payload)'
+        TabOrder = 4
+      end
+      object edtPassword: TEdit
+        Left = 64
+        Height = 23
+        Top = 70
+        Width = 160
+        TabOrder = 5
+      end
+    end
+  end
+end

+ 84 - 0
src/gui/wizards/UWIZChangeAccountPrivateKey_TransactionPayload.pas

@@ -0,0 +1,84 @@
+unit UWIZChangeAccountPrivateKey_TransactionPayload;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, Buttons, UCommon, UCommon.Collections,
+  UWizard, UWIZChangeAccountPrivateKey;
+
+type
+
+  { TWIZChangeAccountPrivateKey_TransactionPayload }
+
+  TWIZChangeAccountPrivateKey_TransactionPayload = class(TWizardForm<TWIZChangeAccountPrivateKeyModel>)
+    edtPassword: TEdit;
+    grpPayload: TGroupBox;
+    Label1: TLabel;
+    lblPassword: TLabel;
+    mmoPayload: TMemo;
+    paPayload: TPanel;
+    rbEncryptedWithOldEC: TRadioButton;
+    rbEncryptedWithEC: TRadioButton;
+    rbEncryptedWithPassword: TRadioButton;
+    rbNotEncrypted: TRadioButton;
+  public
+    procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses
+  UAccounts, UUserInterface;
+
+{ TWIZChangeAccountPrivateKey_TransactionPayload }
+
+procedure TWIZChangeAccountPrivateKey_TransactionPayload.OnNext;
+begin
+  Model.Payload := mmoPayload.Lines.Text;
+  if rbEncryptedWithOldEC.Checked then
+  begin
+    Model.PayloadEncryptionMode := akaEncryptWithOldEC;
+  end
+  else
+  if rbEncryptedWithEC.Checked then
+  begin
+    Model.PayloadEncryptionMode := akaEncryptWithEC;
+  end
+  else
+  if rbEncryptedWithPassword.Checked then
+  begin
+    Model.PayloadEncryptionMode := akaEncryptWithPassword;
+  end
+  else
+  if rbNotEncrypted.Checked then
+  begin
+    Model.PayloadEncryptionMode := akaNotEncrypt;
+  end;
+end;
+
+function TWIZChangeAccountPrivateKey_TransactionPayload.Validate(out message: ansistring): boolean;
+begin
+  if (not rbNotEncrypted.Checked) and (not rbEncryptedWithEC.Checked) and
+    (not rbEncryptedWithOldEC.Checked) and (not rbEncryptedWithPassword.Checked) then
+  begin
+    message := 'you must select an encryption option for payload';
+    Result := False;
+    Exit;
+  end;
+end;
+
+end.

+ 33 - 1
src/pascalcoin_wallet.lpi

@@ -33,7 +33,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="83">
+    <Units Count="88">
       <Unit0>
       <Unit0>
         <Filename Value="pascalcoin_wallet.dpr"/>
         <Filename Value="pascalcoin_wallet.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -467,6 +467,38 @@
         <HasResources Value="True"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
         <ResourceBaseClass Value="Form"/>
       </Unit82>
       </Unit82>
+      <Unit83>
+        <Filename Value="gui\wizards\UWIZChangeAccountPrivateKey.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit83>
+      <Unit84>
+        <Filename Value="gui\wizards\UWIZChangeAccountPrivateKey_Confirmation.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeAccountPrivateKey_Confirmation"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit84>
+      <Unit85>
+        <Filename Value="gui\wizards\UWIZChangeAccountPrivateKey_Start.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeAccountPrivateKey_Start"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit85>
+      <Unit86>
+        <Filename Value="gui\wizards\UWIZChangeAccountPrivateKey_Transaction.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeAccountPrivateKey_Transaction"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit86>
+      <Unit87>
+        <Filename Value="gui\wizards\UWIZChangeAccountPrivateKey_TransactionPayload.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeAccountPrivateKey_TransactionPayload"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit87>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>