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-05 22:46:52 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 22:46:52 +0000
commitceae3d076b5ad11ee84729ae43f14e9312896646 (patch)
tree7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/main.cpp
parentbe63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff)
parent66e0b9158d050c46b1ff2509faeee3f4573cac1c (diff)
Bring main onto the line this project actually ships
main had drifted to a history with no ancestor in common with the tree we build and deploy. It sat at be63e83 (2026-08-09) with 28 commits; the shipped line, deploy/soak-2026-08-21, had 383. The two shared nothing -- git merge-base returns no result at all -- so anyone cloning this repository landed on a default branch that was neither current nor related to the running node. The 28 commits on main were audited before this merge: every one is already present in the tree by content, including the two that are hardest to see because they were rewritten afterwards -- the payout reorg-safety work (PayoutBlockStillOnChain, since evolved to re-check on every retry rather than once at maturity) and the nostr relay certificate verification (SSL_set1_host, now with a Windows root-store path). Nothing on main is lost by this merge. Recorded as a merge rather than a force-push: main only ever moves forward, its old history stays reachable as the first parent, and this is revertible. The tree is taken wholesale from the merged branch, so main's content is now exactly what was built and tested. What this brings onto main: - compactblock: BIP152-style compact block relay ported from upstream 1.2.23, primitives and unit test only, with the RAND_bytes failure path closed and the reconstruction allocation bounded. - upstream/main 17741d9: isolated testnet parameters and genesis, optional 2 MB large pages for RandomX. Mainnet genesis verified byte-identical through upstream's BuildGenesisBlock() refactor. - deploy: every image built on bookworm so binaries run on Debian 12, and the container build now honours the RandomX and libsecp256k1 pins it had been silently ignoring since before 1.2.15. Gates at this tree: 15/15 selftests, compactblock's 17 checks, consensus constants unchanged against e4f898c apart from the pre-existing MAX_BLOCK_SIZE_GEN, mainnet genesis intact, money-log check ok.
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}