forked from PascalCoinDev/PascalCoin
/
UAccounts.pas
6662 lines (6227 loc) · 263 KB
/
UAccounts.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit UAccounts;
{ 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
{$I ./../config.inc}
uses
Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
UPCHardcodedRandomHashTable, UJSONFunctions,
{$IFDEF USE_ABSTRACTMEM}
UPCAbstractMem, UPCAbstractMemAccountKeys,
{$ELSE}
{$ENDIF}
UPCAccountsOrdenations,
{$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
Type
{ TPascalCoinProtocol }
TPascalCoinProtocol = Class
public
FPCHardcodedRandomHashTable : TPCHardcodedRandomHashTable;
public
constructor Create;
destructor Destroy; override;
Class Function MinimumTarget(protocol_version : Integer): Cardinal;
Class Function ResetTarget(current_target : Cardinal; protocol_version : Integer): Cardinal;
Class Function GetRewardForNewLine(line_index: Cardinal): UInt64;
Class Function CalcTotalBalance(ABlockCount : Cardinal): Int64;
Class Function TargetToCompact(target: TRawBytes; protocol_version : Integer): Cardinal;
Class Function TargetFromCompact(encoded: Cardinal; protocol_version : Integer): TRawBytes;
Class Function GetNewTarget(vteorical, vreal: Cardinal; protocol_version : Integer; isSlowMovement : Boolean; Const actualTarget: TRawBytes): TRawBytes;
Class Procedure CalcProofOfWork_Part1(const operationBlock : TOperationBlock; out Part1 : TRawBytes);
Class Procedure CalcProofOfWork_Part3(const operationBlock : TOperationBlock; out Part3 : TRawBytes);
Class Procedure CalcProofOfWork(const operationBlock : TOperationBlock; out PoW : TRawBytes);
Class Function IsValidMinerBlockPayload(const newBlockPayload : TRawBytes) : Boolean;
class procedure GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
class Function CalcSafeBoxHash(ABlocksHashBuffer : TBytesBuffer; protocol_version : Integer) : TRawBytes;
class Function AllowUseHardcodedRandomHashTable(const AHardcodedFileName : String; const AHardcodedSha256Value : TRawBytes) : Boolean;
end;
TAccount_Helper = record helper for TAccount
procedure SerializeAccount(AStream : TStream; current_protocol : Word);
end;
{ TAccountComp }
TAccountComp = Class
private
public
Class Function IsValidAccountKey(const AAccountInfo: TAccountKey; ACurrentProtocol : Word; var errors : String): Boolean;
Class function IsNullAccountKey(const AAccountInfo : TAccountKey) : Boolean;
Class function IsValidNewAccountKey(const AAccountInfo : TAccountInfo; const ANewKey : TAccountKey; AProtocolVersion : Integer) : Boolean;
Class Function IsValidAccountInfo(const AAccountInfo: TAccountInfo; ACurrentProtocol : Word; var errors : String): Boolean;
Class Function IsValidAccountInfoHashLockKey(const AAccountInfo : TAccountInfo; const AKey : TRawBytes) : Boolean;
Class Function IsValidHashLockKey(const AKey : TRawBytes; out AError : String) : Boolean;
Class Function CalculateHashLock(const AKey : TRawBytes) : T32Bytes;
Class Function IsAccountForSale(const AAccountInfo: TAccountInfo) : Boolean;
Class function IsAccountForPrivateSale(const AAccountInfo: TAccountInfo): Boolean;
Class function IsAccountForPublicSale(const AAccountInfo: TAccountInfo): Boolean;
Class Function IsAccountForSwap(const AAccountInfo: TAccountInfo) : Boolean;
Class function IsAccountForCoinSwap(const AAccountInfo: TAccountInfo) : Boolean;
Class function IsAccountForAccountSwap(const AAccountInfo: TAccountInfo) : Boolean;
Class Function IsAccountForSaleOrSwap(const AAccountInfo: TAccountInfo) : Boolean;
Class Function IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word; const APayload : TRawBytes) : Boolean;
Class Function IsOperationRecipientSignable(const ASender, ATarget : TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
Class Function GetECInfoTxt(Const EC_OpenSSL_NID: Word) : String;
Class Function IsValidEC_OpenSSL_NID(ANID : Word) : Boolean;
Class Procedure ValidsEC_OpenSSL_NID(list : TList<Word>);
Class Function AccountKey2RawString(const account: TAccountKey): TRawBytes; overload;
Class procedure AccountKey2RawString(const account: TAccountKey; var dest: TRawBytes); overload;
Class Function RawString2Accountkey(const rawaccstr: TRawBytes): TAccountKey; overload;
Class procedure RawString2Accountkey(const rawaccstr: TRawBytes; var dest: TAccountKey); overload;
Class Function PrivateToAccountkey(key: TECPrivateKey): TAccountKey;
Class Function IsAccountBlockedByProtocol(account_number, blocks_count : Cardinal) : Boolean;
Class Function EqualAccountInfos(const accountInfo1,accountInfo2 : TAccountInfo) : Boolean;
Class Function EqualAccountKeys(const account1,account2 : TAccountKey) : Boolean;
Class Function EqualAccounts(const account1,account2 : TAccount) : Boolean;
Class Function EqualOperationBlocks(const opBlock1,opBlock2 : TOperationBlock) : Boolean;
Class Function EqualBlockAccounts(const blockAccount1,blockAccount2 : TBlockAccount) : Boolean;
Class Function AccountNumberToAccountTxtNumber(account_number : Cardinal) : String;
Class function AccountTxtNumberToAccountNumber(Const account_txt_number : String; var account_number : Cardinal) : Boolean;
Class function FormatMoney(Money : Int64) : String;
Class function FormatMoneyDecimal(Money : Int64) : Currency;
Class Function TxtToMoney(Const moneytxt : String; var money : Int64) : Boolean;
Class Function AccountKeyFromImport(Const HumanReadable : String; var account : TAccountKey; var errors : String) : Boolean;
Class Function AccountPublicKeyExport(Const account : TAccountKey) : String;
Class Function AccountPublicKeyImport(Const HumanReadable : String; var account : TAccountKey; var errors : String) : Boolean;
Class Function AccountBlock(Const account_number : Cardinal) : Cardinal;
Class Function AccountInfo2RawString(const AccountInfo : TAccountInfo) : TRawBytes; overload;
Class procedure AccountInfo2RawString(const AccountInfo : TAccountInfo; var dest : TRawBytes); overload;
Class procedure SaveAccountToAStream(Stream: TStream; const Account : TAccount; current_protocol : Word);
Class function LoadAccountFromStream(Stream: TStream; var Account : TAccount) : Boolean;
Class Function RawString2AccountInfo(const rawaccstr: TRawBytes): TAccountInfo; overload;
Class procedure RawString2AccountInfo(const rawaccstr: TRawBytes; var dest : TAccountInfo); overload;
Class Function IsAccountLocked(const AccountInfo : TAccountInfo; blocks_count : Cardinal) : Boolean;
Class procedure SaveTOperationBlockToStream(const stream : TStream; const operationBlock:TOperationBlock);
Class Function LoadTOperationBlockFromStream(const stream : TStream; var operationBlock:TOperationBlock) : Boolean;
Class Function AccountToTxt(const Account : TAccount) : String;
Class Function AccountCanRecover(const Account: TAccount; currentBlockCount: Cardinal; ASafeboxCurrentProtocol : Integer) : Boolean;
End;
TPCSafeBox = Class;
TAccountKeyArray = array of TAccountKey;
TAccountList = TList<TAccount>;
// This is a class to quickly find accountkeys and their respective account number/s
{ TOrderedAccountKeysList }
{$IFDEF USE_ABSTRACTMEM}
TAccountsNumbersList = TAccountsUsingThisKey;
{$ELSE}
TAccountsNumbersList = TOrderedCardinalList;
{$ENDIF}
TOrderedAccountKeysList = Class
Private
FAutoAddAll : Boolean;
FAccountList : TPCSafeBox;
FOrderedAccountKeysList : TPCThreadList<Pointer>; // An ordered list of pointers to quickly find account keys in account list
FTotalChanges : Integer;
Function Find(lockedList : TList<Pointer>; Const AccountKey: TAccountKey; var Index: Integer): Boolean;
function GetAccountKeyChanges(index : Integer): Integer;
function GetAccountKeyList(index: Integer): TAccountsNumbersList;
function GetAccountKey(index: Integer): TAccountKey;
protected
Procedure ClearAccounts(RemoveAccountList : Boolean);
public
Constructor Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
Destructor Destroy; override;
Procedure AddAccountKey(Const AccountKey : TAccountKey);
Procedure AddAccountKeys(Const AccountKeys : array of TAccountKey);
Procedure RemoveAccountKey(Const AccountKey : TAccountKey);
{$IFnDEF USE_ABSTRACTMEM}
Procedure AddAccounts(Const AccountKey : TAccountKey; const accounts : Array of Cardinal);
Procedure RemoveAccounts(Const AccountKey : TAccountKey; const accounts : Array of Cardinal);
{$ENDIF}
Function IndexOfAccountKey(Const AccountKey : TAccountKey) : Integer;
Property AccountKeyList[index : Integer] : TAccountsNumbersList read GetAccountKeyList;
Property AccountKey[index : Integer] : TAccountKey read GetAccountKey;
Property AccountKeyChanges[index : Integer] : Integer read GetAccountKeyChanges;
procedure ClearAccountKeyChanges;
Function Count : Integer;
Property SafeBox : TPCSafeBox read FAccountList;
Procedure Clear;
function ToArray : TAccountKeyArray;
function Lock : TList<Pointer>;
procedure Unlock;
function HasAccountKeyChanged : Boolean;
procedure CopyFrom(const source : TOrderedAccountKeysList);
function GetAccountsUsingThisKey(Const AAccountKey : TAccountKey) : TAccountsNumbersList;
End;
// SafeBox is a box that only can be updated using SafeBoxTransaction, and this
// happens only when a new BlockChain is included. After this, a new "SafeBoxHash"
// is created, so each SafeBox has a unique SafeBoxHash
TPCSafeBoxTransaction = Class;
TOrderedAccountList = Class;
TOrderedBlockAccountList = Class;
{ TProgressNotify }
TProgressNotify = procedure(sender : TObject; const message : String; curPos, totalCount : Int64) of object;
{ TProgressNotifyMany }
TProgressNotifyMany = TArray<TProgressNotify>;
{ TProgressNotifyManyHelper }
TProgressNotifyManyHelper = record helper for TProgressNotifyMany
procedure Add(listener : TProgressNotify);
procedure Remove(listener : TProgressNotify);
procedure Invoke(sender : TObject; const message : String; curPos, totalCount : Int64);
end;
{$IFDEF USE_ABSTRACTMEM}
TSafeboxPubKeysAndAccounts = TPCAbstractMemAccountKeys;
{$ELSE}
TSafeboxPubKeysAndAccounts = TOrderedAccountKeysList;
{$ENDIF}
{ TPCSafeBox }
TAccountUpdateStyle = (aus_transaction_commit, aus_rollback, aus_commiting_from_otherchain);
TPCSafeBox = Class
private
{$IFDEF USE_ABSTRACTMEM}
FPCAbstractMem : TPCAbstractMem;
{$ELSE}
FBlockAccountsList : TList<Pointer>; // Used when has no PreviousSafebox
FBufferBlocksHash: TBytesBuffer32Safebox;
FAggregatedHashrate : TBigNum;
FOrderedByName : TOrderedRawList;
// OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
FOrderedAccountKeysList : TSafeboxPubKeysAndAccounts;
FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
{$ENDIF}
FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
//
FListOfOrderedAccountKeysList : TList<TOrderedAccountKeysList>;
FTotalBalance: Int64;
FSafeBoxHash : TRawBytes;
FLock: TPCCriticalSection; // Thread safe
FWorkSum : UInt64;
FCurrentProtocol: Integer;
// Snapshots utility new on V3
FSnapshots : TList<Pointer>; // Will save a Snapshots lists in order to rollback Safebox to a previous block state
FMaxSafeboxSnapshots : Integer;
// To be added to next snapshot
FModifiedBlocksPreviousState : TOrderedBlockAccountList;
FModifiedBlocksFinalState : TOrderedBlockAccountList;
FAddedNamesSincePreviousSafebox : TOrderedRawList;
FDeletedNamesSincePreviousSafebox : TOrderedRawList;
// Is capturing data from a snapshot?
FPreviousSafeBox : TPCSafeBox; // PreviousSafebox is the Safebox with snpashots where this safebox searches
FPreviousSafeboxOriginBlock : Integer;
// Has chains based on this Safebox?
FSubChains : TList<TPCSafeBox>; // Will link to subchains (other safebox) based on a current snapshot of this safebox
//
Procedure AccountKeyListAddAccounts(Const AccountKey : TAccountKey; const accounts : Array of Cardinal);
Procedure AccountKeyListRemoveAccount(Const AccountKey : TAccountKey; const accounts : Array of Cardinal);
// V3
procedure SearchBlockWhenOnSeparatedChain(blockNumber : Cardinal; out blockAccount : TBlockAccount);
function GetAggregatedHashrate: TBigNum;
function GetOrderedAccountKeysList: TSafeboxPubKeysAndAccounts;
function GetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
protected
FTotalFee: Int64;
Procedure UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo; const newName : TRawBytes; newType : Word;
newBalance: UInt64; newN_operation: Cardinal;
const newAccountData, newAccountSeal : TRawBytes;
accountUpdateStyle : TAccountUpdateStyle; newUpdated_block_pasive_mode, newUpdated_block_active_mode : Cardinal;
AHasBenUpdatedOnActiveMode, AHasBenUpdatedOnPasiveMode : Boolean);
Function AddNew(Const blockChain : TOperationBlock) : TBlockAccount;
function DoUpgradeToProtocol2 : Boolean;
function DoUpgradeToProtocol3 : Boolean;
function DoUpgradeToProtocol4 : Boolean;
function DoUpgradeToProtocol5 : Boolean;
function DoUpgradeToProtocol6 : Boolean;
function BufferBlocksHash : TBytesBuffer32Safebox;
public
Constructor Create;
Destructor Destroy; override;
procedure SetToPrevious(APreviousSafeBox : TPCSafeBox; StartBlock : Cardinal);
procedure CommitToPrevious;
procedure RollBackToSnapshot(snapshotBlock : Cardinal);
function AccountsCount: Integer;
Function BlocksCount : Integer;
Procedure CopyFrom(accounts : TPCSafeBox);
Class Function CalcBlockHash(const block : TBlockAccount; current_protocol : Word):TRawBytes;
Class Function BlockAccountToText(Const block : TBlockAccount):String;
Function LoadSafeBoxChunkFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var ALastReadBlock : TBlockAccount; var errors : String) : Boolean;
Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; var LastReadBlock : TBlockAccount; var errors : String) : Boolean; overload;
Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var ALastReadBlock : TBlockAccount; var errors : String) : Boolean; overload;
Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader; out AStreamFinalPos : Int64) : Boolean; overload;
Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader) : Boolean; overload;
Class Function SaveSafeBoxStreamHeader(Stream : TStream; protocol : Word; OffsetStartBlock, OffsetEndBlock, CurrentSafeBoxBlocksCount : Cardinal) : Boolean;
Class Function MustSafeBoxBeSaved(BlocksCount : Cardinal) : Boolean;
Class Function InitialSafeboxHash : TRawBytes;
Class Procedure SaveSafeBoxBlockToAStream(ADestStream : TStream; ACurrentProtocol : Integer; const ABlock : TBlockAccount);
Procedure SaveSafeBoxToAStream(Stream : TStream; FromBlock, ToBlock : Cardinal);
class Function CopySafeBoxStream(Source,Dest : TStream; FromBlock, ToBlock : Cardinal; var errors : String) : Boolean;
class Function ConcatSafeBoxStream(Source1, Source2, Dest : TStream; var errors : String) : Boolean;
class function ValidAccountName(const new_name : TRawBytes; var errors : String) : Boolean;
Function IsValidNewOperationsBlock(Const newOperationBlock : TOperationBlock; checkSafeBoxHash, checkValidOperationsBlock : Boolean; var errors : String) : Boolean;
class Function IsValidOperationBlock(Const newOperationBlock : TOperationBlock; var errors : String) : Boolean;
Function GetActualTargetHash(protocolVersion : Word): TRawBytes;
Function GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
Function FindAccountByName(const aName : String) : Integer; overload;
Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
Procedure Clear;
Function Account(account_number : Cardinal) : TAccount;
Function GetBlock(block_number : Cardinal) : TBlockAccount;
Function GetBlockInfo(ABlockNumber : Cardinal) : TOperationBlock;
Function CalcSafeBoxHash : TRawBytes;
Function CalcBlockHashRateInKhs(block_number : Cardinal; Previous_blocks_average : Cardinal) : Int64;
Function CalcBlockHashRateInHs(block_number : Cardinal; Previous_blocks_average : Cardinal) : TBigNum;
Property TotalBalance : Int64 read FTotalBalance;
Procedure StartThreadSafe;
Procedure EndThreadSave;
Property SafeBoxHash : TRawBytes read FSafeBoxHash;
Property WorkSum : UInt64 read FWorkSum;
Property CurrentProtocol : Integer read FCurrentProtocol;
function CanUpgradeToProtocol(newProtocolVersion : Word) : Boolean;
procedure CheckMemory;
Property PreviousSafeboxOriginBlock : Integer Read FPreviousSafeboxOriginBlock;
Function GetMinimumAvailableSnapshotBlock : Integer;
Function HasSnapshotForBlock(block_number : Cardinal) : Boolean;
Property OrderedAccountKeysList : TSafeboxPubKeysAndAccounts read GetOrderedAccountKeysList;
Property AggregatedHashrate : TBigNum read GetAggregatedHashrate;
procedure GetAggregatedHashrateOnBlock(ABlockNumber : Cardinal; const AAggregatedHashrate : TBigNum);
{$IFDEF USE_ABSTRACTMEM}
procedure SetSafeboxFileName(ASafeboxFileName : String);
procedure SaveCheckpointing(ACheckpointingSafeboxFileName : String);
procedure UpdateSafeboxFileName(const ANewSafeboxFileName : String);
procedure ClearSafeboxfile;
class Function CopyAbstractMemToSafeBoxStream(ASource : TPCAbstractMem; ADestStream : TStream; AFromBlock, AToBlock : Cardinal; var AErrors : String) : Boolean;
property PCAbstractMem : TPCAbstractMem read FPCAbstractMem;
{$ENDIF}
Function AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
Function AccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
End;
{ TOrderedBlockAccountList }
TOrderedBlockAccountList = Class
private
FMaxBlockNumber : Integer;
FList : TList<Pointer>;
Function Find(const block_number: Cardinal; out Index: Integer): Boolean;
Function SaveBlockAccount(Const blockAccount : TBlockAccount; UpdateIfFound : Boolean) : Integer;
public
Constructor Create;
Destructor Destroy; Override;
Procedure Clear;
Function AddIfNotExists(Const blockAccount : TBlockAccount) : Integer;
Function Add(Const blockAccount : TBlockAccount) : Integer;
Function Count : Integer;
Function Get(index : Integer) : TBlockAccount;
Function MaxBlockNumber : Integer;
End;
TOrderedAccountList = Class
private
FList : TList<Pointer>;
Function Find(const account_number: Cardinal; var Index: Integer): Boolean;
public
Constructor Create;
Destructor Destroy; Override;
Procedure Clear;
Function Add(Const account : TAccount) : Integer;
Function Count : Integer;
Function Get(index : Integer) : TAccount;
Function IndexOf(account_number: Cardinal) : Integer;
End;
TAccountPreviousBlockInfoData = Record
Account : Cardinal;
Previous_updated_block : Cardinal;
end;
{ TAccountPreviousBlockInfo }
TAccountPreviousBlockInfo = Class
private
FList : TList<Pointer>;
Function FindAccount(const account: Cardinal; var Index: Integer): Boolean;
function GetData(index : Integer): TAccountPreviousBlockInfoData;
public
Constructor Create;
Destructor Destroy; override;
Procedure UpdateIfLower(account, previous_updated_block : Cardinal);
Function Add(account, previous_updated_block : Cardinal) : Integer;
Procedure Remove(account : Cardinal);
Procedure Clear;
Procedure CopyFrom(Sender : TAccountPreviousBlockInfo);
Function IndexOfAccount(account : Cardinal) : Integer;
Property Data[index : Integer] : TAccountPreviousBlockInfoData read GetData;
Function GetPreviousUpdatedBlock(account : Cardinal; defaultValue : Cardinal) : Cardinal;
Function Count : Integer;
procedure SaveToStream(stream : TStream);
function LoadFromStream(stream : TStream) : Boolean;
end;
{ TPCSafeBoxTransaction }
TPCSafeBoxTransaction = Class
private
type
TSealedAccount = Record
LatestOpIDUsedForSeal : TRawBytes;
AccountSealed : PAccount;
SealChangesCounter : Integer;
UsedAsPasiveMode : Boolean;
UsedAsActiveMode : Boolean;
End;
PSealedAccount = ^TSealedAccount;
{TSealedAccountList}
TSealedAccountList = Class
private
FSafeBoxTransaction : TPCSafeBoxTransaction;
FList : TList<Pointer>;
Function Find(const account_number: Cardinal; var Index: Integer): Boolean;
public
Constructor Create(ASafeBoxTransaction : TPCSafeBoxTransaction);
Destructor Destroy; Override;
Procedure Clear;
function Count : Integer;
function GetAccount_Whitout_Sealing(account_number: Cardinal) : PSealedAccount;
procedure DoUpdateSealIfNeeded(APtrSealedAccount : PSealedAccount; const AOpID : TRawBytes);
procedure CopyFrom(ASource : TSealedAccountList);
End;
private
FOrderedList : TSealedAccountList;
FFreezedAccounts : TPCSafeBox;
FTotalBalance: Int64;
FTotalFee: Int64;
FOldSafeBoxHash : TRawBytes;
FAccountNames_Deleted : TOrderedRawList;
FAccountNames_Added : TOrderedRawList;
Function Origin_BlocksCount : Cardinal;
Function Origin_SafeboxHash : TRawBytes;
Function Origin_TotalBalance : Int64;
Function Origin_TotalFee : Int64;
Function Origin_FindAccountByName(const account_name : TRawBytes) : Integer;
protected
Function GetInternalAccount(account_number : Cardinal; var APtrSealedAccount : PSealedAccount) : PAccount;
procedure UpdateSealAndActiveModeFlag(APtrSealedAccount : PSealedAccount; AOpID : TRawBytes; ASetUsedAsActiveMode : Boolean);
public
Constructor Create(SafeBox : TPCSafeBox);
Destructor Destroy; override;
Function TransferAmount(previous : TAccountPreviousBlockInfo; const AOpID : TRawBytes; sender,signer,target : Cardinal; n_operation : Cardinal; amount, fee : UInt64; var errors : String) : Boolean;
Function TransferAmounts(previous : TAccountPreviousBlockInfo; const AOpID : TRawBytes; const senders, n_operations : Array of Cardinal; const sender_amounts : Array of UInt64; const receivers : Array of Cardinal; const receivers_amounts : Array of UInt64; var errors : String) : Boolean;
Function UpdateAccountInfo(previous : TAccountPreviousBlockInfo; const AOpID : TRawBytes; signer_account, signer_n_operation, target_account: Cardinal; const accountInfo: TAccountInfo; const newName, newData : TRawBytes; newType : Word; fee: UInt64; var errors : String) : Boolean;
Function BuyAccount(APrevious : TAccountPreviousBlockInfo; const AOpID : TRawBytes; ABuyer,AAccountToBuy,ASeller: Cardinal; ANOperation : Cardinal; AAmount, AAccountPrice, AFee : UInt64; const ANewAccountKey : TAccountKey; const AHashLockKey : TRawBytes; ARecipientSigned : Boolean; var AErrors : String) : Boolean;
Function Commit(Const operationBlock : TOperationBlock; var errors : String) : Boolean;
Function Account(account_number : Cardinal) : TAccount;
Procedure Rollback;
Function CheckIntegrity : Boolean;
Property FreezedSafeBox : TPCSafeBox read FFreezedAccounts;
Property TotalFee : Int64 read FTotalFee;
Property TotalBalance : Int64 read FTotalBalance;
Procedure CopyFrom(transaction : TPCSafeBoxTransaction);
Procedure CleanTransaction;
Function FindAccountByNameInTransaction(const findName : TRawBytes; out isAddedInThisTransaction, isDeletedInThisTransaction : Boolean) : Integer;
End;
{ TStreamOp }
TStreamOp = Class
public
class Function WriteAnsiString(Stream: TStream; const value: TRawBytes): Integer; overload;
class Function WriteAnsiString(Stream: TStream; const value: T32Bytes): Integer; overload;
class Function WriteString(Stream: TStream; const value: String): Integer;
class Function WriteTBytes(Stream: TStream; const value: TBytes): Integer;
class Function ReadAnsiString(Stream: TStream; var value: TRawBytes; ACheckLength : Integer = 0) : Integer; overload;
class Function ReadAnsiString(Stream: TStream; var value: T32Bytes): Integer; overload;
class Function ReadString(Stream: TStream; var value: String): Integer;
class Function ReadTBytes(Stream: TStream; var ABytes : TBytes; ACheckLength : Integer = 0): Integer;
class Function WriteAccountKey(Stream: TStream; const value: TAccountKey): Integer;
class Function ReadAccountKey(Stream: TStream; var value : TAccountKey): Integer;
class Function SaveStreamToRaw(Stream: TStream) : TRawBytes;
class procedure LoadStreamFromRaw(Stream: TStream; const raw : TRawBytes);
class Function WriteGUID(AStream : TStream; const AGUID : TGUID) : Integer;
class Function ReadGUID(AStream : TStream; var AGUID : TGUID) : Integer;
End;
Const
CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil;previous_proof_of_work:Nil);
CT_SafeBoxChunkIdentificator = 'SafeBoxChunk';
CT_HashLock_NUL : T32Bytes = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
function Check_Safebox_Names_Consistency(sb : TPCSafeBox; const title :String; var errors : String) : Boolean;
Procedure Check_Safebox_Integrity(sb : TPCSafebox; title: String);
implementation
uses
ULog, {$IFnDEF USE_ABSTRACTMEM} UAccountKeyStorage,{$ENDIF} math, UCommon, UPCOperationsBlockValidator, UPCTemporalFileStream, UEncoding;
{$IFDEF FPC}
{$DEFINE USE_BIGBLOCKS_MEM_ON_DISK}
// USE_BIGBLOCKS_MEM_ON_DISK directive is used in order to prevent a FreePascal issue with Heap allocation strategy that
// reuses big blocks of disposed memory and fragments it, this causes that when a new big block of same size that previously
// freeded mem is needed it will not reuse because has been fragmented...
// Tested on FPC version 3.2.0 (2020-11-03) and order versions
// Defragmention documented here: https://www.freepascal.org/docs-html/current/prog/progsu172.html
// This issue is not detected on current Delphi memory manager (Tested on Delphi 10.3.2)
{$ENDIF}
{ This function is for testing purpose only.
Will check if Account Names are well assigned and stored }
function Check_Safebox_Names_Consistency(sb : TPCSafeBox; const title :String; var errors : String) : Boolean;
Var i,j : Integer;
acc : TAccount;
auxs : TRawBytes;
tc : TTickCount;
LErrsString : TStrings;
Begin
tc := TPlatform.GetTickCount;
Try
errors := '';
Result := True;
for i:=0 to sb.AccountsCount-1 do begin
acc := sb.Account(i);
If Length(acc.name)>0 then begin
j := sb.FindAccountByName(acc.name);
If j<>i then begin
errors :=errors + Format(' > Account %d name:%s found at:%d<>Theorical:%d',[acc.account,acc.name.ToPrintable,j,i]);
end;
end;
end;
{$IFDEF USE_ABSTRACTMEM}
LErrsString := TStringList.Create;
try
sb.FPCAbstractMem.CheckConsistency(LErrsString);
if LErrsString.Count>0 then begin
errors := errors + #10+ LErrsString.Text;
end;
finally
LErrsString.Free;
end;
{$ELSE}
// Reverse
for i:=0 to sb.FOrderedByName.Count-1 do begin
j := sb.FOrderedByName.GetTag(i);
auxs := sb.FOrderedByName.Get(i);
acc := sb.Account(j);
If (auxs<>acc.name) then begin
errors :=errors + Format(' > Name:%s at thorical account %d not valid (found %s)',[auxs.ToPrintable,j,acc.name.ToPrintable]);
end;
end;
{$ENDIF}
If (errors<>'') then begin
errors := title+' '+errors;
Result := False;
TLog.NewLog(lterror,'Check_Safebox_Names_Consistency',errors);
end;
finally
TLog.NewLog(ltDebug,'Check_Safebox_Names_Consistency','Used time '+IntToStr(TPlatform.GetElapsedMilliseconds(tc))+' milliseconds');
end;
end;
{ This function is for testing purpose only.
Will check if Accounts are Ok }
Procedure Check_Safebox_Integrity(sb : TPCSafebox; title: String);
var i,j,maxBlock : Integer;
bl_my, bl_modified : TBlockAccount;
auxH : TBytesBuffer;
Begin
For i:=0 to sb.FModifiedBlocksFinalState.Count-1 do begin
bl_modified := sb.FModifiedBlocksFinalState.Get(i);
bl_my := sb.GetBlock(bl_modified.blockchainInfo.block);
If Not TAccountComp.EqualBlockAccounts(bl_my,bl_modified) then begin
Raise Exception.Create(Format('%s Integrity on modified (i)=%d for block number:%d',[title, i,bl_my.blockchainInfo.block]));
end;
If TBaseType.BinStrComp( sb.CalcBlockHash(bl_modified,sb.FCurrentProtocol), bl_modified.block_hash)<>0 then begin
Raise Exception.Create(Format('%s Integrity on block hash (i)=%d for block number:%d',[title, i,bl_my.blockchainInfo.block]));
end;
end;
auxH := TBytesBuffer.Create(1024);
Try
maxBlock := sb.BlocksCount;
auxH.SetLength(sb.BlocksCount*32);
for i:=0 to sb.BlocksCount-1 do begin
bl_my := sb.GetBlock(i);
for j:=Low(bl_my.accounts) to High(bl_my.accounts) do begin
If (maxBlock < (bl_my.accounts[j].updated_on_block_passive_mode)) or (maxBlock < (bl_my.accounts[j].updated_on_block_active_mode)) then begin
Raise Exception.Create(Format('%s Integrity on (i)=%d for block account:%d pasive updated on %d , active updated on %d ,maxBlock %d',[title, i,bl_my.accounts[j].account,bl_my.accounts[j].updated_on_block_passive_mode,bl_my.accounts[j].updated_on_block_active_mode,maxBlock]));
end;
end;
auxH.Replace(i*32,bl_my.block_hash[Low(bl_my.block_hash)],Length(bl_my.block_hash));
end;
if (sb.BufferBlocksHash.Compare(auxH)<>0) then begin
Raise Exception.Create(Format('%s Integrity different Buffer Block Hash',[title]));
end;
Finally
auxH.Free;
End;
end;
{ TPascalCoinProtocol }
var _INTERNAL_PascalCoinProtocol : TPascalCoinProtocol = Nil;
class function TPascalCoinProtocol.GetNewTarget(vteorical, vreal: Cardinal; protocol_version : Integer; isSlowMovement : Boolean; const actualTarget: TRawBytes): TRawBytes;
Var
bnact, bnaux: TBigNum;
tsTeorical, tsReal, factor, factorMin, factorMax, factorDivider: Int64;
begin
{ Given a teorical time in seconds (vteorical>0) and a real time in seconds (vreal>0)
and an actual target, calculates a new target
by % of difference of teorical vs real.
Increment/decrement is adjusted to +-200% in a full CT_CalcNewTargetBlocksAverage round
...so each new target is a maximum +-(100% DIV (CT_CalcNewTargetBlocksAverage DIV 2)) of
previous target. This makes target more stable.
}
tsTeorical := vteorical;
tsReal := vreal;
{ On protocol 1,2 the increment was limited in a integer value between -10..20
On protocol 3 we increase decimals, so increment could be a integer
between -1000..2000, using 2 more decimals for percent. Also will introduce
a "isSlowMovement" variable that will limit to a maximum +-0.5% increment}
if (protocol_version<CT_PROTOCOL_3) then begin
factorDivider := 1000;
factor := (((tsTeorical - tsReal) * 1000) DIV (tsTeorical)) * (-1);
{ Important: Note that a -500 is the same that divide by 2 (-100%), and
1000 is the same that multiply by 2 (+100%), so we limit increase
in a limit [-500..+1000] for a complete (CT_CalcNewTargetBlocksAverage DIV 2) round }
if CT_CalcNewTargetBlocksAverage>1 then begin
factorMin := (-500) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
factorMax := (1000) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
end else begin
factorMin := (-500);
factorMax := (1000);
end;
end else begin
// Protocol 3:
factorDivider := 100000;
If (isSlowMovement) then begin
// Limit to 0.5% instead of 2% (When CT_CalcNewTargetBlocksAverage = 100)
factorMin := (-50000) DIV (CT_CalcNewTargetBlocksAverage * 2);
factorMax := (100000) DIV (CT_CalcNewTargetBlocksAverage * 2);
end else begin
if CT_CalcNewTargetBlocksAverage>1 then begin
factorMin := (-50000) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
factorMax := (100000) DIV (CT_CalcNewTargetBlocksAverage DIV 2);
end else begin
factorMin := (-50000);
factorMax := (100000);
end;
end;
end;
factor := (((tsTeorical - tsReal) * factorDivider) DIV (tsTeorical)) * (-1);
if factor < factorMin then factor := factorMin
else if factor > factorMax then factor := factorMax
else if factor=0 then begin
Result := actualTarget;
exit;
end;
// Calc new target by increasing factor (-500 <= x <= 1000)
bnact := TBigNum.Create(0);
try
bnact.RawValue := actualTarget;
bnaux := bnact.Copy;
try
bnact.Multiply(factor).Divide(factorDivider).Add(bnaux);
finally
bnaux.Free;
end;
// Adjust to TargetCompact limitations:
Result := TargetFromCompact(TargetToCompact(bnact.RawValue,protocol_version),protocol_version);
//
finally
bnact.Free;
end;
end;
class procedure TPascalCoinProtocol.CalcProofOfWork_Part1(const operationBlock: TOperationBlock; out Part1: TRawBytes);
var ms : TMemoryStream;
accKeyRaw : TRawBytes;
begin
ms := TMemoryStream.Create;
try
// Part 1
ms.Write(operationBlock.block,Sizeof(operationBlock.block)); // Little endian
accKeyRaw := TAccountComp.AccountKey2RawString(operationBlock.account_key);
ms.WriteBuffer(accKeyRaw[Low(accKeyRaw)],Length(accKeyRaw));
ms.Write(operationBlock.reward,Sizeof(operationBlock.reward)); // Little endian
ms.Write(operationBlock.protocol_version,Sizeof(operationBlock.protocol_version)); // Little endian
ms.Write(operationBlock.protocol_available,Sizeof(operationBlock.protocol_available)); // Little endian
ms.Write(operationBlock.compact_target,Sizeof(operationBlock.compact_target)); // Little endian
SetLength(Part1,ms.Size);
ms.Position:=0;
ms.Read(Part1[Low(Part1)],ms.Size);
finally
ms.Free;
end;
end;
class procedure TPascalCoinProtocol.CalcProofOfWork_Part3(const operationBlock: TOperationBlock; out Part3: TRawBytes);
var ms : TMemoryStream;
begin
ms := TMemoryStream.Create;
try
ms.WriteBuffer(operationBlock.initial_safe_box_hash[Low(operationBlock.initial_safe_box_hash)],length(operationBlock.initial_safe_box_hash));
ms.WriteBuffer(operationBlock.operations_hash[Low(operationBlock.operations_hash)],length(operationBlock.operations_hash));
if operationBlock.protocol_version<CT_PROTOCOL_5 then begin
// Note about fee: Fee is stored in 8 bytes, but only digest first 4 low bytes
ms.Write(operationBlock.fee,4);
end else begin
// UPDATE PROTOCOL 5 - September 2019
ms.Write(operationBlock.fee,SizeOf(operationBlock.fee)); // Changed from 4 to 8 bytes in Little endian
// UPDATE PROTOCOL 5 - September 2019
//
// IMPORTANT SECURITY FIX:
//
// Since protocol 2 the Safebox can be checkpointed, this means that the
// Safebox can be self-checked mantaining Proof-of-Work consistency.
// (Introduced on PIP-0003 created by Herman Schoenfeld)
//
// The problem is that in order to protect "parallelization" of the PoW
// process must ensure that cannot create a header for block N until N-1
// has been created. On V4 version this protection is made using the
// "initial_safe_box_hash" that is correct and protects parallelization
// when adding a block on a previous safebox. The issue is that when
// only computing PoW of the Safebox (without blockchain) then this N-1
// value obtained on "initial_safe_box_hash" cannot be checked.
//
// In order to eliminate parallelization possibility for a hacked
// new Safebox the best way is to include in N block info obtained
// after N-1 block has been calculated (like blockchain does).
//
// The solution is to include the "previous PoW" obtained on N-1 on the
// digest for next N block, like a traditional blockchain does
//
// This important issue and security fix was discovered by Herman:
// Herman Schoenfeld <herman@sphere10.com>
ms.WriteBuffer(operationBlock.previous_proof_of_work[Low(operationBlock.previous_proof_of_work)],Length(operationBlock.previous_proof_of_work));
end;
SetLength(Part3,ms.Size);
ms.Position := 0;
ms.ReadBuffer(Part3[Low(Part3)],ms.Size);
finally
ms.Free;
end;
end;
class function TPascalCoinProtocol.CalcSafeBoxHash(ABlocksHashBuffer: TBytesBuffer; protocol_version: Integer): TRawBytes;
begin
// If No buffer to hash is because it's fist block... so use Genesis: CT_Genesis_Magic_String_For_Old_Block_Hash
if (ABlocksHashBuffer.Length=0) then Result := TPCSafebox.InitialSafeboxHash
else begin
// Protection
Assert((ABlocksHashBuffer.Length MOD 32)=0,'ABlocksHashBuffer invalid length not modulo 32 = 0');
Assert((ABlocksHashBuffer.Length>0),'ABlocksHashBuffer length = 0');
if protocol_version<=CT_PROTOCOL_4 then begin
// A single SHA256 over the full size is made
Result := TCrypto.DoSha256(ABlocksHashBuffer.Memory,ABlocksHashBuffer.Length);
end else begin
// Implementation of PIP-0030, SafeboxHash will be calculated based on a MerkleTree obtaining a SafeboxRootHash
if ABlocksHashBuffer is TBytesBuffer32Safebox then begin
TBytesBuffer32Safebox(ABlocksHashBuffer).SafeBoxHashCalcType := sbh_Merkle_Root_Hash;
Result := TBytesBuffer32Safebox(ABlocksHashBuffer).GetSafeBoxHash;
end else begin
Result := TPCSafeboxRootHash.CalcSafeBoxRootHash(ABlocksHashBuffer);
end;
end;
end;
end;
class function TPascalCoinProtocol.CalcTotalBalance(ABlockCount: Cardinal): Int64;
var LCurrReward : Int64;
LNextBlock : Integer;
begin
LCurrReward := CT_FirstReward;
LNextBlock := CT_NewLineRewardDecrease;
Result := 0;
while (LNextBlock < ABlockCount) do begin
inc(Result, Int64(CT_NewLineRewardDecrease * LCurrReward));
LCurrReward := LCurrReward DIV 2;
inc(LNextBlock,CT_NewLineRewardDecrease);
end;
inc(Result, Int64(Int64(ABlockCount MOD CT_NewLineRewardDecrease) * LCurrReward));
end;
class function TPascalCoinProtocol.AllowUseHardcodedRandomHashTable(
const AHardcodedFileName: String;
const AHardcodedSha256Value: TRawBytes): Boolean;
var LTmp : TPCHardcodedRandomHashTable;
LFileStream : TFileStream;
LInternalHardcodedSha256 : TRawBytes;
begin
Result := False;
{$IFDEF ASSUME_VALID_POW_OLD_PROTOCOLS}
// In this case will not use Hardcoded RandomHash Table
Exit;
{$ENDIF}
If Not FileExists(AHardcodedFileName) then begin
TLog.NewLog(ltdebug,ClassName,Format('Hardcoded RandomHash from file not found:%s',
[AHardcodedFileName] ));
Exit;
end;
LTmp := TPCHardcodedRandomHashTable.Create;
try
LFileStream := TFileStream.Create(AHardcodedFileName,fmOpenRead+fmShareDenyNone);
try
if LTmp.LoadFromStream(LFileStream,LInternalHardcodedSha256) then begin
if TBaseType.Equals(LInternalHardcodedSha256, AHardcodedSha256Value) then begin
if Not Assigned(_INTERNAL_PascalCoinProtocol) then begin
_INTERNAL_PascalCoinProtocol := TPascalCoinProtocol.Create;
end;
_INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.CopyFrom(LTmp);
TLog.NewLog(ltinfo,ClassName,Format('Added %d (%d) Hardcoded RandomHash from file:%s (%s)',
[LTmp.Count,_INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.Count,
AHardcodedFileName,AHardcodedSha256Value.ToHexaString] ));
Result := True;
end;
end;
if not Result then begin
TLog.NewLog(lterror,ClassName,Format('Hardcoded RandomHash file invalid:%s (%s %s) %d',
[AHardcodedFileName,AHardcodedSha256Value.ToHexaString,
LInternalHardcodedSha256.ToHexaString,
LTmp.Count] ));
end;
finally
LFileStream.Free;
end;
finally
LTmp.Free;
end;
end;
constructor TPascalCoinProtocol.Create;
begin
FPCHardcodedRandomHashTable := TPCHardcodedRandomHashTable.Create;
end;
destructor TPascalCoinProtocol.Destroy;
begin
FreeAndNil(FPCHardcodedRandomHashTable);
inherited;
end;
class procedure TPascalCoinProtocol.CalcProofOfWork(const operationBlock: TOperationBlock; out PoW: TRawBytes);
var ms : TMemoryStream;
accKeyRaw : TRawBytes;
LDigest : TRawBytes;
begin
ms := TMemoryStream.Create;
try
// Part 1
ms.Write(operationBlock.block,Sizeof(operationBlock.block)); // Little endian
accKeyRaw := TAccountComp.AccountKey2RawString(operationBlock.account_key);
ms.WriteBuffer(accKeyRaw[Low(accKeyRaw)],Length(accKeyRaw));
ms.Write(operationBlock.reward,Sizeof(operationBlock.reward)); // Little endian
ms.Write(operationBlock.protocol_version,Sizeof(operationBlock.protocol_version)); // Little endian
ms.Write(operationBlock.protocol_available,Sizeof(operationBlock.protocol_available)); // Little endian
ms.Write(operationBlock.compact_target,Sizeof(operationBlock.compact_target)); // Little endian
// Part 2
ms.WriteBuffer(operationBlock.block_payload[Low(operationBlock.block_payload)],Length(operationBlock.block_payload));
// Part 3
ms.WriteBuffer(operationBlock.initial_safe_box_hash[Low(operationBlock.initial_safe_box_hash)],length(operationBlock.initial_safe_box_hash));
ms.WriteBuffer(operationBlock.operations_hash[Low(operationBlock.operations_hash)],length(operationBlock.operations_hash));
if operationBlock.protocol_version<CT_PROTOCOL_5 then begin
// Note about fee: Fee is stored in 8 bytes (Int64), but only digest first 4 low bytes
ms.Write(operationBlock.fee,4);
end else begin
// UPDATE PROTOCOL 5 - September 2019
ms.Write(operationBlock.fee,SizeOf(operationBlock.fee)); // Changed from 4 to 8 bytes in Little endian
ms.WriteBuffer(operationBlock.previous_proof_of_work[Low(operationBlock.previous_proof_of_work)],Length(operationBlock.previous_proof_of_work));
end;
ms.Write(operationBlock.timestamp,4);
ms.Write(operationBlock.nonce,4);
if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then begin
if (operationBlock.protocol_version < CT_PROTOCOL_5) then begin
if Assigned(_INTERNAL_PascalCoinProtocol) then begin
SetLength(LDigest,ms.Size);
Move(ms.Memory^,LDigest[0],ms.Size);
if _INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.FindRandomHashByDigest(LDigest,PoW) then Exit;
end;
TCrypto.DoRandomHash(ms.Memory,ms.Size,PoW);
end else TCrypto.DoRandomHash2(ms.Memory,ms.Size,PoW);
end else
TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
finally
ms.Free;
end;
end;
class function TPascalCoinProtocol.IsValidMinerBlockPayload(const newBlockPayload: TRawBytes): Boolean;
var i : Integer;
begin
Result := False;
if Length(newBlockPayload)>CT_MaxPayloadSize then Exit;
// Checking Miner Payload valid chars
for i := Low(newBlockPayload) to High(newBlockPayload) do begin
if Not (newBlockPayload[i] in [32..254]) then begin
exit;
end;
end;
Result := True;
end;
class procedure TPascalCoinProtocol.GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
begin
if OperationBlock.protocol_version<CT_PROTOCOL_3 then begin
acc_0_miner_reward := OperationBlock.reward + OperationBlock.fee;
acc_4_dev_reward := 0;
acc_4_for_dev := False;
end else begin
acc_4_dev_reward := (OperationBlock.reward * CT_Protocol_v3_PIP11_Percent) DIV 100;
acc_0_miner_reward := OperationBlock.reward + OperationBlock.fee - acc_4_dev_reward;
acc_4_for_dev := True;
end;
end;
class function TPascalCoinProtocol.GetRewardForNewLine(line_index: Cardinal): UInt64;
Var n, i : Cardinal;
begin
{$IFDEF TESTNET}
// TESTNET used (line_index +1), but PRODUCTION must use (line_index)
n := (line_index + 1) DIV CT_NewLineRewardDecrease; // TESTNET BAD USE (line_index + 1)
{$ELSE}
n := line_index DIV CT_NewLineRewardDecrease; // FOR PRODUCTION
{$ENDIF}
Result := CT_FirstReward;
for i := 1 to n do begin
Result := Result DIV 2;
end;
if (Result < CT_MinReward) then
Result := CT_MinReward;
end;
class function TPascalCoinProtocol.MinimumTarget(protocol_version: Integer): Cardinal;
begin
if protocol_version>=CT_PROTOCOL_5 then begin
Result := CT_MinCompactTarget_v5
end else if protocol_version>=CT_PROTOCOL_4 then begin
Result := CT_MinCompactTarget_v4;
end else begin
Result := CT_MinCompactTarget_v1;
end;
end;
class function TPascalCoinProtocol.ResetTarget(current_target: Cardinal; protocol_version: Integer): Cardinal;
begin
{$IFDEF ACTIVATE_RANDOMHASH_V4}
if protocol_version=CT_PROTOCOL_4 then begin
Result := CT_CompactTarget_Reset_v4
end else Result := current_target;
{$ELSE}
Result := current_target;
{$ENDIF}
end;
class function TPascalCoinProtocol.TargetFromCompact(encoded: Cardinal; protocol_version : Integer): TRawBytes;
Var
nbits, offset, i: Cardinal;
bn: TBigNum;
raw : TRawBytes;
begin
{
Compact Target is a 4 byte value that tells how many "0" must have the hash at left if presented in binay format.
First byte indicates haw many "0 bits" are on left, so can be from 0x00 to 0xE7
(Because 24 bits are reserved for 3 bytes, and 1 bit is implicit, max: 256-24-1=231=0xE7)
Next 3 bytes indicates next value in XOR, stored in RAW format
Example: If we want a hash lower than 0x0000 0000 0000 65A0 A2F4 +29 bytes
Binary "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 1111 0100"
That is 49 zeros on left before first 1. So first byte is 49 decimal = 0x31
After we have "110 0101 1010 0000 1010 0010 1111 0100 1111 0100" but we only can accept first 3 bytes,
also note that first "1" is implicit, so value is transformed in
binary as "10 0101 1010 0000 1010 0010 11" that is 0x96828B
But note that we must XOR this value, so result offset is: 0x697D74
Compacted value is: 0x31697D74
When translate compact target back to target: ( 0x31697D74 )
0x31 = 49 bits at "0", then 1 bit at "1" followed by XOR 0x697D74 = 0x96828B
49 "0" bits "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0"
0x96828B "1001 0110 1000 0010 1000 1011"
Hash target = "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 11.. ...."
Fill last "." with "1"
Hash target = "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 0101 1010 0000 1010 0010 1111 1111"
Hash target = 0x00 00 00 00 00 00 65 A0 A2 FF + 29 bytes
Note that is not exactly the same than expected due to compacted format
}
nbits := encoded shr 24;