-
Notifications
You must be signed in to change notification settings - Fork 173
/
main.cpp
executable file
·8835 lines (7597 loc) · 327 KB
/
main.cpp
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
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "util.h"
#include "net.h"
#include "alert.h"
#include "checkpoints.h"
#include "db.h"
#include "txdb.h"
#include "init.h"
#include "ui_interface.h"
#include "kernel.h"
#include "block.h"
#include "scrypt.h"
#include "global_objects_noui.hpp"
#include "cpid.h"
#include "rpcserver.h"
#include "rpcclient.h"
#include "boinc.h"
#include "beacon.h"
#include "miner.h"
#include "neuralnet.h"
#include "backup.h"
#include "appcache.h"
#include "tally.h"
#include "contract/contract.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
#include <boost/algorithm/string/join.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <openssl/md5.h>
#include <ctime>
#include <math.h>
extern std::string NodeAddress(CNode* pfrom);
extern std::string ConvertBinToHex(std::string a);
extern std::string ConvertHexToBin(std::string a);
extern bool WalletOutOfSync();
extern bool WriteKey(std::string sKey, std::string sValue);
bool AdvertiseBeacon(std::string &sOutPrivKey, std::string &sOutPubKey, std::string &sError, std::string &sMessage);
extern void CleanInboundConnections(bool bClearAll);
bool RequestSupermajorityNeuralData();
extern bool AskForOutstandingBlocks(uint256 hashStart);
extern bool CleanChain();
extern void ResetTimerMain(std::string timer_name);
bool TallyResearchAverages(CBlockIndex* index);
bool TallyResearchAverages_retired(CBlockIndex* index);
bool TallyResearchAverages_v9(CBlockIndex* index);
extern void IncrementCurrentNeuralNetworkSupermajority(std::string NeuralHash, std::string GRCAddress, double distance);
extern MiningCPID GetInitializedMiningCPID(std::string name, std::map<std::string, MiningCPID>& vRef);
extern std::string getHardDriveSerial();
extern double ExtractMagnitudeFromExplainMagnitude();
extern void GridcoinServices();
extern double SnapToGrid(double d);
extern bool StrLessThanReferenceHash(std::string rh);
extern bool IsContract(CBlockIndex* pIndex);
std::string ExtractValue(std::string data, std::string delimiter, int pos);
extern MiningCPID GetBoincBlockByIndex(CBlockIndex* pblockindex);
UniValue MagnitudeReport(std::string cpid);
extern void AddCPIDBlockHash(const std::string& cpid, const uint256& blockhash);
void RemoveCPIDBlockHash(const std::string& cpid, const uint256& blockhash);
extern void ZeroOutResearcherTotals(std::string cpid);
extern StructCPID GetLifetimeCPID(const std::string& cpid, const std::string& sFrom);
extern std::string getCpuHash();
std::string TimestampToHRDate(double dtm);
bool CPIDAcidTest2(std::string bpk, std::string externalcpid);
extern bool BlockNeedsChecked(int64_t BlockTime);
extern void FixInvalidResearchTotals(std::vector<CBlockIndex*> vDisconnect, std::vector<CBlockIndex*> vConnect);
int64_t GetEarliestWalletTransaction();
extern void IncrementVersionCount(const std::string& Version);
double GetSuperblockAvgMag(std::string data,double& out_beacon_count,double& out_participant_count,double& out_avg,bool bIgnoreBeacons, int nHeight);
extern bool LoadAdminMessages(bool bFullTableScan,std::string& out_errors);
extern std::string GetCurrentNeuralNetworkSupermajorityHash(double& out_popularity);
extern double CalculatedMagnitude2(std::string cpid, int64_t locktime,bool bUseLederstrumpf);
double DoubleFromAmount(int64_t amount);
extern bool UpdateNeuralNetworkQuorumData();
bool AsyncNeuralRequest(std::string command_name,std::string cpid,int NodeLimit);
extern bool FullSyncWithDPORNodes();
bool CheckMessageSignature(std::string sMessageAction, std::string sMessageType, std::string sMsg, std::string sSig,std::string opt_pubkey);
extern std::string strReplace(std::string& str, const std::string& oldStr, const std::string& newStr);
extern bool GetEarliestStakeTime(std::string grcaddress, std::string cpid);
extern double GetTotalBalance();
extern std::string PubKeyToAddress(const CScript& scriptPubKey);
extern void IncrementNeuralNetworkSupermajority(const std::string& NeuralHash, const std::string& GRCAddress, double distance, const CBlockIndex* pblockindex);
extern CBlockIndex* GetHistoricalMagnitude(std::string cpid);
extern double GetOutstandingAmountOwed(StructCPID &mag, std::string cpid, int64_t locktime, double& total_owed, double block_magnitude);
extern double GetOwedAmount(std::string cpid);
bool TallyMagnitudesInSuperblock();
extern std::string GetNeuralNetworkReport();
std::string GetCommandNonce(std::string command);
std::string DefaultBlockKey(int key_length);
extern std::string ToOfficialName(std::string proj);
extern double GRCMagnitudeUnit(int64_t locktime);
unsigned int nNodeLifespan;
using namespace std;
using namespace boost;
//
// Global state
//
CCriticalSection cs_setpwalletRegistered;
set<CWallet*> setpwalletRegistered;
CCriticalSection cs_main;
extern std::string NodeAddress(CNode* pfrom);
CTxMemPool mempool;
unsigned int WHITELISTED_PROJECTS = 0;
int64_t nLastPing = 0;
int64_t nLastAskedForBlocks = 0;
int64_t nBootup = 0;
int64_t nLastLoadAdminMessages = 0;
int64_t nLastGRCtallied = 0;
int64_t nLastCleaned = 0;
extern bool IsCPIDValidv3(std::string cpidv2, bool allow_investor);
double GetLastPaymentTimeByCPID(std::string cpid);
extern double CoinToDouble(double surrogate);
int64_t GetRSAWeightByCPID(std::string cpid);
extern MiningCPID GetMiningCPID();
extern StructCPID GetStructCPID();
int64_t nLastBlockSolved = 0; //Future timestamp
int64_t nLastBlockSubmitted = 0;
int64_t nLastCheckedForUpdate = 0;
///////////////////////MINOR VERSION////////////////////////////////
std::string msMasterProjectPublicKey = "049ac003b3318d9fe28b2830f6a95a2624ce2a69fb0c0c7ac0b513efcc1e93a6a6e8eba84481155dd82f2f1104e0ff62c69d662b0094639b7106abc5d84f948c0a";
// The Private Key is revealed by design, for public messages only:
std::string msMasterMessagePrivateKey = "308201130201010420fbd45ffb02ff05a3322c0d77e1e7aea264866c24e81e5ab6a8e150666b4dc6d8a081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200044b2938fbc38071f24bede21e838a0758a52a0085f2e034e7f971df445436a252467f692ec9c5ba7e5eaa898ab99cbd9949496f7e3cafbf56304b1cc2e5bdf06e";
std::string msMasterMessagePublicKey = "044b2938fbc38071f24bede21e838a0758a52a0085f2e034e7f971df445436a252467f692ec9c5ba7e5eaa898ab99cbd9949496f7e3cafbf56304b1cc2e5bdf06e";
std::string YesNo(bool bin);
int64_t GetMaximumBoincSubsidy(int64_t nTime);
extern double CalculatedMagnitude(int64_t locktime,bool bUseLederstrumpf);
extern int64_t GetCoinYearReward(int64_t nTime);
BlockMap mapBlockIndex;
set<pair<COutPoint, unsigned int> > setStakeSeen;
CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); // "standard" scrypt target limit for proof of work, results with 0,000244140625 proof-of-work difficulty
CBigNum bnProofOfStakeLimit(~uint256(0) >> 20);
CBigNum bnProofOfStakeLimitV2(~uint256(0) >> 20);
CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
//Gridcoin Minimum Stake Age (16 Hours)
unsigned int nStakeMinAge = 16 * 60 * 60; // 16 hours
unsigned int nStakeMaxAge = -1; // unlimited
unsigned int nModifierInterval = 10 * 60; // time to elapse before new modifier is computed
bool bOPReturnEnabled = true;
// Gridcoin:
int nCoinbaseMaturity = 100;
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
uint256 nBestChainTrust = 0;
uint256 nBestInvalidTrust = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64_t nTimeBestReceived = 0;
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
map<uint256, CTransaction> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
const string strMessageMagic = "Gridcoin Signed Message:\n";
// Settings
int64_t nTransactionFee = MIN_TX_FEE;
int64_t nReserveBalance = 0;
int64_t nMinimumInputValue = 0;
std::map<std::string, double> mvNeuralNetworkHash;
std::map<std::string, double> mvCurrentNeuralNetworkHash;
std::map<std::string, double> mvNeuralVersion;
std::map<std::string, StructCPID> mvDPOR;
std::map<std::string, StructCPID> mvDPORCopy;
std::map<std::string, StructCPID> mvResearchAge;
std::map<std::string, HashSet> mvCPIDBlockHashes;
BlockFinder blockFinder;
// Gridcoin - Rob Halford
extern std::string RetrieveMd5(std::string s1);
extern std::string aes_complex_hash(uint256 scrypt_hash);
bool bNetAveragesLoaded = false;
bool bForceUpdate = false;
bool bGlobalcomInitialized = false;
bool bStakeMinerOutOfSyncWithNetwork = false;
bool fQtActive = false;
bool bGridcoinGUILoaded = false;
extern double LederstrumpfMagnitude2(double Magnitude, int64_t locktime);
extern void GetGlobalStatus();
extern bool ProjectIsValid(std::string project);
double GetNetworkAvgByProject(std::string projectname);
extern bool IsCPIDValid_Retired(std::string cpid, std::string ENCboincpubkey);
extern bool IsCPIDValidv2(MiningCPID& mc, int height);
extern std::string getfilecontents(std::string filename);
extern bool LessVerbose(int iMax1000);
extern MiningCPID GetNextProject(bool bForce);
extern void HarvestCPIDs(bool cleardata);
///////////////////////////////
// Standard Boinc Projects ////
///////////////////////////////
//Global variables to display current mined project in various places:
std::string msMiningProject;
std::string msMiningCPID;
std::string msPrimaryCPID;
double mdPORNonce = 0;
double mdLastPorNonce = 0;
double mdMachineTimerLast = 0;
// Mining status variables
std::string msHashBoinc;
std::string msMiningErrors;
std::string msPoll;
std::string msMiningErrors5;
std::string msMiningErrors6;
std::string msMiningErrors7;
std::string msMiningErrors8;
std::string msAttachmentGuid;
std::string msMiningErrorsIncluded;
std::string msMiningErrorsExcluded;
std::string msNeuralResponse;
std::string msHDDSerial;
//When syncing, we grandfather block rejection rules up to this block, as rules became stricter over time and fields changed
int nGrandfather = 1034700;
int nNewIndex = 271625;
int nNewIndex2 = 364500;
int64_t nGenesisSupply = 340569880;
// Stats for Main Screen:
globalStatusType GlobalStatusStruct;
bool fColdBoot = true;
bool fEnforceCanonical = true;
bool fUseFastIndex = false;
// Gridcoin status *************
MiningCPID GlobalCPUMiningCPID = GetMiningCPID();
int nBoincUtilization = 0;
std::string sRegVer;
std::map<std::string, StructCPID> mvCPIDs; //Contains the project stats at the user level
std::map<std::string, StructCPID> mvCreditNode; //Contains the verified stats at the user level
std::map<std::string, StructCPID> mvNetwork; //Contains the project stats at the network level
std::map<std::string, StructCPID> mvNetworkCopy; //Contains the project stats at the network level
std::map<std::string, StructCPID> mvCreditNodeCPID; // Contains verified CPID Magnitudes;
std::map<std::string, StructCPID> mvMagnitudes; // Contains Magnitudes by CPID & Outstanding Payments Owed per CPID
std::map<std::string, StructCPID> mvMagnitudesCopy; // Contains Magnitudes by CPID & Outstanding Payments Owed per CPID
std::map<std::string, int> mvTimers; // Contains event timers that reset after max ms duration iterator is exceeded
// End of Gridcoin Global vars
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
//
void ResetTimerMain(std::string timer_name)
{
mvTimers[timer_name] = 0;
}
bool TimerMain(std::string timer_name, int max_ms)
{
mvTimers[timer_name] = mvTimers[timer_name] + 1;
if (mvTimers[timer_name] > max_ms)
{
mvTimers[timer_name]=0;
return true;
}
return false;
}
bool UpdateNeuralNetworkQuorumData()
{
if (!bGlobalcomInitialized) return false;
int64_t superblock_time = ReadCache("superblock", "magnitudes").timestamp;
int64_t superblock_age = GetAdjustedTime() - superblock_time;
std::string myNeuralHash = "";
double popularity = 0;
std::string consensus_hash = GetNeuralNetworkSupermajorityHash(popularity);
std::string sAge = ToString(superblock_age);
std::string sBlock = ReadCache("superblock", "block_number").value;
std::string sTimestamp = TimestampToHRDate(superblock_time);
std::string data = "<QUORUMDATA><AGE>" + sAge + "</AGE><HASH>" + consensus_hash + "</HASH><BLOCKNUMBER>" + sBlock + "</BLOCKNUMBER><TIMESTAMP>"
+ sTimestamp + "</TIMESTAMP><PRIMARYCPID>" + msPrimaryCPID + "</PRIMARYCPID></QUORUMDATA>";
NN::ExecuteDotNetStringFunction("SetQuorumData",data);
return true;
}
bool FullSyncWithDPORNodes()
{
if(!NN::IsEnabled())
return false;
// 3-30-2016 : First try to get the master database from another neural network node if these conditions occur:
// The foreign node is fully synced. The foreign nodes quorum hash matches the supermajority hash. My hash != supermajority hash.
double dCurrentPopularity = 0;
std::string sCurrentNeuralSupermajorityHash = GetCurrentNeuralNetworkSupermajorityHash(dCurrentPopularity);
std::string sMyNeuralHash = "";
sMyNeuralHash = NN::GetNeuralHash();
if (!sMyNeuralHash.empty() && !sCurrentNeuralSupermajorityHash.empty() && sMyNeuralHash != sCurrentNeuralSupermajorityHash)
{
bool bNodeOnline = RequestSupermajorityNeuralData();
if (bNodeOnline) return false; // Async call to another node will continue after the node responds.
}
std::string errors1;
LoadAdminMessages(false,errors1);
const int64_t iEndTime= (GetAdjustedTime()-CONSENSUS_LOOKBACK) - ( (GetAdjustedTime()-CONSENSUS_LOOKBACK) % BLOCK_GRANULARITY);
const int64_t nLookback = 30 * 6 * 86400;
const int64_t iStartTime = (iEndTime - nLookback) - ( (iEndTime - nLookback) % BLOCK_GRANULARITY);
std::string cpiddata = GetListOf("beacon", iStartTime, iEndTime);
std::string sWhitelist = GetListOf("project");
int64_t superblock_time = ReadCache("superblock", "magnitudes").timestamp;
int64_t superblock_age = GetAdjustedTime() - superblock_time;
double popularity = 0;
std::string consensus_hash = GetNeuralNetworkSupermajorityHash(popularity);
std::string sAge = ToString(superblock_age);
std::string sBlock = ReadCache("superblock", "block_number").value;
std::string sTimestamp = TimestampToHRDate(superblock_time);
std::string data = "<WHITELIST>" + sWhitelist + "</WHITELIST><CPIDDATA>"
+ cpiddata + "</CPIDDATA><QUORUMDATA><AGE>" + sAge + "</AGE><HASH>" + consensus_hash + "</HASH><BLOCKNUMBER>" + sBlock + "</BLOCKNUMBER><TIMESTAMP>"
+ sTimestamp + "</TIMESTAMP><PRIMARYCPID>" + msPrimaryCPID + "</PRIMARYCPID></QUORUMDATA>";
NN::SynchronizeDPOR(data);
return true;
}
double GetEstimatedNetworkWeight(unsigned int nPoSInterval)
{
// The number of stakes to include in the average has been reduced to 40 (default) from 72. 72 stakes represented 1.8 hours at
// standard spacing. This is too long. 40 blocks is nominally 1 hour.
double result;
// The constant below comes from (MaxHash / StandardDifficultyTarget) * 16 sec / 90 sec. If you divide it by 80 to convert to GRC you
// get the familiar 9544517.40667
result = 763561392.533 * GetAverageDifficulty(nPoSInterval);
if (fDebug10) LogPrintf("GetEstimatedNetworkWeight debug: Network Weight = %f", result);
if (fDebug10) LogPrintf("GetEstimatedNetworkWeight debug: Network Weight in GRC = %f", result / 80.0);
return result;
}
double GetAverageDifficulty(unsigned int nPoSInterval)
{
/*
* Diff is inversely related to Target (without the coinweight multiplier), but proportional to the
* effective number of coins on the network. This is tricky, if you want to get the average target value
* used over an interval you should use a harmonic average, since target is inversely related to diff. If
* on the other hand, you want to average diff in a way to also determine the average coins active in
* the network, you should simply use an arithmetic average. See the relation between diff and estimated
* network weight above. We do not need to take into account the actual spacing of the blocks, because this
* already handled by the retargeting in GetNextTargetRequiredV2, and in fact, given the random distribution
* of block spacing, it would be harmful to use a spacing correction for small nPoSInterval sizes.
*
* Also... The number of stakes to include in the average has been reduced to 40 (default) from 72.
* 72 stakes represented 1.8 hours at standard spacing. This is too long. 40 blocks is nominally 1 hour.
*/
double dDiff = 1.0;
double dDiffSum = 0.0;
unsigned int nStakesHandled = 0;
double result;
CBlockIndex* pindex = pindexBest;
while (pindex && nStakesHandled < nPoSInterval)
{
if (pindex->IsProofOfStake())
{
dDiff = GetDifficulty(pindex);
// dDiff should never be zero, but just in case, skip the block and move to the next one.
if (dDiff)
{
dDiffSum += dDiff;
nStakesHandled++;
if (fDebug10) LogPrintf("GetAverageDifficulty debug: dDiff = %f", dDiff);
if (fDebug10) LogPrintf("GetAverageDifficulty debug: nStakesHandled = %u", nStakesHandled);
}
}
pindex = pindex->pprev;
}
result = nStakesHandled ? dDiffSum / nStakesHandled : 0;
if (fDebug10) LogPrintf("GetAverageDifficulty debug: Average dDiff = %f", result);
return result;
}
double GetEstimatedTimetoStake(double dDiff, double dConfidence)
{
/*
* The algorithm below is an attempt to come up with a more accurate way of estimating Time to Stake (ETTS) based on
* the actual situation of the miner and UTXO's. A simple equation will not provide good results, because in mainnet,
* the cooldown period is 16 hours, and depending on how many UTXO's and where they are with respect to getting out of
* cooldown has a lot to do with the expected time to stake.
*
* The way to conceptualize the approach below is to think of the UTXO's as bars on a Gantt Chart. It is a negative Gantt
* chart, meaning that each UTXO bar is cooldown period long, and while the current time is in that bar, the staking probability
* for the UTXO is zero, and UnitStakingProbability elsewhere. A timestamp mask of 16x the normal mask is used to reduce
* the work in the nested loop, so that a 16 hour interval will have a maximum of 225 events, and most likely far less.
* This is important, because the inner loop will be the number of UTXO's. A future improvement to this algorithm would
* also be to quantize (group) the UTXO's themselves (the Gantt bars) so that the work would be further reduced.
* You will see that once the UTXO's are sorted in ascending order based on the time of the end of each of their cooldowns, this
* becomes a manageable algorithm to piece the probabilities together.
*
* You will note that the compound Poisson (geometric) recursive probability relation is used, since you cannot simply add
* the probabilities due to consideration of high confidence (CDF) values of 80% or more.
*
* Thin local data structures are used to hold the UTXO information. This minimizes the amount of time
* that locks on the wallet need to be held at the expense of a little memory consumption.
*/
double result = 0.0;
// dDiff must be >= 0 and dConfidence must lie on the interval [0,1) otherwise this is an error.
assert(dDiff >= 0 && dConfidence >= 0 && dConfidence < 1);
// if dConfidence = 0, then the result must be 0.
if (!dConfidence)
{
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: Confidence of 0 specified: ETTS = %f", result);
return result;
}
bool staking = MinerStatus.nLastCoinStakeSearchInterval && MinerStatus.WeightSum;
// Get out early if not staking and set return value of 0.
if (!staking)
{
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: Not staking: ETTS = %f", result);
return result;
}
int64_t nValue = 0;
int64_t nCurrentTime = GetAdjustedTime();
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: nCurrentTime = %i", nCurrentTime);
CTxDB txdb("r");
// Here I am defining a time mask 16 times as long as the normal stake time mask. This is to quantize the UTXO's into a maximum of
// 16 hours * 3600 / 256 = 225 time bins for evaluation. Otherwise for a large number of UTXO's, this algorithm could become
// really expensive.
const int ETTS_TIMESTAMP_MASK = (16 * (STAKE_TIMESTAMP_MASK + 1)) - 1;
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: ETTS_TIMESTAMP_MASK = %x", ETTS_TIMESTAMP_MASK);
int64_t BalanceAvailForStaking = 0;
vector<COutput> vCoins;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
BalanceAvailForStaking = pwalletMain->GetBalance() - nReserveBalance;
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: BalanceAvailForStaking = %u", BalanceAvailForStaking);
// Get out early if no balance available and set return value of 0. This should already have happened above, because with no
// balance left after reserve, staking should be disabled; however, just to be safe...
if (BalanceAvailForStaking <= 0)
{
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: No balance available: ETTS = %f", result);
return result;
}
//reminder... void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL, bool fIncludeStakingCoins=false) const;
pwalletMain->AvailableCoins(vCoins, true, NULL, true);
}
// An efficient local structure to store the UTXO's with the bare minimum info we need.
typedef vector< std::pair<int64_t, int64_t> > vCoinsExt;
vCoinsExt vUTXO;
// A local ordered set to store the unique "bins" corresponding to the UTXO transaction times. We are going to use this
// for the outer loop.
std::set<int64_t> UniqueUTXOTimes;
// We want the first "event" to be the CurrentTime. This does not have to be quantized.
UniqueUTXOTimes.insert(nCurrentTime);
// Debug output cooldown...
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: nStakeMinAge = %i", nStakeMinAge);
// If dDiff = 0 from supplied argument (which is also the default), then derive a smoothed difficulty over the default PoSInterval of 40 blocks by calling
// GetAverageDifficulty(40), otherwise let supplied argument dDiff stand.
if (!dDiff) dDiff = GetAverageDifficulty(40);
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dDiff = %f", dDiff);
// The stake probability per "throw" of 1 weight unit = target value at diff of 1.0 / (maxhash * diff). This happens effectively every STAKE_TIMESTAMP_MASK+1 sec.
double dUnitStakeProbability = 1 / (4295032833.0 * dDiff);
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dUnitStakeProbability = %e", dUnitStakeProbability);
int64_t nTime = 0;
for (const auto& out : vCoins)
{
CTxIndex txindex;
CBlock CoinBlock; //Block which contains CoinTx
if (!txdb.ReadTxIndex(out.tx->GetHash(), txindex))
continue; //error?
if (!CoinBlock.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
continue;
// We are going to store as an event the time that the UTXO matures (is available for staking again.)
nTime = (CoinBlock.GetBlockTime() & ~ETTS_TIMESTAMP_MASK) + nStakeMinAge;
nValue = out.tx->vout[out.i].nValue;
// Only consider UTXO's that are actually stakeable - which means that each one must be less than the available balance
// subtracting the reserve. Each UTXO also has to be greater than 1/80 GRC to result in a weight greater than zero in the CreateCoinStake loop,
// so eliminate UTXO's with less than 0.0125 GRC balances right here. The test with Satoshi units for that is
// nValue >= 1250000.
if(BalanceAvailForStaking >= nValue && nValue >= 1250000)
{
vUTXO.push_back(std::pair<int64_t, int64_t>( nTime, nValue));
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: pair (relative to current time: <%i, %i>", nTime - nCurrentTime, nValue);
// Only record a time below if it is after nCurrentTime, because UTXO's that have matured already are already stakeable and can be grouped (will be found)
// by the nCurrentTime record that was already injected above.
if(nTime > nCurrentTime) UniqueUTXOTimes.insert(nTime);
}
}
int64_t nTimePrev = nCurrentTime;
int64_t nDeltaTime = 0;
int64_t nThrows = 0;
int64_t nCoinWeight = 0;
double dProbAccumulator = 0;
double dCumulativeProbability = 0;
// Note: Even though this is a compound Poisson process leading to a compound geometric distribution, and the individual probabilities are
// small, we are mounting to high CDFs. This means to be reasonably accurate, we cannot just add the probabilities, because the intersections
// become significant. The CDF of a compound geometric distribution as you do tosses with different probabilities follows the
// recursion relation... CDF.i = 1 - (1 - CDF.i-1)(1 - p.i). If all probabilities are the same, this reduces to the familiar
// CDF.k = 1 - (1 - p)^k where ^ is exponentiation.
for(const auto& itertime : UniqueUTXOTimes)
{
nTime = itertime;
dProbAccumulator = 0;
for( auto& iterUTXO : vUTXO)
{
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: Unique UTXO Time: %u, vector pair <%u, %u>", nTime, iterUTXO.first, iterUTXO.second);
// If the "negative Gantt chart bar" is ending or has ended for a UTXO, it now accumulates probability. (I.e. the event time being checked
// is greater than or equal to the cooldown expiration of the UTXO.)
// accumulation for that UTXO.
if(nTime >= iterUTXO.first)
{
// The below weight calculation is just like the CalculateStakeWeightV8 in kernel.cpp.
nCoinWeight = iterUTXO.second / 1250000;
dProbAccumulator = 1 - ((1 - dProbAccumulator) * (1 - (dUnitStakeProbability * nCoinWeight)));
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dProbAccumulator = %e", dProbAccumulator);
}
}
nDeltaTime = nTime - nTimePrev;
nThrows = nDeltaTime / (STAKE_TIMESTAMP_MASK + 1);
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: nThrows = %i", nThrows);
dCumulativeProbability = 1 - ((1 - dCumulativeProbability) * pow((1 - dProbAccumulator), nThrows));
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dCumulativeProbability = %e", dCumulativeProbability);
if(dCumulativeProbability >= dConfidence)
break;
nTimePrev = nTime;
}
// If (dConfidence - dCumulativeProbability) > 0, it means we exited the negative Gantt chart area and the desired confidence level
// has not been reached. All of the eligible UTXO's are contributing probability, and this is the final dProbAccumulator value.
// If the loop above is degenerate (i.e. only the current time pass through), then dCumulativeProbability will be zero.
// If it was not degenerate and the positive reqions in the Gantt chart area contributed some probability, then dCumulativeProbability will
// be greater than zero. We must compute the amount of time beyond nTime that is required to bridge the gap between
// dCumulativeProbability and dConfidence. If (dConfidence - dCumulativeProbability) <= 0 then we overshot during the Gantt chart area,
// and we will back off by nThrows amount, which will now be negative.
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dProbAccumulator = %e", dProbAccumulator);
// Shouldn't happen because if we are down here, we are staking, and there have to be eligible UTXO's, but just in case...
if (dProbAccumulator == 0.0)
{
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: ERROR in dProbAccumulator calculations");
return result;
}
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: dConfidence = %f", dConfidence);
// If nThrows is negative, this just means we overshot in the Gantt chart loop and have to backtrack by nThrows.
nThrows = (int64_t)((log(1 - dConfidence) - log(1 - dCumulativeProbability)) / log(1 - dProbAccumulator));
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: nThrows = %i", nThrows);
nDeltaTime = nThrows * (STAKE_TIMESTAMP_MASK + 1);
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: nDeltaTime = %i", nDeltaTime);
// Because we are looking at the delta time required past nTime, which is where we exited the Gantt chart loop.
result = nDeltaTime + nTime - nCurrentTime;
if (fDebug10) LogPrintf("GetEstimatedTimetoStake debug: ETTS at %d confidence = %i", dConfidence, result);
// The old calculation for comparative purposes, only done if fDebug10 set. Note that this is the "fixed" old
// calculation, because the old network weight calculation was wrong too...
if (fDebug10)
{
double oldETTS = 0;
oldETTS = GetTargetSpacing(nBestHeight) * GetEstimatedNetworkWeight(40) / MinerStatus.WeightSum;
LogPrintf("GetEstimatedTimetoStake debug: oldETTS = %f", oldETTS);
}
return result;
}
void GetGlobalStatus()
{
//Populate overview
try
{
double boincmagnitude = CalculatedMagnitude(GetAdjustedTime(),false);
uint64_t nWeight = 0;
pwalletMain->GetStakeWeight(nWeight);
nBoincUtilization = boincmagnitude; //Legacy Support for the about screen
double weight = nWeight/COIN;
double PORDiff = GetDifficulty(GetLastBlockIndex(pindexBest, true));
std::string sWeight = RoundToString((double)weight,0);
//9-6-2015 Add RSA fields to overview
if ((double)weight > 100000000000000)
{
sWeight = sWeight.substr(0,13) + "E" + RoundToString((double)sWeight.length()-13,0);
}
// It is necessary to assign a local variable for ETTS to avoid an occasional deadlock between the lock below,
// the lock on cs_main in GetEstimateTimetoStake(), and the corresponding lock in the stakeminer.
double dETTS = GetEstimatedTimetoStake()/86400.0;
LOCK(GlobalStatusStruct.lock);
{ LOCK(MinerStatus.lock);
GlobalStatusStruct.blocks = ToString(nBestHeight);
GlobalStatusStruct.difficulty = RoundToString(PORDiff,3);
GlobalStatusStruct.netWeight = RoundToString(GetEstimatedNetworkWeight() / 80.0,2);
//todo: use the real weight from miner status (requires scaling)
GlobalStatusStruct.coinWeight = sWeight;
GlobalStatusStruct.magnitude = RoundToString(boincmagnitude,2);
GlobalStatusStruct.ETTS = RoundToString(dETTS,3);
GlobalStatusStruct.ERRperday = RoundToString(boincmagnitude * GRCMagnitudeUnit(GetAdjustedTime()),2);
GlobalStatusStruct.project = msMiningProject;
GlobalStatusStruct.cpid = GlobalCPUMiningCPID.cpid;
GlobalStatusStruct.poll = msPoll;
GlobalStatusStruct.status.clear();
if(MinerStatus.WeightSum)
GlobalStatusStruct.coinWeight = RoundToString(MinerStatus.WeightSum / 80.0,2);
GlobalStatusStruct.errors.clear();
std::string Alerts = GetWarnings("statusbar");
if(!Alerts.empty())
GlobalStatusStruct.errors += _("Alert: ") + Alerts + "; ";
if (PORDiff < 0.1)
GlobalStatusStruct.errors += _("Low difficulty!; ");
if(!MinerStatus.ReasonNotStaking.empty())
GlobalStatusStruct.errors += _("Miner: ") + MinerStatus.ReasonNotStaking;
unsigned long stk_dropped = MinerStatus.KernelsFound - MinerStatus.AcceptedCnt;
if(stk_dropped)
GlobalStatusStruct.errors += "Rejected " + ToString(stk_dropped) + " stakes;";
if(!msMiningErrors6.empty())
GlobalStatusStruct.errors +=msMiningErrors6 + "; ";
if(!msMiningErrors7.empty())
GlobalStatusStruct.errors += msMiningErrors7 + "; ";
if(!msMiningErrors8.empty())
GlobalStatusStruct.errors += msMiningErrors8 + "; ";
}
return;
}
catch (std::exception& e)
{
msMiningErrors = _("Error obtaining status.");
LogPrintf("Error obtaining status");
return;
}
}
bool Timer_Main(std::string timer_name, int max_ms)
{
mvTimers[timer_name] = mvTimers[timer_name] + 1;
if (mvTimers[timer_name] > max_ms)
{
mvTimers[timer_name]=0;
return true;
}
return false;
}
void RegisterWallet(CWallet* pwalletIn)
{
{
LOCK(cs_setpwalletRegistered);
setpwalletRegistered.insert(pwalletIn);
}
}
void UnregisterWallet(CWallet* pwalletIn)
{
{
LOCK(cs_setpwalletRegistered);
setpwalletRegistered.erase(pwalletIn);
}
}
MiningCPID GetInitializedGlobalCPUMiningCPID(std::string cpid)
{
MiningCPID mc = GetMiningCPID();
mc.initialized = true;
mc.cpid=cpid;
mc.projectname = cpid;
mc.cpidv2=cpid;
mc.cpidhash = "";
mc.email = cpid;
mc.boincruntimepublickey = cpid;
mc.rac=0;
mc.encboincpublickey = "";
mc.enccpid = "";
mc.NetworkRAC = 0;
mc.Magnitude = 0;
mc.clientversion = "";
mc.RSAWeight = GetRSAWeightByCPID(cpid);
mc.LastPaymentTime = nLastBlockSolved;
mc.diffbytes = 0;
mc.lastblockhash = "0";
// Reuse for debugging
mc.Organization = GetArg("-org", "");
return mc;
}
MiningCPID GetNextProject(bool bForce)
{
if (GlobalCPUMiningCPID.projectname.length() > 3 && GlobalCPUMiningCPID.projectname != "INVESTOR" && GlobalCPUMiningCPID.Magnitude >= 1)
{
if (!Timer_Main("globalcpuminingcpid",10))
{
//Prevent Thrashing
return GlobalCPUMiningCPID;
}
}
std::string sBoincKey = GetArgument("boinckey","");
if (!sBoincKey.empty())
{
if (fDebug3 && LessVerbose(50)) LogPrintf("Using cached boinckey for project %s",GlobalCPUMiningCPID.projectname);
msMiningProject = GlobalCPUMiningCPID.projectname;
msMiningCPID = GlobalCPUMiningCPID.cpid;
if (LessVerbose(5)) LogPrintf("BoincKey - Mining project %s RAC(%f)", GlobalCPUMiningCPID.projectname, GlobalCPUMiningCPID.rac);
double ProjectRAC = GetNetworkAvgByProject(GlobalCPUMiningCPID.projectname);
GlobalCPUMiningCPID.NetworkRAC = ProjectRAC;
GlobalCPUMiningCPID.Magnitude = CalculatedMagnitude(GetAdjustedTime(),false);
if (fDebug3) LogPrintf("(boinckey) For CPID %s Verified Magnitude = %f",GlobalCPUMiningCPID.cpid,GlobalCPUMiningCPID.Magnitude);
msMiningErrors = (msMiningCPID == "INVESTOR" || msPrimaryCPID=="INVESTOR" || msMiningCPID.empty()) ? _("Staking Interest") : _("Mining");
GlobalCPUMiningCPID.RSAWeight = GetRSAWeightByCPID(GlobalCPUMiningCPID.cpid);
GlobalCPUMiningCPID.LastPaymentTime = GetLastPaymentTimeByCPID(GlobalCPUMiningCPID.cpid);
return GlobalCPUMiningCPID;
}
msMiningProject = "";
msMiningCPID = "";
GlobalCPUMiningCPID = GetInitializedGlobalCPUMiningCPID("");
std::string email = GetArgument("email", "NA");
boost::to_lower(email);
if (IsInitialBlockDownload() && !bForce)
{
if (LessVerbose(100)) LogPrintf("CPUMiner: Gridcoin is downloading blocks Or CPIDs are not yet loaded...");
MilliSleep(1);
return GlobalCPUMiningCPID;
}
try
{
if (mvCPIDs.size() < 1)
{
if (fDebug && LessVerbose(10)) LogPrintf("Gridcoin has no CPIDs...");
//Let control reach the investor area
}
int iValidProjects=0;
//Count valid projects:
for(map<string,StructCPID>::iterator ii=mvCPIDs.begin(); ii!=mvCPIDs.end(); ++ii)
{
StructCPID structcpid = mvCPIDs[(*ii).first];
if ( msPrimaryCPID == structcpid.cpid &&
structcpid.initialized && structcpid.Iscpidvalid) iValidProjects++;
}
// Find next available CPU project:
int iDistributedProject = 0;
int iRow = 0;
if (email=="" || email=="NA") iValidProjects = 0; //Let control reach investor area
if (iValidProjects > 0)
{
for (int i = 0; i <= 4;i++)
{
iRow=0;
iDistributedProject = (rand() % iValidProjects)+1;
for(map<string,StructCPID>::iterator ii=mvCPIDs.begin(); ii!=mvCPIDs.end(); ++ii)
{
StructCPID structcpid = mvCPIDs[(*ii).first];
if (structcpid.initialized)
{
if (msPrimaryCPID == structcpid.cpid &&
structcpid.Iscpidvalid && structcpid.projectname.length() > 1)
{
iRow++;
if (i==4 || iDistributedProject == iRow)
{
if (true)
{
GlobalCPUMiningCPID.enccpid = structcpid.boincpublickey;
bool checkcpid = IsCPIDValid_Retired(structcpid.cpid,GlobalCPUMiningCPID.enccpid);
if (!checkcpid)
{
LogPrintf("CPID invalid %s 1. ",structcpid.cpid);
continue;
}
if (checkcpid)
{
GlobalCPUMiningCPID.email = email;
if (LessVerbose(1) || fDebug || fDebug3) LogPrintf("Ready to CPU Mine project %s with CPID %s, RAC(%f) ",
structcpid.projectname.c_str(),structcpid.cpid.c_str(),
structcpid.rac);
//Required for project to be mined in a block:
GlobalCPUMiningCPID.cpid=structcpid.cpid;
GlobalCPUMiningCPID.projectname = structcpid.projectname;
GlobalCPUMiningCPID.rac=structcpid.rac;
GlobalCPUMiningCPID.encboincpublickey = structcpid.boincpublickey;
GlobalCPUMiningCPID.encaes = structcpid.boincpublickey;
GlobalCPUMiningCPID.boincruntimepublickey = structcpid.cpidhash;
if(fDebug) LogPrintf("GNP: Setting bpk to %s",structcpid.cpidhash);
uint256 pbh = 1;
GlobalCPUMiningCPID.cpidv2 = ComputeCPIDv2(GlobalCPUMiningCPID.email,GlobalCPUMiningCPID.boincruntimepublickey, pbh);
GlobalCPUMiningCPID.lastblockhash = "0";
// Sign the block
GlobalCPUMiningCPID.BoincPublicKey = GetBeaconPublicKey(structcpid.cpid, false);
std::string sSignature;
std::string sError;
bool bResult = SignBlockWithCPID(GlobalCPUMiningCPID.cpid, GlobalCPUMiningCPID.lastblockhash, sSignature, sError, true);
# if 0
if (!bResult)
{
LogPrintf("GetNextProject: failed to sign block with cpid -> %s", sError);
continue;
}
GlobalCPUMiningCPID.BoincSignature = sSignature;
if (!IsCPIDValidv2(GlobalCPUMiningCPID,1))
{
LogPrintf("CPID INVALID (GetNextProject) %s, %s ",GlobalCPUMiningCPID.cpid,GlobalCPUMiningCPID.cpidv2);
continue;
}
# endif
//Only used for global status:
msMiningProject = structcpid.projectname;
msMiningCPID = structcpid.cpid;
double ProjectRAC = GetNetworkAvgByProject(GlobalCPUMiningCPID.projectname);
GlobalCPUMiningCPID.NetworkRAC = ProjectRAC;
GlobalCPUMiningCPID.Magnitude = CalculatedMagnitude(GetAdjustedTime(),false);
if (fDebug && LessVerbose(2)) LogPrintf("For CPID %s Verified Magnitude = %f",GlobalCPUMiningCPID.cpid, GlobalCPUMiningCPID.Magnitude);
//Reserved for GRC Speech Synthesis
msMiningErrors = (msMiningCPID == "INVESTOR" || !IsResearcher(msPrimaryCPID) || msMiningCPID.empty()) ? _("Staking Interest") : _("Boinc Mining");
GlobalCPUMiningCPID.RSAWeight = GetRSAWeightByCPID(GlobalCPUMiningCPID.cpid);
GlobalCPUMiningCPID.LastPaymentTime = GetLastPaymentTimeByCPID(GlobalCPUMiningCPID.cpid);
return GlobalCPUMiningCPID;
}
}
}
}
}
}
}
}
msMiningErrors = (IsResearcher(msPrimaryCPID)) ? _("All BOINC projects exhausted.") : "";
msMiningProject = "INVESTOR";
msMiningCPID = "INVESTOR";
GlobalCPUMiningCPID = GetInitializedGlobalCPUMiningCPID("INVESTOR");
if (fDebug10) LogPrintf("-Investor mode-");
}
catch (std::exception& e)
{
msMiningErrors = _("Error obtaining next project. Error 16172014.");
LogPrintf("Error obtaining next project");
}
catch(...)
{
msMiningErrors = _("Error obtaining next project. Error 06172014.");
LogPrintf("Error obtaining next project 2.");
}
return GlobalCPUMiningCPID;
}
// check whether the passed transaction is from us
bool static IsFromMe(CTransaction& tx)
{
for (auto const& pwallet : setpwalletRegistered)
if (pwallet->IsFromMe(tx))
return true;
return false;
}
// get the wallet transaction with the given hash (if it exists)
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
{
for (auto const& pwallet : setpwalletRegistered)
if (pwallet->GetTransaction(hashTx,wtx))
return true;
return false;
}