12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489 |
- unit UOpTransaction;
- { Copyright (c) 2016 by Albert Molina
- Distributed under the MIT software license, see the accompanying file LICENSE
- or visit http://www.opensource.org/licenses/mit-license.php.
- This unit is a part of the PascalCoin Project, an infinitely scalable
- cryptocurrency. Find us here:
- Web: https://www.pascalcoin.org
- Source: https://github.com/PascalCoin/PascalCoin
- If you like it, consider a donation using Bitcoin:
- 16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
- THIS LICENSE HEADER MUST NOT BE REMOVED.
- }
- {$IFDEF FPC}
- {$MODE Delphi}
- {$ENDIF}
- interface
- Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
- {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
- UPCDataTypes;
- Type
- // Operations Type
- TOpTransactionStyle = (transaction, transaction_with_auto_buy_account, buy_account);
- TOpTransactionData = Record
- sender: Cardinal;
- n_operation : Cardinal;
- target: Cardinal;
- amount: UInt64;
- fee: UInt64;
- payload: TRawBytes;
- public_key: TECDSA_Public;
- sign: TECDSA_SIG;
- // Protocol 2
- // Next values will only be filled after this operation is executed
- opTransactionStyle : TOpTransactionStyle;
- AccountPrice : UInt64;
- SellerAccount : Cardinal;
- new_accountkey : TAccountKey;
- End;
- TOpChangeKeyData = Record
- account_signer,
- account_target: Cardinal;
- n_operation : Cardinal;
- fee: UInt64;
- payload: TRawBytes;
- public_key: TECDSA_Public;
- new_accountkey: TAccountKey;
- sign: TECDSA_SIG;
- End;
- TOpRecoverFoundsData = Record
- account: Cardinal;
- n_operation : Cardinal;
- fee: UInt64;
- End;
- Const
- 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));
- 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));
- CT_TOpRecoverFoundsData_NUL : TOpRecoverFoundsData = (account:0;n_operation:0;fee:0);
- Type
- { TOpTransaction }
- TOpTransaction = Class(TPCOperation)
- private
- FData : TOpTransactionData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- //
- class function OpType : Byte; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function DestinationAccount : Int64; override;
- function SellerAccount : Int64; override;
- function N_Operation : Cardinal; override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- Property Data : TOpTransactionData read FData;
- Constructor CreateTransaction(current_protocol : Word; sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- { TOpChangeKey }
- TOpChangeKey = Class(TPCOperation)
- private
- FData : TOpChangeKeyData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- class function OpType : Byte; override;
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function DestinationAccount : Int64; override;
- function N_Operation : Cardinal; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- Constructor Create(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
- Property Data : TOpChangeKeyData read FData;
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- { TOpChangeKeySigned }
- TOpChangeKeySigned = Class(TOpChangeKey)
- public
- class function OpType : Byte; override;
- end;
- { TOpRecoverFounds }
- TOpRecoverFounds = Class(TPCOperation)
- private
- FData : TOpRecoverFoundsData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- class function OpType : Byte; override;
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function N_Operation : Cardinal; override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
- Property Data : TOpRecoverFoundsData read FData;
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- // NEW OPERATIONS PROTOCOL 2
- TOpListAccountOperationType = (lat_Unknown, lat_ListForSale, lat_DelistAccount);
- TOpListAccountData = Record
- account_signer,
- account_target: Cardinal;
- operation_type : TOpListAccountOperationType;
- n_operation : Cardinal;
- account_price: UInt64;
- account_to_pay : Cardinal;
- fee: UInt64;
- payload: TRawBytes;
- public_key: TAccountKey;
- new_public_key: TAccountKey; // If EC_OpenSSL_NID=0 then is OPEN, otherwise is for only 1 public key
- locked_until_block : Cardinal; //
- sign: TECDSA_SIG;
- End;
- TOpChangeAccountInfoData = Record
- account_signer,
- account_target: Cardinal;
- n_operation : Cardinal;
- fee: UInt64;
- payload: TRawBytes;
- public_key: TECDSA_Public;
- changes_type : TOpChangeAccountInfoTypes; // bits mask. $0001 = New account key , $0002 = New name , $0004 = New type
- new_accountkey: TAccountKey; // If (changes_mask and $0001)=$0001 then change account key
- new_name: TRawBytes; // If (changes_mask and $0002)=$0002 then change name
- new_type: Word; // If (changes_mask and $0004)=$0004 then change type
- sign: TECDSA_SIG;
- End;
- Const
- 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));
- 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:[];
- new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_name:Nil;new_type:0;sign:(r:Nil;s:Nil));
- Type
- { TOpListAccount }
- TOpListAccount = Class(TPCOperation)
- private
- FData : TOpListAccountData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- Function IsPrivateSale : Boolean;
- Function IsDelist : Boolean; virtual; abstract;
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function DestinationAccount : Int64; override;
- function SellerAccount : Int64; override;
- function N_Operation : Cardinal; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- Property Data : TOpListAccountData read FData;
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- TOpListAccountForSale = Class(TOpListAccount)
- public
- class function OpType : Byte; override;
- 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);
- Function IsDelist : Boolean; override;
- End;
- TOpDelistAccountForSale = Class(TOpListAccount)
- public
- class function OpType : Byte; override;
- Constructor CreateDelistAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
- Function IsDelist : Boolean; override;
- End;
- { TOpBuyAccount }
- TOpBuyAccount = Class(TOpTransaction)
- protected
- procedure InitializeData; override;
- public
- class function OpType : Byte; override;
- 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);
- End;
- { TOpChangeAccountInfo }
- TOpChangeAccountInfo = Class(TPCOperation)
- private
- FData : TOpChangeAccountInfoData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- class function OpType : Byte; override;
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function DestinationAccount : Int64; override;
- function N_Operation : Cardinal; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- Constructor CreateChangeAccountInfo(current_protocol : word;
- account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
- change_key : Boolean; const new_account_key : TAccountKey;
- change_name: Boolean; const new_name : TRawBytes;
- change_type: Boolean; const new_type : Word;
- fee: UInt64; payload: TRawBytes);
- Property Data : TOpChangeAccountInfoData read FData;
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- TOpDataData = Record
- account_signer, // The account paying fees (if any) and signing operation
- account_sender, // The account sender. Public key must be EQUAL to account_signer public key
- account_target: Cardinal; // The destination account. Will recive DATA and amount (if any)
- n_operation : Cardinal; // Signer n_operation
- dataType : Word; // 2 byte data type
- dataSequence : Word; // 2 byte data sequence
- amount: UInt64; // Allow amount=0
- fee: UInt64; // Allow fee=0
- payload: TRawBytes; // Standard arbitrary data with length<=256
- sign: TECDSA_SIG;
- End;
- { TOpData }
- TOpData = Class(TPCOperation)
- private
- FData : TOpDataData;
- protected
- procedure InitializeData; override;
- function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
- function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
- procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
- public
- class function OpType : Byte; override;
- function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
- function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
- function OperationAmount : Int64; override;
- function OperationFee : Int64; override;
- function OperationPayload : TRawBytes; override;
- function SignerAccount : Cardinal; override;
- function DestinationAccount : Int64; override;
- function N_Operation : Cardinal; override;
- procedure AffectedAccounts(list : TList<Cardinal>); override;
- function OperationAmountByAccount(account : Cardinal) : Int64; override;
- Constructor CreateOpData(
- account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey;
- n_operation : Cardinal;
- dataType, dataSequence : Word;
- amount, fee : UInt64;
- payload: TRawBytes);
- Property Data : TOpDataData read FData;
- Function toString : String; Override;
- Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
- function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
- End;
- Const
- 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));
- Procedure RegisterOperationsClass;
- implementation
- uses
- SysUtils, UConst, ULog, UTxMultiOperation;
- Procedure RegisterOperationsClass;
- Begin
- TPCOperationsComp.RegisterOperationClass(TOpTransaction);
- TPCOperationsComp.RegisterOperationClass(TOpChangeKey);
- TPCOperationsComp.RegisterOperationClass(TOpRecoverFounds);
- TPCOperationsComp.RegisterOperationClass(TOpListAccountForSale);
- TPCOperationsComp.RegisterOperationClass(TOpDelistAccountForSale);
- TPCOperationsComp.RegisterOperationClass(TOpBuyAccount);
- TPCOperationsComp.RegisterOperationClass(TOpChangeKeySigned);
- TPCOperationsComp.RegisterOperationClass(TOpChangeAccountInfo);
- TPCOperationsComp.RegisterOperationClass(TOpMultiOperation);
- TPCOperationsComp.RegisterOperationClass(TOpData);
- End;
- { TOpChangeAccountInfo }
- procedure TOpChangeAccountInfo.InitializeData;
- begin
- inherited InitializeData;
- FData := CT_TOpChangeAccountInfoData_NUL;
- end;
- function TOpChangeAccountInfo.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- var LAccount : TAccount;
- begin
- LAccount := ASafeBoxTransaction.Account(FData.account_signer);
- Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
- end;
- function TOpChangeAccountInfo.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
- var b : byte;
- begin
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- TStreamOp.WriteAccountKey(Stream,FData.public_key);
- b := 0;
- if (public_key in FData.changes_type) then b:=b OR $01;
- if (account_name in FData.changes_type) then b:=b OR $02;
- if (account_type in FData.changes_type) then b:=b OR $04;
- Stream.Write(b,Sizeof(b));
- TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
- TStreamOp.WriteAnsiString(Stream,FData.new_name);
- Stream.Write(FData.new_type,Sizeof(FData.new_type));
- TStreamOp.WriteAnsiString(Stream,FData.sign.r);
- TStreamOp.WriteAnsiString(Stream,FData.sign.s);
- Result := true;
- end;
- function TOpChangeAccountInfo.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
- var b : Byte;
- begin
- Result := False;
- If Stream.Size - Stream.Position < 20 then exit;
- Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Read(FData.account_target,Sizeof(FData.account_target));
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Read(FData.fee,Sizeof(FData.fee));
- if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then Exit;
- if TStreamOp.ReadAccountKey(Stream,FData.public_key)<0 then Exit;
- Stream.Read(b,SizeOf(b));
- FData.changes_type:=[];
- if (b AND $01)=$01 then FData.changes_type:=FData.changes_type + [public_key];
- if (b AND $02)=$02 then FData.changes_type:=FData.changes_type + [account_name];
- if (b AND $04)=$04 then FData.changes_type:=FData.changes_type + [account_type];
- // Check
- if (b AND $F8)<>0 then Exit;
- if TStreamOp.ReadAccountKey(Stream,FData.new_accountkey)<0 then Exit;
- if TStreamOp.ReadAnsiString(Stream,FData.new_name)<0 then Exit;
- Stream.Read(FData.new_type,Sizeof(FData.new_type));
- if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then Exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then Exit;
- Result := true;
- end;
- procedure TOpChangeAccountInfo.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account := FData.account_target;
- OperationResume.Changers[0].Changes_type := FData.changes_type;
- OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
- OperationResume.Changers[0].New_Name := FData.new_name;
- OperationResume.Changers[0].New_Type := FData.new_type;
- If (FData.account_signer=FData.account_target) then begin
- OperationResume.Changers[0].N_Operation := FData.n_operation;
- OperationResume.Changers[0].Signature := FData.sign;
- OperationResume.Changers[0].Fee := FData.fee;
- end else begin
- SetLength(OperationResume.Changers,2);
- OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[1].Account := FData.account_signer;
- OperationResume.Changers[1].N_Operation := FData.n_operation;
- OperationResume.Changers[1].Fee := FData.fee;
- OperationResume.Changers[1].Signature := FData.sign;
- end;
- end;
- class function TOpChangeAccountInfo.OpType: Byte;
- begin
- Result := CT_Op_ChangeAccountInfo;
- end;
- function TOpChangeAccountInfo.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
- begin
- Result:=inherited GetBufferForOpHash(True);
- end;
- function TOpChangeAccountInfo.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
- Var account_signer, account_target : TAccount;
- begin
- Result := false;
- if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid account number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'account is blocked for protocol';
- Exit;
- end;
- If (FData.account_signer<>FData.account_target) then begin
- if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid account target number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Target account is blocked for protocol';
- Exit;
- end;
- end;
- if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := 'Invalid fee: '+Inttostr(FData.fee);
- exit;
- end;
- account_signer := AccountTransaction.Account(FData.account_signer);
- account_target := AccountTransaction.Account(FData.account_target);
- if ((account_signer.n_operation+1)<>FData.n_operation) then begin
- errors := 'Invalid n_operation';
- Exit;
- end;
- if (account_signer.balance<FData.fee) then begin
- errors := 'Insuficient founds';
- exit;
- end;
- if (length(FData.payload)>CT_MaxPayloadSize) then begin
- errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
- If (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
- Exit; // BUG from protocol 1
- end;
- end;
- // Is locked? Protocol 2 check
- if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Account signer is currently locked';
- exit;
- end;
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'NOT ALLOWED ON PROTOCOL 1';
- exit;
- end;
- If (public_key in FData.changes_type) then begin
- If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, errors ) then begin
- exit;
- end;
- end;
- If (account_name in FData.changes_type) then begin
- If (Length(FData.new_name)>0) then begin
- If Not TPCSafeBox.ValidAccountName(FData.new_name,errors) then Exit;
- end;
- end else begin
- If (Length(FData.new_name)>0) then begin
- errors := 'Invalid data in new_name field';
- Exit;
- end;
- end;
- If (FData.changes_type=[]) then begin
- errors := 'No change';
- Exit;
- end;
- 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
- errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
- FData.account_signer,
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
- exit;
- end;
- If (FData.account_signer<>FData.account_target) then begin
- if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Account target is currently locked';
- exit;
- end;
- // Check have same public key
- If Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
- errors := 'Signer and target accounts have different public key';
- exit;
- end;
- end;
- // Check signature
- If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
- errors := 'Invalid ECDSA signature';
- Exit;
- end;
- FPrevious_Signer_updated_block := account_signer.updated_block;
- FPrevious_Destination_updated_block := account_target.updated_block;
- If (public_key in FData.changes_type) then begin
- account_target.accountInfo.accountKey := FData.new_accountkey;
- end;
- If (account_name in FData.changes_type) then begin
- account_target.name := FData.new_name;
- end;
- If (account_type in FData.changes_type) then begin
- account_target.account_type := FData.new_type;
- end;
- Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
- FData.account_signer,FData.n_operation,FData.account_target,
- account_target.accountInfo,
- account_target.name,
- account_target.account_type,
- FData.fee,errors);
- end;
- function TOpChangeAccountInfo.OperationAmount: Int64;
- begin
- Result := 0;
- end;
- function TOpChangeAccountInfo.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpChangeAccountInfo.OperationPayload: TRawBytes;
- begin
- Result := FData.payload;
- end;
- function TOpChangeAccountInfo.SignerAccount: Cardinal;
- begin
- Result := FData.account_signer;
- end;
- function TOpChangeAccountInfo.DestinationAccount: Int64;
- begin
- Result := FData.account_target;
- end;
- function TOpChangeAccountInfo.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- procedure TOpChangeAccountInfo.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.account_signer);
- if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
- end;
- function TOpChangeAccountInfo.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
- else Result := 0;
- end;
- constructor TOpChangeAccountInfo.CreateChangeAccountInfo(current_protocol : word;
- account_signer, n_operation,
- account_target: Cardinal; key: TECPrivateKey; change_key: Boolean;
- const new_account_key: TAccountKey; change_name: Boolean;
- const new_name: TRawBytes; change_type: Boolean; const new_type: Word;
- fee: UInt64; payload: TRawBytes);
- begin
- inherited Create;
- FData.account_signer:=account_signer;
- FData.account_target:=account_target;
- FData.n_operation:=n_operation;
- FData.fee:=fee;
- FData.payload:=payload;
- // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
- // FData.public_key:=key.PublicKey;
- FData.changes_type:=[];
- If change_key then begin
- FData.changes_type:=FData.changes_type + [public_key];
- FData.new_accountkey:=new_account_key;
- end;
- If change_name then begin
- FData.changes_type:=FData.changes_type + [account_name];
- FData.new_name:=new_name;
- end;
- If change_type then begin
- FData.changes_type:=FData.changes_type + [account_type];
- FData.new_type:=new_type;
- end;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new Change Info operation');
- FHasValidSignature := false;
- end;
- end;
- function TOpChangeAccountInfo.toString: String;
- var s : String;
- begin
- s := '';
- If (public_key IN FData.changes_type) then s := 'new public key '+TAccountComp.GetECInfoTxt(FData.new_accountkey.EC_OpenSSL_NID);
- If (account_name IN FData.changes_type) then begin
- if s<>'' then s:=s+', ';
- s := s + 'new name to "'+FData.new_name.ToPrintable+'"';
- end;
- If (account_type IN FData.changes_type) then begin
- if s<>'' then s:=s+', ';
- s := s + 'new type to '+IntToStr(FData.new_type);
- end;
- Result := Format('Change account %s info: %s fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
- s,
- TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
- end;
- function TOpChangeAccountInfo.GetDigestToSign(current_protocol : Word): TRawBytes;
- var Stream : TMemoryStream;
- b : Byte;
- begin
- Stream := TMemoryStream.Create;
- try
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- TStreamOp.WriteAccountKey(Stream,FData.public_key);
- b := 0;
- if (public_key in FData.changes_type) then b:=b OR $01;
- if (account_name in FData.changes_type) then b:=b OR $02;
- if (account_type in FData.changes_type) then b:=b OR $04;
- Stream.Write(b,Sizeof(b));
- TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
- TStreamOp.WriteAnsiString(Stream,FData.new_name);
- Stream.Write(FData.new_type,Sizeof(FData.new_type));
- if (current_protocol<=CT_PROTOCOL_3) then begin
- Stream.Position := 0;
- setlength(Result,Stream.Size);
- Stream.ReadBuffer(Result[Low(Result)],Stream.Size);
- end else begin
- b := OpType;
- Stream.Write(b,1);
- Result := TCrypto.DoSha256(Stream.Memory,Stream.Size);
- end;
- finally
- Stream.Free;
- end;
- end;
- { TOpTransaction }
- procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.sender);
- list.Add(FData.target);
- if (FData.opTransactionStyle in [transaction_with_auto_buy_account, buy_account]) then begin
- list.Add(FData.SellerAccount);
- end;
- end;
- constructor TOpTransaction.CreateTransaction(current_protocol : Word;
- sender, n_operation, target: Cardinal;
- key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
- begin
- inherited Create;
- FData.sender := sender;
- FData.n_operation := n_operation;
- FData.target := target;
- FData.amount := amount;
- FData.fee := fee;
- FData.payload := payload;
- // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
- // FData.public_key := key.PublicKey;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new Transaction');
- FHasValidSignature := false;
- end;
- end;
- function TOpTransaction.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
- Var s_new, t_new : Int64;
- totalamount : Cardinal;
- sender,target,seller : TAccount;
- _IsBuyTransaction : Boolean;
- begin
- Result := false;
- errors := '';
- //
- if (FData.sender>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := Format('Invalid sender %d',[FData.sender]);
- Exit;
- end;
- if (FData.target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := Format('Invalid target %d',[FData.target]);
- Exit;
- end;
- if (FData.sender=FData.target) then begin
- errors := Format('Sender=Target %d',[FData.sender]);
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.sender,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := Format('sender (%d) is blocked for protocol',[FData.sender]);
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.target,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := Format('target (%d) is blocked for protocol',[FData.target]);
- Exit;
- end;
- if (FData.amount<=0) Or (FData.amount>CT_MaxTransactionAmount) then begin
- errors := Format('Invalid amount %d (0 or max: %d)',[FData.amount,CT_MaxTransactionAmount]);
- Exit;
- end;
- if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := Format('Invalid fee %d (max %d)',[FData.fee,CT_MaxTransactionFee]);
- Exit;
- end;
- if (length(FData.payload)>CT_MaxPayloadSize) then begin
- errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
- If (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
- Exit; // BUG from protocol 1
- end;
- end;
- sender := AccountTransaction.Account(FData.sender);
- target := AccountTransaction.Account(FData.target);
- if ((sender.n_operation+1)<>FData.n_operation) then begin
- errors := Format('Invalid n_operation %d (expected %d)',[FData.n_operation,sender.n_operation+1]);
- Exit;
- end;
- totalamount := FData.amount + FData.fee;
- if (sender.balance<totalamount) then begin
- errors := Format('Insuficient founds %d < (%d + %d = %d)',[sender.balance,FData.amount,FData.fee,totalamount]);
- Exit;
- end;
- if (target.balance+FData.amount>CT_MaxWalletAmount) then begin
- 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]);
- Exit;
- end;
- // Is locked? Protocol 2 check
- if (TAccountComp.IsAccountLocked(sender.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Sender Account is currently locked';
- exit;
- end;
- // Build 1.4
- 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
- errors := Format('Invalid sender public key for account %d. Distinct from SafeBox public key! %s <> %s',[
- FData.sender,
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(sender.accountInfo.accountkey))]);
- exit;
- end;
- // Check signature
- If Not IsValidECDSASignature(sender.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
- errors := 'Invalid ECDSA signature';
- Exit;
- end;
- FPrevious_Signer_updated_block := sender.updated_block;
- FPrevious_Destination_updated_block := target.updated_block;
- // Is buy account ?
- if (FData.opTransactionStyle=buy_Account) then begin
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'Buy account is not allowed on Protocol 1';
- exit;
- end;
- seller := AccountTransaction.Account(FData.SellerAccount);
- if Not (TAccountComp.IsAccountForSale(target.accountInfo)) then begin
- errors := Format('%d is not for sale',[target.account]);
- exit;
- end;
- // Check that seller is the expected seller
- If (target.accountInfo.account_to_pay<>seller.account) then begin
- errors := Format('Seller account %d is not expected account %d',[FData.SellerAccount,target.accountInfo.account_to_pay]);
- exit;
- end;
- if (target.balance + FData.amount < target.accountInfo.price) then begin
- errors := Format('Account %d balance (%d) + amount (%d) < price (%d)',[target.account,target.balance,FData.amount,target.accountInfo.price]);
- exit;
- end;
- if (FData.AccountPrice<>target.accountInfo.price) then begin
- errors := Format('Signed price (%d) is not the same of account price (%d)',[FData.AccountPrice,target.accountInfo.price]);
- exit;
- end;
- If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,errors)) then exit; // BUG 20171511
- _IsBuyTransaction := True;
- FPrevious_Seller_updated_block := seller.updated_block;
- end else if
- // Is automatic buy account?
- (FData.opTransactionStyle = transaction_with_auto_buy_account)
- Or // New automatic buy ?
- ( (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) And
- (FData.opTransactionStyle=transaction) And
- (TAccountComp.IsAccountForSaleAcceptingTransactions(target.accountInfo)) And
- (target.balance + FData.amount >= target.accountInfo.price) ) then begin
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'Tx-Buy account is not allowed on Protocol 1';
- exit;
- end;
- If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,errors)) then exit; // BUG 20171511
- _IsBuyTransaction := true; // Automatic buy
- // Fill the purchase data
- FData.opTransactionStyle := transaction_with_auto_buy_account; // Set this data!
- FData.AccountPrice := target.accountInfo.price;
- FData.SellerAccount := target.accountInfo.account_to_pay;
- seller := AccountTransaction.Account(target.accountInfo.account_to_pay);
- FPrevious_Seller_updated_block := seller.updated_block;
- FData.new_accountkey := target.accountInfo.new_publicKey;
- end else begin
- _IsBuyTransaction := false;
- end;
- if (_IsBuyTransaction) then begin
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'NOT ALLOWED ON PROTOCOL 1';
- exit;
- end;
- Result := AccountTransaction.BuyAccount(AccountPreviousUpdatedBlock,sender.account,target.account,seller.account,FData.n_operation,FData.amount,target.accountInfo.price,FData.fee,FData.new_accountkey,errors);
- end else begin
- Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.sender,FData.sender,FData.target,FData.n_operation,FData.amount,FData.fee,errors);
- end;
- end;
- function TOpTransaction.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
- Var ms : TMemoryStream;
- begin
- If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
- else begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.sender,Sizeof(FData.sender));
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.target,Sizeof(FData.target));
- ms.Write(FData.amount,Sizeof(FData.amount));
- ms.Write(FData.fee,Sizeof(FData.fee));
- if Length(FData.payload)>0 then
- ms.WriteBuffer(FData.payload[Low(FData.payload)],Length(FData.payload));
- ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- if Length(FData.public_key.x)>0 then
- ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
- if Length(FData.public_key.y)>0 then
- ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
- if Length(FData.sign.r)>0 then
- ms.WriteBuffer(FData.sign.r[Low(FData.sign.r)],Length(FData.sign.r));
- if Length(FData.sign.s)>0 then
- ms.WriteBuffer(FData.sign.s[Low(FData.sign.s)],Length(FData.sign.s));
- SetLength(Result,ms.Size);
- ms.Position := 0;
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- finally
- ms.Free;
- end;
- end;
- end;
- procedure TOpTransaction.InitializeData;
- begin
- inherited;
- FData := CT_TOpTransactionData_NUL;
- end;
- function TOpTransaction.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- var LAccount : TAccount;
- begin
- LAccount := ASafeBoxTransaction.Account(FData.sender);
- Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
- end;
- function TOpTransaction.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
- var b : Byte;
- begin
- Result := false;
- if Stream.Size-Stream.Position < 28 then exit; // Invalid stream
- Stream.Read(FData.sender,Sizeof(FData.sender));
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Read(FData.target,Sizeof(FData.target));
- Stream.Read(FData.amount,Sizeof(FData.amount));
- Stream.Read(FData.fee,Sizeof(FData.fee));
- if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
- if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
- if ((LoadExtendedData) Or (Self is TOpBuyAccount)) then begin
- If Stream.Read(b,1)<>1 then begin
- exit;
- end;
- case b of
- 0 : FData.opTransactionStyle := transaction;
- 1 : FData.opTransactionStyle := transaction_with_auto_buy_account;
- 2 : Begin
- FData.opTransactionStyle := buy_account;
- if (Not (Self is TOpBuyAccount)) then exit;
- End
- else exit;
- end;
- if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account]) then begin
- Stream.Read(FData.AccountPrice,SizeOf(FData.AccountPrice));
- Stream.Read(FData.SellerAccount,SizeOf(FData.SellerAccount));
- if Stream.Read(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID))<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.new_accountkey.x)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.new_accountkey.y)<0 then exit;
- end;
- end;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
- Result := true;
- end;
- procedure TOpTransaction.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
- SetLength(OperationResume.Senders,1);
- OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
- OperationResume.Senders[0].Account:=FData.sender;
- OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee);
- OperationResume.Senders[0].N_Operation:=FData.n_operation;
- OperationResume.Senders[0].Payload:=FData.payload;
- OperationResume.Senders[0].Signature:=FData.sign;
- case FData.opTransactionStyle of
- transaction : begin
- SetLength(OperationResume.Receivers,1);
- OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
- OperationResume.Receivers[0].Account:=FData.target;
- OperationResume.Receivers[0].Amount:=FData.amount;
- OperationResume.Receivers[0].Payload:=FData.payload;
- end;
- buy_account,transaction_with_auto_buy_account : begin
- SetLength(OperationResume.Receivers,2);
- OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
- OperationResume.Receivers[0].Account:=FData.target;
- OperationResume.Receivers[0].Amount:= (FData.amount - FData.AccountPrice);
- OperationResume.Receivers[0].Payload:=FData.payload;
- OperationResume.Receivers[1] := CT_TMultiOpReceiver_NUL;
- OperationResume.Receivers[1].Account:=FData.SellerAccount;
- OperationResume.Receivers[1].Amount:= FData.AccountPrice;
- OperationResume.Receivers[1].Payload:=FData.payload;
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account := FData.target;
- OperationResume.Changers[0].Changes_type := [public_key];
- OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
- end;
- end;
- end;
- function TOpTransaction.OperationAmount: Int64;
- begin
- Result := FData.amount;
- end;
- function TOpTransaction.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpTransaction.OperationPayload: TRawBytes;
- begin
- Result := FData.payload;
- end;
- class function TOpTransaction.OpType: Byte;
- begin
- Result := CT_Op_Transaction;
- end;
- function TOpTransaction.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
- Var b : Byte;
- begin
- Stream.Write(FData.sender,Sizeof(FData.sender));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.target,Sizeof(FData.target));
- Stream.Write(FData.amount,Sizeof(FData.amount));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
- TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
- if ((SaveExtendedData) Or (Self is TOpBuyAccount)) then begin
- case FData.opTransactionStyle of
- transaction : b:=0;
- transaction_with_auto_buy_account : b:=1;
- buy_account : b:=2;
- else raise Exception.Create('ERROR DEV 20170424-1');
- end;
- Stream.Write(b,1);
- if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account]) then begin
- Stream.Write(FData.AccountPrice,SizeOf(FData.AccountPrice));
- Stream.Write(FData.SellerAccount,SizeOf(FData.SellerAccount));
- Stream.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
- TStreamOp.WriteAnsiString(Stream,FData.new_accountkey.x);
- TStreamOp.WriteAnsiString(Stream,FData.new_accountkey.y);
- end;
- end;
- TStreamOp.WriteAnsiString(Stream,FData.sign.r);
- TStreamOp.WriteAnsiString(Stream,FData.sign.s);
- Result := true;
- end;
- function TOpTransaction.SignerAccount: Cardinal;
- begin
- Result := FData.sender;
- end;
- function TOpTransaction.DestinationAccount: Int64;
- begin
- Result:=FData.target;
- end;
- function TOpTransaction.SellerAccount: Int64;
- begin
- Case FData.opTransactionStyle of
- transaction_with_auto_buy_account, buy_account : Result := FData.SellerAccount;
- else Result:=inherited SellerAccount;
- end;
- end;
- function TOpTransaction.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- function TOpTransaction.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- Result := 0;
- If (FData.sender = account) then inc(Result, Int64(FData.amount+FData.fee) * (-1));
- If (FData.target = account) then begin
- if (FData.opTransactionStyle in [buy_account,transaction_with_auto_buy_account]) then inc(Result, FData.amount - FData.AccountPrice)
- else inc(Result,FData.amount);
- end;
- If ((FData.SellerAccount = account) And (FData.opTransactionStyle in [buy_account,transaction_with_auto_buy_account] )) then begin
- inc(Result, FData.AccountPrice)
- end;
- end;
- function TOpTransaction.toString: String;
- begin
- case FData.opTransactionStyle of
- transaction :
- Result := Format('Transaction from %s to %s amount:%s fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
- TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
- TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
- transaction_with_auto_buy_account :
- Result := Format('Transaction/Buy account %s by %s paying %s to %s amount:%s fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
- TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
- TAccountComp.FormatMoney(FData.AccountPrice), TAccountComp.AccountNumberToAccountTxtNumber(FData.SellerAccount),
- TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
- buy_account :
- Result := Format('Buy account %s by %s paying %s to %s amount:%s fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.target),
- TAccountComp.AccountNumberToAccountTxtNumber(FData.sender),
- TAccountComp.FormatMoney(FData.AccountPrice), TAccountComp.AccountNumberToAccountTxtNumber(FData.SellerAccount),
- TAccountComp.FormatMoney(FData.amount),TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
- else raise Exception.Create('ERROR DEV 20170424-2');
- end;
- end;
- function TOpTransaction.GetDigestToSign(current_protocol : Word): TRawBytes;
- Var ms : TMemoryStream;
- b : Byte;
- begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.sender,Sizeof(FData.sender));
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.target,Sizeof(FData.target));
- ms.Write(FData.amount,Sizeof(FData.amount));
- ms.Write(FData.fee,Sizeof(FData.fee));
- if Length(FData.payload)>0 then
- ms.WriteBuffer(FData.payload[Low(FData.payload)],Length(FData.payload));
- ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- if Length(FData.public_key.x)>0 then
- ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
- if Length(FData.public_key.y)>0 then
- ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
- if FData.opTransactionStyle=buy_account then begin
- ms.Write(FData.AccountPrice,Sizeof(FData.AccountPrice));
- ms.Write(FData.SellerAccount,Sizeof(FData.SellerAccount));
- ms.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
- if Length(FData.new_accountkey.x)>0 then
- ms.WriteBuffer(FData.new_accountkey.x[Low(FData.new_accountkey.x)],Length(FData.new_accountkey.x));
- if Length(FData.new_accountkey.y)>0 then
- ms.WriteBuffer(FData.new_accountkey.y[Low(FData.new_accountkey.y)],Length(FData.new_accountkey.y));
- end;
- if (current_protocol<=CT_PROTOCOL_3) then begin
- ms.Position := 0;
- SetLength(Result,ms.Size);
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- end else begin
- b := OpType;
- ms.Write(b,1);
- Result := TCrypto.DoSha256(ms.Memory,ms.Size);
- end;
- finally
- ms.Free;
- end;
- end;
- { TOpChangeKey }
- procedure TOpChangeKey.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.account_signer);
- if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
- end;
- function TOpChangeKey.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
- else Result := 0;
- end;
- constructor TOpChangeKey.Create(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
- begin
- inherited Create;
- FData.account_signer := account_signer;
- FData.account_target := account_target;
- If (OpType=CT_Op_Changekey) then begin
- If (account_signer<>account_target) then Raise Exception.Create('ERROR DEV 20170530-4');
- end else if (OpType=CT_Op_ChangeKeySigned) then begin
- // Allowed signer<>target
- end else Raise Exception.Create('ERROR DEV 20170530-5');
- FData.n_operation := n_operation;
- FData.fee := fee;
- FData.payload := payload;
- // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
- // FData.public_key := key.PublicKey;
- FData.new_accountkey := new_account_key;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new Change key');
- FHasValidSignature := false;
- end;
- end;
- function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
- Var account_signer, account_target : TAccount;
- begin
- Result := false;
- if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid account number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'account is blocked for protocol';
- Exit;
- end;
- If (FData.account_signer<>FData.account_target) then begin
- if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid account target number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Target account is blocked for protocol';
- Exit;
- end;
- end;
- if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := 'Invalid fee: '+Inttostr(FData.fee);
- exit;
- end;
- account_signer := AccountTransaction.Account(FData.account_signer);
- account_target := AccountTransaction.Account(FData.account_target);
- if ((account_signer.n_operation+1)<>FData.n_operation) then begin
- errors := 'Invalid n_operation';
- Exit;
- end;
- if (account_signer.balance<FData.fee) then begin
- errors := 'Insuficient founds';
- exit;
- end;
- if (length(FData.payload)>CT_MaxPayloadSize) then begin
- errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
- If (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
- Exit; // BUG from protocol 1
- end;
- end;
- // Is locked? Protocol 2 check
- if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Account signer is currently locked';
- exit;
- end;
- If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, errors ) then begin
- exit;
- end;
- // NEW v2 protocol protection: Does not allow to change key for same key
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
- if (TAccountComp.EqualAccountKeys(account_target.accountInfo.accountKey,FData.new_accountkey)) then begin
- errors := 'New public key is the same public key';
- exit;
- end;
- end;
- // Build 1.4
- 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
- errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
- FData.account_signer,
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
- exit;
- end;
- If (FData.account_signer<>FData.account_target) then begin
- if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Account target is currently locked';
- exit;
- end;
- // Check have same public key
- If Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
- errors := 'Signer and target accounts have different public key';
- exit;
- end;
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'NOT ALLOWED ON PROTOCOL 1';
- exit;
- end;
- end;
- // Check signature
- If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
- errors := 'Invalid ECDSA signature';
- Exit;
- end;
- FPrevious_Signer_updated_block := account_signer.updated_block;
- FPrevious_Destination_updated_block := account_target.updated_block;
- account_target.accountInfo.accountKey := FData.new_accountkey;
- // Set to normal:
- account_target.accountInfo.state := as_Normal;
- account_target.accountInfo.locked_until_block := 0;
- account_target.accountInfo.price := 0;
- account_target.accountInfo.account_to_pay := 0;
- account_target.accountInfo.new_publicKey := CT_TECDSA_Public_Nul;
- Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
- FData.account_signer,FData.n_operation,FData.account_target,
- account_target.accountInfo,
- account_target.name,
- account_target.account_type,
- FData.fee,errors);
- end;
- function TOpChangeKey.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
- var ms : TMemoryStream;
- raw : TRawBytes;
- begin
- If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
- else begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.account_signer,Sizeof(FData.account_signer)); //Protocol 1 does not allow signer/target. signer=target always
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.fee,Sizeof(FData.fee));
- if Length(FData.payload)>0 then
- ms.WriteBuffer(FData.payload[Low(FData.payload)],Length(FData.payload));
- ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- if Length(FData.public_key.x)>0 then
- ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
- if Length(FData.public_key.y)>0 then
- ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
- raw := TAccountComp.AccountKey2RawString(FData.new_accountkey);
- if Length(raw)>0 then
- ms.WriteBuffer(raw[Low(raw)],Length(raw));
- if Length(FData.sign.r)>0 then
- ms.WriteBuffer(FData.sign.r[Low(FData.sign.r)],Length(FData.sign.r));
- if Length(FData.sign.s)>0 then
- ms.WriteBuffer(FData.sign.s[Low(FData.sign.s)],Length(FData.sign.s));
- ms.Position := 0;
- setlength(Result,ms.Size);
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- finally
- ms.Free;
- end;
- end;
- end;
- procedure TOpChangeKey.InitializeData;
- begin
- inherited;
- FData := CT_TOpChangeKeyData_NUL;
- end;
- function TOpChangeKey.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- var LAccount : TAccount;
- begin
- LAccount := ASafeBoxTransaction.Account(FData.account_signer);
- Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
- end;
- function TOpChangeKey.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
- var raw : TRawBytes;
- begin
- Result := false;
- if Stream.Size-Stream.Position < 16 then exit; // Invalid stream
- Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
- If (OpType=CT_Op_ChangeKey) then begin
- FData.account_target:=FData.account_signer;
- end else if (OpType=CT_Op_ChangeKeySigned) then begin
- Stream.Read(FData.account_target,Sizeof(FData.account_target));
- end else Raise Exception.Create('ERROR DEV 20170530-1');
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Read(FData.fee,Sizeof(FData.fee));
- if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
- if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,raw)<0 then exit;
- FData.new_accountkey := TAccountComp.RawString2Accountkey(raw);
- if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
- Result := true;
- end;
- procedure TOpChangeKey.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account := FData.account_target;
- OperationResume.Changers[0].Changes_type := [public_key];
- OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
- if (FData.account_signer=FData.account_target) then begin
- OperationResume.Changers[0].N_Operation := FData.n_operation;
- OperationResume.Changers[0].Fee := FData.fee;
- OperationResume.Changers[0].Signature := FData.sign;
- end else begin
- SetLength(OperationResume.Changers,2);
- OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[1].Account := FData.account_signer;
- OperationResume.Changers[1].N_Operation := FData.n_operation;
- OperationResume.Changers[1].Fee := FData.fee;
- OperationResume.Changers[1].Signature := FData.sign;
- end;
- end;
- function TOpChangeKey.OperationAmount: Int64;
- begin
- Result := 0;
- end;
- function TOpChangeKey.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpChangeKey.OperationPayload: TRawBytes;
- begin
- Result := FData.payload;
- end;
- class function TOpChangeKey.OpType: Byte;
- begin
- Result := CT_Op_Changekey;
- end;
- function TOpChangeKey.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
- begin
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- If (OpType=CT_Op_ChangeKey) then begin
- If FData.account_target<>FData.account_signer then Raise Exception.Create('ERROR DEV 20170530-2');
- end else if (OpType=CT_Op_ChangeKeySigned) then begin
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- end else Raise Exception.Create('ERROR DEV 20170530-3');
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
- TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
- TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FData.new_accountkey));
- TStreamOp.WriteAnsiString(Stream,FData.sign.r);
- TStreamOp.WriteAnsiString(Stream,FData.sign.s);
- Result := true;
- end;
- function TOpChangeKey.SignerAccount: Cardinal;
- begin
- Result := FData.account_signer;
- end;
- function TOpChangeKey.DestinationAccount: Int64;
- begin
- Result := FData.account_target;
- end;
- function TOpChangeKey.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- function TOpChangeKey.toString: String;
- begin
- Result := Format('Change key of %s to new key: %s fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
- TAccountComp.GetECInfoTxt(FData.new_accountkey.EC_OpenSSL_NID),
- TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
- end;
- function TOpChangeKey.GetDigestToSign(current_protocol : Word): TRawBytes;
- var ms : TMemoryStream;
- raw : TRawBytes;
- b : Byte;
- begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.account_signer,Sizeof(FData.account_signer));
- if (FData.account_signer<>FData.account_target) then ms.Write(FData.account_target,Sizeof(FData.account_target));
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.fee,Sizeof(FData.fee));
- if Length(FData.payload)>0 then
- ms.WriteBuffer(FData.payload[Low(FData.payload)],Length(FData.payload));
- ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- if Length(FData.public_key.x)>0 then
- ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
- if Length(FData.public_key.y)>0 then
- ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
- raw := TAccountComp.AccountKey2RawString(FData.new_accountkey);
- if Length(raw)>0 then
- ms.WriteBuffer(raw[Low(raw)],Length(raw));
- if (current_protocol<=CT_PROTOCOL_3) then begin
- ms.Position := 0;
- SetLength(Result,ms.Size);
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- end else begin
- b := OpType;
- ms.Write(b,1);
- Result := TCrypto.DoSha256(ms.Memory,ms.Size);
- end;
- finally
- ms.Free;
- end;
- end;
- { TOpChangeKeySigned }
- class function TOpChangeKeySigned.OpType: Byte;
- begin
- Result:=CT_Op_ChangeKeySigned;
- end;
- { TOpRecoverFounds }
- procedure TOpRecoverFounds.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.account);
- end;
- constructor TOpRecoverFounds.Create(account_number, n_operation : Cardinal; fee: UInt64);
- begin
- inherited Create;
- FData.account := account_number;
- FData.n_operation := n_operation;
- FData.fee := fee;
- FHasValidSignature := true; // Recover founds doesn't need a signature
- end;
- function TOpRecoverFounds.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
- Var acc : TAccount;
- begin
- Result := false;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'account is blocked for protocol';
- Exit;
- end;
- acc := AccountTransaction.Account(FData.account);
- if (acc.updated_block + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- 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]);
- Exit;
- end;
- // Build 1.0.8 ... there was a BUG. Need to prevent recent created accounts
- if (TAccountComp.AccountBlock(FData.account) + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := Format('AccountBlock is active to recover founds! AccountBlock %d + %d >= BlockCount : %d',[TAccountComp.AccountBlock(FData.account),CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
- Exit;
- end;
- if ((acc.n_operation+1)<>FData.n_operation) then begin
- errors := 'Invalid n_operation';
- Exit;
- end;
- if (FData.fee<=0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := 'Invalid fee '+Inttostr(FData.fee);
- exit;
- end;
- if (acc.balance<FData.fee) then begin
- errors := 'Insuficient founds';
- exit;
- end;
- FPrevious_Signer_updated_block := acc.updated_block;
- Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.account,FData.account,FData.account,FData.n_operation,0,FData.fee,errors);
- end;
- function TOpRecoverFounds.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
- var ms : TMemoryStream;
- begin
- If UseProtocolV2 then Result := inherited GetBufferForOpHash(UseProtocolV2)
- else begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.account,Sizeof(FData.account));
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.fee,Sizeof(FData.fee));
- ms.Position := 0;
- SetLength(Result,ms.Size);
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- finally
- ms.Free;
- end;
- end;
- end;
- procedure TOpRecoverFounds.InitializeData;
- begin
- inherited;
- FData := CT_TOpRecoverFoundsData_NUL;
- end;
- function TOpRecoverFounds.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- begin
- Result := True; // Nobody signs here
- end;
- function TOpRecoverFounds.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
- begin
- Result := false;
- if Stream.Size - Stream.Position<16 then exit;
- Stream.Read(FData.account,Sizeof(FData.account));
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Read(FData.fee,Sizeof(FData.fee));
- Result := true;
- end;
- procedure TOpRecoverFounds.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account := FData.account;
- OperationResume.Changers[0].Fee := FData.fee;
- OperationResume.Changers[0].N_Operation := FData.n_operation;
- end;
- function TOpRecoverFounds.OperationAmount: Int64;
- begin
- Result := 0;
- end;
- function TOpRecoverFounds.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpRecoverFounds.OperationPayload: TRawBytes;
- begin
- SetLength(Result,0);
- end;
- class function TOpRecoverFounds.OpType: Byte;
- begin
- Result := CT_Op_Recover;
- end;
- function TOpRecoverFounds.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
- begin
- Stream.Write(FData.account,Sizeof(FData.account));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- Result := true;
- end;
- function TOpRecoverFounds.SignerAccount: Cardinal;
- begin
- Result := FData.account;
- end;
- function TOpRecoverFounds.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- function TOpRecoverFounds.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- if (FData.account = account) then Result := Int64(FData.fee)*(-1)
- else Result := 0;
- end;
- function TOpRecoverFounds.toString: String;
- begin
- Result := Format('Recover founds of account %s fee:%s (n_op:%d)',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account),
- TAccountComp.FormatMoney(FData.fee),fData.n_operation]);
- end;
- function TOpRecoverFounds.GetDigestToSign(current_protocol : Word): TRawBytes;
- begin
- SetLength(Result,0); // Nothing to be signed!
- end;
- { TOpListAccount }
- procedure TOpListAccount.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.account_signer);
- if FData.account_signer<>FData.account_target then
- list.Add(FData.account_target);
- end;
- function TOpListAccount.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- if (FData.account_signer = account) then Result := Int64(FData.fee)*(-1)
- else Result := 0;
- end;
- function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
- Var account_signer, account_target : TAccount;
- begin
- Result := false;
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
- errors := 'List/Delist Account is not allowed on Protocol 1';
- exit;
- end;
- if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid signer account number';
- Exit;
- end;
- if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid target account number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Signer account is blocked for protocol';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Target account is blocked for protocol';
- Exit;
- end;
- if Not IsDelist then begin
- if (FData.account_to_pay>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid account to pay number';
- Exit;
- end;
- if (FData.account_target = FData.account_to_pay) then begin
- errors := 'Account to pay is itself';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_to_pay, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Account to pay is blocked for protocol';
- Exit;
- end;
- if (FData.account_price<=0) then begin
- errors := 'Account for sale price must be > 0';
- exit;
- end;
- if (FData.locked_until_block > (AccountTransaction.FreezedSafeBox.BlocksCount + CT_MaxFutureBlocksLockedAccount)) then begin
- errors := 'Invalid locked block: Current block '+Inttostr(AccountTransaction.FreezedSafeBox.BlocksCount)+' cannot lock to block '+IntToStr(FData.locked_until_block);
- exit;
- end;
- if IsPrivateSale then begin
- If Not TAccountComp.IsValidAccountKey( FData.new_public_key, errors ) then begin
- errors := 'Invalid new public key: '+errors;
- exit;
- end;
- end;
- end;
- if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := 'Invalid fee: '+Inttostr(FData.fee);
- exit;
- end;
- account_signer := AccountTransaction.Account(FData.account_signer);
- account_target := AccountTransaction.Account(FData.account_target);
- if (FData.account_signer<>FData.account_target) then begin
- // Both accounts must have same PUBLIC KEY!
- if Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_target.accountInfo.accountKey) then begin
- errors := 'Signer and affected accounts have different public key';
- Exit;
- end;
- end;
- if ((account_signer.n_operation+1)<>FData.n_operation) then begin
- errors := 'Invalid n_operation';
- Exit;
- end;
- if (account_signer.balance<FData.fee) then begin
- errors := 'Insuficient founds';
- exit;
- end;
- if (length(FData.payload)>CT_MaxPayloadSize) then begin
- errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
- Exit;
- end;
- // Is locked?
- if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Signer account is currently locked';
- exit;
- end;
- if (TAccountComp.IsAccountLocked(account_target.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Target account is currently locked';
- exit;
- end;
- if (IsPrivateSale) then begin
- if TAccountComp.EqualAccountKeys(account_target.accountInfo.accountKey,FData.new_public_key) then begin
- errors := 'New public key for private sale is the same public key';
- Exit;
- end;
- end;
- //
- // Build 1.4
- 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
- errors := Format('Invalid public key for account %d. Distinct from SafeBox public key! %s <> %s',[
- FData.account_signer,
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(FData.public_key)),
- TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account_signer.accountInfo.accountkey))]);
- exit;
- end;
- // Check signature
- If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
- errors := 'Invalid ECDSA signature';
- Exit;
- end;
- FPrevious_Signer_updated_block := account_signer.updated_block;
- FPrevious_Destination_updated_block := account_target.updated_block;
- if IsDelist then begin
- account_target.accountInfo.state := as_Normal;
- account_target.accountInfo.locked_until_block := CT_AccountInfo_NUL.locked_until_block;
- account_target.accountInfo.price := CT_AccountInfo_NUL.price;
- account_target.accountInfo.account_to_pay := CT_AccountInfo_NUL.account_to_pay;
- account_target.accountInfo.new_publicKey := CT_AccountInfo_NUL.new_publicKey;
- end else begin
- account_target.accountInfo.state := as_ForSale;
- account_target.accountInfo.locked_until_block := FData.locked_until_block;
- account_target.accountInfo.price := FData.account_price;
- account_target.accountInfo.account_to_pay := FData.account_to_pay;
- account_target.accountInfo.new_publicKey := FData.new_public_key;
- end;
- Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,FData.account_signer,FData.n_operation,FData.account_target,
- account_target.accountInfo,
- account_target.name,
- account_target.account_type,
- FData.fee,errors);
- end;
- function TOpListAccount.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
- begin
- // This Operation is new from protocol V2, so we cannot hash it as a previous protocol!
- Result := inherited GetBufferForOpHash(true);
- end;
- procedure TOpListAccount.InitializeData;
- begin
- inherited;
- FData := CT_TOpListAccountData_NUL;
- end;
- function TOpListAccount.IsPrivateSale: Boolean;
- begin
- Result := (Not IsDelist) And (FData.new_public_key.EC_OpenSSL_NID<>0);
- end;
- function TOpListAccount.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- var LAccount : TAccount;
- begin
- LAccount := ASafeBoxTransaction.Account(FData.account_signer);
- Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
- end;
- function TOpListAccount.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
- var raw : TRawBytes;
- w : Word;
- begin
- Result := false;
- if Stream.Size-Stream.Position < 14 then exit; // Invalid stream
- Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Read(FData.account_target,Sizeof(FData.account_target));
- Stream.Read(w,2);
- case w of
- CT_Op_ListAccountForSale : FData.operation_type := lat_ListForSale;
- CT_Op_DelistAccount : FData.operation_type := lat_DelistAccount;
- else exit; // Invalid data info
- end;
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- if (FData.operation_type = lat_ListForSale) then begin
- Stream.Read(FData.account_price,Sizeof(FData.account_price));
- Stream.Read(FData.account_to_pay,Sizeof(FData.account_to_pay));
- if Stream.Read(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID))<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.x)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.public_key.y)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,raw)<0 then exit;
- FData.new_public_key := TAccountComp.RawString2Accountkey(raw);
- Stream.Read(FData.locked_until_block,Sizeof(FData.locked_until_block));
- end;
- Stream.Read(FData.fee,Sizeof(FData.fee));
- if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
- Result := true;
- end;
- procedure TOpListAccount.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account:=FData.account_target;
- case FData.operation_type of
- lat_ListForSale : begin
- if (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
- OperationResume.Changers[0].Changes_type:=[list_for_public_sale];
- end else begin
- OperationResume.Changers[0].Changes_type:=[list_for_private_sale];
- OperationResume.Changers[0].New_Accountkey := FData.new_public_key;
- OperationResume.Changers[0].Locked_Until_Block := FData.locked_until_block;
- end;
- OperationResume.Changers[0].Seller_Account:=FData.account_to_pay;
- OperationResume.Changers[0].Account_Price:=FData.account_price;
- end;
- lat_DelistAccount : begin
- OperationResume.Changers[0].Changes_type:=[delist];
- end;
- end;
- if (FData.account_signer = FData.account_target) then begin
- OperationResume.Changers[0].Fee:=FData.fee;
- OperationResume.Changers[0].N_Operation:=FData.n_operation;
- OperationResume.Changers[0].Signature:=FData.sign;
- end else begin
- SetLength(OperationResume.Changers,2);
- OperationResume.Changers[1] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[1].Account := FData.account_signer;
- OperationResume.Changers[1].N_Operation := FData.n_operation;
- OperationResume.Changers[1].Fee := FData.fee;
- OperationResume.Changers[1].Signature := FData.sign;
- end;
- end;
- function TOpListAccount.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- function TOpListAccount.OperationAmount: Int64;
- begin
- Result := 0;
- end;
- function TOpListAccount.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpListAccount.OperationPayload: TRawBytes;
- begin
- Result := FData.payload;
- end;
- function TOpListAccount.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
- Var w : Word;
- begin
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- case FData.operation_type of
- lat_ListForSale : w := CT_Op_ListAccountForSale;
- lat_DelistAccount : w := CT_Op_DelistAccount;
- else raise Exception.Create('ERROR DEV 20170412-1');
- end;
- Stream.Write(w,2);
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- if FData.operation_type=lat_ListForSale then begin
- Stream.Write(FData.account_price,Sizeof(FData.account_price));
- Stream.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
- Stream.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- TStreamOp.WriteAnsiString(Stream,FData.public_key.x);
- TStreamOp.WriteAnsiString(Stream,FData.public_key.y);
- TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountKey2RawString(FData.new_public_key));
- Stream.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
- end;
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- TStreamOp.WriteAnsiString(Stream,FData.sign.r);
- TStreamOp.WriteAnsiString(Stream,FData.sign.s);
- Result := true;
- end;
- function TOpListAccount.SignerAccount: Cardinal;
- begin
- Result := FData.account_signer;
- end;
- function TOpListAccount.DestinationAccount: Int64;
- begin
- Result := FData.account_target;
- end;
- function TOpListAccount.SellerAccount: Int64;
- begin
- Case FData.operation_type of
- lat_ListForSale : Result := FData.account_to_pay;
- else Result:=inherited SellerAccount;
- end;
- end;
- function TOpListAccount.toString: String;
- begin
- case FData.operation_type of
- lat_ListForSale : begin
- if (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
- Result := Format('List account %s for sale price %s locked until block:%d fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.account_price),
- FData.locked_until_block, TAccountComp.FormatMoney(FData.fee),
- FData.n_operation, Length(FData.payload)])
- end else begin
- 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',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.account_price),
- TAccountComp.GetECInfoTxt(FData.new_public_key.EC_OpenSSL_NID),
- FData.locked_until_block, TAccountComp.FormatMoney(FData.fee),
- FData.n_operation, Length(FData.payload)])
- end;
- end;
- lat_DelistAccount : begin
- Result := Format('Delist account %s for sale fee:%s (n_op:%d) payload size:%d',[
- TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target), TAccountComp.FormatMoney(FData.fee),
- FData.n_operation, Length(FData.payload)])
- end;
- else Result := 'ERROR DEV 20170414-2';
- end;
- end;
- function TOpListAccount.GetDigestToSign(current_protocol : Word): TRawBytes;
- var ms : TMemoryStream;
- s : TRawBytes;
- b : Byte;
- begin
- ms := TMemoryStream.Create;
- try
- ms.Write(FData.account_signer,Sizeof(FData.account_signer));
- ms.Write(FData.account_target,Sizeof(FData.account_target));
- ms.Write(FData.n_operation,Sizeof(FData.n_operation));
- ms.Write(FData.account_price,Sizeof(FData.account_price));
- ms.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
- ms.Write(FData.fee,Sizeof(FData.fee));
- if Length(FData.payload)>0 then
- ms.WriteBuffer(FData.payload[Low(FData.payload)],Length(FData.payload));
- ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
- if Length(FData.public_key.x)>0 then
- ms.WriteBuffer(FData.public_key.x[Low(FData.public_key.x)],Length(FData.public_key.x));
- if Length(FData.public_key.y)>0 then
- ms.WriteBuffer(FData.public_key.y[Low(FData.public_key.y)],Length(FData.public_key.y));
- s := TAccountComp.AccountKey2RawString(FData.new_public_key);
- if Length(s)>0 then
- ms.WriteBuffer(s[Low(s)],Length(s));
- ms.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
- if (current_protocol<=CT_PROTOCOL_3) then begin
- ms.Position := 0;
- SetLength(Result,ms.Size);
- ms.ReadBuffer(Result[Low(Result)],ms.Size);
- end else begin
- b := OpType;
- ms.Write(b,1);
- Result := TCrypto.DoSha256(ms.Memory,ms.Size);
- end;
- finally
- ms.Free;
- end;
- end;
- { TOpListAccountForSale }
- constructor TOpListAccountForSale.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);
- begin
- inherited Create;
- FData.account_signer := account_signer;
- FData.account_target := account_target;
- FData.operation_type := lat_ListForSale;
- FData.n_operation := n_operation;
- FData.account_price := account_price;
- FData.account_to_pay := account_to_pay;
- FData.fee := fee;
- FData.payload := payload;
- // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
- // FData.public_key := key.PublicKey;
- FData.new_public_key := new_public_key;
- FData.locked_until_block := locked_until_block;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new list account for sale operation');
- FHasValidSignature := false;
- end;
- end;
- function TOpListAccountForSale.IsDelist: Boolean;
- begin
- Result := False;
- end;
- class function TOpListAccountForSale.OpType: Byte;
- begin
- Result := CT_Op_ListAccountForSale;
- end;
- { TOpDelistAccountForSale }
- constructor TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
- begin
- inherited Create;
- FData.account_signer := account_signer;
- FData.account_target := account_target;
- FData.operation_type := lat_DelistAccount;
- FData.n_operation := n_operation;
- FData.fee := fee;
- FData.payload := payload;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a delist account operation');
- FHasValidSignature := false;
- end;
- end;
- function TOpDelistAccountForSale.IsDelist: Boolean;
- begin
- Result := True;
- end;
- class function TOpDelistAccountForSale.OpType: Byte;
- begin
- Result := CT_Op_DelistAccount;
- end;
- { TOpBuyAccount }
- constructor TOpBuyAccount.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);
- begin
- inherited Create;
- FData.sender := account_number;
- FData.n_operation := n_operation;
- FData.target := account_to_buy;
- FData.amount := amount;
- FData.fee := fee;
- FData.payload := payload;
- // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
- // FData.public_key := key.PublicKey;
- FData.opTransactionStyle := buy_account;
- FData.AccountPrice := price;
- FData.SellerAccount := account_to_pay;
- FData.new_accountkey := new_public_key;
- if Assigned(key) then begin
- FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new Buy operation');
- FHasValidSignature := false;
- end;
- end;
- procedure TOpBuyAccount.InitializeData;
- begin
- inherited;
- FData.opTransactionStyle := buy_account;
- end;
- class function TOpBuyAccount.OpType: Byte;
- begin
- Result := CT_Op_BuyAccount;
- end;
- { TOpData }
- procedure TOpData.InitializeData;
- begin
- inherited InitializeData;
- FData := CT_TOpDataData_NUL;
- end;
- function TOpData.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
- var LAccount : TAccount;
- begin
- LAccount := ASafeBoxTransaction.Account(FData.account_signer);
- Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
- end;
- function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
- begin
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.dataType,Sizeof(FData.dataType));
- Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
- Stream.Write(FData.amount,Sizeof(FData.amount));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- TStreamOp.WriteAnsiString(Stream,FData.sign.r);
- TStreamOp.WriteAnsiString(Stream,FData.sign.s);
- Result := true;
- end;
- function TOpData.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
- begin
- Result := false;
- if Stream.Size-Stream.Position < 36 then exit; // Invalid stream
- Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Read(FData.account_sender,Sizeof(FData.account_sender));
- Stream.Read(FData.account_target,Sizeof(FData.account_target));
- Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Read(FData.dataType,Sizeof(FData.dataType));
- Stream.Read(FData.dataSequence,Sizeof(FData.dataSequence));
- Stream.Read(FData.amount,Sizeof(FData.amount));
- Stream.Read(FData.fee,Sizeof(FData.fee));
- if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
- if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
- Result := true;
- end;
- procedure TOpData.FillOperationResume(Block: Cardinal;
- getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal;
- var OperationResume: TOperationResume);
- begin
- inherited FillOperationResume(Block, getInfoForAllAccounts,Affected_account_number, OperationResume);
- if (getInfoForAllAccounts) then
- OperationResume.OpSubtype := CT_OpSubtype_Data_GlobalInfo
- else if (Affected_account_number=FData.account_sender) then
- OperationResume.OpSubtype := CT_OpSubtype_Data_Sender
- else if (Affected_account_number=FData.account_target) then
- OperationResume.OpSubtype := CT_OpSubtype_Data_Receiver
- else if (Affected_account_number=FData.account_signer) then
- OperationResume.OpSubtype := CT_OpSubtype_Data_Signer;
- SetLength(OperationResume.Senders,1);
- OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
- OperationResume.Senders[0].Account:=FData.account_sender;
- OperationResume.Senders[0].Payload:=FData.payload;
- if (FData.account_sender=FData.account_signer) then begin
- OperationResume.Senders[0].N_Operation:=FData.n_operation;
- OperationResume.Senders[0].Signature:=FData.sign;
- OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee)*(-1);
- OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
- OperationResume.Senders[0].Data.Sequence := FData.dataSequence;
- OperationResume.Senders[0].Data.&Type := FData.dataType;
- end else begin
- OperationResume.Senders[0].Amount:=Int64(FData.amount)*(-1);
- SetLength(OperationResume.Changers,1);
- OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
- OperationResume.Changers[0].Account:=FData.account_signer;
- OperationResume.Changers[0].Fee:=FData.fee;
- OperationResume.Changers[0].N_Operation:=FData.n_operation;
- OperationResume.Changers[0].Signature:=FData.sign;
- OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
- OperationResume.Changers[0].Data.Sequence := FData.dataSequence;
- OperationResume.Changers[0].Data.&Type := FData.dataType;
- end;
- //
- SetLength(OperationResume.Receivers,1);
- OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
- OperationResume.Receivers[0].Account:=FData.account_target;
- OperationResume.Receivers[0].Amount:=FData.amount;
- OperationResume.Receivers[0].Payload:=FData.payload;
- OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
- OperationResume.Receivers[0].Data.Sequence := FData.dataSequence;
- OperationResume.Receivers[0].Data.&Type := FData.dataType;
- //
- OperationResume.n_operation:=FData.n_operation;
- if (Affected_account_number = FData.account_signer) or (getInfoForAllAccounts) then begin
- OperationResume.Fee:=Int64(FData.fee)*(-1);
- end else OperationResume.Fee:=0;
- OperationResume.OperationTxt := ToString;
- if (getInfoForAllAccounts) then OperationResume.Amount:= OperationAmount
- else OperationResume.Amount := OperationAmountByAccount(Affected_account_number);
- end;
- class function TOpData.OpType: Byte;
- begin
- Result := CT_Op_Data;
- end;
- function TOpData.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
- begin
- Result:=inherited GetBufferForOpHash(UseProtocolV2);
- end;
- function TOpData.DoOperation(
- AccountPreviousUpdatedBlock: TAccountPreviousBlockInfo;
- AccountTransaction: TPCSafeBoxTransaction; var errors: String): Boolean;
- Var account_signer, account_sender, account_target : TAccount;
- begin
- Result := false;
- if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_4) then begin
- errors := 'OpData is not allowed on Protocol < 4';
- exit;
- end;
- if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid signer account number';
- Exit;
- end;
- if (FData.account_sender>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid sender account number';
- Exit;
- end;
- if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
- errors := 'Invalid target account number';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Signer account is blocked for protocol';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_sender, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Sender account is blocked for protocol';
- Exit;
- end;
- if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
- errors := 'Target account is blocked for protocol';
- Exit;
- end;
- if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
- errors := 'Invalid fee: '+Inttostr(FData.fee);
- exit;
- end;
- if (FData.amount<0) Or (FData.amount>CT_MaxTransactionAmount) then begin
- errors := 'Invalid amount: '+Inttostr(FData.fee);
- exit;
- end;
- account_signer := AccountTransaction.Account(FData.account_signer);
- account_sender := AccountTransaction.Account(FData.account_sender);
- account_target := AccountTransaction.Account(FData.account_target);
- // Is signer locked?
- if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Signer account is currently locked';
- exit;
- end;
- if (FData.account_signer<>FData.account_sender) then begin
- // Both accounts must have same PUBLIC KEY!
- if Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_sender.accountInfo.accountKey) then begin
- errors := 'Signer and sender accounts have different public key';
- Exit;
- end;
- if (account_signer.balance<FData.fee) then begin
- errors := 'Insuficient signer funds';
- exit;
- end;
- if (account_sender.balance<FData.amount) then begin
- errors := 'Insuficient sender funds';
- exit;
- end;
- // Is sender locked?
- if (TAccountComp.IsAccountLocked(account_sender.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
- errors := 'Sender account is currently locked';
- exit;
- end;
- end else begin
- // signer = sender
- if (account_signer.balance<(FData.fee + FData.amount)) then begin
- errors := 'Insuficient funds';
- exit;
- end;
- end;
- if ((account_signer.n_operation+1)<>FData.n_operation) then begin
- errors := 'Invalid n_operation';
- Exit;
- end;
- if (length(FData.payload)>CT_MaxPayloadSize) then begin
- errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
- Exit;
- end;
- // Check signature
- If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
- errors := 'Invalid ECDSA signature';
- Exit;
- end;
- Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,
- FData.account_sender,FData.account_signer,FData.account_target,
- FData.n_operation,FData.amount,FData.fee,errors);
- end;
- function TOpData.OperationAmount: Int64;
- begin
- Result := FData.amount + FData.fee;
- end;
- function TOpData.OperationFee: Int64;
- begin
- Result := FData.fee;
- end;
- function TOpData.OperationPayload: TRawBytes;
- begin
- Result := FData.payload;
- end;
- function TOpData.SignerAccount: Cardinal;
- begin
- Result := FData.account_signer;
- end;
- function TOpData.DestinationAccount: Int64;
- begin
- Result := FData.account_target;
- end;
- function TOpData.N_Operation: Cardinal;
- begin
- Result := FData.n_operation;
- end;
- procedure TOpData.AffectedAccounts(list: TList<Cardinal>);
- begin
- list.Add(FData.account_signer);
- if (FData.account_signer<>FData.account_sender) then begin
- list.Add(FData.account_sender);
- end;
- if (FData.account_signer<>FData.account_target) And (FData.account_sender<>FData.account_target) then begin
- list.Add(FData.account_target);
- end;
- end;
- function TOpData.OperationAmountByAccount(account: Cardinal): Int64;
- begin
- Result := 0;
- if (account = FData.account_signer) then begin
- Result := Int64(FData.fee) * (-1);
- end;
- if (account = FData.account_sender) then begin
- Result := Result + (Int64(FData.amount)*(-1));
- end;
- if (account = FData.account_target) then begin
- Result := Result + FData.amount;
- end;
- end;
- constructor TOpData.CreateOpData(account_signer, account_sender,
- account_target: Cardinal; signer_key: TECPrivateKey; n_operation: Cardinal;
- dataType, dataSequence: Word; amount, fee: UInt64; payload: TRawBytes);
- begin
- Inherited Create;
- FData.account_sender:=account_sender;
- FData.account_signer:=account_signer;
- FData.account_target:=account_target;
- FData.amount:=amount;
- FData.fee:=fee;
- FData.n_operation:=n_operation;
- FData.payload:=payload;
- FData.dataSequence:=dataSequence;
- FData.dataType:=dataType;
- if Assigned(signer_key) then begin
- FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(CT_PROTOCOL_4));
- FHasValidSignature := true;
- FUsedPubkeyForSignature := signer_key.PublicKey;
- end else begin
- TLog.NewLog(ltdebug,Classname,'No key for signing a new OpData');
- FHasValidSignature := false;
- end;
- end;
- function TOpData.toString: String;
- begin
- Result := Format('OpData from:%d to:%d type:%d sequence:%d Amount:%s',
- [FData.account_sender,FData.account_target,FData.dataType,FData.dataSequence,
- TAccountComp.FormatMoney(FData.amount)]);
- end;
- function TOpData.GetDigestToSign(current_protocol: Word): TRawBytes;
- var Stream : TStream;
- b : Byte;
- begin
- Stream := TMemoryStream.Create;
- Try
- Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
- Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
- Stream.Write(FData.account_target,Sizeof(FData.account_target));
- Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
- Stream.Write(FData.dataType,Sizeof(FData.dataType));
- Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
- Stream.Write(FData.amount,Sizeof(FData.amount));
- Stream.Write(FData.fee,Sizeof(FData.fee));
- TStreamOp.WriteAnsiString(Stream,FData.payload);
- b := OpType;
- Stream.Write(b,1);
- Result := TStreamOp.SaveStreamToRaw(Stream);
- finally
- Stream.Free;
- end;
- end;
- initialization
- RegisterOperationsClass;
- end.
|