UOpTransaction.pas 100 KB

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