|
@@ -22,8 +22,10 @@ unit UCoreUtils;
|
|
|
interface
|
|
|
|
|
|
uses
|
|
|
- Classes, SysUtils, UCrypto, UAccounts, UBlockChain, UOpTransaction, UNode, UCommon, UNetProtocol,
|
|
|
- Generics.Collections, Generics.Defaults, UCoreObjects, Forms, Dialogs, LCLType, UCellRenderers, UCommon.Collections;
|
|
|
+ Classes, SysUtils, UCrypto, UAccounts, UBlockChain, UOpTransaction, UNode,
|
|
|
+ UBaseTypes, UCommon, UNetProtocol, UCoreObjects,UCommon.Collections,
|
|
|
+ Generics.Collections, Generics.Defaults,
|
|
|
+ Forms, Dialogs, LCLType, UCellRenderers;
|
|
|
|
|
|
type
|
|
|
|
|
@@ -107,10 +109,23 @@ type
|
|
|
function TotalBlockCount : Integer;
|
|
|
end;
|
|
|
|
|
|
+ { TWIZOperationsHelper }
|
|
|
+
|
|
|
+ TWIZOperationsHelper = class
|
|
|
+ private
|
|
|
+ class function UpdatePayload(const ASenderPublicKey, ADestinationPublicKey: TAccountKey; const APayloadEncryptionMode: 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 OthersFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean; static;
|
|
|
+ public
|
|
|
+ class function ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; const ADestinationAccount, ASignerAccount: TAccount; AAmount, AFee: int64; const ASendPASCMode: TSendPASCMode; const APayloadEncryptionMode: 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: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
|
|
|
+ class function ExecuteEnlistAccountForSale(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount, ASellerAccount: TAccount; const APublicKey: TAccountKey; AFee, ASalePrice: int64; ALockedUntilBlock: UInt32; const AAccountSaleMode: TAccountSaleMode; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
|
|
|
+ end;
|
|
|
+
|
|
|
implementation
|
|
|
|
|
|
uses
|
|
|
- UMemory, UConst, UWallet, UECIES, UAES, ULog;
|
|
|
+ UMemory, UConst, UWallet, UUserInterface, UECIES, UAES, ULog;
|
|
|
|
|
|
{ TCoreTool }
|
|
|
|
|
@@ -248,7 +263,6 @@ begin
|
|
|
Result := LAccs.ToArray;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
{ TNodeHelper }
|
|
|
|
|
|
function TNodeHelper.HasBestKnownBlockchainTip: boolean;
|
|
@@ -605,4 +619,629 @@ begin
|
|
|
Result := Round(Self.TotalSeconds / CT_NewLineSecondsAvg);
|
|
|
end;
|
|
|
|
|
|
+{ TWIZOperationsHelper }
|
|
|
+
|
|
|
+class function TWIZOperationsHelper.SendPASCFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; ATotalAmount, ATotalFee: int64; AOperationsHashTree: TOperationsHashTree; var AErrorMessage: string): boolean;
|
|
|
+var
|
|
|
+ LAuxs, LOperationsTxt: string;
|
|
|
+ i: integer;
|
|
|
+begin
|
|
|
+ LOperationsTxt := AOperationsTxt;
|
|
|
+ if (ANoOfOperations > 1) then
|
|
|
+ begin
|
|
|
+ LAuxs := 'Total amount that dest will receive: ' + TAccountComp.FormatMoney(
|
|
|
+ ATotalAmount) + #10;
|
|
|
+ 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 TWIZOperationsHelper.UpdatePayload(const ASenderPublicKey, ADestinationPublicKey: TAccountKey; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
|
|
|
+var
|
|
|
+ LValid: boolean;
|
|
|
+begin
|
|
|
+
|
|
|
+ if (APayloadContent = '') then
|
|
|
+ Exit(True);
|
|
|
+
|
|
|
+ LValid := False;
|
|
|
+ AErrorMessage := 'An Error Occured During Payload Encryption.';
|
|
|
+
|
|
|
+ try
|
|
|
+
|
|
|
+ case APayloadEncryptionMode of
|
|
|
+
|
|
|
+ pemEncryptWithSender:
|
|
|
+ begin
|
|
|
+ // Use sender public key
|
|
|
+ AEncodedPayloadBytes := ECIESEncrypt(ASenderPublicKey, APayloadContent);
|
|
|
+ LValid := AEncodedPayloadBytes <> '';
|
|
|
+ end;
|
|
|
+
|
|
|
+ pemEncryptWithReceiver:
|
|
|
+ begin
|
|
|
+ // With destination public key
|
|
|
+ AEncodedPayloadBytes := ECIESEncrypt(ADestinationPublicKey, APayloadContent);
|
|
|
+ LValid := AEncodedPayloadBytes <> '';
|
|
|
+ end;
|
|
|
+
|
|
|
+ pemEncryptWithPassword:
|
|
|
+ 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;
|
|
|
+
|
|
|
+ pemNotEncrypt:
|
|
|
+ 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 TWIZOperationsHelper.OthersFinalizeAndDisplayMessage(const AOperationsTxt, AOperationToString: string; ANoOfOperations: integer; 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 TWIZOperationsHelper.ExecuteSendPASC(const ASelectedAccounts: TArray<TAccount>; const ADestinationAccount, ASignerAccount: TAccount; AAmount, AFee: int64; const ASendPASCMode: TSendPASCMode; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
|
|
|
+var
|
|
|
+ LWalletKey: TWalletKey;
|
|
|
+ LWalletKeys: TWalletKeys;
|
|
|
+ LNode: TNode;
|
|
|
+ LPCOperation: TPCOperation;
|
|
|
+ LOperationsHashTree: TOperationsHashTree;
|
|
|
+ LTotalAmount, LTotalSignerFee, 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 Selected 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;
|
|
|
+ LTotalSignerFee := 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 UpdatePayload(LCurrentAccount.accountInfo.accountKey, ADestinationAccount.accountInfo.accountKey, APayloadEncryptionMode, APayloadContent, LPayloadEncodedBytes, APayloadEncryptionPassword, AErrorMessage) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := Format('Error Encoding Payload Of Selected 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('Selected 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(
|
|
|
+ TUserInterface.Node.Bank.Safebox.CurrentProtocol, LCurrentAccount.account, LCurrentAccount.n_operation + 1, ADestinationAccount.account, LWalletKey.PrivateKey, LAmount, LFee, LPayloadEncodedBytes);
|
|
|
+ try
|
|
|
+ LOperationsTxt := Format('Transaction To "%s"', [ADestinationAccount.AccountString]);
|
|
|
+
|
|
|
+ if Assigned(LPCOperation) then
|
|
|
+ begin
|
|
|
+ LOperationsHashTree.AddOperationToHashTree(LPCOperation);
|
|
|
+ Inc(LTotalAmount, LAmount);
|
|
|
+ Inc(LTotalSignerFee, LFee);
|
|
|
+ Inc(LNoOfOperations);
|
|
|
+ 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;
|
|
|
+
|
|
|
+ Exit(TWIZOperationsHelper.SendPASCFinalizeAndDisplayMessage(LOperationsTxt, LOperationToString, LNoOfOperations, LTotalAmount, LTotalSignerFee, LOperationsHashTree, AErrorMessage));
|
|
|
+ finally
|
|
|
+ LOperationsHashTree.Free;
|
|
|
+ end;
|
|
|
+
|
|
|
+end;
|
|
|
+
|
|
|
+class function TWIZOperationsHelper.ExecuteChangeKey(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount: TAccount; APublicKey: TAccountKey; AFee: int64; const APayloadEncryptionMode: 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, LNoOfOperations: integer;
|
|
|
+ LCurrentAccount, LSignerAccount: TAccount;
|
|
|
+ LPayloadEncodedBytes: TRawBytes;
|
|
|
+label
|
|
|
+ loop_start;
|
|
|
+begin
|
|
|
+ if Length(ASelectedAccounts) = 0 then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'No Selected 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;
|
|
|
+ LNoOfOperations := 0;
|
|
|
+ LOperationsTxt := '';
|
|
|
+ LOperationToString := '';
|
|
|
+ for LAccountIdx := Low(ASelectedAccounts) to High(ASelectedAccounts) do
|
|
|
+ begin
|
|
|
+ loop_start:
|
|
|
+ LPCOperation := nil; // reset LPCOperation to Nil
|
|
|
+ LCurrentAccount := ASelectedAccounts[LAccountIdx];
|
|
|
+
|
|
|
+ if (TAccountComp.EqualAccountKeys(LCurrentAccount.accountInfo.accountKey,
|
|
|
+ APublicKey)) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'New Key Is Same As Current Key';
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+
|
|
|
+ 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 not UpdatePayload(LCurrentAccount.accountInfo.accountKey, APublicKey, APayloadEncryptionMode, APayloadContent, LPayloadEncodedBytes, APayloadEncryptionPassword, AErrorMessage) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := Format('Error Encoding Payload Of Selected 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('Selected 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(TUserInterface.Node.Bank.Safebox.CurrentProtocol, LSignerAccount.account,
|
|
|
+ LSignerAccount.n_operation + LNoOfOperations + 1, LCurrentAccount.account,
|
|
|
+ LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ LPCOperation := TOpChangeKey.Create(TUserInterface.Node.Bank.Safebox.CurrentProtocol, LCurrentAccount.account, LCurrentAccount.n_operation +
|
|
|
+ 1, LCurrentAccount.account, LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
|
|
|
+
|
|
|
+ try
|
|
|
+ LOperationsTxt := Format('Change Key To "%s"', [TAccountComp.GetECInfoTxt(APublicKey.EC_OpenSSL_NID)]);
|
|
|
+ if Assigned(LPCOperation) then
|
|
|
+ begin
|
|
|
+ LOperationsHashTree.AddOperationToHashTree(LPCOperation);
|
|
|
+ Inc(LNoOfOperations);
|
|
|
+ Inc(LTotalSignerFee, LFee);
|
|
|
+ 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(TWIZOperationsHelper.OthersFinalizeAndDisplayMessage(LOperationsTxt, LOperationToString, LNoOfOperations, LTotalSignerFee, LOperationsHashTree, AErrorMessage));
|
|
|
+ finally
|
|
|
+ LOperationsHashTree.Free;
|
|
|
+ end;
|
|
|
+
|
|
|
+end;
|
|
|
+
|
|
|
+class function TWIZOperationsHelper.ExecuteEnlistAccountForSale(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount, ASellerAccount: TAccount; const APublicKey: TAccountKey; AFee, ASalePrice: int64; ALockedUntilBlock: UInt32; const AAccountSaleMode: TAccountSaleMode; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean;
|
|
|
+var
|
|
|
+ LWalletKey: TWalletKey;
|
|
|
+ LWalletKeys: TWalletKeys;
|
|
|
+ LNode: TNode;
|
|
|
+ LPCOperation: TPCOperation;
|
|
|
+ LOperationsHashTree: TOperationsHashTree;
|
|
|
+ LTotalSignerFee, LFee: int64;
|
|
|
+ LOperationsTxt, LOperationToString: string;
|
|
|
+ LIdx, LAccountIdx, LNoOfOperations: integer;
|
|
|
+ LCurrentAccount, LSignerAccount: TAccount;
|
|
|
+ LPayloadEncodedBytes: TRawBytes;
|
|
|
+begin
|
|
|
+ if Length(ASelectedAccounts) = 0 then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'No Selected 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
|
|
|
+ LTotalSignerFee := 0;
|
|
|
+ LNoOfOperations := 0;
|
|
|
+ LOperationsTxt := '';
|
|
|
+ LOperationToString := '';
|
|
|
+
|
|
|
+ for LAccountIdx := Low(ASelectedAccounts) to High(ASelectedAccounts) do
|
|
|
+ begin
|
|
|
+ LPCOperation := nil; // reset LPCOperation to Nil
|
|
|
+ LCurrentAccount := ASelectedAccounts[LAccountIdx];
|
|
|
+
|
|
|
+ if TAccountComp.IsAccountForSale(LCurrentAccount.accountInfo) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := Format('Account "%s" Is Already Enlisted For Sale', [LCurrentAccount.AccountString]);
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if (ASellerAccount.account = LCurrentAccount.account) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'Seller Account Cannot Be Same As Account To Be Sold.';
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if (LNode.Node.Bank.SafeBox.CurrentProtocol = CT_PROTOCOL_1) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'This Operation Needs PROTOCOL 2 Active';
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if AAccountSaleMode = akaPrivateSale then
|
|
|
+ begin
|
|
|
+
|
|
|
+ if TAccountComp.EqualAccountKeys(APublicKey,
|
|
|
+ LCurrentAccount.accountInfo.accountKey) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'You Cannot Sell To An Account That You Want To Enlist For Sale.';
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if ALockedUntilBlock = 0 then
|
|
|
+ begin
|
|
|
+ AErrorMessage := 'You Didn''t Insert a Locking Block.';
|
|
|
+ Exit(False);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if not UpdatePayload(LCurrentAccount.accountInfo.accountKey, APublicKey, APayloadEncryptionMode, APayloadContent, LPayloadEncodedBytes, APayloadEncryptionPassword, AErrorMessage) then
|
|
|
+ begin
|
|
|
+ AErrorMessage := Format('Error Encoding Payload Of Selected 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('Selected 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 ASignerAccount.balance > AFee then
|
|
|
+ LFee := AFee
|
|
|
+ else
|
|
|
+ LFee := ASignerAccount.balance;
|
|
|
+
|
|
|
+ case AAccountSaleMode of
|
|
|
+ akaPublicSale:
|
|
|
+
|
|
|
+ LPCOperation := TOpListAccountForSale.CreateListAccountForSale(
|
|
|
+ TUserInterface.Node.Bank.Safebox.CurrentProtocol, ASignerAccount.account, ASignerAccount.n_operation + 1 + LAccountIdx,
|
|
|
+ LCurrentAccount.account, ASalePrice, LFee, ASellerAccount.account,
|
|
|
+ APublicKey, 0, LWalletKey.PrivateKey, LPayloadEncodedBytes);
|
|
|
+
|
|
|
+ akaPrivateSale:
|
|
|
+
|
|
|
+ LPCOperation := TOpListAccountForSale.CreateListAccountForSale(
|
|
|
+ TUserInterface.Node.Bank.Safebox.CurrentProtocol, ASignerAccount.account, ASignerAccount.n_operation + 1 + LAccountIdx,
|
|
|
+ LCurrentAccount.account, ASalePrice, LFee, ASellerAccount.account,
|
|
|
+ APublicKey, ALockedUntilBlock, LWalletKey.PrivateKey, LPayloadEncodedBytes)
|
|
|
+ else
|
|
|
+ raise Exception.Create('Invalid Account Sale Type')
|
|
|
+ end;
|
|
|
+
|
|
|
+ try
|
|
|
+ LOperationsTxt := Format('Enlist Account For Sale At a Price Of "%s" PASC', [TAccountComp.FormatMoney(ASalePrice)]);
|
|
|
+ if Assigned(LPCOperation) then
|
|
|
+ begin
|
|
|
+ LOperationsHashTree.AddOperationToHashTree(LPCOperation);
|
|
|
+ Inc(LNoOfOperations);
|
|
|
+ Inc(LTotalSignerFee, LFee);
|
|
|
+ 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(TWIZOperationsHelper.OthersFinalizeAndDisplayMessage(LOperationsTxt, LOperationToString, LNoOfOperations, LTotalSignerFee, LOperationsHashTree, AErrorMessage));
|
|
|
+
|
|
|
+ finally
|
|
|
+ LOperationsHashTree.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
end.
|