123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- 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, UCrypto, UAccounts, UBlockChain, UOpTransaction, UNode, UCommon, UNetProtocol,
- Generics.Collections, Generics.Defaults, UCoreObjects, Forms, Dialogs, LCLType, UCellRenderers, UCommon.Collections;
- 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>;
- 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;
- implementation
- uses
- UMemory, UConst, UWallet, UECIES, UAES, ULog;
- { 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;
- Disposables: TDisposables;
- begin
- Balance := CT_BalanceSummary_Nil;
- LAccs := Disposables.AddObject(TList<TAccount>.Create) as TList<TAccount>;
- TNode.Node.Bank.SafeBox.StartThreadSafe;
- 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 := TNode.Node.Operations.SafeBoxTransaction.Account(LList.Get(j))
- else begin
- LAcc := TNode.Node.Bank.SafeBox.Account(LList.Get(j));
- end;
- LAccs.Add(LAcc);
- Inc(Balance.TotalPASA);
- Inc(Balance.TotalPASC, LAcc.Balance);
- end;
- end;
- finally
- 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;
- { 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;
- begin
- SetLength(Result, Length(AAccountNumbers));
- if AIncludePending then
- for i := Low(AAccountNumbers) to High(AAccountNumbers) do
- Result[i] := Self.Operations.SafeBoxTransaction.Account(AAccountNumbers[i])
- 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 : Classes.TList;
- LOps : TList<TOperationResume>;
- LOp : TPCOperation;
- LOpResume : TOperationResume;
- LAccNo : Cardinal;
- LNumOps, i : Integer;
- GC : TDisposables;
- begin
- LNumOps := 0;
- LList := GC.AddObject(Classes.TList.Create) as Classes.TList;
- LOps := GC.AddObject( TList<TOperationResume>.Create ) as TList<TOperationResume>;
- for LAccNo in AAccountNumbers do begin
- LList.Clear;
- Self.Operations.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 := Self.Operations.OperationsHashTree.GetOperation(PtrInt(LList[i]));
- if TPCOperation.OperationToOperationResume(0, LOp, False, LAccNo, LOpResume) then begin
- LOpResume.NOpInsideBlock := i;
- LOpResume.Block := Node.Operations.OperationBlock.block;
- LOpResume.Balance := Node.Operations.SafeBoxTransaction.Account(LAccNo {Op.SignerAccount}).balance;
- LOps.Add(LOpResume);
- end;
- end;
- 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 : Classes.TList;
- 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( Classes.TList.Create ) as Classes.TList;
- // 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[PtrInt(LRelevantBlockOps.Items[i])];
- If TPCOperation.OperationToOperationResume(i, LOp, False, LAccNo, LOpResume) then begin
- // LOpResume.NOpInsideBlock := LOp.tag; // Note: Used Op.tag to include operation index inside a list
- 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 := BinStrComp(ALeft.x, ARight.x);
- if Result = 0 then
- Result := BinStrComp(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 + AValue.y);
- end;
- { TAccountHelper }
- function TAccountHelper.GetAccountString: ansistring;
- begin
- Result := TAccountComp.AccountNumberToAccountTxtNumber(Self.account);
- end;
- function TAccountHelper.GetDisplayString: ansistring;
- begin
- Result := GetAccountString;
- if Self.Name <> '' then
- Result := Result + ': ' + Self.Name;
- 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 <> '', 'Name: ' + Self.Name, ''), Self.account_type]));
- builder.Append('');
- builder.Append(Format('Current balance: %s', [TAccountComp.FormatMoney(Self.balance)]));
- builder.Append('');
- builder.Append(Format('Updated on block: %d (%d blocks ago)', [Self.updated_block, ABank.BlocksCount - Self.updated_block]));
- builder.Append(Format('Public key type: %s', [TAccountComp.GetECInfoTxt(Self.accountInfo.accountKey.EC_OpenSSL_NID)]));
- builder.Append(Format('Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.accountKey)]));
- if TAccountComp.IsAccountForSale(Self.accountInfo) then
- begin
- builder.Append('');
- builder.Append('** Account is for sale: **');
- builder.Append(Format('Price: %s', [TAccountComp.FormatMoney(Self.accountInfo.price)]));
- builder.Append(Format('Seller account (where to pay): %s', [TAccountComp.AccountNumberToAccountTxtNumber(Self.accountInfo.account_to_pay)]));
- if TAccountComp.IsAccountForSaleAcceptingTransactions(Self.accountInfo) then
- begin
- builder.Append('');
- builder.Append('** Private sale **');
- builder.Append(Format('New Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.new_publicKey)]));
- builder.Append('');
- if TAccountComp.IsAccountLocked(Self.accountInfo, ABank.BlocksCount) then
- builder.Append(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
- [Self.accountInfo.locked_until_block, ABank.BlocksCount, Self.accountInfo.locked_until_block - ABank.BlocksCount]))
- else
- builder.Append(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
- [Self.accountInfo.locked_until_block, ABank.BlocksCount]));
- end;
- end;
- 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 <> '') then
- builder.Add(Format('Old Operation Hash (old_ophash): %s', [TCrypto.ToHexaString(Self.OperationHash_OLD)]));
- if (Self.OriginalPayload <> '') then
- begin
- builder.Add(Format('Payload length:%d', [length(Self.OriginalPayload)]));
- if Self.PrintablePayload <> '' then
- builder.Add(Format('Payload (human): %s', [Self.PrintablePayload]));
- builder.Add(Format('Payload (Hexadecimal): %s', [TCrypto.ToHexaString(Self.OriginalPayload)]));
- end;
- if Self.Balance >= 0 then
- builder.Add(Format('Final balance: %s', [TAccountComp.FormatMoney(Self.Balance)]));
- Result := builder.Text;
- end;
- { TTimeSpanHelper }
- function TTimeSpanHelper.TotalBlockCount: integer;
- begin
- Result := Round(Self.TotalSeconds / CT_NewLineSecondsAvg);
- end;
- end.
|