UNode.pas 62 KB

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