UBlockChain.pas 141 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615
  1. unit UBlockChain;
  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. {$IFDEF FPC}
  14. {$MODE Delphi}
  15. {$ENDIF}
  16. interface
  17. uses
  18. Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
  19. {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
  20. UPCDataTypes;
  21. {$I config.inc}
  22. {
  23. Bank BlockChain:
  24. Safe Box content: (See Unit "UAccounts.pas" to see pascal code)
  25. +--------------+--------------------------------------------------+------------+------------+
  26. + BlockAccount + Each BlockAccount has N "Account" + Timestamp + Block Hash +
  27. + +--------------------------------------------------+ + +
  28. + + Addr B0 + Public key + Balance + updated + n_op + + +
  29. + + Addr B1 + Public key + Balance + updated + n_op + + +
  30. + + ...... + + +
  31. + + Addr B4 + Public key + Balance + updated + n_op + + +
  32. +--------------+---------+----------------------------------------+------------+------------+
  33. + 0 + 0 + pk_aaaaaaa + 100.0000 + 0 + 0 + 1461701856 + Sha256() +
  34. + + 1 + pk_aaaaaaa + 0.0000 + 0 + 0 + + = h1111111 +
  35. + + 2 + pk_aaaaaaa + 0.0000 + 0 + 0 + + +
  36. + + 3 + pk_aaaaaaa + 0.0000 + 0 + 0 + + +
  37. + + 4 + pk_aaaaaaa + 0.0000 + 0 + 0 + + +
  38. +--------------+---------+----------------------------------------+------------+------------+
  39. + 1 + 5 + pk_bbbbbbb + 100.0000 + 0 + 0 + 1461702960 + Sha256() +
  40. + + 6 + pk_bbbbbbb + 0.0000 + 0 + 0 + + = h2222222 +
  41. + + 7 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  42. + + 8 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  43. + + 9 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  44. +--------------+---------+----------------------------------------+------------+------------+
  45. + ................ +
  46. +--------------+---------+----------------------------------------+------------+------------+
  47. + 5 + 25 + pk_bbbbbbb + 100.0000 + 0 + 0 + 1461713484 + Sha256() +
  48. + + 26 + pk_bbbbbbb + 0.0000 + 0 + 0 + + = h3333333 +
  49. + + 27 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  50. + + 28 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  51. + + 29 + pk_bbbbbbb + 0.0000 + 0 + 0 + + +
  52. +--------------+---------+----------------------------------------+------------+------------+
  53. + Safe Box Hash : Sha256(h1111111 + h2222222 + ... + h3333333) = sbh_A1 +
  54. +-------------------------------------------------------------------------------------------+
  55. BlockChain:
  56. To generate a BlockChain (block X) we need the previous "Safe Box Hash"
  57. (the Safe Box Hash number X-1, generated when BlockChain X-1 was generated)
  58. Each BlockChain block generates a new "Safe Box" with a new "Safe Box Hash"
  59. With this method, Safe Box is unique after a BlockChain, so we can assume
  60. that a hard coded Safe Box X is the same that to load all previous BlockChain
  61. from 0 to X. Conclusion: It's not necessary historical operations (block chains)
  62. to work with Pascal Coin
  63. Some BlockChain fields:
  64. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  65. + Block + Account key + reward + fee + protocols + timestamp + target + nonce + Miner Payload + safe box hash + operations hash + Proof of Work + Operations stream +
  66. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  67. + 0 + (hard coded) + 100.0000 + 0 + 1 + 0 + 1461701856 + trgt_1 + ... + (Hard coded) + (Hard coded) + Sha256(Operat.) + 000000C3F5... + Operations of block 0 +
  68. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  69. + 1 + hhhhhhhhhhhhhhh + 100.0000 + 0 + 1 + 0 + 1461701987 + trgt_1 + ... + ... + SFH block 0 + Sha256(Operat.) + 000000A987... + Operations of block 1 +
  70. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  71. + 2 + iiiiiiiiiiiiiii + 100.0000 + 0.43 + 1 + 0 + 1461702460 + trgt_1 + ... + ... + SFH block 1 + Sha256(Operat.) + 0000003A1C... + Operations of block 2 +
  72. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  73. + ..... +
  74. +-------+-----------------+----------+------+-----+-----+------------+--------+-------+---------------+---------------+-----------------+---------------+-----------------------+
  75. Considerations:
  76. - Account Key: Is a public key that will have all new generated Accounts of the Safe Box
  77. - Protocols are 2 values: First indicate protocol of this block, second future candidate protocol that is allowed by miner who made this. (For protocol upgrades)
  78. - Safe Box Has: Each Block of the Bloch Chain is made in base of a previous Safe Box. This value hard codes consistency
  79. - Operations Stream includes all the operations that will be made to the Safe Box after this block is generated. A hash value of Operations stream is "Operations Hash"
  80. Operations:
  81. Each Block of the Block Chain has its owns operations that will be used to change Safe Box after block is completed and included in BlockChain
  82. Operations of actual Protocol (version 1) can be one of this:
  83. - Transaction from 1 account to 1 account
  84. - Change AccountKey of an account
  85. - Recover balance from an unused account (lost keys)
  86. Each Operation has a Hash value that is used to generate "Operations Hash". Operations Hash is a Sha256 of all the Operations included
  87. inside it hashed like a Merkle Tree.
  88. In unit "UOpTransaction.pas" you can see how each Operation Works.
  89. }
  90. Type
  91. // Moved from UOpTransaction to here
  92. TOpChangeAccountInfoType = (public_key, account_name, account_type, list_for_public_sale, list_for_private_sale, delist, account_data, list_for_account_swap, list_for_coin_swap );
  93. TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
  94. TOperationPayload = record
  95. { As described on PIP-0027 (introduced on Protocol V5)
  96. the payload of an operation will contain an initial byte that will
  97. provide information about the payload content.
  98. The "payload_type" byte value will help in payload decoding if good used
  99. but there is no core checking that payload_type has been used properly.
  100. It's job of any third party app (Layer 2) working with payloads to
  101. check/ensure they can read/decode properly Payload value if the
  102. content is not saved using E-PASA standard (PIP-0027) }
  103. payload_type : Byte;
  104. payload_raw : TRawBytes;
  105. end;
  106. // MultiOp... will allow a MultiOperation
  107. TMultiOpData = record
  108. ID : TGUID;
  109. Sequence : UInt16;
  110. &Type : UInt16;
  111. end;
  112. TMultiOpSender = Record
  113. Account : Cardinal;
  114. Amount : Int64;
  115. N_Operation : Cardinal;
  116. OpData : TMultiOpData; // Filled only when Operation is TOpData type
  117. Payload : TOperationPayload;
  118. Signature : TECDSA_SIG;
  119. end;
  120. TMultiOpSenders = Array of TMultiOpSender;
  121. TMultiOpReceiver = Record
  122. Account : Cardinal;
  123. Amount : Int64;
  124. Payload : TOperationPayload;
  125. end;
  126. TMultiOpReceivers = Array of TMultiOpReceiver;
  127. TMultiOpChangeInfo = Record
  128. Account: Cardinal;
  129. N_Operation : Cardinal;
  130. Changes_type : TOpChangeAccountInfoTypes; // bits mask. $0001 = New account key , $0002 = New name , $0004 = New type
  131. New_Accountkey: TAccountKey; // If (changes_mask and $0001)=$0001 then change account key
  132. New_Name: TRawBytes; // If (changes_mask and $0002)=$0002 then change name
  133. New_Type: Word; // If (changes_mask and $0004)=$0004 then change type
  134. New_Data: TRawBytes;
  135. Seller_Account : Int64;
  136. Account_Price : Int64;
  137. Locked_Until_Block : Cardinal;
  138. Hashed_secret : TRawBytes;
  139. Fee: Int64;
  140. Signature: TECDSA_SIG;
  141. end;
  142. TMultiOpChangesInfo = Array of TMultiOpChangeInfo;
  143. TOperationResume = Record
  144. valid : Boolean;
  145. Block : Cardinal;
  146. NOpInsideBlock : Integer;
  147. OpType : Word;
  148. OpSubtype : Word;
  149. time : Cardinal;
  150. AffectedAccount : Cardinal;
  151. SignerAccount : Int64; // Is the account that executes this operation
  152. n_operation : Cardinal;
  153. DestAccount : Int64; //
  154. SellerAccount : Int64; // Protocol 2 - only used when is a pay to transaction
  155. newKey : TAccountKey;
  156. OperationTxt : String;
  157. Amount : Int64;
  158. Fee : Int64;
  159. Balance : Int64;
  160. OriginalPayload : TOperationPayload;
  161. PrintablePayload : String;
  162. OperationHash : TRawBytes;
  163. OperationHash_OLD : TRawBytes; // Will include old oeration hash value
  164. errors : String;
  165. // New on V3 for PIP-0017
  166. isMultiOperation : Boolean;
  167. Senders : TMultiOpSenders;
  168. Receivers : TMultiOpReceivers;
  169. Changers : TMultiOpChangesInfo;
  170. end;
  171. TPCBank = Class;
  172. TPCBankNotify = Class;
  173. TPCOperation = Class;
  174. TPCOperationClass = Class of TPCOperation;
  175. TOperationsResumeList = Class
  176. private
  177. FList : TPCThreadList<Pointer>;
  178. function GetOperationResume(index: Integer): TOperationResume;
  179. public
  180. Constructor Create;
  181. Destructor Destroy; override;
  182. Procedure Add(Const OperationResume : TOperationResume);
  183. Function Count : Integer;
  184. Procedure Delete(index : Integer);
  185. Procedure Clear;
  186. Property OperationResume[index : Integer] : TOperationResume read GetOperationResume; default;
  187. End;
  188. TOpReference = UInt64;
  189. TOpReferenceArray = Array of TopReference;
  190. { TPCOperation }
  191. TPCOperation = Class
  192. Protected
  193. FProtocolVersion : Word;
  194. FHasValidSignature : Boolean;
  195. FUsedPubkeyForSignature : TECDSA_Public;
  196. FBufferedSha256 : TRawBytes;
  197. FBufferedRipeMD160 : TRawBytes; // OPID is a RipeMD160 of the GetBufferForOpHash(True) value, 20 bytes length
  198. procedure InitializeData(AProtocolVersion : Word); virtual;
  199. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; virtual; abstract;
  200. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; virtual; abstract;
  201. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); virtual;
  202. function IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
  203. procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); virtual;
  204. function SaveOperationPayloadToStream(const AStream : TStream; const APayload : TOperationPayload) : Boolean;
  205. function LoadOperationPayloadFromStream(const AStream : TStream; out APayload : TOperationPayload) : Boolean;
  206. public
  207. constructor Create(AProtocolVersion : Word); virtual;
  208. destructor Destroy; override;
  209. property ProtocolVersion : Word read FProtocolVersion;
  210. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; virtual;
  211. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors: String): Boolean; virtual; abstract;
  212. procedure AffectedAccounts(list : TList<Cardinal>); virtual; abstract;
  213. class function OpType: Byte; virtual; abstract;
  214. Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
  215. Function GetDigestToSign : TRawBytes; virtual; abstract;
  216. function OperationAmount : Int64; virtual; abstract;
  217. function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
  218. function OperationFee: Int64; virtual; abstract;
  219. function OperationPayload : TOperationPayload; virtual; abstract;
  220. function SignerAccount : Cardinal; virtual; abstract;
  221. procedure SignerAccounts(list : TList<Cardinal>); virtual;
  222. function IsSignerAccount(account : Cardinal) : Boolean; virtual;
  223. function IsAffectedAccount(account : Cardinal) : Boolean; virtual;
  224. function DestinationAccount : Int64; virtual;
  225. function SellerAccount : Int64; virtual;
  226. function N_Operation : Cardinal; virtual; abstract;
  227. function GetAccountN_Operation(account : Cardinal) : Cardinal; virtual;
  228. function SaveToNettransfer(Stream: TStream): Boolean;
  229. function LoadFromNettransfer(Stream: TStream): Boolean;
  230. function SaveToStorage(Stream: TStream): Boolean;
  231. function LoadFromStorage(Stream: TStream; LoadProtocolVersion : Word; APreviousUpdatedBlocks : TAccountPreviousBlockInfo): Boolean;
  232. Property HasValidSignature : Boolean read FHasValidSignature;
  233. Class function OperationHash_OLD(op : TPCOperation; Block : Cardinal) : TRawBytes;
  234. Class function OperationHashValid(op : TPCOperation; Block : Cardinal) : TRawBytes;
  235. class function IsValidOperationHash(const AOpHash : String) : Boolean;
  236. class function TryParseOperationHash(const AOpHash : String; var block, account, n_operation: Cardinal; var md160Hash : TRawBytes) : Boolean;
  237. Class function DecodeOperationHash(Const operationHash : TRawBytes; var block, account,n_operation : Cardinal; var md160Hash : TRawBytes) : Boolean;
  238. Class function EqualOperationHashes(Const operationHash1, operationHash2 : TRawBytes) : Boolean;
  239. Class function FinalOperationHashAsHexa(Const operationHash : TRawBytes) : String;
  240. class function OperationHashAsHexa(const operationHash : TRawBytes) : String;
  241. class function GetOpReferenceAccount(const opReference : TOpReference) : Cardinal;
  242. class function GetOpReferenceN_Operation(const opReference : TOpReference) : Cardinal;
  243. function Sha256 : TRawBytes;
  244. function RipeMD160 : TRawBytes;
  245. function GetOpReference : TOpReference;
  246. function GetOpID : TRawBytes; // OPID is RipeMD160 hash of the operation
  247. //
  248. function GetOperationStreamData : TBytes;
  249. class function GetOperationFromStreamData(ACurrentProtocol: word; StreamData : TBytes) : TPCOperation;
  250. //
  251. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; virtual; abstract;
  252. End;
  253. TPCOperationStorage = Record
  254. ptrPCOperation : TPCOperation;
  255. locksCount : Integer;
  256. end;
  257. PPCOperationTStorage = ^TPCOperationStorage;
  258. { TPCOperationsStorage }
  259. // TPCOperationsStorage will be used as a global Operations storage useful when
  260. // operations are stored on TOperationsHashTree because will use only one instance
  261. // of operation used on multiple OperationsHashTree lists. For example when
  262. // propagating operations to connected nodes, will only use one instance
  263. TPCOperationsStorage = Class
  264. private
  265. FIntTotalNewOps : Integer;
  266. FIntTotalAdded : Integer;
  267. FIntTotalDeleted : Integer;
  268. FMaxLocksCount : Integer;
  269. FMaxLocksValue : Integer;
  270. FPCOperationsStorageList : TPCThreadList<Pointer>; // Lock thread to POperationTStorage list
  271. Function FindOrderedByPtrPCOperation(lockedThreadList : TList<Pointer>; const Value: TPCOperation; out Index: Integer): Boolean;
  272. protected
  273. public
  274. Constructor Create;
  275. Destructor Destroy; override;
  276. //
  277. function LockPCOperationsStorage : TList<Pointer>;
  278. procedure UnlockPCOperationsStorage;
  279. Function Count : Integer;
  280. procedure AddPCOperation(APCOperation : TPCOperation);
  281. procedure RemovePCOperation(APCOperation : TPCOperation);
  282. function FindPCOperation(APCOperation : TPCOperation) : Boolean;
  283. function FindPCOperationAndIncCounterIfFound(APCOperation : TPCOperation) : Boolean;
  284. class function PCOperationsStorage : TPCOperationsStorage;
  285. procedure GetStats(strings : TStrings);
  286. end;
  287. { TOperationsHashTree }
  288. TOperationsHashTree = Class
  289. private
  290. FListOrderedByAccountsData : TList<Pointer>;
  291. FListOrderedBySha256 : TList<Integer>; // Improvement TOperationsHashTree speed 2.1.6
  292. FListOrderedByOpReference : TList<Integer>;
  293. FHashTreeOperations : TPCThreadList<Pointer>; // Improvement TOperationsHashTree speed 2.1.6
  294. FHashTree: TRawBytes;
  295. FOnChanged: TNotifyEvent;
  296. FTotalAmount : Int64;
  297. FTotalFee : Int64;
  298. FMax0feeOperationsBySigner : Integer;
  299. function InternalCanAddOperationToHashTree(lockedThreadList : TList<Pointer>; op : TPCOperation) : Boolean;
  300. function InternalAddOperationToHashTree(list : TList<Pointer>; op : TPCOperation; CalcNewHashTree : Boolean) : Boolean;
  301. Function FindOrderedByOpReference(lockedThreadList : TList<Pointer>; const Value: TOpReference; var Index: Integer): Boolean;
  302. Function FindOrderedBySha(lockedThreadList : TList<Pointer>; const Value: TRawBytes; var Index: Integer): Boolean;
  303. Function FindOrderedByAccountData(lockedThreadList : TList<Pointer>; const account_number : Cardinal; var Index: Integer): Boolean;
  304. function GetHashTree: TRawBytes;
  305. procedure SetMax0feeOperationsBySigner(const Value: Integer);
  306. public
  307. Constructor Create;
  308. Destructor Destroy; Override;
  309. function CanAddOperationToHashTree(op : TPCOperation) : Boolean;
  310. function AddOperationToHashTree(op : TPCOperation) : Boolean;
  311. Procedure ClearHastThree;
  312. Property HashTree : TRawBytes read GetHashTree;
  313. Function OperationsCount : Integer;
  314. Function GetOperation(index : Integer) : TPCOperation;
  315. Function GetOperationsAffectingAccount(account_number : Cardinal; List : TList<Cardinal>) : Integer;
  316. Procedure CopyFromHashTree(Sender : TOperationsHashTree);
  317. Property TotalAmount : Int64 read FTotalAmount;
  318. Property TotalFee : Int64 read FTotalFee;
  319. function SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage : Boolean): Boolean;
  320. function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
  321. function IndexOfOperation(op : TPCOperation) : Integer;
  322. function CountOperationsBySameSignerWithoutFee(account_number : Cardinal) : Integer;
  323. Procedure Delete(index : Integer);
  324. function IndexOfOpReference(const opReference : TOpReference) : Integer;
  325. procedure RemoveByOpReference(const opReference : TOpReference);
  326. Property OnChanged : TNotifyEvent read FOnChanged write FOnChanged;
  327. Property Max0feeOperationsBySigner : Integer Read FMax0feeOperationsBySigner write SetMax0feeOperationsBySigner;
  328. procedure MarkVerifiedECDSASignatures(operationsHashTreeToMark : TOperationsHashTree);
  329. // Will add all operations of the HashTree to then end of AList without removing previous objects
  330. function GetOperationsList(AList : TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
  331. End;
  332. { TPCOperationsComp }
  333. TPCOperationsComp = Class
  334. private
  335. FBank: TPCBank;
  336. FSafeBoxTransaction : TPCSafeBoxTransaction;
  337. FOperationBlock: TOperationBlock;
  338. FOperationsHashTree : TOperationsHashTree;
  339. FDigest_Part1 : TRawBytes;
  340. FDigest_Part2_Payload : TRawBytes;
  341. FDigest_Part3 : TRawBytes;
  342. FIsOnlyOperationBlock: Boolean;
  343. FStreamPoW : TMemoryStream;
  344. FDisableds : Integer;
  345. FOperationsLock : TPCCriticalSection;
  346. FPreviousUpdatedBlocks : TAccountPreviousBlockInfo; // New Protocol V3 struct to store previous updated blocks
  347. FHasValidOperationBlockInfo : Boolean;
  348. function GetOperation(index: Integer): TPCOperation;
  349. procedure SetBank(const value: TPCBank);
  350. procedure SetnOnce(const value: Cardinal);
  351. procedure Settimestamp(const value: Cardinal);
  352. function GetnOnce: Cardinal;
  353. function Gettimestamp: Cardinal;
  354. procedure SetAccountKey(const value: TAccountKey);
  355. function GetAccountKey: TAccountKey;
  356. Procedure Calc_Digest_Parts;
  357. Procedure Calc_Digest_Part3;
  358. Procedure CalcProofOfWork(fullcalculation : Boolean; var PoW: TRawBytes);
  359. function GetBlockPayload: TRawBytes;
  360. procedure SetBlockPayload(const Value: TRawBytes);
  361. procedure OnOperationsHashTreeChanged(Sender : TObject);
  362. protected
  363. function SaveBlockToStreamExt(save_only_OperationBlock : Boolean; Stream: TStream; SaveToStorage : Boolean): Boolean;
  364. function LoadBlockFromStreamExt(Stream: TStream; LoadingFromStorage : Boolean; var errors: String): Boolean;
  365. public
  366. Constructor Create(ABank: TPCBank);
  367. Destructor Destroy; Override;
  368. Procedure CopyFromExceptAddressKey(Operations : TPCOperationsComp);
  369. Procedure CopyFrom(Operations : TPCOperationsComp);
  370. Function AddOperation(Execute : Boolean; op: TPCOperation; var errors: String): Boolean;
  371. Function AddOperations(operations: TOperationsHashTree; var errors: String): Integer;
  372. Property Operation[index: Integer]: TPCOperation read GetOperation;
  373. Property bank: TPCBank read FBank write SetBank;
  374. Procedure Clear(DeleteOperations : Boolean);
  375. Function Count: Integer;
  376. Property OperationBlock: TOperationBlock read FOperationBlock;
  377. Class Function OperationBlockToText(const OperationBlock: TOperationBlock) : String;
  378. Class Function SaveOperationBlockToStream(Const OperationBlock: TOperationBlock; Stream: TStream) : Boolean;
  379. Property AccountKey: TAccountKey read GetAccountKey write SetAccountKey;
  380. Property nonce: Cardinal read GetnOnce write SetnOnce;
  381. Property timestamp: Cardinal read Gettimestamp write Settimestamp;
  382. Property BlockPayload : TRawBytes read GetBlockPayload write SetBlockPayload;
  383. function Update_And_RecalcPOW(newNOnce, newTimestamp : Cardinal; newBlockPayload : TRawBytes) : Boolean;
  384. procedure UpdateTimestamp;
  385. function SaveBlockToStorage(Stream: TStream): Boolean;
  386. function SaveBlockToStream(save_only_OperationBlock : Boolean; Stream: TStream): Boolean;
  387. function LoadBlockFromStorage(Stream: TStream; var errors: String): Boolean;
  388. function LoadBlockFromStream(Stream: TStream; var errors: String): Boolean;
  389. //
  390. Function GetMinerRewardPseudoOperation : TOperationResume;
  391. Function ValidateOperationBlock(var errors : String) : Boolean;
  392. Property IsOnlyOperationBlock : Boolean read FIsOnlyOperationBlock;
  393. Procedure Lock;
  394. Procedure Unlock;
  395. //
  396. Procedure SanitizeOperations;
  397. Class Function RegisterOperationClass(OpClass: TPCOperationClass): Boolean;
  398. Class Function IndexOfOperationClass(OpClass: TPCOperationClass): Integer;
  399. Class Function IndexOfOperationClassByOpType(OpType: Cardinal): Integer;
  400. Class Function GetOperationClassByOpType(OpType: Cardinal): TPCOperationClass;
  401. Class Function GetFirstBlock : TOperationBlock;
  402. Class Function EqualsOperationBlock(Const OperationBlock1,OperationBlock2 : TOperationBlock):Boolean;
  403. //
  404. Property SafeBoxTransaction : TPCSafeBoxTransaction read FSafeBoxTransaction;
  405. Property OperationsHashTree : TOperationsHashTree read FOperationsHashTree;
  406. Property PoW_Digest_Part1 : TRawBytes read FDigest_Part1;
  407. Property PoW_Digest_Part2_Payload : TRawBytes read FDigest_Part2_Payload;
  408. Property PoW_Digest_Part3 : TRawBytes read FDigest_Part3;
  409. //
  410. Property PreviousUpdatedBlocks : TAccountPreviousBlockInfo read FPreviousUpdatedBlocks; // New Protocol V3 struct to store previous updated blocks
  411. Property HasValidOperationBlockInfo : Boolean read FHasValidOperationBlockInfo write FHasValidOperationBlockInfo;
  412. End;
  413. TPCBankLog = procedure(sender: TPCBank; Operations: TPCOperationsComp; Logtype: TLogType ; const Logtxt: String) of object;
  414. TPCBankNotify = Class(TComponent)
  415. private
  416. FOnNewBlock: TNotifyEvent;
  417. FBank: TPCBank;
  418. procedure SetBank(const Value: TPCBank);
  419. protected
  420. procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
  421. Procedure NotifyNewBlock;
  422. public
  423. Constructor Create(AOwner: TComponent); Override;
  424. Destructor Destroy; Override;
  425. Property Bank : TPCBank read FBank write SetBank;
  426. Property OnNewBlock : TNotifyEvent read FOnNewBlock write FOnNewBlock;
  427. End;
  428. TOrphan = RawByteString;
  429. { TStorage }
  430. TStorage = Class(TComponent)
  431. private
  432. FOrphan: TOrphan;
  433. FBank : TPCBank;
  434. FReadOnly: Boolean;
  435. procedure SetBank(const Value: TPCBank);
  436. protected
  437. FIsMovingBlockchain : Boolean;
  438. procedure SetOrphan(const Value: TOrphan); virtual;
  439. procedure SetReadOnly(const Value: Boolean); virtual;
  440. Function DoLoadBlockChain(Operations : TPCOperationsComp; Block : Cardinal) : Boolean; virtual; abstract;
  441. Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
  442. Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; virtual; abstract;
  443. Function DoSaveBank : Boolean; virtual; abstract;
  444. Function DoRestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify) : Boolean; virtual; abstract;
  445. Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); virtual; abstract;
  446. Function DoBlockExists(Block : Cardinal) : Boolean; virtual; abstract;
  447. function GetFirstBlockNumber: Int64; virtual; abstract;
  448. function GetLastBlockNumber: Int64; virtual; abstract;
  449. function DoInitialize:Boolean; virtual; abstract;
  450. Function DoCreateSafeBoxStream(blockCount : Cardinal) : TStream; virtual; abstract;
  451. Procedure DoEraseStorage; virtual; abstract;
  452. Procedure DoSavePendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
  453. Procedure DoLoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree); virtual; abstract;
  454. public
  455. Function LoadBlockChainBlock(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
  456. Function SaveBlockChainBlock(Operations : TPCOperationsComp) : Boolean;
  457. Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
  458. Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
  459. Function SaveBank(forceSave : Boolean) : Boolean;
  460. Function RestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil) : Boolean;
  461. Constructor Create(AOwner : TComponent); Override;
  462. Property Orphan : TOrphan read FOrphan write SetOrphan;
  463. Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
  464. Property Bank : TPCBank read FBank write SetBank;
  465. Procedure CopyConfiguration(Const CopyFrom : TStorage); virtual;
  466. Property FirstBlock : Int64 read GetFirstBlockNumber;
  467. Property LastBlock : Int64 read GetLastBlockNumber;
  468. Function Initialize : Boolean;
  469. Function CreateSafeBoxStream(blockCount : Cardinal) : TStream;
  470. Function HasUpgradedToVersion2 : Boolean; virtual; abstract;
  471. Procedure CleanupVersion1Data; virtual; abstract;
  472. Procedure EraseStorage;
  473. Procedure SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
  474. Procedure LoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
  475. Function BlockExists(Block : Cardinal) : Boolean;
  476. End;
  477. TStorageClass = Class of TStorage;
  478. { TPCBank }
  479. TPCBank = Class(TComponent)
  480. private
  481. FStorage : TStorage;
  482. FSafeBox: TPCSafeBox;
  483. FLastBlockCache : TPCOperationsComp;
  484. FLastOperationBlock: TOperationBlock;
  485. FIsRestoringFromFile: Boolean;
  486. FUpgradingToV2: Boolean;
  487. FOnLog: TPCBankLog;
  488. FBankLock: TPCCriticalSection;
  489. FNotifyList : TList<TPCBankNotify>;
  490. FStorageClass: TStorageClass;
  491. function GetStorage: TStorage;
  492. procedure SetStorageClass(const Value: TStorageClass);
  493. public
  494. Constructor Create(AOwner: TComponent); Override;
  495. Destructor Destroy; Override;
  496. Function BlocksCount: Cardinal;
  497. Function AccountsCount : Cardinal;
  498. procedure AssignTo(Dest: TPersistent); Override;
  499. function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
  500. function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): Real;
  501. function LoadBankFromStream(Stream : TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors : String) : Boolean;
  502. Procedure Clear;
  503. Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
  504. Property SafeBox : TPCSafeBox read FSafeBox;
  505. Function AddNewBlockChainBlock(Operations: TPCOperationsComp; MaxAllowedTimestamp : Cardinal; var newBlock: TBlockAccount; var errors: String): Boolean;
  506. Procedure DiskRestoreFromOperations(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil);
  507. Procedure UpdateValuesFromSafebox;
  508. Procedure NewLog(Operations: TPCOperationsComp; Logtype: TLogType; const Logtxt: String);
  509. Property OnLog: TPCBankLog read FOnLog write FOnLog;
  510. Property LastOperationBlock : TOperationBlock read FLastOperationBlock; // TODO: Use
  511. Property Storage : TStorage read GetStorage;
  512. Property StorageClass : TStorageClass read FStorageClass write SetStorageClass;
  513. Function IsReady(Var CurrentProcess : String) : Boolean;
  514. Property LastBlockFound : TPCOperationsComp read FLastBlockCache;
  515. Property UpgradingToV2 : Boolean read FUpgradingToV2;
  516. End;
  517. Const
  518. CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
  519. CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
  520. CT_TMultiOpSender_NUL : TMultiOpSender = (Account:0;Amount:0;N_Operation:0;Payload:(payload_type:0;payload_raw:Nil);Signature:(r:Nil;s:Nil));
  521. CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:(payload_type:0;payload_raw:Nil));
  522. CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;New_Data:Nil;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;
  523. Hashed_secret:Nil;
  524. Fee:0;Signature:(r:Nil;s:Nil));
  525. CT_TOpChangeAccountInfoType_Txt : Array[Low(TOpChangeAccountInfoType)..High(TOpChangeAccountInfoType)] of String = ('public_key','account_name','account_type','list_for_public_sale','list_for_private_sale', 'delist', 'account_data','list_for_account_swap','list_for_coin_swap');
  526. implementation
  527. uses
  528. Variants,
  529. UTime, UConst, UOpTransaction, UPCOrderedLists,
  530. UPCOperationsSignatureValidator,
  531. UPCOperationsBlockValidator;
  532. { TPCOperationsStorage }
  533. var
  534. _PCOperationsStorage : TPCOperationsStorage;
  535. function TPCOperationsStorage.FindOrderedByPtrPCOperation(lockedThreadList: TList<Pointer>; const Value: TPCOperation; out Index: Integer): Boolean;
  536. var L, H, I: Integer;
  537. C : PtrInt;
  538. begin
  539. Result := False;
  540. L := 0;
  541. H := lockedThreadList.Count - 1;
  542. while L <= H do
  543. begin
  544. I := (L + H) shr 1;
  545. C := PtrInt(PPCOperationTStorage(lockedThreadList[I])^.ptrPCOperation) - PtrInt(Value);
  546. if C < 0 then L := I + 1 else
  547. begin
  548. H := I - 1;
  549. if C = 0 then
  550. begin
  551. Result := True;
  552. L := I;
  553. end;
  554. end;
  555. end;
  556. Index := L;
  557. end;
  558. constructor TPCOperationsStorage.Create;
  559. begin
  560. FPCOperationsStorageList := TPCThreadList<Pointer>.Create(ClassName);
  561. FIntTotalNewOps := 0;
  562. FIntTotalAdded := 0;
  563. FIntTotalDeleted := 0;
  564. FMaxLocksCount := 0;
  565. FMaxLocksValue := 0;
  566. end;
  567. destructor TPCOperationsStorage.Destroy;
  568. Var list : TList<Pointer>;
  569. P : PPCOperationTStorage;
  570. i : Integer;
  571. pc : TPCOperation;
  572. begin
  573. list := LockPCOperationsStorage;
  574. try
  575. for i:=0 to list.Count-1 do begin
  576. P := list[i];
  577. pc := P^.ptrPCOperation;
  578. P^.ptrPCOperation := Nil;
  579. P^.locksCount:=-1;
  580. pc.Free;
  581. Dispose(P);
  582. end;
  583. inc(FIntTotalDeleted,list.Count);
  584. finally
  585. list.Clear;
  586. UnlockPCOperationsStorage;
  587. end;
  588. FreeAndNil(FPCOperationsStorageList);
  589. inherited Destroy;
  590. end;
  591. function TPCOperationsStorage.LockPCOperationsStorage: TList<Pointer>;
  592. begin
  593. Result := FPCOperationsStorageList.LockList;
  594. end;
  595. procedure TPCOperationsStorage.UnlockPCOperationsStorage;
  596. begin
  597. FPCOperationsStorageList.UnlockList;
  598. end;
  599. function TPCOperationsStorage.Count: Integer;
  600. var list : TList<Pointer>;
  601. begin
  602. list := LockPCOperationsStorage;
  603. try
  604. Result := list.Count;
  605. finally
  606. UnlockPCOperationsStorage;
  607. end;
  608. end;
  609. procedure TPCOperationsStorage.AddPCOperation(APCOperation: TPCOperation);
  610. var P : PPCOperationTStorage;
  611. list : TList<Pointer>;
  612. iPos : Integer;
  613. begin
  614. list := LockPCOperationsStorage;
  615. try
  616. if FindOrderedByPtrPCOperation(list,APCOperation,iPos) then begin
  617. P := list[iPos];
  618. end else begin
  619. New(P);
  620. P^.locksCount:=0;
  621. P^.ptrPCOperation := APCOperation;
  622. list.Insert(iPos,P);
  623. inc(FIntTotalNewOps);
  624. end;
  625. inc(P^.locksCount);
  626. inc(FIntTotalAdded);
  627. if (P^.locksCount>FMaxLocksValue) then begin
  628. FMaxLocksValue:=P^.locksCount;
  629. FMaxLocksCount:=0;
  630. end;
  631. inc(FMaxLocksCount);
  632. finally
  633. UnlockPCOperationsStorage;
  634. end;
  635. end;
  636. procedure TPCOperationsStorage.RemovePCOperation(APCOperation: TPCOperation);
  637. var P : PPCOperationTStorage;
  638. list : TList<Pointer>;
  639. iPos : Integer;
  640. begin
  641. list := LockPCOperationsStorage;
  642. try
  643. if FindOrderedByPtrPCOperation(list,APCOperation,iPos) then begin
  644. P := list[iPos];
  645. Dec(P^.locksCount);
  646. if (P^.locksCount<=0) then begin
  647. // Remove
  648. list.Delete(iPos);
  649. P^.ptrPCOperation := Nil;
  650. Dispose(P);
  651. APCOperation.Free;
  652. end;
  653. inc(FIntTotalDeleted);
  654. end else begin
  655. TLog.NewLog(lterror,ClassName,'ERROR DEV 20181218-2 Operation not found in storage to remove: '+APCOperation.ToString);
  656. end;
  657. finally
  658. UnlockPCOperationsStorage;
  659. end;
  660. end;
  661. function TPCOperationsStorage.FindPCOperation(APCOperation: TPCOperation): Boolean;
  662. var list : TList<Pointer>;
  663. iPos : Integer;
  664. begin
  665. list := LockPCOperationsStorage;
  666. Try
  667. Result := FindOrderedByPtrPCOperation(list,APCOperation,iPos);
  668. finally
  669. UnlockPCOperationsStorage;
  670. end;
  671. end;
  672. function TPCOperationsStorage.FindPCOperationAndIncCounterIfFound(APCOperation: TPCOperation): Boolean;
  673. var list : TList<Pointer>;
  674. iPos : Integer;
  675. begin
  676. list := LockPCOperationsStorage;
  677. Try
  678. Result := FindOrderedByPtrPCOperation(list,APCOperation,iPos);
  679. if Result then begin
  680. Inc(PPCOperationTStorage(list[iPos])^.locksCount);
  681. inc(FIntTotalAdded);
  682. if (PPCOperationTStorage(list[iPos])^.locksCount>FMaxLocksValue) then begin
  683. FMaxLocksValue:=PPCOperationTStorage(list[iPos])^.locksCount;
  684. FMaxLocksCount:=0;
  685. end;
  686. inc(FMaxLocksCount);
  687. end;
  688. finally
  689. UnlockPCOperationsStorage;
  690. end;
  691. end;
  692. class function TPCOperationsStorage.PCOperationsStorage: TPCOperationsStorage;
  693. begin
  694. Result := _PCOperationsStorage;
  695. end;
  696. procedure TPCOperationsStorage.GetStats(strings: TStrings);
  697. var list : TList<Pointer>;
  698. i : Integer;
  699. P : PPCOperationTStorage;
  700. begin
  701. list := LockPCOperationsStorage;
  702. try
  703. strings.Add(Format('%s Operations:%d NewAdded:%d Added:%d Deleted:%d',[ClassName,list.Count,FIntTotalNewOps,FIntTotalAdded,FIntTotalDeleted]));
  704. strings.Add(Format('MaxLocks:%d MaxLocksCount:%d',[FMaxLocksValue,FMaxLocksCount]));
  705. for i:=0 to list.Count-1 do begin
  706. P := PPCOperationTStorage(list[i]);
  707. strings.Add(Format('%d %s',[P^.locksCount,P^.ptrPCOperation.ToString]));
  708. end;
  709. finally
  710. UnlockPCOperationsStorage;
  711. end;
  712. end;
  713. { TPCBank }
  714. function TPCBank.AccountsCount: Cardinal;
  715. begin
  716. Result := FSafeBox.AccountsCount;
  717. end;
  718. function TPCBank.AddNewBlockChainBlock(Operations: TPCOperationsComp; MaxAllowedTimestamp : Cardinal; var newBlock: TBlockAccount; var errors: String): Boolean;
  719. Var i : Integer;
  720. begin
  721. TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
  722. Try
  723. Result := False;
  724. errors := '';
  725. Operations.Lock; // New Protection
  726. Try
  727. If Not Operations.ValidateOperationBlock(errors) then begin
  728. exit;
  729. end;
  730. if (Operations.OperationBlock.block > 0) then begin
  731. if ((MaxAllowedTimestamp>0) And (Operations.OperationBlock.timestamp>MaxAllowedTimestamp)) then begin
  732. errors := 'Invalid timestamp (Future time: New timestamp '+Inttostr(Operations.OperationBlock.timestamp)+' > max allowed '+inttostr(MaxAllowedTimestamp)+')';
  733. exit;
  734. end;
  735. end;
  736. // Ok, include!
  737. // WINNER !!!
  738. // Congrats!
  739. if Not Operations.SafeBoxTransaction.Commit(Operations.OperationBlock,errors) then begin
  740. exit;
  741. end;
  742. newBlock := SafeBox.Block(SafeBox.BlocksCount-1);
  743. // Initialize values
  744. FLastOperationBlock := Operations.OperationBlock;
  745. // log it!
  746. NewLog(Operations, ltupdate,
  747. Format('New block height:%d nOnce:%d timestamp:%d Operations:%d Fee:%d SafeBoxBalance:%d=%d PoW:%s Operations previous Safe Box hash:%s Future old Safe Box hash for next block:%s',
  748. [ Operations.OperationBlock.block,Operations.OperationBlock.nonce,Operations.OperationBlock.timestamp,
  749. Operations.Count,
  750. Operations.OperationBlock.fee,
  751. SafeBox.TotalBalance,
  752. Operations.SafeBoxTransaction.TotalBalance,
  753. TCrypto.ToHexaString(Operations.OperationBlock.proof_of_work),
  754. TCrypto.ToHexaString(Operations.OperationBlock.initial_safe_box_hash),
  755. TCrypto.ToHexaString(SafeBox.SafeBoxHash)]));
  756. // Save Operations to disk
  757. if Not FIsRestoringFromFile then begin
  758. Storage.SaveBlockChainBlock(Operations);
  759. end;
  760. FLastBlockCache.CopyFrom(Operations);
  761. Operations.Clear(true);
  762. Result := true;
  763. Finally
  764. if Not Result then begin
  765. NewLog(Operations, lterror, 'Invalid new block '+inttostr(Operations.OperationBlock.block)+': ' + errors+ ' > '+TPCOperationsComp.OperationBlockToText(Operations.OperationBlock));
  766. end;
  767. Operations.Unlock;
  768. End;
  769. Finally
  770. FBankLock.Release;
  771. End;
  772. if Result then begin
  773. for i := 0 to FNotifyList.Count - 1 do begin
  774. TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
  775. end;
  776. end;
  777. end;
  778. procedure TPCBank.AssignTo(Dest: TPersistent);
  779. var d : TPCBank;
  780. begin
  781. if (Not (Dest is TPCBank)) then begin
  782. inherited;
  783. exit;
  784. end;
  785. if (Self=Dest) then exit;
  786. d := TPCBank(Dest);
  787. d.SafeBox.CopyFrom(SafeBox);
  788. d.FLastOperationBlock := FLastOperationBlock;
  789. d.FIsRestoringFromFile := FIsRestoringFromFile;
  790. d.FLastBlockCache.CopyFrom( FLastBlockCache );
  791. end;
  792. function TPCBank.BlocksCount: Cardinal;
  793. begin
  794. Result := SafeBox.BlocksCount;
  795. end;
  796. procedure TPCBank.Clear;
  797. begin
  798. SafeBox.Clear;
  799. FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
  800. FLastOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Genesis hash
  801. FLastBlockCache.Clear(true);
  802. {$IFDEF HIGHLOG}NewLog(Nil, ltdebug, 'Clear Bank');{$ENDIF}
  803. end;
  804. constructor TPCBank.Create(AOwner: TComponent);
  805. begin
  806. inherited;
  807. FStorage := Nil;
  808. FStorageClass := Nil;
  809. FBankLock := TPCCriticalSection.Create('TPCBank_BANKLOCK');
  810. FIsRestoringFromFile := False;
  811. FOnLog := Nil;
  812. FSafeBox := TPCSafeBox.Create;
  813. FNotifyList := TList<TPCBankNotify>.Create;
  814. FLastBlockCache := TPCOperationsComp.Create(Nil);
  815. FIsRestoringFromFile:=False;
  816. FUpgradingToV2:=False;
  817. Clear;
  818. end;
  819. destructor TPCBank.Destroy;
  820. var step : String;
  821. begin
  822. Try
  823. step := 'Deleting critical section';
  824. FreeAndNil(FBankLock);
  825. step := 'Clear';
  826. Clear;
  827. step := 'Destroying LastBlockCache';
  828. FreeAndNil(FLastBlockCache);
  829. step := 'Destroying SafeBox';
  830. FreeAndNil(FSafeBox);
  831. step := 'Destroying NotifyList';
  832. FreeAndNil(FNotifyList);
  833. step := 'Destroying Storage';
  834. FreeAndNil(FStorage);
  835. step := 'inherited';
  836. inherited;
  837. Except
  838. On E:Exception do begin
  839. TLog.NewLog(lterror,Classname,'Error destroying Bank step: '+step+' Errors ('+E.ClassName+'): ' +E.Message);
  840. Raise;
  841. end;
  842. End;
  843. end;
  844. procedure TPCBank.DiskRestoreFromOperations(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil);
  845. Var
  846. errors: String;
  847. newBlock: TBlockAccount;
  848. n : Int64;
  849. tc : TTickCount;
  850. LBlocks : TList<TPCOperationsComp>;
  851. LTmpPCOperationsComp : TPCOperationsComp;
  852. i,j, LProgressBlock, LProgressEndBlock, LOpsInBlocks : Integer;
  853. LSafeboxTransaction : TPCSafeBoxTransaction;
  854. begin
  855. if FIsRestoringFromFile then begin
  856. TLog.NewLog(lterror,Classname,'Is Restoring!!!');
  857. raise Exception.Create('Is restoring!');
  858. end;
  859. tc := TPlatform.GetTickCount;
  860. TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
  861. try
  862. FUpgradingToV2 := NOT Storage.HasUpgradedToVersion2;
  863. FIsRestoringFromFile := true;
  864. try
  865. Clear;
  866. Storage.Initialize;
  867. If (max_block<Storage.LastBlock) then n := max_block
  868. else n := Storage.LastBlock;
  869. Storage.RestoreBank(n,restoreProgressNotify);
  870. // Restore last blockchain
  871. if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
  872. if Not Storage.LoadBlockChainBlock(FLastBlockCache,BlocksCount-1) then begin
  873. NewLog(nil,lterror,'Cannot find blockchain '+inttostr(BlocksCount-1)+' so cannot accept bank current block '+inttostr(BlocksCount));
  874. Clear;
  875. end else begin
  876. FLastOperationBlock := FLastBlockCache.OperationBlock;
  877. end;
  878. end;
  879. NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') BlockCount: '+inttostr(BlocksCount)+' Orphan: ' +Storage.Orphan);
  880. LBlocks := TList<TPCOperationsComp>.Create;
  881. try
  882. LProgressBlock := 0;
  883. LProgressEndBlock := Storage.LastBlock - BlocksCount;
  884. while ((BlocksCount<=max_block)) do begin
  885. i := BlocksCount;
  886. j := i + 99;
  887. // Load a batch of TPCOperationsComp;
  888. try
  889. LOpsInBlocks := 0;
  890. while ((i<=max_block) and (i<=j)) do begin
  891. if Storage.BlockExists(i) then begin
  892. LTmpPCOperationsComp := TPCOperationsComp.Create(Self);
  893. if Storage.LoadBlockChainBlock(LTmpPCOperationsComp,i) then begin
  894. LBlocks.Add(LTmpPCOperationsComp);
  895. inc(LOpsInBlocks, LTmpPCOperationsComp.Count);
  896. inc(i);
  897. end else begin
  898. LTmpPCOperationsComp.Free;
  899. Break;
  900. end;
  901. end else Break;
  902. end;
  903. if (LBlocks.Count=0) then Exit;
  904. if Assigned(restoreProgressNotify) then begin
  905. restoreProgressNotify(Self,Format('Reading blocks from %d to %d with %d operations',[BlocksCount,i,LOpsInBlocks]),0,0);
  906. end;
  907. TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(LBlocks);
  908. LSafeboxTransaction := TPCSafeBoxTransaction.Create(SafeBox);
  909. try
  910. TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LSafeboxTransaction,LBlocks,restoreProgressNotify);
  911. finally
  912. LSafeboxTransaction.Free;
  913. end;
  914. for i := 0 to LBlocks.Count-1 do begin
  915. inc(LProgressBlock);
  916. SetLength(errors,0);
  917. if Not AddNewBlockChainBlock(LBlocks[i],0,newBlock,errors) then begin
  918. NewLog(LBlocks[i], lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
  919. Storage.DeleteBlockChainBlocks(BlocksCount);
  920. Exit;
  921. end else begin
  922. // To prevent continuous saving...
  923. if ((BlocksCount+(CT_BankToDiskEveryNBlocks*2)) >= Storage.LastBlock ) or
  924. ((BlocksCount MOD (CT_BankToDiskEveryNBlocks*10))=0) then begin
  925. Storage.SaveBank(False);
  926. end;
  927. if (Assigned(restoreProgressNotify)) And (TPlatform.GetElapsedMilliseconds(tc)>1000) then begin
  928. tc := TPlatform.GetTickCount;
  929. restoreProgressNotify(Self,Format('Reading blockchain block %d/%d',[LBlocks[i].OperationBlock.block,Storage.LastBlock]),LProgressBlock,LProgressEndBlock);
  930. end;
  931. end;
  932. end;
  933. finally
  934. // Free blocks
  935. for i := 0 to LBlocks.Count-1 do begin
  936. LBlocks[i].Free;
  937. end;
  938. LBlocks.Clear;
  939. end;
  940. end; // while
  941. finally
  942. LBlocks.Free;
  943. if FUpgradingToV2 then Storage.CleanupVersion1Data;
  944. NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
  945. end;
  946. finally
  947. FIsRestoringFromFile := False;
  948. FUpgradingToV2 := false;
  949. end;
  950. finally
  951. FBankLock.Release;
  952. end;
  953. end;
  954. procedure TPCBank.UpdateValuesFromSafebox;
  955. Var aux : String;
  956. i : Integer;
  957. begin
  958. { Will update current Bank state based on Safebox state
  959. Used when commiting a Safebox or rolling back }
  960. Try
  961. TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
  962. try
  963. FLastBlockCache.Clear(True);
  964. FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
  965. FLastOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Genesis hash
  966. If FSafeBox.BlocksCount>0 then begin
  967. Storage.Initialize;
  968. If Storage.LoadBlockChainBlock(FLastBlockCache,FSafeBox.BlocksCount-1) then begin
  969. FLastOperationBlock := FLastBlockCache.OperationBlock;
  970. end else begin
  971. aux := 'Cannot read last operations block '+IntToStr(FSafeBox.BlocksCount-1)+' from blockchain';
  972. TLog.NewLog(lterror,ClassName,aux);
  973. Raise Exception.Create(aux);
  974. end;
  975. end;
  976. TLog.NewLog(ltinfo,ClassName,Format('Updated Bank with Safebox values. Current block:%d ',[FLastOperationBlock.block]));
  977. finally
  978. FBankLock.Release;
  979. end;
  980. finally
  981. for i := 0 to FNotifyList.Count - 1 do begin
  982. TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
  983. end;
  984. end;
  985. end;
  986. function TPCBank.GetActualTargetSecondsAverage(BackBlocks: Cardinal): Real;
  987. Var ts1, ts2: Int64;
  988. begin
  989. if BlocksCount>BackBlocks then begin
  990. ts1 := SafeBox.Block(BlocksCount-1).blockchainInfo.timestamp;
  991. ts2 := SafeBox.Block(BlocksCount-BackBlocks-1).blockchainInfo.timestamp;
  992. end else if (BlocksCount>1) then begin
  993. ts1 := SafeBox.Block(BlocksCount-1).blockchainInfo.timestamp;
  994. ts2 := SafeBox.Block(0).blockchainInfo.timestamp;
  995. BackBlocks := BlocksCount-1;
  996. end else begin
  997. Result := 0;
  998. exit;
  999. end;
  1000. Result := (ts1 - ts2) / BackBlocks;
  1001. end;
  1002. function TPCBank.GetTargetSecondsAverage(FromBlock, BackBlocks: Cardinal): Real;
  1003. Var ts1, ts2: Int64;
  1004. begin
  1005. If FromBlock>=BlocksCount then begin
  1006. Result := 0;
  1007. exit;
  1008. end;
  1009. if FromBlock>BackBlocks then begin
  1010. ts1 := SafeBox.Block(FromBlock-1).blockchainInfo.timestamp;
  1011. ts2 := SafeBox.Block(FromBlock-BackBlocks-1).blockchainInfo.timestamp;
  1012. end else if (FromBlock>1) then begin
  1013. ts1 := SafeBox.Block(FromBlock-1).blockchainInfo.timestamp;
  1014. ts2 := SafeBox.Block(0).blockchainInfo.timestamp;
  1015. BackBlocks := FromBlock-1;
  1016. end else begin
  1017. Result := 0;
  1018. exit;
  1019. end;
  1020. Result := (ts1 - ts2) / BackBlocks;
  1021. end;
  1022. function TPCBank.GetStorage: TStorage;
  1023. begin
  1024. if Not Assigned(FStorage) then begin
  1025. if Not Assigned(FStorageClass) then raise Exception.Create('StorageClass not defined');
  1026. FStorage := FStorageClass.Create(Self);
  1027. FStorage.Bank := Self;
  1028. end;
  1029. Result := FStorage;
  1030. end;
  1031. function TPCBank.IsReady(Var CurrentProcess: String): Boolean;
  1032. begin
  1033. Result := false;
  1034. CurrentProcess := '';
  1035. if FIsRestoringFromFile then begin
  1036. if FUpgradingToV2 then
  1037. CurrentProcess := 'Migrating to version 2 format'
  1038. else
  1039. CurrentProcess := 'Restoring from file'
  1040. end else Result := true;
  1041. end;
  1042. function TPCBank.LoadBankFromStream(Stream: TStream; useSecureLoad : Boolean; checkSafeboxHash : TRawBytes; previousCheckedSafebox : TPCSafebox; progressNotify : TProgressNotify; var errors: String): Boolean;
  1043. Var LastReadBlock : TBlockAccount;
  1044. i : Integer;
  1045. auxSB : TPCSafeBox;
  1046. begin
  1047. auxSB := Nil;
  1048. Try
  1049. If useSecureLoad then begin
  1050. // When on secure load will load Stream in a separate SafeBox, changing only real SafeBox if successfully
  1051. auxSB := TPCSafeBox.Create;
  1052. Result := auxSB.LoadSafeBoxFromStream(Stream,true,checkSafeboxHash,progressNotify,previousCheckedSafebox,LastReadBlock,errors);
  1053. If Not Result then Exit;
  1054. end;
  1055. TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
  1056. try
  1057. If Assigned(auxSB) then begin
  1058. SafeBox.CopyFrom(auxSB);
  1059. end else begin
  1060. Result := SafeBox.LoadSafeBoxFromStream(Stream,False,checkSafeboxHash,progressNotify,previousCheckedSafebox,LastReadBlock,errors);
  1061. end;
  1062. If Not Result then exit;
  1063. If SafeBox.BlocksCount>0 then FLastOperationBlock := SafeBox.Block(SafeBox.BlocksCount-1).blockchainInfo
  1064. else begin
  1065. FLastOperationBlock := TPCOperationsComp.GetFirstBlock;
  1066. FLastOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Genesis hash
  1067. end;
  1068. finally
  1069. FBankLock.Release;
  1070. end;
  1071. for i := 0 to FNotifyList.Count - 1 do begin
  1072. TPCBankNotify(FNotifyList.Items[i]).NotifyNewBlock;
  1073. end;
  1074. finally
  1075. If Assigned(auxSB) then auxSB.Free;
  1076. end;
  1077. end;
  1078. function TPCBank.LoadOperations(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
  1079. begin
  1080. TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
  1081. try
  1082. if (Block>0) AND (Block=FLastBlockCache.OperationBlock.block) then begin
  1083. // Same as cache, sending cache
  1084. Operations.CopyFrom(FLastBlockCache);
  1085. Result := true;
  1086. end else begin
  1087. Result := Storage.LoadBlockChainBlock(Operations,Block);
  1088. end;
  1089. finally
  1090. FBankLock.Release;
  1091. end;
  1092. end;
  1093. procedure TPCBank.NewLog(Operations: TPCOperationsComp; Logtype: TLogType; const Logtxt: String);
  1094. var s : String;
  1095. begin
  1096. if Assigned(Operations) then s := Operations.ClassName
  1097. else s := Classname;
  1098. TLog.NewLog(Logtype,s,Logtxt);
  1099. if Assigned(FOnLog) then
  1100. FOnLog(Self, Operations, Logtype, Logtxt);
  1101. end;
  1102. procedure TPCBank.SetStorageClass(const Value: TStorageClass);
  1103. begin
  1104. if FStorageClass=Value then exit;
  1105. FStorageClass := Value;
  1106. if Assigned(FStorage) then FreeAndNil(FStorage);
  1107. end;
  1108. { TPCOperationsComp }
  1109. var
  1110. _OperationsClass: Array of TPCOperationClass;
  1111. function TPCOperationsComp.AddOperation(Execute: Boolean; op: TPCOperation; var errors: String): Boolean;
  1112. var i : Integer;
  1113. auxs : String;
  1114. Begin
  1115. Lock;
  1116. Try
  1117. errors := '';
  1118. Result := False;
  1119. if Execute then begin
  1120. if (FBank = Nil) then begin
  1121. errors := 'No Bank';
  1122. exit;
  1123. end;
  1124. if (FBank.BlocksCount<>OperationBlock.block) then begin
  1125. errors := 'Bank blockcount<>OperationBlock.Block';
  1126. exit;
  1127. end;
  1128. if OperationBlock.protocol_version <> op.ProtocolVersion then begin
  1129. errors := Format('Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
  1130. Tlog.NewLog(lterror,ClassName,errors);
  1131. Exit;
  1132. end;
  1133. // Only process when in current address, prevent do it when reading operations from file
  1134. if FOperationsHashTree.CanAddOperationToHashTree(op) then begin
  1135. Result := op.DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, errors);
  1136. end else begin
  1137. errors := 'Cannot add operation. Limits reached';
  1138. Exit;
  1139. end;
  1140. end else Result := true;
  1141. if Result then begin
  1142. if FOperationsHashTree.AddOperationToHashTree(op) then begin
  1143. if FIsOnlyOperationBlock then begin
  1144. // Clear fee values and put to False
  1145. FIsOnlyOperationBlock := False;
  1146. FOperationBlock.fee := op.OperationFee;
  1147. end else begin
  1148. FOperationBlock.fee := FOperationBlock.fee + op.OperationFee;
  1149. end;
  1150. FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
  1151. if FDisableds<=0 then Calc_Digest_Parts;
  1152. end else begin
  1153. errors := 'Cannot add operation. Limits reached';
  1154. if (Execute) then begin
  1155. // Undo execute
  1156. TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute due limits reached. Executing %d operations',[FOperationsHashTree.OperationsCount]));
  1157. FPreviousUpdatedBlocks.Clear;
  1158. FSafeBoxTransaction.Rollback;
  1159. for i := 0 to FOperationsHashTree.OperationsCount-1 do FOperationsHashTree.GetOperation(i).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
  1160. end;
  1161. Result := False;
  1162. end;
  1163. end;
  1164. finally
  1165. Unlock;
  1166. end;
  1167. End;
  1168. function TPCOperationsComp.AddOperations(operations: TOperationsHashTree; var errors: String): Integer;
  1169. Var i : Integer;
  1170. e : String;
  1171. begin
  1172. Lock;
  1173. try
  1174. Result := 0;
  1175. errors := '';
  1176. if operations=FOperationsHashTree then exit;
  1177. inc(FDisableds);
  1178. try
  1179. for i := 0 to operations.OperationsCount - 1 do begin
  1180. if not AddOperation(true,operations.GetOperation(i),e) then begin
  1181. if (errors<>'') then errors := errors+' ';
  1182. errors := errors + 'Op'+inttostr(i+1)+'/'+inttostr(operations.OperationsCount)+':'+e;
  1183. end else inc(Result);
  1184. end;
  1185. finally
  1186. Dec(FDisableds);
  1187. Calc_Digest_Parts;
  1188. end;
  1189. finally
  1190. Unlock;
  1191. end;
  1192. end;
  1193. procedure TPCOperationsComp.CalcProofOfWork(fullcalculation: Boolean; var PoW: TRawBytes);
  1194. begin
  1195. if fullcalculation then begin
  1196. Calc_Digest_Parts;
  1197. end;
  1198. FStreamPoW.Position := 0;
  1199. FStreamPoW.WriteBuffer(FDigest_Part1[Low(FDigest_Part1)],Length(FDigest_Part1));
  1200. FStreamPoW.WriteBuffer(FDigest_Part2_Payload[Low(FDigest_Part2_Payload)],Length(FDigest_Part2_Payload));
  1201. FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
  1202. FStreamPoW.Write(FOperationBlock.timestamp,4);
  1203. FStreamPoW.Write(FOperationBlock.nonce,4);
  1204. if CT_ACTIVATE_RANDOMHASH_V4 AND (FOperationBlock.protocol_version >= CT_PROTOCOL_4) then begin
  1205. if (FOperationBlock.protocol_version < CT_PROTOCOL_5) then
  1206. TCrypto.DoRandomHash(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW)
  1207. else
  1208. TCrypto.DoRandomHash2(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
  1209. end else TCrypto.DoDoubleSha256(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
  1210. end;
  1211. procedure TPCOperationsComp.Calc_Digest_Parts;
  1212. begin
  1213. TPascalCoinProtocol.CalcProofOfWork_Part1(FOperationBlock,FDigest_Part1);
  1214. FDigest_Part2_Payload := FOperationBlock.block_payload;
  1215. Calc_Digest_Part3;
  1216. end;
  1217. procedure TPCOperationsComp.Calc_Digest_Part3;
  1218. begin
  1219. FOperationBlock.operations_hash:=FOperationsHashTree.HashTree;
  1220. TPascalCoinProtocol.CalcProofOfWork_Part3(FOperationBlock,FDigest_Part3);
  1221. end;
  1222. procedure TPCOperationsComp.Clear(DeleteOperations : Boolean);
  1223. var resetNewTarget : Boolean;
  1224. begin
  1225. Lock;
  1226. Try
  1227. if DeleteOperations then begin
  1228. FOperationsHashTree.ClearHastThree;
  1229. FPreviousUpdatedBlocks.Clear;
  1230. if Assigned(FSafeBoxTransaction) then
  1231. FSafeBoxTransaction.CleanTransaction;
  1232. end;
  1233. // Note:
  1234. // This function does not initializes "account_key" nor "block_payload" fields
  1235. FHasValidOperationBlockInfo := False;
  1236. FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
  1237. if Assigned(FBank) then begin
  1238. resetNewTarget := False;
  1239. FOperationBlock.protocol_version := FBank.SafeBox.CurrentProtocol;
  1240. If (FOperationBlock.protocol_version=CT_PROTOCOL_1) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_2)) then begin
  1241. FOperationBlock.protocol_version := CT_PROTOCOL_2; // If minting... upgrade to Protocol 2
  1242. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
  1243. FOperationBlock.protocol_version := CT_PROTOCOL_3; // If minting... upgrade to Protocol 3
  1244. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_3) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_4)) then begin
  1245. FOperationBlock.protocol_version := CT_PROTOCOL_4; // If minting... upgrade to Protocol 4
  1246. {$IFDEF ACTIVATE_RANDOMHASH_V4}
  1247. resetNewTarget := True; // RandomHash algo will reset new target on V4
  1248. {$ENDIF}
  1249. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_4) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_5)) then begin
  1250. FOperationBlock.protocol_version := CT_PROTOCOL_5; // If minting... upgrade to Protocol 5
  1251. end;
  1252. if (FOperationBlock.protocol_version>=CT_PROTOCOL_4) then begin
  1253. FOperationsHashTree.Max0feeOperationsBySigner := 1; // Limit to 1 0-fee operation by signer
  1254. end else FOperationsHashTree.Max0feeOperationsBySigner := -1;
  1255. FOperationBlock.block := FBank.BlocksCount;
  1256. FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
  1257. if (resetNewTarget) then begin
  1258. FOperationBlock.compact_target := TPascalCoinProtocol.ResetTarget(FOperationBlock.compact_target,FOperationBlock.protocol_version);
  1259. end else begin
  1260. FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
  1261. end;
  1262. FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
  1263. FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
  1264. If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
  1265. FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
  1266. end else begin
  1267. FOperationBlock.block := 0;
  1268. FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(0);
  1269. FOperationBlock.compact_target := CT_MinCompactTarget_v1;
  1270. FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Nothing for first line
  1271. FOperationBlock.protocol_version := CT_PROTOCOL_1;
  1272. FOperationsHashTree.Max0feeOperationsBySigner := -1;
  1273. FOperationBlock.previous_proof_of_work := Nil;
  1274. end;
  1275. FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
  1276. FOperationBlock.fee := 0;
  1277. FOperationBlock.nonce := 0;
  1278. FOperationBlock.proof_of_work:=Nil;
  1279. FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
  1280. FIsOnlyOperationBlock := false;
  1281. Finally
  1282. try
  1283. Calc_Digest_Parts; // Does not need to recalc PoW
  1284. finally
  1285. Unlock;
  1286. end;
  1287. End;
  1288. end;
  1289. procedure TPCOperationsComp.CopyFrom(Operations: TPCOperationsComp);
  1290. begin
  1291. if Self=Operations then exit;
  1292. Lock;
  1293. Operations.Lock;
  1294. Try
  1295. FOperationBlock := Operations.FOperationBlock;
  1296. FIsOnlyOperationBlock := Operations.FIsOnlyOperationBlock;
  1297. FOperationsHashTree.CopyFromHashTree(Operations.FOperationsHashTree);
  1298. if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
  1299. FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
  1300. end;
  1301. FPreviousUpdatedBlocks.CopyFrom(Operations.FPreviousUpdatedBlocks);
  1302. FDigest_Part1 := Operations.FDigest_Part1;
  1303. FDigest_Part2_Payload := Operations.FDigest_Part2_Payload;
  1304. FDigest_Part3 := Operations.FDigest_Part3;
  1305. FHasValidOperationBlockInfo := Operations.FHasValidOperationBlockInfo;
  1306. finally
  1307. Operations.Unlock;
  1308. Unlock;
  1309. end;
  1310. end;
  1311. procedure TPCOperationsComp.CopyFromExceptAddressKey(Operations: TPCOperationsComp);
  1312. var lastopb : TOperationBlock;
  1313. begin
  1314. Lock;
  1315. Try
  1316. if Self=Operations then exit;
  1317. lastopb := FOperationBlock;
  1318. FOperationBlock := Operations.FOperationBlock;
  1319. FOperationBlock.account_key := lastopb.account_key; // Except AddressKey
  1320. FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
  1321. FIsOnlyOperationBlock := Operations.FIsOnlyOperationBlock;
  1322. FOperationsHashTree.CopyFromHashTree(Operations.FOperationsHashTree);
  1323. FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
  1324. if Assigned(FSafeBoxTransaction) And Assigned(Operations.FSafeBoxTransaction) then begin
  1325. FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
  1326. end;
  1327. FPreviousUpdatedBlocks.CopyFrom(Operations.FPreviousUpdatedBlocks);
  1328. FHasValidOperationBlockInfo := False;
  1329. // Recalc all
  1330. Calc_Digest_Parts; // Does not need to recalc PoW
  1331. finally
  1332. Unlock;
  1333. end;
  1334. end;
  1335. function TPCOperationsComp.Count: Integer;
  1336. begin
  1337. Result := FOperationsHashTree.OperationsCount;
  1338. end;
  1339. constructor TPCOperationsComp.Create(ABank: TPCBank);
  1340. begin
  1341. FOperationsLock := TPCCriticalSection.Create('TPCOperationsComp_OPERATIONSLOCK');
  1342. FDisableds := 0;
  1343. FStreamPoW := TMemoryStream.Create;
  1344. FStreamPoW.Position := 0;
  1345. FOperationsHashTree := TOperationsHashTree.Create;
  1346. FOperationsHashTree.OnChanged:= OnOperationsHashTreeChanged;
  1347. FBank := Nil;
  1348. FOperationBlock := GetFirstBlock;
  1349. FSafeBoxTransaction := Nil;
  1350. FPreviousUpdatedBlocks := TAccountPreviousBlockInfo.Create;
  1351. FHasValidOperationBlockInfo := False;
  1352. if Assigned(ABank) then begin
  1353. SetBank( TPCBank(ABank) );
  1354. end else Clear(true);
  1355. end;
  1356. destructor TPCOperationsComp.Destroy;
  1357. begin
  1358. FOperationsLock.Acquire;
  1359. try
  1360. Clear(true);
  1361. FreeAndNil(FOperationsHashTree);
  1362. if Assigned(FSafeBoxTransaction) then begin
  1363. FreeAndNil(FSafeBoxTransaction);
  1364. end;
  1365. FreeAndNil(FStreamPoW);
  1366. FreeAndNil(FPreviousUpdatedBlocks);
  1367. finally
  1368. FreeAndNil(FOperationsLock);
  1369. end;
  1370. inherited;
  1371. end;
  1372. class function TPCOperationsComp.EqualsOperationBlock(const OperationBlock1,
  1373. OperationBlock2: TOperationBlock): Boolean;
  1374. begin
  1375. Result := TAccountComp.EqualOperationBlocks(OperationBlock1,OperationBlock2);
  1376. end;
  1377. function TPCOperationsComp.GetAccountKey: TAccountKey;
  1378. begin
  1379. Result := FOperationBlock.account_key;
  1380. end;
  1381. function TPCOperationsComp.GetBlockPayload: TRawBytes;
  1382. begin
  1383. Result := FOperationBlock.block_payload;
  1384. end;
  1385. class function TPCOperationsComp.GetFirstBlock: TOperationBlock;
  1386. begin
  1387. Result := CT_OperationBlock_NUL;
  1388. end;
  1389. function TPCOperationsComp.GetnOnce: Cardinal;
  1390. begin
  1391. Result := FOperationBlock.nonce;
  1392. end;
  1393. function TPCOperationsComp.GetOperation(index: Integer): TPCOperation;
  1394. begin
  1395. Result := FOperationsHashTree.GetOperation(index);
  1396. end;
  1397. class function TPCOperationsComp.GetOperationClassByOpType(OpType: Cardinal): TPCOperationClass;
  1398. Var i : Integer;
  1399. begin
  1400. i := IndexOfOperationClassByOpType(OpType);
  1401. if i<0 then result := Nil
  1402. else Result := TPCOperationClass( _OperationsClass[i] );
  1403. end;
  1404. function TPCOperationsComp.Gettimestamp: Cardinal;
  1405. begin
  1406. Result := FOperationBlock.timestamp;
  1407. end;
  1408. class function TPCOperationsComp.IndexOfOperationClass(OpClass: TPCOperationClass): Integer;
  1409. begin
  1410. for Result := low(_OperationsClass) to high(_OperationsClass) do
  1411. begin
  1412. if (_OperationsClass[Result] = OpClass) then
  1413. exit;
  1414. end;
  1415. Result := -1;
  1416. end;
  1417. class function TPCOperationsComp.IndexOfOperationClassByOpType(OpType: Cardinal): Integer;
  1418. begin
  1419. for Result := low(_OperationsClass) to high(_OperationsClass) do
  1420. begin
  1421. if (_OperationsClass[Result].OpType = OpType) then
  1422. exit;
  1423. end;
  1424. Result := -1;
  1425. end;
  1426. function TPCOperationsComp.LoadBlockFromStorage(Stream: TStream; var errors: String): Boolean;
  1427. begin
  1428. Result := LoadBlockFromStreamExt(Stream,true,errors);
  1429. end;
  1430. function TPCOperationsComp.LoadBlockFromStream(Stream: TStream; var errors: String): Boolean;
  1431. begin
  1432. Result := LoadBlockFromStreamExt(Stream,false,errors);
  1433. end;
  1434. function TPCOperationsComp.LoadBlockFromStreamExt(Stream: TStream; LoadingFromStorage: Boolean; var errors: String): Boolean;
  1435. Var i : Integer;
  1436. lastfee : UInt64;
  1437. soob : Byte;
  1438. raw: TRawBytes;
  1439. load_protocol_version : Word;
  1440. LLoadPreviousUpdatedBlocks : Boolean;
  1441. begin
  1442. Lock;
  1443. Try
  1444. Clear(true);
  1445. Result := False;
  1446. //
  1447. errors := '';
  1448. if (Stream.Size - Stream.Position < 5) then begin
  1449. errors := 'Invalid protocol structure. Check application version!';
  1450. exit;
  1451. end;
  1452. soob := 255;
  1453. Stream.Read(soob,1);
  1454. // About soob var:
  1455. // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
  1456. // In build 1.0.4 soob can has 2 more values: 2 or 3
  1457. // In build 2.0 soob can has 1 more value: 4
  1458. // In build 3.0 soob can have value: 5
  1459. // In future, old values 0 and 1 will no longer be used!
  1460. // - Value 0 and 2 means that contains also operations
  1461. // - Value 1 and 3 means that only contains operationblock info
  1462. // - Value 2 and 3 means that contains protocol info prior to block number
  1463. // - Value 4 means that is loading from storage using protocol v2 (so, includes always operations)
  1464. // - Value 5 means that is loading from storage using TAccountPreviousBlockInfo
  1465. load_protocol_version := CT_PROTOCOL_1;
  1466. LLoadPreviousUpdatedBlocks := False;
  1467. if (soob in [0,2]) then FIsOnlyOperationBlock:=false
  1468. else if (soob in [1,3]) then FIsOnlyOperationBlock:=true
  1469. else if (soob in [4]) then begin
  1470. FIsOnlyOperationBlock:=false;
  1471. load_protocol_version := CT_PROTOCOL_2;
  1472. end else if (soob in [5]) then begin
  1473. FIsOnlyOperationBlock:=False;
  1474. load_protocol_version := CT_PROTOCOL_3;
  1475. LLoadPreviousUpdatedBlocks := True;
  1476. end else begin
  1477. errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
  1478. exit;
  1479. end;
  1480. if (soob in [2,3,4,5]) then begin
  1481. Stream.Read(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
  1482. Stream.Read(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
  1483. end else begin
  1484. // We assume that protocol_version is 1 and protocol_available is 0
  1485. FOperationBlock.protocol_version := 1;
  1486. FOperationBlock.protocol_available := 0;
  1487. end;
  1488. if Stream.Read(FOperationBlock.block, Sizeof(FOperationBlock.block))<0 then exit;
  1489. if TStreamOp.ReadAnsiString(Stream, raw) < 0 then exit;
  1490. FOperationBlock.account_key := TAccountComp.RawString2Accountkey(raw);
  1491. if Stream.Read(FOperationBlock.reward, Sizeof(FOperationBlock.reward)) < 0 then exit;
  1492. if Stream.Read(FOperationBlock.fee, Sizeof(FOperationBlock.fee)) < 0 then exit;
  1493. if Stream.Read(FOperationBlock.timestamp, Sizeof(FOperationBlock.timestamp)) < 0 then exit;
  1494. if Stream.Read(FOperationBlock.compact_target, Sizeof(FOperationBlock.compact_target)) < 0 then exit;
  1495. if Stream.Read(FOperationBlock.nonce, Sizeof(FOperationBlock.nonce)) < 0 then exit;
  1496. if TStreamOp.ReadAnsiString(Stream, FOperationBlock.block_payload) < 0 then exit;
  1497. if TStreamOp.ReadAnsiString(Stream, FOperationBlock.initial_safe_box_hash) < 0 then exit;
  1498. if TStreamOp.ReadAnsiString(Stream, FOperationBlock.operations_hash) < 0 then exit;
  1499. if TStreamOp.ReadAnsiString(Stream, FOperationBlock.proof_of_work) < 0 then exit;
  1500. if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
  1501. if TStreamOp.ReadAnsiString(Stream, FOperationBlock.previous_proof_of_work) < 0 then exit;
  1502. load_protocol_version := FOperationBlock.protocol_version;
  1503. end;
  1504. If FIsOnlyOperationBlock then begin
  1505. Result := true;
  1506. exit;
  1507. end;
  1508. //
  1509. // Fee will be calculated for each operation. Set it to 0 and check later for integrity
  1510. lastfee := OperationBlock.fee;
  1511. FOperationBlock.fee := 0;
  1512. if FOperationBlock.protocol_version>=CT_PROTOCOL_4 then begin
  1513. FOperationsHashTree.Max0feeOperationsBySigner := 1;
  1514. end else FOperationsHashTree.Max0feeOperationsBySigner := -1;
  1515. Result := FOperationsHashTree.LoadOperationsHashTreeFromStream(Stream,LoadingFromStorage,FOperationBlock.protocol_version,load_protocol_version,FPreviousUpdatedBlocks,errors);
  1516. if not Result then begin
  1517. exit;
  1518. end;
  1519. if LLoadPreviousUpdatedBlocks then begin
  1520. Result := FPreviousUpdatedBlocks.LoadFromStream(Stream);
  1521. If Not Result then begin
  1522. errors := 'Invalid PreviousUpdatedBlock stream';
  1523. Exit;
  1524. end;
  1525. end;
  1526. //
  1527. FOperationBlock.fee := FOperationsHashTree.TotalFee;
  1528. FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
  1529. Calc_Digest_Parts;
  1530. // Validation control:
  1531. if (lastfee<>OperationBlock.fee) then begin
  1532. errors := 'Corrupted operations fee old:'+inttostr(lastfee)+' new:'+inttostr(OperationBlock.fee);
  1533. for i := 0 to FOperationsHashTree.OperationsCount - 1 do begin
  1534. errors := errors + ' Op'+inttostr(i+1)+':'+FOperationsHashTree.GetOperation(i).ToString;
  1535. end;
  1536. Result := false;
  1537. exit;
  1538. end;
  1539. Result := true;
  1540. finally
  1541. Unlock;
  1542. end;
  1543. end;
  1544. class function TPCOperationsComp.OperationBlockToText(const OperationBlock: TOperationBlock): String;
  1545. begin
  1546. Result := Format('Block:%d Timestamp:%d Reward:%d Fee:%d Target:%d PoW:%s Payload:%s Nonce:%d OperationsHash:%s SBH:%s',[operationBlock.block,
  1547. operationblock.timestamp,operationblock.reward,operationblock.fee, OperationBlock.compact_target, TCrypto.ToHexaString(operationblock.proof_of_work),
  1548. OperationBlock.block_payload.ToPrintable,OperationBlock.nonce,TCrypto.ToHexaString(OperationBlock.operations_hash),
  1549. TCrypto.ToHexaString(OperationBlock.initial_safe_box_hash)]);
  1550. end;
  1551. class function TPCOperationsComp.RegisterOperationClass(OpClass: TPCOperationClass): Boolean;
  1552. Var
  1553. i: Integer;
  1554. begin
  1555. i := IndexOfOperationClass(OpClass);
  1556. if i >= 0 then
  1557. exit;
  1558. SetLength(_OperationsClass, Length(_OperationsClass) + 1);
  1559. _OperationsClass[ high(_OperationsClass)] := OpClass;
  1560. end;
  1561. procedure TPCOperationsComp.SanitizeOperations;
  1562. { This function check operationblock with bank and updates itself if necessary
  1563. Then checks if operations are ok, and deletes old ones.
  1564. Finally calculates new operation pow
  1565. It's used when a new account has beed found by other chanels (miners o nodes...)
  1566. }
  1567. Var i,n,lastn, iUndo : Integer;
  1568. op : TPCOperation;
  1569. errors, auxs : String;
  1570. aux,aux2 : TOperationsHashTree;
  1571. resetNewTarget : Boolean;
  1572. begin
  1573. Lock;
  1574. Try
  1575. FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
  1576. if Assigned(FBank) then begin
  1577. resetNewTarget := False;
  1578. FOperationBlock.protocol_version := FBank.SafeBox.CurrentProtocol;
  1579. If (FOperationBlock.protocol_version=CT_PROTOCOL_1) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_2)) then begin
  1580. TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 2 at sanitize');
  1581. FOperationBlock.protocol_version := CT_PROTOCOL_2;
  1582. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
  1583. TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 3 at sanitize');
  1584. FOperationBlock.protocol_version := CT_PROTOCOL_3;
  1585. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_3) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_4)) then begin
  1586. TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 4 at sanitize');
  1587. FOperationBlock.protocol_version := CT_PROTOCOL_4;
  1588. {$IFDEF ACTIVATE_RANDOMHASH_V4}
  1589. resetNewTarget := True; // RandomHash algo will reset new target on V4
  1590. {$ENDIF}
  1591. end else if (FOperationBlock.protocol_version=CT_PROTOCOL_4) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_5)) then begin
  1592. FOperationBlock.protocol_version := CT_PROTOCOL_5; // If minting... upgrade to Protocol 5
  1593. end;
  1594. FOperationBlock.block := FBank.BlocksCount;
  1595. FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
  1596. if (resetNewTarget) then begin
  1597. FOperationBlock.compact_target := TPascalCoinProtocol.ResetTarget(FOperationBlock.compact_target,FOperationBlock.protocol_version);
  1598. end else begin
  1599. FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
  1600. end;
  1601. FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
  1602. If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then begin
  1603. FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
  1604. end;
  1605. FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
  1606. end else begin
  1607. FOperationBlock.block := 0;
  1608. FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(0);
  1609. FOperationBlock.compact_target := CT_MinCompactTarget_v1;
  1610. FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash;
  1611. FOperationBlock.protocol_version := CT_PROTOCOL_1;
  1612. FOperationBlock.previous_proof_of_work := Nil;
  1613. end;
  1614. FOperationBlock.proof_of_work:=Nil;
  1615. FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
  1616. n := 0;
  1617. FOperationBlock.fee := 0;
  1618. //
  1619. SafeBoxTransaction.CleanTransaction;
  1620. FPreviousUpdatedBlocks.Clear;
  1621. aux := TOperationsHashTree.Create;
  1622. Try
  1623. if (FOperationBlock.protocol_version>=CT_PROTOCOL_4) then begin
  1624. aux.Max0feeOperationsBySigner := 1;
  1625. end else aux.Max0feeOperationsBySigner := -1;
  1626. lastn := FOperationsHashTree.OperationsCount;
  1627. for i:=0 to lastn-1 do begin
  1628. op := FOperationsHashTree.GetOperation(i);
  1629. if OperationBlock.protocol_version <> op.ProtocolVersion then begin
  1630. errors := Format('Sanitize Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
  1631. Tlog.NewLog(lterror,ClassName,errors);
  1632. end else begin
  1633. if (aux.CanAddOperationToHashTree(op)) then begin
  1634. if (op.DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors)) then begin
  1635. if aux.AddOperationToHashTree(op) then begin
  1636. inc(n);
  1637. inc(FOperationBlock.fee,op.OperationFee);
  1638. {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
  1639. end else begin
  1640. TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute at Sanitize due limits reached. Executing %d operations',[aux.OperationsCount]));
  1641. FPreviousUpdatedBlocks.Clear;
  1642. FSafeBoxTransaction.Rollback;
  1643. for iUndo := 0 to aux.OperationsCount-1 do aux.GetOperation(iUndo).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
  1644. end;
  1645. end;
  1646. end;
  1647. end;
  1648. end;
  1649. Finally
  1650. aux2 := FOperationsHashTree;
  1651. FOperationsHashTree := aux;
  1652. aux2.Free;
  1653. FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
  1654. End;
  1655. Finally
  1656. Calc_Digest_Parts; // Does not need to recalc PoW
  1657. Unlock;
  1658. End;
  1659. if (n>0) or (lastn<>n) then begin
  1660. TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
  1661. end;
  1662. end;
  1663. function TPCOperationsComp.SaveBlockToStorage(Stream: TStream): Boolean;
  1664. begin
  1665. Result := SaveBlockToStreamExt(false,Stream,true);
  1666. end;
  1667. function TPCOperationsComp.SaveBlockToStream(save_only_OperationBlock : Boolean; Stream: TStream): Boolean;
  1668. begin
  1669. Result := SaveBlockToStreamExt(save_only_OperationBlock,Stream,false);
  1670. end;
  1671. function TPCOperationsComp.SaveBlockToStreamExt(save_only_OperationBlock: Boolean; Stream: TStream; SaveToStorage: Boolean): Boolean;
  1672. Var soob : Byte;
  1673. begin
  1674. Lock;
  1675. Try
  1676. if save_only_OperationBlock then begin
  1677. {Old versions:
  1678. if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 1
  1679. else soob := 3;}
  1680. soob := 3;
  1681. end else begin
  1682. {Old versions:
  1683. if (FOperationBlock.protocol_version=1) And (FOperationBlock.protocol_available=0) then soob := 0
  1684. else soob := 2;}
  1685. soob := 2;
  1686. if (SaveToStorage) then begin
  1687. {Old versions:
  1688. // Introduced on protocol v2: soob = 4 when saving to storage
  1689. soob := 4;}
  1690. // Introduced on protocol v3: soob = 5 when saving to storage
  1691. soob := 5; // V3 will always save PreviousUpdatedBlocks
  1692. end;
  1693. end;
  1694. Stream.Write(soob,1);
  1695. if (soob>=2) then begin
  1696. Stream.Write(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
  1697. Stream.Write(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
  1698. end;
  1699. //
  1700. Stream.Write(FOperationBlock.block, Sizeof(FOperationBlock.block));
  1701. //
  1702. TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FOperationBlock.account_key));
  1703. Stream.Write(FOperationBlock.reward, Sizeof(FOperationBlock.reward));
  1704. Stream.Write(FOperationBlock.fee, Sizeof(FOperationBlock.fee));
  1705. Stream.Write(FOperationBlock.timestamp, Sizeof(FOperationBlock.timestamp));
  1706. Stream.Write(FOperationBlock.compact_target, Sizeof(FOperationBlock.compact_target));
  1707. Stream.Write(FOperationBlock.nonce, Sizeof(FOperationBlock.nonce));
  1708. TStreamOp.WriteAnsiString(Stream, FOperationBlock.block_payload);
  1709. TStreamOp.WriteAnsiString(Stream, FOperationBlock.initial_safe_box_hash);
  1710. TStreamOp.WriteAnsiString(Stream, FOperationBlock.operations_hash);
  1711. TStreamOp.WriteAnsiString(Stream, FOperationBlock.proof_of_work);
  1712. if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
  1713. TStreamOp.WriteAnsiString(Stream, FOperationBlock.previous_proof_of_work);
  1714. end;
  1715. { Basic size calculation:
  1716. protocols : 2 words = 4 bytes
  1717. block : 4 bytes
  1718. Account_key (VARIABLE LENGTH) at least 2 + 34 + 34 for secp256k1 key = 70 bytes
  1719. reward, fee, timestamp, compact_target, nonce = 8+8+4+4+4 = 28 bytes
  1720. payload (VARIABLE LENGTH) minimum 2 bytes... but usually 40 by average = 40 bytes
  1721. sbh, operations_hash, pow ( 32 + 32 + 32 ) = 96 bytes
  1722. Total, by average: 242 bytes
  1723. }
  1724. if (Not save_only_OperationBlock) then begin
  1725. Result := FOperationsHashTree.SaveOperationsHashTreeToStream(Stream,SaveToStorage);
  1726. If (Result) And (SaveToStorage) And (soob=5) then begin
  1727. FPreviousUpdatedBlocks.SaveToStream(Stream);
  1728. end;
  1729. end else Result := true;
  1730. finally
  1731. Unlock;
  1732. end;
  1733. end;
  1734. class function TPCOperationsComp.SaveOperationBlockToStream(const OperationBlock: TOperationBlock; Stream: TStream): Boolean;
  1735. Var soob : Byte;
  1736. begin
  1737. soob := 3;
  1738. Stream.Write(soob,1);
  1739. Stream.Write(OperationBlock.protocol_version, Sizeof(OperationBlock.protocol_version));
  1740. Stream.Write(OperationBlock.protocol_available, Sizeof(OperationBlock.protocol_available));
  1741. //
  1742. Stream.Write(OperationBlock.block, Sizeof(OperationBlock.block));
  1743. //
  1744. TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(OperationBlock.account_key));
  1745. Stream.Write(OperationBlock.reward, Sizeof(OperationBlock.reward));
  1746. Stream.Write(OperationBlock.fee, Sizeof(OperationBlock.fee));
  1747. Stream.Write(OperationBlock.timestamp, Sizeof(OperationBlock.timestamp));
  1748. Stream.Write(OperationBlock.compact_target, Sizeof(OperationBlock.compact_target));
  1749. Stream.Write(OperationBlock.nonce, Sizeof(OperationBlock.nonce));
  1750. TStreamOp.WriteAnsiString(Stream, OperationBlock.block_payload);
  1751. TStreamOp.WriteAnsiString(Stream, OperationBlock.initial_safe_box_hash);
  1752. TStreamOp.WriteAnsiString(Stream, OperationBlock.operations_hash);
  1753. TStreamOp.WriteAnsiString(Stream, OperationBlock.proof_of_work);
  1754. if OperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
  1755. TStreamOp.WriteAnsiString(Stream, OperationBlock.previous_proof_of_work);
  1756. end;
  1757. Result := true;
  1758. end;
  1759. function TPCOperationsComp.Update_And_RecalcPOW(newNOnce, newTimestamp: Cardinal; newBlockPayload: TRawBytes) : Boolean;
  1760. Var i : Integer;
  1761. _changedPayload : Boolean;
  1762. begin
  1763. Lock;
  1764. Try
  1765. If newBlockPayload<>FOperationBlock.block_payload then begin
  1766. _changedPayload := TPascalCoinProtocol.IsValidMinerBlockPayload(newBlockPayload);
  1767. end else _changedPayload:=False;
  1768. If (_changedPayload) Or (newNOnce<>FOperationBlock.nonce) Or (newTimestamp<>FOperationBlock.timestamp) then begin
  1769. If _changedPayload then FOperationBlock.block_payload:=newBlockPayload;
  1770. FOperationBlock.nonce:=newNOnce;
  1771. FOperationBlock.timestamp:=newTimestamp;
  1772. CalcProofOfWork(_changedPayload,FOperationBlock.proof_of_work);
  1773. Result := True;
  1774. end else Result := False;
  1775. finally
  1776. Unlock;
  1777. end;
  1778. end;
  1779. procedure TPCOperationsComp.SetAccountKey(const value: TAccountKey);
  1780. begin
  1781. Lock;
  1782. Try
  1783. if TAccountComp.AccountKey2RawString(value)=TAccountComp.AccountKey2RawString(FOperationBlock.account_key) then exit;
  1784. FOperationBlock.account_key := value;
  1785. Calc_Digest_Parts;
  1786. finally
  1787. Unlock;
  1788. end;
  1789. end;
  1790. procedure TPCOperationsComp.SetBank(const value: TPCBank);
  1791. begin
  1792. if FBank = value then exit;
  1793. if Assigned(FBank) then begin
  1794. FreeAndNil(FSafeBoxTransaction);
  1795. end;
  1796. FBank := value;
  1797. if Assigned(value) then begin
  1798. FSafeBoxTransaction := TPCSafeBoxTransaction.Create(FBank.SafeBox);
  1799. end;
  1800. Clear(true);
  1801. end;
  1802. procedure TPCOperationsComp.SetBlockPayload(const Value: TRawBytes);
  1803. begin
  1804. Update_And_RecalcPOW(FOperationBlock.nonce,FOperationBlock.timestamp,Value);
  1805. end;
  1806. procedure TPCOperationsComp.OnOperationsHashTreeChanged(Sender: TObject);
  1807. begin
  1808. FOperationBlock.operations_hash:=FOperationsHashTree.HashTree;
  1809. Calc_Digest_Part3;
  1810. end;
  1811. procedure TPCOperationsComp.SetnOnce(const value: Cardinal);
  1812. begin
  1813. Update_And_RecalcPOW(value,FOperationBlock.timestamp,FOperationBlock.block_payload);
  1814. end;
  1815. procedure TPCOperationsComp.Settimestamp(const value: Cardinal);
  1816. begin
  1817. Update_And_RecalcPOW(FOperationBlock.nonce,value,FOperationBlock.block_payload);
  1818. end;
  1819. procedure TPCOperationsComp.UpdateTimestamp;
  1820. Var ts : Cardinal;
  1821. begin
  1822. Lock;
  1823. Try
  1824. ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
  1825. if Assigned(FBank) then begin
  1826. If FBank.FLastOperationBlock.timestamp>ts then ts := FBank.FLastOperationBlock.timestamp;
  1827. end;
  1828. timestamp := ts;
  1829. finally
  1830. Unlock;
  1831. end;
  1832. end;
  1833. function TPCOperationsComp.GetMinerRewardPseudoOperation : TOperationResume;
  1834. begin
  1835. Result := CT_TOperationResume_NUL;
  1836. Result.valid := true;
  1837. Result.Block := FOperationBlock.block;
  1838. Result.time := self.OperationBlock.timestamp;
  1839. Result.AffectedAccount := FOperationBlock.block * CT_AccountsPerBlock;
  1840. Result.Amount := self.OperationBlock.reward;
  1841. Result.Fee := self.OperationBlock.fee;
  1842. Result.Balance := Result.Amount+Result.Fee;
  1843. Result.OperationTxt := 'Miner reward';
  1844. end;
  1845. function TPCOperationsComp.ValidateOperationBlock(var errors : String): Boolean;
  1846. Var i : Integer;
  1847. begin
  1848. errors := '';
  1849. Result := False;
  1850. Lock;
  1851. Try
  1852. If Not Assigned(SafeBoxTransaction) then begin
  1853. errors := 'ERROR DEV 20170523-1';
  1854. exit;
  1855. end;
  1856. If Not Assigned(SafeBoxTransaction.FreezedSafeBox) then begin
  1857. errors := 'ERROR DEV 20170523-2';
  1858. exit;
  1859. end;
  1860. // Check OperationBlock info:
  1861. If not SafeBoxTransaction.FreezedSafeBox.IsValidNewOperationsBlock(OperationBlock,True,Not HasValidOperationBlockInfo, errors) then exit;
  1862. // Execute SafeBoxTransaction operations:
  1863. SafeBoxTransaction.Rollback;
  1864. FPreviousUpdatedBlocks.Clear;
  1865. //
  1866. TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree,Nil);
  1867. //
  1868. for i := 0 to Count - 1 do begin
  1869. if (Operation[i].ProtocolVersion<>OperationBlock.protocol_version) then begin
  1870. errors := 'Error executing operation invalid protocol at '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
  1871. exit;
  1872. end;
  1873. If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
  1874. errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
  1875. exit;
  1876. end;
  1877. end;
  1878. // Check OperationsHash value is valid
  1879. // New Build 2.1.7 use safe BinStrComp
  1880. if TBaseType.BinStrComp(FOperationsHashTree.HashTree,OperationBlock.operations_hash)<>0 then begin
  1881. errors := 'Invalid Operations Hash '+TCrypto.ToHexaString(OperationBlock.operations_hash)+'<>'+TCrypto.ToHexaString(FOperationsHashTree.HashTree);
  1882. exit;
  1883. end;
  1884. // Check OperationBlock with SafeBox info:
  1885. if (SafeBoxTransaction.FreezedSafeBox.TotalBalance<>(SafeBoxTransaction.TotalBalance+SafeBoxTransaction.TotalFee)) then begin
  1886. errors := Format('Invalid integrity balance at SafeBox. Actual Balance:%d New Balance:(%d + fee %d = %d)',
  1887. [SafeBoxTransaction.FreezedSafeBox.TotalBalance,
  1888. SafeBoxTransaction.TotalBalance,
  1889. SafeBoxTransaction.TotalFee,
  1890. SafeBoxTransaction.TotalBalance+SafeBoxTransaction.TotalFee]);
  1891. exit;
  1892. end;
  1893. // Check fee value
  1894. if (SafeBoxTransaction.TotalFee<>OperationBlock.fee) then begin
  1895. errors := Format('Invalid fee integrity at SafeBoxTransaction. New Balance:(%d + fee %d = %d) OperationBlock.fee:%d',
  1896. [
  1897. SafeBoxTransaction.TotalBalance,
  1898. SafeBoxTransaction.TotalFee,
  1899. SafeBoxTransaction.TotalBalance+SafeBoxTransaction.TotalFee,
  1900. OperationBlock.fee]);
  1901. exit;
  1902. end;
  1903. Result := true;
  1904. finally
  1905. Unlock;
  1906. end;
  1907. end;
  1908. procedure TPCOperationsComp.Lock;
  1909. begin
  1910. FOperationsLock.Acquire;
  1911. end;
  1912. procedure TPCOperationsComp.Unlock;
  1913. begin
  1914. FOperationsLock.Release;
  1915. end;
  1916. { TPCBankNotify }
  1917. constructor TPCBankNotify.Create(AOwner: TComponent);
  1918. begin
  1919. inherited;
  1920. FBank := Nil;
  1921. end;
  1922. destructor TPCBankNotify.Destroy;
  1923. begin
  1924. Bank := Nil;
  1925. inherited;
  1926. end;
  1927. procedure TPCBankNotify.Notification(AComponent: TComponent;
  1928. Operation: TOperation);
  1929. begin
  1930. inherited;
  1931. if (operation=opremove) then if AComponent=FBank then FBank:=nil;
  1932. end;
  1933. procedure TPCBankNotify.NotifyNewBlock;
  1934. begin
  1935. if Assigned(FOnNewBlock) Then FOnNewBlock(Bank);
  1936. end;
  1937. procedure TPCBankNotify.SetBank(const Value: TPCBank);
  1938. begin
  1939. if Assigned(FBank) then begin
  1940. FBank.FNotifyList.Remove(Self);
  1941. FBank.RemoveFreeNotification(Self);
  1942. end;
  1943. FBank := Value;
  1944. if Assigned(FBank) then begin
  1945. FBank.FreeNotification(Self);
  1946. FBank.FNotifyList.Add(Self);
  1947. end;
  1948. end;
  1949. { TOperationsHashTree }
  1950. Type TOperationHashTreeReg = Record
  1951. Op : TPCOperation;
  1952. end;
  1953. POperationHashTreeReg = ^TOperationHashTreeReg;
  1954. TOperationsHashAccountsData = Record
  1955. account_number : Cardinal;
  1956. account_count : Integer;
  1957. account_without_fee : Integer;
  1958. end;
  1959. POperationsHashAccountsData = ^TOperationsHashAccountsData;
  1960. function TOperationsHashTree.AddOperationToHashTree(op: TPCOperation) : Boolean;
  1961. Var l : TList<Pointer>;
  1962. begin
  1963. l := FHashTreeOperations.LockList;
  1964. try
  1965. Result := InternalAddOperationToHashTree(l,op,True);
  1966. finally
  1967. FHashTreeOperations.UnlockList;
  1968. end;
  1969. end;
  1970. function TOperationsHashTree.CanAddOperationToHashTree(op: TPCOperation): Boolean;
  1971. Var lockedList : TList<Pointer>;
  1972. begin
  1973. lockedList := FHashTreeOperations.LockList;
  1974. Try
  1975. Result := InternalCanAddOperationToHashTree(lockedList,op);
  1976. Finally
  1977. FHashTreeOperations.UnlockList;
  1978. End;
  1979. end;
  1980. procedure TOperationsHashTree.ClearHastThree;
  1981. var l : TList<Pointer>;
  1982. i : Integer;
  1983. P : POperationHashTreeReg;
  1984. PaccData : POperationsHashAccountsData;
  1985. begin
  1986. l := FHashTreeOperations.LockList;
  1987. try
  1988. FTotalAmount := 0;
  1989. FTotalFee := 0;
  1990. Try
  1991. for i := 0 to l.Count - 1 do begin
  1992. P := l[i];
  1993. _PCOperationsStorage.RemovePCOperation(P^.Op);
  1994. Dispose(P);
  1995. end;
  1996. for i:=0 to FListOrderedByAccountsData.Count-1 do begin
  1997. PaccData := FListOrderedByAccountsData[i];
  1998. Dispose(PaccData);
  1999. end;
  2000. Finally
  2001. l.Clear;
  2002. FListOrderedBySha256.Clear;
  2003. FListOrderedByAccountsData.Clear;
  2004. FListOrderedByOpReference.Clear;
  2005. FHashTree:=Nil;
  2006. End;
  2007. If Assigned(FOnChanged) then FOnChanged(Self);
  2008. finally
  2009. FHashTreeOperations.UnlockList;
  2010. end;
  2011. end;
  2012. procedure TOperationsHashTree.CopyFromHashTree(Sender: TOperationsHashTree);
  2013. Var i : Integer;
  2014. lme, lsender : TList<Pointer>;
  2015. PSender : POperationHashTreeReg;
  2016. lastNE : TNotifyEvent;
  2017. begin
  2018. if (Sender = Self) then begin
  2019. exit;
  2020. end;
  2021. lme := FHashTreeOperations.LockList;
  2022. try
  2023. lastNE := FOnChanged;
  2024. FOnChanged := Nil;
  2025. try
  2026. ClearHastThree;
  2027. FMax0feeOperationsBySigner := Sender.Max0feeOperationsBySigner;
  2028. lsender := Sender.FHashTreeOperations.LockList;
  2029. try
  2030. for i := 0 to lsender.Count - 1 do begin
  2031. PSender := lsender[i];
  2032. InternalAddOperationToHashTree(lme,PSender^.Op,False);
  2033. end;
  2034. // Improvement TOperationsHashTree speed 2.1.6
  2035. // FHashTree value updated now, not on every for cycle
  2036. FHashTree:=Sender.FHashTree;
  2037. finally
  2038. Sender.FHashTreeOperations.UnlockList;
  2039. end;
  2040. finally
  2041. FOnChanged := lastNE;
  2042. end;
  2043. If Assigned(FOnChanged) then FOnChanged(Self);
  2044. finally
  2045. FHashTreeOperations.UnlockList;
  2046. end;
  2047. end;
  2048. constructor TOperationsHashTree.Create;
  2049. begin
  2050. FOnChanged:=Nil;
  2051. FListOrderedBySha256 := TList<Integer>.Create;
  2052. FListOrderedByAccountsData := TList<Pointer>.Create;
  2053. FListOrderedByOpReference := TList<Integer>.Create;
  2054. FTotalAmount := 0;
  2055. FTotalFee := 0;
  2056. FHashTree := Nil;
  2057. FMax0feeOperationsBySigner := -1; // Unlimited by default
  2058. FHashTreeOperations := TPCThreadList<Pointer>.Create('TOperationsHashTree_HashTreeOperations');
  2059. end;
  2060. procedure TOperationsHashTree.Delete(index: Integer);
  2061. Var l : TList<Pointer>;
  2062. P : POperationHashTreeReg;
  2063. i,iDel,iValuePosDeleted : Integer;
  2064. PaccData : POperationsHashAccountsData;
  2065. begin
  2066. l := FHashTreeOperations.LockList;
  2067. try
  2068. P := l[index];
  2069. // Delete from Ordered by OpReference
  2070. if Not FindOrderedByOpReference(l,P^.Op.GetOpReference,iDel) then begin
  2071. TLog.NewLog(ltError,ClassName,'DEV ERROR 20180927-1 Operation not found in ordered by reference list: '+P^.Op.ToString);
  2072. end else begin
  2073. iValuePosDeleted := PtrInt(FListOrderedByOpReference[iDel]);
  2074. if (iValuePosDeleted<>index) then begin
  2075. if (POperationHashTreeReg(l[iValuePosDeleted])^.Op.GetOpReference <> P^.Op.GetOpReference) then
  2076. TLog.NewLog(lterror,ClassName,Format('DEV ERROR 20180928-2 [%d]=%d <> %d',[iDel,iValuePosDeleted,index]));
  2077. end;
  2078. FListOrderedByOpReference.Delete(iDel);
  2079. end;
  2080. // Decrease FListOrderedByOpReference values > index
  2081. for i := 0 to FListOrderedByOpReference.Count - 1 do begin
  2082. if PtrInt(FListOrderedByOpReference[i])>index then begin
  2083. FListOrderedByOpReference[i] := ( (FListOrderedByOpReference[i]) - 1 );
  2084. end;
  2085. end;
  2086. // Delete from Ordered
  2087. If Not FindOrderedBySha(l,P^.Op.Sha256,iDel) then begin
  2088. TLog.NewLog(ltError,ClassName,'DEV ERROR 20180213-1 Operation not found in ordered list: '+P^.Op.ToString);
  2089. end else begin
  2090. iValuePosDeleted := PtrInt(FListOrderedBySha256[iDel]);
  2091. if (iValuePosDeleted<>index) then
  2092. TLog.NewLog(lterror,ClassName,Format('DEV ERROR 20180928-3 [%d]=%d <> %d',[iDel,iValuePosDeleted,index]));
  2093. FListOrderedBySha256.Delete(iDel);
  2094. end;
  2095. // Decrease FListOrderedBySha256 values > index
  2096. for i := 0 to FListOrderedBySha256.Count - 1 do begin
  2097. if PtrInt(FListOrderedBySha256[i])>index then begin
  2098. FListOrderedBySha256[i] := ( (FListOrderedBySha256[i]) - 1 );
  2099. end;
  2100. end;
  2101. // Delete from account Data
  2102. If Not FindOrderedByAccountData(l,P^.Op.SignerAccount,i) then begin
  2103. TLog.NewLog(ltError,ClassName,Format('DEV ERROR 20180213-3 account %d not found in ordered list: %s',[P^.Op.SignerAccount,P^.Op.ToString]));
  2104. end else begin
  2105. PaccData := POperationsHashAccountsData( FListOrderedByAccountsData[i] );
  2106. Dec(PaccData.account_count);
  2107. If (P^.Op.OperationFee=0) then Dec(PaccData.account_without_fee);
  2108. If (PaccData.account_count<=0) then begin
  2109. Dispose(PaccData);
  2110. FListOrderedByAccountsData.Delete(i);
  2111. end;
  2112. end;
  2113. l.Delete(index);
  2114. _PCOperationsStorage.RemovePCOperation(P^.Op);
  2115. Dispose(P);
  2116. // Recalc operations hash
  2117. FTotalAmount := 0;
  2118. FTotalFee := 0;
  2119. FHashTree := Nil; // Init to future recalc
  2120. for i := 0 to l.Count - 1 do begin
  2121. P := l[i];
  2122. // Include to hash tree
  2123. inc(FTotalAmount,P^.Op.OperationAmount);
  2124. inc(FTotalFee,P^.Op.OperationFee);
  2125. end;
  2126. If Assigned(FOnChanged) then FOnChanged(Self);
  2127. finally
  2128. FHashTreeOperations.UnlockList;
  2129. end;
  2130. end;
  2131. destructor TOperationsHashTree.Destroy;
  2132. begin
  2133. FOnChanged := Nil;
  2134. ClearHastThree;
  2135. FreeAndNil(FHashTreeOperations);
  2136. FHashTree := Nil;
  2137. FreeAndNil(FListOrderedBySha256);
  2138. FreeAndNil(FListOrderedByAccountsData);
  2139. FreeAndNil(FListOrderedByOpReference);
  2140. inherited;
  2141. end;
  2142. function TOperationsHashTree.GetHashTree: TRawBytes;
  2143. Var l : TList<Pointer>;
  2144. i : Integer;
  2145. P : POperationHashTreeReg;
  2146. tmpRaw : TRawBytes;
  2147. begin
  2148. if Length(FHashTree)<>32 then begin
  2149. l := FHashTreeOperations.LockList;
  2150. Try
  2151. TCrypto.DoSha256(Nil,FHashTree);
  2152. for i := 0 to l.Count - 1 do begin
  2153. P := l[i];
  2154. // Include to hash tree
  2155. // TCrypto.DoSha256(FHashTree+P^.Op.Sha256,FHashTree); COMPILER BUG 2.1.6: Using FHashTree as a "out" param can be initialized prior to be updated first parameter!
  2156. TBaseType.Concat(FHashTree,P^.Op.Sha256,tmpRaw);
  2157. FHashTree := TCrypto.DoSha256(tmpRaw);
  2158. end;
  2159. Finally
  2160. FHashTreeOperations.UnlockList;
  2161. End;
  2162. end;
  2163. Result := FHashTree;
  2164. end;
  2165. function TOperationsHashTree.GetOperation(index: Integer): TPCOperation;
  2166. Var l : TList<Pointer>;
  2167. begin
  2168. l := FHashTreeOperations.LockList;
  2169. try
  2170. Result := POperationHashTreeReg(l[index])^.Op;
  2171. finally
  2172. FHashTreeOperations.UnlockList;
  2173. end;
  2174. end;
  2175. function TOperationsHashTree.GetOperationsAffectingAccount(account_number: Cardinal; List: TList<Cardinal>): Integer;
  2176. // This function retrieves operations from HashTree that affeccts to an account_number
  2177. Var l : TList<Pointer>;
  2178. intl : TList<Cardinal>;
  2179. i,j : Integer;
  2180. begin
  2181. List.Clear;
  2182. l := FHashTreeOperations.LockList;
  2183. try
  2184. intl := TList<Cardinal>.Create;
  2185. try
  2186. for i := 0 to l.Count - 1 do begin
  2187. intl.Clear;
  2188. POperationHashTreeReg(l[i])^.Op.AffectedAccounts(intl);
  2189. if intl.IndexOf(account_number)>=0 then List.Add(Cardinal(i));
  2190. end;
  2191. finally
  2192. intl.Free;
  2193. end;
  2194. Result := List.Count;
  2195. finally
  2196. FHashTreeOperations.UnlockList;
  2197. end;
  2198. end;
  2199. function TOperationsHashTree.GetOperationsList(AList: TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
  2200. Var LList : TList<Pointer>;
  2201. i : Integer;
  2202. LOp : TPCOperation;
  2203. begin
  2204. Result := 0;
  2205. LList := FHashTreeOperations.LockList;
  2206. try
  2207. for i := 0 to LList.Count-1 do begin
  2208. LOp := POperationHashTreeReg(LList[i])^.Op;
  2209. if (Not AAddOnlyOperationsWithoutNotVerifiedSignature) or
  2210. (AAddOnlyOperationsWithoutNotVerifiedSignature and (not LOp.HasValidSignature)) then begin
  2211. AList.Add( LOp );
  2212. inc(Result);
  2213. end;
  2214. end;
  2215. finally
  2216. FHashTreeOperations.UnlockList;
  2217. end;
  2218. end;
  2219. function TOperationsHashTree.IndexOfOperation(op: TPCOperation): Integer;
  2220. Var iPosInOrdered : Integer;
  2221. l : TList<Pointer>;
  2222. OpSha256 : TRawBytes;
  2223. begin
  2224. OpSha256 := op.Sha256;
  2225. l := FHashTreeOperations.LockList;
  2226. Try
  2227. // Improvement TOperationsHashTree speed 2.1.5.1
  2228. // Use ordered search
  2229. If FindOrderedBySha(l,OpSha256,iPosInOrdered) then begin
  2230. Result := PtrInt(FListOrderedBySha256.Items[iPosInOrdered]);
  2231. end else Result := -1;
  2232. Finally
  2233. FHashTreeOperations.UnlockList;
  2234. End;
  2235. end;
  2236. function TOperationsHashTree.IndexOfOpReference(const opReference: TOpReference): Integer;
  2237. Var l : TList<Pointer>;
  2238. begin
  2239. l := FHashTreeOperations.LockList;
  2240. Try
  2241. if not FindOrderedByOpReference(l,opReference,Result) then Result := -1
  2242. else Result := PtrInt(FListOrderedByOpReference.Items[Result]);
  2243. Finally
  2244. FHashTreeOperations.UnlockList;
  2245. End;
  2246. end;
  2247. function TOperationsHashTree.CountOperationsBySameSignerWithoutFee(account_number: Cardinal): Integer;
  2248. Var l : TList<Pointer>;
  2249. i : Integer;
  2250. begin
  2251. Result := 0;
  2252. l := FHashTreeOperations.LockList;
  2253. Try
  2254. // Improvement TOperationsHashTree speed 2.1.5.1
  2255. // Use ordered accounts Data search
  2256. If FindOrderedByAccountData(l,account_number,i) then begin
  2257. Result := POperationsHashAccountsData(FListOrderedByAccountsData[i])^.account_without_fee;
  2258. end else Result := 0;
  2259. Finally
  2260. FHashTreeOperations.UnlockList;
  2261. End;
  2262. end;
  2263. function TOperationsHashTree.InternalAddOperationToHashTree(list: TList<Pointer>; op: TPCOperation; CalcNewHashTree : Boolean) : Boolean;
  2264. Var msCopy : TMemoryStream;
  2265. hForNewHash : TRawBytes;
  2266. P : POperationHashTreeReg;
  2267. PaccData : POperationsHashAccountsData;
  2268. i,npos,iListSigners : Integer;
  2269. listSigners : TList<Cardinal>;
  2270. begin
  2271. if Not InternalCanAddOperationToHashTree(list,op) then begin
  2272. Result := False;
  2273. Exit;
  2274. end else Result := True; // Will add:
  2275. New(P);
  2276. if Not _PCOperationsStorage.FindPCOperationAndIncCounterIfFound(op) then begin
  2277. msCopy := TMemoryStream.Create;
  2278. try
  2279. P^.Op := TPCOperation( op.NewInstance );
  2280. P^.Op.InitializeData(op.ProtocolVersion);
  2281. op.SaveOpToStream(msCopy,true);
  2282. msCopy.Position := 0;
  2283. P^.Op.LoadOpFromStream(msCopy, true);
  2284. P^.Op.FHasValidSignature := op.FHasValidSignature; // Improvement speed v4.0.2 reusing previously signed value
  2285. P^.Op.FUsedPubkeyForSignature := op.FUsedPubkeyForSignature;
  2286. P^.Op.FBufferedSha256:=op.FBufferedSha256;
  2287. P^.Op.CopyUsedPubkeySignatureFrom(op);
  2288. _PCOperationsStorage.AddPCOperation(P^.Op);
  2289. finally
  2290. msCopy.Free;
  2291. end;
  2292. end else P^.Op := op; // Use same!
  2293. // Improvement TOperationsHashTree speed 2.1.6
  2294. // Include to hash tree (Only if CalcNewHashTree=True)
  2295. If (CalcNewHashTree) And (Length(FHashTree)=32) then begin
  2296. // TCrypto.DoSha256(FHashTree+op.Sha256,FHashTree); COMPILER BUG 2.1.6: Using FHashTree as a "out" param can be initialized prior to be updated first parameter!
  2297. TBaseType.Concat(FHashTree,op.Sha256,hForNewHash);
  2298. TCrypto.DoSha256(hForNewHash,FHashTree);
  2299. end;
  2300. npos := list.Add(P);
  2301. //
  2302. if Not FindOrderedByOpReference(list,op.GetOpReference,i) then begin
  2303. FListOrderedByOpReference.Insert(i, npos);
  2304. end; // TODO: Do not allow duplicate OpReferences?
  2305. // Improvement: Will allow to add duplicate Operations, so add only first to orderedBySha
  2306. If Not FindOrderedBySha(list,op.Sha256,i) then begin
  2307. // Protection: Will add only once
  2308. FListOrderedBySha256.Insert(i, npos);
  2309. end;
  2310. // Improvement TOperationsHashTree speed 2.1.6
  2311. // Mantain an ordered Accounts list with data
  2312. listSigners := TList<Cardinal>.Create;
  2313. try
  2314. op.SignerAccounts(listSigners);
  2315. for iListSigners:=0 to listSigners.Count-1 do begin
  2316. If Not FindOrderedByAccountData(list,listSigners[iListSigners],i) then begin
  2317. New(PaccData);
  2318. PaccData^.account_number:=listSigners[iListSigners];
  2319. PaccData^.account_count:=0;
  2320. PaccData^.account_without_fee:=0;
  2321. FListOrderedByAccountsData.Insert(i,PaccData);
  2322. end else PaccData := FListOrderedByAccountsData[i];
  2323. Inc(PaccData^.account_count);
  2324. If op.OperationFee=0 then begin
  2325. Inc(PaccData^.account_without_fee);
  2326. end;
  2327. end;
  2328. finally
  2329. listSigners.Free;
  2330. end;
  2331. inc(FTotalAmount,op.OperationAmount);
  2332. inc(FTotalFee,op.OperationFee);
  2333. If Assigned(FOnChanged) then FOnChanged(Self);
  2334. end;
  2335. function TOperationsHashTree.InternalCanAddOperationToHashTree(lockedThreadList : TList<Pointer>; op: TPCOperation): Boolean;
  2336. Var PaccData : POperationsHashAccountsData;
  2337. iListSigners,iFound : Integer;
  2338. listSigners : TList<Cardinal>;
  2339. begin
  2340. Result := False;
  2341. // Protections:
  2342. // Protect 0-fee operations
  2343. if (op.OperationFee=0) And (FMax0feeOperationsBySigner>=0) then begin
  2344. if (FMax0feeOperationsBySigner=0) then Exit // Not allowed 0-fee operations!
  2345. else if (FMax0feeOperationsBySigner>0) then begin
  2346. listSigners := TList<Cardinal>.Create;
  2347. try
  2348. op.SignerAccounts(listSigners);
  2349. for iListSigners:=0 to listSigners.Count-1 do begin
  2350. If FindOrderedByAccountData(lockedThreadList,(listSigners[iListSigners]),iFound) then begin
  2351. PaccData := FListOrderedByAccountsData[iFound];
  2352. if (PaccData^.account_without_fee>=FMax0feeOperationsBySigner) then Exit; // Limit 0-fee reached
  2353. end;
  2354. end;
  2355. finally
  2356. listSigners.Free;
  2357. end;
  2358. end;
  2359. end;
  2360. Result := True;
  2361. end;
  2362. function TOperationsHashTree.FindOrderedBySha(lockedThreadList : TList<Pointer>; const Value: TRawBytes; var Index: Integer): Boolean;
  2363. var L, H, I : Integer;
  2364. iLockedThreadListPos : PtrInt;
  2365. C : Int64;
  2366. P : POperationHashTreeReg;
  2367. begin
  2368. Result := False;
  2369. L := 0;
  2370. H := FListOrderedBySha256.Count - 1;
  2371. while L <= H do
  2372. begin
  2373. I := (L + H) shr 1;
  2374. iLockedThreadListPos := PtrInt(FListOrderedBySha256[I]);
  2375. C := TBaseType.BinStrComp(POperationHashTreeReg(lockedThreadList[iLockedThreadListPos])^.Op.Sha256,Value);
  2376. if C < 0 then L := I + 1 else
  2377. begin
  2378. H := I - 1;
  2379. if C = 0 then
  2380. begin
  2381. Result := True;
  2382. L := I;
  2383. end;
  2384. end;
  2385. end;
  2386. Index := L;
  2387. end;
  2388. function TOperationsHashTree.FindOrderedByAccountData(lockedThreadList: TList<Pointer>; const account_number: Cardinal; var Index: Integer): Boolean;
  2389. var L, H, I : Integer;
  2390. C : Int64;
  2391. begin
  2392. Result := False;
  2393. L := 0;
  2394. H := FListOrderedByAccountsData.Count - 1;
  2395. while L <= H do
  2396. begin
  2397. I := (L + H) shr 1;
  2398. C := Int64(POperationsHashAccountsData(FListOrderedByAccountsData[I])^.account_number) - Int64(account_number);
  2399. if C < 0 then L := I + 1 else
  2400. begin
  2401. H := I - 1;
  2402. if C = 0 then
  2403. begin
  2404. Result := True;
  2405. L := I;
  2406. end;
  2407. end;
  2408. end;
  2409. Index := L;
  2410. end;
  2411. function TOperationsHashTree.FindOrderedByOpReference(lockedThreadList: TList<Pointer>; const Value: TOpReference; var Index: Integer): Boolean;
  2412. var L, H, I : Integer;
  2413. iLockedThreadListPos : PtrInt;
  2414. C : Int64;
  2415. P : POperationHashTreeReg;
  2416. begin
  2417. Result := False;
  2418. L := 0;
  2419. H := FListOrderedByOpReference.Count - 1;
  2420. while L <= H do
  2421. begin
  2422. I := (L + H) shr 1;
  2423. iLockedThreadListPos := PtrInt(FListOrderedByOpReference[I]);
  2424. C := Int64(POperationHashTreeReg(lockedThreadList[iLockedThreadListPos])^.Op.GetOpReference) - Int64(Value);
  2425. if C < 0 then L := I + 1 else
  2426. begin
  2427. H := I - 1;
  2428. if C = 0 then
  2429. begin
  2430. Result := True;
  2431. L := I;
  2432. end;
  2433. end;
  2434. end;
  2435. Index := L;
  2436. end;
  2437. function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
  2438. Var c, i: Cardinal;
  2439. OpType: Cardinal;
  2440. bcop: TPCOperation;
  2441. j: Integer;
  2442. OpClass: TPCOperationClass;
  2443. lastNE : TNotifyEvent;
  2444. begin
  2445. Result := false;
  2446. //
  2447. If Stream.Read(c, 4)<4 then begin
  2448. errors := 'Cannot read operations count';
  2449. exit;
  2450. end;
  2451. lastNE := FOnChanged;
  2452. FOnChanged:=Nil;
  2453. try
  2454. // c = operations count
  2455. for i := 1 to c do begin
  2456. if Stream.Size - Stream.Position < 4 then begin
  2457. errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
  2458. exit;
  2459. end;
  2460. Stream.Read(OpType, 4);
  2461. j := TPCOperationsComp.IndexOfOperationClassByOpType(OpType);
  2462. if j >= 0 then
  2463. OpClass := _OperationsClass[j]
  2464. else
  2465. OpClass := Nil;
  2466. if Not Assigned(OpClass) then begin
  2467. errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(OpType, 4);
  2468. exit;
  2469. end;
  2470. bcop := OpClass.Create(ASetOperationsToProtocolVersion);
  2471. Try
  2472. if LoadingFromStorage then begin
  2473. If not bcop.LoadFromStorage(Stream,ALoadVersion,PreviousUpdatedBlocks) then begin
  2474. errors := 'Invalid operation load from storage ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
  2475. exit;
  2476. end;
  2477. end else if not bcop.LoadFromNettransfer(Stream) then begin
  2478. errors := 'Invalid operation load from stream ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
  2479. exit;
  2480. end;
  2481. AddOperationToHashTree(bcop);
  2482. Finally
  2483. FreeAndNil(bcop);
  2484. end;
  2485. end;
  2486. finally
  2487. FOnChanged := lastNE;
  2488. end;
  2489. If Assigned(FOnChanged) then FOnChanged(Self);
  2490. errors := '';
  2491. Result := true;
  2492. end;
  2493. procedure TOperationsHashTree.MarkVerifiedECDSASignatures(operationsHashTreeToMark: TOperationsHashTree);
  2494. var i, iPosInMyList, nMarkedAsGood, nAlreadyMarked : Integer;
  2495. opToMark, opInMyList : TPCOperation;
  2496. myList, listToMark : TList<Pointer>;
  2497. begin
  2498. // Introduced on Build 4.0.2 to increase speed
  2499. // Will search each "operationsHashTreeToMark" operation on my current list. If found, will set same FHasValidSignature in order to mark operation in "operationsHashTreeToMark" as verified
  2500. If Self=operationsHashTreeToMark then Exit;
  2501. nMarkedAsGood := 0;
  2502. nAlreadyMarked := 0;
  2503. myList := FHashTreeOperations.LockList;
  2504. try
  2505. if myList.Count<=0 then Exit; // Nothing to search...
  2506. listToMark := operationsHashTreeToMark.FHashTreeOperations.LockList;
  2507. Try
  2508. if listToMark.Count<=0 then Exit; // Nothing to search...
  2509. for i:=0 to listToMark.Count-1 do begin
  2510. opToMark := POperationHashTreeReg(listToMark[i])^.Op;
  2511. if Not opToMark.FHasValidSignature then begin
  2512. // Check if found
  2513. iPosInMyList := Self.IndexOfOperation(opToMark);
  2514. if (iPosInMyList>=0) then begin
  2515. opInMyList := POperationHashTreeReg(myList[iPosInMyList])^.Op;
  2516. if (opInMyList.FHasValidSignature) then begin
  2517. if (opToMark.FHasValidSignature) then inc(nAlreadyMarked)
  2518. else begin
  2519. opToMark.FHasValidSignature:=True;
  2520. opToMark.FUsedPubkeyForSignature:=opInMyList.FUsedPubkeyForSignature;
  2521. opToMark.CopyUsedPubkeySignatureFrom(opInMyList);
  2522. inc(nMarkedAsGood);
  2523. end;
  2524. end;
  2525. end;
  2526. end;
  2527. end;
  2528. TLog.NewLog(ltdebug,ClassName,Format('Marked %d/%d operations as ValidSignature (%d before) from MemPool with %d operations',[nMarkedAsGood,listToMark.Count,nAlreadyMarked,myList.Count]));
  2529. finally
  2530. operationsHashTreeToMark.FHashTreeOperations.UnlockList;
  2531. end;
  2532. finally
  2533. FHashTreeOperations.UnlockList;
  2534. end;
  2535. end;
  2536. function TOperationsHashTree.OperationsCount: Integer;
  2537. Var l : TList<Pointer>;
  2538. begin
  2539. l := FHashTreeOperations.LockList;
  2540. try
  2541. Result := l.Count;
  2542. finally
  2543. FHashTreeOperations.UnlockList;
  2544. end;
  2545. end;
  2546. procedure TOperationsHashTree.RemoveByOpReference(const opReference: TOpReference);
  2547. var i : Integer;
  2548. l : TList<Pointer>;
  2549. iLockedThreadListPos : PtrInt;
  2550. begin
  2551. l := FHashTreeOperations.LockList;
  2552. Try
  2553. if FindOrderedByOpReference(l,opReference,i) then begin
  2554. iLockedThreadListPos := PtrInt(FListOrderedByOpReference[i]);
  2555. Delete(iLockedThreadListPos);
  2556. end;
  2557. Finally
  2558. FHashTreeOperations.UnlockList;
  2559. End;
  2560. end;
  2561. function TOperationsHashTree.SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage: Boolean): Boolean;
  2562. Var c, i, OpType: Cardinal;
  2563. bcop: TPCOperation;
  2564. l : TList<Pointer>;
  2565. begin
  2566. l := FHashTreeOperations.LockList;
  2567. Try
  2568. c := l.Count;
  2569. Stream.Write(c, 4);
  2570. // c = operations count
  2571. for i := 1 to c do begin
  2572. bcop := GetOperation(i - 1);
  2573. OpType := bcop.OpType;
  2574. Stream.write(OpType, 4);
  2575. if SaveToStorage then bcop.SaveToStorage(Stream)
  2576. else bcop.SaveToNettransfer(Stream);
  2577. end;
  2578. Result := true;
  2579. Finally
  2580. FHashTreeOperations.UnlockList;
  2581. End;
  2582. end;
  2583. procedure TOperationsHashTree.SetMax0feeOperationsBySigner(const Value: Integer);
  2584. var nv : Integer;
  2585. begin
  2586. if Value<0 then nv:=-1
  2587. else nv := Value;
  2588. if nv=FMax0feeOperationsBySigner then Exit;
  2589. FMax0feeOperationsBySigner := nv;
  2590. ClearHastThree;
  2591. end;
  2592. { TStorage }
  2593. function TStorage.BlockExists(Block: Cardinal): Boolean;
  2594. begin
  2595. Result := DoBlockExists(Block);
  2596. end;
  2597. procedure TStorage.CopyConfiguration(const CopyFrom: TStorage);
  2598. begin
  2599. Orphan := CopyFrom.Orphan;
  2600. end;
  2601. constructor TStorage.Create(AOwner: TComponent);
  2602. begin
  2603. inherited;
  2604. FOrphan := '';
  2605. FReadOnly := false;
  2606. FIsMovingBlockchain := False;
  2607. end;
  2608. procedure TStorage.DeleteBlockChainBlocks(StartingDeleteBlock: Cardinal);
  2609. begin
  2610. if ReadOnly then raise Exception.Create('Cannot delete blocks because is ReadOnly');
  2611. DoDeleteBlockChainBlocks(StartingDeleteBlock);
  2612. end;
  2613. function TStorage.Initialize: Boolean;
  2614. begin
  2615. Result := DoInitialize;
  2616. end;
  2617. function TStorage.CreateSafeBoxStream(blockCount: Cardinal): TStream;
  2618. begin
  2619. Result := DoCreateSafeBoxStream(blockCount);
  2620. end;
  2621. procedure TStorage.EraseStorage;
  2622. begin
  2623. TLog.NewLog(ltInfo,ClassName,'Executing EraseStorage');
  2624. DoEraseStorage;
  2625. end;
  2626. procedure TStorage.SavePendingBufferOperations(OperationsHashTree : TOperationsHashTree);
  2627. begin
  2628. DoSavePendingBufferOperations(OperationsHashTree);
  2629. end;
  2630. procedure TStorage.LoadPendingBufferOperations(OperationsHashTree : TOperationsHashTree);
  2631. begin
  2632. DoLoadPendingBufferOperations(OperationsHashTree);
  2633. end;
  2634. function TStorage.LoadBlockChainBlock(Operations: TPCOperationsComp; Block: Cardinal): Boolean;
  2635. begin
  2636. if (Block<FirstBlock) Or (Block>LastBlock) then result := false
  2637. else Result := DoLoadBlockChain(Operations,Block);
  2638. end;
  2639. function TStorage.MoveBlockChainBlocks(StartBlock: Cardinal; const DestOrphan: TOrphan; DestStorage : TStorage): Boolean;
  2640. begin
  2641. if Assigned(DestStorage) then begin
  2642. if DestStorage.ReadOnly then raise Exception.Create('Cannot move blocks because is ReadOnly');
  2643. end else if ReadOnly then raise Exception.Create('Cannot move blocks from myself because is ReadOnly');
  2644. Result := DoMoveBlockChain(StartBlock,DestOrphan,DestStorage);
  2645. end;
  2646. function TStorage.RestoreBank(max_block: Int64; restoreProgressNotify : TProgressNotify = Nil): Boolean;
  2647. begin
  2648. Result := DoRestoreBank(max_block,restoreProgressNotify);
  2649. end;
  2650. function TStorage.SaveBank(forceSave : Boolean): Boolean;
  2651. begin
  2652. Result := true;
  2653. If FIsMovingBlockchain then Exit;
  2654. if (Not forceSave) AND (Not TPCSafeBox.MustSafeBoxBeSaved(Bank.BlocksCount)) then exit; // No save
  2655. Try
  2656. Result := DoSaveBank;
  2657. FBank.SafeBox.CheckMemory;
  2658. Except
  2659. On E:Exception do begin
  2660. TLog.NewLog(lterror,Classname,'Error saving Bank: '+E.Message);
  2661. Raise;
  2662. end;
  2663. End;
  2664. end;
  2665. function TStorage.SaveBlockChainBlock(Operations: TPCOperationsComp): Boolean;
  2666. begin
  2667. Try
  2668. if ReadOnly then raise Exception.Create('Cannot save because is ReadOnly');
  2669. Result := DoSaveBlockChain(Operations);
  2670. Except
  2671. On E:Exception do begin
  2672. TLog.NewLog(lterror,Classname,'Error saving block chain: '+E.Message);
  2673. Raise;
  2674. end;
  2675. End;
  2676. end;
  2677. procedure TStorage.SetBank(const Value: TPCBank);
  2678. begin
  2679. FBank := Value;
  2680. end;
  2681. procedure TStorage.SetOrphan(const Value: TOrphan);
  2682. begin
  2683. FOrphan := Value;
  2684. end;
  2685. procedure TStorage.SetReadOnly(const Value: Boolean);
  2686. begin
  2687. FReadOnly := Value;
  2688. end;
  2689. { TPCOperation }
  2690. constructor TPCOperation.Create(AProtocolVersion : Word);
  2691. begin
  2692. FHasValidSignature := False;
  2693. FBufferedSha256:=Nil;
  2694. FBufferedRipeMD160:=Nil;
  2695. FUsedPubkeyForSignature := CT_TECDSA_Public_Nul;
  2696. InitializeData(AProtocolVersion);
  2697. end;
  2698. destructor TPCOperation.Destroy;
  2699. begin
  2700. inherited Destroy;
  2701. end;
  2702. function TPCOperation.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
  2703. Var ms : TMemoryStream;
  2704. begin
  2705. // Protocol v2 change:
  2706. // In previous builds (previous to 2.0) there was a distinct method to
  2707. // save data for ophash and for calculate Sha256 value on merkle tree
  2708. //
  2709. // Starting in v2 we will use only 1 method to do both calcs
  2710. // We will use "UseProtocolV2" bool value to indicate which method
  2711. // want to calc.
  2712. // Note: This method will be overrided by OpTransaction, OpChange and OpRecover only
  2713. if (UseProtocolV2) then begin
  2714. ms := TMemoryStream.Create;
  2715. try
  2716. SaveOpToStream(ms,False);
  2717. ms.Position := 0;
  2718. SetLength(Result,ms.Size);
  2719. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  2720. finally
  2721. ms.Free;
  2722. end;
  2723. end else Raise Exception.Create('ERROR DEV 20170426-1'); // This should never happen, if good coded
  2724. end;
  2725. class function TPCOperation.GetOperationFromStreamData(ACurrentProtocol: word; StreamData : TBytes): TPCOperation;
  2726. // Loads an TPCOperation saved using "GetOperationStreamData"
  2727. // 1 byte for OpType
  2728. // N bytes for Operation specific data (saved at SaveOpToStream)
  2729. Var stream : TStream;
  2730. b : Byte;
  2731. j: Integer;
  2732. OpClass: TPCOperationClass;
  2733. auxOp: TPCOperation;
  2734. begin
  2735. Result := Nil;
  2736. stream := TMemoryStream.Create;
  2737. Try
  2738. stream.WriteBuffer(StreamData[0],Length(StreamData)); // Fixed bug 4.0.0
  2739. stream.Position := 0;
  2740. stream.Read(b,1);
  2741. j := TPCOperationsComp.IndexOfOperationClassByOpType(b);
  2742. if j >= 0 then
  2743. OpClass := _OperationsClass[j]
  2744. else Exit;
  2745. auxOp := OpClass.Create(ACurrentProtocol);
  2746. if auxOp.LoadOpFromStream(stream,False) then Result := auxOp
  2747. else auxOp.Free;
  2748. Finally
  2749. stream.Free;
  2750. End;
  2751. end;
  2752. function TPCOperation.GetOperationStreamData: TBytes;
  2753. // OperationStreamData fills an array of bytes with info needed to store an operation
  2754. // 1 byte for OpType
  2755. // N bytes for Operation specific data (saved at SaveOpToStream)
  2756. var stream : TStream;
  2757. b : Byte;
  2758. begin
  2759. stream := TMemoryStream.Create;
  2760. Try
  2761. b := OpType;
  2762. stream.Write(b,1);
  2763. SaveOpToStream(stream,False);
  2764. SetLength(Result,stream.Size);
  2765. stream.Position := 0;
  2766. stream.ReadBuffer(Result[0],stream.Size); // Fixed bug 4.0.0
  2767. Finally
  2768. stream.Free;
  2769. End;
  2770. end;
  2771. function TPCOperation.GetOpID: TRawBytes;
  2772. begin
  2773. Result := RipeMD160;
  2774. end;
  2775. function TPCOperation.GetOpReference: TOpReference;
  2776. // Described on PIP-0015 by Herman Schoenfeld
  2777. // Will return a 64 bit value composed by SignerAccount (first 4 bytes) and n_Operation (last 4 bytes)
  2778. // Will allow to quick search an Operation in a TOperationsHashTree object
  2779. begin
  2780. Result := ((UInt64(SignerAccount) SHL 32) OR UInt64(N_Operation));
  2781. end;
  2782. class function TPCOperation.GetOpReferenceAccount(const opReference: TOpReference): Cardinal;
  2783. begin
  2784. Result := Cardinal(opReference SHR 32);
  2785. end;
  2786. class function TPCOperation.GetOpReferenceN_Operation(const opReference: TOpReference): Cardinal;
  2787. begin
  2788. Result := Cardinal(opReference);
  2789. end;
  2790. procedure TPCOperation.SignerAccounts(list: TList<Cardinal>);
  2791. begin
  2792. list.Clear;
  2793. list.Add(SignerAccount);
  2794. end;
  2795. class function TPCOperation.DecodeOperationHash(const operationHash: TRawBytes;
  2796. var block, account, n_operation: Cardinal; var md160Hash : TRawBytes) : Boolean;
  2797. { Decodes a previously generated OperationHash }
  2798. var ms : TMemoryStream;
  2799. begin
  2800. Result := false;
  2801. block :=0;
  2802. account :=0;
  2803. n_operation :=0;
  2804. md160Hash := Nil;
  2805. if length(operationHash)<>32 then exit;
  2806. ms := TMemoryStream.Create;
  2807. try
  2808. ms.Write(operationHash[Low(operationHash)],Length(operationHash));
  2809. ms.position := 0;
  2810. ms.Read(block,4);
  2811. ms.Read(account,4);
  2812. ms.Read(n_operation,4);
  2813. SetLength(md160Hash, 20);
  2814. ms.ReadBuffer(md160Hash[Low(md160Hash)], 20);
  2815. Result := true;
  2816. finally
  2817. ms.free;
  2818. end;
  2819. end;
  2820. class function TPCOperation.IsValidOperationHash(const AOpHash : String) : Boolean;
  2821. var block, account, n_operation: Cardinal; md160Hash : TRawBytes;
  2822. begin
  2823. Result := TryParseOperationHash(AOpHash, block, account, n_operation, md160Hash);
  2824. end;
  2825. class function TPCOperation.TryParseOperationHash(const AOpHash : String; var block, account, n_operation: Cardinal; var md160Hash : TRawBytes) : Boolean;
  2826. var
  2827. ophash : TRawBytes;
  2828. begin
  2829. ophash := TCrypto.HexaToRaw(Trim(AOpHash));
  2830. if Length(ophash) = 0 then
  2831. Exit(false);
  2832. If not TPCOperation.DecodeOperationHash(ophash,block,account,n_operation,md160Hash) then
  2833. Exit(false);
  2834. Result := true;
  2835. end;
  2836. class function TPCOperation.EqualOperationHashes(const operationHash1,operationHash2: TRawBytes): Boolean;
  2837. // operationHash1 and operationHash2 must be in RAW format (Not hexadecimal string!)
  2838. var b0,b1,b2,r1,r2 : TRawBytes;
  2839. begin
  2840. // First 4 bytes of OpHash are block number. If block=0 then is an unknown block, otherwise must match
  2841. b1 := copy(operationHash1,Low(operationHash1),4);
  2842. b2 := copy(operationHash2,Low(operationHash2),4);
  2843. r1 := copy(operationHash1,4,Length(operationHash1)-4);
  2844. r2 := copy(operationHash2,4,Length(operationHash2)-4);
  2845. b0 := TCrypto.HexaToRaw('00000000');
  2846. Result := (TBaseType.BinStrComp(r1,r2)=0) // Both right parts must be equal
  2847. AND ((TBaseType.BinStrComp(b1,b0)=0) Or (TBaseType.BinStrComp(b2,b0)=0) Or (TBaseType.BinStrComp(b1,b2)=0)); // b is 0 value or b1=b2 (b = block number)
  2848. end;
  2849. class function TPCOperation.FinalOperationHashAsHexa(const operationHash: TRawBytes): String;
  2850. begin
  2851. Result := TCrypto.ToHexaString(Copy(operationHash,4,28));
  2852. end;
  2853. class function TPCOperation.OperationHashAsHexa(const operationHash: TRawBytes): String;
  2854. begin
  2855. Result := TCrypto.ToHexaString(operationHash);
  2856. end;
  2857. procedure TPCOperation.InitializeData(AProtocolVersion : Word);
  2858. begin
  2859. FProtocolVersion := AProtocolVersion;
  2860. FHasValidSignature := false;
  2861. FUsedPubkeyForSignature:=CT_TECDSA_Public_Nul;
  2862. FBufferedSha256 := Nil;
  2863. FBufferedRipeMD160 := Nil;
  2864. end;
  2865. procedure TPCOperation.FillOperationResume(Block: Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  2866. begin
  2867. //
  2868. end;
  2869. function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
  2870. begin
  2871. // Will reuse FHasValidSignature if checked previously and was True
  2872. // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
  2873. if (FHasValidSignature) then begin
  2874. If Not TAccountComp.EqualAccountKeys(PubKey,FUsedPubkeyForSignature) then begin
  2875. TLog.NewLog(lterror,ClassName,Format('Detected incorrect previous use of signature used pubkey:%s current pubkey:%s',[TAccountComp.AccountPublicKeyExport(FUsedPubkeyForSignature),TAccountComp.AccountPublicKeyExport(PubKey)]));
  2876. FHasValidSignature := False;
  2877. FUsedPubkeyForSignature := CT_TECDSA_Public_Nul;
  2878. end;
  2879. end;
  2880. if (Not FHasValidSignature) then begin
  2881. FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign,Signature);
  2882. If FHasValidSignature then begin;
  2883. FUsedPubkeyForSignature := PubKey;
  2884. end;
  2885. end;
  2886. Result := FHasValidSignature;
  2887. end;
  2888. procedure TPCOperation.CopyUsedPubkeySignatureFrom(SourceOperation: TPCOperation);
  2889. begin
  2890. //
  2891. end;
  2892. function TPCOperation.LoadFromNettransfer(Stream: TStream): Boolean;
  2893. begin
  2894. Result := LoadOpFromStream(Stream, False);
  2895. end;
  2896. function TPCOperation.LoadFromStorage(Stream: TStream; LoadProtocolVersion:Word; APreviousUpdatedBlocks : TAccountPreviousBlockInfo): Boolean;
  2897. var LPrevious_Signer, LPrevious_Destination, LPrevious_Seller : Cardinal;
  2898. begin
  2899. Result := false;
  2900. If LoadOpFromStream(Stream, LoadProtocolVersion>=CT_PROTOCOL_2) then begin
  2901. If LoadProtocolVersion<CT_PROTOCOL_3 then begin
  2902. if Stream.Size - Stream.Position<8 then exit;
  2903. Stream.Read(LPrevious_Signer,Sizeof(LPrevious_Signer));
  2904. Stream.Read(LPrevious_Destination,Sizeof(LPrevious_Destination));
  2905. if (LoadProtocolVersion=CT_PROTOCOL_2) then begin
  2906. Stream.Read(LPrevious_Seller,Sizeof(LPrevious_Seller));
  2907. end;
  2908. if Assigned(APreviousUpdatedBlocks) then begin
  2909. // Add to previous list!
  2910. if SignerAccount>=0 then
  2911. APreviousUpdatedBlocks.UpdateIfLower(SignerAccount,LPrevious_Signer);
  2912. if DestinationAccount>=0 then
  2913. APreviousUpdatedBlocks.UpdateIfLower(DestinationAccount,LPrevious_Destination);
  2914. if SellerAccount>=0 then
  2915. APreviousUpdatedBlocks.UpdateIfLower(SellerAccount,LPrevious_Seller);
  2916. end;
  2917. end;
  2918. Result := true;
  2919. end;
  2920. end;
  2921. function TPCOperation.LoadOperationPayloadFromStream(const AStream: TStream; out APayload: TOperationPayload): Boolean;
  2922. begin
  2923. APayload := CT_TOperationPayload_NUL;
  2924. if FProtocolVersion>=CT_PROTOCOL_5 then begin
  2925. // payload_type will only be available on protocol 5
  2926. if AStream.Read(APayload.payload_type,SizeOf(APayload.payload_type))<>SizeOf(APayload.payload_type) then Exit(False);
  2927. end;
  2928. if TStreamOp.ReadAnsiString(AStream,APayload.payload_raw)<0 then Exit(False);
  2929. Result := True;
  2930. end;
  2931. function TPCOperation.SaveOperationPayloadToStream(const AStream: TStream; const APayload: TOperationPayload): Boolean;
  2932. begin
  2933. if FProtocolVersion>=CT_PROTOCOL_5 then begin
  2934. // payload_type will only be available on protocol 5
  2935. AStream.Write(APayload.payload_type,SizeOf(APayload.payload_type));
  2936. end;
  2937. TStreamOp.WriteAnsiString(AStream,APayload.payload_raw);
  2938. Result := True;
  2939. end;
  2940. class function TPCOperation.OperationHash_OLD(op: TPCOperation; Block : Cardinal): TRawBytes;
  2941. { OperationHash is a 32 bytes value.
  2942. First 4 bytes (0..3) are Block in little endian
  2943. Next 4 bytes (4..7) are Account in little endian
  2944. Next 4 bytes (8..11) are N_Operation in little endian
  2945. Next 20 bytes (12..31) are a RipeMD160 of the operation buffer to hash
  2946. //
  2947. This format is easy to undecode because include account and n_operation
  2948. }
  2949. var ms : TMemoryStream;
  2950. r : TRawBytes;
  2951. _a,_o : Cardinal;
  2952. begin
  2953. ms := TMemoryStream.Create;
  2954. try
  2955. ms.Write(Block,4);
  2956. _a := op.SignerAccount;
  2957. _o := op.N_Operation;
  2958. ms.Write(_a,4);
  2959. ms.Write(_o,4);
  2960. // BUG IN PREVIOUS VERSIONS: (1.5.5 and prior)
  2961. // Function DoRipeMD160 returned a 40 bytes value, because data was converted in hexa string!
  2962. // So, here we used only first 20 bytes, and WHERE HEXA values, so only 16 diff values per 2 byte!
  2963. ms.WriteBuffer(TCrypto.DoRipeMD160_HEXASTRING(op.GetBufferForOpHash(False))[Low(TRawBytes)],20);
  2964. SetLength(Result,ms.size);
  2965. ms.Position:=0;
  2966. ms.Read(Result[Low(Result)],ms.size);
  2967. finally
  2968. ms.Free;
  2969. end;
  2970. end;
  2971. class function TPCOperation.OperationHashValid(op: TPCOperation; Block : Cardinal): TRawBytes;
  2972. { OperationHash is a 32 bytes value.
  2973. First 4 bytes (0..3) are Block in little endian
  2974. Next 4 bytes (4..7) are Account in little endian
  2975. Next 4 bytes (8..11) are N_Operation in little endian
  2976. Next 20 bytes (12..31) are a RipeMD160 of the SAME data used to calc Sha256
  2977. //
  2978. This format is easy to undecode because include account and n_operation
  2979. }
  2980. var ms : TMemoryStream;
  2981. _a,_o : Cardinal;
  2982. begin
  2983. ms := TMemoryStream.Create;
  2984. try
  2985. ms.Write(Block,4); // Save block (4 bytes)
  2986. _a := op.SignerAccount;
  2987. _o := op.N_Operation;
  2988. ms.Write(_a,4); // Save Account (4 bytes)
  2989. ms.Write(_o,4); // Save N_Operation (4 bytes)
  2990. ms.WriteBuffer(op.RipeMD160[Low(TRawBytes)],20); // Calling GetBufferForOpHash(TRUE) is the same than data used for Sha256
  2991. SetLength(Result,ms.size);
  2992. ms.Position:=0;
  2993. ms.Read(Result[Low(Result)],ms.size);
  2994. finally
  2995. ms.Free;
  2996. end;
  2997. end;
  2998. class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
  2999. Var s : String;
  3000. LOpToText : String;
  3001. begin
  3002. OperationResume := CT_TOperationResume_NUL;
  3003. OperationResume.Block:=Block;
  3004. If Operation.SignerAccount=Affected_account_number then begin
  3005. OperationResume.Fee := (-1)*Int64(Operation.OperationFee);
  3006. end;
  3007. OperationResume.AffectedAccount := Affected_account_number;
  3008. OperationResume.OpType:=Operation.OpType;
  3009. OperationResume.SignerAccount := Operation.SignerAccount;
  3010. OperationResume.n_operation := Operation.N_Operation;
  3011. Result := false;
  3012. case Operation.OpType of
  3013. CT_Op_Transaction : Begin
  3014. // Assume that Operation is TOpTransaction
  3015. OperationResume.DestAccount:=TOpTransaction(Operation).Data.target;
  3016. if (TOpTransaction(Operation).Data.opTransactionStyle = transaction_with_auto_buy_account) then begin
  3017. if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
  3018. OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionBuyer;
  3019. OperationResume.OperationTxt := 'Tx-Out (PASA '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target)+' Purchase) '+TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount)+' PASC from '+
  3020. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender)+' to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
  3021. If (TOpTransaction(Operation).Data.sender=TOpTransaction(Operation).Data.SellerAccount) then begin
  3022. // Valid calc when sender is the same than seller
  3023. OperationResume.Amount := (Int64(TOpTransaction(Operation).Data.amount) - (TOpTransaction(Operation).Data.AccountPrice)) * (-1);
  3024. end else OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
  3025. Result := true;
  3026. end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
  3027. OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionTarget;
  3028. OperationResume.OperationTxt := 'Tx-In (PASA '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target)+' Purchase) '+TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount)+' PASC from '+
  3029. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender)+' to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
  3030. OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) - Int64(TOpTransaction(Operation).Data.AccountPrice);
  3031. OperationResume.Fee := 0;
  3032. Result := true;
  3033. end else if TOpTransaction(Operation).Data.SellerAccount=Affected_account_number then begin
  3034. OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionSeller;
  3035. OperationResume.OperationTxt := 'Tx-In Sold account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target)+' price '+TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice)+' PASC';
  3036. OperationResume.Amount := TOpTransaction(Operation).Data.AccountPrice;
  3037. OperationResume.Fee := 0;
  3038. Result := true;
  3039. end else exit;
  3040. end else if (TOpTransaction(Operation).Data.opTransactionStyle = transaction_with_auto_atomic_swap) then begin
  3041. if TOpTransaction(Operation).Data.new_accountkey.EC_OpenSSL_NID=0 then begin
  3042. // COIN SWAP
  3043. LOpToText := Format('COIN SWAP %s PASC from %s to %s',[
  3044. TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
  3045. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
  3046. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.SellerAccount)]);
  3047. end else begin
  3048. // ACCOUNT SWAP
  3049. LOpToText := Format('ACCOUNT SWAP %s to new PublicKey %s',[
  3050. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
  3051. TAccountComp.AccountPublicKeyExport(TOpTransaction(Operation).Data.new_accountkey)]);
  3052. end;
  3053. if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
  3054. // The sender of the transaction
  3055. OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionSender;
  3056. OperationResume.OperationTxt := Format('Tx-Out %s PASC from %s to %s with %s',
  3057. [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
  3058. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
  3059. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
  3060. LOpToText]);
  3061. If (TOpTransaction(Operation).Data.sender=TOpTransaction(Operation).Data.SellerAccount) then begin
  3062. // Valid calc when sender is the same than seller
  3063. OperationResume.Amount := (Int64(TOpTransaction(Operation).Data.amount) - (TOpTransaction(Operation).Data.AccountPrice)) * (-1);
  3064. end else OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
  3065. Result := true;
  3066. end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
  3067. OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionTarget;
  3068. OperationResume.OperationTxt := Format('Tx-In %s PASC from %s to %s with %s',
  3069. [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
  3070. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
  3071. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
  3072. LOpToText]);
  3073. OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) - Int64(TOpTransaction(Operation).Data.AccountPrice);
  3074. OperationResume.Fee := 0;
  3075. Result := true;
  3076. end else if TOpTransaction(Operation).Data.SellerAccount=Affected_account_number then begin
  3077. OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionSeller;
  3078. OperationResume.OperationTxt := Format('Tx-In seller receiving %s PASC from Tx between %s to %s with %s',
  3079. [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
  3080. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
  3081. TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
  3082. LOpToText]);
  3083. OperationResume.Amount := TOpTransaction(Operation).Data.AccountPrice;
  3084. OperationResume.Fee := 0;
  3085. Result := true;
  3086. end else exit;
  3087. end else begin
  3088. if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
  3089. OperationResume.OpSubtype := CT_OpSubtype_TransactionSender;
  3090. OperationResume.OperationTxt := 'Tx-Out '+TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount)+' PASC from '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender)+' to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
  3091. OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
  3092. Result := true;
  3093. end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
  3094. OperationResume.OpSubtype := CT_OpSubtype_TransactionReceiver;
  3095. OperationResume.OperationTxt := 'Tx-In '+TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount)+' PASC from '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender)+' to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target);
  3096. OperationResume.Amount := TOpTransaction(Operation).Data.amount;
  3097. OperationResume.Fee := 0;
  3098. Result := true;
  3099. end else exit;
  3100. end;
  3101. End;
  3102. CT_Op_Changekey : Begin
  3103. OperationResume.OpSubtype := CT_OpSubtype_ChangeKey;
  3104. OperationResume.newKey := TOpChangeKey(Operation).Data.new_accountkey;
  3105. OperationResume.DestAccount := TOpChangeKey(Operation).Data.account_target;
  3106. OperationResume.OperationTxt := 'Change Key to '+TAccountComp.GetECInfoTxt( OperationResume.newKey.EC_OpenSSL_NID );
  3107. Result := true;
  3108. End;
  3109. CT_Op_ChangeKeySigned : Begin
  3110. OperationResume.OpSubtype := CT_OpSubtype_ChangeKeySigned;
  3111. OperationResume.newKey := TOpChangeKeySigned(Operation).Data.new_accountkey;
  3112. OperationResume.DestAccount := TOpChangeKeySigned(Operation).Data.account_target;
  3113. OperationResume.OperationTxt := 'Change '+TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.DestAccount)+' account key to '+TAccountComp.GetECInfoTxt( OperationResume.newKey.EC_OpenSSL_NID );
  3114. Result := true;
  3115. end;
  3116. CT_Op_Recover : Begin
  3117. OperationResume.OpSubtype := CT_OpSubtype_Recover;
  3118. OperationResume.OperationTxt := 'Recover founds';
  3119. Result := true;
  3120. End;
  3121. CT_Op_ListAccountForSale : begin
  3122. case TOpListAccountForSaleOrSwap(Operation).OpSubType of
  3123. CT_OpSubtype_ListAccountForPrivateSale: begin
  3124. OperationResume.OpSubtype := CT_OpSubtype_ListAccountForPrivateSale;
  3125. OperationResume.OperationTxt := 'List account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccount(Operation).Data.account_target)+' for private sale price '+
  3126. TAccountComp.FormatMoney(TOpListAccount(Operation).Data.account_price)+' PASC pay to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccount(Operation).Data.account_to_pay);
  3127. end;
  3128. CT_OpSubtype_ListAccountForPublicSale: begin
  3129. OperationResume.OpSubtype := CT_OpSubtype_ListAccountForPublicSale;
  3130. OperationResume.OperationTxt := 'List account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccount(Operation).Data.account_target)+' for sale price '+
  3131. TAccountComp.FormatMoney(TOpListAccount(Operation).Data.account_price)+' PASC pay to '+TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccount(Operation).Data.account_to_pay);
  3132. end;
  3133. CT_OpSubtype_ListAccountForAccountSwap: begin
  3134. OperationResume.OpSubtype := CT_OpSubtype_ListAccountForAccountSwap;
  3135. OperationResume.OperationTxt :=
  3136. 'List account ' + TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccountForSaleOrSwap(Operation).Data.account_target) +
  3137. ' for atomic account swap with hash-lock "' + TCrypto.ToHexaString( TBaseType.ToRawBytes( TOpListAccountForSaleOrSwap(Operation).Data.hash_lock) ) + '"' +
  3138. ' time-locked till ' + inttostr(TOpListAccountForSaleOrSwap(Operation).Data.locked_until_block) +
  3139. ' to counterparty key ' + TAccountComp.AccountPublicKeyExport( TOpListAccountForSaleOrSwap(Operation).Data.new_public_key);
  3140. end;
  3141. CT_OpSubtype_ListAccountForCoinSwap: begin
  3142. OperationResume.OpSubtype := CT_OpSubtype_ListAccountForCoinSwap;
  3143. OperationResume.OperationTxt :=
  3144. 'List account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccountForSaleOrSwap(Operation).Data.account_target)+
  3145. ' for atomic coin swap of ' + TAccountComp.FormatMoney(TOpListAccountForSaleOrSwap(Operation).Data.account_price) + ' PASC' +
  3146. ' with hash-lock "' + TCrypto.ToHexaString( TBaseType.ToRawBytes( TOpListAccountForSaleOrSwap(Operation).Data.hash_lock) ) + '"' +
  3147. ' time-locked till ' + inttostr(TOpListAccountForSaleOrSwap(Operation).Data.locked_until_block) +
  3148. ' to counterparty account ' + TAccountComp.AccountNumberToAccountTxtNumber(TOpListAccountForSaleOrSwap(Operation).Data.account_to_pay);
  3149. end;
  3150. end;
  3151. OperationResume.newKey := TOpListAccount(Operation).Data.new_public_key;
  3152. OperationResume.SellerAccount := Operation.SellerAccount;
  3153. Result := true;
  3154. End;
  3155. CT_Op_DelistAccount : Begin
  3156. OperationResume.OpSubtype := CT_OpSubtype_DelistAccount;
  3157. OperationResume.OperationTxt := 'Delist account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpDelistAccountForSale(Operation).Data.account_target)+' for sale';
  3158. Result := true;
  3159. End;
  3160. CT_Op_BuyAccount : Begin
  3161. OperationResume.DestAccount:=TOpBuyAccount(Operation).Data.target;
  3162. if TOpBuyAccount(Operation).Data.sender=Affected_account_number then begin
  3163. OperationResume.OpSubtype := CT_OpSubtype_BuyAccountBuyer;
  3164. OperationResume.OperationTxt := 'Buy account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpBuyAccount(Operation).Data.target)+' for '+TAccountComp.FormatMoney(TOpBuyAccount(Operation).Data.AccountPrice)+' PASC';
  3165. OperationResume.Amount := Int64(TOpBuyAccount(Operation).Data.amount) * (-1);
  3166. Result := true;
  3167. end else if TOpBuyAccount(Operation).Data.target=Affected_account_number then begin
  3168. OperationResume.OpSubtype := CT_OpSubtype_BuyAccountTarget;
  3169. OperationResume.OperationTxt := 'Purchased account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpBuyAccount(Operation).Data.target)+' by '+
  3170. TAccountComp.AccountNumberToAccountTxtNumber(TOpBuyAccount(Operation).Data.sender)+' for '+TAccountComp.FormatMoney(TOpBuyAccount(Operation).Data.AccountPrice)+' PASC';
  3171. OperationResume.Amount := Int64(TOpBuyAccount(Operation).Data.amount) - Int64(TOpBuyAccount(Operation).Data.AccountPrice);
  3172. OperationResume.Fee := 0;
  3173. Result := true;
  3174. end else if TOpBuyAccount(Operation).Data.SellerAccount=Affected_account_number then begin
  3175. OperationResume.OpSubtype := CT_OpSubtype_BuyAccountSeller;
  3176. OperationResume.OperationTxt := 'Sold account '+TAccountComp.AccountNumberToAccountTxtNumber(TOpBuyAccount(Operation).Data.target)+' by '+
  3177. TAccountComp.AccountNumberToAccountTxtNumber(TOpBuyAccount(Operation).Data.sender)+' for '+TAccountComp.FormatMoney(TOpBuyAccount(Operation).Data.AccountPrice)+' PASC';
  3178. OperationResume.Amount := TOpBuyAccount(Operation).Data.AccountPrice;
  3179. OperationResume.Fee := 0;
  3180. Result := true;
  3181. end else exit;
  3182. if (TOpBuyAccount(Operation).Data.sender = TOpBuyAccount(Operation).Data.target) then begin
  3183. OperationResume.Amount := TOpBuyAccount(Operation).Data.AccountPrice;
  3184. end;
  3185. End;
  3186. CT_Op_ChangeAccountInfo : Begin
  3187. OperationResume.DestAccount := Operation.DestinationAccount;
  3188. s := '';
  3189. if (public_key in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
  3190. s := 'key';
  3191. end;
  3192. if (account_name in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
  3193. if s<>'' then s:=s+',';
  3194. s := s + 'name';
  3195. end;
  3196. if (account_type in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
  3197. if s<>'' then s:=s+',';
  3198. s := s + 'type';
  3199. end;
  3200. if (account_data in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
  3201. if s<>'' then s:=s+',';
  3202. s := s + 'data';
  3203. end;
  3204. OperationResume.OperationTxt:= 'Changed '+s+' of account '+TAccountComp.AccountNumberToAccountTxtNumber(Operation.DestinationAccount);
  3205. OperationResume.OpSubtype:=CT_OpSubtype_ChangeAccountInfo;
  3206. Result := True;
  3207. end;
  3208. CT_Op_MultiOperation : Begin
  3209. OperationResume.isMultiOperation:=True;
  3210. OperationResume.OperationTxt := Operation.ToString;
  3211. OperationResume.Amount := Operation.OperationAmountByAccount(Affected_account_number);
  3212. OperationResume.Fee := 0;
  3213. Result := True;
  3214. end;
  3215. CT_Op_Data : Begin
  3216. Result := True;
  3217. end
  3218. else Exit;
  3219. end;
  3220. OperationResume.OriginalPayload := Operation.OperationPayload;
  3221. If TCrypto.IsHumanReadable(OperationResume.OriginalPayload.payload_raw) then OperationResume.PrintablePayload := OperationResume.OriginalPayload.payload_raw.ToPrintable
  3222. else OperationResume.PrintablePayload := TCrypto.ToHexaString(OperationResume.OriginalPayload.payload_raw);
  3223. OperationResume.OperationHash:=TPCOperation.OperationHashValid(Operation,Block);
  3224. if (Block>0) And (Block<CT_Protocol_Upgrade_v2_MinBlock) then begin
  3225. OperationResume.OperationHash_OLD:=TPCOperation.OperationHash_OLD(Operation,Block);
  3226. end;
  3227. OperationResume.valid := true;
  3228. Operation.FillOperationResume(Block,getInfoForAllAccounts,Affected_account_number,OperationResume);
  3229. end;
  3230. function TPCOperation.RipeMD160: TRawBytes;
  3231. begin
  3232. If Length(FBufferedRipeMD160)=0 then begin
  3233. FBufferedRipeMD160 := TCrypto.DoRipeMD160AsRaw(GetBufferForOpHash(true));
  3234. end;
  3235. Result := FBufferedRipeMD160;
  3236. end;
  3237. function TPCOperation.IsSignerAccount(account: Cardinal): Boolean;
  3238. begin
  3239. Result := SignerAccount = account;
  3240. end;
  3241. function TPCOperation.IsAffectedAccount(account: Cardinal): Boolean;
  3242. Var l : TList<Cardinal>;
  3243. begin
  3244. l := TList<Cardinal>.Create;
  3245. Try
  3246. AffectedAccounts(l);
  3247. Result := (l.IndexOf(account)>=0);
  3248. finally
  3249. l.Free;
  3250. end;
  3251. end;
  3252. function TPCOperation.DestinationAccount: Int64;
  3253. begin
  3254. Result := -1;
  3255. end;
  3256. function TPCOperation.SellerAccount: Int64;
  3257. begin
  3258. Result := -1;
  3259. end;
  3260. function TPCOperation.GetAccountN_Operation(account: Cardinal): Cardinal;
  3261. begin
  3262. If (SignerAccount = account) then Result := N_Operation
  3263. else Result := 0;
  3264. end;
  3265. function TPCOperation.SaveToNettransfer(Stream: TStream): Boolean;
  3266. begin
  3267. Result := SaveOpToStream(Stream,False);
  3268. end;
  3269. function TPCOperation.SaveToStorage(Stream: TStream): Boolean;
  3270. begin
  3271. Result := SaveOpToStream(Stream,True);
  3272. end;
  3273. function TPCOperation.Sha256: TRawBytes;
  3274. begin
  3275. If Length(FBufferedSha256)=0 then begin
  3276. FBufferedSha256 := TCrypto.DoSha256(GetBufferForOpHash(true));
  3277. end;
  3278. Result := FBufferedSha256;
  3279. end;
  3280. function TPCOperation.OperationAmountByAccount(account: Cardinal): Int64;
  3281. begin
  3282. Result := 0;
  3283. end;
  3284. { TOperationsResumeList }
  3285. Type POperationResume = ^TOperationResume;
  3286. procedure TOperationsResumeList.Add(const OperationResume: TOperationResume);
  3287. Var P : POperationResume;
  3288. begin
  3289. New(P);
  3290. P^ := OperationResume;
  3291. FList.Add(P);
  3292. end;
  3293. procedure TOperationsResumeList.Clear;
  3294. Var P : POperationResume;
  3295. i : Integer;
  3296. l : TList<Pointer>;
  3297. begin
  3298. l := FList.LockList;
  3299. try
  3300. for i := 0 to l.Count - 1 do begin
  3301. P := l[i];
  3302. Dispose(P);
  3303. end;
  3304. l.Clear;
  3305. finally
  3306. FList.UnlockList;
  3307. end;
  3308. end;
  3309. function TOperationsResumeList.Count: Integer;
  3310. Var l : TList<Pointer>;
  3311. begin
  3312. l := FList.LockList;
  3313. Try
  3314. Result := l.Count;
  3315. Finally
  3316. FList.UnlockList;
  3317. End;
  3318. end;
  3319. constructor TOperationsResumeList.Create;
  3320. begin
  3321. FList := TPCThreadList<Pointer>.Create('TOperationsResumeList_List');
  3322. end;
  3323. procedure TOperationsResumeList.Delete(index: Integer);
  3324. Var P : POperationResume;
  3325. l : TList<Pointer>;
  3326. begin
  3327. l := FList.LockList;
  3328. Try
  3329. P := l[index];
  3330. l.Delete(index);
  3331. Dispose(P);
  3332. Finally
  3333. FList.UnlockList;
  3334. End;
  3335. end;
  3336. destructor TOperationsResumeList.Destroy;
  3337. begin
  3338. Clear;
  3339. FreeAndNil(FList);
  3340. inherited;
  3341. end;
  3342. function TOperationsResumeList.GetOperationResume(index: Integer): TOperationResume;
  3343. Var l : TList<Pointer>;
  3344. begin
  3345. l := FList.LockList;
  3346. try
  3347. if index<l.Count then Result := POperationResume(l[index])^
  3348. else Result := CT_TOperationResume_NUL;
  3349. finally
  3350. FList.UnlockList;
  3351. end;
  3352. end;
  3353. initialization
  3354. SetLength(_OperationsClass, 0);
  3355. RegisterOperationsClass;
  3356. _PCOperationsStorage := TPCOperationsStorage.Create;
  3357. finalization
  3358. FreeAndNil(_PCOperationsStorage);
  3359. end.