UNode.pas 49 KB

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