upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-06 01:53:03 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-06 01:53:03 +0000
commitef9f06d43fc6577c15df2256abbb25b20d4227b8 (patch)
tree7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/main.cpp
parentbe63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff)
parentceae3d076b5ad11ee84729ae43f14e9312896646 (diff)
Merge branch 'chore/main-catchup-2026-08-26' into 'main'HEADmain
Bring main onto the line this project actually ships (incl. the 1.2.23 compactblock port) Closes #6, #7, #8, #11, #35, #34, #29, #28, #27, and #16 See merge request !16
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp1000
1 files changed, 936 insertions, 64 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 77aba89..0fa6b89 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -14,6 +14,7 @@
14#include <atomic> 14#include <atomic>
15#include <mutex> 15#include <mutex>
16#include <thread> 16#include <thread>
17#include <system_error>
17#include "bip32.h" 18#include "bip32.h"
18#include "btfaddr.h" 19#include "btfaddr.h"
19#include "btftunnel.h" 20#include "btftunnel.h"
@@ -41,12 +42,30 @@ unsigned int nTransactionsUpdated = 0;
41map<COutPoint, CInPoint> mapNextTx; 42map<COutPoint, CInPoint> mapNextTx;
42 43
43map<uint256, CBlockIndex*> mapBlockIndex; 44map<uint256, CBlockIndex*> mapBlockIndex;
44// Bitflash genesis -- mined under RandomX PoW on 2026-07-22 45// Bitflash mainnet genesis -- mined under RandomX PoW for the 2026 stable
45// (nTime 1753142400, nBits 0x1f0fffff, RandomX key "Bitflash/RandomX/v1/...") 46// relaunch. Testnet gets its own resettable genesis below so block indexes,
46// Bitflash 2026 stable relaunch genesis (2-min blocks, RandomX). Mined values. 47// headers, and wallet scans cannot be accidentally mixed across networks.
47unsigned int GENESIS_NONCE = 3141; 48static const unsigned int MAINNET_GENESIS_TIME = 1753315200; // 2026-07-24 00:00:00 UTC
48uint256 hashGenesisBlock("0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"); 49static const unsigned int MAINNET_GENESIS_NONCE = 3141;
49uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"); 50static const char* MAINNET_GENESIS_TIMESTAMP =
51 "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine";
52static const char* MAINNET_GENESIS_HASH =
53 "0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169";
54static const char* MAINNET_GENESIS_MERKLE =
55 "0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325";
56
57static const unsigned int TESTNET_GENESIS_TIME = 1787184000; // 2026-08-20 00:00:00 UTC
58static const unsigned int TESTNET_GENESIS_NONCE = 3199;
59static const char* TESTNET_GENESIS_TIMESTAMP =
60 "Bitflash testnet 20/Aug/2026: resettable public test chain";
61static const char* TESTNET_GENESIS_HASH =
62 "0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb";
63static const char* TESTNET_GENESIS_MERKLE =
64 "0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c";
65
66unsigned int GENESIS_NONCE = MAINNET_GENESIS_NONCE;
67uint256 hashGenesisBlock(MAINNET_GENESIS_HASH);
68uint256 hashGenesisMerkleRoot(MAINNET_GENESIS_MERKLE);
50CBlockIndex* pindexGenesisBlock = NULL; 69CBlockIndex* pindexGenesisBlock = NULL;
51int nBestHeight = -1; 70int nBestHeight = -1;
52uint256 hashBestChain = 0; 71uint256 hashBestChain = 0;
@@ -64,6 +83,137 @@ CBlockIndex* pindexBest = NULL;
64map<uint256, CBlock*> mapOrphanBlocks; 83map<uint256, CBlock*> mapOrphanBlocks;
65multimap<uint256, CBlock*> mapOrphanBlocksByPrev; 84multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
66 85
86// Block bodies we have sent a getdata for and not yet received, so the
87// failover pass in SendMessages can tell "requested and being served" from
88// "requested and gone quiet" and re-issue the request to a different peer
89// (#33). Keyed by block hash; setAsked is the peers already tried since the
90// round last restarted, so the retry rotates instead of hammering the
91// announcer that is not serving. Guarded by cs_main, which both the message
92// handlers and SendMessages already hold.
93//
94// Capped small on purpose. At the tip there is one block in flight; during
95// initial download there are legitimately hundreds outstanding to one peer,
96// and rotating those would duplicate the whole download to every other peer.
97//
98// The cap is a shared resource and nothing on the path from inv to insertion
99// can tell a real block hash from 32 random bytes -- AlreadyHave only asks
100// whether we already hold the block, and there is no header to check work
101// against. So the table does not refuse a new hash when it is full; it makes
102// room. See MakeRoomInBlockFetch for the rule, which is what stops one peer
103// filling all eight slots and switching failover off for everybody.
104//
105// The initialisers matter: the insertion path uses operator[] and then tests
106// nTimeFirstAsked == 0 to detect a fresh entry. That worked on
107// value-initialisation alone, but only for as long as nobody gave this struct
108// a constructor.
109struct CBlockFetch
110{
111 int64 nTimeFirstAsked = 0; // when the first getdata for this block went out
112 int64 nTimeAsked = 0; // when the latest getdata went out
113 CAddress addrAnnounced; // peer whose announcement put this hash here
114 set<CAddress> setAsked; // peers tried since the round last restarted
115};
116map<uint256, CBlockFetch> mapBlockFetch;
117static const int BLOCK_FETCH_TRACK_MAX = 8;
118// A block nobody has served for this long is not a fetch problem: the hash may
119// have been announced and never existed. Drop the entry rather than getdata
120// forever; a re-announcement recreates it.
121static const int BLOCK_FETCH_GIVEUP_SECS = 10 * 60;
122
123// Failover firing at all is worth saying out loud: it means peers are
124// announcing blocks they then do not serve, which is the pathology of #33 and
125// the thing nobody could otherwise see. Naming the peer on every re-request is
126// a different matter. A peer can drive that line as often as it likes by
127// announcing hashes it will not serve, and what it prints is a peer address,
128// which is the one thing the .btf transport otherwise takes trouble not to
129// spread around. So the per-peer detail sits behind the net category and the
130// unconditional line is a count over a window no peer can shorten: the first
131// failover after a quiet spell prints at once, because the change from "blocks
132// arrive" to "blocks are announced and not served" is the event worth seeing,
133// and everything after it is summarised once a minute.
134static const int BLOCK_FETCH_REPORT_SECS = 60;
135static int64 nBlockFetchWindowStart = 0;
136static int nBlockFetchFailovers = 0;
137
138// How stale our own best block may be before we stop calling this the tip.
139// Blocks target two minutes, so this is thirty intervals: far longer than any
140// stall worth failing over from -- the one in #33 ran sixteen minutes -- and
141// far shorter than catching up, which starts hours or days back.
142static const int BLOCK_FETCH_TIP_AGE_SECS = 60 * 60;
143
144// Whether this node is tracking the tip rather than still catching up.
145//
146// Measured off the age of our own best block, not off any height a peer told
147// us. nStartingHeight is optional in the version message and every node
148// released so far sends the short form, so it is -1 for essentially every
149// peer; and where it is filled in later it is filled in from the height of a
150// block that peer served us, which during catch-up tracks our own height and
151// would report "at the tip" for the whole download. Block timestamps are the
152// one clock here that peers cannot move: CheckBlock refuses anything more than
153// two hours ahead and AcceptBlock refuses anything at or before the previous
154// median, so the only way to make this read "at the tip" falsely is to mine,
155// and all that buys is the 15s spacing this code had unconditionally anyway.
156bool IsBlockFetchNearTip()
157{
158 return pindexBest && (int64)pindexBest->nTime > GetTime() - BLOCK_FETCH_TIP_AGE_SECS;
159}
160
161// Drop one entry so a hash announced by addrNew can be tracked.
162//
163// Refusing the insertion instead was a remote off-switch for the whole
164// feature. An inv carrying eight hashes that never existed filled every slot
165// -- nothing between inv and here can tell those from real blocks -- and no
166// genuinely new block was tracked again until the give-up timer expired ten
167// minutes later, re-armable for one message per ten minutes from a single
168// connection.
169//
170// So evict instead, by occupancy: a peer may only take a slot from a peer
171// holding strictly more slots than itself, and otherwise recycles one of its
172// own. That is what keeps eviction from becoming the same attack in a new
173// shape. A peer that has filled the table is by definition the heaviest
174// holder, so its own entries are the only ones it can displace, while a peer
175// announcing its first block holds nothing and therefore always evicts from
176// the heaviest -- a genuine announcement is never refused a slot, whatever the
177// attacker holds or however many connections it holds it from. The one case
178// that costs a real fetch is eight distinct peers holding one entry each,
179// where a ninth takes the oldest; that is a busy healthy node, not an attack,
180// and losing the oldest of eight beats refusing the ninth.
181static void MakeRoomInBlockFetch(const CAddress& addrNew)
182{
183 if (mapBlockFetch.empty())
184 return;
185
186 map<CAddress, int> mapHeld;
187 for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end(); ++mi)
188 mapHeld[(*mi).second.addrAnnounced]++;
189
190 int nMine = mapHeld.count(addrNew) ? mapHeld[addrNew] : 0;
191
192 int nMax = 0;
193 CAddress addrVictim;
194 for (map<CAddress, int>::iterator mi = mapHeld.begin(); mi != mapHeld.end(); ++mi)
195 if ((*mi).second > nMax)
196 {
197 nMax = (*mi).second;
198 addrVictim = (*mi).first;
199 }
200
201 // Nobody is holding more than this peer, so it recycles its own oldest
202 // rather than taking a slot off an equal.
203 if (nMax <= nMine)
204 addrVictim = addrNew;
205
206 map<uint256, CBlockFetch>::iterator miVictim = mapBlockFetch.end();
207 for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end(); ++mi)
208 if ((*mi).second.addrAnnounced == addrVictim &&
209 (miVictim == mapBlockFetch.end() ||
210 (*mi).second.nTimeFirstAsked < (*miVictim).second.nTimeFirstAsked))
211 miVictim = mi;
212
213 if (miVictim != mapBlockFetch.end())
214 mapBlockFetch.erase(miVictim);
215}
216
67map<uint256, CDataStream*> mapOrphanTransactions; 217map<uint256, CDataStream*> mapOrphanTransactions;
68multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev; 218multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
69 219
@@ -109,7 +259,11 @@ string strPayoutFile; // participant: /payoutfile with one addres
109int nHashThreads = 0; // participant: /hashthreads override (0 = cores-1) 259int nHashThreads = 0; // participant: /hashthreads override (0 = cores-1)
110string strPoolName = "Bitflash Pool"; 260string strPoolName = "Bitflash Pool";
111string strPoolDashboardUrl; 261string strPoolDashboardUrl;
262string strPoolStatusFile;
263string strPoolRoundsFile;
112double dPoolFeePercent = 0.0; 264double dPoolFeePercent = 0.0;
265bool fStratumBridge = false;
266int nStratumBridgePort = 3333;
113 267
114static std::atomic<uint64> gParticipantSharesSent{0}; 268static std::atomic<uint64> gParticipantSharesSent{0};
115static std::atomic<uint64> gParticipantSharesAccepted{0}; 269static std::atomic<uint64> gParticipantSharesAccepted{0};
@@ -118,6 +272,63 @@ static std::atomic<uint64> gParticipantHashRateX1000{0};
118static std::mutex gParticipantStatusMutex; 272static std::mutex gParticipantStatusMutex;
119static std::string gParticipantStatus; 273static std::string gParticipantStatus;
120 274
275void SelectGenesisParams(bool fTestNetIn)
276{
277 static bool fGenesisParamsSelected = false;
278 static bool fGenesisParamsTestNet = false;
279 if (fGenesisParamsSelected && fGenesisParamsTestNet == fTestNetIn)
280 return;
281
282 fGenesisParamsSelected = true;
283 fGenesisParamsTestNet = fTestNetIn;
284
285 if (fTestNetIn)
286 {
287 GENESIS_NONCE = TESTNET_GENESIS_NONCE;
288 hashGenesisBlock = uint256(TESTNET_GENESIS_HASH);
289 hashGenesisMerkleRoot = uint256(TESTNET_GENESIS_MERKLE);
290 }
291 else
292 {
293 GENESIS_NONCE = MAINNET_GENESIS_NONCE;
294 hashGenesisBlock = uint256(MAINNET_GENESIS_HASH);
295 hashGenesisMerkleRoot = uint256(MAINNET_GENESIS_MERKLE);
296 }
297}
298
299void SelectChainParams(bool fTestNetIn)
300{
301 SelectNetworkParams(fTestNetIn);
302 SelectGenesisParams(fTestNetIn);
303}
304
305static CBlock BuildGenesisBlock()
306{
307 const char* pszTimestamp = IsTestNet() ?
308 TESTNET_GENESIS_TIMESTAMP : MAINNET_GENESIS_TIMESTAMP;
309
310 CTransaction txNew;
311 txNew.vin.resize(1);
312 txNew.vout.resize(1);
313 txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) <<
314 vector<unsigned char>((unsigned char*)pszTimestamp,
315 (unsigned char*)pszTimestamp + strlen(pszTimestamp));
316 txNew.vout[0].nValue = 50 * COIN;
317 txNew.vout[0].scriptPubKey = CScript() <<
318 CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") <<
319 OP_CHECKSIG;
320
321 CBlock block;
322 block.vtx.push_back(txNew);
323 block.hashPrevBlock = 0;
324 block.hashMerkleRoot = block.BuildMerkleTree();
325 block.nVersion = 1;
326 block.nTime = IsTestNet() ? TESTNET_GENESIS_TIME : MAINNET_GENESIS_TIME;
327 block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor)
328 block.nNonce = GENESIS_NONCE;
329 return block;
330}
331
121void SetParticipantMiningStatus(const std::string& status) 332void SetParticipantMiningStatus(const std::string& status)
122{ 333{
123 // Updates the GUI status string only. Logging is done at each call site 334 // Updates the GUI status string only. Logging is done at each call site
@@ -190,6 +401,9 @@ int ParticipantHashThreadCount()
190 return nWorkers - 1; 401 return nWorkers - 1;
191} 402}
192 403
404static void SecureClearBytes(vector<unsigned char>& vch);
405static void SecureClearKeyingMaterial(CKeyingMaterial& vch);
406
193bool AddKey(const CKey& key) 407bool AddKey(const CKey& key)
194{ 408{
195 if (IsWalletEncrypted()) 409 if (IsWalletEncrypted())
@@ -199,9 +413,13 @@ bool AddKey(const CKey& key)
199 vector<unsigned char> vchCryptedSecret; 413 vector<unsigned char> vchCryptedSecret;
200 if (IsWalletLocked()) 414 if (IsWalletLocked())
201 return error("AddKey() : wallet is locked\n"); 415 return error("AddKey() : wallet is locked\n");
202 if (!EncryptSecret(vWalletMasterKey, vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()), 416 vector<unsigned char> vchPlainSecret(vchPrivKey.begin(), vchPrivKey.end());
203 WalletKeyIV(key.GetPubKey()), vchCryptedSecret)) 417 if (!EncryptSecret(vWalletMasterKey, vchPlainSecret, WalletKeyIV(key.GetPubKey()), vchCryptedSecret))
418 {
419 SecureClearBytes(vchPlainSecret);
204 return error("AddKey() : encrypting key failed\n"); 420 return error("AddKey() : encrypting key failed\n");
421 }
422 SecureClearBytes(vchPlainSecret);
205 if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) 423 if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret))
206 return false; 424 return false;
207 return CWalletDB().WriteCryptedKey(key.GetPubKey(), vchCryptedSecret); 425 return CWalletDB().WriteCryptedKey(key.GetPubKey(), vchCryptedSecret);
@@ -225,17 +443,31 @@ bool IsWalletLocked()
225 return fWalletEncrypted && fWalletLocked; 443 return fWalletEncrypted && fWalletLocked;
226} 444}
227 445
446static void SecureClearBytes(vector<unsigned char>& vch)
447{
448 if (!vch.empty())
449 OPENSSL_cleanse(&vch[0], vch.size());
450 vch.clear();
451}
452
453static void SecureClearKeyingMaterial(CKeyingMaterial& vch)
454{
455 if (!vch.empty())
456 OPENSSL_cleanse(&vch[0], vch.size());
457 vch.clear();
458}
459
228void LockWallet() 460void LockWallet()
229{ 461{
230 if (!fWalletEncrypted) 462 if (!fWalletEncrypted)
231 return; 463 return;
232 CRITICAL_BLOCK(cs_mapKeys) 464 CRITICAL_BLOCK(cs_mapKeys)
233 { 465 {
234 vWalletMasterKey.clear(); 466 SecureClearKeyingMaterial(vWalletMasterKey);
235 if (!vchCryptedHDMaster.empty()) 467 if (!vchCryptedHDMaster.empty())
236 vchHDMaster.clear(); 468 SecureClearBytes(vchHDMaster);
237 if (!vchCryptedHDChainCode.empty()) 469 if (!vchCryptedHDChainCode.empty())
238 vchHDChainCode.clear(); 470 SecureClearBytes(vchHDChainCode);
239 fWalletLocked = true; 471 fWalletLocked = true;
240 } 472 }
241} 473}
@@ -260,14 +492,14 @@ bool DeriveWalletPassphraseKey(const string& strPassphrase,
260 chKey, chIV); 492 chKey, chIV);
261 if (n != 32) 493 if (n != 32)
262 { 494 {
263 memset(chKey, 0, sizeof(chKey)); 495 OPENSSL_cleanse(chKey, sizeof(chKey));
264 memset(chIV, 0, sizeof(chIV)); 496 OPENSSL_cleanse(chIV, sizeof(chIV));
265 return false; 497 return false;
266 } 498 }
267 vchKeyRet.assign(chKey, chKey + sizeof(chKey)); 499 vchKeyRet.assign(chKey, chKey + sizeof(chKey));
268 vchIVRet.assign(chIV, chIV + sizeof(chIV)); 500 vchIVRet.assign(chIV, chIV + sizeof(chIV));
269 memset(chKey, 0, sizeof(chKey)); 501 OPENSSL_cleanse(chKey, sizeof(chKey));
270 memset(chIV, 0, sizeof(chIV)); 502 OPENSSL_cleanse(chIV, sizeof(chIV));
271 return true; 503 return true;
272} 504}
273 505
@@ -328,6 +560,8 @@ bool DecryptSecret(const CKeyingMaterial& vchKey,
328 vchPlaintextRet.swap(vchOut); 560 vchPlaintextRet.swap(vchOut);
329 fOk = true; 561 fOk = true;
330 } 562 }
563 if (!fOk)
564 SecureClearBytes(vchOut);
331 EVP_CIPHER_CTX_free(ctx); 565 EVP_CIPHER_CTX_free(ctx);
332 return fOk; 566 return fOk;
333} 567}
@@ -403,6 +637,7 @@ bool GetWalletPrivKey(const vector<unsigned char>& vchPubKey,
403 } 637 }
404 638
405 CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); 639 CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end());
640 SecureClearBytes(vchPlaintext);
406 CKey key; 641 CKey key;
407 if (!key.SetPrivKey(vchPrivKey) || key.GetPubKey() != vchPubKey) 642 if (!key.SetPrivKey(vchPrivKey) || key.GetPubKey() != vchPubKey)
408 { 643 {
@@ -437,11 +672,20 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet)
437 vector<unsigned char> vchMasterPlain; 672 vector<unsigned char> vchMasterPlain;
438 if (!DecryptSecret(vchPassKey, kMasterKey.vchCryptedKey, 673 if (!DecryptSecret(vchPassKey, kMasterKey.vchCryptedKey,
439 vchPassIV, vchMasterPlain)) 674 vchPassIV, vchMasterPlain))
675 {
676 SecureClearBytes(vchPassIV);
440 continue; 677 continue;
678 }
441 if (vchMasterPlain.size() != 32) 679 if (vchMasterPlain.size() != 32)
680 {
681 SecureClearBytes(vchMasterPlain);
682 SecureClearBytes(vchPassIV);
442 continue; 683 continue;
684 }
443 685
444 CKeyingMaterial vchCandidate(vchMasterPlain.begin(), vchMasterPlain.end()); 686 CKeyingMaterial vchCandidate(vchMasterPlain.begin(), vchMasterPlain.end());
687 SecureClearBytes(vchMasterPlain);
688 SecureClearBytes(vchPassIV);
445 bool fValid = mapCryptedKeys.empty(); 689 bool fValid = mapCryptedKeys.empty();
446 for (map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.begin(); 690 for (map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.begin();
447 mci != mapCryptedKeys.end(); ++mci) 691 mci != mapCryptedKeys.end(); ++mci)
@@ -450,6 +694,7 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet)
450 if (!DecryptSecret(vchCandidate, (*mci).second, WalletKeyIV((*mci).first), vchPlaintext)) 694 if (!DecryptSecret(vchCandidate, (*mci).second, WalletKeyIV((*mci).first), vchPlaintext))
451 break; 695 break;
452 CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); 696 CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end());
697 SecureClearBytes(vchPlaintext);
453 CKey key; 698 CKey key;
454 if (key.SetPrivKey(vchPrivKey) && key.GetPubKey() == (*mci).first) 699 if (key.SetPrivKey(vchPrivKey) && key.GetPubKey() == (*mci).first)
455 fValid = true; 700 fValid = true;
@@ -465,10 +710,12 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet)
465 WalletSecretIV("hdmaster"), vchPlainHDMaster) || 710 WalletSecretIV("hdmaster"), vchPlainHDMaster) ||
466 vchPlainHDMaster.size() != 32) 711 vchPlainHDMaster.size() != 32)
467 { 712 {
713 SecureClearBytes(vchPlainHDMaster);
468 strErrorRet = "could not decrypt HD master key"; 714 strErrorRet = "could not decrypt HD master key";
469 return false; 715 return false;
470 } 716 }
471 vchHDMaster = vchPlainHDMaster; 717 vchHDMaster = vchPlainHDMaster;
718 SecureClearBytes(vchPlainHDMaster);
472 } 719 }
473 if (!vchCryptedHDChainCode.empty()) 720 if (!vchCryptedHDChainCode.empty())
474 { 721 {
@@ -477,10 +724,12 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet)
477 WalletSecretIV("hdchaincode"), vchPlainHDChainCode) || 724 WalletSecretIV("hdchaincode"), vchPlainHDChainCode) ||
478 vchPlainHDChainCode.size() != 32) 725 vchPlainHDChainCode.size() != 32)
479 { 726 {
727 SecureClearBytes(vchPlainHDChainCode);
480 strErrorRet = "could not decrypt HD chain code"; 728 strErrorRet = "could not decrypt HD chain code";
481 return false; 729 return false;
482 } 730 }
483 vchHDChainCode = vchPlainHDChainCode; 731 vchHDChainCode = vchPlainHDChainCode;
732 SecureClearBytes(vchPlainHDChainCode);
484 } 733 }
485 fWalletLocked = false; 734 fWalletLocked = false;
486 return true; 735 return true;
@@ -625,7 +874,7 @@ unsigned int nHDNext = 0;
625int nHDKeySchema = HD_SCHEMA_NONE; 874int nHDKeySchema = HD_SCHEMA_NONE;
626unsigned int nHDReceiveNext = 0; 875unsigned int nHDReceiveNext = 0;
627unsigned int nHDChangeNext = 0; 876unsigned int nHDChangeNext = 0;
628unsigned int nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL; 877unsigned int nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH;
629 878
630string HDKeySchemaName(int nSchema) 879string HDKeySchemaName(int nSchema)
631{ 880{
@@ -781,7 +1030,7 @@ bool SetHDSeedFromMnemonic(const string& strMnemonic, string& strErrorRet)
781 1030
782 if (!CWalletDB().WriteHDMaster(master.privateKey, master.chainCode) || 1031 if (!CWalletDB().WriteHDMaster(master.privateKey, master.chainCode) ||
783 !CWalletDB().WriteHDSchema(HD_SCHEMA_BIP44) || 1032 !CWalletDB().WriteHDSchema(HD_SCHEMA_BIP44) ||
784 !CWalletDB().WriteHDCoinType(HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL) || 1033 !CWalletDB().WriteHDCoinType(HD_BIP44_COIN_TYPE_BITFLASH) ||
785 !CWalletDB().WriteHDNext(0) || 1034 !CWalletDB().WriteHDNext(0) ||
786 !CWalletDB().WriteHDReceiveNext(1) || 1035 !CWalletDB().WriteHDReceiveNext(1) ||
787 !CWalletDB().WriteHDChangeNext(0)) 1036 !CWalletDB().WriteHDChangeNext(0))
@@ -794,7 +1043,7 @@ bool SetHDSeedFromMnemonic(const string& strMnemonic, string& strErrorRet)
794 nHDKeySchema = HD_SCHEMA_BIP44; 1043 nHDKeySchema = HD_SCHEMA_BIP44;
795 nHDReceiveNext = 1; 1044 nHDReceiveNext = 1;
796 nHDChangeNext = 0; 1045 nHDChangeNext = 0;
797 nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL; 1046 nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH;
798 1047
799 // Index 0 is spoken for before it is derived, so a failure below cannot 1048 // Index 0 is spoken for before it is derived, so a failure below cannot
800 // leave the pool free to hand it out as an ordinary key. The cost of 1049 // leave the pool free to hand it out as an ordinary key. The cost of
@@ -1670,7 +1919,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
1670 uint256 hash = GetHash(); 1919 uint256 hash = GetHash();
1671 if (!txdb.ContainsTx(hash)) 1920 if (!txdb.ContainsTx(hash))
1672 { 1921 {
1673 if (LogAcceptsCategory("net")) printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str()); 1922 if (LogAcceptsCategory("relay")) printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str());
1674 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); 1923 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
1675 } 1924 }
1676 } 1925 }
@@ -2480,11 +2729,21 @@ string GetAppDir()
2480 { 2729 {
2481 return "."; 2730 return ".";
2482 } 2731 }
2483 static bool fMkdirDone; 2732 static string strCreatedBaseDir;
2484 if (!fMkdirDone) 2733 if (strCreatedBaseDir != strDir)
2485 { 2734 {
2486 fMkdirDone = true;
2487 _mkdir(strDir.c_str()); 2735 _mkdir(strDir.c_str());
2736 strCreatedBaseDir = strDir;
2737 }
2738 if (IsTestNet())
2739 {
2740 strDir += "/testnet";
2741 static string strCreatedTestnetDir;
2742 if (strCreatedTestnetDir != strDir)
2743 {
2744 _mkdir(strDir.c_str());
2745 strCreatedTestnetDir = strDir;
2746 }
2488 } 2747 }
2489 return strDir; 2748 return strDir;
2490} 2749}
@@ -2636,22 +2895,36 @@ bool LoadBlockIndex(bool fAllowNew)
2636 // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) 2895 // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
2637 // vMerkleTree: 4a5e1e 2896 // vMerkleTree: 4a5e1e
2638 2897
2639 // Bitflash genesis block -- own header (fair launch, 2026 stable relaunch) 2898 // Bitflash genesis block -- own header (fair launch)
2640 char* pszTimestamp = "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; 2899 //
2641 CTransaction txNew; 2900 // Upstream moved the construction into BuildGenesisBlock() so mainnet
2642 txNew.vin.resize(1); 2901 // and the isolated testnet can share it. The mainnet inputs it uses are
2643 txNew.vout.resize(1); 2902 // byte-identical to what this block built inline before the merge --
2644 txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp)); 2903 // timestamp string, nTime, nNonce, scriptPubKey, nBits and nVersion all
2645 txNew.vout[0].nValue = 50 * COIN; 2904 // verified equal against MAINNET_GENESIS_* -- so the mainnet genesis
2646 txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG; 2905 // hash is unchanged. The warning below is why that was checked rather
2647 CBlock block; 2906 // than assumed, and it still applies to those constants:
2648 block.vtx.push_back(txNew); 2907 //
2649 block.hashPrevBlock = 0; 2908 // Do not change MAINNET_GENESIS_TIMESTAMP, the scriptPubKey,
2650 block.hashMerkleRoot = block.BuildMerkleTree(); 2909 // MAINNET_GENESIS_TIME or MAINNET_GENESIS_NONCE. They are the inputs to
2651 block.nVersion = 1; 2910 // the genesis hash, and the genesis hash is consensus. Changing one on
2652 block.nTime = 1753315200; // 2026-07-24 00:00:00 UTC (stable relaunch) 2911 // its own trips the assert below, which is loud and reverts cleanly;
2653 block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor) 2912 // changing one and regenerating GENESIS_NONCE and hashGenesisBlock to
2654 block.nNonce = GENESIS_NONCE; 2913 // match -- which is exactly what the re-mining branch below prints out
2914 // and invites -- gives a node that starts without complaint, builds its
2915 // own chain from height 0, and rejects every real peer as being on a
2916 // different chain. That one fails silently, and the damage scales with
2917 // how many people run the "fixed" build.
2918 //
2919 // MAINNET_GENESIS_TIME is 1753315200, which is 2025-07-24T00:00:00Z,
2920 // while the headline in MAINNET_GENESIS_TIMESTAMP reads 2026 -- as does
2921 // the comment upstream put on that constant, which is wrong about the
2922 // year. This is a known discrepancy and not a typo in the value: it is
2923 // only consensus-critical as an input to the genesis hash, the block
2924 // hashes to hashGenesisBlock with it, and every node on the network
2925 // agrees. The mismatch is in the launched chain and cannot be corrected
2926 // -- see the warning above before deciding otherwise.
2927 CBlock block = BuildGenesisBlock();
2655 2928
2656 // If the nonce has not been found yet, mine the genesis once (RandomX) 2929 // If the nonce has not been found yet, mine the genesis once (RandomX)
2657 // and print the values to be hardcoded in the constants above. 2930 // and print the values to be hardcoded in the constants above.
@@ -2676,6 +2949,11 @@ bool LoadBlockIndex(bool fAllowNew)
2676 printf("powhash = %s\n", block.GetPoWHash().ToString().c_str()); 2949 printf("powhash = %s\n", block.GetPoWHash().ToString().c_str());
2677 printf("hash = %s\n", block.GetHash().ToString().c_str()); 2950 printf("hash = %s\n", block.GetHash().ToString().c_str());
2678 printf("merkle = %s\n", block.hashMerkleRoot.ToString().c_str()); 2951 printf("merkle = %s\n", block.hashMerkleRoot.ToString().c_str());
2952 printf("----------------------------------------\n");
2953 printf("These constants bring up a NEW chain. Do not paste them\n");
2954 printf("into a build for this network to make an edited nTime or\n");
2955 printf("pszTimestamp fit -- a node whose genesis differs starts\n");
2956 printf("cleanly on a chain of its own and rejects every real peer.\n");
2679 printf("========================================\n"); 2957 printf("========================================\n");
2680 } 2958 }
2681 2959
@@ -3221,15 +3499,61 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3221 vRecv >> *pblock; 3499 vRecv >> *pblock;
3222 3500
3223 //// debug print 3501 //// debug print
3224 if (LogAcceptsCategory("net")) { printf("received block:\n"); pblock->print(); } 3502 if (LogAcceptsCategory("relay")) { printf("received block:\n"); pblock->print(); }
3225 3503
3226 CInv inv(MSG_BLOCK, pblock->GetHash()); 3504 CInv inv(MSG_BLOCK, pblock->GetHash());
3227 pfrom->AddInventoryKnown(inv); 3505 pfrom->AddInventoryKnown(inv);
3228 3506
3507 // GetHash() covers the 80-byte header and nothing else, so the hash
3508 // coming out right does not make this the block we asked for. Every
3509 // peer that saw X announced holds X's header, and can answer our
3510 // getdata with that header bolted onto a transaction list of its own
3511 // invention -- the reply still hashes to X. hashMerkleRoot is the only
3512 // thing binding the header to the body, so check it here, before the
3513 // mapBlockFetch entry is touched and before CheckBlock spends a
3514 // RandomX hash on a body already known to be forged.
3515 //
3516 // There is no honest way to arrive here. Peer traffic rides the .btf
3517 // channel, which is XChaCha20-Poly1305, so a body that arrives at all
3518 // arrived intact: a mismatch is the peer's own construction, not line
3519 // noise. Drop the peer and leave the fetch outstanding so the failover
3520 // sweep asks somebody else for the real X, rather than recording X as
3521 // delivered on the strength of a header anyone could copy.
3522 if (pblock->hashMerkleRoot != pblock->BuildMerkleTree())
3523 {
3524 if (LogAcceptsCategory("net"))
3525 printf("peer %s served a forged body for %s\n",
3526 pfrom->addr.ToString().c_str(), inv.ToString().c_str());
3527 // One line per connection: ProcessMessages keeps draining the
3528 // receive buffer after fDisconnect is set, so a peer that packs
3529 // many forged bodies into one buffer must not get one log line
3530 // for each of them.
3531 if (!pfrom->fDisconnect)
3532 printf("block fetch: a peer answered the request for %s with a body that is not "
3533 "that block -- dropping it\n", inv.hash.ToString().substr(0,14).c_str());
3534 pfrom->fDisconnect = true;
3535 return error("ProcessMessage() : forged block body for %s",
3536 inv.hash.ToString().substr(0,14).c_str());
3537 }
3538
3229 if (ProcessBlock(pfrom, pblock.release())) 3539 if (ProcessBlock(pfrom, pblock.release()))
3230 { 3540 {
3231 mapAlreadyAskedFor.erase(inv); 3541 mapAlreadyAskedFor.erase(inv);
3232 3542
3543 // The body arrived and validated, so the fetch really is over:
3544 // the block is now in mapBlockIndex, or parked in mapOrphanBlocks,
3545 // and either way AlreadyHave answers for it from here on.
3546 //
3547 // This has to stay inside the success branch, next to the erase
3548 // above. When validation rejects a body nothing is stored,
3549 // AlreadyHave stays false, and the mapAskFor entry was already
3550 // consumed when the getdata went out -- so erasing the tracking
3551 // here would leave the block requested from nobody at all until
3552 // some peer happened to re-announce it. That is exactly the stall
3553 // of #33, and erasing before ProcessBlock made it reachable on
3554 // demand.
3555 mapBlockFetch.erase(inv.hash);
3556
3233 // A peer that just handed us a block is demonstrably at least at 3557 // A peer that just handed us a block is demonstrably at least at
3234 // that block's height. Without this, nStartingHeight is a snapshot 3558 // that block's height. Without this, nStartingHeight is a snapshot
3235 // taken once during the version handshake and never touched again, 3559 // taken once during the version handshake and never touched again,
@@ -3354,12 +3678,119 @@ bool SendMessages(CNode* pto)
3354 const CInv& inv = (*pto->mapAskFor.begin()).second; 3678 const CInv& inv = (*pto->mapAskFor.begin()).second;
3355 if (LogAcceptsCategory("net")) printf("sending getdata: %s\n", inv.ToString().c_str()); 3679 if (LogAcceptsCategory("net")) printf("sending getdata: %s\n", inv.ToString().c_str());
3356 if (!AlreadyHave(txdb, inv)) 3680 if (!AlreadyHave(txdb, inv))
3681 {
3357 vAskFor.push_back(inv); 3682 vAskFor.push_back(inv);
3683 // Blocks get their delivery tracked so the failover pass
3684 // below can chase the ones that go quiet.
3685 if (inv.type == MSG_BLOCK)
3686 {
3687 // Full, and this is a hash we are not already tracking:
3688 // make room rather than drop it on the floor, or one peer
3689 // announcing eight hashes turns failover off for everyone.
3690 if (!mapBlockFetch.count(inv.hash) &&
3691 mapBlockFetch.size() >= BLOCK_FETCH_TRACK_MAX)
3692 MakeRoomInBlockFetch(pto->addr);
3693
3694 if (mapBlockFetch.count(inv.hash) ||
3695 mapBlockFetch.size() < BLOCK_FETCH_TRACK_MAX)
3696 {
3697 CBlockFetch& fetch = mapBlockFetch[inv.hash];
3698 if (fetch.nTimeFirstAsked == 0)
3699 {
3700 fetch.nTimeFirstAsked = GetTime();
3701 // Who is answerable for this hash existing. Set
3702 // once, on creation: the failover sweep asks other
3703 // peers later, and none of them chose to announce
3704 // it, so none of them should be charged for it.
3705 fetch.addrAnnounced = pto->addr;
3706 }
3707 fetch.nTimeAsked = GetTime();
3708 fetch.setAsked.insert(pto->addr);
3709 }
3710 }
3711 }
3358 pto->mapAskFor.erase(pto->mapAskFor.begin()); 3712 pto->mapAskFor.erase(pto->mapAskFor.begin());
3359 } 3713 }
3360 if (!vAskFor.empty()) 3714 if (!vAskFor.empty())
3361 pto->PushMessage("getdata", vAskFor); 3715 pto->PushMessage("getdata", vAskFor);
3362 3716
3717
3718 //
3719 // Message: getdata (block fetch failover)
3720 //
3721 // A requested block that has not arrived in BLOCK_FETCH_RETRY_SECS is
3722 // not still in transit -- a body is at most 1 MB and somebody
3723 // demonstrably has it -- so ask somebody else instead of waiting on
3724 // whoever was asked first (#33). Each peer the message handler visits
3725 // takes at most one turn per block per round; once every connected
3726 // peer has had its turn and a further interval passes with nobody
3727 // new to ask, the round restarts. A peer that declared itself a
3728 // client cannot serve block bodies and is no use as a target.
3729 if (!pto->fClient)
3730 {
3731 vector<CInv> vFailover;
3732 int64 nNowSecs = GetTime();
3733 for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end();)
3734 {
3735 CBlockFetch& fetch = (*mi).second;
3736 CInv inv(MSG_BLOCK, (*mi).first);
3737 if (AlreadyHave(txdb, inv))
3738 {
3739 // Arrived by some other road -- our own miner, an orphan
3740 // parked in mapOrphanBlocks. Nothing left to chase.
3741 mapBlockFetch.erase(mi++);
3742 continue;
3743 }
3744 if (nNowSecs - fetch.nTimeFirstAsked > BLOCK_FETCH_GIVEUP_SECS)
3745 {
3746 printf("block fetch: giving up on %s -- no peer served it in %d s\n",
3747 inv.ToString().c_str(), (int)(nNowSecs - fetch.nTimeFirstAsked));
3748 mapBlockFetch.erase(mi++);
3749 continue;
3750 }
3751 if (nNowSecs - fetch.nTimeAsked >= BLOCK_FETCH_RETRY_SECS)
3752 {
3753 // The handler loop visits every peer within ~100ms, so a
3754 // request stale by two full intervals means nobody outside
3755 // setAsked was there to ask. Start the round over.
3756 if (nNowSecs - fetch.nTimeAsked >= 2 * BLOCK_FETCH_RETRY_SECS)
3757 fetch.setAsked.clear();
3758 if (!fetch.setAsked.count(pto->addr))
3759 {
3760 // Which block, how long, and which peer we are trying
3761 // next. Behind the category because it names a peer
3762 // and a peer decides how often it is written.
3763 if (LogAcceptsCategory("net"))
3764 printf("block fetch failover: %s unanswered for %d s, re-requesting from %s\n",
3765 inv.ToString().c_str(), (int)(nNowSecs - fetch.nTimeAsked),
3766 pto->addr.ToString().c_str());
3767
3768 // The unconditional half: no addresses, and at most
3769 // one line per BLOCK_FETCH_REPORT_SECS however hard a
3770 // peer tries. See the counters for the reasoning.
3771 nBlockFetchFailovers++;
3772 if (nNowSecs - nBlockFetchWindowStart >= BLOCK_FETCH_REPORT_SECS)
3773 {
3774 printf("block fetch: %d block request%s went unanswered and %s been "
3775 "re-asked of other peers (%d tracked)\n",
3776 nBlockFetchFailovers, nBlockFetchFailovers == 1 ? "" : "s",
3777 nBlockFetchFailovers == 1 ? "has" : "have",
3778 (int)mapBlockFetch.size());
3779 nBlockFetchWindowStart = nNowSecs;
3780 nBlockFetchFailovers = 0;
3781 }
3782
3783 vFailover.push_back(inv);
3784 fetch.nTimeAsked = nNowSecs;
3785 fetch.setAsked.insert(pto->addr);
3786 }
3787 }
3788 ++mi;
3789 }
3790 if (!vFailover.empty())
3791 pto->PushMessage("getdata", vFailover);
3792 }
3793
3363 } 3794 }
3364 return true; 3795 return true;
3365} 3796}
@@ -3620,6 +4051,14 @@ static void ParticipantHashWorker(ParticipantJobShared* sh, int idx, int nWorker
3620 RandomXDestroyMinerVM(vm); 4051 RandomXDestroyMinerVM(vm);
3621} 4052}
3622 4053
4054// Run one participant session: resolve the pool, open the tunnel, subscribe,
4055// hash, submit shares. Returns true only if the pool actually handed out work
4056// -- at least one well-formed mining.notify arrived. Everything else is false,
4057// including a tunnel that opened and was dropped before any job.
4058//
4059// The caller's reconnect backoff keys off this return value, so any path added
4060// here that leaves the session without work MUST return false. "The session
4061// ended tidily" is not the question being asked.
3623static bool PoolParticipantMiner() 4062static bool PoolParticipantMiner()
3624{ 4063{
3625 // ------------------------------------------------------------------ 4064 // ------------------------------------------------------------------
@@ -3697,6 +4136,33 @@ static bool PoolParticipantMiner()
3697 } 4136 }
3698 LogPrint("worker", "[worker] pool resolved to meeting node %s\n", meetingHostPort.c_str()); 4137 LogPrint("worker", "[worker] pool resolved to meeting node %s\n", meetingHostPort.c_str());
3699 4138
4139 // The descriptor names ONE meeting node, but the pool parks a
4140 // registration at every relay it knows (one accept thread per relay,
4141 // rpc.cpp:AcceptOneFn), so any seed relay can pair us. Insisting on the
4142 // advertised entry forever turns one bad path into a miner that idles
4143 // indefinitely while a working path exists (issue #17); after a few
4144 // consecutive tunnel failures, rotate the seed relays into the attempts
4145 // too. Only ONE participant session runs (StartMinerThreads), so these
4146 // statics are single-threaded.
4147 static const int TUNNEL_FAILOVER_AFTER = 3;
4148 static int nTunnelFails = 0; // consecutive tunnel failures, this pool
4149 static size_t iFailoverRelay = 0; // rotation cursor over vBtfMeetingRelays
4150 static std::string strFailPool; // pool the counters belong to
4151 if (strFailPool != strParticipantPool) {
4152 strFailPool = strParticipantPool;
4153 nTunnelFails = 0;
4154 iFailoverRelay = 0;
4155 }
4156 if (nTunnelFails >= TUNNEL_FAILOVER_AFTER && !vBtfMeetingRelays.empty()) {
4157 std::string alt = vBtfMeetingRelays[iFailoverRelay++ % vBtfMeetingRelays.size()];
4158 if (alt != meetingHostPort) {
4159 LogPrint("worker", "[worker] advertised meeting node %s failed %d"
4160 " times in a row -- trying seed relay %s instead\n",
4161 meetingHostPort.c_str(), nTunnelFails, alt.c_str());
4162 meetingHostPort = alt;
4163 }
4164 }
4165
3700 size_t colon = meetingHostPort.rfind(':'); 4166 size_t colon = meetingHostPort.rfind(':');
3701 if (colon == std::string::npos) { 4167 if (colon == std::string::npos) {
3702 LogPrint("worker", "[worker] bad meeting node format '%s'\n", meetingHostPort.c_str()); 4168 LogPrint("worker", "[worker] bad meeting node format '%s'\n", meetingHostPort.c_str());
@@ -3716,15 +4182,18 @@ static bool PoolParticipantMiner()
3716 // Step 2b: open encrypted tunnel to pool 4182 // Step 2b: open encrypted tunnel to pool
3717 // ------------------------------------------------------------------ 4183 // ------------------------------------------------------------------
3718 SetParticipantMiningStatus("opening tunnel"); 4184 SetParticipantMiningStatus("opening tunnel");
4185 btf::TunnelFail tunnelFail = btf::TUNNEL_FAIL_NONE;
3719 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, 4186 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
3720 target_pubkey, service_enc_pub); 4187 target_pubkey, service_enc_pub, &tunnelFail);
3721 if (s == INVALID_SOCKET) { 4188 if (s == INVALID_SOCKET) {
3722 LogPrint("worker", "[worker] tunnel connect FAILED via %s" 4189 nTunnelFails++;
3723 " (relay unreachable or pool not listening)\n", 4190 LogPrint("worker", "[worker] tunnel connect FAILED via %s: %s\n",
3724 meetingHostPort.c_str()); 4191 meetingHostPort.c_str(), btf::TunnelFailText(tunnelFail));
3725 SetParticipantMiningStatus("tunnel connect failed"); 4192 SetParticipantMiningStatus(std::string("tunnel connect failed: ")
4193 + btf::TunnelFailText(tunnelFail));
3726 return false; 4194 return false;
3727 } 4195 }
4196 nTunnelFails = 0;
3728 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); 4197 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str());
3729 SetParticipantMiningStatus("tunnel open"); 4198 SetParticipantMiningStatus("tunnel open");
3730 } 4199 }
@@ -3857,9 +4326,32 @@ connected:
3857 std::vector<ParticipantShareHit> shareHits; 4326 std::vector<ParticipantShareHit> shareHits;
3858 std::mutex shareHitsMtx; 4327 std::mutex shareHitsMtx;
3859 std::vector<std::thread> hashThreads; 4328 std::vector<std::thread> hashThreads;
3860 for (int i = 0; i < nWorkers; i++) 4329 try
3861 hashThreads.emplace_back(ParticipantHashWorker, &jobShared, i, nWorkers, 4330 {
3862 &shareHits, &shareHitsMtx); 4331 for (int i = 0; i < nWorkers; i++)
4332 hashThreads.emplace_back(ParticipantHashWorker, &jobShared, i, nWorkers,
4333 &shareHits, &shareHitsMtx);
4334 }
4335 catch (const std::system_error& e)
4336 {
4337 // A refused pthread_create used to reach std::terminate here and
4338 // kill the worker mid-session -- observed twice on an otherwise
4339 // healthy participant. Stop the workers that did start the same way
4340 // the disconnect path below does, drop this pool connection, and let
4341 // the caller's reconnect loop try again once the pressure clears.
4342 LogPrint("worker", "[worker] cannot spawn hash threads (%s)"
4343 " -- dropping pool connection\n", e.what());
4344 SetParticipantMiningStatus("hash thread spawn failed");
4345 {
4346 std::lock_guard<std::mutex> lk(jobShared.mtx);
4347 jobShared.stop = true;
4348 jobShared.haveJob = false;
4349 }
4350 for (std::thread& t : hashThreads)
4351 t.join();
4352 closesocket(s);
4353 return false;
4354 }
3863 LogPrint("worker", "[worker] spawned %d hash threads on shared RandomX dataset\n", 4355 LogPrint("worker", "[worker] spawned %d hash threads on shared RandomX dataset\n",
3864 nWorkers); 4356 nWorkers);
3865 const uint64 sessionStartHashes = gParticipantHashes.load(); 4357 const uint64 sessionStartHashes = gParticipantHashes.load();
@@ -3870,8 +4362,17 @@ connected:
3870 std::string jobId; 4362 std::string jobId;
3871 unsigned char header[80] = {}; 4363 unsigned char header[80] = {};
3872 uint256 target; 4364 uint256 target;
4365 // Cached answer to "which block is this job built on", for the
4366 // mining.notify handler below -- see the comment there. Session-local, so
4367 // a reconnect re-resolves rather than trusting an answer obtained before
4368 // a possible change of pool.
4369 uint256 hashPrevResolved; // prevhash the cached answer describes
4370 bool fPrevResolved = false; // whether that answer exists yet
4371 int nPrevHeight = -1; // its height, or -1 == unknown to us
4372 int nBestHeightResolved = -1; // nBestHeight when we last resolved
3873 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool 4373 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool
3874 bool haveJob = false; 4374 bool haveJob = false;
4375 bool gotAnyJob = false; // any job at all this session -- the return value
3875 bool gotSubscribeAck = false; 4376 bool gotSubscribeAck = false;
3876 bool gotAuthorizeAck = false; 4377 bool gotAuthorizeAck = false;
3877 int64 authWaitStart = 0; 4378 int64 authWaitStart = 0;
@@ -3988,8 +4489,9 @@ connected:
3988 jobId = newJobId; 4489 jobId = newJobId;
3989 memcpy(header, hdrBytes.data(), 80); 4490 memcpy(header, hdrBytes.data(), 80);
3990 memcpy(&target, tgtBytes.data(), 32); 4491 memcpy(&target, tgtBytes.data(), 32);
3991 haveJob = true; 4492 haveJob = true;
3992 nNonce = 1; 4493 gotAnyJob = true;
4494 nNonce = 1;
3993 { 4495 {
3994 std::lock_guard<std::mutex> lk(jobShared.mtx); 4496 std::lock_guard<std::mutex> lk(jobShared.mtx);
3995 memcpy(jobShared.header, header, 80); 4497 memcpy(jobShared.header, header, 80);
@@ -3998,8 +4500,64 @@ connected:
3998 jobShared.jobSeq++; 4500 jobShared.jobSeq++;
3999 jobShared.haveJob = true; 4501 jobShared.haveJob = true;
4000 } 4502 }
4001 LogPrint("worker", "[pool->worker] new job %s received\n", 4503 // Log the chain position the pool is
4002 jobId.c_str()); 4504 // building on, so a stale or private
4505 // fork is auditable from worker logs.
4506 // The prevhash comes from the header we
4507 // actually hash (== p[2] of the notify);
4508 // a brief cs_main lookup is safe here --
4509 // this thread holds no other lock.
4510 //
4511 // Resolving it is the expensive half, and
4512 // this is the socket-reading thread: block
4513 // validation can hold cs_main for a long
4514 // time, and every moment spent waiting on
4515 // it here is a moment set_difficulty and
4516 // share responses are not being handled.
4517 // Most notifies in a round are a new job
4518 // on the SAME tip, so the answer is cached
4519 // and re-resolved only when it could come
4520 // out differently: a prevhash we have not
4521 // resolved, or one we could NOT resolve
4522 // last time while the chain has since
4523 // moved (unknown -> known). A block's
4524 // height is fixed once mapBlockIndex knows
4525 // it, so a cached "known" never goes
4526 // stale. nBestHeight is read unlocked on
4527 // purpose -- it only decides whether to
4528 // repeat a lookup, never what gets
4529 // printed, so a stale read costs at most
4530 // one deferred or one extra lookup.
4531 // The line still prints in full for every
4532 // job, cached or not.
4533 uint256 hashPrev;
4534 memcpy(&hashPrev, header + 4, 32);
4535 if (LogAcceptsCategory("worker"))
4536 {
4537 int nBestHeightNow = nBestHeight;
4538 if (!fPrevResolved
4539 || hashPrev != hashPrevResolved
4540 || (nPrevHeight < 0 && nBestHeightNow != nBestHeightResolved))
4541 {
4542 nPrevHeight = -1;
4543 CRITICAL_BLOCK(cs_main)
4544 {
4545 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrev);
4546 if (mi != mapBlockIndex.end())
4547 nPrevHeight = (*mi).second->nHeight;
4548 }
4549 hashPrevResolved = hashPrev;
4550 nBestHeightResolved = nBestHeightNow;
4551 fPrevResolved = true;
4552 }
4553 if (nPrevHeight >= 0)
4554 LogPrint("worker", "[pool->worker] new job %s received, prevhash %s (known, job height %d)\n",
4555 jobId.c_str(), hashPrev.GetHex().c_str(),
4556 nPrevHeight + 1);
4557 else
4558 LogPrint("worker", "[pool->worker] new job %s received, prevhash %s (unknown to our chain)\n",
4559 jobId.c_str(), hashPrev.GetHex().c_str());
4560 }
4003 SetParticipantMiningStatus("hashing job " + jobId); 4561 SetParticipantMiningStatus("hashing job " + jobId);
4004 } 4562 }
4005 } 4563 }
@@ -4125,9 +4683,197 @@ connected:
4125 (unsigned long long)hashesSinceSample, 4683 (unsigned long long)hashesSinceSample,
4126 (unsigned long long)sessionSharesSent, 4684 (unsigned long long)sessionSharesSent,
4127 (unsigned long long)sessionSharesAccepted); 4685 (unsigned long long)sessionSharesAccepted);
4686 // Not "we got here", which is true of a tunnel the pool accepted and then
4687 // dropped without ever sending a job -- exactly the case the caller's
4688 // backoff exists for. Reaching the end of the session is not evidence the
4689 // session was worth anything; a job is.
4690 return gotAnyJob;
4691}
4692
4693// ---------------------------------------------------------------------------
4694// Local Stratum bridge -- lets external miners speak plain Stratum to
4695// 127.0.0.1 while Bitflash opens the .btf tunnel to the selected pool.
4696// ---------------------------------------------------------------------------
4697
4698struct StratumBridgeClientCtx
4699{
4700 SOCKET miner;
4701 std::string pool;
4702};
4703
4704static bool BridgeSendAll(SOCKET s, const char* p, int n)
4705{
4706 int off = 0;
4707 while (off < n)
4708 {
4709 int r = send(s, p + off, n - off, 0);
4710 if (r <= 0)
4711 return false;
4712 off += r;
4713 }
4128 return true; 4714 return true;
4129} 4715}
4130 4716
4717static void BridgePump(SOCKET a, SOCKET b)
4718{
4719 char buf[8192];
4720 while (!fShutdown)
4721 {
4722 fd_set rd;
4723 FD_ZERO(&rd);
4724 FD_SET(a, &rd);
4725 FD_SET(b, &rd);
4726 SOCKET maxfd = a > b ? a : b;
4727 struct timeval tv = {1, 0};
4728 int r = select((int)maxfd + 1, &rd, NULL, NULL, &tv);
4729 if (r <= 0)
4730 continue;
4731
4732 if (FD_ISSET(a, &rd))
4733 {
4734 int n = recv(a, buf, sizeof(buf), 0);
4735 if (n <= 0 || !BridgeSendAll(b, buf, n))
4736 break;
4737 }
4738 if (FD_ISSET(b, &rd))
4739 {
4740 int n = recv(b, buf, sizeof(buf), 0);
4741 if (n <= 0 || !BridgeSendAll(a, buf, n))
4742 break;
4743 }
4744 }
4745}
4746
4747static void ThreadStratumBridgeClient(void* arg)
4748{
4749 StratumBridgeClientCtx* ctx = (StratumBridgeClientCtx*)arg;
4750 SOCKET miner = ctx->miner;
4751 std::string pool = ctx->pool;
4752 delete ctx;
4753
4754 unsigned char target_pubkey[32];
4755 if (!btf::ParseAddress(pool, target_pubkey))
4756 {
4757 LogPrint("worker", "[bridge] invalid pool .btf address: %s\n", pool.c_str());
4758 BtfCloseSocket(miner);
4759 return;
4760 }
4761
4762 std::string meetingHostPort;
4763 unsigned char service_enc_pub[32];
4764 if (!BtfResolve(pool, meetingHostPort, service_enc_pub))
4765 {
4766 LogPrint("worker", "[bridge] failed to resolve pool %s\n", pool.c_str());
4767 BtfCloseSocket(miner);
4768 return;
4769 }
4770
4771 size_t colon = meetingHostPort.rfind(':');
4772 if (colon == std::string::npos)
4773 {
4774 LogPrint("worker", "[bridge] bad pool endpoint '%s'\n", meetingHostPort.c_str());
4775 BtfCloseSocket(miner);
4776 return;
4777 }
4778 std::string host = meetingHostPort.substr(0, colon);
4779 int port = atoi(meetingHostPort.substr(colon + 1).c_str());
4780 if (port <= 0 || port > 65535)
4781 {
4782 LogPrint("worker", "[bridge] bad pool port in '%s'\n", meetingHostPort.c_str());
4783 BtfCloseSocket(miner);
4784 return;
4785 }
4786
4787 SOCKET poolSock = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
4788 target_pubkey, service_enc_pub);
4789 if (poolSock == INVALID_SOCKET)
4790 {
4791 LogPrint("worker", "[bridge] tunnel connect failed for pool %s via %s\n",
4792 pool.c_str(), meetingHostPort.c_str());
4793 BtfCloseSocket(miner);
4794 return;
4795 }
4796
4797 LogPrint("worker", "[bridge] miner connected to pool %s via %s\n",
4798 pool.c_str(), meetingHostPort.c_str());
4799 BridgePump(miner, poolSock);
4800 BtfCloseSocket(poolSock);
4801 BtfCloseSocket(miner);
4802 LogPrint("worker", "[bridge] miner disconnected from pool %s\n", pool.c_str());
4803}
4804
4805void ThreadStratumBridge(void*)
4806{
4807 if (strParticipantPool.empty())
4808 {
4809 printf("Stratum bridge: no pool selected. Use /stratumbridge=POOL_BTF_ADDRESS\n");
4810 return;
4811 }
4812
4813 SOCKET listener = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP),
4814 SOCK_STRATUM_BRIDGE);
4815 if (listener == INVALID_SOCKET)
4816 {
4817 printf("Stratum bridge: could not create listen socket\n");
4818 return;
4819 }
4820
4821 int yes = 1;
4822 setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
4823
4824 sockaddr_in addr;
4825 memset(&addr, 0, sizeof(addr));
4826 addr.sin_family = AF_INET;
4827 addr.sin_addr.s_addr = htonl(0x7f000001);
4828 addr.sin_port = htons((unsigned short)nStratumBridgePort);
4829
4830 if (bind(listener, (sockaddr*)&addr, sizeof(addr)) != 0 ||
4831 listen(listener, 16) != 0)
4832 {
4833 printf("Stratum bridge: could not listen on 127.0.0.1:%d\n",
4834 nStratumBridgePort);
4835 BtfCloseSocket(listener);
4836 return;
4837 }
4838
4839 printf("Stratum bridge: listening on 127.0.0.1:%d for pool %s\n",
4840 nStratumBridgePort, strParticipantPool.c_str());
4841
4842 while (!fShutdown)
4843 {
4844 fd_set rd;
4845 FD_ZERO(&rd);
4846 FD_SET(listener, &rd);
4847 struct timeval tv = {1, 0};
4848 if (select((int)listener + 1, &rd, NULL, NULL, &tv) <= 0)
4849 continue;
4850
4851 sockaddr_in cli;
4852#ifdef _WIN32
4853 int len = sizeof(cli);
4854#else
4855 socklen_t len = sizeof(cli);
4856#endif
4857 SOCKET miner = BtfSocketTag(accept(listener, (sockaddr*)&cli, &len),
4858 SOCK_STRATUM_BRIDGE);
4859 if (miner == INVALID_SOCKET)
4860 continue;
4861
4862 StratumBridgeClientCtx* ctx = new StratumBridgeClientCtx();
4863 ctx->miner = miner;
4864 ctx->pool = strParticipantPool;
4865 if (_beginthread(ThreadStratumBridgeClient, 0, ctx) == (uintptr_t)-1)
4866 {
4867 delete ctx;
4868 BtfCloseSocket(miner);
4869 LogPrint("worker", "[bridge] failed to spawn client thread\n");
4870 }
4871 }
4872
4873 BtfCloseSocket(listener);
4874 printf("Stratum bridge: stopped\n");
4875}
4876
4131bool BitcoinMiner(int nThreadId) 4877bool BitcoinMiner(int nThreadId)
4132{ 4878{
4133 // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here 4879 // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here
@@ -4138,11 +4884,51 @@ bool BitcoinMiner(int nThreadId)
4138 // Participant mode: connect to external pool instead of mining solo/operator 4884 // Participant mode: connect to external pool instead of mining solo/operator
4139 if (nMineMode == MINE_PARTICIPANT) 4885 if (nMineMode == MINE_PARTICIPANT)
4140 { 4886 {
4887 // An attempt that never got work backs off (10s doubling to 120s)
4888 // instead of hammering the same path every flat 10s, and a streak
4889 // lasting minutes is said at printf level: the identical once-per-10s
4890 // LogPrint line is how a worker idled all night unnoticed (issue #17).
4891 //
4892 // Only a session the pool actually sent a job to resets either. The
4893 // weaker test -- "the session returned" -- was gameable: a pool that
4894 // completes the tunnel and drops without ever sending work leaves by
4895 // the same tidy cleanup path a healthy one does, which would pin the
4896 // retry at 10s forever and defeat the backoff in exactly the scenario
4897 // it exists for.
4898 int nRetrySecs = 10;
4899 int64 nFailingSince = 0;
4141 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4900 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4142 PoolParticipantMiner(); 4901 bool fGotWork = PoolParticipantMiner();
4143 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4902 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4144 LogPrint("worker", "PoolParticipantMiner: reconnecting in 10s\n"); 4903 if (fGotWork) {
4145 for (int i = 0; i < 10 && !fShutdown; i++) Sleep(1000); 4904 nRetrySecs = 10;
4905 nFailingSince = 0;
4906 } else {
4907 if (nFailingSince == 0)
4908 nFailingSince = GetTime();
4909 int64 nMins = (GetTime() - nFailingSince) / 60;
4910 if (nMins >= 1)
4911 printf("[worker] connection to pool %s has been failing"
4912 " for %lld minutes\n",
4913 strParticipantPool.c_str(), (long long)nMins);
4914 }
4915 LogPrint("worker", "PoolParticipantMiner: reconnecting in %ds\n",
4916 nRetrySecs);
4917 // Poll everything the enclosing while polls, not just
4918 // fShutdown. Raising the ceiling to 120s made a
4919 // shutdown-only predicate expensive: after the operator
4920 // clears Generate or switches to Relay, the session -- and
4921 // the vfThreadRunning[3] slot that gates restarting it --
4922 // would stay held for up to two minutes. Measured 119s
4923 // before, 1s after.
4924 for (int i = 0; i < nRetrySecs && !fShutdown && fGenerateBitcoins
4925 && nMineMode == MINE_PARTICIPANT; i++)
4926 Sleep(1000);
4927 if (!fGotWork && nRetrySecs < 120) {
4928 nRetrySecs *= 2;
4929 if (nRetrySecs > 120)
4930 nRetrySecs = 120;
4931 }
4146 } 4932 }
4147 } 4933 }
4148 return true; 4934 return true;
@@ -4244,7 +5030,6 @@ bool BitcoinMiner(int nThreadId)
4244 map<uint256, CTxIndex> mapTestPool; 5030 map<uint256, CTxIndex> mapTestPool;
4245 vector<char> vfAlreadyAdded(mapTransactions.size()); 5031 vector<char> vfAlreadyAdded(mapTransactions.size());
4246 bool fFoundSomething = true; 5032 bool fFoundSomething = true;
4247 unsigned int nBlockSize = 0;
4248 // Assembly must stop below the consensus cap that CheckBlock() 5033 // Assembly must stop below the consensus cap that CheckBlock()
4249 // enforces, or we mine a block our own node then rejects. 5034 // enforces, or we mine a block our own node then rejects.
4250 // 5035 //
@@ -4255,9 +5040,30 @@ bool BitcoinMiner(int nThreadId)
4255 // meant solving a block and throwing it away -- with this pool at 5040 // meant solving a block and throwing it away -- with this pool at
4256 // ~90% of hashrate, that is the network stalling, not just us. 5041 // ~90% of hashrate, that is the network stalling, not just us.
4257 // 5042 //
4258 // Half the cap, keeping the original margin: the test runs before 5043 // The budget test then sat only on this outer while, and the
4259 // a transaction is added, so the loop can overshoot by one tx. 5044 // comment claimed that bounded the overshoot to a single
4260 while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE/2) 5045 // transaction. It did not. The inner for walks all of
5046 // mapTransactions with no test of its own, so one pass could append
5047 // the entire mempool: the overshoot was bounded by mempool size,
5048 // which is to say not bounded at all. Ask per transaction, before
5049 // appending, and the overshoot is zero rather than small.
5050 //
5051 // Seeded with what the block already weighs -- header, tx count,
5052 // coinbase -- because CheckBlock() weighs the whole serialized
5053 // block and not just the transactions we chose to add to it.
5054 unsigned int nBlockSize = ::GetSerializeSize(*pblock, SER_DISK);
5055 // Sigops are a budget too, and CheckBlock() enforces one that no
5056 // assembler here counted against. Bytes do not bound validation
5057 // cost: every OP_CHECKSIG is an elliptic-curve verification, so a
5058 // template well inside the size limit can still be refused for
5059 // demanding more work than a block is allowed to. Counted with
5060 // CTransaction::GetSigOpCount, the function CheckBlock() itself
5061 // sums, so the two cannot disagree about what a transaction costs.
5062 //
5063 // Seeded from the coinbase because CheckBlock() sums over all of
5064 // vtx, coinbase included.
5065 unsigned int nBlockSigOps = pblock->vtx[0].GetSigOpCount();
5066 while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE_GEN)
4261 { 5067 {
4262 fFoundSomething = false; 5068 fFoundSomething = false;
4263 unsigned int n = 0; 5069 unsigned int n = 0;
@@ -4269,6 +5075,29 @@ bool BitcoinMiner(int nThreadId)
4269 if (tx.IsCoinBase() || !tx.IsFinal()) 5075 if (tx.IsCoinBase() || !tx.IsFinal())
4270 continue; 5076 continue;
4271 5077
5078 // Skip, not break: a transaction too big for the room left
5079 // says nothing about the next one, which may still fit.
5080 //
5081 // This is also the only ceiling on a single transaction's
5082 // size anywhere on the path into a block. CheckTransaction
5083 // does not cap it and neither does AcceptTransaction, so
5084 // the mempool will hold one of any size the wire allows;
5085 // without this test a lone 1.5 MB transaction offering a
5086 // fat fee is selected, and every template built from that
5087 // mempool is one CheckBlock() throws away. The fee is never
5088 // paid, because the block never confirms.
5089 //
5090 // Deliberately before ConnectInputs and not after:
5091 // ConnectInputs adds this transaction's fee to nFees by
5092 // reference on success. Passing it and then declining the
5093 // transaction would credit the coinbase for a fee no
5094 // transaction in the block pays, and the block would be
5095 // rejected for a wholly different reason.
5096 unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
5097 unsigned int nTxSigOps = tx.GetSigOpCount();
5098 if (!BlockTemplateFits(nBlockSize, nBlockSigOps, nTxSize, nTxSigOps))
5099 continue;
5100
4272 // Transaction fee requirements, mainly only needed for flood control 5101 // Transaction fee requirements, mainly only needed for flood control
4273 // Under 10K (about 80 inputs) is free for first 100 transactions 5102 // Under 10K (about 80 inputs) is free for first 100 transactions
4274 // Base rate is 0.01 per KB 5103 // Base rate is 0.01 per KB
@@ -4280,7 +5109,8 @@ bool BitcoinMiner(int nThreadId)
4280 swap(mapTestPool, mapTestPoolTmp); 5109 swap(mapTestPool, mapTestPoolTmp);
4281 5110
4282 pblock->vtx.push_back(tx); 5111 pblock->vtx.push_back(tx);
4283 nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); 5112 nBlockSize += nTxSize;
5113 nBlockSigOps += nTxSigOps;
4284 vfAlreadyAdded[n] = true; 5114 vfAlreadyAdded[n] = true;
4285 fFoundSomething = true; 5115 fFoundSomething = true;
4286 } 5116 }
@@ -4329,7 +5159,7 @@ bool BitcoinMiner(int nThreadId)
4329 //// debug print 5159 //// debug print
4330 if (LogAcceptsCategory("net")) printf("BitcoinMiner:\n"); 5160 if (LogAcceptsCategory("net")) printf("BitcoinMiner:\n");
4331 if (LogAcceptsCategory("net")) printf("RandomX proof-of-work found \n powhash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); 5161 if (LogAcceptsCategory("net")) printf("RandomX proof-of-work found \n powhash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
4332 if (LogAcceptsCategory("net")) pblock->print(); 5162 if (LogAcceptsCategory("relay")) pblock->print();
4333 5163
4334 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); 5164 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
4335 CRITICAL_BLOCK(cs_main) 5165 CRITICAL_BLOCK(cs_main)
@@ -4435,6 +5265,9 @@ WalletRecoveryAudit GetWalletRecoveryAudit()
4435 CRITICAL_BLOCK(cs_keyPool) 5265 CRITICAL_BLOCK(cs_keyPool)
4436 { 5266 {
4437 audit.fHaveSeed = HaveHDSeed(); 5267 audit.fHaveSeed = HaveHDSeed();
5268 audit.fSeedEncryptedLocked = !audit.fHaveSeed &&
5269 (!vchCryptedHDMaster.empty() ||
5270 !vchCryptedHDChainCode.empty());
4438 audit.nSchema = nHDKeySchema; 5271 audit.nSchema = nHDKeySchema;
4439 audit.nDerivedKnown = nHDNext; 5272 audit.nDerivedKnown = nHDNext;
4440 audit.nReceiveNext = nHDReceiveNext; 5273 audit.nReceiveNext = nHDReceiveNext;
@@ -4681,9 +5514,17 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
4681 5514
4682 5515
4683 5516
4684bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, int64& nFeeRequiredRet) 5517// presultRet, when given, says WHY this returned false: the caller cannot work
5518// it out afterwards. "Coin selection came up short" and "the wallet could not
5519// build the transaction" are indistinguishable from the outside -- both are
5520// just `false` -- and the difference decides whether a pool defers a payment
5521// or counts it against the payment's retry budget. See WalletSendResult.
5522bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, int64& nFeeRequiredRet,
5523 WalletSendResult* presultRet)
4685{ 5524{
4686 nFeeRequiredRet = 0; 5525 nFeeRequiredRet = 0;
5526 if (presultRet)
5527 *presultRet = WALLET_SEND_FAILED;
4687 CRITICAL_BLOCK(cs_main) 5528 CRITICAL_BLOCK(cs_main)
4688 { 5529 {
4689 // txdb must be opened before the mapWallet lock 5530 // txdb must be opened before the mapWallet lock
@@ -4714,10 +5555,21 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
4714 // fixed this in 107d9e288. 5555 // fixed this in 107d9e288.
4715 int64 nTotalValue = nValue + nFee; 5556 int64 nTotalValue = nValue + nFee;
4716 5557
4717 // Choose coins to use 5558 // Choose coins to use.
5559 //
5560 // Failing here is a funding shortfall and nothing else: there
5561 // is not enough spendable coin for nValue plus the fee this
5562 // pass is working with. On the second and later passes that
5563 // fee is GetMinFee() of the transaction the first pass built,
5564 // so this is also how a wallet that can afford the amount but
5565 // not the 11 * CENT charged at 10,000 bytes reports itself.
4718 set<CWalletTx*> setCoins; 5566 set<CWalletTx*> setCoins;
4719 if (!SelectCoins(nTotalValue, setCoins)) 5567 if (!SelectCoins(nTotalValue, setCoins))
5568 {
5569 if (presultRet)
5570 *presultRet = WALLET_SEND_INSUFFICIENT_FUNDS;
4720 return false; 5571 return false;
5572 }
4721 int64 nValueIn = 0; 5573 int64 nValueIn = 0;
4722 foreach(CWalletTx* pcoin, setCoins) 5574 foreach(CWalletTx* pcoin, setCoins)
4723 nValueIn += pcoin->GetCredit(); 5575 nValueIn += pcoin->GetCredit();
@@ -4787,6 +5639,8 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
4787 } 5639 }
4788 } 5640 }
4789 } 5641 }
5642 if (presultRet)
5643 *presultRet = WALLET_SEND_OK;
4790 return true; 5644 return true;
4791} 5645}
4792 5646
@@ -4849,13 +5703,24 @@ bool CommitTransactionSpent(const CWalletTx& wtxNew)
4849 5703
4850 5704
4851 5705
4852bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew) 5706// outcomeRet reports what happened rather than only whether it worked. A
5707// caller that has to decide what a failure MEANS -- the pool payout thread --
5708// cannot reconstruct it from a balance reading afterwards, because the fee
5709// that was actually required depends on the size of a transaction that was
5710// never built. See WalletSendResult.
5711bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
5712 SendMoneyOutcome& outcomeRet)
4853{ 5713{
5714 outcomeRet.result = WALLET_SEND_FAILED;
5715 outcomeRet.nFeeRequired = 0;
4854 CRITICAL_BLOCK(cs_main) 5716 CRITICAL_BLOCK(cs_main)
4855 { 5717 {
4856 int64 nFeeRequired; 5718 int64 nFeeRequired;
4857 if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired)) 5719 WalletSendResult createResult = WALLET_SEND_FAILED;
5720 if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired, &createResult))
4858 { 5721 {
5722 outcomeRet.result = createResult;
5723 outcomeRet.nFeeRequired = nFeeRequired;
4859 string strError; 5724 string strError;
4860 if (nValue + nFeeRequired > GetBalance()) 5725 if (nValue + nFeeRequired > GetBalance())
4861 // Not an oversized transaction -- this branch is simply "the 5726 // Not an oversized transaction -- this branch is simply "the
@@ -4893,6 +5758,13 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
4893 } 5758 }
4894 wtxNew.RelayWalletTransaction(); 5759 wtxNew.RelayWalletTransaction();
4895 } 5760 }
5761 outcomeRet.result = WALLET_SEND_OK;
4896 MainFrameRepaint(); 5762 MainFrameRepaint();
4897 return true; 5763 return true;
4898} 5764}
5765
5766bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
5767{
5768 SendMoneyOutcome ignored;
5769 return SendMoney(scriptPubKey, nValue, wtxNew, ignored);
5770}