UBlockChain.pas 113 KB

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