123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686 |
- unit UCoreUtils;
- { Copyright (c) 2018 by Herman Schoenfeld
- Distributed under the MIT software license, see the accompanying file LICENSE
- or visit http://www.opensource.org/licenses/mit-license.php.
- This unit is a part of the PascalCoin Project, an infinitely scalable
- cryptocurrency. Find us here:
- Web: https://www.pascalcoin.org
- Source: https://github.com/PascalCoin/PascalCoin
- Acknowledgements:
- - Ugochukwu Mmaduekwe - added TOperationsManager class
- THIS LICENSE HEADER MUST NOT BE REMOVED.
- }
- {$mode delphi}
- {$modeswitch nestedprocvars}
- interface
- uses
- Classes, SysUtils, Forms, Dialogs, LCLType, UAccounts, UBlockChain, UNode, UWallet,
- UBaseTypes, UCommon, UCoreObjects, UCommon.Collections, Generics.Defaults;
- type
- { TAccountComparer }
- TAccountComparer = class(TComparer<TAccount>)
- function Compare(constref ALeft, ARight: T): integer; override;
- class function DoCompare(constref ALeft, ARight: TAccount): integer; inline;
- end;
- { TAccountEqualityComparer }
- TAccountEqualityComparer = class(TEqualityComparer<TAccount>)
- public
- function Equals(constref ALeft, ARight: TAccount): boolean; override;
- function GetHashCode(constref AValue: TAccount): UInt32; override;
- class function AreEqual(constref ALeft, ARight: TAccount): boolean;
- class function CalcHashCode(constref AValue: TAccount): UInt32;
- end;
- { TAccountKeyComparer }
- TAccountKeyComparer = class(TComparer<TAccountKey>)
- function Compare(constref ALeft, ARight: T): integer; override;
- class function DoCompare(constref ALeft, ARight: TAccountKey): integer; inline;
- end;
- { TAccountKeyEqualityComparer }
- TAccountKeyEqualityComparer = class(TEqualityComparer<TAccountKey>)
- public
- function Equals(constref ALeft, ARight: TAccountKey): boolean; override;
- function GetHashCode(constref AValue: TAccountKey): UInt32; override;
- class function AreEqual(constref ALeft, ARight: TAccountKey): boolean;
- class function CalcHashCode(constref AValue: TAccountKey): UInt32;
- end;
- { TCoreTool }
- TCoreTool = class
- public
- class function GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>; static;
- class function GetOperationShortText(const OpType, OpSubType: DWord): ansistring; static; inline;
- class function GetUserBalance(IncludePending: boolean = False): TBalanceSummary;
- class function GetUserAccounts(IncludePending: boolean = False): TArray<TAccount>; overload;
- class function GetUserAccounts(out Balance: TBalanceSummary; IncludePending: boolean = False): TArray<TAccount>; overload;
- class function GetUserAccountNumbers: TArray<cardinal>;
- class function AreAccountBalancesGreaterThan(const ACandidates: array of TAccount; AAmount: int64; var AFaultyAccount: TAccount): boolean; static;
- end;
- { TNodeHelper }
- TNodeHelper = class helper for TNode
- function HasBestKnownBlockchainTip: boolean;
- function BlockTip: cardinal;
- function GetAccount(AAccountNumber: cardinal; AIncludePending: boolean = True): TAccount;
- function GetAccounts(const AAccountNumbers: array of cardinal; AIncludePending: boolean = True): TArray<TAccount>;
- function GetPendingOperationsAffectingAccounts(const AAccountNumbers: array of cardinal; ASkipCount, ATakeCount: integer): TArray<TOperationResume>;
- function GetStoredOperationsAffectingAccounts(const AAccountNumbers: array of cardinal; ABlockDepth, ASkipCount, ATakeCount: integer): TArray<TOperationResume>;
- end;
- { TAccountHelper }
- TAccountHelper = record helper for TAccount
- function GetAccountString : AnsiString;
- function GetDisplayString : AnsiString;
- function GetInfoText(const ABank : TPCBank) : utf8string;
- property AccountString : AnsiString read GetAccountString;
- property DisplayString : AnsiString read GetDisplayString;
- end;
- { TOperationResumeHelper }
- TOperationResumeHelper = record helper for TOperationResume
- function GetPrintableOPHASH : AnsiString;
- function GetInfoText(const ABank : TPCBank) : utf8string;
- end;
- { TTimeSpanHelper }
- TTimeSpanHelper = record helper for TTimeSpan
- function TotalBlockCount : Integer;
- end;
- { TWIZOperationsHelper }
- TWIZOperationsHelper = class
- private
- class function IsOwnerOfWallet(AAccount: TAccount; AWalletKeys: TWalletKeys; out AWalletKey: TWalletKey; var AErrorMessage: string): boolean; static;
- class function ValidateOperationsInput(const ASelectedAccounts: TArray<TAccount>; AWalletKeys: TWalletKeys; ANode: TNode; var AErrorMessage: string): boolean; static;
- class function UpdatePayload(const ASenderPublicKey, ADestinationPublicKey: TAccountKey; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent: string; var AEncodedPayloadBytes: TRawBytes; const APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
- 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: 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;
- class function ExecuteDelistAccountFromSale(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount: TAccount; AFee: int64; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; var AErrorMessage: string): boolean; static;
- class function ExecuteChangeAccountInfo(const ASelectedAccounts, ASignerAccounts: TArray<TAccount>; AFee: int64; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; const ANewName: TRawBytes; const ANewType: word; var AErrorMessage: string): boolean; static;
- class function ExecuteBuyAccount(const ASelectedAccounts: TArray<TAccount>; const AAccountToBuy: TAccount; AFee: int64; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; const AAmount: int64; const ANewOwnerPublicKey: TAccountKey; var AErrorMessage: string): boolean; static;
- end;
- implementation
- uses
- ULog,
- UAES,
- UConst,
- UECIES,
- UCrypto,
- UMemory,
- UNetProtocol,
- UOpTransaction,
- UPCOrderedLists,
- Generics.Collections;
- { TCoreTool }
- class function TCoreTool.GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>;
- var
- i, PoorSenderCount: integer;
- Fee, maxSignerFee, minSignerFee: int64;
- acc: TAccount;
- begin
- //make deep copy of accounts!!! Very Important
- Result := TArrayTool<TAccount>.Copy(ACandidates);
- Fee := ASingleOperationFee;
- PoorSenderCount := 0;
- for i := Low(Result) to High(Result) do
- begin
- acc := Result[i];
- if (acc.Balance < Fee) then
- Inc(PoorSenderCount);
- end;
- maxSignerFee := ANumOps * Fee;
- minSignerFee := maxSignerFee - (PoorSenderCount * Fee);
- for i := High(Result) downto Low(Result) do
- begin
- acc := Result[i];
- if not (acc.Balance >= maxSignerFee) then
- TArrayTool<TAccount>.RemoveAt(Result, i);
- end;
- end;
- class function TCoreTool.GetOperationShortText(const OpType, OpSubType: DWord): ansistring;
- begin
- case OpType of
- CT_PseudoOp_Reward: case OpSubType of
- 0, CT_PseudoOpSubtype_Miner: Result := 'Miner Reward';
- CT_PseudoOpSubtype_Developer: Result := 'Developer Reward';
- else
- Result := 'Unknown';
- end;
- CT_Op_Transaction: case OpSubType of
- CT_OpSubtype_TransactionSender: Result := 'Send';
- CT_OpSubtype_TransactionReceiver: Result := 'Receive';
- CT_OpSubtype_BuyTransactionBuyer: Result := 'Buy Account Direct';
- CT_OpSubtype_BuyTransactionTarget: Result := 'Purchased Account Direct';
- CT_OpSubtype_BuyTransactionSeller: Result := 'Sold Account Direct';
- else
- Result := 'Unknown';
- end;
- CT_Op_Changekey: Result := 'Change Key (legacy)';
- CT_Op_Recover: Result := 'Recover';
- CT_Op_ListAccountForSale: case OpSubType of
- CT_OpSubtype_ListAccountForPublicSale: Result := 'For Sale';
- CT_OpSubtype_ListAccountForPrivateSale: Result := 'Exclusive Sale';
- else
- Result := 'Unknown';
- end;
- CT_Op_DelistAccount: Result := 'Remove Sale';
- CT_Op_BuyAccount: case OpSubType of
- CT_OpSubtype_BuyAccountBuyer: Result := 'Buy Account';
- CT_OpSubtype_BuyAccountTarget: Result := 'Purchased Account';
- CT_OpSubtype_BuyAccountSeller: Result := 'Sold Account';
- else
- Result := 'Unknown';
- end;
- CT_Op_ChangeKeySigned: Result := 'Change Key';
- CT_Op_ChangeAccountInfo: Result := 'Change Info';
- CT_Op_MultiOperation: case OpSubType of
- CT_OpSubtype_MultiOperation_Global: Result := 'Mixed-Transfer';
- CT_OpSubtype_MultiOperation_AccountInfo: Result := 'Mixed-Change';
- end;
- else
- Result := 'Unknown';
- end;
- end;
- class function TCoreTool.GetUserBalance(IncludePending: boolean = False): TBalanceSummary;
- begin
- GetUserAccounts(Result, IncludePending);
- end;
- class function TCoreTool.GetUserAccounts(IncludePending: boolean = False): TArray<TAccount>;
- var
- LBalance: TBalanceSummary;
- begin
- Result := GetUserAccounts(LBalance, IncludePending);
- end;
- class function TCoreTool.GetUserAccounts(out Balance: TBalanceSummary; IncludePending: boolean = False): TArray<TAccount>;
- var
- i, j: integer;
- LAccs: TList<TAccount>;
- LAcc: TAccount;
- LList: TOrderedCardinalList;
- LMemPool: TPCOperationsComp;
- Disposables: TDisposables;
- begin
- Balance := CT_BalanceSummary_Nil;
- LAccs := Disposables.AddObject(TList<TAccount>.Create) as TList<TAccount>;
- // TNode.Node.Bank.SafeBox.StartThreadSafe;
- LMemPool := TNode.Node.LockMempoolRead;
- try
- for i := 0 to TWallet.Keys.Count - 1 do
- begin
- LList := TWallet.Keys.AccountsKeyList.AccountKeyList[i];
- for j := 0 to LList.Count - 1 do
- begin
- if IncludePending then
- LAcc := LMemPool.SafeBoxTransaction.Account(LList.Get(j))
- else
- LAcc := TNode.Node.Bank.SafeBox.Account(LList.Get(j));
- LAccs.Add(LAcc);
- Inc(Balance.TotalPASA);
- Inc(Balance.TotalPASC, LAcc.Balance);
- end;
- end;
- finally
- TNode.Node.UnlockMempoolRead;
- // TNode.Node.Bank.SafeBox.EndThreadSave;
- end;
- LAccs.Sort(TAccountComparer.Create);
- Result := LAccs.ToArray;
- end;
- class function TCoreTool.GetUserAccountNumbers: TArray<cardinal>;
- var
- i, j: integer;
- LAccs: TSortedList<cardinal>;
- LList: TOrderedCardinalList;
- Disposables: TDisposables;
- begin
- LAccs := Disposables.AddObject(TSortedList<cardinal>.Create) as TSortedList<cardinal>;
- for i := 0 to TWallet.Keys.AccountsKeyList.Count - 1 do
- begin
- LList := TWallet.Keys.AccountsKeyList.AccountKeyList[i];
- for j := 0 to LList.Count - 1 do
- LAccs.Add(LList.Get(j));
- end;
- Result := LAccs.ToArray;
- end;
- class function TCoreTool.AreAccountBalancesGreaterThan(const ACandidates: array of TAccount; AAmount: int64; var AFaultyAccount: TAccount): boolean;
- var
- LIdx: integer;
- LAcc: TAccount;
- begin
- Result := True;
- for LIdx := Low(ACandidates) to High(ACandidates) do
- begin
- LAcc := ACandidates[LIdx];
- if not (LAcc.Balance > AAmount) then
- begin
- AFaultyAccount := LAcc;
- Exit(False);
- end;
- end;
- end;
- { TNodeHelper }
- function TNodeHelper.HasBestKnownBlockchainTip: boolean;
- var
- LReady: boolean;
- LMsg: ansistring;
- LDestBlock: cardinal;
- begin
- LReady := Self.Bank.IsReady(LMsg);
- if LReady and TNetData.NetData.IsGettingNewBlockChainFromClient(LMsg) then
- begin
- LDestBlock := TNetData.NetData.MaxRemoteOperationBlock.block;
- Result := Self.Bank.BlocksCount = TNetData.NetData.MaxRemoteOperationBlock.block;
- end;
- end;
- function TNodeHelper.BlockTip: cardinal;
- begin
- Result := ClipValue(Self.Bank.BlocksCount - 1, 0, MaxInt);
- end;
- function TNodeHelper.GetAccount(AAccountNumber: cardinal; AIncludePending: boolean = True): TAccount;
- var
- LOps: TArray<TAccount>;
- begin
- LOps := Self.GetAccounts([AAccountNumber], AIncludePending);
- Result := LOps[Low(Lops)];
- end;
- function TNodeHelper.GetAccounts(const AAccountNumbers: array of cardinal; AIncludePending: boolean = True): TArray<TAccount>;
- var
- i: integer;
- LMemPool : TPCOperationsComp;
- begin
- SetLength(Result, Length(AAccountNumbers));
- if AIncludePending then begin
- LMemPool := Self.LockMempoolRead;
- try
- for i := Low(AAccountNumbers) to High(AAccountNumbers) do
- Result[i] := LMemPool.SafeBoxTransaction.Account(AAccountNumbers[i])
- finally
- Self.UnlockMempoolRead;
- end;
- end
- else
- for i := Low(AAccountNumbers) to High(AAccountNumbers) do
- Result[i] := Self.Bank.SafeBox.Account(AAccountNumbers[i]);
- end;
- function TNodeHelper.GetPendingOperationsAffectingAccounts(const AAccountNumbers: array of cardinal; ASkipCount, ATakeCount: integer): TArray<TOperationResume>;
- var
- LList: TList<Cardinal>;
- LOps: TList<TOperationResume>;
- LOp: TPCOperation;
- LOpResume: TOperationResume;
- LMemPool : TPCOperationsComp;
- LAccNo: cardinal;
- LNumOps, i: integer;
- GC: TDisposables;
- begin
- LNumOps := 0;
- LList := GC.AddObject(TList<Cardinal>.Create) as TList<Cardinal>;
- LOps := GC.AddObject(TList<TOperationResume>.Create) as TList<TOperationResume>;
- for LAccNo in AAccountNumbers do
- begin
- LMemPool := Self.LockMempoolRead;
- try
- LList.Clear;
- LMemPool.OperationsHashTree.GetOperationsAffectingAccount(LAccNo, LList);
- if LList.Count > 0 then
- for i := LList.Count - 1 downto 0 do
- begin
- Inc(LNumOps);
- if (LNumOps > ASkipCount) and (LNumOps <= ASkipCount + ATakeCount) then
- begin
- LOp := LMemPool.OperationsHashTree.GetOperation(LList[i]);
- if TPCOperation.OperationToOperationResume(0, LOp, False, LAccNo, LOpResume) then
- begin
- LOpResume.NOpInsideBlock := i;
- LOpResume.Block := LMemPool.OperationBlock.block;
- LOpResume.Balance := LMemPool.SafeBoxTransaction.Account(LAccNo {Op.SignerAccount}).balance;
- LOps.Add(LOpResume);
- end;
- end;
- end;
- finally
- Self.UnlockMempoolRead;
- end;
- end;
- Result := LOps.ToArray;
- end;
- function TNodeHelper.GetStoredOperationsAffectingAccounts(const AAccountNumbers: array of cardinal; ABlockDepth, ASkipCount, ATakeCount: integer): TArray<TOperationResume>;
- type
- __TList_Cardinal = TList<cardinal>;
- var
- i: integer;
- LBlock: cardinal;
- LRelevantBlockOps: TList<Cardinal>;
- LOp: TPCOperation;
- LOpResume: TOperationResume;
- LFoundOps: TList<TOperationResume>;
- LOpsComp: TPCOperationsComp;
- LAccountBalances: TDictionary<cardinal, cardinal>;
- LAccounts: TArray<TAccount>;
- LDisposables: TDisposables;
- LBlockEnd, LNumOps: integer;
- LBlockTraversal: TSortedHashSet<cardinal>;
- LAccountsToScanAtBlock: TObjectDictionary<cardinal, __TList_Cardinal>;
- LAcc: TAccount;
- procedure MarkAccountAsScannableAtBlock(AAccountNo, ABlockNo: cardinal);
- begin
- if not LAccountsToScanAtBlock.ContainsKey(ABlockNo) then
- LAccountsToScanAtBlock.Add(ABlockNo, __TList_Cardinal.Create);
- LAccountsToScanAtBlock[ABlockNo].Add(AAccountNo);
- end;
- procedure ScanBlock(ABlockNum: cardinal);
- var
- i: integer;
- LAccNo: cardinal;
- LPrevUpdatedBlock: cardinal;
- LDisposables: TDisposables;
- begin
- LOpsComp := LDisposables.AddObject(TPCOperationsComp.Create(nil)) as TPCOperationsComp;
- LRelevantBlockOps := LDisposables.AddObject(TList<Cardinal>.Create) as TList<Cardinal>;
- // load block
- if not Bank.Storage.LoadBlockChainBlock(LOpsComp, ABlockNum) then
- begin
- TLog.NewLog(ltdebug, ClassName, 'Block ' + IntToStr(ABlockNum) + ' not found. Cannot read operations');
- exit;
- end;
- // scan for each account
- for LAccNo in LAccountsToScanAtBlock[ABlockNum] do
- begin
- LRelevantBlockOps.Clear;
- LOpsComp.OperationsHashTree.GetOperationsAffectingAccount(LAccNo, LRelevantBlockOps);
- for i := LRelevantBlockOps.Count - 1 downto 0 do
- begin
- LOp := LOpsComp.Operation[LRelevantBlockOps.Items[i]];
- if TPCOperation.OperationToOperationResume(i, LOp, False, LAccNo, LOpResume) then
- begin
- LOpResume.NOpInsideBlock := LRelevantBlockOps.Items[i];
- LOpResume.time := LOpsComp.OperationBlock.timestamp;
- LOpResume.Block := ABlockNum;
- if LAccountBalances[LAccNo] >= 0 then
- begin
- LOpResume.Balance := LAccountBalances[LAccNo];
- LAccountBalances.AddOrSetValue(LAccNo, LOpResume.Balance - (LOpResume.Amount + LOpResume.Fee));
- end
- else
- LOpResume.Balance := -1; // Undetermined
- // Apply skip/take
- Inc(LNumOps);
- if (LNumOps > ASkipCount) and (LNumOps <= ASkipCount + ATakeCount) then
- LFoundOps.Add(LOpResume);
- // short-cirtcuit exit if taken enough
- if LFoundOps.Count >= ATakeCount then
- exit;
- end;
- end;
- // Add previous updated block into traversal set
- LPrevUpdatedBlock := LOpsComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(LAccNo, ABlockNum);
- if LPrevUpdatedBlock < ABlockNum then
- begin
- LBlockTraversal.Add(LPrevUpdatedBlock);
- MarkAccountAsScannableAtBlock(LAccNo, LPrevUpdatedBlock);
- end;
- end;
- end;
- function GetAccountLastUpdateBlock(constref AAccount: TAccount): cardinal;
- begin
- Result := AAccount.updated_block;
- end;
- begin
- // Init
- LNumOps := 0;
- LBlockTraversal := LDisposables.AddObject(TSortedHashSet<cardinal>.Create(TComparerTool<cardinal>.Inverted(TComparer<cardinal>.Default))) as TSortedHashSet<cardinal>;
- LAccountsToScanAtBlock := LDisposables.AddObject(TObjectDictionary<cardinal, __TList_Cardinal>.Create([doOwnsValues])) as TObjectDictionary<cardinal, __TList_Cardinal>;
- LFoundOps := LDisposables.AddObject(TList<TOperationResume>.Create) as TList<TOperationResume>;
- LAccountBalances := LDisposables.AddObject(TDictionary<cardinal, cardinal>.Create) as TDictionary<cardinal, cardinal>;
- LBlockEnd := ClipValue(Self.BlockTip - ABlockDepth, 0, Self.BlockTip);
- // First get all accounts, their balances and initial traversal set
- LAccounts := Self.GetAccounts(AAccountNumbers, False);
- for i := Low(LAccounts) to High(LAccounts) do
- begin
- // if account is modified in block-tip
- LAcc := LAccounts[i];
- LAccountBalances.AddOrSetValue(LAcc.account, LAcc.Balance); // track account balances
- LBlockTraversal.Add(LAcc.updated_block);
- MarkAccountAsScannableAtBlock(LAcc.account, LAcc.updated_block);
- end;
- // Traverse the set of "last updated" blocks in DESCENDING order
- while LBlockTraversal.Count > 0 do
- begin
- LBlock := TSortedHashSetTool<cardinal>.Pop(LBlockTraversal);
- if LBlock < LBlockEnd then
- continue;
- ScanBlock(LBlock); // note: this will update LBlockTraversals with prev updated blocks, so loops until finished
- if LFoundOps.Count >= ATakeCount then
- exit;
- end;
- // return array result
- Result := LFoundOps.ToArray;
- end;
- { TAccountComparer }
- function TAccountComparer.Compare(constref ALeft, ARight: TAccount): integer;
- begin
- Result := TAccountComparer.DoCompare(ALeft, ARight);
- end;
- class function TAccountComparer.DoCompare(constref ALeft, ARight: TAccount): integer;
- begin
- Result := TCompare.UInt64(ALeft.account, ARight.account);
- end;
- { TAccountEqualityComparer }
- function TAccountEqualityComparer.Equals(constref ALeft, ARight: TAccount): boolean;
- begin
- Result := TAccountEqualityComparer.AreEqual(ALeft, ARight);
- end;
- function TAccountEqualityComparer.GetHashCode(constref AValue: TAccount): UInt32;
- begin
- Result := TAccountEqualityComparer.CalcHashCode(AValue);
- end;
- class function TAccountEqualityComparer.AreEqual(constref ALeft, ARight: TAccount): boolean;
- begin
- Result :=
- (ALeft.account = ARight.account) and
- (ALeft.balance = ARight.balance) and
- (ALeft.updated_block = ARight.updated_block) and
- (ALeft.n_operation = ARight.n_operation) and
- TAccountKeyEqualityComparer.AreEqual(ALeft.accountInfo.accountKey, ARight.accountInfo.accountKey);
- end;
- class function TAccountEqualityComparer.CalcHashCode(constref AValue: TAccount): UInt32;
- begin
- Result := AValue.account;
- end;
- { TAccountKeyComparer }
- function TAccountKeyComparer.Compare(constref ALeft, ARight: T): integer;
- begin
- Result := TAccountKeyComparer.DoCompare(ALeft, ARight);
- end;
- class function TAccountKeyComparer.DoCompare(constref ALeft, ARight: TAccountKey): integer;
- begin
- Result := BytesCompare(ALeft.x, ARight.x);
- if Result = 0 then
- Result := BytesCompare(ALeft.y, ARight.y);
- end;
- { TAccountKeyEqualityComparer }
- function TAccountKeyEqualityComparer.Equals(constref ALeft, ARight: TAccountKey): boolean;
- begin
- Result := TAccountKeyEqualityComparer.AreEqual(ALeft, ARight);
- end;
- function TAccountKeyEqualityComparer.GetHashCode(constref AValue: TAccountKey): UInt32;
- begin
- Result := TAccountKeyEqualityComparer.CalcHashCode(AValue);
- end;
- class function TAccountKeyEqualityComparer.AreEqual(constref ALeft, ARight: TAccountKey): boolean;
- begin
- Result := TAccountKeyComparer.DoCompare(ALeft, ARight) = 0;
- end;
- class function TAccountKeyEqualityComparer.CalcHashCode(constref AValue: TAccountKey): UInt32;
- begin
- Result := TEqualityComparer<ansistring>.Default.GetHashCode(IntToStr(AValue.EC_OpenSSL_NID) + AValue.x.ToString + AValue.y.ToString);
- end;
- { TAccountHelper }
- function TAccountHelper.GetAccountString: ansistring;
- begin
- Result := TAccountComp.AccountNumberToAccountTxtNumber(Self.account);
- end;
- function TAccountHelper.GetDisplayString: ansistring;
- begin
- Result := GetAccountString;
- if Self.Name <> nil then
- Result := Result + ': ' + Self.Name.ToString;
- end;
- function TAccountHelper.GetInfoText(const ABank: TPCBank): utf8string;
- var
- builder: TStrings;
- GC: TDisposables;
- begin
- builder := GC.AddObject(TStringList.Create) as TStrings;
- builder.Append(Format('Account: %s %s Type:%d', [TAccountComp.AccountNumberToAccountTxtNumber(self.account), IIF(Self.Name <> nil, 'Name: ' + Self.Name.ToString, ''), Self.account_type]));
- builder.Append('');
- builder.Append(Format('Current balance: %s', [TAccountComp.FormatMoney(Self.balance)]));
- builder.Append('');
- builder.Append(Format('Updated on block: %d (%d blocks ago)', [Self.updated_block, ABank.BlocksCount - Self.updated_block]));
- builder.Append(Format('Public key type: %s', [TAccountComp.GetECInfoTxt(Self.accountInfo.accountKey.EC_OpenSSL_NID)]));
- builder.Append(Format('Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.accountKey)]));
- if TAccountComp.IsAccountForSale(Self.accountInfo) then
- begin
- builder.Append('');
- builder.Append('** Account is for sale: **');
- builder.Append(Format('Price: %s', [TAccountComp.FormatMoney(Self.accountInfo.price)]));
- builder.Append(Format('Seller account (where to pay): %s', [TAccountComp.AccountNumberToAccountTxtNumber(Self.accountInfo.account_to_pay)]));
- if TAccountComp.IsAccountForSaleAcceptingTransactions(Self.accountInfo) then
- begin
- builder.Append('');
- builder.Append('** Private sale **');
- builder.Append(Format('New Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.new_publicKey)]));
- builder.Append('');
- if TAccountComp.IsAccountLocked(Self.accountInfo, ABank.BlocksCount) then
- builder.Append(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
- [Self.accountInfo.locked_until_block, ABank.BlocksCount, Self.accountInfo.locked_until_block - ABank.BlocksCount]))
- else
- builder.Append(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
- [Self.accountInfo.locked_until_block, ABank.BlocksCount]));
- end;
- end;
- Result := builder.Text;
- end;
- { TOperationResumeHelper }
- function TOperationResumeHelper.GetPrintableOPHASH: ansistring;
- begin
- Result := TCrypto.ToHexaString(Self.OperationHash);
- end;
- function TOperationResumeHelper.GetInfoText(const ABank: TPCBank): utf8string;
- var
- builder: TStrings;
- GC: TDisposables;
- begin
- if (not Self.valid) then
- exit;
- builder := GC.AddObject(TStringList.Create) as TStrings;
- if Self.Block < ABank.BlocksCount then
- if (Self.NOpInsideBlock >= 0) then
- builder.Add(Format('Block: %d/%d', [Self.Block, Self.NOpInsideBlock]))
- else
- begin
- builder.Add(Format('Block: %d', [Self.Block]));
- end
- else
- builder.Add('** Pending operation not included on blockchain **');
- builder.Add(Format('%s', [Self.OperationTxt]));
- builder.Add(Format('OpType:%d Subtype:%d', [Self.OpType, Self.OpSubtype]));
- builder.Add(Format('Operation Hash (ophash): %s', [TCrypto.ToHexaString(Self.OperationHash)]));
- if (Self.OperationHash_OLD <> nil) then
- builder.Add(Format('Old Operation Hash (old_ophash): %s', [TCrypto.ToHexaString(Self.OperationHash_OLD)]));
- if (Self.OriginalPayload <> nil) then
- begin
- builder.Add(Format('Payload length:%d', [length(Self.OriginalPayload)]));
- if Self.PrintablePayload <> '' then
- builder.Add(Format('Payload (human): %s', [Self.PrintablePayload]));
- builder.Add(Format('Payload (Hexadecimal): %s', [TCrypto.ToHexaString(Self.OriginalPayload)]));
- end;
- if Self.Balance >= 0 then
- builder.Add(Format('Final balance: %s', [TAccountComp.FormatMoney(Self.Balance)]));
- Result := builder.Text;
- end;
- { TTimeSpanHelper }
- function TTimeSpanHelper.TotalBlockCount: integer;
- begin
- Result := Round(Self.TotalSeconds / CT_NewLineSecondsAvg);
- end;
- { TWIZOperationsHelper }
- class function TWIZOperationsHelper.ValidateOperationsInput(const ASelectedAccounts: TArray<TAccount>; AWalletKeys: TWalletKeys; ANode: TNode; var AErrorMessage: string): boolean;
- begin
- Result := True;
- if Length(ASelectedAccounts) = 0 then
- begin
- AErrorMessage := 'No Selected Account Found';
- Exit(False);
- end;
- if not Assigned(AWalletKeys) then
- begin
- AErrorMessage := 'No Wallet Keys Found';
- Exit(False);
- end;
- if not Assigned(ANode) then
- begin
- AErrorMessage := 'No Node Found';
- Exit(False);
- end;
- end;
- class function TWIZOperationsHelper.IsOwnerOfWallet(AAccount: TAccount; AWalletKeys: TWalletKeys; out AWalletKey: TWalletKey; var AErrorMessage: string): boolean;
- var
- LIdx: Int32;
- begin
- Result := True;
- LIdx := AWalletKeys.IndexOfAccountKey(AAccount.accountInfo.accountKey);
- if LIdx < 0 then
- begin
- AErrorMessage := Format('Selected Account "%s" Private Key Not Found In Wallet', [AAccount.AccountString]);
- Exit(False);
- end;
- AWalletKey := AWalletKeys.Key[LIdx];
- if not Assigned(AWalletKey.PrivateKey) then
- begin
- if AWalletKey.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', [AAccount.AccountString]);
- Exit(False);
- end;
- end;
- 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, TEncoding.ANSI.GetBytes(APayloadContent));
- LValid := AEncodedPayloadBytes <> nil;
- end;
- pemEncryptWithRecipient:
- begin
- // With destination public key
- AEncodedPayloadBytes := ECIESEncrypt(ADestinationPublicKey, TEncoding.ANSI.GetBytes(APayloadContent));
- LValid := AEncodedPayloadBytes <> nil;
- 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(
- TEncoding.ANSI.GetBytes(APayloadContent), TEncoding.ANSI.GetBytes(APayloadEncryptionPassword));
- LValid := AEncodedPayloadBytes <> nil;
- end;
- pemNotEncrypt:
- begin
- // no encryption
- AEncodedPayloadBytes := TEncoding.ANSI.GetBytes(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: 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, LTemp: string;
- LAccountIdx, LNoOfOperations: integer;
- LCurrentAccount: TAccount;
- LPayloadEncodedBytes: TRawBytes;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- 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 not TWIZOperationsHelper.IsOwnerOfWallet(LCurrentAccount, LWalletKeys, LWalletKey, AErrorMessage) then
- Exit(False);
- 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;
- LDoOperation := True;
- case ASendPASCMode of
- akaAllBalance:
- if LCurrentAccount.balance > 0 then
- begin
- if LCurrentAccount.balance > AFee then
- begin
- LAmount := LCurrentAccount.balance - AFee;
- LFee := AFee;
- end
- else
- begin
- LAmount := LCurrentAccount.balance;
- LFee := 0;
- end;
- end
- else
- LDoOperation := False;
- akaSpecifiedAmount:
- if LCurrentAccount.balance > UInt64(AFee) then
- LFee := AFee
- else
- LFee := LCurrentAccount.balance;
- end;
- if LDoOperation then
- begin
- LPCOperation := TOpTransaction.CreateTransaction(
- TNode.Node.Bank.Safebox.CurrentProtocol, LCurrentAccount.account, LCurrentAccount.n_operation + 1, ADestinationAccount.account, LWalletKey.PrivateKey, LAmount, LFee, LPayloadEncodedBytes);
- try
- LTemp := Format('%d. Transfer of %s PASC from %s to %s %s', [LNoOfOperations + 1, TAccountComp.FormatMoney(LAmount), LCurrentAccount.AccountString, ADestinationAccount.AccountString, sLineBreak]);
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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, LTemp: string;
- LAccountIdx, LNoOfOperations: integer;
- LCurrentAccount, LSignerAccount: TAccount;
- LPayloadEncodedBytes: TRawBytes;
- label
- loop_start;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- LSignerAccount := ASignerAccount;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- 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 not TWIZOperationsHelper.IsOwnerOfWallet(LCurrentAccount, LWalletKeys, LWalletKey, AErrorMessage) then
- Exit(False);
- 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 >= CT_PROTOCOL_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;
- 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(LNode.Bank.Safebox.CurrentProtocol, LSignerAccount.account,
- LSignerAccount.n_operation + LNoOfOperations + 1, LCurrentAccount.account,
- LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
- end
- else
- LPCOperation := TOpChangeKey.Create(LNode.Bank.Safebox.CurrentProtocol, LCurrentAccount.account, LCurrentAccount.n_operation +
- 1, LCurrentAccount.account, LWalletKey.PrivateKey, APublicKey, LFee, LPayloadEncodedBytes);
- try
- LTemp := Format('%d. Change Key To %s', [LNoOfOperations + 1, TAccountComp.GetECInfoTxt(APublicKey.EC_OpenSSL_NID), sLineBreak]);
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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, LTemp: string;
- LAccountIdx, LNoOfOperations: integer;
- LCurrentAccount, LSignerAccount: TAccount;
- LPayloadEncodedBytes: TRawBytes;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- LSignerAccount := ASignerAccount;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- 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 not TWIZOperationsHelper.IsOwnerOfWallet(LCurrentAccount, LWalletKeys, LWalletKey, AErrorMessage) then
- Exit(False);
- 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 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 Valid Locking Block.';
- Exit(False);
- end;
- end;
- if (LNode.Bank.SafeBox.CurrentProtocol = CT_PROTOCOL_1) then
- begin
- AErrorMessage := 'This Operation Needs PROTOCOL 2 Active';
- Exit(False);
- end;
- if LSignerAccount.balance > UInt64(AFee) then
- LFee := AFee
- else
- LFee := LSignerAccount.balance;
- if not UpdatePayload(LCurrentAccount.accountInfo.accountKey, LSignerAccount.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;
- case AAccountSaleMode of
- akaPublicSale:
- LPCOperation := TOpListAccountForSale.CreateListAccountForSale(
- LNode.Bank.Safebox.CurrentProtocol, LSignerAccount.account, LSignerAccount.n_operation + 1 + LAccountIdx,
- LCurrentAccount.account, ASalePrice, LFee, ASellerAccount.account,
- APublicKey, 0, LWalletKey.PrivateKey, LPayloadEncodedBytes);
- akaPrivateSale:
- LPCOperation := TOpListAccountForSale.CreateListAccountForSale(
- LNode.Bank.Safebox.CurrentProtocol, LSignerAccount.account, LSignerAccount.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
- LTemp := Format('%d. Enlist Account %s For Sale At a Price Of %s PASC %s', [LNoOfOperations + 1, LCurrentAccount.DisplayString, TAccountComp.FormatMoney(ASalePrice), sLineBreak]);
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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.ExecuteDelistAccountFromSale(const ASelectedAccounts: TArray<TAccount>; const ASignerAccount: TAccount; 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;
- LOperationsTxt, LOperationToString, LTemp: string;
- LAccountIdx, LNoOfOperations: integer;
- LCurrentAccount, LSignerAccount: TAccount;
- LPayloadEncodedBytes: TRawBytes;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- LSignerAccount := ASignerAccount;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- 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 not TWIZOperationsHelper.IsOwnerOfWallet(LCurrentAccount, LWalletKeys, LWalletKey, AErrorMessage) then
- Exit(False);
- if not TAccountComp.IsAccountForSale(LCurrentAccount.accountInfo) then
- begin
- AErrorMessage := Format('Account "%s" is not enlisted for sale so cannot be delisted', [LCurrentAccount.AccountString]);
- Exit(False);
- end;
- if (TAccountComp.IsAccountLocked(LCurrentAccount.accountInfo, LNode.Bank.BlocksCount)) then
- begin
- AErrorMessage := Format('Target Account "%s" Is Locked Until Block %u', [LCurrentAccount.AccountString, LCurrentAccount.accountInfo.locked_until_block]);
- Exit(False);
- end;
- 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 Delisted Account %s', [LSignerAccount.AccountString, LCurrentAccount.AccountString]);
- Exit(False);
- end;
- if (LNode.Bank.SafeBox.CurrentProtocol = CT_PROTOCOL_1) then
- begin
- AErrorMessage := 'This Operation Needs PROTOCOL 2 Active';
- Exit(False);
- end;
- if LSignerAccount.balance > UInt64(AFee) then
- LFee := AFee
- else
- LFee := LSignerAccount.balance;
- if not UpdatePayload(LCurrentAccount.accountInfo.accountKey, LSignerAccount.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;
- LPCOperation := TOpDelistAccountForSale.CreateDelistAccountForSale(LNode.Bank.Safebox.CurrentProtocol,
- LSignerAccount.account, LSignerAccount.n_operation + 1 + LAccountIdx, LCurrentAccount.account, LFee, LWalletKey.PrivateKey,
- LPayloadEncodedBytes);
- try
- LTemp := Format('%d. Delist Account %s From Sale %s', [LNoOfOperations + 1, LCurrentAccount.DisplayString, sLineBreak]);
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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.ExecuteChangeAccountInfo(const ASelectedAccounts, ASignerAccounts: TArray<TAccount>; AFee: int64; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; const ANewName: TRawBytes; const ANewType: word; var AErrorMessage: string): boolean;
- var
- LWalletKey: TWalletKey;
- LWalletKeys: TWalletKeys;
- LNode: TNode;
- LPCOperation: TPCOperation;
- LOperationsHashTree: TOperationsHashTree;
- LTotalSignerFee, LFee: int64;
- LOperationsTxt, LOperationToString, LTemp: string;
- LAccountIdx, LNoOfOperations, LAccNumberIndex: integer;
- LCurrentAccount, LSignerAccount: TAccount;
- LPayloadEncodedBytes, LNewName: TRawBytes;
- LChangeType, LChangeName: boolean;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- LChangeName := False;
- LChangeType := False;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- 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 Length(ASelectedAccounts) = 1 then
- begin
- LSignerAccount := ASignerAccounts[0];
- LNewName := ANewName;
- if not TBaseType.Equals(LNewName, LCurrentAccount.Name) then
- begin
- LChangeName := True;
- if LNewName <> nil then
- begin
- if (not TPCSafeBox.ValidAccountName(LNewName, AErrorMessage)) then
- begin
- AErrorMessage := Format('New name "%s" is not a valid name: %s ', [LNewName.ToPrintable, AErrorMessage]);
- Exit(False);
- end;
- LAccNumberIndex := (TNode.Node.Bank.SafeBox.FindAccountByName(LNewName));
- if (LAccNumberIndex >= 0) then
- begin
- AErrorMessage := Format('Name "%s" is used by account %s ', [LNewName.ToPrintable, TAccountComp.AccountNumberToAccountTxtNumber(LAccNumberIndex)]);
- Exit(False);
- end;
- end;
- end;
- if (TBaseType.Equals(LNewName, LCurrentAccount.Name)) and (ANewType = LCurrentAccount.account_type) then
- begin
- AErrorMessage := 'New account name and type are same as former.';
- Exit(False);
- end;
- end
- else
- begin
- LSignerAccount := LCurrentAccount;
- end;
- LChangeType := ANewType <> LCurrentAccount.account_type;
- if not TWIZOperationsHelper.IsOwnerOfWallet(LCurrentAccount, LWalletKeys, LWalletKey, AErrorMessage) then
- Exit(False);
- if (TAccountComp.IsAccountLocked(LCurrentAccount.accountInfo, LNode.Bank.BlocksCount)) then
- begin
- AErrorMessage := Format('Target Account "%s" Is Locked Until Block %u', [LCurrentAccount.AccountString, LCurrentAccount.accountInfo.locked_until_block]);
- Exit(False);
- end;
- 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 Target Account %s', [LSignerAccount.AccountString, LCurrentAccount.AccountString]);
- Exit(False);
- end;
- if (LNode.Bank.SafeBox.CurrentProtocol = CT_PROTOCOL_1) then
- begin
- AErrorMessage := 'This Operation Needs PROTOCOL 2 Active';
- Exit(False);
- end;
- if LSignerAccount.balance > UInt64(AFee) then
- LFee := AFee
- else
- LFee := LSignerAccount.balance;
- if not UpdatePayload(LCurrentAccount.accountInfo.accountKey, LSignerAccount.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;
- LPCOperation := TOpChangeAccountInfo.CreateChangeAccountInfo(LNode.Bank.Safebox.CurrentProtocol,
- LSignerAccount.account, LSignerAccount.n_operation + 1, LCurrentAccount.account, LWalletKey.PrivateKey, False, CT_TECDSA_Public_Nul,
- LChangeName, LNewName, LChangeType, ANewType, LFee, LPayloadEncodedBytes);
- try
- if (LChangeName) and (LChangeType) then
- begin
- LTemp := Format('%d. Change Account %s Name and Type from [%s, %d] To [%s, %d] %s', [LNoOfOperations + 1, LCurrentAccount.DisplayString, LCurrentAccount.Name.ToPrintable, LCurrentAccount.account_type, LNewName.ToPrintable, ANewType, sLineBreak]);
- end
- else if LChangeName then
- begin
- LTemp := Format('%d. Change Account %s Name from [%s] To [%s] %s', [LNoOfOperations + 1, LCurrentAccount.DisplayString, LCurrentAccount.Name.ToPrintable, LNewName.ToPrintable, sLineBreak]);
- end
- else if LChangeType then
- begin
- LTemp := Format('%d. Change Account %s Type from [%d] To [%d] %s', [LNoOfOperations + 1, LCurrentAccount.DisplayString, LCurrentAccount.account_type, ANewType, sLineBreak]);
- end;
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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.ExecuteBuyAccount(const ASelectedAccounts: TArray<TAccount>; const AAccountToBuy: TAccount; AFee: int64; const APayloadEncryptionMode: TPayloadEncryptionMode; const APayloadContent, APayloadEncryptionPassword: string; const AAmount: int64; const ANewOwnerPublicKey: TAccountKey; var AErrorMessage: string): boolean;
- var
- LWalletKey: TWalletKey;
- LWalletKeys: TWalletKeys;
- LNode: TNode;
- LPCOperation: TPCOperation;
- LOperationsHashTree: TOperationsHashTree;
- LTotalSignerFee, LFee: int64;
- LOperationsTxt, LOperationToString, LTemp: string;
- LAccountIdx, LNoOfOperations, LAccNumberIndex: integer;
- LCurrentAccount, LSignerAccount: TAccount;
- LPayloadEncodedBytes, LNewName: TRawBytes;
- begin
- LWalletKeys := TWallet.Keys;
- LNode := TNode.Node;
- if not TWIZOperationsHelper.ValidateOperationsInput(ASelectedAccounts, LWalletKeys, LNode, AErrorMessage) then
- Exit(False);
- LOperationsHashTree := TOperationsHashTree.Create;
- try
- LTotalSignerFee := 0;
- LNoOfOperations := 0;
- LOperationsTxt := '';
- LOperationToString := '';
- // although buyaccount does not support multioperation for now, this loop was intentionally put here
- for LAccountIdx := Low(ASelectedAccounts) to High(ASelectedAccounts) do
- begin
- LPCOperation := nil; // reset LPCOperation to Nil
- LCurrentAccount := ASelectedAccounts[LAccountIdx];
- LSignerAccount := LCurrentAccount;
- if Length(ASelectedAccounts) <> 1 then
- begin
- AErrorMessage := 'Cannot Buy Accounts With MultiOperations. Use Only 1 Account';
- Exit(False);
- end;
- if (LNode.Bank.SafeBox.CurrentProtocol = CT_PROTOCOL_1) then
- begin
- AErrorMessage := 'This Operation Needs PROTOCOL 2 Active';
- Exit(False);
- end;
- if not TAccountComp.IsAccountForSale(AAccountToBuy.accountInfo) then
- begin
- AErrorMessage := Format('Account "%s" is not enlisted for sale', [AAccountToBuy.AccountString]);
- Exit(False);
- end;
- if LSignerAccount.balance > UInt64(AFee) then
- LFee := AFee
- else
- LFee := LSignerAccount.balance;
- if ((AAmount + LFee) > LCurrentAccount.balance) then
- begin
- AErrorMessage := Format('Insufficient Funds in Account "%s" to buy Account "%s"', [LCurrentAccount.AccountString, AAccountToBuy.AccountString]);
- Exit(False);
- end;
- if not UpdatePayload(LCurrentAccount.accountInfo.accountKey, LSignerAccount.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;
- LPCOperation := TOpBuyAccount.CreateBuy(LNode.Bank.Safebox.CurrentProtocol, LCurrentAccount.account, LCurrentAccount.n_operation + 1, AAccountToBuy.account, AAccountToBuy.accountInfo.account_to_pay,
- AAccountToBuy.accountInfo.price, AAmount, LFee, ANewOwnerPublicKey, LWalletKey.PrivateKey, LPayloadEncodedBytes);
- try
- LTemp := Format('%d. Buy Account %s for %s PASC %s', [LNoOfOperations + 1, AAccountToBuy.AccountString, TAccountComp.FormatMoney(AAmount), sLineBreak]);
- if LOperationsTxt <> '' then
- LOperationsTxt := LOperationsTxt + LTemp + sLineBreak
- else
- LOperationsTxt := sLineBreak + LTemp;
- 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.
|