UNode.pas 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849
  1. unit UNode;
  2. { Copyright (c) 2016 by Albert Molina
  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. If you like it, consider a donation using Bitcoin:
  10. 16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
  11. THIS LICENSE HEADER MUST NOT BE REMOVED.
  12. }
  13. { UNode contains the basic structure to operate
  14. - An app can only contains 1 node.
  15. - A node contains:
  16. - 1 Bank
  17. - 1 NetServer (Accepting incoming connections)
  18. - 1 Operations (Operations has actual BlockChain with Operations and SafeBankTransaction to operate with the Bank)
  19. - 0..x NetClients
  20. - 0..x Miners
  21. }
  22. {$IFDEF FPC}
  23. {$MODE Delphi}
  24. {$ENDIF}
  25. interface
  26. uses
  27. Classes, SysUtils,
  28. {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes,
  29. UBlockChain, UNetProtocol, UAccounts, UCrypto, UEPasa, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
  30. {$I ./../config.inc}
  31. Type
  32. { TNode }
  33. TSearchOperationResult = (found, invalid_params, blockchain_block_not_found);
  34. TNodeNotifyEvents = Class;
  35. TNode = Class(TComponent)
  36. private
  37. FNodeLog : TLog;
  38. FLockMempool : TPCCriticalSection;
  39. FOperationSequenceLock : TPCCriticalSection;
  40. FNotifyList : TList<TNodeNotifyEvents>;
  41. FBank : TPCBank;
  42. FMemPoolOperationsComp : TPCOperationsComp;
  43. FMemPoolAddingOperationsList : TOrderedRawList;
  44. FNetServer : TNetServer;
  45. FBCBankNotify : TPCBankNotify;
  46. FPeerCache : String;
  47. FDisabledsNewBlocksCount : Integer;
  48. FSentOperations : TOrderedRawList;
  49. FBroadcastData : Boolean;
  50. FUpdateBlockchain: Boolean;
  51. {$IFDEF BufferOfFutureOperations}
  52. FBufferAuxWaitingOperations : TOperationsHashTree;
  53. {$ENDIF}
  54. Procedure OnBankNewBlock(Sender : TObject);
  55. procedure SetNodeLogFilename(const Value: String);
  56. function GetNodeLogFilename: String;
  57. protected
  58. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  59. public
  60. Class Function Node : TNode;
  61. Class Function NodeExists : Boolean;
  62. Class Procedure DecodeIpStringToNodeServerAddressArray(Const Ips : String; Var NodeServerAddressArray : TNodeServerAddressArray);
  63. Class Function EncodeNodeServerAddressArrayToIpString(Const NodeServerAddressArray : TNodeServerAddressArray) : String;
  64. Constructor Create(AOwner : TComponent); override;
  65. Destructor Destroy; override;
  66. Property Bank : TPCBank read FBank;
  67. Function NetServer : TNetServer;
  68. Procedure NotifyNetClientMessage(Sender : TNetConnection; Const TheMessage : String);
  69. // Return Operations count in the Mempool
  70. function MempoolOperationsCount : Integer;
  71. // Return Account based on current state (Safebox + Mempool operations)
  72. function GetMempoolAccount(AAccountNumber : Cardinal) : TAccount;
  73. // Locking methods to access to the Mempool
  74. function LockMempoolRead : TPCOperationsComp;
  75. procedure UnlockMempoolRead;
  76. function LockMempoolWrite : TPCOperationsComp;
  77. procedure UnlockMempoolWrite;
  78. //
  79. Function AddNewBlockChain(SenderConnection : TNetConnection; NewBlockOperations: TPCOperationsComp; var errors: String): Boolean;
  80. Function AddOperations(SenderConnection : TNetConnection; AOperationsHashTreeToAdd : TOperationsHashTree; OperationsResult : TOperationsResumeList; var errors: String): Integer;
  81. Function AddOperation(SenderConnection : TNetConnection; Operation : TPCOperation; var errors: String): Boolean;
  82. Function SendNodeMessage(Target : TNetConnection; const TheMessage : String; var errors : String) : Boolean;
  83. //
  84. Procedure NotifyBlocksChanged;
  85. //
  86. procedure GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
  87. procedure GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation : Integer; SearchBackwardsStartingAtBlock : Cardinal=0); overload;
  88. Function FindOperation(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : Boolean;
  89. Function FindOperationExt(Const OperationComp : TPCOperationsComp; Const OperationHash : TRawBytes; var block : Cardinal; var operation_block_index : Integer) : TSearchOperationResult;
  90. Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOperationResult;
  91. Function FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high : Cardinal; OpResumeList : TOperationsResumeList) : TSearchOperationResult;
  92. //
  93. Procedure InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
  94. Procedure AutoDiscoverNodes(Const ips : String);
  95. Function IsBlockChainValid(var WhyNot : String) : Boolean;
  96. Function IsReady(Var CurrentProcess : String) : Boolean;
  97. Property PeerCache : String read FPeerCache write FPeerCache;
  98. Procedure DisableNewBlocks;
  99. Procedure EnableNewBlocks;
  100. Property NodeLogFilename : String read GetNodeLogFilename write SetNodeLogFilename;
  101. Property OperationSequenceLock : TPCCriticalSection read FOperationSequenceLock;
  102. function TryLockNode(MaxWaitMilliseconds : Cardinal) : Boolean;
  103. procedure UnlockNode;
  104. //
  105. function GetAccountsAvailableByPublicKey(const APubKeys : TList<TAccountKey>; out AOnSafebox, AOnMempool : Integer) : Integer; overload;
  106. function GetAccountsAvailableByPublicKey(const APubKey : TAccountKey; out AOnSafebox, AOnMempool : Integer) : Integer; overload;
  107. //
  108. Property BroadcastData : Boolean read FBroadcastData write FBroadcastData;
  109. Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
  110. procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
  111. class function NodeVersion : String;
  112. class function GetPascalCoinDataFolder : String;
  113. class procedure SetPascalCoinDataFolder(const ANewDataFolder : String);
  114. //
  115. function TryFindAccountByKey(const APubKey : TAccountKey; out AAccountNumber : Cardinal) : Boolean;
  116. function TryFindPublicSaleAccount(AMaximumPrice : Int64; APreventRaceCondition : Boolean; out AAccountNumber : Cardinal) : Boolean;
  117. Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean; overload;
  118. Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean; overload;
  119. Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean; overload;
  120. Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean; overload;
  121. End;
  122. TThreadSafeNodeNotifyEvent = Class(TPCThread)
  123. FNodeNotifyEvents : TNodeNotifyEvents;
  124. FNotifyBlocksChanged : Boolean;
  125. FNotifyOperationsChanged : Boolean;
  126. Procedure SynchronizedProcess;
  127. protected
  128. procedure BCExecute; override;
  129. public
  130. Constructor Create(ANodeNotifyEvents : TNodeNotifyEvents);
  131. End;
  132. { TNodeMessage Event }
  133. TNodeMessageEvent = Procedure(NetConnection : TNetConnection; MessageData : String) of object;
  134. { TNodeMessageManyEvent }
  135. TNodeMessageManyEvent = TArray<TNodeMessageEvent>;
  136. { TNodeMessageManyEventHelper }
  137. TNodeMessageManyEventHelper = record helper for TNodeMessageManyEvent
  138. procedure Add(listener : TNodeMessageEvent);
  139. procedure Remove(listener : TNodeMessageEvent);
  140. procedure Invoke(NetConnection : TNetConnection; MessageData : String);
  141. end;
  142. { TNodeNotifyEvents is ThreadSafe and will only notify in the main thread }
  143. TNodeNotifyEvents = Class(TComponent)
  144. private
  145. FNode: TNode;
  146. FOnKeyActivity: TNotifyEvent;
  147. FPendingNotificationsList : TPCThreadList<Pointer>;
  148. FThreadSafeNodeNotifyEvent : TThreadSafeNodeNotifyEvent;
  149. FOnBlocksChanged: TNotifyEvent;
  150. FOnOperationsChanged: TNotifyEvent;
  151. FMessages : TStringList;
  152. FOnNodeMessageEvent: TNodeMessageEvent;
  153. FWatchKeys: TOrderedAccountKeysList;
  154. procedure SetNode(const Value: TNode);
  155. Procedure NotifyBlocksChanged;
  156. Procedure NotifyOperationsChanged;
  157. procedure SetWatchKeys(AValue: TOrderedAccountKeysList);
  158. protected
  159. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  160. public
  161. Constructor Create(AOwner : TComponent); override;
  162. Destructor Destroy; override;
  163. Property Node : TNode read FNode write SetNode;
  164. Property OnBlocksChanged : TNotifyEvent read FOnBlocksChanged write FOnBlocksChanged;
  165. Property OnOperationsChanged : TNotifyEvent read FOnOperationsChanged write FOnOperationsChanged;
  166. Property OnNodeMessageEvent : TNodeMessageEvent read FOnNodeMessageEvent write FOnNodeMessageEvent;
  167. Property WatchKeys : TOrderedAccountKeysList read FWatchKeys write SetWatchKeys;
  168. Property OnKeyActivity : TNotifyEvent read FOnKeyActivity write FOnKeyActivity;
  169. End;
  170. TThreadNodeNotifyNewBlock = Class(TPCThread)
  171. FNetConnection : TNetConnection;
  172. FSanitizedOperationsHashTree : TOperationsHashTree;
  173. FNewBlockOperations : TPCOperationsComp;
  174. protected
  175. procedure BCExecute; override;
  176. public
  177. Constructor Create(NetConnection : TNetConnection; MakeACopyOfNewBlockOperations: TPCOperationsComp; MakeACopyOfSanitizedOperationsHashTree : TOperationsHashTree);
  178. destructor Destroy; override;
  179. End;
  180. TThreadNodeNotifyOperations = Class(TPCThread)
  181. FNetConnection : TNetConnection;
  182. protected
  183. procedure BCExecute; override;
  184. public
  185. Constructor Create(NetConnection : TNetConnection; MakeACopyOfOperationsHashTree : TOperationsHashTree);
  186. destructor Destroy; override;
  187. End;
  188. implementation
  189. Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator,
  190. UFolderHelper, USettings;
  191. var _Node : TNode;
  192. _PascalCoinDataFolder : String;
  193. { TNode }
  194. function TNode.AddNewBlockChain(SenderConnection: TNetConnection; NewBlockOperations: TPCOperationsComp; var errors: String): Boolean;
  195. Var i,j,maxResend : Integer;
  196. nc : TNetConnection;
  197. s,sClientRemoteAddr : String;
  198. OpBlock : TOperationBlock;
  199. opsht : TOperationsHashTree;
  200. minBlockResend : Cardinal;
  201. resendOp : TPCOperation;
  202. LLockedMempool : TPCOperationsComp;
  203. begin
  204. Result := false;
  205. errors := '';
  206. if Assigned(SenderConnection) then sClientRemoteAddr := SenderConnection.ClientRemoteAddr
  207. else sClientRemoteAddr:='(SELF)';
  208. if FDisabledsNewBlocksCount>0 then begin
  209. TLog.NewLog(lterror,Classname,Format('Cannot Add new BlockChain due is adding disabled - Connection:%s NewBlock:%s',[
  210. sClientRemoteAddr,TPCOperationsComp.OperationBlockToText(NewBlockOperations.OperationBlock)]));
  211. errors := 'Adding blocks is disabled';
  212. exit;
  213. end;
  214. NewBlockOperations.Lock; // New protection
  215. Try
  216. If NewBlockOperations.OperationBlock.block<>Bank.BlocksCount then begin
  217. errors := 'New block number ('+IntToStr(NewBlockOperations.OperationBlock.block)+') not valid! (Expected '+IntToStr(Bank.BlocksCount)+')';
  218. exit;
  219. end;
  220. OpBlock := NewBlockOperations.OperationBlock;
  221. TLog.NewLog(ltdebug,Classname,Format('Starting AddNewBlockChain %d Operations %d from %s NewBlock:%s',[
  222. OpBlock.block,NewBlockOperations.Count,sClientRemoteAddr,TPCOperationsComp.OperationBlockToText(OpBlock)]));
  223. If Not TPCThread.TryProtectEnterCriticalSection(Self,5000,FLockMempool) then begin
  224. If NewBlockOperations.OperationBlock.block<>Bank.BlocksCount then exit;
  225. s := 'Cannot AddNewBlockChain due blocking lock operations node';
  226. TLog.NewLog(lterror,Classname,s);
  227. if TThread.CurrentThread.ThreadID=MainThreadID then raise Exception.Create(s) else exit;
  228. end;
  229. try
  230. // Check block number:
  231. if TPCOperationsComp.EqualsOperationBlock(Bank.LastOperationBlock,NewBlockOperations.OperationBlock) then begin
  232. errors := 'Duplicated block';
  233. exit;
  234. end;
  235. MarkVerifiedECDSASignaturesFromMemPool(NewBlockOperations); // Improvement speed v4.0.2
  236. // Improvement TNode speed 2.1.6
  237. // Does not need to save a FOperations backup because is Sanitized by "TNode.OnBankNewBlock"
  238. Result := Bank.AddNewBlockChainBlock(NewBlockOperations,TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock,errors);
  239. if Result then begin
  240. {$IFDEF USE_ABSTRACTMEM}
  241. Bank.SafeBox.PCAbstractMem.FlushCache;
  242. {$ENDIF}
  243. if Assigned(SenderConnection) then begin
  244. FNodeLog.NotifyNewLog(ltupdate,SenderConnection.ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,sClientRemoteAddr,OpBlock.block_payload.ToPrintable,
  245. OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
  246. end else begin
  247. FNodeLog.NotifyNewLog(ltupdate,ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload.ToPrintable,
  248. OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
  249. end;
  250. end else begin
  251. if Assigned(SenderConnection) then begin
  252. FNodeLog.NotifyNewLog(lterror,SenderConnection.ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,sClientRemoteAddr,OpBlock.block_payload.ToPrintable,errors,
  253. OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
  254. end else begin
  255. FNodeLog.NotifyNewLog(lterror,ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload.ToPrintable,errors,
  256. OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
  257. end;
  258. end;
  259. if Result then begin
  260. opsht := TOperationsHashTree.Create;
  261. Try
  262. j := Random(3); // j=0,1 or 2
  263. If (Bank.LastBlockFound.OperationBlock.block>j) then
  264. minBlockResend:=Bank.LastBlockFound.OperationBlock.block - j
  265. else minBlockResend:=1;
  266. maxResend := CT_MaxResendMemPoolOperations;
  267. i := 0;
  268. LLockedMempool := LockMempoolRead;
  269. Try
  270. While (opsht.OperationsCount<maxResend) And (i<LLockedMempool.Count) do begin
  271. resendOp := LLockedMempool.Operation[i];
  272. j := FSentOperations.GetTag(resendOp.Sha256);
  273. if (j=0) Or (j<=minBlockResend) then begin
  274. // Only will "re-send" operations that where received on block <= minBlockResend
  275. opsht.AddOperationToHashTree(resendOp);
  276. // Add to sent operations
  277. FSentOperations.SetTag(resendOp.Sha256,LLockedMempool.OperationBlock.block); // Set tag new value
  278. FSentOperations.Add(LLockedMempool.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
  279. end else begin
  280. {$IFDEF HIGHLOG}TLog.NewLog(ltInfo,ClassName,'Sanitized operation not included to resend (j:'+IntToStr(j)+'>'+inttostr(minBlockResend)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);{$ENDIF}
  281. end;
  282. inc(i);
  283. end;
  284. If LLockedMempool.Count>0 then begin
  285. TLog.NewLog(ltinfo,classname,Format('Resending %d operations for new block (Buffer Pending Operations:%d)',[opsht.OperationsCount,LLockedMempool.Count]));
  286. {$IFDEF HIGHLOG}
  287. if opsht.OperationsCount>0 then begin
  288. for i := 0 to opsht.OperationsCount - 1 do begin
  289. TLog.NewLog(ltInfo,ClassName,'Resending ('+inttostr(i+1)+'/'+inttostr(opsht.OperationsCount)+'): '+opsht.GetOperation(i).ToString);
  290. end;
  291. end
  292. {$ENDIF}
  293. end;
  294. Finally
  295. UnlockMempoolRead;
  296. End;
  297. // Clean sent operations buffer
  298. j := 0;
  299. for i := FSentOperations.Count-1 downto 0 do begin
  300. If (FSentOperations.GetTag(i)<Bank.LastBlockFound.OperationBlock.block-2) then begin
  301. FSentOperations.Delete(i);
  302. inc(j);
  303. end;
  304. end;
  305. TLog.NewLog(ltdebug,ClassName,'Buffer Sent operations: '+IntToStr(FSentOperations.Count)+' Deleted old operations: '+IntToStr(j));
  306. // Notify to clients
  307. {$IFnDEF TESTING_NO_POW_CHECK}
  308. if FBroadcastData then begin
  309. j := TNetData.NetData.ConnectionsCountAll;
  310. for i:=0 to j-1 do begin
  311. if (TNetData.NetData.GetConnection(i,nc)) then begin
  312. if (nc.Connected) And (nc.RemoteOperationBlock.block>0) then begin
  313. if (nc<>SenderConnection) then begin
  314. TThreadNodeNotifyNewBlock.Create(nc,Bank.LastBlockFound,opsht);
  315. end else if (opsht.OperationsCount>0) then begin
  316. // New 4.0.1 Notify not added operations
  317. TThreadNodeNotifyOperations.Create(nc,opsht);
  318. end;
  319. end;
  320. end;
  321. end;
  322. end;
  323. {$ENDIF}
  324. Finally
  325. opsht.Free;
  326. End;
  327. end;
  328. finally
  329. FLockMempool.Release;
  330. TLog.NewLog(ltdebug,Classname,Format('Finalizing AddNewBlockChain %d Operations %d from %s NewBlock:%s',[
  331. OpBlock.block,NewBlockOperations.Count,sClientRemoteAddr,TPCOperationsComp.OperationBlockToText(OpBlock)]));
  332. End;
  333. finally
  334. NewBlockOperations.Unlock;
  335. end;
  336. if Result then begin
  337. // Notify it!
  338. NotifyBlocksChanged;
  339. end;
  340. end;
  341. function TNode.AddOperation(SenderConnection : TNetConnection; Operation: TPCOperation; var errors: String): Boolean;
  342. var ops : TOperationsHashTree;
  343. begin
  344. ops := TOperationsHashTree.Create;
  345. Try
  346. ops.AddOperationToHashTree(Operation);
  347. Result := AddOperations(SenderConnection,ops,Nil,errors)=1;
  348. Finally
  349. ops.Free;
  350. End;
  351. end;
  352. function TNode.AddOperations(SenderConnection : TNetConnection; AOperationsHashTreeToAdd : TOperationsHashTree; OperationsResult : TOperationsResumeList; var errors: String): Integer;
  353. {$IFDEF BufferOfFutureOperations}
  354. Procedure Process_BufferOfFutureOperations(ALockedMempool : TPCOperationsComp; valids_operations : TOperationsHashTree);
  355. Var i,j, nAdded, nDeleted : Integer;
  356. sAcc : TAccount;
  357. ActOp : TPCOperation;
  358. e : String;
  359. Begin
  360. // Prior to add new operations, will try to add waiting ones
  361. nAdded := 0; nDeleted := 0;
  362. For j:=0 to 3 do begin
  363. i := 0;
  364. While (i<FBufferAuxWaitingOperations.OperationsCount) do begin
  365. ActOp := FBufferAuxWaitingOperations.GetOperation(i);
  366. If ALockedMempool.AddOperation(true,ActOp,e) then begin
  367. TLog.NewLog(ltInfo,Classname,Format('AddOperation FromBufferWaitingOperations %d/%d: %s',[i+1,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
  368. inc(nAdded);
  369. valids_operations.AddOperationToHashTree(ActOp);
  370. FBufferAuxWaitingOperations.Delete(i);
  371. end else begin
  372. sAcc := ALockedMempool.SafeBoxTransaction.Account(ActOp.SignerAccount);
  373. If (sAcc.n_operation>ActOp.N_Operation) Or
  374. ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance>0)) then begin
  375. FBufferAuxWaitingOperations.Delete(i);
  376. inc(nDeleted);
  377. end else inc(i);
  378. end;
  379. end;
  380. end;
  381. If (nAdded>0) or (nDeleted>0) or (FBufferAuxWaitingOperations.OperationsCount>0) then begin
  382. TLog.NewLog(ltInfo,Classname,Format('FromBufferWaitingOperations status - Added:%d Deleted:%d Buffer:%d',[nAdded,nDeleted,FBufferAuxWaitingOperations.OperationsCount]));
  383. end;
  384. end;
  385. {$ENDIF}
  386. Var
  387. i,j,nSpam,nError,nRepeated : Integer;
  388. LValids_operations : TOperationsHashTree;
  389. nc : TNetConnection;
  390. e : String;
  391. s : String;
  392. OPR : TOperationResume;
  393. ActOp : TPCOperation;
  394. {$IFDEF BufferOfFutureOperations}sAcc : TAccount;{$ENDIF}
  395. LLockedMempool : TPCOperationsComp;
  396. LOpsToAdd : TList<TPCOperation>;
  397. LTempSafeboxTransaction : TPCSafeBoxTransaction;
  398. LTickCount : TTickCount;
  399. begin
  400. Result := -1; // -1 Means Node is blocked or disabled
  401. if Assigned(OperationsResult) then OperationsResult.Clear;
  402. if FDisabledsNewBlocksCount>0 then begin
  403. errors := Format('Cannot Add Operations due is adding disabled - OpCount:%d',[AOperationsHashTreeToAdd.OperationsCount]);
  404. TLog.NewLog(ltinfo,Classname,errors);
  405. exit;
  406. end;
  407. nSpam := 0;
  408. nRepeated := 0;
  409. nError := 0;
  410. errors := '';
  411. Result := 0;
  412. LTickCount := TPlatform.GetTickCount;
  413. LValids_operations := TOperationsHashTree.Create;
  414. try
  415. LOpsToAdd := TList<TPCOperation>.Create;
  416. try
  417. // In order to allow income operations from multiple threads will divide the
  418. // process in LOCKING steps: (instead of a single global locking)
  419. // 1 - Add received AOperationsHashTreeToAdd in global FMemPoolAddingOperationsList
  420. // without duplicates. This allows receive same operation twice and execute
  421. // only first received
  422. // 2 - Verify signatures in a multithread (if CPU's available)
  423. // 3 - For each not repeated operation, try to add to mempool
  424. // Step 1: Add operations to FMemPoolAddingOperationsList
  425. LLockedMempool := LockMempoolWrite;
  426. try
  427. for i := 0 to AOperationsHashTreeToAdd.OperationsCount-1 do begin
  428. ActOp := AOperationsHashTreeToAdd.GetOperation(i);
  429. j := FMemPoolAddingOperationsList.IndexOf( ActOp.Sha256 );
  430. if (j<0) then begin
  431. LOpsToAdd.Add(ActOp);
  432. FMemPoolAddingOperationsList.Add(ActOp.Sha256);
  433. end;
  434. end;
  435. finally
  436. UnlockMempoolWrite;
  437. end;
  438. // Step 2:
  439. LTempSafeboxTransaction := TPCSafeBoxTransaction.Create(Bank.SafeBox);
  440. try
  441. TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LTempSafeboxTransaction,LOpsToAdd,Nil);
  442. finally
  443. LTempSafeboxTransaction.Free;
  444. end;
  445. {$IFDEF BufferOfFutureOperations}
  446. LLockedMempool := LockMempoolWrite;
  447. try
  448. Process_BufferOfFutureOperations(LLockedMempool,LValids_operations);
  449. finally
  450. UnlockMempoolWrite;
  451. end;
  452. {$ENDIF}
  453. // Step 3:
  454. for j := 0 to LOpsToAdd.Count-1 do begin
  455. ActOp := LOpsToAdd[j];
  456. LLockedMempool := LockMempoolWrite;
  457. try
  458. If (LLockedMempool.OperationsHashTree.IndexOfOperation(ActOp)<0) then begin
  459. // Protocol 2 limitation: In order to prevent spam of operations without Fee, will protect it
  460. If (ActOp.OperationFee=0) And (Bank.SafeBox.CurrentProtocol>=CT_PROTOCOL_2) And
  461. (LLockedMempool.OperationsHashTree.CountOperationsBySameSignerWithoutFee(ActOp.SignerAccount)>=CT_MaxAccountOperationsPerBlockWithoutFee) then begin
  462. inc(nSpam);
  463. e := Format('Account %s zero fee operations per block limit:%d',[TAccountComp.AccountNumberToAccountTxtNumber(ActOp.SignerAccount),CT_MaxAccountOperationsPerBlockWithoutFee]);
  464. if (nSpam<=5) then begin // To Limit errors in a String... speed up
  465. if (errors<>'') then errors := errors+' ';
  466. errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(LOpsToAdd.Count)+':'+e;
  467. end;
  468. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('AddOperation spam %d/%d: %s - Error:%s',[(j+1),LOpsToAdd.Count,ActOp.ToString,e]));{$ENDIF}
  469. if Assigned(OperationsResult) then begin
  470. TPCOperation.OperationToOperationResume(0,ActOp,True,ActOp.SignerAccount,OPR);
  471. OPR.valid := false;
  472. OPR.NOpInsideBlock:=-1;
  473. OPR.OperationHash := Nil;
  474. OPR.errors := e;
  475. OperationsResult.Add(OPR);
  476. end;
  477. end else begin
  478. if (LLockedMempool.AddOperation(true,ActOp,e)) then begin
  479. inc(Result);
  480. FSentOperations.Add(ActOp.Sha256,LLockedMempool.OperationBlock.block);
  481. LValids_operations.AddOperationToHashTree(ActOp);
  482. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('AddOperation %d/%d: %s',[(j+1),LOpsToAdd.Count,ActOp.ToString]));{$ENDIF}
  483. if Assigned(OperationsResult) then begin
  484. TPCOperation.OperationToOperationResume(0,ActOp,True,ActOp.SignerAccount,OPR);
  485. OPR.NOpInsideBlock:=LLockedMempool.Count-1;
  486. OPR.Balance := LLockedMempool.SafeBoxTransaction.Account(ActOp.SignerAccount).balance;
  487. OperationsResult.Add(OPR);
  488. end;
  489. end else begin
  490. inc(nError);
  491. if (nError<=5) then begin // To Limit errors in a String... speed up
  492. if (errors<>'') then errors := errors+' ';
  493. errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(LOpsToAdd.Count)+':'+e;
  494. end;
  495. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('AddOperation invalid/duplicated %d/%d: %s - Error:%s',[(j+1),LOpsToAdd.Count,ActOp.ToString,e]));{$ENDIF}
  496. if Assigned(OperationsResult) then begin
  497. TPCOperation.OperationToOperationResume(0,ActOp,True,ActOp.SignerAccount,OPR);
  498. OPR.valid := false;
  499. OPR.NOpInsideBlock:=-1;
  500. OPR.OperationHash := Nil;
  501. OPR.errors := e;
  502. OperationsResult.Add(OPR);
  503. end;
  504. {$IFDEF BufferOfFutureOperations}
  505. // Used to solve 2.0.0 "invalid order of operations" bug
  506. If (Assigned(SenderConnection)) Then begin
  507. if ActOp.SignerAccount<LLockedMempool.SafeBoxTransaction.FreezedSafeBox.AccountsCount then begin
  508. sAcc := LLockedMempool.SafeBoxTransaction.Account(ActOp.SignerAccount);
  509. If (sAcc.n_operation<ActOp.N_Operation) Or
  510. ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance=0) And (ActOp.OperationFee>0) And (ActOp.OpType = CT_Op_Changekey)) then begin
  511. If FBufferAuxWaitingOperations.IndexOfOperation(ActOp)<0 then begin
  512. FBufferAuxWaitingOperations.AddOperationToHashTree(ActOp);
  513. TLog.NewLog(ltInfo,Classname,Format('New FromBufferWaitingOperations %d/%d (new buffer size:%d): %s',[j+1,LOpsToAdd.Count,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
  514. end;
  515. end;
  516. end;
  517. end;
  518. {$ENDIF}
  519. end;
  520. end;
  521. end else begin
  522. inc(nRepeated);
  523. e := Format('AddOperation made before %d/%d: %s',[(j+1),LOpsToAdd.Count,ActOp.ToString]);
  524. if (nRepeated<=5) then begin // To Limit errors in a String... speed up
  525. if (errors<>'') then errors := errors+' ';
  526. errors := errors + e;
  527. end;
  528. if Assigned(OperationsResult) then begin
  529. TPCOperation.OperationToOperationResume(0,ActOp,True,ActOp.SignerAccount,OPR);
  530. OPR.valid := false;
  531. OPR.NOpInsideBlock:=-1;
  532. OPR.OperationHash := Nil;
  533. OPR.errors := e;
  534. OperationsResult.Add(OPR);
  535. end;
  536. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,Format('AddOperation made before %d/%d: %s',[(j+1),LOpsToAdd.Count,ActOp.ToString]));{$ENDIF}
  537. end;
  538. finally
  539. UnlockMempoolWrite;
  540. end;
  541. end; // for i
  542. If Result<>0 then begin
  543. LLockedMempool := LockMempoolRead;
  544. try
  545. // Save operations buffer
  546. Bank.Storage.SavePendingBufferOperations(LLockedMempool.OperationsHashTree);
  547. finally
  548. UnlockMempoolRead;
  549. end;
  550. LTickCount := TPlatform.GetElapsedMilliseconds(LTickCount);
  551. if LTickCount=0 then LTickCount:=1;
  552. if Assigned(SenderConnection) then begin
  553. s := SenderConnection.ClientRemoteAddr;
  554. end else s := '(SELF)';
  555. TLog.NewLog(ltdebug,Classname,Format('Finalizing AddOperations from %s Operations:%d of %d valids:%d spam:%d invalids:%d repeated:%d Miliseconds:%d %.1f ops/sec',
  556. [s,LOpsToAdd.Count,AOperationsHashTreeToAdd.OperationsCount,Result,nSpam,nError,nRepeated,LTickCount,LOpsToAdd.Count * 1000 / LTickCount]));
  557. if FBroadcastData then begin
  558. // Send to other nodes
  559. j := TNetData.NetData.ConnectionsCountAll;
  560. for i:=0 to j-1 do begin
  561. If TNetData.NetData.GetConnection(i,nc) then begin
  562. if (nc<>SenderConnection) And (nc.Connected) And (nc.RemoteOperationBlock.block>0) then TThreadNodeNotifyOperations.Create(nc,LValids_operations);
  563. end;
  564. end;
  565. end;
  566. end;
  567. finally
  568. // Remove LOpsToAdd from FMemPoolAddingOperationsList
  569. LLockedMempool := LockMempoolWrite;
  570. try
  571. for i := 0 to LOpsToAdd.Count-1 do begin
  572. ActOp := LOpsToAdd[i];
  573. FMemPoolAddingOperationsList.Remove(ActOp.Sha256);
  574. end;
  575. finally
  576. UnlockMempoolWrite;
  577. end;
  578. LOpsToAdd.Free;
  579. end;
  580. finally
  581. LValids_operations.Free;
  582. end;
  583. // Notify it!
  584. for i := 0 to FNotifyList.Count-1 do begin
  585. TNodeNotifyEvents( FNotifyList[i] ).NotifyOperationsChanged;
  586. end;
  587. end;
  588. procedure TNode.AutoDiscoverNodes(const ips: String);
  589. Var i,j : Integer;
  590. nsarr : TNodeServerAddressArray;
  591. begin
  592. DecodeIpStringToNodeServerAddressArray(ips+';'+PeerCache,nsarr);
  593. for i := low(nsarr) to high(nsarr) do begin
  594. TNetData.NetData.AddServer(nsarr[i]);
  595. end;
  596. j := (CT_MaxServersConnected - TNetData.NetData.ConnectionsCount(true));
  597. if j<=0 then exit;
  598. TNetData.NetData.DiscoverServers;
  599. end;
  600. constructor TNode.Create(AOwner: TComponent);
  601. begin
  602. FSentOperations := TOrderedRawList.Create;
  603. FNodeLog := TLog.Create(Self);
  604. FNodeLog.ProcessGlobalLogs := false;
  605. RegisterOperationsClass;
  606. if Assigned(_Node) then raise Exception.Create('Duplicate nodes protection');
  607. TLog.NewLog(ltInfo,ClassName,'TNode.Create '+NodeVersion);
  608. inherited;
  609. FDisabledsNewBlocksCount := 0;
  610. FLockMempool := TPCCriticalSection.Create('TNode_LockMempool');
  611. FOperationSequenceLock := TPCCriticalSection.Create('TNode_OperationSequenceLock');
  612. FBank := TPCBank.Create(Self);
  613. FBCBankNotify := TPCBankNotify.Create(Self);
  614. FBCBankNotify.Bank := FBank;
  615. FBCBankNotify.OnNewBlock := OnBankNewBlock;
  616. FNetServer := TNetServer.Create;
  617. FMemPoolOperationsComp := TPCOperationsComp.Create(Nil);
  618. FMemPoolOperationsComp.bank := FBank;
  619. FNotifyList := TList<TNodeNotifyEvents>.Create;
  620. FMemPoolAddingOperationsList := TOrderedRawList.Create;
  621. {$IFDEF BufferOfFutureOperations}
  622. FBufferAuxWaitingOperations := TOperationsHashTree.Create;
  623. {$ENDIF}
  624. FBroadcastData := True;
  625. FUpdateBlockchain := True;
  626. if Not Assigned(_Node) then _Node := Self;
  627. end;
  628. class procedure TNode.DecodeIpStringToNodeServerAddressArray(const Ips: String;
  629. var NodeServerAddressArray: TNodeServerAddressArray);
  630. Function GetIp(var ips_string : String; var nsa : TNodeServerAddress) : Boolean;
  631. Const CT_IP_CHARS = ['a'..'z','A'..'Z','0'..'9','.','-','_'];
  632. var i : Integer;
  633. port : String;
  634. begin
  635. nsa := CT_TNodeServerAddress_NUL;
  636. Result := false;
  637. if length(trim(ips_string))=0 then begin
  638. ips_string := '';
  639. exit;
  640. end;
  641. // Delete invalid chars:
  642. i := 0;
  643. while (i<=High(ips_string)) AND (NOT (ips_string.Chars[i] IN CT_IP_CHARS)) do inc(i);
  644. if (i>Low(ips_string)) then ips_string := ips_string.Substring(i,Length(ips_string));
  645. // Capture IP value
  646. i := 0;
  647. while (i<=High(ips_string)) and (ips_string.Chars[i] in CT_IP_CHARS) do inc(i);
  648. if (i>0) then begin
  649. nsa.ip := ips_string.Substring(0,i);
  650. // Capture possible :Port value
  651. if (i<=High(ips_string)) and (ips_string.Chars[i]=':') then begin
  652. inc(i);
  653. port := '';
  654. while (i<=High(ips_string)) and (ips_string.Chars[i] in ['0'..'9']) do begin
  655. port := port + ips_string.Chars[i];
  656. inc(i);
  657. end;
  658. nsa.port := StrToIntDef(port,0);
  659. end;
  660. end;
  661. ips_string := ips_string.Substring(i+1,Length(ips_string));
  662. if nsa.port=0 then nsa.port := CT_NetServer_Port;
  663. Result := (Trim(nsa.ip)<>'');
  664. end;
  665. Var i,j : Integer;
  666. ips_string : String;
  667. nsa : TNodeServerAddress;
  668. begin
  669. SetLength(NodeServerAddressArray,0);
  670. ips_string := Ips;
  671. repeat
  672. If GetIp(ips_string,nsa) then begin
  673. SetLength(NodeServerAddressArray,length(NodeServerAddressArray)+1);
  674. NodeServerAddressArray[High(NodeServerAddressArray)] := nsa;
  675. end;
  676. until (Length(ips_string)=0);
  677. end;
  678. destructor TNode.Destroy;
  679. Var step : String;
  680. begin
  681. TLog.NewLog(ltInfo,ClassName,'TNode.Destroy START');
  682. Try
  683. step := 'Deleting critical section';
  684. FreeAndNil(FLockMempool);
  685. FreeAndNil(FOperationSequenceLock);
  686. step := 'Desactivating server';
  687. FNetServer.Active := false;
  688. step := 'Destroying NetServer';
  689. FreeAndNil(FNetServer);
  690. step := 'Destroying NotifyList';
  691. FreeAndNil(FNotifyList);
  692. step := 'Destroying Operations';
  693. FreeAndNil(FMemPoolOperationsComp);
  694. FreeAndNil(FMemPoolAddingOperationsList);
  695. step := 'Assigning NIL to node var';
  696. if _Node=Self then _Node := Nil;
  697. Step := 'Destroying SentOperations list';
  698. FreeAndNil(FSentOperations);
  699. step := 'Destroying Bank';
  700. FreeAndNil(FBCBankNotify);
  701. FreeAndNil(FBank);
  702. {$IFDEF BufferOfFutureOperations}
  703. FreeAndNil(FBufferAuxWaitingOperations);
  704. {$ENDIF}
  705. step := 'inherited';
  706. FreeAndNil(FNodeLog);
  707. inherited;
  708. Except
  709. On E:Exception do begin
  710. TLog.NewLog(lterror,Classname,'Error destroying Node step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
  711. Raise;
  712. end;
  713. End;
  714. TLog.NewLog(ltInfo,ClassName,'TNode.Destroy END');
  715. end;
  716. procedure TNode.DisableNewBlocks;
  717. begin
  718. inc(FDisabledsNewBlocksCount);
  719. end;
  720. procedure TNode.EnableNewBlocks;
  721. begin
  722. if FDisabledsNewBlocksCount=0 then raise Exception.Create('Dev error 20160924-1');
  723. dec(FDisabledsNewBlocksCount);
  724. end;
  725. function TNode.TryFindAccountByKey(const APubKey: TAccountKey;
  726. out AAccountNumber: Cardinal): Boolean;
  727. // Finds the smallest numbered account with selected key (or returns false)
  728. var Lpka : TSafeboxPubKeysAndAccounts;
  729. LAccountsNumberList : TAccountsNumbersList;
  730. begin
  731. Result := False;
  732. Lpka := Bank.SafeBox.OrderedAccountKeysList;
  733. if Assigned(Lpka) then begin
  734. LAccountsNumberList := Lpka.GetAccountsUsingThisKey(APubKey);
  735. if Assigned(LAccountsNumberList) then begin
  736. if LAccountsNumberList.Count>0 then begin
  737. AAccountNumber := LAccountsNumberList.Get(0);
  738. Result := True;
  739. end;
  740. end;
  741. end;
  742. end;
  743. function TNode.TryFindPublicSaleAccount(AMaximumPrice: Int64; APreventRaceCondition : Boolean;
  744. out AAccountNumber: Cardinal): Boolean;
  745. // Finds an account at or below argument purchase price (or returns false)
  746. // APreventRaceCondition: When True will return a random account in valid range price
  747. // Limitations: Account must be >0
  748. var LtempAccNumber : Integer;
  749. LLastValidAccount, LCurrAccount : TAccount;
  750. LContinueSearching : Boolean;
  751. begin
  752. Result := False;
  753. // Sorted list: Bank.SafeBox.AccountsOrderedBySalePrice
  754. // Note: List is sorted by Sale price (ASCENDING), but NOT by public/private sale, must check
  755. if Not Bank.SafeBox.AccountsOrderedBySalePrice.FindLowest(LtempAccNumber) then Exit(False);
  756. LCurrAccount := GetMempoolAccount(LtempAccNumber);
  757. if (LCurrAccount.accountInfo.price<=AMaximumPrice)
  758. and (TAccountComp.IsAccountForPublicSale(LCurrAccount.accountInfo)) then begin
  759. LLastValidAccount := LCurrAccount;
  760. LContinueSearching := (APreventRaceCondition) And (Random(50)=0);
  761. end else begin
  762. LLastValidAccount := CT_Account_NUL;
  763. LContinueSearching := True;
  764. end;
  765. while (LCurrAccount.accountInfo.price<=AMaximumPrice) and (LContinueSearching) do begin
  766. if TAccountComp.IsAccountForPublicSale(LCurrAccount.accountInfo) then LLastValidAccount := LCurrAccount;
  767. if Not (Bank.SafeBox.AccountsOrderedBySalePrice.FindSuccessor(LtempAccNumber,LtempAccNumber)) then Break;
  768. LCurrAccount := GetMempoolAccount(LtempAccNumber);
  769. // If price increased, then do not continue and use LastValidAccount
  770. if (LLastValidAccount.account>0)
  771. and (LLastValidAccount.accountInfo.price <> LCurrAccount.accountInfo.price) then Break;
  772. // Continue?
  773. LContinueSearching :=
  774. (LLastValidAccount.account=0) // This means that no valid account has been found yet...
  775. or
  776. (LContinueSearching And (Random(50)=0)); // Random prevention
  777. end;
  778. if (LLastValidAccount.account>0) then begin
  779. AAccountNumber := LLastValidAccount.account;
  780. Result := True;
  781. end else begin
  782. AAccountNumber := 0;
  783. Result := False;
  784. end;
  785. end;
  786. Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean;
  787. var LErrMsg : String;
  788. begin
  789. Result := TryResolveEPASA(AEPasa, AResolvedAccount, LErrMsg);
  790. end;
  791. Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean;
  792. var
  793. LAccountKey : TAccountKey;
  794. LRequiresPurchase : Boolean;
  795. begin
  796. Result := TryResolveEPASA(AEPasa, AResolvedAccount, LAccountKey, LRequiresPurchase, AErrorMessage);
  797. if Result AND AEPasa.IsPayToKey then begin
  798. Result := False;
  799. AErrorMessage := 'EPASA was a pay-to-key style';
  800. end;
  801. end;
  802. Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean;
  803. var LErrMsg : String;
  804. begin
  805. Result := TryResolveEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, LErrMsg);
  806. end;
  807. Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean;
  808. var
  809. LErrMsg : String;
  810. begin
  811. if (AEPasa.IsPayToKey) then begin
  812. // Parse account key in EPASA
  813. if NOT TAccountComp.AccountPublicKeyImport(AEPasa.Payload, AResolvedKey, LErrMsg) then begin
  814. AResolvedAccount := CT_AccountNo_NUL;
  815. AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
  816. ARequiresPurchase := False;
  817. AErrorMessage := Format('Invalid key specified in PayToKey EPASA "%s". %s',[AEPasa.ToString(), LErrMsg]);
  818. Exit(False);
  819. end;
  820. // Try to find key in safebox
  821. if TryFindAccountByKey(AResolvedKey, AResolvedAccount) then begin
  822. // Key already exists in SafeBox, so send to that account
  823. ARequiresPurchase := False;
  824. Exit(True);
  825. end;
  826. // If no key found, find optimal public purchase account
  827. if TryFindPublicSaleAccount(TSettings.MaxPayToKeyPurchasePrice, True, AResolvedAccount) then begin
  828. // Account needs to be purchased
  829. ARequiresPurchase := True;
  830. Exit(True);
  831. end;
  832. // Account could not be resolved
  833. AResolvedAccount := CT_AccountNo_NUL;
  834. AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
  835. ARequiresPurchase := False;
  836. AErrorMessage := 'No account could be resolved for pay to key EPASA';
  837. Exit(False);
  838. end else if (AEPasa.IsAddressedByName) then begin
  839. // Find account by name
  840. AResolvedAccount := Bank.SafeBox.FindAccountByName(AEPasa.AccountName);
  841. AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
  842. ARequiresPurchase := False;
  843. if AResolvedAccount = CT_AccountNo_NUL then begin
  844. // No account with name found
  845. AResolvedAccount := CT_AccountNo_NUL;
  846. AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
  847. ARequiresPurchase := False;
  848. AErrorMessage := Format('No account with name "%s" was found', [AEPasa.AccountName]);
  849. Exit(False);
  850. end;
  851. Exit(True);
  852. end;
  853. // addressed by number
  854. if NOT AEPasa.IsAddressedByNumber then raise Exception.Create('Internal Error c8ecd69d-3621-4f5e-b4f1-9926ab2f5013');
  855. if NOT AEPasa.Account.HasValue then raise Exception.Create('Internal Error 544c8cb9-b700-4b5f-93ca-4d045d0a06ae');
  856. AResolvedAccount := AEPasa.Account.Value;
  857. if (AResolvedAccount < 0) or (AResolvedAccount >= Self.Bank.AccountsCount) then begin
  858. AResolvedAccount := CT_AccountNo_NUL;
  859. AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
  860. ARequiresPurchase := False;
  861. AErrorMessage := Format('Account number %d does not exist in safebox',[AResolvedAccount]);
  862. Exit(False);
  863. end;
  864. Result := true;
  865. end;
  866. function TNode.TryLockNode(MaxWaitMilliseconds: Cardinal): Boolean;
  867. begin
  868. Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);
  869. end;
  870. procedure TNode.UnlockNode;
  871. begin
  872. FLockMempool.Release;
  873. end;
  874. procedure TNode.MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate: TPCOperationsComp);
  875. var LLockedMempool : TPCOperationsComp;
  876. begin
  877. // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
  878. // Will check if "newOperationsToValidate" operations are on MEMPOOL. If found, will set same FHasValidSignature value in order to mark as verified
  879. LLockedMempool := LockMempoolRead;
  880. try
  881. if newOperationsToValidate = LLockedMempool then Exit; // Is the same, do nothing
  882. if newOperationsToValidate.OperationBlock.protocol_version <> newOperationsToValidate.OperationBlock.protocol_version then Exit; // Must be same protocol
  883. newOperationsToValidate.Lock;
  884. try
  885. LLockedMempool.OperationsHashTree.MarkVerifiedECDSASignatures(newOperationsToValidate.OperationsHashTree);
  886. finally
  887. newOperationsToValidate.Unlock;
  888. end;
  889. finally
  890. UnlockMempoolRead;
  891. end;
  892. end;
  893. class function TNode.EncodeNodeServerAddressArrayToIpString(const NodeServerAddressArray: TNodeServerAddressArray): String;
  894. var i : Integer;
  895. begin
  896. Result := '';
  897. for i := low(NodeServerAddressArray) to high(NodeServerAddressArray) do begin
  898. if (Result<>'') then Result := Result + ';';
  899. Result := Result + NodeServerAddressArray[i].ip;
  900. if NodeServerAddressArray[i].port>0 then begin
  901. Result := Result + ':'+IntToStr(NodeServerAddressArray[i].port);
  902. end;
  903. end;
  904. end;
  905. function TNode.GetNodeLogFilename: String;
  906. begin
  907. Result := FNodeLog.FileName;
  908. end;
  909. function TNode.IsBlockChainValid(var WhyNot : String): Boolean;
  910. Var unixtimediff : Integer;
  911. begin
  912. Result :=false;
  913. if (TNetData.NetData.NetStatistics.ActiveConnections<=0) then begin
  914. WhyNot := 'No connection to check blockchain';
  915. exit;
  916. end;
  917. if (Bank.LastOperationBlock.block<=0) then begin
  918. WhyNot := 'No blockchain';
  919. exit;
  920. end;
  921. unixtimediff := UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - Bank.LastOperationBlock.timestamp;
  922. If (unixtimediff<0) then begin
  923. WhyNot := 'Invalid Last Block Time';
  924. exit;
  925. end;
  926. if (unixtimediff>(CT_NewLineSecondsAvg*10)) then begin
  927. WhyNot := 'Last block has a long time ago... '+inttostr(unixtimediff);
  928. exit;
  929. end;
  930. Result := true;
  931. end;
  932. function TNode.IsReady(var CurrentProcess: String): Boolean;
  933. var LLockedMempool : TPCOperationsComp;
  934. begin
  935. Result := false;
  936. CurrentProcess := '';
  937. if FBank.IsReady(CurrentProcess) then begin
  938. if FNetServer.Active then begin
  939. if Not TNetData.NetData.IsGettingNewBlockChainFromClient(CurrentProcess) then begin
  940. LLockedMempool := LockMempoolRead;
  941. try
  942. if TNetData.NetData.MaxRemoteOperationBlock.block>LLockedMempool.OperationBlock.block then begin
  943. CurrentProcess := 'Found block '+inttostr(TNetData.NetData.MaxRemoteOperationBlock.block)+' (Wait until downloaded)';
  944. end else begin
  945. CurrentProcess := '';
  946. Result := true;
  947. end;
  948. finally
  949. UnlockMempoolRead;
  950. end;
  951. end;
  952. end else begin
  953. CurrentProcess := 'Server not active';
  954. end;
  955. end;
  956. end;
  957. function TNode.NetServer: TNetServer;
  958. begin
  959. Result := FNetServer;
  960. end;
  961. class function TNode.Node: TNode;
  962. begin
  963. if not assigned(_Node) then _Node := TNode.Create(Nil);
  964. Result := _Node;
  965. end;
  966. class function TNode.NodeExists: Boolean;
  967. begin
  968. Result := Assigned(_Node);
  969. end;
  970. class function TNode.NodeVersion: String;
  971. begin
  972. Result := CT_ClientAppVersion
  973. {$IFDEF USE_ABSTRACTMEM}+'am'{$ENDIF}
  974. {$IFDEF LINUX}+'L'{$ELSE}+'W'{$ENDIF}
  975. {$IFDEF FPC}{$IFDEF LCL}+'l'{$ELSE}+'f'{$ENDIF}{$ENDIF}
  976. {$IFDEF FPC}{$IFDEF CPU32}+'32b'{$ELSE}+'64b'{$ENDIF}{$ELSE}{$IFDEF CPU32BITS}+'32b'{$ELSE}+'64b'{$ENDIF}{$ENDIF}
  977. {$IFDEF Use_CryptoLib4Pascal}+'CL4P'{$ENDIF};
  978. end;
  979. procedure TNode.Notification(AComponent: TComponent; Operation: TOperation);
  980. begin
  981. inherited;
  982. end;
  983. procedure TNode.NotifyBlocksChanged;
  984. Var i : Integer;
  985. begin
  986. for i := 0 to FNotifyList.Count-1 do begin
  987. TNodeNotifyEvents( FNotifyList[i] ).NotifyBlocksChanged;
  988. end;
  989. end;
  990. procedure TNode.GetStoredOperationsFromAccount(AOwnerThread : TPCThread; const OperationsResume: TList<TOperationResume>; account_number: Cardinal;
  991. MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock: Cardinal);
  992. // Optimization:
  993. // For better performance, will only include at "OperationsResume" values betweeen "startOperation" and "endOperation"
  994. // New use case: Will allow to start in an unknown block when first_block_is_unknows
  995. Procedure DoGetFromBlock(block_number : Integer; last_balance : Int64; act_depth : Integer; nOpsCounter : Integer; first_block_is_unknown : Boolean);
  996. var opc : TPCOperationsComp;
  997. op : TPCOperation;
  998. OPR : TOperationResume;
  999. l : TList<Cardinal>;
  1000. i : Integer;
  1001. last_block_number : Integer;
  1002. found_in_block : Boolean;
  1003. acc_0_miner_reward, acc_4_dev_reward : Int64;
  1004. acc_4_for_dev : Boolean;
  1005. begin
  1006. if Assigned(AOwnerThread) then begin
  1007. if AOwnerThread.terminated then Exit;
  1008. end;
  1009. if (act_depth<=0) then exit;
  1010. opc := TPCOperationsComp.Create(Nil);
  1011. Try
  1012. l := TList<Cardinal>.Create;
  1013. try
  1014. last_block_number := block_number+1;
  1015. while (last_block_number>block_number) And (act_depth>0)
  1016. And (block_number >= (account_number DIV CT_AccountsPerBlock))
  1017. And (nOpsCounter <= EndOperation) do begin
  1018. if Assigned(AOwnerThread) then begin
  1019. if AOwnerThread.terminated then Exit;
  1020. end;
  1021. found_in_block := False;
  1022. last_block_number := block_number;
  1023. l.Clear;
  1024. If not Bank.Storage.LoadBlockChainBlock(opc,block_number) then begin
  1025. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(block_number)+' not found. Cannot read operations');{$ENDIF}
  1026. exit;
  1027. end;
  1028. opc.OperationsHashTree.GetOperationsAffectingAccount(account_number,l);
  1029. for i := l.Count - 1 downto 0 do begin
  1030. op := opc.Operation[PtrInt(l.Items[i])];
  1031. If TPCOperation.OperationToOperationResume(block_number,Op,False,account_number,OPR) then begin
  1032. OPR.NOpInsideBlock := PtrInt(l.Items[i]);
  1033. OPR.time := opc.OperationBlock.timestamp;
  1034. OPR.Block := block_number;
  1035. If last_balance>=0 then begin
  1036. OPR.Balance := last_balance;
  1037. last_balance := last_balance - ( OPR.Amount + OPR.Fee );
  1038. end else OPR.Balance := -1; // Undetermined
  1039. if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
  1040. OperationsResume.Add(OPR);
  1041. end;
  1042. inc(nOpsCounter);
  1043. found_in_block := True;
  1044. end;
  1045. end;
  1046. // Is a new block operation?
  1047. if (TAccountComp.AccountBlock(account_number)=block_number) then begin
  1048. TPascalCoinProtocol.GetRewardDistributionForNewBlock(opc.OperationBlock,acc_0_miner_reward,acc_4_dev_reward,acc_4_for_dev);
  1049. If ((account_number MOD CT_AccountsPerBlock)=0) Or
  1050. ( ((account_number MOD CT_AccountsPerBlock)=CT_AccountsPerBlock-1) AND (acc_4_for_dev) ) then begin
  1051. OPR := CT_TOperationResume_NUL;
  1052. OPR.OpType:=CT_PseudoOp_Reward;
  1053. OPR.valid := true;
  1054. OPR.Block := block_number;
  1055. OPR.time := opc.OperationBlock.timestamp;
  1056. OPR.AffectedAccount := account_number;
  1057. If ((account_number MOD CT_AccountsPerBlock)=0) then begin
  1058. OPR.Amount := acc_0_miner_reward;
  1059. OPR.OperationTxt := 'Miner reward';
  1060. OPR.OpSubtype:=CT_PseudoOpSubtype_Miner;
  1061. end else begin
  1062. OPR.Amount := acc_4_dev_reward;
  1063. OPR.OperationTxt := 'Dev reward';
  1064. OPR.OpSubtype:=CT_PseudoOpSubtype_Developer;
  1065. end;
  1066. If last_balance>=0 then begin
  1067. OPR.Balance := last_balance;
  1068. end else OPR.Balance := -1; // Undetermined
  1069. if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
  1070. OperationsResume.Add(OPR);
  1071. end;
  1072. inc(nOpsCounter);
  1073. found_in_block := True;
  1074. end;
  1075. end;
  1076. //
  1077. dec(act_depth);
  1078. If (Not found_in_block) And (first_block_is_unknown) then begin
  1079. Dec(block_number);
  1080. end else begin
  1081. block_number := opc.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(account_number,block_number);
  1082. end;
  1083. opc.Clear(true);
  1084. end;
  1085. finally
  1086. l.Free;
  1087. end;
  1088. Finally
  1089. opc.Free;
  1090. End;
  1091. end;
  1092. Var acc : TAccount;
  1093. startBlock : Cardinal;
  1094. lastBalance : Int64;
  1095. begin
  1096. if MaxDepth<0 then Exit;
  1097. if account_number>=Bank.SafeBox.AccountsCount then Exit;
  1098. if StartOperation>EndOperation then Exit;
  1099. acc := Bank.SafeBox.Account(account_number);
  1100. if (acc.GetLastUpdatedBlock>0) Or (acc.account=0) then Begin
  1101. if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.GetLastUpdatedBlock) then begin
  1102. startBlock := acc.GetLastUpdatedBlock;
  1103. lastBalance := acc.balance;
  1104. end else begin
  1105. startBlock := SearchBackwardsStartingAtBlock;
  1106. lastBalance := -1;
  1107. end;
  1108. DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.GetLastUpdatedBlock);
  1109. end;
  1110. end;
  1111. procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperationsResumeList; account_number: Cardinal; MaxDepth, StartOperation, EndOperation: Integer; SearchBackwardsStartingAtBlock : Cardinal = 0);
  1112. var LOpList : TList<TOperationResume>;
  1113. i : Integer;
  1114. begin
  1115. LOpList := TList<TOperationResume>.Create;
  1116. try
  1117. GetStoredOperationsFromAccount(Nil,LOpList,account_number,MaxDepth,StartOperation,EndOperation,SearchBackwardsStartingAtBlock);
  1118. for i := 0 to LOpList.Count-1 do begin
  1119. OperationsResume.Add(LOpList[i]);
  1120. end;
  1121. finally
  1122. LOpList.Free;
  1123. end;
  1124. end;
  1125. function TNode.FindNOperation(block, account, n_operation: Cardinal;
  1126. var OpResume: TOperationResume): TSearchOperationResult;
  1127. // Note: block = 0 search in all blocks. If Block>0 must match a valid block with operation with this account
  1128. var oprl : TOperationsResumeList;
  1129. begin
  1130. oprl := TOperationsResumeList.Create;
  1131. try
  1132. Result := FindNOperations(account,block,block=0,n_operation,n_operation,oprl);
  1133. If oprl.Count>0 then begin
  1134. OpResume := oprl.OperationResume[0];
  1135. end else OpResume := CT_TOperationResume_NUL;
  1136. finally
  1137. oprl.Free;
  1138. end;
  1139. end;
  1140. function TNode.FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high: Cardinal; OpResumeList: TOperationsResumeList): TSearchOperationResult;
  1141. var i : Integer;
  1142. op : TPCOperation;
  1143. aux_block, block : Cardinal;
  1144. OperationComp : TPCOperationsComp;
  1145. opr : TOperationResume;
  1146. n_operation, found_n_operation : Cardinal;
  1147. LLockedMempool : TPCOperationsComp;
  1148. begin
  1149. OpResumeList.Clear;
  1150. Result := invalid_params;
  1151. block := start_block;
  1152. If (block>=Bank.BlocksCount) then exit; // Invalid block number
  1153. If (account>=Bank.AccountsCount) then exit; // Invalid account number
  1154. If (n_operation_high<n_operation_low) then exit;
  1155. LLockedMempool := LockMempoolRead;
  1156. try
  1157. n_operation := LLockedMempool.SafeBoxTransaction.Account(account).n_operation;
  1158. finally
  1159. UnlockMempoolRead;
  1160. end;
  1161. if (n_operation>n_operation_high) then n_operation := n_operation_high;
  1162. if (n_operation<n_operation_low) then Exit;
  1163. If (block=0) then begin
  1164. // Start searching on pending blocks
  1165. LLockedMempool := LockMempoolRead;
  1166. try
  1167. For i:=LLockedMempool.Count-1 downto 0 do begin
  1168. op := LLockedMempool.Operation[i];
  1169. If (op.IsSignerAccount(account)) then begin
  1170. found_n_operation := op.GetAccountN_Operation(account);
  1171. if (found_n_operation<n_operation_low) then Exit; // Not found
  1172. If (found_n_operation<=n_operation) then begin
  1173. TPCOperation.OperationToOperationResume(0,op,False,account,opr);
  1174. opr.Balance:=-1;
  1175. OpResumeList.Add(opr);
  1176. if (n_operation>n_operation_low) then dec(n_operation)
  1177. else begin
  1178. Result := found;
  1179. Exit;
  1180. end;
  1181. end;
  1182. end;
  1183. end;
  1184. block := Bank.SafeBox.Account(account).GetLastUpdatedBlock;
  1185. finally
  1186. UnlockMempoolRead;
  1187. end;
  1188. end;
  1189. // Search in previous blocks
  1190. OperationComp := TPCOperationsComp.Create(Nil);
  1191. Try
  1192. While (n_operation>0) And (n_operation>=n_operation_low) And (block>0) do begin
  1193. aux_block := block;
  1194. If Not Bank.LoadOperations(OperationComp,block) then begin
  1195. Result := blockchain_block_not_found; // Cannot continue searching!
  1196. exit;
  1197. end;
  1198. For i:=OperationComp.Count-1 downto 0 do begin
  1199. op := OperationComp.Operation[i];
  1200. if (op.IsSignerAccount(account)) then begin
  1201. If (n_operation_high=n_operation_low) and (op.GetAccountN_Operation(account)=n_operation) // If searching only 1 n_operation, n_operation must match
  1202. Or
  1203. (n_operation_high>n_operation_low) and (op.GetAccountN_Operation(account)<=n_operation) and (op.GetAccountN_Operation(account)>=n_operation_low) and (op.GetAccountN_Operation(account)<=n_operation_high) then begin
  1204. TPCOperation.OperationToOperationResume(block,op,True,account,opr);
  1205. opr.time:=Bank.SafeBox.GetBlockInfo(block).timestamp;
  1206. opr.NOpInsideBlock:=i;
  1207. opr.Balance:=-1;
  1208. OpResumeList.Add(opr);
  1209. if (n_operation>n_operation_low) then dec(n_operation)
  1210. else begin
  1211. Result := found;
  1212. Exit;
  1213. end;
  1214. end else begin
  1215. If (op.GetAccountN_Operation(account) < n_operation) then begin
  1216. If (n_operation_high>n_operation_low) then Result := found; // multiple search, result is found (not an error)
  1217. Exit // First occurrence is lower
  1218. end;
  1219. end;
  1220. end;
  1221. end;
  1222. block := OperationComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(account,block);
  1223. if (block>aux_block) then exit // Error... not found a valid block positioning
  1224. else if (block=aux_block) then begin
  1225. if ((start_block=0) Or (allow_search_previous)) then dec(block) // downgrade until found a block with operations
  1226. else Exit; // Not found in current block
  1227. end else if (start_block>0) and (not allow_search_previous) and (OpResumeList.Count=0) then Exit; // does not need to decrease
  1228. end;
  1229. finally
  1230. OperationComp.Free;
  1231. end;
  1232. Result := found;
  1233. end;
  1234. procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
  1235. var opht : TOperationsHashTree;
  1236. oprl : TOperationsResumeList;
  1237. errors : String;
  1238. n : Integer;
  1239. begin
  1240. Bank.DiskRestoreFromOperations(max_block_to_read,restoreProgressNotify);
  1241. opht := TOperationsHashTree.Create;
  1242. oprl := TOperationsResumeList.Create;
  1243. try
  1244. Bank.Storage.LoadPendingBufferOperations(opht); // New Build 2.1.4 to load pending operations buffer
  1245. n := AddOperations(Nil,opht,oprl,errors);
  1246. TLog.NewLog(ltInfo,ClassName,Format('Pending buffer restored operations:%d added:%d final_operations:%d errors:%s',[opht.OperationsCount,n,MempoolOperationsCount,errors]));
  1247. finally
  1248. opht.Free;
  1249. oprl.Free;
  1250. end;
  1251. end;
  1252. function TNode.FindOperationExt(const OperationComp: TPCOperationsComp;
  1253. const OperationHash: TRawBytes; var block: Cardinal;
  1254. var operation_block_index: Integer): TSearchOperationResult;
  1255. { With a OperationHash, search it }
  1256. var account,n_operation : Cardinal;
  1257. i : Integer;
  1258. op : TPCOperation;
  1259. initial_block, aux_block, aux_n_op : Cardinal;
  1260. opHashValid, opHash_OLD : TRawBytes;
  1261. md160 : TRawBytes;
  1262. LLockedMempool : TPCOperationsComp;
  1263. begin
  1264. Result := invalid_params;
  1265. // Decode OperationHash
  1266. If not TPCOperation.DecodeOperationHash(OperationHash,block,account,n_operation,md160) then exit;
  1267. initial_block := block;
  1268. //
  1269. If (account>=Bank.AccountsCount) then exit; // Invalid account number
  1270. // If block=0 then we must search in pending operations first
  1271. if (block=0) then begin
  1272. LLockedMempool := LockMempoolRead;
  1273. Try
  1274. LLockedMempool.Lock;
  1275. Try
  1276. For i:=0 to LLockedMempool.Count-1 do begin
  1277. op := LLockedMempool.Operation[i];
  1278. If (op.SignerAccount=account) then begin
  1279. opHashValid := TPCOperation.OperationHashValid(op,0);
  1280. opHash_OLD := TPCOperation.OperationHash_OLD(op,0);
  1281. If TBaseType.Equals(opHashValid,OperationHash) or
  1282. ((FBank.BlocksCount<CT_Protocol_Upgrade_v2_MinBlock) And (TBaseType.Equals(opHash_OLD,OperationHash))) then begin
  1283. operation_block_index:=i;
  1284. OperationComp.CopyFrom(LLockedMempool);
  1285. Result := found;
  1286. exit;
  1287. end;
  1288. end;
  1289. end;
  1290. finally
  1291. LLockedMempool.Unlock;
  1292. end;
  1293. Finally
  1294. UnlockMempoolRead;
  1295. End;
  1296. // block=0 and not found... start searching at block updated by account updated_block
  1297. block := Bank.SafeBox.Account(account).GetLastUpdatedBlock;
  1298. if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
  1299. end;
  1300. if (block=0) or (block>=Bank.BlocksCount) then exit;
  1301. // Search in previous blocks
  1302. While (block>0) do begin
  1303. aux_block := block;
  1304. If Not Bank.LoadOperations(OperationComp,block) then begin
  1305. Result := blockchain_block_not_found;
  1306. exit;
  1307. end;
  1308. For i:=OperationComp.Count-1 downto 0 do begin
  1309. op := OperationComp.Operation[i];
  1310. if (op.IsSignerAccount(account)) then begin
  1311. aux_n_op := op.GetAccountN_Operation(account);
  1312. If (aux_n_op<n_operation) then exit; // n_operation is greaten than found
  1313. If (aux_n_op=n_operation) then begin
  1314. // Possible candidate or dead
  1315. opHashValid := TPCOperation.OperationHashValid(op,initial_block);
  1316. If (TBaseType.Equals(opHashValid,OperationHash)) then begin
  1317. operation_block_index:=i;
  1318. Result := found;
  1319. exit;
  1320. end else if (block<CT_Protocol_Upgrade_v2_MinBlock) then begin
  1321. opHash_OLD := TPCOperation.OperationHash_OLD(op,initial_block);
  1322. if (TBaseType.Equals(opHash_OLD,OperationHash)) then begin
  1323. operation_block_index:=i;
  1324. Result := found;
  1325. exit;
  1326. end else exit; // Not found!
  1327. end else exit; // Not found!
  1328. end;
  1329. end;
  1330. end;
  1331. block := OperationComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(account,block);
  1332. if (block>=aux_block) then exit; // Error... not found a valid block positioning
  1333. if (initial_block<>0) then exit; // If not found in specified block, no valid hash
  1334. end;
  1335. end;
  1336. function TNode.FindOperation(const OperationComp: TPCOperationsComp;
  1337. const OperationHash: TRawBytes; var block: Cardinal;
  1338. var operation_block_index: Integer): Boolean;
  1339. { With a OperationHash, search it }
  1340. var sor : TSearchOperationResult;
  1341. begin
  1342. sor := FindOperationExt(OperationComp,OperationHash,block,operation_block_index);
  1343. Result := sor = found;
  1344. end;
  1345. procedure TNode.NotifyNetClientMessage(Sender: TNetConnection; const TheMessage: String);
  1346. Var i : Integer;
  1347. begin
  1348. for i := 0 to FNotifyList.Count-1 do begin
  1349. if Assigned( TNodeNotifyEvents( FNotifyList[i] ).OnNodeMessageEvent) then begin
  1350. TNodeNotifyEvents( FNotifyList[i] ).FMessages.AddObject(TheMessage,Sender);
  1351. end;
  1352. end;
  1353. end;
  1354. function TNode.MempoolOperationsCount: Integer;
  1355. var LLockedMempool : TPCOperationsComp;
  1356. begin
  1357. LLockedMempool := LockMempoolRead;
  1358. try
  1359. Result := LLockedMempool.Count;
  1360. finally
  1361. UnlockMempoolRead;
  1362. end;
  1363. end;
  1364. function TNode.GetAccountsAvailableByPublicKey(const APubKey: TAccountKey;
  1365. out AOnSafebox, AOnMempool: Integer): Integer;
  1366. var LPubKeys: TList<TAccountKey>;
  1367. begin
  1368. LPubKeys := TList<TAccountKey>.Create;
  1369. Try
  1370. LPubKeys.Add(APubKey);
  1371. Result := GetAccountsAvailableByPublicKey(LPubKeys,AOnSafebox,AOnMempool);
  1372. Finally
  1373. LPubKeys.Free;
  1374. End;
  1375. end;
  1376. function TNode.GetAccountsAvailableByPublicKey(
  1377. const APubKeys: TList<TAccountKey>; out AOnSafebox,
  1378. AOnMempool: Integer): Integer;
  1379. var Lmempool : TPCOperationsComp;
  1380. i,j,k : Integer;
  1381. Lop : TPCOperation;
  1382. LopResume : TOperationResume;
  1383. Lpubkeys : TSafeboxPubKeysAndAccounts;
  1384. Laccounts : TAccountsNumbersList;
  1385. begin
  1386. AOnMempool := 0;
  1387. AOnSafebox := 0;
  1388. // Check safebox
  1389. Lpubkeys := Bank.SafeBox.OrderedAccountKeysList;
  1390. if Assigned(Lpubkeys) then begin
  1391. for i := 0 to APubKeys.Count-1 do begin
  1392. Laccounts := Lpubkeys.GetAccountsUsingThisKey(APubKeys[i]);
  1393. if Assigned(Laccounts) then begin
  1394. Inc(AOnSafebox,Laccounts.Count);
  1395. end;
  1396. end;
  1397. end else AOnSafebox := -1;
  1398. for i := 0 to APubKeys.Count-1 do begin
  1399. // Check mempool
  1400. Lmempool := LockMempoolRead;
  1401. try
  1402. for j := 0 to Lmempool.Count-1 do begin
  1403. Lop := Lmempool.Operation[j];
  1404. Lop.OperationToOperationResume(Bank.BlocksCount,Lop,True,Lop.SignerAccount,LopResume);
  1405. for k:=0 to Length(LopResume.Changers)-1 do begin
  1406. if (public_key in LopResume.Changers[k].Changes_type) and (LopResume.Changers[k].New_Accountkey.IsEqualTo(APubKeys[i])) then begin
  1407. // New account is on the mempool!
  1408. inc(AOnMempool);
  1409. end;
  1410. end;
  1411. end;
  1412. finally
  1413. UnlockMempoolRead;
  1414. end;
  1415. end;
  1416. if AOnSafebox>=0 then Result := (AOnMempool + AOnsafebox)
  1417. else Result := AOnMempool;
  1418. end;
  1419. function TNode.GetMempoolAccount(AAccountNumber : Cardinal): TAccount;
  1420. var LLockedMempool : TPCOperationsComp;
  1421. begin
  1422. LLockedMempool := LockMempoolRead;
  1423. try
  1424. Result := LLockedMempool.SafeBoxTransaction.Account(AAccountNumber);
  1425. finally
  1426. UnlockMempoolRead;
  1427. end;
  1428. end;
  1429. class function TNode.GetPascalCoinDataFolder: String;
  1430. begin
  1431. if (_PascalCoinDataFolder.Trim.Length>0) then begin
  1432. Result := _PascalCoinDataFolder;
  1433. end else begin
  1434. Result := TFolderHelper.GetDataFolder(CT_PascalCoin_Data_Folder);
  1435. end;
  1436. end;
  1437. class procedure TNode.SetPascalCoinDataFolder(const ANewDataFolder: String);
  1438. begin
  1439. _PascalCoinDataFolder := ANewDataFolder;
  1440. end;
  1441. function TNode.LockMempoolRead: TPCOperationsComp;
  1442. begin
  1443. FLockMempool.Acquire;
  1444. Result := FMemPoolOperationsComp;
  1445. end;
  1446. procedure TNode.UnlockMempoolRead;
  1447. begin
  1448. FLockMempool.Release;
  1449. end;
  1450. function TNode.LockMempoolWrite: TPCOperationsComp;
  1451. begin
  1452. // TODO: Must lock WRITE EXCLUSIVE NO READ !!! XXXXXXXXXXXXXXXX
  1453. FLockMempool.Acquire;
  1454. Result := FMemPoolOperationsComp;
  1455. end;
  1456. procedure TNode.UnlockMempoolWrite;
  1457. begin
  1458. FLockMempool.Release;
  1459. end;
  1460. procedure TNode.OnBankNewBlock(Sender: TObject);
  1461. var LLockedMempool : TPCOperationsComp;
  1462. begin
  1463. LLockedMempool := LockMempoolWrite;
  1464. try
  1465. LLockedMempool.SanitizeOperations;
  1466. finally
  1467. UnlockMempoolWrite;
  1468. end;
  1469. NotifyBlocksChanged;
  1470. end;
  1471. function TNode.SendNodeMessage(Target: TNetConnection; const TheMessage: String; var errors: String): Boolean;
  1472. Var i,j : Integer;
  1473. nc : TNetConnection;
  1474. s : String;
  1475. begin
  1476. Result := false;
  1477. if Not TPCThread.TryProtectEnterCriticalSection(Self,4000,FLockMempool) then begin
  1478. s := 'Cannot Send node message due blocking lock operations node';
  1479. TLog.NewLog(lterror,Classname,s);
  1480. if TThread.CurrentThread.ThreadID=MainThreadID then raise Exception.Create(s) else exit;
  1481. end;
  1482. try
  1483. errors := '';
  1484. if assigned(Target) then begin
  1485. Target.Send_Message(TheMessage);
  1486. end else begin
  1487. j := TNetData.NetData.ConnectionsCountAll;
  1488. for i:=0 to j-1 do begin
  1489. if TNetData.NetData.GetConnection(i,nc) then begin
  1490. If TNetData.NetData.ConnectionLock(Self,nc,500) then begin
  1491. try
  1492. nc.Send_Message(TheMessage);
  1493. finally
  1494. TNetData.NetData.ConnectionUnlock(nc)
  1495. end;
  1496. end;
  1497. end;
  1498. end;
  1499. end;
  1500. result := true;
  1501. finally
  1502. FLockMempool.Release;
  1503. end;
  1504. end;
  1505. procedure TNode.SetNodeLogFilename(const Value: String);
  1506. begin
  1507. FNodeLog.FileName := Value;
  1508. end;
  1509. { TNodeMessageManyEventHelper }
  1510. procedure TNodeMessageManyEventHelper.Add(listener : TNodeMessageEvent);
  1511. begin
  1512. if TArrayTool<TNodeMessageEvent>.IndexOf(self, listener) = -1 then begin
  1513. TArrayTool<TNodeMessageEvent>.Add(self, listener);
  1514. end;
  1515. end;
  1516. procedure TNodeMessageManyEventHelper.Remove(listener : TNodeMessageEvent);
  1517. begin
  1518. TArrayTool<TNodeMessageEvent>.Remove(self, listener);
  1519. end;
  1520. procedure TNodeMessageManyEventHelper.Invoke(NetConnection : TNetConnection; MessageData : String);
  1521. var i : Integer;
  1522. begin
  1523. for i := low(self) to high(self) do
  1524. self[i](NetConnection, MessageData);
  1525. end;
  1526. { TNodeNotifyEvents }
  1527. constructor TNodeNotifyEvents.Create(AOwner: TComponent);
  1528. begin
  1529. inherited;
  1530. FOnOperationsChanged := Nil;
  1531. FOnBlocksChanged := Nil;
  1532. FOnNodeMessageEvent := Nil;
  1533. FWatchKeys := Nil;
  1534. FOnKeyActivity:=Nil;
  1535. FMessages := TStringList.Create;
  1536. FPendingNotificationsList := TPCThreadList<Pointer>.Create('TNodeNotifyEvents_PendingNotificationsList');
  1537. FThreadSafeNodeNotifyEvent := TThreadSafeNodeNotifyEvent.Create(Self);
  1538. Node := _Node;
  1539. end;
  1540. destructor TNodeNotifyEvents.Destroy;
  1541. begin
  1542. if Assigned(FNode) then FNode.FNotifyList.Remove(Self);
  1543. FThreadSafeNodeNotifyEvent.FNodeNotifyEvents := Nil;
  1544. FThreadSafeNodeNotifyEvent.Terminate;
  1545. FThreadSafeNodeNotifyEvent.WaitFor;
  1546. FreeAndNil(FThreadSafeNodeNotifyEvent);
  1547. FreeAndNil(FPendingNotificationsList);
  1548. FreeAndNil(FMessages);
  1549. inherited;
  1550. end;
  1551. procedure TNodeNotifyEvents.Notification(AComponent: TComponent; Operation: TOperation);
  1552. begin
  1553. inherited;
  1554. if (Operation=opremove) then begin
  1555. if AComponent=FNode then FNode := Nil;
  1556. end;
  1557. end;
  1558. procedure TNodeNotifyEvents.NotifyBlocksChanged;
  1559. begin
  1560. if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyBlocksChanged := true;
  1561. end;
  1562. procedure TNodeNotifyEvents.NotifyOperationsChanged;
  1563. begin
  1564. if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyOperationsChanged := true;
  1565. end;
  1566. procedure TNodeNotifyEvents.SetWatchKeys(AValue: TOrderedAccountKeysList);
  1567. begin
  1568. if FWatchKeys=AValue then Exit;
  1569. FWatchKeys:=AValue;
  1570. end;
  1571. procedure TNodeNotifyEvents.SetNode(const Value: TNode);
  1572. begin
  1573. if FNode=Value then exit;
  1574. if Assigned(FNode) then begin
  1575. FNode.RemoveFreeNotification(Self);
  1576. FNode.FNotifyList.Remove(Self);
  1577. end;
  1578. FNode := Value;
  1579. if Assigned(FNode) then begin
  1580. FNode.FreeNotification(Self);
  1581. FNode.FNotifyList.Add(Self);
  1582. end;
  1583. end;
  1584. { TThreadSafeNodeNotifyEvent }
  1585. procedure TThreadSafeNodeNotifyEvent.BCExecute;
  1586. begin
  1587. while (not Terminated) AND (Assigned(FNodeNotifyEvents)) do begin
  1588. if (FNotifyOperationsChanged) Or (FNotifyBlocksChanged) Or (FNodeNotifyEvents.FMessages.Count>0) then Synchronize(SynchronizedProcess);
  1589. Sleep(100);
  1590. end;
  1591. end;
  1592. constructor TThreadSafeNodeNotifyEvent.Create(ANodeNotifyEvents: TNodeNotifyEvents);
  1593. begin
  1594. FNodeNotifyEvents := ANodeNotifyEvents;
  1595. Inherited Create(False);
  1596. end;
  1597. procedure TThreadSafeNodeNotifyEvent.SynchronizedProcess;
  1598. Var i : Integer;
  1599. can_alert_keys : Boolean;
  1600. begin
  1601. Try
  1602. If (Terminated) Or (Not Assigned(FNodeNotifyEvents)) then exit;
  1603. can_alert_keys := False;
  1604. if FNotifyBlocksChanged then begin
  1605. FNotifyBlocksChanged := false;
  1606. can_alert_keys := True;
  1607. DebugStep:='Notify OnBlocksChanged';
  1608. if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnBlocksChanged)) then
  1609. FNodeNotifyEvents.FOnBlocksChanged(FNodeNotifyEvents);
  1610. end;
  1611. if FNotifyOperationsChanged then begin
  1612. FNotifyOperationsChanged := false;
  1613. can_alert_keys := True;
  1614. DebugStep:='Notify OnOperationsChanged';
  1615. if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnOperationsChanged)) then
  1616. FNodeNotifyEvents.FOnOperationsChanged(FNodeNotifyEvents);
  1617. end;
  1618. if FNodeNotifyEvents.FMessages.Count>0 then begin
  1619. DebugStep:='Notify OnNodeMessageEvent';
  1620. if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnNodeMessageEvent)) then begin
  1621. for i := 0 to FNodeNotifyEvents.FMessages.Count - 1 do begin
  1622. DebugStep:='Notify OnNodeMessageEvent '+inttostr(i+1)+'/'+inttostr(FNodeNotifyEvents.FMessages.Count);
  1623. FNodeNotifyEvents.FOnNodeMessageEvent(TNetConnection(FNodeNotifyEvents.FMessages.Objects[i]),FNodeNotifyEvents.FMessages.Strings[i]);
  1624. end;
  1625. end;
  1626. FNodeNotifyEvents.FMessages.Clear;
  1627. end;
  1628. if (can_alert_keys) And Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FWatchKeys)) then begin
  1629. DebugStep:='Notify WatchKeys';
  1630. If FNodeNotifyEvents.FWatchKeys.HasAccountKeyChanged then begin
  1631. FNodeNotifyEvents.FWatchKeys.ClearAccountKeyChanges;
  1632. if Assigned(FNodeNotifyEvents.FOnKeyActivity) then begin
  1633. DebugStep:='Notify WatchKeys OnKeyActivity';
  1634. FNodeNotifyEvents.FOnKeyActivity(FNodeNotifyEvents);
  1635. end;
  1636. end;
  1637. end;
  1638. Except
  1639. On E:Exception do begin
  1640. TLog.NewLog(lterror,ClassName,'Exception inside a Synchronized process: '+E.ClassName+':'+E.Message+' Step:'+DebugStep);
  1641. end;
  1642. End;
  1643. end;
  1644. { TThreadNodeNotifyNewBlock }
  1645. procedure TThreadNodeNotifyNewBlock.BCExecute;
  1646. begin
  1647. DebugStep := 'Locking';
  1648. if TNetData.NetData.ConnectionLock(Self,FNetConnection,5000) then begin
  1649. try
  1650. DebugStep := 'Checking connected';
  1651. if Not FNetconnection.Connected then exit;
  1652. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,'Sending new block found to '+FNetConnection.Client.ClientRemoteAddr);{$ENDIF}
  1653. DebugStep := 'Sending';
  1654. FNetConnection.Send_NewBlockFound(FNewBlockOperations);
  1655. DebugStep := 'Checking connected again';
  1656. if Not FNetConnection.Connected then exit;
  1657. DebugStep := 'Need send opreations?';
  1658. if FSanitizedOperationsHashTree.OperationsCount>0 then begin
  1659. DebugStep := 'Sending '+inttostr(FSanitizedOperationsHashTree.OperationsCount)+' sanitized operations';
  1660. TLog.NewLog(ltdebug,ClassName,'Sending '+inttostr(FSanitizedOperationsHashTree.OperationsCount)+' sanitized operations to '+FNetConnection.ClientRemoteAddr);
  1661. TThreadNodeNotifyOperations.Create(FNetConnection,FSanitizedOperationsHashTree);
  1662. end;
  1663. DebugStep := 'Unlocking';
  1664. finally
  1665. TNetData.NetData.ConnectionUnlock(FNetConnection);
  1666. end;
  1667. end;
  1668. DebugStep := 'Finalizing';
  1669. end;
  1670. constructor TThreadNodeNotifyNewBlock.Create(NetConnection: TNetConnection; MakeACopyOfNewBlockOperations: TPCOperationsComp; MakeACopyOfSanitizedOperationsHashTree : TOperationsHashTree);
  1671. begin
  1672. FNetConnection := NetConnection;
  1673. FSanitizedOperationsHashTree := TOperationsHashTree.Create;
  1674. FSanitizedOperationsHashTree.CopyFromHashTree(MakeACopyOfSanitizedOperationsHashTree);
  1675. FNewBlockOperations := TPCOperationsComp.Create(Nil);
  1676. FNewBlockOperations.CopyFrom(MakeACopyOfNewBlockOperations);
  1677. Inherited Create(True);
  1678. FreeOnTerminate := true;
  1679. Suspended:=False;
  1680. end;
  1681. destructor TThreadNodeNotifyNewBlock.Destroy;
  1682. begin
  1683. FreeAndNil(FSanitizedOperationsHashTree);
  1684. FreeAndNil(FNewBlockOperations);
  1685. inherited;
  1686. end;
  1687. { TThreadNodeNotifyOperations }
  1688. procedure TThreadNodeNotifyOperations.BCExecute;
  1689. begin
  1690. Sleep(Random(3000)); // Delay 0..3 seconds to allow receive data and don't send if not necessary
  1691. if TNetData.NetData.ConnectionLock(Self, FNetConnection, 5000) then begin
  1692. try
  1693. if Not FNetconnection.Connected then exit;
  1694. FNetConnection.Send_AddOperations(Nil);
  1695. finally
  1696. TNetData.NetData.ConnectionUnlock(FNetConnection);
  1697. end;
  1698. end;
  1699. end;
  1700. constructor TThreadNodeNotifyOperations.Create(NetConnection: TNetConnection; MakeACopyOfOperationsHashTree: TOperationsHashTree);
  1701. begin
  1702. FNetConnection := NetConnection;
  1703. FNetConnection.AddOperationsToBufferForSend(MakeACopyOfOperationsHashTree);
  1704. Inherited Create(True);
  1705. FreeOnTerminate := true;
  1706. Suspended:=False;
  1707. end;
  1708. destructor TThreadNodeNotifyOperations.Destroy;
  1709. begin
  1710. inherited;
  1711. end;
  1712. initialization
  1713. _Node := Nil;
  1714. _PascalCoinDataFolder := '';
  1715. finalization
  1716. FreeAndNil(_Node);
  1717. end.