UCoreUtils.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. unit UCoreUtils;
  2. { Copyright (c) 2018 by Herman Schoenfeld
  3. Distributed under the MIT software license, see the accompanying file LICENSE
  4. or visit http://www.opensource.org/licenses/mit-license.php.
  5. This unit is a part of the PascalCoin Project, an infinitely scalable
  6. cryptocurrency. Find us here:
  7. Web: https://www.pascalcoin.org
  8. Source: https://github.com/PascalCoin/PascalCoin
  9. Acknowledgements:
  10. - Ugochukwu Mmaduekwe - added TOperationsManager class
  11. THIS LICENSE HEADER MUST NOT BE REMOVED.
  12. }
  13. {$mode delphi}
  14. {$modeswitch nestedprocvars}
  15. interface
  16. uses
  17. Classes, SysUtils, UCrypto, UAccounts, UBlockChain, UOpTransaction, UNode, UCommon, UNetProtocol,
  18. Generics.Collections, Generics.Defaults, UCoreObjects, Forms, Dialogs, LCLType, UCellRenderers, UCommon.Collections;
  19. type
  20. { TAccountComparer }
  21. TAccountComparer = class(TComparer<TAccount>)
  22. function Compare(constref ALeft, ARight: T): integer; override;
  23. class function DoCompare(constref ALeft, ARight: TAccount): integer; inline;
  24. end;
  25. { TAccountEqualityComparer }
  26. TAccountEqualityComparer = class(TEqualityComparer<TAccount>)
  27. public
  28. function Equals(constref ALeft, ARight: TAccount): boolean; override;
  29. function GetHashCode(constref AValue: TAccount): UInt32; override;
  30. class function AreEqual(constref ALeft, ARight: TAccount): boolean;
  31. class function CalcHashCode(constref AValue: TAccount): UInt32;
  32. end;
  33. { TAccountKeyComparer }
  34. TAccountKeyComparer = class(TComparer<TAccountKey>)
  35. function Compare(constref ALeft, ARight: T): integer; override;
  36. class function DoCompare(constref ALeft, ARight: TAccountKey): integer; inline;
  37. end;
  38. { TAccountKeyEqualityComparer }
  39. TAccountKeyEqualityComparer = class(TEqualityComparer<TAccountKey>)
  40. public
  41. function Equals(constref ALeft, ARight: TAccountKey): boolean; override;
  42. function GetHashCode(constref AValue: TAccountKey): UInt32; override;
  43. class function AreEqual(constref ALeft, ARight: TAccountKey): boolean;
  44. class function CalcHashCode(constref AValue: TAccountKey): UInt32;
  45. end;
  46. { TCoreTool }
  47. TCoreTool = class
  48. public
  49. class function GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>; static;
  50. class function GetOperationShortText(const OpType, OpSubType: DWord): ansistring; static; inline;
  51. class function GetUserBalance(IncludePending: boolean = False): TBalanceSummary;
  52. class function GetUserAccounts(IncludePending: boolean = False): TArray<TAccount>; overload;
  53. class function GetUserAccounts(out Balance: TBalanceSummary; IncludePending: boolean = False): TArray<TAccount>; overload;
  54. class function GetUserAccountNumbers: TArray<cardinal>;
  55. end;
  56. { TNodeHelper }
  57. TNodeHelper = class helper for TNode
  58. function HasBestKnownBlockchainTip: boolean;
  59. function BlockTip : Cardinal;
  60. function GetAccount(AAccountNumber : Cardinal; AIncludePending : boolean = true) : TAccount;
  61. function GetAccounts(const AAccountNumbers : array of Cardinal; AIncludePending : boolean = true) : TArray<TAccount>;
  62. function GetPendingOperationsAffectingAccounts(const AAccountNumbers: array of Cardinal; ASkipCount, ATakeCount : Integer) : TArray<TOperationResume>;
  63. function GetStoredOperationsAffectingAccounts(const AAccountNumbers : array of Cardinal; ABlockDepth, ASkipCount, ATakeCount : Integer) : TArray<TOperationResume>;
  64. end;
  65. { TAccountHelper }
  66. TAccountHelper = record helper for TAccount
  67. function GetAccountString : AnsiString;
  68. function GetDisplayString : AnsiString;
  69. function GetInfoText(const ABank : TPCBank) : utf8string;
  70. property AccountString : AnsiString read GetAccountString;
  71. property DisplayString : AnsiString read GetDisplayString;
  72. end;
  73. { TOperationResumeHelper }
  74. TOperationResumeHelper = record helper for TOperationResume
  75. function GetPrintableOPHASH : AnsiString;
  76. function GetInfoText(const ABank : TPCBank) : utf8string;
  77. end;
  78. { TTimeSpanHelper }
  79. TTimeSpanHelper = record helper for TTimeSpan
  80. function TotalBlockCount : Integer;
  81. end;
  82. implementation
  83. uses
  84. UMemory, UConst, UWallet, UECIES, UAES, ULog;
  85. { TCoreTool }
  86. class function TCoreTool.GetSignerCandidates(ANumOps: integer; ASingleOperationFee: int64; const ACandidates: array of TAccount): TArray<TAccount>;
  87. var
  88. i, PoorSenderCount: integer;
  89. Fee, maxSignerFee, minSignerFee: int64;
  90. acc: TAccount;
  91. begin
  92. //make deep copy of accounts!!! Very Important
  93. Result := TArrayTool<TAccount>.Copy(ACandidates);
  94. Fee := ASingleOperationFee;
  95. PoorSenderCount := 0;
  96. for i := Low(Result) to High(Result) do
  97. begin
  98. acc := Result[i];
  99. if (acc.Balance < Fee) then
  100. Inc(PoorSenderCount);
  101. end;
  102. maxSignerFee := ANumOps * Fee;
  103. minSignerFee := maxSignerFee - (PoorSenderCount * Fee);
  104. for i := High(Result) downto Low(Result) do
  105. begin
  106. acc := Result[i];
  107. if not (acc.Balance >= maxSignerFee) then
  108. TArrayTool<TAccount>.RemoveAt(Result, i);
  109. end;
  110. end;
  111. class function TCoreTool.GetOperationShortText(const OpType, OpSubType: DWord): ansistring;
  112. begin
  113. case OpType of
  114. CT_PseudoOp_Reward: case OpSubType of
  115. 0, CT_PseudoOpSubtype_Miner: Result := 'Miner Reward';
  116. CT_PseudoOpSubtype_Developer: Result := 'Developer Reward';
  117. else
  118. Result := 'Unknown';
  119. end;
  120. CT_Op_Transaction: case OpSubType of
  121. CT_OpSubtype_TransactionSender: Result := 'Send';
  122. CT_OpSubtype_TransactionReceiver: Result := 'Receive';
  123. CT_OpSubtype_BuyTransactionBuyer: Result := 'Buy Account Direct';
  124. CT_OpSubtype_BuyTransactionTarget: Result := 'Purchased Account Direct';
  125. CT_OpSubtype_BuyTransactionSeller: Result := 'Sold Account Direct';
  126. else
  127. Result := 'Unknown';
  128. end;
  129. CT_Op_Changekey: Result := 'Change Key (legacy)';
  130. CT_Op_Recover: Result := 'Recover';
  131. CT_Op_ListAccountForSale: case OpSubType of
  132. CT_OpSubtype_ListAccountForPublicSale: Result := 'For Sale';
  133. CT_OpSubtype_ListAccountForPrivateSale: Result := 'Exclusive Sale';
  134. else
  135. Result := 'Unknown';
  136. end;
  137. CT_Op_DelistAccount: Result := 'Remove Sale';
  138. CT_Op_BuyAccount: case OpSubType of
  139. CT_OpSubtype_BuyAccountBuyer: Result := 'Buy Account';
  140. CT_OpSubtype_BuyAccountTarget: Result := 'Purchased Account';
  141. CT_OpSubtype_BuyAccountSeller: Result := 'Sold Account';
  142. else
  143. Result := 'Unknown';
  144. end;
  145. CT_Op_ChangeKeySigned: Result := 'Change Key';
  146. CT_Op_ChangeAccountInfo: Result := 'Change Info';
  147. CT_Op_MultiOperation: case OpSubType of
  148. CT_OpSubtype_MultiOperation_Global: Result := 'Mixed-Transfer';
  149. CT_OpSubtype_MultiOperation_AccountInfo: Result := 'Mixed-Change';
  150. end;
  151. else
  152. Result := 'Unknown';
  153. end;
  154. end;
  155. class function TCoreTool.GetUserBalance(IncludePending: boolean = False): TBalanceSummary;
  156. begin
  157. GetUserAccounts(Result, IncludePending);
  158. end;
  159. class function TCoreTool.GetUserAccounts(IncludePending: boolean = False): TArray<TAccount>;
  160. var
  161. LBalance : TBalanceSummary;
  162. begin
  163. Result := GetUserAccounts(LBalance, IncludePending);
  164. end;
  165. class function TCoreTool.GetUserAccounts(out Balance: TBalanceSummary; IncludePending: boolean = False): TArray<TAccount>;
  166. var
  167. i, j: integer;
  168. LAccs: TList<TAccount>;
  169. LAcc: TAccount;
  170. LList: TOrderedCardinalList;
  171. Disposables: TDisposables;
  172. begin
  173. Balance := CT_BalanceSummary_Nil;
  174. LAccs := Disposables.AddObject(TList<TAccount>.Create) as TList<TAccount>;
  175. TNode.Node.Bank.SafeBox.StartThreadSafe;
  176. try
  177. for i := 0 to TWallet.Keys.Count - 1 do begin
  178. LList := TWallet.Keys.AccountsKeyList.AccountKeyList[i];
  179. for j := 0 to LList.Count - 1 do begin
  180. if IncludePending then
  181. LAcc := TNode.Node.Operations.SafeBoxTransaction.Account(LList.Get(j))
  182. else begin
  183. LAcc := TNode.Node.Bank.SafeBox.Account(LList.Get(j));
  184. end;
  185. LAccs.Add(LAcc);
  186. Inc(Balance.TotalPASA);
  187. Inc(Balance.TotalPASC, LAcc.Balance);
  188. end;
  189. end;
  190. finally
  191. TNode.Node.Bank.SafeBox.EndThreadSave;
  192. end;
  193. LAccs.Sort(TAccountComparer.Create);
  194. Result := LAccs.ToArray;
  195. end;
  196. class function TCoreTool.GetUserAccountNumbers: TArray<cardinal>;
  197. var
  198. i, j: integer;
  199. LAccs: TSortedList<cardinal>;
  200. LList: TOrderedCardinalList;
  201. Disposables: TDisposables;
  202. begin
  203. LAccs := Disposables.AddObject(TSortedList<cardinal>.Create) as TSortedList<cardinal>;
  204. for i := 0 to TWallet.Keys.AccountsKeyList.Count - 1 do
  205. begin
  206. LList := TWallet.Keys.AccountsKeyList.AccountKeyList[i];
  207. for j := 0 to LList.Count - 1 do
  208. LAccs.Add(LList.Get(j));
  209. end;
  210. Result := LAccs.ToArray;
  211. end;
  212. { TNodeHelper }
  213. function TNodeHelper.HasBestKnownBlockchainTip: boolean;
  214. var
  215. LReady: boolean;
  216. LMsg: ansistring;
  217. LDestBlock : Cardinal;
  218. begin
  219. LReady := Self.Bank.IsReady(LMsg);
  220. if LReady and TNetData.NetData.IsGettingNewBlockChainFromClient(LMsg) then begin
  221. LDestBlock := TNetData.NetData.MaxRemoteOperationBlock.block;
  222. Result := Self.Bank.BlocksCount = TNetData.NetData.MaxRemoteOperationBlock.block;
  223. end;
  224. end;
  225. function TNodeHelper.BlockTip : Cardinal;
  226. begin
  227. Result := ClipValue(Self.Bank.BlocksCount - 1, 0, MaxInt);
  228. end;
  229. function TNodeHelper.GetAccount(AAccountNumber : Cardinal; AIncludePending : boolean = true) : TAccount;
  230. var LOps : TArray<TAccount>;
  231. begin
  232. LOps := Self.GetAccounts([AAccountNumber], AIncludePending);
  233. Result := LOps[Low(Lops)];
  234. end;
  235. function TNodeHelper.GetAccounts(const AAccountNumbers : array of Cardinal; AIncludePending : boolean = true) : TArray<TAccount>;
  236. var i : integer;
  237. begin
  238. SetLength(Result, Length(AAccountNumbers));
  239. if AIncludePending then
  240. for i := Low(AAccountNumbers) to High(AAccountNumbers) do
  241. Result[i] := Self.Operations.SafeBoxTransaction.Account(AAccountNumbers[i])
  242. else
  243. for i := Low(AAccountNumbers) to High(AAccountNumbers) do
  244. Result[i] := Self.Bank.SafeBox.Account(AAccountNumbers[i]);
  245. end;
  246. function TNodeHelper.GetPendingOperationsAffectingAccounts(const AAccountNumbers: array of Cardinal; ASkipCount, ATakeCount : Integer) : TArray<TOperationResume>;
  247. var
  248. LList : Classes.TList;
  249. LOps : TList<TOperationResume>;
  250. LOp : TPCOperation;
  251. LOpResume : TOperationResume;
  252. LAccNo : Cardinal;
  253. LNumOps, i : Integer;
  254. GC : TDisposables;
  255. begin
  256. LNumOps := 0;
  257. LList := GC.AddObject(Classes.TList.Create) as Classes.TList;
  258. LOps := GC.AddObject( TList<TOperationResume>.Create ) as TList<TOperationResume>;
  259. for LAccNo in AAccountNumbers do begin
  260. LList.Clear;
  261. Self.Operations.OperationsHashTree.GetOperationsAffectingAccount(LAccNo, LList);
  262. if LList.Count > 0 then
  263. for i := LList.Count - 1 downto 0 do begin
  264. Inc(LNumOps);
  265. if (LNumOps > ASkipCount) AND (LNumOps <= ASkipCount + ATakeCount) then begin
  266. LOp := Self.Operations.OperationsHashTree.GetOperation(PtrInt(LList[i]));
  267. if TPCOperation.OperationToOperationResume(0, LOp, False, LAccNo, LOpResume) then begin
  268. LOpResume.NOpInsideBlock := i;
  269. LOpResume.Block := Node.Operations.OperationBlock.block;
  270. LOpResume.Balance := Node.Operations.SafeBoxTransaction.Account(LAccNo {Op.SignerAccount}).balance;
  271. LOps.Add(LOpResume);
  272. end;
  273. end;
  274. end;
  275. end;
  276. Result := LOps.ToArray;
  277. end;
  278. function TNodeHelper.GetStoredOperationsAffectingAccounts(const AAccountNumbers : array of Cardinal; ABlockDepth, ASkipCount, ATakeCount : Integer) : TArray<TOperationResume>;
  279. type
  280. __TList_Cardinal = TList<Cardinal>;
  281. var
  282. i : Integer;
  283. LBlock : Cardinal;
  284. LRelevantBlockOps : Classes.TList;
  285. LOp : TPCOperation;
  286. LOpResume : TOperationResume;
  287. LFoundOps : TList<TOperationResume>;
  288. LOpsComp : TPCOperationsComp;
  289. LAccountBalances : TDictionary<Cardinal, Cardinal>;
  290. LAccounts : TArray<TAccount>;
  291. LDisposables : TDisposables;
  292. LBlockEnd, LNumOps : integer;
  293. LBlockTraversal : TSortedHashSet<Cardinal>;
  294. LAccountsToScanAtBlock : TObjectDictionary<Cardinal, __TList_Cardinal>;
  295. LAcc : TAccount;
  296. procedure MarkAccountAsScannableAtBlock(AAccountNo, ABlockNo : cardinal);
  297. begin
  298. if NOT LAccountsToScanAtBlock.ContainsKey(ABlockNo) then
  299. LAccountsToScanAtBlock.Add(ABlockNo, __TList_Cardinal.Create);
  300. LAccountsToScanAtBlock[ABlockNo].Add(AAccountNo);
  301. end;
  302. procedure ScanBlock(ABlockNum : Cardinal);
  303. var
  304. i : integer;
  305. LAccNo : Cardinal;
  306. LPrevUpdatedBlock : Cardinal;
  307. LDisposables : TDisposables;
  308. begin
  309. LOpsComp := LDisposables.AddObject( TPCOperationsComp.Create(nil) ) as TPCOperationsComp;
  310. LRelevantBlockOps := LDisposables.AddObject( Classes.TList.Create ) as Classes.TList;
  311. // load block
  312. if not Bank.Storage.LoadBlockChainBlock(LOpsComp, ABlockNum) then begin
  313. TLog.NewLog(ltdebug, ClassName, 'Block ' + inttostr(ABlockNum)+' not found. Cannot read operations');
  314. exit;
  315. end;
  316. // scan for each account
  317. for LAccNo in LAccountsToScanAtBlock[ABlockNum] do begin
  318. LRelevantBlockOps.Clear;
  319. LOpsComp.OperationsHashTree.GetOperationsAffectingAccount(LAccNo, LRelevantBlockOps);
  320. for i := LRelevantBlockOps.Count - 1 downto 0 do begin
  321. LOp := LOpsComp.Operation[PtrInt(LRelevantBlockOps.Items[i])];
  322. If TPCOperation.OperationToOperationResume(i, LOp, False, LAccNo, LOpResume) then begin
  323. // LOpResume.NOpInsideBlock := LOp.tag; // Note: Used Op.tag to include operation index inside a list
  324. LOpResume.time := LOpsComp.OperationBlock.timestamp;
  325. LOpResume.Block := ABlockNum;
  326. If LAccountBalances[LAccNo] >= 0 then begin
  327. LOpResume.Balance := LAccountBalances[LAccNo];
  328. LAccountBalances.AddOrSetValue(LAccNo, LOpResume.Balance - (LOpResume.Amount + LOpResume.Fee));
  329. end else LOpResume.Balance := -1; // Undetermined
  330. // Apply skip/take
  331. inc(LNumOps);
  332. if (LNumOps > ASkipCount) And (LNumOps <= ASkipCount + ATakeCount) then
  333. LFoundOps.Add(LOpResume);
  334. // short-cirtcuit exit if taken enough
  335. if LFoundOps.Count >= ATakeCount then exit;
  336. end;
  337. end;
  338. // Add previous updated block into traversal set
  339. LPrevUpdatedBlock := LOpsComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(LAccNo, ABlockNum);
  340. if LPrevUpdatedBlock < ABlockNum then begin
  341. LBlockTraversal.Add(LPrevUpdatedBlock);
  342. MarkAccountAsScannableAtBlock(LAccNo, LPrevUpdatedBlock);
  343. end;
  344. end;
  345. end;
  346. function GetAccountLastUpdateBlock(constref AAccount : TAccount) : Cardinal;
  347. begin
  348. Result := AAccount.updated_block;
  349. end;
  350. begin
  351. // Init
  352. LNumOps := 0;
  353. LBlockTraversal := LDisposables.AddObject( TSortedHashSet<Cardinal>.Create( TComparerTool<Cardinal>.Inverted( TComparer<Cardinal>.Default ) ) ) as TSortedHashSet<Cardinal>;
  354. LAccountsToScanAtBlock := LDisposables.AddObject( TObjectDictionary<Cardinal, __TList_Cardinal>.Create([doOwnsValues])) as TObjectDictionary<Cardinal, __TList_Cardinal>;
  355. LFoundOps := LDisposables.AddObject( TList<TOperationResume>.Create ) as TList<TOperationResume>;
  356. LAccountBalances := LDisposables.AddObject(TDictionary<Cardinal, Cardinal>.Create) as TDictionary<Cardinal, Cardinal>;
  357. LBlockEnd := ClipValue( Self.BlockTip - ABlockDepth, 0, Self.BlockTip);
  358. // First get all accounts, their balances and initial traversal set
  359. LAccounts := Self.GetAccounts(AAccountNumbers, False);
  360. for i := Low(LAccounts) to High(LAccounts) do begin
  361. // if account is modified in block-tip
  362. LAcc := LAccounts[i];
  363. LAccountBalances.AddOrSetValue(LAcc.account, LAcc.Balance); // track account balances
  364. LBlockTraversal.Add(LAcc.updated_block);
  365. MarkAccountAsScannableAtBlock(LAcc.account, LAcc.updated_block);
  366. end;
  367. // Traverse the set of "last updated" blocks in DESCENDING order
  368. while LBlockTraversal.Count > 0 do begin
  369. LBlock := TSortedHashSetTool<Cardinal>.Pop( LBlockTraversal );
  370. if LBlock < LBlockEnd then continue;
  371. ScanBlock(LBlock); // note: this will update LBlockTraversals with prev updated blocks, so loops until finished
  372. if LFoundOps.Count >= ATakeCount then exit;
  373. end;
  374. // return array result
  375. Result := LFoundOps.ToArray;
  376. end;
  377. { TAccountComparer }
  378. function TAccountComparer.Compare(constref ALeft, ARight: TAccount): integer;
  379. begin
  380. Result := TAccountComparer.DoCompare(ALeft, ARight);
  381. end;
  382. class function TAccountComparer.DoCompare(constref ALeft, ARight: TAccount): integer;
  383. begin
  384. Result := TCompare.UInt64(ALeft.account, ARight.account);
  385. end;
  386. { TAccountEqualityComparer }
  387. function TAccountEqualityComparer.Equals(constref ALeft, ARight: TAccount): boolean;
  388. begin
  389. Result := TAccountEqualityComparer.AreEqual(ALeft, ARight);
  390. end;
  391. function TAccountEqualityComparer.GetHashCode(constref AValue: TAccount): UInt32;
  392. begin
  393. Result := TAccountEqualityComparer.CalcHashCode(AValue);
  394. end;
  395. class function TAccountEqualityComparer.AreEqual(constref ALeft, ARight: TAccount): boolean;
  396. begin
  397. Result :=
  398. (ALeft.account = ARight.account) and
  399. (ALeft.balance = ARight.balance) and
  400. (ALeft.updated_block = ARight.updated_block) and
  401. (ALeft.n_operation = ARight.n_operation) and
  402. TAccountKeyEqualityComparer.AreEqual(ALeft.accountInfo.accountKey, ARight.accountInfo.accountKey);
  403. end;
  404. class function TAccountEqualityComparer.CalcHashCode(constref AValue: TAccount): UInt32;
  405. begin
  406. Result := AValue.account;
  407. end;
  408. { TAccountKeyComparer }
  409. function TAccountKeyComparer.Compare(constref ALeft, ARight: T): integer;
  410. begin
  411. Result := TAccountKeyComparer.DoCompare(ALeft, ARight);
  412. end;
  413. class function TAccountKeyComparer.DoCompare(constref ALeft, ARight: TAccountKey): integer;
  414. begin
  415. Result := BinStrComp(ALeft.x, ARight.x);
  416. if Result = 0 then
  417. Result := BinStrComp(ALeft.y, ARight.y);
  418. end;
  419. { TAccountKeyEqualityComparer }
  420. function TAccountKeyEqualityComparer.Equals(constref ALeft, ARight: TAccountKey): boolean;
  421. begin
  422. Result := TAccountKeyEqualityComparer.AreEqual(ALeft, ARight);
  423. end;
  424. function TAccountKeyEqualityComparer.GetHashCode(constref AValue: TAccountKey): UInt32;
  425. begin
  426. Result := TAccountKeyEqualityComparer.CalcHashCode(AValue);
  427. end;
  428. class function TAccountKeyEqualityComparer.AreEqual(constref ALeft, ARight: TAccountKey): boolean;
  429. begin
  430. Result := TAccountKeyComparer.DoCompare(ALeft, ARight) = 0;
  431. end;
  432. class function TAccountKeyEqualityComparer.CalcHashCode(constref AValue: TAccountKey): UInt32;
  433. begin
  434. Result := TEqualityComparer<ansistring>.Default.GetHashCode(IntToStr(AValue.EC_OpenSSL_NID) + AValue.x + AValue.y);
  435. end;
  436. { TAccountHelper }
  437. function TAccountHelper.GetAccountString: ansistring;
  438. begin
  439. Result := TAccountComp.AccountNumberToAccountTxtNumber(Self.account);
  440. end;
  441. function TAccountHelper.GetDisplayString: ansistring;
  442. begin
  443. Result := GetAccountString;
  444. if Self.Name <> '' then
  445. Result := Result + ': ' + Self.Name;
  446. end;
  447. function TAccountHelper.GetInfoText(const ABank: TPCBank): utf8string;
  448. var
  449. builder: TStrings;
  450. GC: TDisposables;
  451. begin
  452. builder := GC.AddObject(TStringList.Create) as TStrings;
  453. builder.Append(Format('Account: %s %s Type:%d', [TAccountComp.AccountNumberToAccountTxtNumber(self.account), IIF(Self.Name <> '', 'Name: ' + Self.Name, ''), Self.account_type]));
  454. builder.Append('');
  455. builder.Append(Format('Current balance: %s', [TAccountComp.FormatMoney(Self.balance)]));
  456. builder.Append('');
  457. builder.Append(Format('Updated on block: %d (%d blocks ago)', [Self.updated_block, ABank.BlocksCount - Self.updated_block]));
  458. builder.Append(Format('Public key type: %s', [TAccountComp.GetECInfoTxt(Self.accountInfo.accountKey.EC_OpenSSL_NID)]));
  459. builder.Append(Format('Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.accountKey)]));
  460. if TAccountComp.IsAccountForSale(Self.accountInfo) then
  461. begin
  462. builder.Append('');
  463. builder.Append('** Account is for sale: **');
  464. builder.Append(Format('Price: %s', [TAccountComp.FormatMoney(Self.accountInfo.price)]));
  465. builder.Append(Format('Seller account (where to pay): %s', [TAccountComp.AccountNumberToAccountTxtNumber(Self.accountInfo.account_to_pay)]));
  466. if TAccountComp.IsAccountForSaleAcceptingTransactions(Self.accountInfo) then
  467. begin
  468. builder.Append('');
  469. builder.Append('** Private sale **');
  470. builder.Append(Format('New Base58 Public key: %s', [TAccountComp.AccountPublicKeyExport(Self.accountInfo.new_publicKey)]));
  471. builder.Append('');
  472. if TAccountComp.IsAccountLocked(Self.accountInfo, ABank.BlocksCount) then
  473. builder.Append(Format('PURCHASE IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
  474. [Self.accountInfo.locked_until_block, ABank.BlocksCount, Self.accountInfo.locked_until_block - ABank.BlocksCount]))
  475. else
  476. builder.Append(Format('PURCHASE IS NOT SECURE (Expired on block %d, current %d)',
  477. [Self.accountInfo.locked_until_block, ABank.BlocksCount]));
  478. end;
  479. end;
  480. Result := builder.Text;
  481. end;
  482. { TOperationResumeHelper }
  483. function TOperationResumeHelper.GetPrintableOPHASH: ansistring;
  484. begin
  485. Result := TCrypto.ToHexaString(Self.OperationHash);
  486. end;
  487. function TOperationResumeHelper.GetInfoText(const ABank: TPCBank): utf8string;
  488. var
  489. builder: TStrings;
  490. GC: TDisposables;
  491. begin
  492. if (not Self.valid) then
  493. exit;
  494. builder := GC.AddObject(TStringList.Create) as TStrings;
  495. if Self.Block < ABank.BlocksCount then
  496. if (Self.NOpInsideBlock >= 0) then
  497. builder.Add(Format('Block: %d/%d', [Self.Block, Self.NOpInsideBlock]))
  498. else
  499. begin
  500. builder.Add(Format('Block: %d', [Self.Block]));
  501. end
  502. else
  503. builder.Add('** Pending operation not included on blockchain **');
  504. builder.Add(Format('%s', [Self.OperationTxt]));
  505. builder.Add(Format('OpType:%d Subtype:%d', [Self.OpType, Self.OpSubtype]));
  506. builder.Add(Format('Operation Hash (ophash): %s', [TCrypto.ToHexaString(Self.OperationHash)]));
  507. if (Self.OperationHash_OLD <> '') then
  508. builder.Add(Format('Old Operation Hash (old_ophash): %s', [TCrypto.ToHexaString(Self.OperationHash_OLD)]));
  509. if (Self.OriginalPayload <> '') then
  510. begin
  511. builder.Add(Format('Payload length:%d', [length(Self.OriginalPayload)]));
  512. if Self.PrintablePayload <> '' then
  513. builder.Add(Format('Payload (human): %s', [Self.PrintablePayload]));
  514. builder.Add(Format('Payload (Hexadecimal): %s', [TCrypto.ToHexaString(Self.OriginalPayload)]));
  515. end;
  516. if Self.Balance >= 0 then
  517. builder.Add(Format('Final balance: %s', [TAccountComp.FormatMoney(Self.Balance)]));
  518. Result := builder.Text;
  519. end;
  520. { TTimeSpanHelper }
  521. function TTimeSpanHelper.TotalBlockCount: integer;
  522. begin
  523. Result := Round(Self.TotalSeconds / CT_NewLineSecondsAvg);
  524. end;
  525. end.