UOpTransaction.pas 117 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880
  1. unit UOpTransaction;
  2. { Copyright (c) 2016 by Albert Molina
  3. Acknowledgements:
  4. - Herman Schoenfeld for implementing atomic swap operations (HLTC)
  5. Distributed under the MIT software license, see the accompanying file LICENSE
  6. or visit http://www.opensource.org/licenses/mit-license.php.
  7. This unit is a part of the PascalCoin Project, an infinitely scalable
  8. cryptocurrency. Find us here:
  9. Web: https://www.pascalcoin.org
  10. Source: https://github.com/PascalCoin/PascalCoin
  11. If you like it, consider a donation using Bitcoin:
  12. 16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
  13. THIS LICENSE HEADER MUST NOT BE REMOVED.
  14. }
  15. {$IFDEF FPC}
  16. {$MODE Delphi}
  17. {$ENDIF}
  18. interface
  19. Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
  20. {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
  21. UPCDataTypes;
  22. Type
  23. // Operations Type
  24. TOpTransactionStyle = (transaction, transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap);
  25. // transaction = Sinlge standard transaction
  26. // transaction_with_auto_buy_account = Single transaction made over an account listed for private sale. For STORING purposes only
  27. // buy_account = A Buy account operation
  28. // transaction_with_auto_atomic_swap = Single transaction made over an account listed for atomic swap (coin swap or account swap)
  29. TOpTransactionData = Record
  30. sender: Cardinal;
  31. n_operation : Cardinal;
  32. target: Cardinal;
  33. amount: UInt64;
  34. fee: UInt64;
  35. payload: TOperationPayload;
  36. public_key: TECDSA_Public;
  37. sign: TECDSA_SIG;
  38. // Protocol 2
  39. // Next values will only be filled after this operation is executed
  40. opTransactionStyle : TOpTransactionStyle;
  41. AccountPrice : UInt64;
  42. SellerAccount : Cardinal;
  43. new_accountkey : TAccountKey;
  44. End;
  45. TOpChangeKeyData = Record
  46. account_signer,
  47. account_target: Cardinal;
  48. n_operation : Cardinal;
  49. fee: UInt64;
  50. payload: TOperationPayload;
  51. public_key: TECDSA_Public;
  52. new_accountkey: TAccountKey;
  53. sign: TECDSA_SIG;
  54. End;
  55. TOpRecoverFoundsData = Record
  56. account: Cardinal;
  57. n_operation : Cardinal;
  58. fee: UInt64;
  59. new_accountkey: TAccountKey;
  60. End;
  61. Const
  62. CT_TOpTransactionData_NUL : TOpTransactionData = (sender:0;n_operation:0;target:0;amount:0;fee:0;payload:(payload_type:0;payload_raw:Nil);public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);sign:(r:Nil;s:Nil);opTransactionStyle:transaction;AccountPrice:0;SellerAccount:0;new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));
  63. CT_TOpChangeKeyData_NUL : TOpChangeKeyData = (account_signer:0;account_target:0;n_operation:0;fee:0;payload:(payload_type:0;payload_raw:Nil);public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);sign:(r:Nil;s:Nil));
  64. CT_TOpRecoverFoundsData_NUL : TOpRecoverFoundsData = (account:0;n_operation:0;fee:0;new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));
  65. Type
  66. { TOpTransaction }
  67. TOpTransaction = Class(TPCOperation)
  68. private
  69. FData : TOpTransactionData;
  70. protected
  71. procedure InitializeData(AProtocolVersion : Word); override;
  72. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  73. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  74. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  75. public
  76. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  77. function DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean; override;
  78. procedure AffectedAccounts(list : TList<Cardinal>); override;
  79. //
  80. class function OpType : Byte; override;
  81. function OperationAmount : Int64; override;
  82. function OperationFee : Int64; override;
  83. function OperationPayload : TOperationPayload; override;
  84. function SignerAccount : Cardinal; override;
  85. function DestinationAccount : Int64; override;
  86. function SellerAccount : Int64; override;
  87. function N_Operation : Cardinal; override;
  88. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  89. Property Data : TOpTransactionData read FData;
  90. Constructor CreateTransaction(ACurrentProtocol : Word; sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; const payload: TOperationPayload);
  91. Function toString : String; Override;
  92. Function GetDigestToSign : TRawBytes; override;
  93. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  94. End;
  95. { TOpChangeKey }
  96. TOpChangeKey = Class(TPCOperation)
  97. private
  98. FData : TOpChangeKeyData;
  99. protected
  100. procedure InitializeData(AProtocolVersion : Word); override;
  101. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  102. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  103. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  104. public
  105. class function OpType : Byte; override;
  106. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  107. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
  108. function OperationAmount : Int64; override;
  109. function OperationFee : Int64; override;
  110. function OperationPayload : TOperationPayload; override;
  111. function SignerAccount : Cardinal; override;
  112. function DestinationAccount : Int64; override;
  113. function N_Operation : Cardinal; override;
  114. procedure AffectedAccounts(list : TList<Cardinal>); override;
  115. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  116. Constructor Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
  117. Property Data : TOpChangeKeyData read FData;
  118. Function toString : String; Override;
  119. Function GetDigestToSign : TRawBytes; override;
  120. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  121. End;
  122. { TOpChangeKeySigned }
  123. TOpChangeKeySigned = Class(TOpChangeKey)
  124. public
  125. class function OpType : Byte; override;
  126. end;
  127. { TOpRecoverFounds }
  128. TOpRecoverFounds = Class(TPCOperation)
  129. private
  130. FData : TOpRecoverFoundsData;
  131. protected
  132. procedure InitializeData(AProtocolVersion : Word); override;
  133. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  134. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  135. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  136. public
  137. class function OpType : Byte; override;
  138. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  139. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
  140. function OperationAmount : Int64; override;
  141. function OperationFee : Int64; override;
  142. function OperationPayload : TOperationPayload; override;
  143. function SignerAccount : Cardinal; override;
  144. function N_Operation : Cardinal; override;
  145. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  146. procedure AffectedAccounts(list : TList<Cardinal>); override;
  147. Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
  148. Property Data : TOpRecoverFoundsData read FData;
  149. Function toString : String; Override;
  150. Function GetDigestToSign : TRawBytes; override;
  151. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  152. End;
  153. // NEW OPERATIONS PROTOCOL 2
  154. TOpListAccountOperationType = (lat_Unknown, lat_ListAccount, lat_DelistAccount);
  155. TOpListAccountData = Record
  156. account_signer,
  157. account_target: Cardinal;
  158. operation_type : TOpListAccountOperationType;
  159. n_operation : Cardinal;
  160. account_state: TAccountState;
  161. account_price: UInt64;
  162. account_to_pay : Cardinal;
  163. fee: UInt64;
  164. hash_lock : T32Bytes;
  165. payload: TOperationPayload;
  166. public_key: TAccountKey;
  167. new_public_key: TAccountKey; // If EC_OpenSSL_NID=0 then is OPEN, otherwise is for only 1 public key
  168. locked_until_block : Cardinal; //
  169. sign: TECDSA_SIG;
  170. End;
  171. TOpChangeAccountInfoData = Record
  172. account_signer,
  173. account_target: Cardinal;
  174. n_operation : Cardinal;
  175. fee: UInt64;
  176. payload: TOperationPayload;
  177. public_key: TECDSA_Public;
  178. changes_type : TOpChangeAccountInfoTypes; // bits mask. $0001 = New account key , $0002 = New name , $0004 = New type , $0008 = New Data
  179. new_accountkey: TAccountKey; // If (changes_mask and $0001)=$0001 then change account key
  180. new_name: TRawBytes; // If (changes_mask and $0002)=$0002 then change name
  181. new_type: Word; // If (changes_mask and $0004)=$0004 then change type
  182. new_data: TRawBytes; // If (changes_mask and $0008)=$0008 then change type
  183. sign: TECDSA_SIG;
  184. End;
  185. Const
  186. CT_TOpListAccountData_NUL : TOpListAccountData = (account_signer:0;account_target:0;operation_type:lat_Unknown;n_operation:0;account_state:as_Unknown;account_price:0;account_to_pay:0;fee:0;
  187. hash_lock:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);payload:(payload_type:0;payload_raw:Nil);public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;sign:(r:Nil;s:Nil));
  188. CT_TOpChangeAccountInfoData_NUL : TOpChangeAccountInfoData = (account_signer:0;account_target:0;n_operation:0;fee:0;payload:(payload_type:0;payload_raw:Nil);public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);changes_type:[];
  189. new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_name:Nil;new_type:0;new_data:Nil;sign:(r:Nil;s:Nil));
  190. Type
  191. { TOpListAccount }
  192. TOpListAccount = Class(TPCOperation)
  193. private
  194. FData : TOpListAccountData;
  195. protected
  196. procedure InitializeData(AProtocolVersion : Word); override;
  197. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  198. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  199. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  200. public
  201. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  202. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
  203. function OperationAmount : Int64; override;
  204. function OperationFee : Int64; override;
  205. function OperationPayload : TOperationPayload; override;
  206. function SignerAccount : Cardinal; override;
  207. function DestinationAccount : Int64; override;
  208. function SellerAccount : Int64; override;
  209. function N_Operation : Cardinal; override;
  210. procedure AffectedAccounts(list : TList<Cardinal>); override;
  211. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  212. Property Data : TOpListAccountData read FData;
  213. Function toString : String; Override;
  214. Function GetDigestToSign : TRawBytes; override;
  215. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  216. End;
  217. TOpListAccountForSaleOrSwap = Class(TOpListAccount)
  218. private
  219. function GetOpSubType : Integer;
  220. public
  221. class function OpType : Byte; override;
  222. Constructor CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal; ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TOperationPayload);
  223. property OpSubType : Integer read GetOpSubType;
  224. End;
  225. TOpDelistAccountForSale = Class(TOpListAccount)
  226. public
  227. class function OpType : Byte; override;
  228. Constructor CreateDelistAccountForSale(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; const payload: TOperationPayload);
  229. End;
  230. { TOpBuyAccount }
  231. TOpBuyAccount = Class(TOpTransaction)
  232. protected
  233. procedure InitializeData(AProtocolVersion : Word); override;
  234. public
  235. class function OpType : Byte; override;
  236. Constructor CreateBuy(ACurrentProtocol : Word; account_number, n_operation, account_to_buy, account_to_pay: Cardinal; price, amount, fee : UInt64; new_public_key:TAccountKey; key:TECPrivateKey; const payload: TOperationPayload);
  237. End;
  238. { TOpChangeAccountInfo }
  239. TOpChangeAccountInfo = Class(TPCOperation)
  240. private
  241. FData : TOpChangeAccountInfoData;
  242. protected
  243. procedure InitializeData(AProtocolVersion : Word); override;
  244. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  245. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  246. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  247. public
  248. class function OpType : Byte; override;
  249. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  250. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
  251. function OperationAmount : Int64; override;
  252. function OperationFee : Int64; override;
  253. function OperationPayload : TOperationPayload; override;
  254. function SignerAccount : Cardinal; override;
  255. function DestinationAccount : Int64; override;
  256. function N_Operation : Cardinal; override;
  257. procedure AffectedAccounts(list : TList<Cardinal>); override;
  258. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  259. Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
  260. account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
  261. change_key : Boolean; const new_account_key : TAccountKey;
  262. change_name: Boolean; const new_name : TRawBytes;
  263. change_type: Boolean; const new_type : Word;
  264. change_data: Boolean; const new_data : TRawBytes;
  265. fee: UInt64; const payload: TOperationPayload);
  266. Property Data : TOpChangeAccountInfoData read FData;
  267. Function toString : String; Override;
  268. Function GetDigestToSign : TRawBytes; override;
  269. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  270. End;
  271. TOpDataData = Record
  272. account_signer, // The account paying fees (if any) and signing operation
  273. account_sender, // The account sender. Public key must be EQUAL to account_signer public key
  274. account_target: Cardinal; // The destination account. Will recive DATA and amount (if any)
  275. n_operation : Cardinal; // Signer n_operation
  276. guid: TGUID; // GUID value, added on Protocol V5
  277. dataType : Word; // 2 byte data type
  278. dataSequence : Word; // 2 byte data sequence
  279. amount: UInt64; // Allow amount=0
  280. fee: UInt64; // Allow fee=0
  281. payload: TOperationPayload; // Standard arbitrary data with length<256
  282. sign: TECDSA_SIG;
  283. End;
  284. { TOpData }
  285. TOpData = Class(TPCOperation)
  286. private
  287. FData : TOpDataData;
  288. protected
  289. procedure InitializeData(AProtocolVersion : Word); override;
  290. function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
  291. function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
  292. procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
  293. public
  294. class function OpType : Byte; override;
  295. function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
  296. function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
  297. function OperationAmount : Int64; override;
  298. function OperationFee : Int64; override;
  299. function OperationPayload : TOperationPayload; override;
  300. function SignerAccount : Cardinal; override;
  301. function DestinationAccount : Int64; override;
  302. function N_Operation : Cardinal; override;
  303. procedure AffectedAccounts(list : TList<Cardinal>); override;
  304. function OperationAmountByAccount(account : Cardinal) : Int64; override;
  305. Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; AGUID : TGUID; amount, fee : UInt64; const payload: TOperationPayload);
  306. Property Data : TOpDataData read FData;
  307. Function toString : String; Override;
  308. Function GetDigestToSign : TRawBytes; override;
  309. function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
  310. End;
  311. Const
  312. CT_TOpDataData_NUL : TOpDataData = (account_signer:0;account_sender:0;account_target:0;n_operation:0;guid:(D1:0;D2:0;D3:0;D4:(0,0,0,0,0,0,0,0));dataType:0;dataSequence:0;amount:0;fee:0;payload:(payload_type:0;payload_raw:Nil);sign:(r:Nil;s:Nil));
  313. Procedure RegisterOperationsClass;
  314. implementation
  315. uses
  316. SysUtils, UConst, ULog, UTxMultiOperation;
  317. Procedure RegisterOperationsClass;
  318. Begin
  319. TPCOperationsComp.RegisterOperationClass(TOpTransaction);
  320. TPCOperationsComp.RegisterOperationClass(TOpChangeKey);
  321. TPCOperationsComp.RegisterOperationClass(TOpRecoverFounds);
  322. TPCOperationsComp.RegisterOperationClass(TOpListAccountForSaleOrSwap);
  323. TPCOperationsComp.RegisterOperationClass(TOpDelistAccountForSale);
  324. TPCOperationsComp.RegisterOperationClass(TOpBuyAccount);
  325. TPCOperationsComp.RegisterOperationClass(TOpChangeKeySigned);
  326. TPCOperationsComp.RegisterOperationClass(TOpChangeAccountInfo);
  327. TPCOperationsComp.RegisterOperationClass(TOpMultiOperation);
  328. TPCOperationsComp.RegisterOperationClass(TOpData);
  329. End;
  330. { TOpChangeAccountInfo }
  331. procedure TOpChangeAccountInfo.InitializeData(AProtocolVersion : Word);
  332. begin
  333. inherited InitializeData(AProtocolVersion);
  334. FData := CT_TOpChangeAccountInfoData_NUL;
  335. end;
  336. function TOpChangeAccountInfo.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  337. var LAccount : TAccount;
  338. begin
  339. LAccount := ASafeBoxTransaction.Account(FData.account_signer);
  340. Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
  341. end;
  342. function TOpChangeAccountInfo.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
  343. var b : byte;
  344. begin
  345. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  346. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  347. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  348. Stream.Write(FData.fee,Sizeof(FData.fee));
  349. SaveOperationPayloadToStream(Stream,FData.payload);
  350. TStreamOp.WriteAccountKey(Stream,FData.public_key);
  351. b := 0;
  352. if (public_key in FData.changes_type) then b:=b OR $01;
  353. if (account_name in FData.changes_type) then b:=b OR $02;
  354. if (account_type in FData.changes_type) then b:=b OR $04;
  355. if (account_data in FData.changes_type) then b:=b OR $08;
  356. Stream.Write(b,Sizeof(b));
  357. TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
  358. TStreamOp.WriteAnsiString(Stream,FData.new_name);
  359. Stream.Write(FData.new_type,Sizeof(FData.new_type));
  360. if FProtocolVersion>=CT_PROTOCOL_5 then begin
  361. TStreamOp.WriteAnsiString(Stream,FData.new_data);
  362. end;
  363. TStreamOp.WriteAnsiString(Stream,FData.sign.r);
  364. TStreamOp.WriteAnsiString(Stream,FData.sign.s);
  365. Result := true;
  366. end;
  367. function TOpChangeAccountInfo.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
  368. var b : Byte;
  369. begin
  370. Result := False;
  371. If Stream.Size - Stream.Position < 20 then exit;
  372. Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
  373. Stream.Read(FData.account_target,Sizeof(FData.account_target));
  374. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  375. Stream.Read(FData.fee,Sizeof(FData.fee));
  376. if Not LoadOperationPayloadFromStream(Stream,FData.payload) then Exit;
  377. if TStreamOp.ReadAccountKey(Stream,FData.public_key)<0 then Exit;
  378. Stream.Read(b,SizeOf(b));
  379. FData.changes_type:=[];
  380. if (b AND $01)=$01 then FData.changes_type:=FData.changes_type + [public_key];
  381. if (b AND $02)=$02 then FData.changes_type:=FData.changes_type + [account_name];
  382. if (b AND $04)=$04 then FData.changes_type:=FData.changes_type + [account_type];
  383. if (b AND $08)=$08 then FData.changes_type:=FData.changes_type + [account_data];
  384. // Check
  385. if (b AND $F0)<>0 then Exit;
  386. if TStreamOp.ReadAccountKey(Stream,FData.new_accountkey)<0 then Exit;
  387. if TStreamOp.ReadAnsiString(Stream,FData.new_name)<0 then Exit;
  388. Stream.Read(FData.new_type,Sizeof(FData.new_type));
  389. if FProtocolVersion>=CT_PROTOCOL_5 then begin
  390. if TStreamOp.ReadAnsiString(Stream,FData.new_data)<0 then Exit;
  391. end else FData.new_data := Nil;
  392. if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then Exit;
  393. if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then Exit;
  394. Result := true;
  395. end;
  396. procedure TOpChangeAccountInfo.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  397. begin
  398. inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
  399. SetLength(OperationResume.Changers,1);
  400. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  401. OperationResume.Changers[0].Account := FData.account_target;
  402. OperationResume.Changers[0].Changes_type := FData.changes_type;
  403. OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
  404. OperationResume.Changers[0].New_Name := FData.new_name;
  405. OperationResume.Changers[0].New_Type := FData.new_type;
  406. OperationResume.Changers[0].New_Data := FData.new_data;
  407. If (FData.account_signer=FData.account_target) then begin
  408. OperationResume.Changers[0].N_Operation := FData.n_operation;
  409. OperationResume.Changers[0].Signature := FData.sign;
  410. OperationResume.Changers[0].Fee := FData.fee;
  411. end else begin
  412. SetLength(OperationResume.Changers,2);
  413. OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
  414. OperationResume.Changers[1].Account := FData.account_signer;
  415. OperationResume.Changers[1].N_Operation := FData.n_operation;
  416. OperationResume.Changers[1].Fee := FData.fee;
  417. OperationResume.Changers[1].Signature := FData.sign;
  418. end;
  419. end;
  420. class function TOpChangeAccountInfo.OpType: Byte;
  421. begin
  422. Result := CT_Op_ChangeAccountInfo;
  423. end;
  424. function TOpChangeAccountInfo.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
  425. begin
  426. Result:=inherited GetBufferForOpHash(True);
  427. end;
  428. function TOpChangeAccountInfo.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
  429. Var account_signer, account_target : TAccount;
  430. LSafeboxCurrentProtocol : Integer;
  431. begin
  432. Result := false;
  433. LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
  434. if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  435. errors := 'Invalid account number';
  436. Exit;
  437. end;
  438. if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  439. errors := 'account is blocked for protocol';
  440. Exit;
  441. end;
  442. If (FData.account_signer<>FData.account_target) then begin
  443. if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  444. errors := 'Invalid account target number';
  445. Exit;
  446. end;
  447. if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  448. errors := 'Target account is blocked for protocol';
  449. Exit;
  450. end;
  451. end;
  452. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  453. errors := 'Invalid fee: '+Inttostr(FData.fee);
  454. exit;
  455. end;
  456. account_signer := AccountTransaction.Account(FData.account_signer);
  457. account_target := AccountTransaction.Account(FData.account_target);
  458. if ((account_signer.n_operation+1)<>FData.n_operation) then begin
  459. errors := 'Invalid n_operation';
  460. Exit;
  461. end;
  462. if (account_signer.balance<FData.fee) then begin
  463. errors := 'Insuficient funds';
  464. exit;
  465. end;
  466. if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
  467. errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
  468. If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
  469. Exit; // BUG from protocol 1
  470. end;
  471. end;
  472. // Is locked? Protocol 2 check
  473. if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  474. errors := 'Account signer is currently locked';
  475. exit;
  476. end;
  477. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  478. errors := 'NOT ALLOWED ON PROTOCOL 1';
  479. exit;
  480. end;
  481. If (public_key in FData.changes_type) then begin
  482. If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, LSafeboxCurrentProtocol, errors) then begin
  483. exit;
  484. end;
  485. end;
  486. If (account_name in FData.changes_type) then begin
  487. If (Length(FData.new_name)>0) then begin
  488. If Not TPCSafeBox.ValidAccountName(FData.new_name,errors) then Exit;
  489. end;
  490. end else begin
  491. If (Length(FData.new_name)>0) then begin
  492. errors := 'Invalid data in new_name field';
  493. Exit;
  494. end;
  495. end;
  496. if (account_data in FData.changes_type) then begin
  497. // TAccount.Data is a 0..32 bytes length
  498. if (Length(FData.new_data)>CT_MaxAccountDataSize) then begin
  499. errors := 'New data length ('+IntToStr(Length(FData.new_data))+') > '+IntToStr(CT_MaxAccountDataSize);
  500. Exit;
  501. end;
  502. end else begin
  503. if Length(FData.new_data)<>0 then begin
  504. errors := 'New data must be null when no data change';
  505. Exit;
  506. end;
  507. end;
  508. If (FData.changes_type=[]) then begin
  509. errors := 'No change';
  510. Exit;
  511. end;
  512. If (FData.public_key.EC_OpenSSL_NID<>CT_TECDSA_Public_Nul.EC_OpenSSL_NID) And (Not TAccountComp.EqualAccountKeys(FData.public_key,account_signer.accountInfo.accountkey)) then begin
  513. errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
  514. FData.account_signer,
  515. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
  516. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
  517. exit;
  518. end;
  519. If (FData.account_signer<>FData.account_target) then begin
  520. if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  521. errors := 'Account target is currently locked';
  522. exit;
  523. end;
  524. // Check have same public key
  525. If Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
  526. errors := 'Signer and target accounts have different public key';
  527. exit;
  528. end;
  529. end;
  530. // Check signature
  531. If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
  532. errors := 'Invalid ECDSA signature';
  533. Exit;
  534. end;
  535. If (public_key in FData.changes_type) then begin
  536. account_target.accountInfo.accountKey := FData.new_accountkey;
  537. end;
  538. If (account_name in FData.changes_type) then begin
  539. account_target.name := FData.new_name;
  540. end;
  541. If (account_type in FData.changes_type) then begin
  542. account_target.account_type := FData.new_type;
  543. end;
  544. If (account_data in FData.changes_type) then begin
  545. account_target.account_data := FData.new_data;
  546. end;
  547. Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
  548. GetOpID,
  549. FData.account_signer,FData.n_operation,FData.account_target,
  550. account_target.accountInfo,
  551. account_target.name,
  552. account_target.account_data,
  553. account_target.account_type,
  554. FData.fee,errors);
  555. end;
  556. function TOpChangeAccountInfo.OperationAmount: Int64;
  557. begin
  558. Result := 0;
  559. end;
  560. function TOpChangeAccountInfo.OperationFee: Int64;
  561. begin
  562. Result := FData.fee;
  563. end;
  564. function TOpChangeAccountInfo.OperationPayload: TOperationPayload;
  565. begin
  566. Result := FData.payload;
  567. end;
  568. function TOpChangeAccountInfo.SignerAccount: Cardinal;
  569. begin
  570. Result := FData.account_signer;
  571. end;
  572. function TOpChangeAccountInfo.DestinationAccount: Int64;
  573. begin
  574. Result := FData.account_target;
  575. end;
  576. function TOpChangeAccountInfo.N_Operation: Cardinal;
  577. begin
  578. Result := FData.n_operation;
  579. end;
  580. procedure TOpChangeAccountInfo.AffectedAccounts(list: TList<Cardinal>);
  581. begin
  582. list.Add(FData.account_signer);
  583. if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
  584. end;
  585. function TOpChangeAccountInfo.OperationAmountByAccount(account: Cardinal): Int64;
  586. begin
  587. if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
  588. else Result := 0;
  589. end;
  590. constructor TOpChangeAccountInfo.CreateChangeAccountInfo(ACurrentProtocol : word;
  591. account_signer, n_operation,
  592. account_target: Cardinal; key: TECPrivateKey; change_key: Boolean;
  593. const new_account_key: TAccountKey; change_name: Boolean;
  594. const new_name: TRawBytes; change_type: Boolean; const new_type: Word;
  595. change_data: Boolean; const new_data : TRawBytes;
  596. fee: UInt64; const payload: TOperationPayload);
  597. begin
  598. inherited Create(ACurrentProtocol);
  599. FData.account_signer:=account_signer;
  600. FData.account_target:=account_target;
  601. FData.n_operation:=n_operation;
  602. FData.fee:=fee;
  603. FData.payload:=payload;
  604. // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
  605. // FData.public_key:=key.PublicKey;
  606. FData.changes_type:=[];
  607. If change_key then begin
  608. FData.changes_type:=FData.changes_type + [public_key];
  609. FData.new_accountkey:=new_account_key;
  610. end;
  611. If change_name then begin
  612. FData.changes_type:=FData.changes_type + [account_name];
  613. FData.new_name:=new_name;
  614. end;
  615. If change_type then begin
  616. FData.changes_type:=FData.changes_type + [account_type];
  617. FData.new_type:=new_type;
  618. end;
  619. If change_data then begin
  620. FData.changes_type:=FData.changes_type + [account_data];
  621. FData.new_data:=new_data;
  622. end;
  623. if Assigned(key) then begin
  624. FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
  625. FHasValidSignature := true;
  626. FUsedPubkeyForSignature := key.PublicKey;
  627. end else begin
  628. TLog.NewLog(ltdebug,Classname,'No key for signing a new Change Info operation');
  629. FHasValidSignature := false;
  630. end;
  631. end;
  632. function TOpChangeAccountInfo.toString: String;
  633. var s : String;
  634. begin
  635. s := '';
  636. If (public_key IN FData.changes_type) then s := 'new public key '+TAccountComp.GetECInfoTxt(FData.new_accountkey.EC_OpenSSL_NID);
  637. If (account_name IN FData.changes_type) then begin
  638. if s<>'' then s:=s+', ';
  639. s := s + 'new name to "'+FData.new_name.ToPrintable+'"';
  640. end;
  641. If (account_type IN FData.changes_type) then begin
  642. if s<>'' then s:=s+', ';
  643. s := s + 'new type to '+IntToStr(FData.new_type);
  644. end;
  645. If (account_data IN FData.changes_type) then begin
  646. if s<>'' then s:=s+', ';
  647. s := s + 'new data to '+FData.new_data.ToHexaString;
  648. end;
  649. Result := Format('Change account %s info: %s fee:%s (n_op:%d) payload size:%d',[
  650. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
  651. s,
  652. TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw)]);
  653. end;
  654. function TOpChangeAccountInfo.GetDigestToSign: TRawBytes;
  655. var Stream : TMemoryStream;
  656. b : Byte;
  657. begin
  658. Stream := TMemoryStream.Create;
  659. try
  660. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  661. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  662. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  663. Stream.Write(FData.fee,Sizeof(FData.fee));
  664. SaveOperationPayloadToStream(Stream,FData.payload);
  665. TStreamOp.WriteAccountKey(Stream,FData.public_key);
  666. b := 0;
  667. if (public_key in FData.changes_type) then b:=b OR $01;
  668. if (account_name in FData.changes_type) then b:=b OR $02;
  669. if (account_type in FData.changes_type) then b:=b OR $04;
  670. if (account_data in FData.changes_type) then b:=b OR $08;
  671. Stream.Write(b,Sizeof(b));
  672. TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
  673. TStreamOp.WriteAnsiString(Stream,FData.new_name);
  674. Stream.Write(FData.new_type,Sizeof(FData.new_type));
  675. if (ProtocolVersion>=CT_PROTOCOL_5) then begin
  676. TStreamOp.WriteAnsiString(Stream,FData.new_data);
  677. end;
  678. if (ProtocolVersion<=CT_PROTOCOL_3) then begin
  679. Stream.Position := 0;
  680. setlength(Result,Stream.Size);
  681. Stream.ReadBuffer(Result[Low(Result)],Stream.Size);
  682. end else begin
  683. b := OpType;
  684. Stream.Write(b,1);
  685. Result := TCrypto.DoSha256(Stream.Memory,Stream.Size);
  686. end;
  687. finally
  688. Stream.Free;
  689. end;
  690. end;
  691. { TOpTransaction }
  692. procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
  693. begin
  694. list.Add(FData.sender);
  695. list.Add(FData.target);
  696. if (FData.opTransactionStyle in [transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap]) then begin
  697. list.Add(FData.SellerAccount);
  698. end;
  699. end;
  700. constructor TOpTransaction.CreateTransaction(ACurrentProtocol : Word;
  701. sender, n_operation, target: Cardinal;
  702. key: TECPrivateKey; amount, fee: UInt64; const payload: TOperationPayload);
  703. begin
  704. inherited Create(ACurrentProtocol);
  705. FData.sender := sender;
  706. FData.n_operation := n_operation;
  707. FData.target := target;
  708. FData.amount := amount;
  709. FData.fee := fee;
  710. FData.payload := payload;
  711. // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
  712. // FData.public_key := key.PublicKey;
  713. if Assigned(key) then begin
  714. FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
  715. FHasValidSignature := true;
  716. FUsedPubkeyForSignature := key.PublicKey;
  717. end else begin
  718. TLog.NewLog(ltdebug,Classname,'No key for signing a new Transaction');
  719. FHasValidSignature := false;
  720. end;
  721. end;
  722. function TOpTransaction.DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean;
  723. Var s_new, t_new : Int64;
  724. LTotalAmount : Cardinal;
  725. LSender,LTarget,LSeller : TAccount;
  726. LRecipientSignable, LIsCoinSwap : Boolean;
  727. LCurrentBlock, LSafeboxCurrentProtocol : Integer;
  728. LBuyAccountNewPubkey : TAccountKey;
  729. begin
  730. Result := false;
  731. AErrors := '';
  732. LCurrentBlock := ASafeBoxTransaction.FreezedSafeBox.BlocksCount;
  733. LSafeboxCurrentProtocol := ASafeboxTransaction.FreezedSafeBox.CurrentProtocol;
  734. {$region 'Common Validation'}
  735. if (FData.sender>=ASafeBoxTransaction.FreezedSafeBox.AccountsCount) then begin
  736. AErrors := Format('Invalid sender %d',[FData.sender]);
  737. Exit;
  738. end;
  739. if (FData.target>=ASafeBoxTransaction.FreezedSafeBox.AccountsCount) then begin
  740. AErrors := Format('Invalid target %d',[FData.target]);
  741. Exit;
  742. end;
  743. if TAccountComp.IsAccountBlockedByProtocol(FData.sender,LCurrentBlock) then begin
  744. AErrors := Format('sender (%d) is blocked for protocol',[FData.sender]);
  745. Exit;
  746. end;
  747. if TAccountComp.IsAccountBlockedByProtocol(FData.target,LCurrentBlock) then begin
  748. AErrors := Format('target (%d) is blocked for protocol',[FData.target]);
  749. Exit;
  750. end;
  751. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  752. AErrors := Format('Invalid fee %d (max %d)',[FData.fee,CT_MaxTransactionFee]);
  753. Exit;
  754. end;
  755. if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
  756. AErrors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
  757. If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
  758. Exit; // BUG from protocol 1
  759. end;
  760. end;
  761. LSender := ASafeBoxTransaction.Account(FData.sender);
  762. LTarget := ASafeBoxTransaction.Account(FData.target);
  763. // V5 - Allow recipient-signed OP_BUY operations. This is defined as
  764. // - Sender Account = Target Account
  765. // - Account (sender = target) is for PRIVATE SALE or ACCOUNT SWAP
  766. // - TIME LOCK not expired
  767. LRecipientSignable :=
  768. ( FData.opTransactionStyle = buy_Account )
  769. And (TAccountComp.IsOperationRecipientSignable(LSender, LTarget, LCurrentBlock, LSafeboxCurrentProtocol));
  770. LIsCoinSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)
  771. And (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LSafeboxCurrentProtocol, FData.payload.payload_raw));
  772. if (FData.sender=FData.target) AND (NOT LRecipientSignable) then begin
  773. AErrors := Format('Sender=Target and Target is not recipient-signable. Account: %d',[FData.sender]);
  774. Exit;
  775. end;
  776. if (LRecipientSignable Or LIsCoinSwap) then begin
  777. IF ((FData.amount < 0) or (FData.amount>CT_MaxTransactionAmount)) then begin
  778. AErrors := Format('Recipient-signed transaction had invalid amount %d. Must be within 0 or %d.)',[FData.amount,CT_MaxTransactionAmount]);
  779. Exit;
  780. end
  781. end else if((FData.amount<=0) Or (FData.amount>CT_MaxTransactionAmount)) then begin
  782. AErrors := Format('Invalid amount %d (1 or max: %d)',[FData.amount,CT_MaxTransactionAmount]);
  783. Exit;
  784. end;
  785. if ((LSender.n_operation+1)<>FData.n_operation) then begin
  786. AErrors := Format('Invalid n_operation %d (expected %d)',[FData.n_operation,LSender.n_operation+1]);
  787. Exit;
  788. end;
  789. LTotalAmount := FData.amount + FData.fee;
  790. if (LSender.balance<LTotalAmount) then begin
  791. AErrors := Format('Insufficient sender funds %d < (%d + %d = %d)',[LSender.balance,FData.amount,FData.fee,LTotalAmount]);
  792. Exit;
  793. end;
  794. if (LTarget.balance+FData.amount>CT_MaxWalletAmount) then begin
  795. AErrors := Format('Target cannot accept this transaction due to max amount %d+%d=%d > %d',[LTarget.balance,FData.amount,LTarget.balance+FData.amount,CT_MaxWalletAmount]);
  796. Exit;
  797. end;
  798. // Is locked? Protocol 2 check
  799. if (NOT LRecipientSignable) AND (TAccountComp.IsAccountLocked(LSender.accountInfo,LCurrentBlock)) then begin
  800. AErrors := 'Sender Account is currently locked';
  801. exit;
  802. end;
  803. // Build 1.4
  804. If (NOT TAccountComp.IsNullAccountKey(FData.public_key)) And (NOT TAccountComp.EqualAccountKeys(FData.public_key,LSender.accountInfo.accountkey)) then begin
  805. AErrors := Format('Invalid sender public key for account %d. Distinct from SafeBox public key! %s <> %s',[
  806. FData.sender,
  807. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
  808. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(LSender.accountInfo.accountkey))]);
  809. exit;
  810. end;
  811. // Check signature
  812. if LRecipientSignable AND (NOT IsValidECDSASignature(LSender.accountInfo.new_publicKey, FData.sign)) then begin
  813. AErrors := 'Invalid recipient-signed ECDSA signature';
  814. Exit;
  815. end else If (NOT LRecipientSignable) AND (NOT IsValidECDSASignature(LSender.accountInfo.accountkey, FData.sign)) then begin
  816. AErrors := 'Invalid ECDSA signature';
  817. Exit;
  818. end;
  819. {$endregion}
  820. // Is buy account ?
  821. if (FData.opTransactionStyle = buy_Account ) then begin
  822. {$region 'Buy Account Validation'}
  823. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  824. AErrors := 'Buy account is not allowed on Protocol 1';
  825. exit;
  826. end;
  827. if (TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)) then begin
  828. AErrors := 'Atomic coin swap cannot be made purchasing, use standard tx instead';
  829. Exit;
  830. end;
  831. if (LSafeboxCurrentProtocol < CT_PROTOCOL_5) then begin
  832. if (TAccountComp.IsAccountForSwap(LTarget.accountInfo)) then begin
  833. AErrors := 'Atomic swaps are not allowed until Protocol 5';
  834. exit;
  835. end;
  836. end else begin
  837. if (Not TAccountComp.IsAccountForPublicSale(LTarget.accountInfo)) then begin
  838. // On V5 cannot BUY accounts with time-lock EXPIRED (private sale or Swaps)
  839. if (Not TAccountComp.IsAccountLocked(LTarget.accountInfo,LCurrentBlock)) then begin
  840. AErrors := Format('Target %s time lock expired on block %d (Current %d)',
  841. [TAccountComp.AccountNumberToAccountTxtNumber(LTarget.account),
  842. LTarget.accountInfo.locked_until_block,
  843. LCurrentBlock]);
  844. Exit;
  845. end;
  846. end;
  847. end;
  848. LSeller := ASafeBoxTransaction.Account(FData.SellerAccount);
  849. if Not TAccountComp.IsAccountForSaleOrSwap(LTarget.accountInfo) then begin
  850. AErrors := Format('%d is not for sale or swap',[LTarget.account]);
  851. exit;
  852. end;
  853. // Check that seller is the expected seller
  854. If (LTarget.accountInfo.account_to_pay<>LSeller.account) then begin
  855. AErrors := Format('Seller account %d is not expected account %d',[FData.SellerAccount,LTarget.accountInfo.account_to_pay]);
  856. exit;
  857. end;
  858. if (LSender.account = LTarget.account) then begin
  859. // Self signed operation, amount is not used because has no effect
  860. if (LSender.balance + FData.fee) < LTarget.accountInfo.price then begin
  861. AErrors := Format('Self signed Account %d balance (%d) + fee (%d) < target price (%d)',[LTarget.account,LTarget.balance,FData.fee,LTarget.accountInfo.price]);
  862. exit;
  863. end;
  864. end else begin
  865. if (LTarget.balance + FData.amount) < LTarget.accountInfo.price then begin
  866. AErrors := Format('Target Account %d balance (%d) + amount (%d) < target price (%d)',[LTarget.account,LTarget.balance,FData.amount,LTarget.accountInfo.price]);
  867. exit;
  868. end;
  869. end;
  870. if (FData.AccountPrice<>LTarget.accountInfo.price) then begin
  871. AErrors := Format('Signed price (%d) is not the same of account price (%d)',[FData.AccountPrice,LTarget.accountInfo.price]);
  872. exit;
  873. end;
  874. if NOT TAccountComp.IsValidNewAccountKey(LTarget.accountInfo, FData.new_accountkey, LSafeboxCurrentProtocol) then begin
  875. AErrors := Format('Specified new public key for %d does not equal (or is not valid) the new public key stored in account: %s <> %s',
  876. [LTarget.account,
  877. TAccountComp.AccountKey2RawString(LTarget.accountInfo.new_publicKey).ToHexaString,
  878. TAccountComp.AccountKey2RawString(FData.new_accountkey).ToHexaString]);
  879. exit;
  880. end;
  881. If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,AErrors)) then exit;
  882. LBuyAccountNewPubkey := FData.new_accountkey;
  883. {$endregion}
  884. end else if // (is auto buy) OR (is transaction that can buy)
  885. (
  886. (FData.opTransactionStyle in [transaction,transaction_with_auto_buy_account,transaction_with_auto_atomic_swap]) AND
  887. (LSafeboxCurrentProtocol >= CT_PROTOCOL_2) AND
  888. (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LSafeboxCurrentProtocol, FData.payload.payload_raw)) AND
  889. ((LTarget.balance + FData.amount >= LTarget.accountInfo.price))
  890. ) then begin
  891. {$region 'Transaction Auto Buy Validation'}
  892. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  893. AErrors := 'Tx-Buy account is not allowed on Protocol 1';
  894. exit;
  895. end;
  896. If (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
  897. if (TAccountComp.IsAccountForSwap( LTarget.accountInfo )) then begin
  898. AErrors := 'Tx-Buy atomic swaps are not allowed until Protocol 5';
  899. exit;
  900. end else begin
  901. // the below line was a bug fix that introduced a new bug, and is retained here for
  902. // V2-V4 consistency
  903. //------
  904. if Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,AErrors)) then exit;
  905. //------
  906. end;
  907. end;
  908. // Check that stored "new_publicKey" is valid (when not in coin swap)
  909. if (Not TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)) and
  910. (Not (TAccountComp.IsValidAccountKey(LTarget.accountInfo.new_publicKey,LSafeboxCurrentProtocol,AErrors))) then exit;
  911. // NOTE: This is a Transaction opereation (not a buy account operation) that
  912. // has some "added" effects (private sale, swap...)
  913. // in order to Store at the blockchain file we will fill this fields not specified
  914. // on a transaction (otherwise JSON-RPC calls will not be abble to know what
  915. // happened in this transaction as extra effect):
  916. //
  917. // FData.opTransactionStyle: TOpTransactionStyle;
  918. // FData.AccountPrice : UInt64;
  919. // FData.SellerAccount : Cardinal;
  920. // FData.new_accountkey : TAccountKey;
  921. // Fill the purchase data
  922. if TAccountComp.IsAccountForSale( LTarget.accountInfo ) then begin
  923. FData.opTransactionStyle := transaction_with_auto_buy_account; // Set this data!
  924. end else begin
  925. FData.opTransactionStyle := transaction_with_auto_atomic_swap; // Set this data!
  926. end;
  927. FData.AccountPrice := LTarget.accountInfo.price;
  928. FData.SellerAccount := LTarget.accountInfo.account_to_pay;
  929. LSeller := ASafeBoxTransaction.Account(LTarget.accountInfo.account_to_pay);
  930. if TAccountComp.IsAccountForCoinSwap( LTarget.accountInfo ) then begin
  931. // We will save extra info that account key has not changed
  932. FData.new_accountkey := CT_TECDSA_Public_Nul;
  933. // COIN SWAP: Ensure public key will not change and will be the same
  934. LBuyAccountNewPubkey := LTarget.accountInfo.accountKey;
  935. end else begin
  936. FData.new_accountkey := LTarget.accountInfo.new_publicKey;
  937. LBuyAccountNewPubkey := LTarget.accountInfo.new_publicKey;
  938. end;
  939. {$endregion}
  940. end else if (FData.opTransactionStyle <> transaction) then begin
  941. AErrors := 'INTERNAL ERROR: 477C2A3C53C34E63A6B82C057741C44D';
  942. exit;
  943. end;
  944. if (FData.opTransactionStyle in [buy_account, transaction_with_auto_buy_account, transaction_with_auto_atomic_swap]) then begin
  945. // account purchase
  946. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  947. AErrors := 'NOT ALLOWED ON PROTOCOL 1';
  948. exit;
  949. end;
  950. if (LTarget.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) AND
  951. (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
  952. AErrors := 'NOT ALLOWED UNTIL PROTOCOL 5';
  953. exit;
  954. end;
  955. Result := ASafeBoxTransaction.BuyAccount(
  956. APrevious,
  957. GetOpID,
  958. LSender.account,
  959. LTarget.account,
  960. LSeller.account,
  961. FData.n_operation,
  962. FData.amount,
  963. LTarget.accountInfo.price,
  964. FData.fee,
  965. LBuyAccountNewPubkey,
  966. FData.payload.payload_raw,
  967. LRecipientSignable,
  968. AErrors
  969. );
  970. end else begin
  971. // Standard transaction
  972. Result := ASafeBoxTransaction.TransferAmount(
  973. APrevious,
  974. GetOpID,
  975. FData.sender,
  976. FData.sender,
  977. FData.target,
  978. FData.n_operation,
  979. FData.amount,
  980. FData.fee,AErrors
  981. );
  982. end;
  983. end;
  984. function TOpTransaction.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
  985. Var ms : TMemoryStream;
  986. begin
  987. If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
  988. else begin
  989. ms := TMemoryStream.Create;
  990. try
  991. ms.Write(FData.sender,Sizeof(FData.sender));
  992. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  993. ms.Write(FData.target,Sizeof(FData.target));
  994. ms.Write(FData.amount,Sizeof(FData.amount));
  995. ms.Write(FData.fee,Sizeof(FData.fee));
  996. if Length(FData.payload.payload_raw)>0 then
  997. ms.WriteBuffer(FData.payload.payload_raw[Low(FData.payload.payload_raw)],Length(FData.payload.payload_raw));
  998. ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  999. if Length(FData.public_key.x)>0 then
  1000. ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
  1001. if Length(FData.public_key.y)>0 then
  1002. ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
  1003. if Length(FData.sign.r)>0 then
  1004. ms.WriteBuffer(FData.sign.r[Low(FData.sign.r)],Length(FData.sign.r));
  1005. if Length(FData.sign.s)>0 then
  1006. ms.WriteBuffer(FData.sign.s[Low(FData.sign.s)],Length(FData.sign.s));
  1007. SetLength(Result,ms.Size);
  1008. ms.Position := 0;
  1009. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  1010. finally
  1011. ms.Free;
  1012. end;
  1013. end;
  1014. end;
  1015. procedure TOpTransaction.InitializeData;
  1016. begin
  1017. inherited;
  1018. FData := CT_TOpTransactionData_NUL;
  1019. end;
  1020. function TOpTransaction.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  1021. var LAccount : TAccount;
  1022. begin
  1023. LAccount := ASafeBoxTransaction.Account(FData.sender);
  1024. Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
  1025. end;
  1026. function TOpTransaction.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
  1027. var b : Byte;
  1028. begin
  1029. Result := false;
  1030. if Stream.Size-Stream.Position < 28 then exit; // Invalid stream
  1031. Stream.Read(FData.sender,Sizeof(FData.sender));
  1032. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  1033. Stream.Read(FData.target,Sizeof(FData.target));
  1034. Stream.Read(FData.amount,Sizeof(FData.amount));
  1035. Stream.Read(FData.fee,Sizeof(FData.fee));
  1036. if Not LoadOperationPayloadFromStream(Stream,FData.payload) then Exit;
  1037. if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
  1038. if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
  1039. if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
  1040. if ((LoadExtendedData) Or (Self is TOpBuyAccount)) then begin
  1041. If Stream.Read(b,1)<>1 then begin
  1042. exit;
  1043. end;
  1044. case b of
  1045. 0 : FData.opTransactionStyle := transaction;
  1046. 1 : FData.opTransactionStyle := transaction_with_auto_buy_account;
  1047. 2 : Begin
  1048. FData.opTransactionStyle := buy_account;
  1049. if (Not (Self is TOpBuyAccount)) then exit;
  1050. End;
  1051. 3 : FData.opTransactionStyle := transaction_with_auto_atomic_swap;
  1052. else exit;
  1053. end;
  1054. if ((Self is TOpBuyAccount) and (FData.opTransactionStyle<>buy_account)) or
  1055. ((Not (Self is TOpBuyAccount)) and (FData.opTransactionStyle=buy_account)) then begin
  1056. // Protection invalid case added 20190705
  1057. Exit;
  1058. end;
  1059. if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account,transaction_with_auto_atomic_swap]) then begin
  1060. Stream.Read(FData.AccountPrice,SizeOf(FData.AccountPrice));
  1061. Stream.Read(FData.SellerAccount,SizeOf(FData.SellerAccount));
  1062. if Stream.Read(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID))<0 then exit;
  1063. if TStreamOp.ReadAnsiString(Stream,FData.new_accountkey.x)<0 then exit;
  1064. if TStreamOp.ReadAnsiString(Stream,FData.new_accountkey.y)<0 then exit;
  1065. end;
  1066. end;
  1067. if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
  1068. if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
  1069. Result := true;
  1070. end;
  1071. procedure TOpTransaction.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  1072. begin
  1073. inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
  1074. SetLength(OperationResume.Senders,1);
  1075. OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
  1076. OperationResume.Senders[0].Account:=FData.sender;
  1077. OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee);
  1078. OperationResume.Senders[0].N_Operation:=FData.n_operation;
  1079. OperationResume.Senders[0].Payload:=FData.payload;
  1080. OperationResume.Senders[0].Signature:=FData.sign;
  1081. case FData.opTransactionStyle of
  1082. transaction : begin
  1083. SetLength(OperationResume.Receivers,1);
  1084. OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
  1085. OperationResume.Receivers[0].Account:=FData.target;
  1086. OperationResume.Receivers[0].Amount:=FData.amount;
  1087. OperationResume.Receivers[0].Payload:=FData.payload;
  1088. end;
  1089. buy_account, transaction_with_auto_buy_account, transaction_with_auto_atomic_swap : begin
  1090. SetLength(OperationResume.Receivers,2);
  1091. OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
  1092. OperationResume.Receivers[0].Account:=FData.target;
  1093. OperationResume.Receivers[0].Amount:= (FData.amount - FData.AccountPrice);
  1094. OperationResume.Receivers[0].Payload:=FData.payload;
  1095. OperationResume.Receivers[1] := CT_TMultiOpReceiver_NUL;
  1096. OperationResume.Receivers[1].Account:=FData.SellerAccount;
  1097. OperationResume.Receivers[1].Amount:= FData.AccountPrice;
  1098. OperationResume.Receivers[1].Payload:=FData.payload;
  1099. if (Not TAccountComp.IsNullAccountKey(FData.new_accountkey)) then begin
  1100. SetLength(OperationResume.Changers,1);
  1101. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  1102. OperationResume.Changers[0].Account := FData.target;
  1103. OperationResume.Changers[0].Changes_type := [public_key];
  1104. OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
  1105. end;
  1106. end;
  1107. end;
  1108. end;
  1109. function TOpTransaction.OperationAmount: Int64;
  1110. begin
  1111. Result := FData.amount;
  1112. end;
  1113. function TOpTransaction.OperationFee: Int64;
  1114. begin
  1115. Result := FData.fee;
  1116. end;
  1117. function TOpTransaction.OperationPayload: TOperationPayload;
  1118. begin
  1119. Result := FData.payload;
  1120. end;
  1121. class function TOpTransaction.OpType: Byte;
  1122. begin
  1123. Result := CT_Op_Transaction;
  1124. end;
  1125. function TOpTransaction.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
  1126. Var b : Byte;
  1127. begin
  1128. Stream.Write(FData.sender,Sizeof(FData.sender));
  1129. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  1130. Stream.Write(FData.target,Sizeof(FData.target));
  1131. Stream.Write(FData.amount,Sizeof(FData.amount));
  1132. Stream.Write(FData.fee,Sizeof(FData.fee));
  1133. SaveOperationPayloadToStream(Stream,FData.payload);
  1134. Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  1135. TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
  1136. TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
  1137. if ((SaveExtendedData) Or (Self is TOpBuyAccount)) then begin
  1138. case FData.opTransactionStyle of
  1139. transaction : b:=0;
  1140. transaction_with_auto_buy_account : b:=1;
  1141. buy_account : b:=2;
  1142. transaction_with_auto_atomic_swap : b:=3;
  1143. else raise Exception.Create('ERROR DEV 20170424-1');
  1144. end;
  1145. Stream.Write(b,1);
  1146. if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account,transaction_with_auto_atomic_swap]) then begin
  1147. Stream.Write(FData.AccountPrice,SizeOf(FData.AccountPrice));
  1148. Stream.Write(FData.SellerAccount,SizeOf(FData.SellerAccount));
  1149. Stream.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
  1150. TStreamOp.WriteAnsiString(Stream,FData.new_accountkey.x);
  1151. TStreamOp.WriteAnsiString(Stream,FData.new_accountkey.y);
  1152. end;
  1153. end;
  1154. TStreamOp.WriteAnsiString(Stream,FData.sign.r);
  1155. TStreamOp.WriteAnsiString(Stream,FData.sign.s);
  1156. Result := true;
  1157. end;
  1158. function TOpTransaction.SignerAccount: Cardinal;
  1159. begin
  1160. Result := FData.sender;
  1161. end;
  1162. function TOpTransaction.DestinationAccount: Int64;
  1163. begin
  1164. Result:=FData.target;
  1165. end;
  1166. function TOpTransaction.SellerAccount: Int64;
  1167. begin
  1168. Case FData.opTransactionStyle of
  1169. transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap : Result := FData.SellerAccount;
  1170. else Result:=inherited SellerAccount;
  1171. end;
  1172. end;
  1173. function TOpTransaction.N_Operation: Cardinal;
  1174. begin
  1175. Result := FData.n_operation;
  1176. end;
  1177. function TOpTransaction.OperationAmountByAccount(account: Cardinal): Int64;
  1178. begin
  1179. Result := 0;
  1180. If (FData.sender = account) then inc(Result, Int64(FData.amount+FData.fee) * (-1));
  1181. If (FData.target = account) then begin
  1182. if (FData.opTransactionStyle in [buy_account,transaction_with_auto_buy_account]) then inc(Result, FData.amount - FData.AccountPrice)
  1183. else inc(Result,FData.amount);
  1184. end;
  1185. If ((FData.SellerAccount = account) And (FData.opTransactionStyle in [buy_account,transaction_with_auto_buy_account] )) then begin
  1186. inc(Result, FData.AccountPrice)
  1187. end;
  1188. end;
  1189. function TOpTransaction.toString: String;
  1190. begin
  1191. case FData.opTransactionStyle of
  1192. transaction :
  1193. Result := Format('Transaction from %s to %s amount:%s fee:%s (n_op:%d) payload size:%d payload:%s',[
  1194. TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
  1195. TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
  1196. TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw),
  1197. TCrypto.ToHexaString(FData.payload.payload_raw)]);
  1198. transaction_with_auto_buy_account :
  1199. Result := Format('Transaction/Buy account %s by %s paying %s to %s amount:%s fee:%s (n_op:%d) payload size:%d payload:%s',[
  1200. TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
  1201. TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
  1202. TAccountComp.FormatMoney(FData.AccountPrice), TAccountComp.AccountNumberToAccountTxtNumber(FData.SellerAccount),
  1203. TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw),
  1204. TCrypto.ToHexaString(FData.payload.payload_raw)]);
  1205. buy_account :
  1206. Result := Format('Buy account %s by %s paying %s to %s amount:%s fee:%s (n_op:%d) payload size:%d payload:%s',[
  1207. TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
  1208. TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
  1209. TAccountComp.FormatMoney(FData.AccountPrice), TAccountComp.AccountNumberToAccountTxtNumber(FData.SellerAccount),
  1210. TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw),
  1211. TCrypto.ToHexaString(FData.payload.payload_raw)]);
  1212. else raise Exception.Create('ERROR DEV 20170424-2');
  1213. end;
  1214. end;
  1215. function TOpTransaction.GetDigestToSign: TRawBytes;
  1216. Var ms : TMemoryStream;
  1217. b : Byte;
  1218. begin
  1219. ms := TMemoryStream.Create;
  1220. try
  1221. ms.Write(FData.sender,Sizeof(FData.sender));
  1222. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  1223. ms.Write(FData.target,Sizeof(FData.target));
  1224. ms.Write(FData.amount,Sizeof(FData.amount));
  1225. ms.Write(FData.fee,Sizeof(FData.fee));
  1226. if ProtocolVersion>=CT_PROTOCOL_5 then begin
  1227. ms.Write(FData.payload.payload_type,SizeOf(FData.payload.payload_type));
  1228. end;
  1229. if Length(FData.payload.payload_raw)>0 then
  1230. ms.WriteBuffer(FData.payload.payload_raw[Low(FData.payload.payload_raw)],Length(FData.payload.payload_raw));
  1231. ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  1232. if Length(FData.public_key.x)>0 then
  1233. ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
  1234. if Length(FData.public_key.y)>0 then
  1235. ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
  1236. if FData.opTransactionStyle=buy_account then begin
  1237. ms.Write(FData.AccountPrice,Sizeof(FData.AccountPrice));
  1238. ms.Write(FData.SellerAccount,Sizeof(FData.SellerAccount));
  1239. ms.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
  1240. if Length(FData.new_accountkey.x)>0 then
  1241. ms.WriteBuffer(FData.new_accountkey.x[Low(FData.new_accountkey.x)],Length(FData.new_accountkey.x));
  1242. if Length(FData.new_accountkey.y)>0 then
  1243. ms.WriteBuffer(FData.new_accountkey.y[Low(FData.new_accountkey.y)],Length(FData.new_accountkey.y));
  1244. end;
  1245. if (ProtocolVersion<=CT_PROTOCOL_3) then begin
  1246. ms.Position := 0;
  1247. SetLength(Result,ms.Size);
  1248. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  1249. end else begin
  1250. b := OpType;
  1251. ms.Write(b,1);
  1252. Result := TCrypto.DoSha256(ms.Memory,ms.Size);
  1253. end;
  1254. finally
  1255. ms.Free;
  1256. end;
  1257. end;
  1258. { TOpChangeKey }
  1259. procedure TOpChangeKey.AffectedAccounts(list: TList<Cardinal>);
  1260. begin
  1261. list.Add(FData.account_signer);
  1262. if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
  1263. end;
  1264. function TOpChangeKey.OperationAmountByAccount(account: Cardinal): Int64;
  1265. begin
  1266. if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
  1267. else Result := 0;
  1268. end;
  1269. constructor TOpChangeKey.Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
  1270. begin
  1271. inherited Create(ACurrentProtocol);
  1272. FData.account_signer := account_signer;
  1273. FData.account_target := account_target;
  1274. If (OpType=CT_Op_Changekey) then begin
  1275. If (account_signer<>account_target) then Raise Exception.Create('ERROR DEV 20170530-4');
  1276. end else if (OpType=CT_Op_ChangeKeySigned) then begin
  1277. // Allowed signer<>target
  1278. end else Raise Exception.Create('ERROR DEV 20170530-5');
  1279. FData.n_operation := n_operation;
  1280. FData.fee := fee;
  1281. FData.payload := payload;
  1282. // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
  1283. // FData.public_key := key.PublicKey;
  1284. FData.new_accountkey := new_account_key;
  1285. if Assigned(key) then begin
  1286. FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
  1287. FHasValidSignature := true;
  1288. FUsedPubkeyForSignature := key.PublicKey;
  1289. end else begin
  1290. TLog.NewLog(ltdebug,Classname,'No key for signing a new Change key');
  1291. FHasValidSignature := false;
  1292. end;
  1293. end;
  1294. function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
  1295. Var account_signer, account_target : TAccount;
  1296. LSafeboxCurrentProtocol : Integer;
  1297. begin
  1298. Result := false;
  1299. LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
  1300. if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  1301. errors := 'Invalid account number';
  1302. Exit;
  1303. end;
  1304. if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1305. errors := 'account is blocked for protocol';
  1306. Exit;
  1307. end;
  1308. If (FData.account_signer<>FData.account_target) then begin
  1309. if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  1310. errors := 'Invalid account target number';
  1311. Exit;
  1312. end;
  1313. if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1314. errors := 'Target account is blocked for protocol';
  1315. Exit;
  1316. end;
  1317. end;
  1318. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  1319. errors := 'Invalid fee: '+Inttostr(FData.fee);
  1320. exit;
  1321. end;
  1322. account_signer := AccountTransaction.Account(FData.account_signer);
  1323. account_target := AccountTransaction.Account(FData.account_target);
  1324. if ((account_signer.n_operation+1)<>FData.n_operation) then begin
  1325. errors := 'Invalid n_operation';
  1326. Exit;
  1327. end;
  1328. if (account_signer.balance<FData.fee) then begin
  1329. errors := 'Insuficient founds';
  1330. exit;
  1331. end;
  1332. if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
  1333. errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
  1334. If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
  1335. Exit; // BUG from protocol 1
  1336. end;
  1337. end;
  1338. // Is locked? Protocol 2 check
  1339. if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  1340. errors := 'Account signer is currently locked';
  1341. exit;
  1342. end;
  1343. If Not TAccountComp.IsValidAccountKey( FData.new_accountkey,LSafeboxCurrentProtocol,errors) then begin
  1344. exit;
  1345. end;
  1346. // NEW v2 protocol protection: Does not allow to change key for same key
  1347. if (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
  1348. if (TAccountComp.EqualAccountKeys(account_target.accountInfo.accountKey,FData.new_accountkey)) then begin
  1349. errors := 'New public key is the same public key';
  1350. exit;
  1351. end;
  1352. end;
  1353. // Build 1.4
  1354. If (FData.public_key.EC_OpenSSL_NID<>CT_TECDSA_Public_Nul.EC_OpenSSL_NID) And (Not TAccountComp.EqualAccountKeys(FData.public_key,account_signer.accountInfo.accountkey)) then begin
  1355. errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
  1356. FData.account_signer,
  1357. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
  1358. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
  1359. exit;
  1360. end;
  1361. If (FData.account_signer<>FData.account_target) then begin
  1362. if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  1363. errors := 'Account target is currently locked';
  1364. exit;
  1365. end;
  1366. // Check have same public key
  1367. If Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
  1368. errors := 'Signer and target accounts have different public key';
  1369. exit;
  1370. end;
  1371. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  1372. errors := 'NOT ALLOWED ON PROTOCOL 1';
  1373. exit;
  1374. end;
  1375. end;
  1376. // Check signature
  1377. If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
  1378. errors := 'Invalid ECDSA signature';
  1379. Exit;
  1380. end;
  1381. account_target.accountInfo.accountKey := FData.new_accountkey;
  1382. // Set to normal:
  1383. account_target.accountInfo.state := as_Normal;
  1384. account_target.accountInfo.locked_until_block := 0;
  1385. account_target.accountInfo.price := 0;
  1386. account_target.accountInfo.account_to_pay := 0;
  1387. account_target.accountInfo.new_publicKey := CT_TECDSA_Public_Nul;
  1388. Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
  1389. GetOpID,
  1390. FData.account_signer,FData.n_operation,FData.account_target,
  1391. account_target.accountInfo,
  1392. account_target.name,
  1393. account_target.account_data,
  1394. account_target.account_type,
  1395. FData.fee,errors);
  1396. end;
  1397. function TOpChangeKey.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
  1398. var ms : TMemoryStream;
  1399. raw : TRawBytes;
  1400. begin
  1401. If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
  1402. else begin
  1403. ms := TMemoryStream.Create;
  1404. try
  1405. ms.Write(FData.account_signer,Sizeof(FData.account_signer)); //Protocol 1 does not allow signer/target. signer=target always
  1406. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  1407. ms.Write(FData.fee,Sizeof(FData.fee));
  1408. if Length(FData.payload.payload_raw)>0 then
  1409. ms.WriteBuffer(FData.payload.payload_raw[Low(FData.payload.payload_raw)],Length(FData.payload.payload_raw));
  1410. ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  1411. if Length(FData.public_key.x)>0 then
  1412. ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
  1413. if Length(FData.public_key.y)>0 then
  1414. ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
  1415. raw := TAccountComp.AccountKey2RawString(FData.new_accountkey);
  1416. if Length(raw)>0 then
  1417. ms.WriteBuffer(raw[Low(raw)],Length(raw));
  1418. if Length(FData.sign.r)>0 then
  1419. ms.WriteBuffer(FData.sign.r[Low(FData.sign.r)],Length(FData.sign.r));
  1420. if Length(FData.sign.s)>0 then
  1421. ms.WriteBuffer(FData.sign.s[Low(FData.sign.s)],Length(FData.sign.s));
  1422. ms.Position := 0;
  1423. setlength(Result,ms.Size);
  1424. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  1425. finally
  1426. ms.Free;
  1427. end;
  1428. end;
  1429. end;
  1430. procedure TOpChangeKey.InitializeData(AProtocolVersion : Word);
  1431. begin
  1432. inherited InitializeData(AProtocolVersion);
  1433. FData := CT_TOpChangeKeyData_NUL;
  1434. end;
  1435. function TOpChangeKey.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  1436. var LAccount : TAccount;
  1437. begin
  1438. LAccount := ASafeBoxTransaction.Account(FData.account_signer);
  1439. Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
  1440. end;
  1441. function TOpChangeKey.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
  1442. var raw : TRawBytes;
  1443. begin
  1444. Result := false;
  1445. if Stream.Size-Stream.Position < 16 then exit; // Invalid stream
  1446. Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
  1447. If (OpType=CT_Op_ChangeKey) then begin
  1448. FData.account_target:=FData.account_signer;
  1449. end else if (OpType=CT_Op_ChangeKeySigned) then begin
  1450. Stream.Read(FData.account_target,Sizeof(FData.account_target));
  1451. end else Raise Exception.Create('ERROR DEV 20170530-1');
  1452. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  1453. Stream.Read(FData.fee,Sizeof(FData.fee));
  1454. if Not LoadOperationPayloadFromStream(Stream,FData.payload) then Exit;
  1455. if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
  1456. if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
  1457. if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
  1458. if TStreamOp.ReadAnsiString(Stream,raw)<0 then exit;
  1459. FData.new_accountkey := TAccountComp.RawString2Accountkey(raw);
  1460. if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
  1461. if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
  1462. Result := true;
  1463. end;
  1464. procedure TOpChangeKey.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  1465. begin
  1466. inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
  1467. SetLength(OperationResume.Changers,1);
  1468. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  1469. OperationResume.Changers[0].Account := FData.account_target;
  1470. OperationResume.Changers[0].Changes_type := [public_key];
  1471. OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
  1472. if (FData.account_signer=FData.account_target) then begin
  1473. OperationResume.Changers[0].N_Operation := FData.n_operation;
  1474. OperationResume.Changers[0].Fee := FData.fee;
  1475. OperationResume.Changers[0].Signature := FData.sign;
  1476. end else begin
  1477. SetLength(OperationResume.Changers,2);
  1478. OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
  1479. OperationResume.Changers[1].Account := FData.account_signer;
  1480. OperationResume.Changers[1].N_Operation := FData.n_operation;
  1481. OperationResume.Changers[1].Fee := FData.fee;
  1482. OperationResume.Changers[1].Signature := FData.sign;
  1483. end;
  1484. end;
  1485. function TOpChangeKey.OperationAmount: Int64;
  1486. begin
  1487. Result := 0;
  1488. end;
  1489. function TOpChangeKey.OperationFee: Int64;
  1490. begin
  1491. Result := FData.fee;
  1492. end;
  1493. function TOpChangeKey.OperationPayload: TOperationPayload;
  1494. begin
  1495. Result := FData.payload;
  1496. end;
  1497. class function TOpChangeKey.OpType: Byte;
  1498. begin
  1499. Result := CT_Op_Changekey;
  1500. end;
  1501. function TOpChangeKey.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
  1502. begin
  1503. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  1504. If (OpType=CT_Op_ChangeKey) then begin
  1505. If FData.account_target<>FData.account_signer then Raise Exception.Create('ERROR DEV 20170530-2');
  1506. end else if (OpType=CT_Op_ChangeKeySigned) then begin
  1507. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  1508. end else Raise Exception.Create('ERROR DEV 20170530-3');
  1509. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  1510. Stream.Write(FData.fee,Sizeof(FData.fee));
  1511. SaveOperationPayloadToStream(Stream,FData.payload);
  1512. Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  1513. TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
  1514. TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
  1515. TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FData.new_accountkey));
  1516. TStreamOp.WriteAnsiString(Stream,FData.sign.r);
  1517. TStreamOp.WriteAnsiString(Stream,FData.sign.s);
  1518. Result := true;
  1519. end;
  1520. function TOpChangeKey.SignerAccount: Cardinal;
  1521. begin
  1522. Result := FData.account_signer;
  1523. end;
  1524. function TOpChangeKey.DestinationAccount: Int64;
  1525. begin
  1526. Result := FData.account_target;
  1527. end;
  1528. function TOpChangeKey.N_Operation: Cardinal;
  1529. begin
  1530. Result := FData.n_operation;
  1531. end;
  1532. function TOpChangeKey.toString: String;
  1533. begin
  1534. Result := Format('Change key of %s to new key: %s fee:%s (n_op:%d) payload size:%d',[
  1535. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
  1536. TAccountComp.GetECInfoTxt(FData.new_accountkey.EC_OpenSSL_NID),
  1537. TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw)]);
  1538. end;
  1539. function TOpChangeKey.GetDigestToSign: TRawBytes;
  1540. var ms : TMemoryStream;
  1541. raw : TRawBytes;
  1542. b : Byte;
  1543. begin
  1544. ms := TMemoryStream.Create;
  1545. try
  1546. ms.Write(FData.account_signer,Sizeof(FData.account_signer));
  1547. if (FData.account_signer<>FData.account_target) then ms.Write(FData.account_target,Sizeof(FData.account_target));
  1548. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  1549. ms.Write(FData.fee,Sizeof(FData.fee));
  1550. if ProtocolVersion>=CT_PROTOCOL_5 then begin
  1551. ms.Write(FData.payload.payload_type,SizeOf(FData.payload.payload_type));
  1552. end;
  1553. if Length(FData.payload.payload_raw)>0 then
  1554. ms.WriteBuffer(FData.payload.payload_raw[Low(FData.payload.payload_raw)],Length(FData.payload.payload_raw));
  1555. ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  1556. if Length(FData.public_key.x)>0 then
  1557. ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
  1558. if Length(FData.public_key.y)>0 then
  1559. ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
  1560. raw := TAccountComp.AccountKey2RawString(FData.new_accountkey);
  1561. if Length(raw)>0 then
  1562. ms.WriteBuffer(raw[Low(raw)],Length(raw));
  1563. if (ProtocolVersion<=CT_PROTOCOL_3) then begin
  1564. ms.Position := 0;
  1565. SetLength(Result,ms.Size);
  1566. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  1567. end else begin
  1568. b := OpType;
  1569. ms.Write(b,1);
  1570. Result := TCrypto.DoSha256(ms.Memory,ms.Size);
  1571. end;
  1572. finally
  1573. ms.Free;
  1574. end;
  1575. end;
  1576. { TOpChangeKeySigned }
  1577. class function TOpChangeKeySigned.OpType: Byte;
  1578. begin
  1579. Result:=CT_Op_ChangeKeySigned;
  1580. end;
  1581. { TOpRecoverFounds }
  1582. procedure TOpRecoverFounds.AffectedAccounts(list: TList<Cardinal>);
  1583. begin
  1584. list.Add(FData.account);
  1585. end;
  1586. constructor TOpRecoverFounds.Create(ACurrentProtocol : word; account_number, n_operation : Cardinal; fee: UInt64; new_accountkey : TAccountKey);
  1587. begin
  1588. inherited Create(ACurrentProtocol);
  1589. FData.account := account_number;
  1590. FData.n_operation := n_operation;
  1591. FData.fee := fee;
  1592. FData.new_accountkey := new_accountkey;
  1593. FHasValidSignature := true; // Recover founds doesn't need a signature
  1594. end;
  1595. function TOpRecoverFounds.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
  1596. Var acc : TAccount;
  1597. LSafeboxCurrentProtocol : Integer;
  1598. begin
  1599. Result := false;
  1600. LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
  1601. if TAccountComp.IsAccountBlockedByProtocol(FData.account,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1602. errors := 'account is blocked for protocol';
  1603. Exit;
  1604. end;
  1605. acc := AccountTransaction.Account(FData.account);
  1606. if TAccountComp.IsAccountLocked(acc.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1607. errors := 'account is locked';
  1608. Exit;
  1609. end;
  1610. if (acc.updated_on_block_active_mode + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1611. errors := Format('Account is active to recover founds! Account %d Updated %d + %d >= BlockCount : %d',[FData.account,acc.updated_on_block_active_mode,CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
  1612. Exit;
  1613. end;
  1614. if (TAccountComp.AccountBlock(FData.account) + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1615. errors := Format('AccountBlock is active to recover founds! AccountBlock %d + %d >= BlockCount : %d',[TAccountComp.AccountBlock(FData.account),CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
  1616. Exit;
  1617. end;
  1618. if ((acc.n_operation+1)<>FData.n_operation) then begin
  1619. errors := 'Invalid n_operation';
  1620. Exit;
  1621. end;
  1622. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  1623. errors := 'Invalid fee '+Inttostr(FData.fee);
  1624. exit;
  1625. end;
  1626. if (acc.balance<FData.fee) then begin
  1627. errors := 'Insuficient funds';
  1628. exit;
  1629. end;
  1630. if Not TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,errors) then begin
  1631. Exit;
  1632. end;
  1633. Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
  1634. GetOpID,
  1635. FData.account,FData.n_operation, FData.account,
  1636. TAccountComp.RawString2AccountInfo( TAccountComp.AccountKey2RawString(FData.new_accountkey) ),
  1637. acc.name, acc.account_data, acc.account_type, FData.fee, errors);
  1638. end;
  1639. function TOpRecoverFounds.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
  1640. var ms : TMemoryStream;
  1641. begin
  1642. If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
  1643. else begin
  1644. ms := TMemoryStream.Create;
  1645. try
  1646. ms.Write(FData.account,Sizeof(FData.account));
  1647. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  1648. ms.Write(FData.fee,Sizeof(FData.fee));
  1649. TStreamOp.WriteAccountKey(ms,FData.new_accountkey);
  1650. ms.Position := 0;
  1651. SetLength(Result,ms.Size);
  1652. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  1653. finally
  1654. ms.Free;
  1655. end;
  1656. end;
  1657. end;
  1658. procedure TOpRecoverFounds.InitializeData;
  1659. begin
  1660. inherited;
  1661. FData := CT_TOpRecoverFoundsData_NUL;
  1662. end;
  1663. function TOpRecoverFounds.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  1664. begin
  1665. Result := True; // Nobody signs here
  1666. end;
  1667. function TOpRecoverFounds.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
  1668. begin
  1669. Result := false;
  1670. if Stream.Size - Stream.Position<16 then exit;
  1671. Stream.Read(FData.account,Sizeof(FData.account));
  1672. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  1673. Stream.Read(FData.fee,Sizeof(FData.fee));
  1674. if TStreamOp.ReadAccountKey(Stream,FData.new_accountkey)<0 then Exit;
  1675. Result := true;
  1676. end;
  1677. procedure TOpRecoverFounds.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  1678. begin
  1679. inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
  1680. SetLength(OperationResume.Changers,1);
  1681. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  1682. OperationResume.Changers[0].Account := FData.account;
  1683. OperationResume.Changers[0].Fee := FData.fee;
  1684. OperationResume.Changers[0].N_Operation := FData.n_operation;
  1685. OperationResume.Changers[0].Changes_type := [public_key];
  1686. OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
  1687. end;
  1688. function TOpRecoverFounds.OperationAmount: Int64;
  1689. begin
  1690. Result := 0;
  1691. end;
  1692. function TOpRecoverFounds.OperationFee: Int64;
  1693. begin
  1694. Result := FData.fee;
  1695. end;
  1696. function TOpRecoverFounds.OperationPayload: TOperationPayload;
  1697. begin
  1698. Result := CT_TOperationPayload_NUL;
  1699. end;
  1700. class function TOpRecoverFounds.OpType: Byte;
  1701. begin
  1702. Result := CT_Op_Recover;
  1703. end;
  1704. function TOpRecoverFounds.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
  1705. begin
  1706. Stream.Write(FData.account,Sizeof(FData.account));
  1707. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  1708. Stream.Write(FData.fee,Sizeof(FData.fee));
  1709. TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
  1710. Result := true;
  1711. end;
  1712. function TOpRecoverFounds.SignerAccount: Cardinal;
  1713. begin
  1714. Result := FData.account;
  1715. end;
  1716. function TOpRecoverFounds.N_Operation: Cardinal;
  1717. begin
  1718. Result := FData.n_operation;
  1719. end;
  1720. function TOpRecoverFounds.OperationAmountByAccount(account: Cardinal): Int64;
  1721. begin
  1722. if (FData.account = account) then Result := Int64(FData.fee)*(-1)
  1723. else Result := 0;
  1724. end;
  1725. function TOpRecoverFounds.toString: String;
  1726. begin
  1727. Result := Format('Recover account %s fee:%s (n_op:%d) set Public key to %s',[
  1728. TAccountComp.AccountNumberToAccountTxtNumber(FData.account),
  1729. TAccountComp.FormatMoney(FData.fee),fData.n_operation,
  1730. TAccountComp.AccountKey2RawString(FData.new_accountkey).ToHexaString]);
  1731. end;
  1732. function TOpRecoverFounds.GetDigestToSign: TRawBytes;
  1733. begin
  1734. SetLength(Result,0); // Nothing to be signed!
  1735. end;
  1736. { TOpListAccount }
  1737. procedure TOpListAccount.AffectedAccounts(list: TList<Cardinal>);
  1738. begin
  1739. list.Add(FData.account_signer);
  1740. if FData.account_signer<>FData.account_target then
  1741. list.Add(FData.account_target);
  1742. end;
  1743. function TOpListAccount.OperationAmountByAccount(account: Cardinal): Int64;
  1744. begin
  1745. if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
  1746. else Result := 0;
  1747. end;
  1748. function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
  1749. Var
  1750. account_signer, account_target : TAccount;
  1751. LIsDelist, LIsSale, LIsPrivateSale, LIsPublicSale, LIsSwap, LIsAccountSwap, LIsCoinSwap : boolean;
  1752. LSafeboxCurrentProtocol : Integer;
  1753. begin
  1754. Result := false;
  1755. // Determine which flow this function will execute
  1756. LIsDelist := OpType = CT_Op_DelistAccount;
  1757. LIsSale := (OpType = CT_Op_ListAccountForSale) AND (FData.account_state = as_ForSale);
  1758. if LIsSale then begin
  1759. if (FData.account_state = as_ForSale) and (FData.new_public_key.EC_OpenSSL_NID <> CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
  1760. LIsPublicSale := false;
  1761. LIsPrivateSale := true;
  1762. end else begin
  1763. LIsPublicSale := true;
  1764. LIsPrivateSale := false;
  1765. end;
  1766. end else begin
  1767. LIsPublicSale := false;
  1768. LIsPrivateSale := false;
  1769. end;
  1770. LIsSwap := (OpType = CT_Op_ListAccountForSale) AND (FData.account_state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]);
  1771. if LIsSwap then begin
  1772. if FData.account_state = as_ForAtomicCoinSwap then begin
  1773. LIsAccountSwap := false;
  1774. LIsCoinSwap := true;
  1775. end else begin
  1776. LIsAccountSwap := true;
  1777. LIsCoinSwap := false;
  1778. end;
  1779. end else begin
  1780. LIsAccountSwap := false;
  1781. LIsCoinSwap := false;
  1782. end;
  1783. LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
  1784. if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
  1785. errors := 'List/Delist Account is not allowed on Protocol 1';
  1786. exit;
  1787. end;
  1788. if LIsSwap AND (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
  1789. errors := 'Atomic Swaps are not allowed before Protocol 5';
  1790. exit;
  1791. end;
  1792. if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  1793. errors := 'Invalid signer account number';
  1794. Exit;
  1795. end;
  1796. if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  1797. errors := 'Invalid target account number';
  1798. Exit;
  1799. end;
  1800. if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1801. errors := 'Signer account is blocked for protocol';
  1802. Exit;
  1803. end;
  1804. if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1805. errors := 'Target account is blocked for protocol';
  1806. Exit;
  1807. end;
  1808. if NOT LIsDelist then begin
  1809. if (FData.account_to_pay>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  1810. errors := 'Invalid account to pay number';
  1811. Exit;
  1812. end;
  1813. if (FData.account_target = FData.account_to_pay) AND NOT (LIsAccountSwap AND (FData.account_price = 0)) then begin
  1814. // Note: atomic account swap with 0 sale price can have tself as seller
  1815. errors := 'Account to pay is itself';
  1816. Exit;
  1817. end;
  1818. if TAccountComp.IsAccountBlockedByProtocol(FData.account_to_pay, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  1819. errors := 'Account to pay is blocked for protocol';
  1820. Exit;
  1821. end;
  1822. if (NOT LIsAccountSwap) AND (FData.account_price<=0) then begin
  1823. if (LIsSale) then errors := 'Account price must be greater than 0'
  1824. else errors := 'Account swap amount must be greater than 0';
  1825. exit;
  1826. end;
  1827. if (LIsAccountSwap) and (FData.account_price<>0) then begin
  1828. errors := 'Account for Account Swap must have 0 price';
  1829. Exit;
  1830. end;
  1831. if (FData.locked_until_block > (AccountTransaction.FreezedSafeBox.BlocksCount + CT_MaxFutureBlocksLockedAccount)) then begin
  1832. errors := 'Invalid locked block: Current block '+Inttostr(AccountTransaction.FreezedSafeBox.BlocksCount)+' cannot lock to block '+IntToStr(FData.locked_until_block)+' (Max is future '+IntToStr(CT_MaxFutureBlocksLockedAccount)+' blocks)';
  1833. exit;
  1834. end;
  1835. if LIsPrivateSale OR LIsAccountSwap then begin
  1836. If Not TAccountComp.IsValidAccountKey( FData.new_public_key,LSafeboxCurrentProtocol,errors) then begin
  1837. errors := 'Invalid new public key: '+errors;
  1838. exit;
  1839. end;
  1840. end else if (LSafeboxCurrentProtocol>=CT_PROTOCOL_5) then begin
  1841. // COIN SWAP or PUBLIC SALE must set FData.new_public_key to NULL
  1842. if Not TAccountComp.IsNullAccountKey(FData.new_public_key) then begin
  1843. errors := 'Coin swap/Public sale needs a NULL new public key';
  1844. Exit;
  1845. end;
  1846. end;
  1847. end;
  1848. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  1849. errors := 'Invalid fee: '+Inttostr(FData.fee);
  1850. exit;
  1851. end;
  1852. account_signer := AccountTransaction.Account(FData.account_signer);
  1853. account_target := AccountTransaction.Account(FData.account_target);
  1854. if (FData.account_signer<>FData.account_target) then begin
  1855. // Both accounts must have same PUBLIC KEY!
  1856. if Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
  1857. errors := 'Signer and affected accounts have different public key';
  1858. Exit;
  1859. end;
  1860. end;
  1861. if ((account_signer.n_operation+1)<>FData.n_operation) then begin
  1862. errors := 'Invalid n_operation';
  1863. Exit;
  1864. end;
  1865. if (account_signer.balance<FData.fee) then begin
  1866. errors := 'Insuficient founds';
  1867. exit;
  1868. end;
  1869. if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
  1870. errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
  1871. Exit;
  1872. end;
  1873. // Is locked?
  1874. if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  1875. errors := 'Signer account is currently locked';
  1876. exit;
  1877. end;
  1878. if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  1879. errors := 'Target account is currently locked';
  1880. exit;
  1881. end;
  1882. if LIsPrivateSale OR LIsAccountSwap then begin
  1883. // NOTE: Atomic coin swap avoids this validation since it is a transfer
  1884. // of account to same owner, paying counter-party in seller field
  1885. if TAccountComp.EqualAccountKeys(account_target.accountInfo.accountKey,FData.new_public_key) then begin
  1886. errors := 'New public key for private sale is the same public key';
  1887. Exit;
  1888. end;
  1889. end else if LIsCoinSwap then begin
  1890. if (account_target.balance < (FData.account_price + FData.fee)) then begin
  1891. errors := 'Not enough funds for coin swap amount and fee';
  1892. exit;
  1893. end;
  1894. end;
  1895. // Build 1.4
  1896. If (FData.public_key.EC_OpenSSL_NID<>CT_TECDSA_Public_Nul.EC_OpenSSL_NID) And (Not TAccountComp.EqualAccountKeys(FData.public_key,account_signer.accountInfo.accountkey)) then begin
  1897. errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
  1898. FData.account_signer,
  1899. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
  1900. TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
  1901. exit;
  1902. end;
  1903. // Check signature
  1904. If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
  1905. errors := 'Invalid ECDSA signature';
  1906. Exit;
  1907. end;
  1908. if LIsDelist then begin
  1909. account_target.accountInfo.state := as_Normal;
  1910. account_target.accountInfo.locked_until_block := CT_AccountInfo_NUL.locked_until_block;
  1911. account_target.accountInfo.price := CT_AccountInfo_NUL.price;
  1912. account_target.accountInfo.account_to_pay := CT_AccountInfo_NUL.account_to_pay;
  1913. account_target.accountInfo.new_publicKey := CT_AccountInfo_NUL.new_publicKey;
  1914. account_target.accountInfo.hashed_secret := CT_AccountInfo_NUL.hashed_secret;
  1915. end else begin
  1916. account_target.accountInfo.state := FData.account_state;
  1917. account_target.accountInfo.locked_until_block := FData.locked_until_block;
  1918. account_target.accountInfo.price := FData.account_price;
  1919. account_target.accountInfo.account_to_pay := FData.account_to_pay;
  1920. account_target.accountInfo.new_publicKey := FData.new_public_key;
  1921. if LIsSwap then begin
  1922. account_target.accountInfo.hashed_secret := TBaseType.ToRawBytes( FData.hash_lock );
  1923. end else begin
  1924. // FData.hash_lock has no utility when no AtomicSwap
  1925. account_target.accountInfo.hashed_secret := CT_AccountInfo_NUL.hashed_secret;
  1926. end;
  1927. end;
  1928. Result := AccountTransaction.UpdateAccountInfo(
  1929. AccountPreviousUpdatedBlock,
  1930. GetOpID,
  1931. FData.account_signer,
  1932. FData.n_operation,
  1933. FData.account_target,
  1934. account_target.accountInfo,
  1935. account_target.name,
  1936. account_target.account_data,
  1937. account_target.account_type,
  1938. FData.fee,
  1939. errors
  1940. );
  1941. end;
  1942. function TOpListAccount.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
  1943. begin
  1944. // This Operation is new from protocol V2, so we cannot hash it as a previous protocol!
  1945. Result := inherited GetBufferForOpHash(true);
  1946. end;
  1947. procedure TOpListAccount.InitializeData;
  1948. begin
  1949. inherited;
  1950. FData := CT_TOpListAccountData_NUL;
  1951. end;
  1952. function TOpListAccount.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  1953. var LAccount : TAccount;
  1954. begin
  1955. LAccount := ASafeBoxTransaction.Account(FData.account_signer);
  1956. Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
  1957. end;
  1958. function TOpListAccount.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
  1959. var raw : TRawBytes;
  1960. w : Word;
  1961. begin
  1962. Result := false;
  1963. if Stream.Size-Stream.Position < 14 then exit; // Invalid stream
  1964. Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
  1965. Stream.Read(FData.account_target,Sizeof(FData.account_target));
  1966. Stream.Read(w,2);
  1967. case w of
  1968. CT_Op_ListAccountForSale : FData.operation_type := lat_ListAccount;
  1969. CT_Op_DelistAccount : FData.operation_type := lat_DelistAccount;
  1970. else exit; // Invalid data info
  1971. end;
  1972. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  1973. if (FData.operation_type in [lat_ListAccount]) then begin
  1974. Stream.Read(FData.account_price,Sizeof(FData.account_price));
  1975. Stream.Read(FData.account_to_pay,Sizeof(FData.account_to_pay));
  1976. if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
  1977. if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
  1978. if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
  1979. if TStreamOp.ReadAnsiString(Stream,raw)<0 then exit;
  1980. FData.new_public_key := TAccountComp.RawString2Accountkey(raw);
  1981. Stream.Read(FData.locked_until_block,Sizeof(FData.locked_until_block));
  1982. // VERSION 5: read the new account state and hash-lock
  1983. if FProtocolVersion >= CT_PROTOCOL_5 then begin
  1984. if Stream.Read(w, 2) < 0 then exit; // the new account state to set
  1985. if Not (w in [Integer(as_ForSale),Integer(as_ForAtomicAccountSwap),Integer(as_ForAtomicCoinSwap)]) then begin
  1986. // Invalid value readed
  1987. Exit;
  1988. end;
  1989. FData.account_state := TAccountState(w);
  1990. if TStreamOp.ReadAnsiString(Stream, FData.hash_lock) < 0 then exit; // the hash-lock if any
  1991. end else begin
  1992. // On V4 and below only as_ForSale is possible
  1993. if FData.operation_type = lat_ListAccount then
  1994. FData.account_state := as_ForSale;
  1995. end;
  1996. end;
  1997. Stream.Read(FData.fee,Sizeof(FData.fee));
  1998. if Not LoadOperationPayloadFromStream(Stream,FData.payload) then Exit;
  1999. if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
  2000. if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
  2001. Result := true;
  2002. end;
  2003. procedure TOpListAccount.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
  2004. begin
  2005. inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
  2006. SetLength(OperationResume.Changers,1);
  2007. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  2008. OperationResume.Changers[0].Account:=FData.account_target;
  2009. case FData.operation_type of
  2010. lat_ListAccount : begin
  2011. if (FData.account_state = as_ForSale) And (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
  2012. OperationResume.Changers[0].Changes_type:=[list_for_public_sale];
  2013. end else begin
  2014. if FData.account_state = as_ForAtomicAccountSwap then
  2015. OperationResume.Changers[0].Changes_type:=[list_for_account_swap]
  2016. else if FData.account_state = as_ForAtomicCoinSwap then
  2017. OperationResume.Changers[0].Changes_type:=[list_for_coin_swap]
  2018. else
  2019. OperationResume.Changers[0].Changes_type:=[list_for_private_sale];
  2020. OperationResume.Changers[0].New_Accountkey := FData.new_public_key;
  2021. OperationResume.Changers[0].Locked_Until_Block := FData.locked_until_block;
  2022. end;
  2023. OperationResume.Changers[0].Seller_Account:=FData.account_to_pay;
  2024. OperationResume.Changers[0].Account_Price:=FData.account_price;
  2025. if (FData.account_state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then begin
  2026. OperationResume.Changers[0].Hashed_secret := TBaseType.ToRawBytes( FData.hash_lock );
  2027. end;
  2028. end;
  2029. lat_DelistAccount : begin
  2030. OperationResume.Changers[0].Changes_type:=[delist];
  2031. end;
  2032. end;
  2033. if (FData.account_signer = FData.account_target) then begin
  2034. OperationResume.Changers[0].Fee:=FData.fee;
  2035. OperationResume.Changers[0].N_Operation:=FData.n_operation;
  2036. OperationResume.Changers[0].Signature:=FData.sign;
  2037. end else begin
  2038. SetLength(OperationResume.Changers,2);
  2039. OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
  2040. OperationResume.Changers[1].Account := FData.account_signer;
  2041. OperationResume.Changers[1].N_Operation := FData.n_operation;
  2042. OperationResume.Changers[1].Fee := FData.fee;
  2043. OperationResume.Changers[1].Signature := FData.sign;
  2044. end;
  2045. end;
  2046. function TOpListAccount.N_Operation: Cardinal;
  2047. begin
  2048. Result := FData.n_operation;
  2049. end;
  2050. function TOpListAccount.OperationAmount: Int64;
  2051. begin
  2052. Result := 0;
  2053. end;
  2054. function TOpListAccount.OperationFee: Int64;
  2055. begin
  2056. Result := FData.fee;
  2057. end;
  2058. function TOpListAccount.OperationPayload: TOperationPayload;
  2059. begin
  2060. Result := FData.payload;
  2061. end;
  2062. function TOpListAccount.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
  2063. Var w : Word;
  2064. begin
  2065. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  2066. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  2067. case FData.operation_type of
  2068. lat_ListAccount : w := CT_Op_ListAccountForSale;
  2069. lat_DelistAccount : w := CT_Op_DelistAccount;
  2070. else raise Exception.Create('ERROR DEV 20170412-1');
  2071. end;
  2072. Stream.Write(w,2);
  2073. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  2074. if FData.operation_type in [lat_ListAccount] then begin
  2075. Stream.Write(FData.account_price,Sizeof(FData.account_price));
  2076. Stream.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
  2077. Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  2078. TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
  2079. TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
  2080. TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FData.new_public_key));
  2081. Stream.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
  2082. // VERSION 5: write the new account state and hash-lock
  2083. if FProtocolVersion >= CT_PROTOCOL_5 then begin
  2084. w := Word(FData.account_state);
  2085. Stream.Write(w, 2); // the new account state to set
  2086. TStreamOp.WriteAnsiString(Stream, FData.hash_lock); // the hash-lock if any
  2087. end;
  2088. end;
  2089. Stream.Write(FData.fee,Sizeof(FData.fee));
  2090. SaveOperationPayloadToStream(Stream,FData.payload);
  2091. TStreamOp.WriteAnsiString(Stream,FData.sign.r);
  2092. TStreamOp.WriteAnsiString(Stream,FData.sign.s);
  2093. Result := true;
  2094. end;
  2095. function TOpListAccount.SignerAccount: Cardinal;
  2096. begin
  2097. Result := FData.account_signer;
  2098. end;
  2099. function TOpListAccount.DestinationAccount: Int64;
  2100. begin
  2101. Result := FData.account_target;
  2102. end;
  2103. function TOpListAccount.SellerAccount: Int64;
  2104. begin
  2105. Case FData.operation_type of
  2106. lat_ListAccount : Result := FData.account_to_pay;
  2107. else Result:=inherited SellerAccount;
  2108. end;
  2109. end;
  2110. function TOpListAccount.toString: String;
  2111. begin
  2112. case FData.operation_type of
  2113. lat_ListAccount : begin
  2114. case FData.account_state of
  2115. as_ForSale: begin
  2116. if (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
  2117. Result := Format('List account %s for sale price %s locked until block:%d fee:%s (n_op:%d) payload size:%d',[
  2118. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.account_price),
  2119. FData.locked_until_block, TAccountComp.FormatMoney(FData.fee),
  2120. FData.n_operation, Length(FData.payload.payload_raw)])
  2121. end else begin
  2122. Result := Format('List account %s for private sale price %s reserved for %s locked until block:%d fee:%s (n_op:%d) payload size:%d',[
  2123. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.account_price),
  2124. TAccountComp.GetECInfoTxt(FData.new_public_key.EC_OpenSSL_NID),
  2125. FData.locked_until_block, TAccountComp.FormatMoney(FData.fee),
  2126. FData.n_operation, Length(FData.payload.payload_raw)])
  2127. end;
  2128. end;
  2129. as_ForAtomicAccountSwap: begin
  2130. Result := Format('List account %s for atomic account swap hash-lock:%s time-lock:%d fee:%s (n_op:%d) payload size:%d',[
  2131. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
  2132. TCrypto.ToHexaString( TBaseType.ToRawBytes( FData.hash_lock ) ),
  2133. FData.locked_until_block,
  2134. TAccountComp.FormatMoney(FData.fee),
  2135. FData.n_operation,
  2136. Length(FData.payload.payload_raw)]
  2137. );
  2138. end;
  2139. as_ForAtomicCoinSwap: begin
  2140. Result := Format('List account %s for atomic coin swap for %s PASC hash-lock:%s time-lock:%d fee:%s (n_op:%d) payload size:%d',[
  2141. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
  2142. TAccountComp.FormatMoney(FData.account_price),
  2143. TCrypto.ToHexaString( TBaseType.ToRawBytes( FData.hash_lock ) ),
  2144. FData.locked_until_block,
  2145. TAccountComp.FormatMoney(FData.fee),
  2146. FData.n_operation,
  2147. Length(FData.payload.payload_raw)]
  2148. );
  2149. end;
  2150. end;
  2151. end;
  2152. lat_DelistAccount : begin
  2153. Result := Format('Delist account %s for sale fee:%s (n_op:%d) payload size:%d',[
  2154. TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.fee),
  2155. FData.n_operation, Length(FData.payload.payload_raw)])
  2156. end;
  2157. else Result := 'ERROR DEV 20170414-2';
  2158. end;
  2159. end;
  2160. function TOpListAccount.GetDigestToSign: TRawBytes;
  2161. var ms : TMemoryStream;
  2162. s : TRawBytes;
  2163. b : Byte;
  2164. w : Word;
  2165. begin
  2166. ms := TMemoryStream.Create;
  2167. try
  2168. ms.Write(FData.account_signer,Sizeof(FData.account_signer));
  2169. ms.Write(FData.account_target,Sizeof(FData.account_target));
  2170. // HS 2019-06-09: NOTE TO ALBERT on de-list, the below fields are included in the signable digest.
  2171. // This is unnecessary, but cannot be changed now.
  2172. ms.Write(FData.n_operation,Sizeof(FData.n_operation));
  2173. ms.Write(FData.account_price,Sizeof(FData.account_price));
  2174. ms.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
  2175. ms.Write(FData.fee,Sizeof(FData.fee));
  2176. if FProtocolVersion>=CT_PROTOCOL_5 then begin
  2177. ms.Write(FData.payload.payload_type,SizeOf(FData.payload.payload_type));
  2178. end;
  2179. if Length(FData.payload.payload_raw)>0 then
  2180. ms.WriteBuffer(FData.payload.payload_raw[Low(FData.payload.payload_raw)],Length(FData.payload.payload_raw));
  2181. ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
  2182. if Length(FData.public_key.x)>0 then
  2183. ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
  2184. if Length(FData.public_key.y)>0 then
  2185. ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
  2186. s := TAccountComp.AccountKey2RawString(FData.new_public_key);
  2187. if Length(s)>0 then
  2188. ms.WriteBuffer(s[Low(s)],Length(s));
  2189. ms.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
  2190. // VERSION 5: write the new account state and hash-lock
  2191. if (FProtocolVersion >= CT_PROTOCOL_5) then begin
  2192. w := Word(FData.account_state);
  2193. ms.Write(w, 2);
  2194. TStreamOp.WriteAnsiString(ms, FData.hash_lock); // the hash-lock if any
  2195. end;
  2196. if (FProtocolVersion<=CT_PROTOCOL_3) then begin
  2197. ms.Position := 0;
  2198. SetLength(Result,ms.Size);
  2199. ms.ReadBuffer(Result[Low(Result)],ms.Size);
  2200. end else begin
  2201. b := OpType;
  2202. ms.Write(b,1);
  2203. Result := TCrypto.DoSha256(ms.Memory,ms.Size);
  2204. end;
  2205. finally
  2206. ms.Free;
  2207. end;
  2208. end;
  2209. { TOpListAccountForSaleOrSwap }
  2210. constructor TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal; ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TOperationPayload);
  2211. begin
  2212. inherited Create(ACurrentProtocol);
  2213. case ANewAccountState of
  2214. as_Normal: raise EArgumentOutOfRangeException.Create('Listing to normal state is not a listing');
  2215. as_ForSale: ;
  2216. as_ForAtomicAccountSwap: ;
  2217. as_ForAtomicCoinSwap: ;
  2218. else
  2219. raise EArgumentOutOfRangeException.Create('Invalid new list account state');
  2220. end;
  2221. FData.account_signer := AAccountSigner;
  2222. FData.account_target := AAccountTarget;
  2223. FData.operation_type := lat_ListAccount;
  2224. FData.n_operation := ANOperation;
  2225. FData.account_state := ANewAccountState;
  2226. if ANewAccountState in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
  2227. // Hash lock is stored only if AtomicSwap
  2228. FData.hash_lock := AHashLock;
  2229. end;
  2230. FData.locked_until_block := ALockedUntilBlock;
  2231. FData.account_price := AAccountPrice;
  2232. FData.account_to_pay := AAccountToPay;
  2233. FData.fee := AFee;
  2234. FData.payload := APayload;
  2235. // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
  2236. // FData.public_key := key.PublicKey;
  2237. if (ANewAccountState in [as_ForAtomicCoinSwap]) then begin
  2238. // Force NULL new_public_key
  2239. FData.new_public_key := CT_TECDSA_Public_Nul;
  2240. end else begin
  2241. FData.new_public_key := ANewPublicKey;
  2242. end;
  2243. if Assigned(AKey) then begin
  2244. FData.sign := TCrypto.ECDSASign(AKey.PrivateKey, GetDigestToSign);
  2245. FHasValidSignature := true;
  2246. FUsedPubkeyForSignature := AKey.PublicKey;
  2247. end else begin
  2248. TLog.NewLog(ltdebug,Classname,'No key for signing a new list account for sale operation');
  2249. FHasValidSignature := false;
  2250. end;
  2251. end;
  2252. class function TOpListAccountForSaleOrSwap.OpType: Byte;
  2253. begin
  2254. Result := CT_Op_ListAccountForSale;
  2255. end;
  2256. function TOpListAccountForSaleOrSwap.GetOpSubType : Integer;
  2257. begin
  2258. case FData.account_state of
  2259. as_ForSale:
  2260. if (FData.new_public_key.EC_OpenSSL_NID<>0) then Exit(CT_OpSubtype_ListAccountForPrivateSale) else Exit(CT_OpSubtype_ListAccountForPublicSale);
  2261. as_ForAtomicAccountSwap: Exit(CT_OpSubtype_ListAccountForAccountSwap);
  2262. as_ForAtomicCoinSwap: Exit(CT_OpSubtype_ListAccountForCoinSwap);
  2263. end;
  2264. end;
  2265. { TOpDelistAccountForSale }
  2266. constructor TOpDelistAccountForSale.CreateDelistAccountForSale(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; const payload: TOperationPayload);
  2267. begin
  2268. inherited Create(ACurrentProtocol);
  2269. FData.account_signer := account_signer;
  2270. FData.account_target := account_target;
  2271. FData.operation_type := lat_DelistAccount;
  2272. FData.n_operation := n_operation;
  2273. FData.fee := fee;
  2274. FData.payload := payload;
  2275. if Assigned(key) then begin
  2276. FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
  2277. FHasValidSignature := true;
  2278. FUsedPubkeyForSignature := key.PublicKey;
  2279. end else begin
  2280. TLog.NewLog(ltdebug,Classname,'No key for signing a delist account operation');
  2281. FHasValidSignature := false;
  2282. end;
  2283. end;
  2284. class function TOpDelistAccountForSale.OpType: Byte;
  2285. begin
  2286. Result := CT_Op_DelistAccount;
  2287. end;
  2288. { TOpBuyAccount }
  2289. constructor TOpBuyAccount.CreateBuy(ACurrentProtocol : Word; account_number, n_operation, account_to_buy,
  2290. account_to_pay: Cardinal; price, amount, fee: UInt64;
  2291. new_public_key: TAccountKey; key: TECPrivateKey; const payload: TOperationPayload);
  2292. begin
  2293. inherited Create(ACurrentProtocol);
  2294. FData.sender := account_number;
  2295. FData.n_operation := n_operation;
  2296. FData.target := account_to_buy;
  2297. FData.amount := amount;
  2298. FData.fee := fee;
  2299. FData.payload := payload;
  2300. // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
  2301. // FData.public_key := key.PublicKey;
  2302. FData.opTransactionStyle := buy_account;
  2303. FData.AccountPrice := price;
  2304. FData.SellerAccount := account_to_pay;
  2305. FData.new_accountkey := new_public_key;
  2306. if Assigned(key) then begin
  2307. FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
  2308. FHasValidSignature := true;
  2309. FUsedPubkeyForSignature := key.PublicKey;
  2310. end else begin
  2311. TLog.NewLog(ltdebug,Classname,'No key for signing a new Buy operation');
  2312. FHasValidSignature := false;
  2313. end;
  2314. end;
  2315. procedure TOpBuyAccount.InitializeData;
  2316. begin
  2317. inherited;
  2318. FData.opTransactionStyle := buy_account;
  2319. end;
  2320. class function TOpBuyAccount.OpType: Byte;
  2321. begin
  2322. Result := CT_Op_BuyAccount;
  2323. end;
  2324. { TOpData }
  2325. procedure TOpData.InitializeData(AProtocolVersion : Word);
  2326. begin
  2327. inherited InitializeData(AProtocolVersion);
  2328. FData := CT_TOpDataData_NUL;
  2329. end;
  2330. function TOpData.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
  2331. var LAccount : TAccount;
  2332. begin
  2333. LAccount := ASafeBoxTransaction.Account(FData.account_signer);
  2334. Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
  2335. end;
  2336. function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
  2337. begin
  2338. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  2339. Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
  2340. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  2341. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  2342. // VERSION 5: write the GUID
  2343. if FProtocolVersion >= CT_PROTOCOL_5 then begin
  2344. TStreamOp.writeGuid(Stream,FData.guid);
  2345. end;
  2346. Stream.Write(FData.dataType,Sizeof(FData.dataType));
  2347. Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
  2348. Stream.Write(FData.amount,Sizeof(FData.amount));
  2349. Stream.Write(FData.fee,Sizeof(FData.fee));
  2350. SaveOperationPayloadToStream(Stream,FData.payload);
  2351. TStreamOp.WriteAnsiString(Stream,FData.sign.r);
  2352. TStreamOp.WriteAnsiString(Stream,FData.sign.s);
  2353. Result := true;
  2354. end;
  2355. function TOpData.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
  2356. begin
  2357. Result := false;
  2358. if Stream.Size-Stream.Position < 16 then exit; // Invalid stream
  2359. Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
  2360. Stream.Read(FData.account_sender,Sizeof(FData.account_sender));
  2361. Stream.Read(FData.account_target,Sizeof(FData.account_target));
  2362. Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
  2363. // VERSION 5: write the GUID
  2364. if FProtocolVersion >= CT_PROTOCOL_5 then begin
  2365. if TStreamOp.ReadGUID(Stream,FData.guid)<16 then Exit;
  2366. end;
  2367. if Stream.Size-Stream.Position < 20 then exit; // Invalid stream
  2368. Stream.Read(FData.dataType,Sizeof(FData.dataType));
  2369. Stream.Read(FData.dataSequence,Sizeof(FData.dataSequence));
  2370. Stream.Read(FData.amount,Sizeof(FData.amount));
  2371. Stream.Read(FData.fee,Sizeof(FData.fee));
  2372. if Not LoadOperationPayloadFromStream(Stream,FData.payload) then Exit;
  2373. if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
  2374. if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
  2375. Result := true;
  2376. end;
  2377. procedure TOpData.FillOperationResume(Block: Cardinal;
  2378. getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal;
  2379. var OperationResume: TOperationResume);
  2380. begin
  2381. inherited FillOperationResume(Block, getInfoForAllAccounts,Affected_account_number, OperationResume);
  2382. if (getInfoForAllAccounts) then
  2383. OperationResume.OpSubtype := CT_OpSubtype_Data_GlobalInfo
  2384. else if (Affected_account_number=FData.account_sender) then
  2385. OperationResume.OpSubtype := CT_OpSubtype_Data_Sender
  2386. else if (Affected_account_number=FData.account_target) then
  2387. OperationResume.OpSubtype := CT_OpSubtype_Data_Receiver
  2388. else if (Affected_account_number=FData.account_signer) then
  2389. OperationResume.OpSubtype := CT_OpSubtype_Data_Signer;
  2390. SetLength(OperationResume.Senders,1);
  2391. OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
  2392. OperationResume.Senders[0].Account:=FData.account_sender;
  2393. OperationResume.Senders[0].Payload:=FData.payload;
  2394. if (FData.account_sender=FData.account_signer) then begin
  2395. OperationResume.Senders[0].N_Operation:=FData.n_operation;
  2396. OperationResume.Senders[0].Signature:=FData.sign;
  2397. OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee)*(-1);
  2398. end else begin
  2399. OperationResume.Senders[0].Amount:=Int64(FData.amount)*(-1);
  2400. SetLength(OperationResume.Changers,1);
  2401. OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
  2402. OperationResume.Changers[0].Account:=FData.account_signer;
  2403. OperationResume.Changers[0].Fee:=FData.fee;
  2404. OperationResume.Changers[0].N_Operation:=FData.n_operation;
  2405. OperationResume.Changers[0].Signature:=FData.sign;
  2406. end;
  2407. //
  2408. SetLength(OperationResume.Receivers,1);
  2409. OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
  2410. OperationResume.Receivers[0].Account:=FData.account_target;
  2411. OperationResume.Receivers[0].Amount:=FData.amount;
  2412. OperationResume.Receivers[0].Payload:=FData.payload;
  2413. // Add OpData missing in V4, added to V5
  2414. OperationResume.Senders[0].OpData.ID := FData.guid;
  2415. OperationResume.Senders[0].OpData.Sequence := FData.dataSequence;
  2416. OperationResume.Senders[0].OpData.&Type := FData.dataType;
  2417. //
  2418. OperationResume.n_operation:=FData.n_operation;
  2419. if (Affected_account_number = FData.account_signer) or (getInfoForAllAccounts) then begin
  2420. OperationResume.Fee:=Int64(FData.fee)*(-1);
  2421. end else OperationResume.Fee:=0;
  2422. OperationResume.OperationTxt := ToString;
  2423. if (getInfoForAllAccounts) then OperationResume.Amount:= OperationAmount
  2424. else OperationResume.Amount := OperationAmountByAccount(Affected_account_number);
  2425. end;
  2426. class function TOpData.OpType: Byte;
  2427. begin
  2428. Result := CT_Op_Data;
  2429. end;
  2430. function TOpData.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
  2431. begin
  2432. Result:=inherited GetBufferForOpHash(UseProtocolV2);
  2433. end;
  2434. function TOpData.DoOperation(
  2435. AccountPreviousUpdatedBlock: TAccountPreviousBlockInfo;
  2436. AccountTransaction: TPCSafeBoxTransaction; var errors: String): Boolean;
  2437. Var account_signer, account_sender, account_target : TAccount;
  2438. LSafeboxCurrentProtocol : Integer;
  2439. begin
  2440. Result := false;
  2441. LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
  2442. if (LSafeboxCurrentProtocol<CT_PROTOCOL_4) then begin
  2443. errors := 'OpData is not allowed on Protocol < 4';
  2444. exit;
  2445. end;
  2446. if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  2447. errors := 'Invalid signer account number';
  2448. Exit;
  2449. end;
  2450. if (FData.account_sender>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  2451. errors := 'Invalid sender account number';
  2452. Exit;
  2453. end;
  2454. if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
  2455. errors := 'Invalid target account number';
  2456. Exit;
  2457. end;
  2458. if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  2459. errors := 'Signer account is blocked for protocol';
  2460. Exit;
  2461. end;
  2462. if TAccountComp.IsAccountBlockedByProtocol(FData.account_sender, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  2463. errors := 'Sender account is blocked for protocol';
  2464. Exit;
  2465. end;
  2466. if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
  2467. errors := 'Target account is blocked for protocol';
  2468. Exit;
  2469. end;
  2470. if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
  2471. errors := 'Invalid fee: '+Inttostr(FData.fee);
  2472. exit;
  2473. end;
  2474. if (FData.amount<0) Or (FData.amount>CT_MaxTransactionAmount) then begin
  2475. errors := 'Invalid amount: '+Inttostr(FData.fee);
  2476. exit;
  2477. end;
  2478. account_signer := AccountTransaction.Account(FData.account_signer);
  2479. account_sender := AccountTransaction.Account(FData.account_sender);
  2480. account_target := AccountTransaction.Account(FData.account_target);
  2481. // Is signer locked?
  2482. if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  2483. errors := 'Signer account is currently locked';
  2484. exit;
  2485. end;
  2486. if (FData.account_signer<>FData.account_sender) then begin
  2487. // Both accounts must have same PUBLIC KEY!
  2488. if Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_sender.accountInfo.accountKey) then begin
  2489. errors := 'Signer and sender accounts have different public key';
  2490. Exit;
  2491. end;
  2492. if (account_signer.balance<FData.fee) then begin
  2493. errors := 'Insuficient signer funds';
  2494. exit;
  2495. end;
  2496. if (account_sender.balance<FData.amount) then begin
  2497. errors := 'Insuficient sender funds';
  2498. exit;
  2499. end;
  2500. // Is sender locked?
  2501. if (TAccountComp.IsAccountLocked(account_sender.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
  2502. errors := 'Sender account is currently locked';
  2503. exit;
  2504. end;
  2505. end else begin
  2506. // signer = sender
  2507. if (account_signer.balance<(FData.fee + FData.amount)) then begin
  2508. errors := 'Insuficient funds';
  2509. exit;
  2510. end;
  2511. end;
  2512. if ((account_signer.n_operation+1)<>FData.n_operation) then begin
  2513. errors := 'Invalid n_operation';
  2514. Exit;
  2515. end;
  2516. if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
  2517. errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
  2518. Exit;
  2519. end;
  2520. // Check signature
  2521. If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
  2522. errors := 'Invalid ECDSA signature';
  2523. Exit;
  2524. end;
  2525. Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,
  2526. GetOpID,
  2527. FData.account_sender,FData.account_signer,FData.account_target,
  2528. FData.n_operation,FData.amount,FData.fee,errors);
  2529. end;
  2530. function TOpData.OperationAmount: Int64;
  2531. begin
  2532. Result := FData.amount + FData.fee;
  2533. end;
  2534. function TOpData.OperationFee: Int64;
  2535. begin
  2536. Result := FData.fee;
  2537. end;
  2538. function TOpData.OperationPayload: TOperationPayload;
  2539. begin
  2540. Result := FData.payload;
  2541. end;
  2542. function TOpData.SignerAccount: Cardinal;
  2543. begin
  2544. Result := FData.account_signer;
  2545. end;
  2546. function TOpData.DestinationAccount: Int64;
  2547. begin
  2548. Result := FData.account_target;
  2549. end;
  2550. function TOpData.N_Operation: Cardinal;
  2551. begin
  2552. Result := FData.n_operation;
  2553. end;
  2554. procedure TOpData.AffectedAccounts(list: TList<Cardinal>);
  2555. begin
  2556. list.Add(FData.account_signer);
  2557. if (FData.account_signer<>FData.account_sender) then begin
  2558. list.Add(FData.account_sender);
  2559. end;
  2560. if (FData.account_signer<>FData.account_target) And (FData.account_sender<>FData.account_target) then begin
  2561. list.Add(FData.account_target);
  2562. end;
  2563. end;
  2564. function TOpData.OperationAmountByAccount(account: Cardinal): Int64;
  2565. begin
  2566. Result := 0;
  2567. if (account = FData.account_signer) then begin
  2568. Result := Int64(FData.fee) * (-1);
  2569. end;
  2570. if (account = FData.account_sender) then begin
  2571. Result := Result + (Int64(FData.amount)*(-1));
  2572. end;
  2573. if (account = FData.account_target) then begin
  2574. Result := Result + FData.amount;
  2575. end;
  2576. end;
  2577. constructor TOpData.CreateOpData(ACurrentProtocol : word; account_signer, account_sender,
  2578. account_target: Cardinal; signer_key: TECPrivateKey; n_operation: Cardinal;
  2579. dataType, dataSequence: Word; AGUID : TGUID; amount, fee: UInt64; const payload: TOperationPayload);
  2580. begin
  2581. Inherited Create(ACurrentProtocol);
  2582. FData.account_sender:=account_sender;
  2583. FData.account_signer:=account_signer;
  2584. FData.account_target:=account_target;
  2585. FData.amount:=amount;
  2586. FData.fee:=fee;
  2587. FData.n_operation:=n_operation;
  2588. FData.payload:=payload;
  2589. FData.dataSequence:=dataSequence;
  2590. FData.dataType:=dataType;
  2591. FData.guid := AGUID;
  2592. if Assigned(signer_key) then begin
  2593. FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign);
  2594. FHasValidSignature := true;
  2595. FUsedPubkeyForSignature := signer_key.PublicKey;
  2596. end else begin
  2597. TLog.NewLog(ltdebug,Classname,'No key for signing a new OpData');
  2598. FHasValidSignature := false;
  2599. end;
  2600. end;
  2601. function TOpData.toString: String;
  2602. begin
  2603. Result := Format('OpData from:%d to:%d type:%d sequence:%d Amount:%s',
  2604. [FData.account_sender,FData.account_target,FData.dataType,FData.dataSequence,
  2605. TAccountComp.FormatMoney(FData.amount)]);
  2606. end;
  2607. function TOpData.GetDigestToSign: TRawBytes;
  2608. var Stream : TMemoryStream;
  2609. b : Byte;
  2610. begin
  2611. Stream := TMemoryStream.Create;
  2612. Try
  2613. Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
  2614. Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
  2615. Stream.Write(FData.account_target,Sizeof(FData.account_target));
  2616. Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
  2617. // VERSION 5: write the GUID to the digest
  2618. if ProtocolVersion >= CT_PROTOCOL_5 then begin
  2619. TStreamOp.WriteGUID(Stream,FData.guid);
  2620. end;
  2621. Stream.Write(FData.dataType,Sizeof(FData.dataType));
  2622. Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
  2623. Stream.Write(FData.amount,Sizeof(FData.amount));
  2624. Stream.Write(FData.fee,Sizeof(FData.fee));
  2625. SaveOperationPayloadToStream(Stream,FData.payload);
  2626. b := OpType;
  2627. Stream.Write(b,1);
  2628. if (ProtocolVersion<=CT_PROTOCOL_4) then begin
  2629. Result := TStreamOp.SaveStreamToRaw(Stream);
  2630. end else begin
  2631. Result := TCrypto.DoSha256(Stream.Memory,Stream.Size);
  2632. end;
  2633. finally
  2634. Stream.Free;
  2635. end;
  2636. end;
  2637. initialization
  2638. RegisterOperationsClass;
  2639. end.