UNode.pas 57 KB

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