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:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp1177
1 files changed, 1033 insertions, 144 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 5d266ff..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"
@@ -82,6 +83,137 @@ CBlockIndex* pindexBest = NULL;
82map<uint256, CBlock*> mapOrphanBlocks; 83map<uint256, CBlock*> mapOrphanBlocks;
83multimap<uint256, CBlock*> mapOrphanBlocksByPrev; 84multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
84 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
85map<uint256, CDataStream*> mapOrphanTransactions; 217map<uint256, CDataStream*> mapOrphanTransactions;
86multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev; 218multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
87 219
@@ -105,6 +237,8 @@ CKey keyUser;
105string strSetDataDir; 237string strSetDataDir;
106int nDropMessagesTest = 0; 238int nDropMessagesTest = 0;
107bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local test) 239bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local test)
240bool fPoolNoAnnounce = false; // /noannounce: operator serves miners but never publishes pool announcements
241bool fPoolMineBlind = false; // /poolmineblind: issue jobs even when no peer has reported a height
108 242
109// Settings 243// Settings
110int fGenerateBitcoins; 244int fGenerateBitcoins;
@@ -120,6 +254,9 @@ CAddress addrIncoming;
120int nMineMode = MINE_RELAY; 254int nMineMode = MINE_RELAY;
121bool fMineModeFromCommandLine = false; 255bool fMineModeFromCommandLine = false;
122string strParticipantPool; // participant mode: pool .btf address 256string strParticipantPool; // participant mode: pool .btf address
257string strPayoutAddress; // participant: /payoutaddr override (mine without a wallet)
258string strPayoutFile; // participant: /payoutfile with one address per line, rotated per reconnect
259int nHashThreads = 0; // participant: /hashthreads override (0 = cores-1)
123string strPoolName = "Bitflash Pool"; 260string strPoolName = "Bitflash Pool";
124string strPoolDashboardUrl; 261string strPoolDashboardUrl;
125string strPoolStatusFile; 262string strPoolStatusFile;
@@ -238,12 +375,32 @@ int MinerThreadCount()
238{ 375{
239 if (nMinerThreads > 0) 376 if (nMinerThreads > 0)
240 return nMinerThreads; 377 return nMinerThreads;
241 unsigned int nCores = std::thread::hardware_concurrency(); 378 unsigned int nCores = AvailableCoreCount();
242 if (nCores <= 1) 379 if (nCores <= 1)
243 return 1; 380 return 1;
244 return (int)(nCores - 1); 381 return (int)(nCores - 1);
245} 382}
246 383
384// Hash threads one participant session runs.
385//
386// Explicit /hashthreads=N is honored exactly, including 1 -- a miner who says
387// one means one. Automatic leaves a core to the node, the Stratum I/O loop and
388// everything else the process still has to do, for the same reason
389// MinerThreadCount() does.
390//
391// Lives here, next to that function, because the two are easy to confuse and
392// StartMinerThreads() needs to be able to tell them apart: only ONE session
393// runs in participant mode, and this is what sizes it.
394int ParticipantHashThreadCount()
395{
396 if (nHashThreads > 0)
397 return nHashThreads;
398 int nWorkers = (int)AvailableCoreCount();
399 if (nWorkers < 2)
400 nWorkers = 2;
401 return nWorkers - 1;
402}
403
247static void SecureClearBytes(vector<unsigned char>& vch); 404static void SecureClearBytes(vector<unsigned char>& vch);
248static void SecureClearKeyingMaterial(CKeyingMaterial& vch); 405static void SecureClearKeyingMaterial(CKeyingMaterial& vch);
249 406
@@ -1626,7 +1783,7 @@ int CMerkleTx::GetBlocksToMaturity() const
1626{ 1783{
1627 if (!IsCoinBase()) 1784 if (!IsCoinBase())
1628 return 0; 1785 return 0;
1629 return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); 1786 return max(0, COINBASE_SPENDABLE_DEPTH - GetDepthInMainChain());
1630} 1787}
1631 1788
1632 1789
@@ -1762,7 +1919,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
1762 uint256 hash = GetHash(); 1919 uint256 hash = GetHash();
1763 if (!txdb.ContainsTx(hash)) 1920 if (!txdb.ContainsTx(hash))
1764 { 1921 {
1765 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());
1766 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); 1923 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
1767 } 1924 }
1768 } 1925 }
@@ -2738,7 +2895,35 @@ bool LoadBlockIndex(bool fAllowNew)
2738 // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) 2895 // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
2739 // vMerkleTree: 4a5e1e 2896 // vMerkleTree: 4a5e1e
2740 2897
2741 // Bitflash genesis block -- own header (fair launch, 2026 stable relaunch) 2898 // Bitflash genesis block -- own header (fair launch)
2899 //
2900 // Upstream moved the construction into BuildGenesisBlock() so mainnet
2901 // and the isolated testnet can share it. The mainnet inputs it uses are
2902 // byte-identical to what this block built inline before the merge --
2903 // timestamp string, nTime, nNonce, scriptPubKey, nBits and nVersion all
2904 // verified equal against MAINNET_GENESIS_* -- so the mainnet genesis
2905 // hash is unchanged. The warning below is why that was checked rather
2906 // than assumed, and it still applies to those constants:
2907 //
2908 // Do not change MAINNET_GENESIS_TIMESTAMP, the scriptPubKey,
2909 // MAINNET_GENESIS_TIME or MAINNET_GENESIS_NONCE. They are the inputs to
2910 // the genesis hash, and the genesis hash is consensus. Changing one on
2911 // its own trips the assert below, which is loud and reverts cleanly;
2912 // changing one and regenerating GENESIS_NONCE and hashGenesisBlock to
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.
2742 CBlock block = BuildGenesisBlock(); 2927 CBlock block = BuildGenesisBlock();
2743 2928
2744 // 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)
@@ -2764,6 +2949,11 @@ bool LoadBlockIndex(bool fAllowNew)
2764 printf("powhash = %s\n", block.GetPoWHash().ToString().c_str()); 2949 printf("powhash = %s\n", block.GetPoWHash().ToString().c_str());
2765 printf("hash = %s\n", block.GetHash().ToString().c_str()); 2950 printf("hash = %s\n", block.GetHash().ToString().c_str());
2766 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");
2767 printf("========================================\n"); 2957 printf("========================================\n");
2768 } 2958 }
2769 2959
@@ -2890,9 +3080,10 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv)
2890 { 3080 {
2891 case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); 3081 case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash);
2892 case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); 3082 case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash);
2893 case MSG_REVIEW: return true;
2894 case MSG_PRODUCT: return mapProducts.count(inv.hash);
2895 } 3083 }
3084 // MSG_REVIEW / MSG_PRODUCT / MSG_TABLE are the 2009 marketplace inventory
3085 // types. Nothing announces or serves them any more (see ProcessMessage),
3086 // so they fall through to the default and are never requested.
2896 // Don't know what it is, just say we already got one 3087 // Don't know what it is, just say we already got one
2897 return true; 3088 return true;
2898} 3089}
@@ -2935,11 +3126,13 @@ bool ProcessMessages(CNode* pfrom)
2935 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", (int)(pstart - vRecv.begin())); 3126 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", (int)(pstart - vRecv.begin()));
2936 vRecv.erase(vRecv.begin(), pstart); 3127 vRecv.erase(vRecv.begin(), pstart);
2937 3128
2938 // Read the header from a copy first. The old path consumed the header, 3129 // Read the header from a copy first, and do not consume it until the
2939 // noticed the payload was incomplete, inserted the header back at the 3130 // whole body is here. The old path consumed the header, noticed the
2940 // front of the receive buffer, then slept inside the shared message 3131 // payload was incomplete, inserted the header back at the front of the
2941 // thread. A peer could announce a large payload and drip bytes forever, 3132 // receive buffer, then slept inside the shared message thread. A peer
2942 // making every pass pay for an O(n) insert and a 100 ms stall. 3133 // could announce a large payload and drip bytes forever, making every
3134 // pass pay for an O(n) insert and a 100 ms stall -- a cheap way for one
3135 // peer to slow the thread that serves all of them. (upstream #109)
2943 CMessageHeader hdr; 3136 CMessageHeader hdr;
2944 CDataStream vHeader(vRecv.begin(), vRecv.begin() + sizeof(CMessageHeader), 3137 CDataStream vHeader(vRecv.begin(), vRecv.begin() + sizeof(CMessageHeader),
2945 vRecv.nType, vRecv.nVersion); 3138 vRecv.nType, vRecv.nVersion);
@@ -2949,7 +3142,10 @@ bool ProcessMessages(CNode* pfrom)
2949 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); 3142 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str());
2950 if (hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_SIZE) 3143 if (hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_SIZE)
2951 pfrom->fDisconnect = true; 3144 pfrom->fDisconnect = true;
2952 pfrom->nIncompleteMessageStart = 0; 3145 pfrom->nMsgWaitSince = 0;
3146 pfrom->nMsgWaitSize = 0;
3147 // The header was read from a copy, so it is still in vRecv. Skip it
3148 // explicitly or this loop never advances.
2953 vRecv.ignore(sizeof(CMessageHeader)); 3149 vRecv.ignore(sizeof(CMessageHeader));
2954 continue; 3150 continue;
2955 } 3151 }
@@ -2960,30 +3156,63 @@ bool ProcessMessages(CNode* pfrom)
2960 unsigned int nPayloadAvailable = vRecv.size() - sizeof(CMessageHeader); 3156 unsigned int nPayloadAvailable = vRecv.size() - sizeof(CMessageHeader);
2961 if (nMessageSize > nPayloadAvailable) 3157 if (nMessageSize > nPayloadAvailable)
2962 { 3158 {
2963 if (pfrom->nIncompleteMessageStart == 0 || 3159 // Wait for the rest of the message. The header stays in vRecv.
2964 pfrom->nIncompleteMessageSize != nMessageSize || 3160 //
2965 pfrom->strIncompleteMessageCommand != strCommand) 3161 // This is the mechanism the original note asked for: "need a
3162 // mechanism to give up waiting for overlong message size error".
3163 // Without one the wait is unbounded, and unbounded here means
3164 // permanent. The header stays at the front of vRecv and the loop
3165 // breaks; if the body never completes, every later pass re-reads
3166 // that same header and breaks in the same place. The peer is never
3167 // parsed again -- no blocks, no inv, not even its version message,
3168 // so it never reports a height either, which is why a wedged node
3169 // has peers and GetPeerMedianHeight() still returns -1.
3170 //
3171 // Nothing rescues it. A header could claim up to 256 MB and still
3172 // pass CMessageHeader::IsValid() -- now capped at
3173 // MAX_PROTOCOL_MESSAGE_SIZE -- and any byte that arrives
3174 // refreshes nLastRecv, so the inactivity timeout that drops a
3175 // silent peer never fires on one that is merely unreadable. The
3176 // socket stays ESTABLISHED and select() keeps offering it.
3177 // Measured on a wedged pool: this branch ran 2014 times in the
3178 // deaf window, against 607 in the healthy window before it.
3179 if (LogAcceptsCategory("net")) printf("MESSAGE-BREAK 2\n");
3180 //
3181 // Time the STALL, not the wait. A big message on a slow link is
3182 // still arriving, and dropping that peer would be a bug of our own
3183 // making; what must be caught is a buffer that has stopped growing
3184 // while we go on expecting more. So the clock restarts whenever
3185 // even one more byte has landed since the last look.
3186 //
3187 // Upstream #109 times this differently: it restarts the clock only
3188 // when the (size, command) pair changes, which drops a peer sending
3189 // one big message slowly but honestly. Progress is the better test,
3190 // so that half is kept as it was.
3191 int64 nNow = GetTime();
3192 if (pfrom->nMsgWaitSince == 0 || nPayloadAvailable > pfrom->nMsgWaitSize)
2966 { 3193 {
2967 pfrom->nIncompleteMessageStart = GetTime(); 3194 pfrom->nMsgWaitSince = nNow;
2968 pfrom->nIncompleteMessageSize = nMessageSize; 3195 pfrom->nMsgWaitSize = nPayloadAvailable;
2969 pfrom->strIncompleteMessageCommand = strCommand;
2970 } 3196 }
2971 else if (GetTime() - pfrom->nIncompleteMessageStart > 3197 else if (nNow - pfrom->nMsgWaitSince > BTF_MSG_WAIT_MAX_SECS)
2972 BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS)
2973 { 3198 {
2974 LogPrint("net", 3199 // Deliberately not behind a debug category. This is the node
2975 "disconnecting %s: incomplete %s message, %u of %u bytes after %d seconds\n", 3200 // going deaf to a peer, which is the thing nobody could see.
2976 pfrom->addr.ToString().c_str(), strCommand.c_str(), 3201 printf("peer %s announced a %u byte '%s' and has sent nothing more "
2977 nPayloadAvailable, nMessageSize, 3202 "for %d s (%d of %u payload bytes) -- dropping, this peer is unreadable\n",
2978 BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS); 3203 pfrom->addr.ToString().c_str(), nMessageSize,
3204 strCommand.c_str(), (int)(nNow - pfrom->nMsgWaitSince),
3205 (int)nPayloadAvailable, nMessageSize);
2979 pfrom->fDisconnect = true; 3206 pfrom->fDisconnect = true;
2980 } 3207 }
3208 // No re-insert, no Sleep(): the header was never consumed, so there
3209 // is nothing to put back, and sleeping here stalls every peer.
2981 break; 3210 break;
2982 } 3211 }
2983 pfrom->nIncompleteMessageStart = 0;
2984 pfrom->nIncompleteMessageSize = 0;
2985 pfrom->strIncompleteMessageCommand.clear();
2986 vRecv.ignore(sizeof(CMessageHeader)); 3212 vRecv.ignore(sizeof(CMessageHeader));
3213 // The body arrived, so this peer is readable again.
3214 pfrom->nMsgWaitSince = 0;
3215 pfrom->nMsgWaitSize = 0;
2987 3216
2988 // Copy message to its own buffer 3217 // Copy message to its own buffer
2989 CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); 3218 CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);
@@ -3012,7 +3241,6 @@ bool ProcessMessages(CNode* pfrom)
3012 3241
3013bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 3242bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3014{ 3243{
3015 static map<unsigned int, vector<unsigned char> > mapReuseKey;
3016 if (LogAcceptsCategory("net")) { printf("received: %-12s (%d bytes) ", strCommand.c_str(), (int)vRecv.size()); for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) printf("%02x ", vRecv[i] & 0xff); printf("\n"); } 3244 if (LogAcceptsCategory("net")) { printf("received: %-12s (%d bytes) ", strCommand.c_str(), (int)vRecv.size()); for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) printf("%02x ", vRecv[i] & 0xff); printf("\n"); }
3017 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) 3245 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0)
3018 { 3246 {
@@ -3251,22 +3479,18 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3251 } 3479 }
3252 3480
3253 3481
3254 else if (strCommand == "review") 3482 // "review" used to be handled here. It took a CReview off the wire from an
3255 { 3483 // unauthenticated peer, checked a signature the sender had made with a key
3256 CDataStream vMsg(vRecv); 3484 // of its own choosing -- which proves possession of that key and authorises
3257 CReview review; 3485 // nothing -- and then appended the message to reviews.dat by reading the
3258 vRecv >> review; 3486 // whole vector for the target hash, pushing one entry, and writing all of
3259 3487 // it back. That is quadratic disk for a stream an attacker controls, on a
3260 CInv inv(MSG_REVIEW, review.GetHash()); 3488 // record whose mapValue is an unbounded map<string,string> read straight
3261 pfrom->AddInventoryKnown(inv); 3489 // from the socket, followed by AddAtomsAndPropagate walking a link graph
3262 3490 // the same peer supplied, and finally RelayMessage forwarding it to every
3263 if (review.AcceptReview()) 3491 // other node. Unbounded remote disk growth with network amplification and
3264 { 3492 // no authorisation anywhere in the path. The marketplace it belonged to has
3265 // Relay the original message as-is in case it's a higher version than we know how to parse 3493 // not existed since 2009; the handler is gone rather than bounded.
3266 RelayMessage(inv, vMsg);
3267 mapAlreadyAskedFor.erase(inv);
3268 }
3269 }
3270 3494
3271 3495
3272 else if (strCommand == "block") 3496 else if (strCommand == "block")
@@ -3275,47 +3499,98 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3275 vRecv >> *pblock; 3499 vRecv >> *pblock;
3276 3500
3277 //// debug print 3501 //// debug print
3278 if (LogAcceptsCategory("net")) { printf("received block:\n"); pblock->print(); } 3502 if (LogAcceptsCategory("relay")) { printf("received block:\n"); pblock->print(); }
3279 3503
3280 CInv inv(MSG_BLOCK, pblock->GetHash()); 3504 CInv inv(MSG_BLOCK, pblock->GetHash());
3281 pfrom->AddInventoryKnown(inv); 3505 pfrom->AddInventoryKnown(inv);
3282 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
3283 if (ProcessBlock(pfrom, pblock.release())) 3539 if (ProcessBlock(pfrom, pblock.release()))
3540 {
3284 mapAlreadyAskedFor.erase(inv); 3541 mapAlreadyAskedFor.erase(inv);
3285 }
3286
3287 3542
3288 else if (strCommand == "checkorder" || strCommand == "submitorder") 3543 // The body arrived and validated, so the fetch really is over:
3289 { 3544 // the block is now in mapBlockIndex, or parked in mapOrphanBlocks,
3290 // Legacy Bitcoin 0.1 marketplace messages are not part of Bitflash's 3545 // and either way AlreadyHave answers for it from here on.
3291 // supported protocol. They used to let a remote peer make this wallet 3546 //
3292 // reserve a key or import/broadcast a wallet transaction. Keep the 3547 // This has to stay inside the success branch, next to the erase
3293 // command names explicit so old peers get a quiet no-op instead of a 3548 // above. When validation rejects a body nothing is stored,
3294 // wallet side effect. 3549 // AlreadyHave stays false, and the mapAskFor entry was already
3295 if (LogAcceptsCategory("net")) 3550 // consumed when the getdata went out -- so erasing the tracking
3296 printf("ProcessMessage(%s) : disabled legacy order message\n", strCommand.c_str()); 3551 // here would leave the block requested from nobody at all until
3297 vRecv.clear(); 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
3557 // A peer that just handed us a block is demonstrably at least at
3558 // that block's height. Without this, nStartingHeight is a snapshot
3559 // taken once during the version handshake and never touched again,
3560 // so on a long-lived connection it is the peer's height from
3561 // however many hours ago it happened to connect. The pool's
3562 // behind-the-network check compares against the median of those
3563 // numbers, so as connections age it drifts toward "everyone is
3564 // behind us" and quietly stops firing -- the opposite of its job.
3565 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
3566 if (mi != mapBlockIndex.end() && mi->second)
3567 pfrom->nStartingHeight = max(pfrom->nStartingHeight, mi->second->nHeight);
3568 }
3298 } 3569 }
3299 3570
3300 3571
3301 else if (strCommand == "reply") 3572 // "checkorder", "submitorder" and "reply" -- the 2009 buy-from-a-merchant
3302 { 3573 // exchange -- used to be handled here. All three are gone.
3303 uint256 hashReply; 3574 //
3304 vRecv >> hashReply; 3575 // "checkorder" called GenerateNewKey() for any peer that asked, which is
3305 3576 // AddKey() and a wallet.dat write, and handed the pubkey back. The comment
3306 CRequestTracker tracker; 3577 // said "keep giving the same key to the same ip until they use it", and on
3307 CRITICAL_BLOCK(pfrom->cs_mapRequests) 3578 // a 2009 network of routable IPs that held. It does not hold here: every
3308 { 3579 // inbound peer arrives through a rendezvous tunnel and is tagged with
3309 map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply); 3580 // BtfMarkerAddr() over three random bytes (net.cpp), so addr.ip is freshly
3310 if (mi != pfrom->mapRequests.end()) 3581 // random per connection and the cache never hits. Reconnect in a loop and
3311 { 3582 // the node mints a key per connection -- unbounded wallet growth, unbounded
3312 tracker = (*mi).second; 3583 // growth of the static map that held them, and every one of those keys
3313 pfrom->mapRequests.erase(mi); 3584 // missing from every backup taken beforehand, which for this pre-BIP32
3314 } 3585 // wallet is how coins are lost.
3315 } 3586 //
3316 if (!tracker.IsNull()) 3587 // "submitorder" took a CWalletTx from an unauthenticated peer and, if it
3317 tracker.fn(tracker.param1, vRecv); 3588 // was merely a valid transaction, AddToWallet()'d it. Valid is not the same
3318 } 3589 // as ours: any peer could write chosen transactions into wallet.dat.
3590 //
3591 // "reply" dispatched to a CRequestTracker callback. Nothing in this tree
3592 // has ever called PushRequest, so mapRequests was permanently empty and the
3593 // handler could only ever be a parser for hostile input.
3319 3594
3320 3595
3321 else if (strCommand == "ping") 3596 else if (strCommand == "ping")
@@ -3403,12 +3678,119 @@ bool SendMessages(CNode* pto)
3403 const CInv& inv = (*pto->mapAskFor.begin()).second; 3678 const CInv& inv = (*pto->mapAskFor.begin()).second;
3404 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());
3405 if (!AlreadyHave(txdb, inv)) 3680 if (!AlreadyHave(txdb, inv))
3681 {
3406 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 }
3407 pto->mapAskFor.erase(pto->mapAskFor.begin()); 3712 pto->mapAskFor.erase(pto->mapAskFor.begin());
3408 } 3713 }
3409 if (!vAskFor.empty()) 3714 if (!vAskFor.empty())
3410 pto->PushMessage("getdata", vAskFor); 3715 pto->PushMessage("getdata", vAskFor);
3411 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
3412 } 3794 }
3413 return true; 3795 return true;
3414} 3796}
@@ -3573,6 +3955,110 @@ static bool StratumRecvLine(SOCKET s, std::string& buf,
3573// How long to wait for a mining.notify after authorize before giving up 3955// How long to wait for a mining.notify after authorize before giving up
3574static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120; 3956static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120;
3575 3957
3958// ------------------------------------------------------------------
3959// Multi-threaded pool worker. The original loop hashed a single nonce
3960// per blocking 50ms socket poll, capping one instance at ~20 H/s no
3961// matter how many cores the machine has. The socket and job bookkeeping
3962// stay on the calling thread; N hash threads grind nonce ranges on their
3963// own RandomX VMs (all sharing the one ~2 GB dataset) and drop found
3964// shares on a queue the I/O thread drains and submits.
3965// ------------------------------------------------------------------
3966struct ParticipantJobShared
3967{
3968 std::mutex mtx;
3969 unsigned char header[80];
3970 uint256 target;
3971 std::string jobId;
3972 uint64 jobSeq;
3973 bool haveJob;
3974 bool stop;
3975 ParticipantJobShared() : jobSeq(0), haveJob(false), stop(false)
3976 {
3977 memset(header, 0, sizeof(header));
3978 }
3979};
3980
3981struct ParticipantShareHit
3982{
3983 uint64 jobSeq;
3984 std::string jobId;
3985 unsigned int nonce;
3986};
3987
3988static void ParticipantHashWorker(ParticipantJobShared* sh, int idx, int nWorkers,
3989 std::vector<ParticipantShareHit>* hits,
3990 std::mutex* hitsMtx)
3991{
3992 void* vm = RandomXCreateMinerVM();
3993 if (!vm)
3994 {
3995 LogPrint("worker", "[worker] hash thread %d: RandomX VM creation failed\n", idx);
3996 return;
3997 }
3998
3999 unsigned char header[80];
4000 uint256 target;
4001 std::string jobId;
4002 uint64 mySeq = 0;
4003 bool active = false;
4004 unsigned int nNonce = 0;
4005 uint64 localHashes = 0;
4006
4007 while (!fShutdown)
4008 {
4009 {
4010 std::lock_guard<std::mutex> lk(sh->mtx);
4011 if (sh->stop)
4012 break;
4013 if (sh->haveJob && sh->jobSeq != mySeq)
4014 {
4015 memcpy(header, sh->header, 80);
4016 target = sh->target;
4017 jobId = sh->jobId;
4018 mySeq = sh->jobSeq;
4019 nNonce = (unsigned int)(idx + 1);
4020 active = true;
4021 }
4022 }
4023 if (!active)
4024 {
4025 Sleep(50);
4026 continue;
4027 }
4028
4029 memcpy(header + 76, &nNonce, 4);
4030 uint256 hash = RandomXHashWithVM(vm, header, 80);
4031 localHashes++;
4032 if ((localHashes & 0x3ff) == 0)
4033 gParticipantHashes.fetch_add(0x400);
4034
4035 if (hash <= target)
4036 {
4037 LogPrint("worker", "[worker] share found! nonce=%u job=%s (thread %d)\n",
4038 nNonce, jobId.c_str(), idx);
4039 std::lock_guard<std::mutex> lk(*hitsMtx);
4040 hits->push_back(ParticipantShareHit{mySeq, jobId, nNonce});
4041 }
4042
4043 unsigned int next = nNonce + (unsigned int)nWorkers;
4044 if (next < nNonce) // wrapped past 2^32: range exhausted, wait for next job
4045 active = false;
4046 else
4047 nNonce = next;
4048 }
4049
4050 gParticipantHashes.fetch_add(localHashes & 0x3ff);
4051 RandomXDestroyMinerVM(vm);
4052}
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.
3576static bool PoolParticipantMiner() 4062static bool PoolParticipantMiner()
3577{ 4063{
3578 // ------------------------------------------------------------------ 4064 // ------------------------------------------------------------------
@@ -3587,6 +4073,51 @@ static bool PoolParticipantMiner()
3587 LogPrint("worker", "[worker] connecting to pool %s\n", strParticipantPool.c_str()); 4073 LogPrint("worker", "[worker] connecting to pool %s\n", strParticipantPool.c_str());
3588 SetParticipantMiningStatus("resolving pool address"); 4074 SetParticipantMiningStatus("resolving pool address");
3589 4075
4076 // ------------------------------------------------------------------
4077 // Steps 1-2: connect to the pool. Two address forms:
4078 // tcp:HOST:PORT -- plain TCP straight to the pool's stratum port
4079 // (e.g. our own operator node on this machine),
4080 // no .btf resolution or rendezvous tunnel involved
4081 // <name>.btf -- resolve via Nostr, dial through a rendezvous relay
4082 // ------------------------------------------------------------------
4083 btf_socket_t s = INVALID_SOCKET;
4084 if (strParticipantPool.compare(0, 4, "tcp:") == 0)
4085 {
4086 std::string hp = strParticipantPool.substr(4);
4087 size_t colon = hp.rfind(':');
4088 if (colon == std::string::npos) {
4089 LogPrint("worker", "[worker] bad tcp pool address '%s' (want tcp:host:port)\n",
4090 strParticipantPool.c_str());
4091 SetParticipantMiningStatus("bad pool address");
4092 return false;
4093 }
4094 std::string host = hp.substr(0, colon);
4095 int port = atoi(hp.substr(colon + 1).c_str());
4096 unsigned long ip = inet_addr(host.c_str());
4097 if (port <= 0 || port > 65535 || ip == INADDR_NONE) {
4098 LogPrint("worker", "[worker] bad tcp pool endpoint '%s'\n", hp.c_str());
4099 SetParticipantMiningStatus("bad pool endpoint");
4100 return false;
4101 }
4102 SetParticipantMiningStatus("connecting to pool (direct tcp)");
4103 struct sockaddr_in sa;
4104 memset(&sa, 0, sizeof(sa));
4105 sa.sin_family = AF_INET;
4106 sa.sin_port = htons((unsigned short)port);
4107 sa.sin_addr.s_addr = ip;
4108 SOCKET cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
4109 if (cs == INVALID_SOCKET || connect(cs, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) {
4110 LogPrint("worker", "[worker] direct connect to %s FAILED\n", hp.c_str());
4111 SetParticipantMiningStatus("pool connect failed");
4112 if (cs != INVALID_SOCKET) closesocket(cs);
4113 return false;
4114 }
4115 LogPrint("worker", "[worker] connected directly to pool %s\n", hp.c_str());
4116 s = (btf_socket_t)cs;
4117 goto connected;
4118 }
4119
4120 {
3590 unsigned char target_pubkey[32]; 4121 unsigned char target_pubkey[32];
3591 if (!btf::ParseAddress(strParticipantPool, target_pubkey)) { 4122 if (!btf::ParseAddress(strParticipantPool, target_pubkey)) {
3592 LogPrint("worker", "[worker] invalid .btf address: %s\n", strParticipantPool.c_str()); 4123 LogPrint("worker", "[worker] invalid .btf address: %s\n", strParticipantPool.c_str());
@@ -3605,6 +4136,33 @@ static bool PoolParticipantMiner()
3605 } 4136 }
3606 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());
3607 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
3608 size_t colon = meetingHostPort.rfind(':'); 4166 size_t colon = meetingHostPort.rfind(':');
3609 if (colon == std::string::npos) { 4167 if (colon == std::string::npos) {
3610 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());
@@ -3621,25 +4179,65 @@ static bool PoolParticipantMiner()
3621 } 4179 }
3622 4180
3623 // ------------------------------------------------------------------ 4181 // ------------------------------------------------------------------
3624 // Step 2: open encrypted tunnel to pool 4182 // Step 2b: open encrypted tunnel to pool
3625 // ------------------------------------------------------------------ 4183 // ------------------------------------------------------------------
3626 SetParticipantMiningStatus("opening tunnel"); 4184 SetParticipantMiningStatus("opening tunnel");
3627 btf_socket_t s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, 4185 btf::TunnelFail tunnelFail = btf::TUNNEL_FAIL_NONE;
3628 target_pubkey, service_enc_pub); 4186 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
4187 target_pubkey, service_enc_pub, &tunnelFail);
3629 if (s == INVALID_SOCKET) { 4188 if (s == INVALID_SOCKET) {
3630 LogPrint("worker", "[worker] tunnel connect FAILED via %s" 4189 nTunnelFails++;
3631 " (relay unreachable or pool not listening)\n", 4190 LogPrint("worker", "[worker] tunnel connect FAILED via %s: %s\n",
3632 meetingHostPort.c_str()); 4191 meetingHostPort.c_str(), btf::TunnelFailText(tunnelFail));
3633 SetParticipantMiningStatus("tunnel connect failed"); 4192 SetParticipantMiningStatus(std::string("tunnel connect failed: ")
4193 + btf::TunnelFailText(tunnelFail));
3634 return false; 4194 return false;
3635 } 4195 }
4196 nTunnelFails = 0;
3636 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); 4197 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str());
3637 SetParticipantMiningStatus("tunnel open"); 4198 SetParticipantMiningStatus("tunnel open");
4199 }
4200
4201connected:
3638 4202
3639 // ------------------------------------------------------------------ 4203 // ------------------------------------------------------------------
3640 // Step 3: determine our payout address (mining username) 4204 // Step 3: determine our payout address (mining username). /payoutaddr
4205 // wins -- it lets a machine mine to an address whose keys live
4206 // elsewhere, so no wallet file is needed on the mining machine.
4207 // /payoutfile rotates through a list of such addresses: one is picked
4208 // per (re)connection, and the session self-limits (see rotation break
4209 // in the main loop) so consecutive rounds land on fresh addresses.
3641 // ------------------------------------------------------------------ 4210 // ------------------------------------------------------------------
3642 std::string username; 4211 static std::vector<std::string> vPayoutAddrs;
4212 static size_t nPayoutRot = 0;
4213 if (!strPayoutFile.empty() && vPayoutAddrs.empty())
4214 {
4215 FILE* f = fopen(strPayoutFile.c_str(), "r");
4216 if (f)
4217 {
4218 char line[256];
4219 while (fgets(line, sizeof(line), f))
4220 {
4221 std::string a = line;
4222 while (!a.empty() && (a.back() == '\n' || a.back() == '\r' ||
4223 a.back() == ' ' || a.back() == '\t'))
4224 a.pop_back();
4225 if (!a.empty())
4226 vPayoutAddrs.push_back(a);
4227 }
4228 fclose(f);
4229 LogPrint("worker", "[worker] loaded %zu payout addresses from %s\n",
4230 vPayoutAddrs.size(), strPayoutFile.c_str());
4231 }
4232 else
4233 LogPrint("worker", "[worker] cannot open payout file %s\n",
4234 strPayoutFile.c_str());
4235 }
4236
4237 std::string username = strPayoutAddress;
4238 if (username.empty() && !vPayoutAddrs.empty())
4239 username = vPayoutAddrs[nPayoutRot++ % vPayoutAddrs.size()];
4240 if (username.empty())
3643 { 4241 {
3644 std::vector<unsigned char> vchPubKey; 4242 std::vector<unsigned char> vchPubKey;
3645 if (CWalletDB("r").ReadDefaultKey(vchPubKey)) 4243 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
@@ -3677,16 +4275,14 @@ static bool PoolParticipantMiner()
3677 // ------------------------------------------------------------------ 4275 // ------------------------------------------------------------------
3678 if (!RandomXFastReady()) { 4276 if (!RandomXFastReady()) {
3679 SetParticipantMiningStatus("building RandomX dataset (~2GB, one-time)"); 4277 SetParticipantMiningStatus("building RandomX dataset (~2GB, one-time)");
3680 unsigned int nThreads = std::thread::hardware_concurrency(); 4278 // Fill with the cores we are permitted to use, not the ones the host
4279 // happens to have. More threads than the cpuset allows does not build
4280 // the dataset any faster -- the CPU budget is the same either way --
4281 // it just oversubscribes, and this runs alongside the pool node.
4282 unsigned int nThreads = AvailableCoreCount();
3681 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); 4283 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
3682 } 4284 }
3683 void* rxvm = RandomXCreateMinerVM(); 4285 LogPrint("worker", "[worker] RandomX dataset ready (%s)\n",
3684 if (!rxvm) {
3685 LogPrint("worker", "[worker] failed to create RandomX VM\n");
3686 SetParticipantMiningStatus("RandomX init failed");
3687 closesocket(s); return false;
3688 }
3689 LogPrint("worker", "[worker] RandomX VM ready (%s)\n",
3690 RandomXFastReady() ? "fast 2GB" : "light 256MB"); 4286 RandomXFastReady() ? "fast 2GB" : "light 256MB");
3691 4287
3692 // ------------------------------------------------------------------ 4288 // ------------------------------------------------------------------
@@ -3704,7 +4300,6 @@ static bool PoolParticipantMiner()
3704 {"params",json::array()}})) { 4300 {"params",json::array()}})) {
3705 LogPrint("worker", "[worker] subscribe send FAILED -- pool dropped connection\n"); 4301 LogPrint("worker", "[worker] subscribe send FAILED -- pool dropped connection\n");
3706 SetParticipantMiningStatus("subscribe send failed"); 4302 SetParticipantMiningStatus("subscribe send failed");
3707 RandomXDestroyMinerVM(rxvm);
3708 closesocket(s); return false; 4303 closesocket(s); return false;
3709 } 4304 }
3710 4305
@@ -3714,18 +4309,70 @@ static bool PoolParticipantMiner()
3714 {"params",json::array({username,"x"})}})) { 4309 {"params",json::array({username,"x"})}})) {
3715 LogPrint("worker", "[worker] authorize send FAILED -- pool dropped connection\n"); 4310 LogPrint("worker", "[worker] authorize send FAILED -- pool dropped connection\n");
3716 SetParticipantMiningStatus("authorize send failed"); 4311 SetParticipantMiningStatus("authorize send failed");
3717 RandomXDestroyMinerVM(rxvm);
3718 closesocket(s); return false; 4312 closesocket(s); return false;
3719 } 4313 }
3720 4314
3721 // ------------------------------------------------------------------ 4315 // ------------------------------------------------------------------
4316 // Step 6: spawn hash threads (each with its own RandomX VM on the
4317 // shared dataset), then run the I/O loop below: receive pool messages,
4318 // publish jobs to the threads, submit the shares they find.
4319 // ------------------------------------------------------------------
4320 // One count, decided in one place (main.cpp:MinerThreadCount's neighbour),
4321 // so that what StartMinerThreads() reports at startup and what actually
4322 // hashes here can never drift apart. Explicit /hashthreads=N is honored
4323 // exactly, including 1; automatic leaves one core for node + I/O threads.
4324 int nWorkers = ParticipantHashThreadCount();
4325 ParticipantJobShared jobShared;
4326 std::vector<ParticipantShareHit> shareHits;
4327 std::mutex shareHitsMtx;
4328 std::vector<std::thread> hashThreads;
4329 try
4330 {
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 }
4355 LogPrint("worker", "[worker] spawned %d hash threads on shared RandomX dataset\n",
4356 nWorkers);
4357 const uint64 sessionStartHashes = gParticipantHashes.load();
4358
4359 // ------------------------------------------------------------------
3722 // Step 6: main loop -- receive messages, hash, submit shares 4360 // Step 6: main loop -- receive messages, hash, submit shares
3723 // ------------------------------------------------------------------ 4361 // ------------------------------------------------------------------
3724 std::string jobId; 4362 std::string jobId;
3725 unsigned char header[80] = {}; 4363 unsigned char header[80] = {};
3726 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
3727 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool 4373 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool
3728 bool haveJob = false; 4374 bool haveJob = false;
4375 bool gotAnyJob = false; // any job at all this session -- the return value
3729 bool gotSubscribeAck = false; 4376 bool gotSubscribeAck = false;
3730 bool gotAuthorizeAck = false; 4377 bool gotAuthorizeAck = false;
3731 int64 authWaitStart = 0; 4378 int64 authWaitStart = 0;
@@ -3735,6 +4382,8 @@ static bool PoolParticipantMiner()
3735 uint64 sessionSharesSent = 0; // this connection only 4382 uint64 sessionSharesSent = 0; // this connection only
3736 uint64 sessionSharesAccepted = 0; 4383 uint64 sessionSharesAccepted = 0;
3737 int64 sampleStart = GetTime(); 4384 int64 sampleStart = GetTime();
4385 int64 connStart = GetTime();
4386 const int64 PAYOUT_ROTATE_SECS = 300; // reconnect with next /payoutfile address
3738 4387
3739 SetParticipantMiningStatus("waiting for pool response"); 4388 SetParticipantMiningStatus("waiting for pool response");
3740 4389
@@ -3840,10 +4489,75 @@ static bool PoolParticipantMiner()
3840 jobId = newJobId; 4489 jobId = newJobId;
3841 memcpy(header, hdrBytes.data(), 80); 4490 memcpy(header, hdrBytes.data(), 80);
3842 memcpy(&target, tgtBytes.data(), 32); 4491 memcpy(&target, tgtBytes.data(), 32);
3843 haveJob = true; 4492 haveJob = true;
3844 nNonce = 1; 4493 gotAnyJob = true;
3845 LogPrint("worker", "[pool->worker] new job %s received\n", 4494 nNonce = 1;
3846 jobId.c_str()); 4495 {
4496 std::lock_guard<std::mutex> lk(jobShared.mtx);
4497 memcpy(jobShared.header, header, 80);
4498 jobShared.target = target;
4499 jobShared.jobId = jobId;
4500 jobShared.jobSeq++;
4501 jobShared.haveJob = true;
4502 }
4503 // Log the chain position the pool is
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 }
3847 SetParticipantMiningStatus("hashing job " + jobId); 4561 SetParticipantMiningStatus("hashing job " + jobId);
3848 } 4562 }
3849 } 4563 }
@@ -3897,46 +4611,71 @@ static bool PoolParticipantMiner()
3897 } 4611 }
3898 } 4612 }
3899 4613
4614 // -- Payout address rotation: end this session cleanly so the outer
4615 // loop reconnects and authorizes the next /payoutfile address --
4616 if (!vPayoutAddrs.empty() && GetTime() - connStart > PAYOUT_ROTATE_SECS)
4617 {
4618 LogPrint("worker", "[worker] payout rotation interval reached"
4619 " -- reconnecting with next address\n");
4620 break;
4621 }
4622
3900 if (!haveJob) { Sleep(100); continue; } 4623 if (!haveJob) { Sleep(100); continue; }
3901 4624
3902 // -- Hash one nonce -- 4625 // -- Hash rate accounting (hashing happens on the worker threads) --
3903 memcpy(header+76, &nNonce, 4); 4626 hashesSinceSample = gParticipantHashes.load() - sessionStartHashes;
3904 uint256 hash = RandomXHashWithVM(rxvm, header, 80); 4627 UpdateParticipantHashRate(hashesSinceSample, sampleStart);
3905 hashesSinceSample++;
3906 gParticipantHashes.fetch_add(1);
3907 if ((hashesSinceSample & 0x3ff) == 0)
3908 UpdateParticipantHashRate(hashesSinceSample, sampleStart);
3909
3910 if (hash <= target) {
3911 LogPrint("worker", "[worker] share found! nonce=%u job=%s difficulty=%.4f\n",
3912 nNonce, jobId.c_str(), currentDifficulty);
3913 SetParticipantMiningStatus("share found; submitting");
3914 int submitId = msgId++;
3915 pendingSubmitIds.insert(submitId);
3916 gParticipantSharesSent.fetch_add(1);
3917 sessionSharesSent++;
3918 json submit = {{"id",submitId},{"method","mining.submit"},
3919 {"params",json::array({username, jobId, ToHexStr(&nNonce,4)})}};
3920 if (!StratumSendLine(s, submit)) {
3921 LogPrint("worker", "[worker] share submit send FAILED"
3922 " -- pool connection lost\n");
3923 break;
3924 }
3925 }
3926 4628
3927 nNonce++; 4629 // -- Submit any shares the hash threads found --
3928 if (nNonce == 0) { 4630 bool submitFailed = false;
3929 LogPrint("worker", "[worker] nonce space exhausted for job %s" 4631 {
3930 " -- waiting for new job\n", jobId.c_str()); 4632 std::vector<ParticipantShareHit> pending;
3931 haveJob = false; 4633 {
4634 std::lock_guard<std::mutex> lk(shareHitsMtx);
4635 pending.swap(shareHits);
4636 }
4637 for (ParticipantShareHit& hit : pending)
4638 {
4639 if (hit.jobId != jobId)
4640 {
4641 LogPrint("worker", "[worker] dropping stale share for old job %s\n",
4642 hit.jobId.c_str());
4643 continue;
4644 }
4645 LogPrint("worker", "[worker] share found! nonce=%u job=%s difficulty=%.4f\n",
4646 hit.nonce, hit.jobId.c_str(), currentDifficulty);
4647 SetParticipantMiningStatus("share found; submitting");
4648 int submitId = msgId++;
4649 pendingSubmitIds.insert(submitId);
4650 gParticipantSharesSent.fetch_add(1);
4651 sessionSharesSent++;
4652 json submit = {{"id",submitId},{"method","mining.submit"},
4653 {"params",json::array({username, hit.jobId,
4654 ToHexStr(&hit.nonce,4)})}};
4655 if (!StratumSendLine(s, submit)) {
4656 LogPrint("worker", "[worker] share submit send FAILED"
4657 " -- pool connection lost\n");
4658 submitFailed = true;
4659 break;
4660 }
4661 }
3932 } 4662 }
4663 if (submitFailed)
4664 break;
3933 } 4665 }
3934 4666
3935 // ------------------------------------------------------------------ 4667 // ------------------------------------------------------------------
3936 // Cleanup 4668 // Cleanup: stop the hash threads, then drop the pool connection
3937 // ------------------------------------------------------------------ 4669 // ------------------------------------------------------------------
3938 RandomXDestroyMinerVM(rxvm); 4670 {
4671 std::lock_guard<std::mutex> lk(jobShared.mtx);
4672 jobShared.stop = true;
4673 jobShared.haveJob = false;
4674 }
4675 for (std::thread& t : hashThreads)
4676 t.join();
3939 closesocket(s); 4677 closesocket(s);
4678 hashesSinceSample = gParticipantHashes.load() - sessionStartHashes;
3940 UpdateParticipantHashRate(hashesSinceSample, sampleStart); 4679 UpdateParticipantHashRate(hashesSinceSample, sampleStart);
3941 SetParticipantMiningStatus("disconnected"); 4680 SetParticipantMiningStatus("disconnected");
3942 LogPrint("worker", "[worker] disconnected from pool" 4681 LogPrint("worker", "[worker] disconnected from pool"
@@ -3944,7 +4683,11 @@ static bool PoolParticipantMiner()
3944 (unsigned long long)hashesSinceSample, 4683 (unsigned long long)hashesSinceSample,
3945 (unsigned long long)sessionSharesSent, 4684 (unsigned long long)sessionSharesSent,
3946 (unsigned long long)sessionSharesAccepted); 4685 (unsigned long long)sessionSharesAccepted);
3947 return true; 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;
3948} 4691}
3949 4692
3950// --------------------------------------------------------------------------- 4693// ---------------------------------------------------------------------------
@@ -4141,11 +4884,51 @@ bool BitcoinMiner(int nThreadId)
4141 // Participant mode: connect to external pool instead of mining solo/operator 4884 // Participant mode: connect to external pool instead of mining solo/operator
4142 if (nMineMode == MINE_PARTICIPANT) 4885 if (nMineMode == MINE_PARTICIPANT)
4143 { 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;
4144 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4900 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4145 PoolParticipantMiner(); 4901 bool fGotWork = PoolParticipantMiner();
4146 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4902 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4147 LogPrint("worker", "PoolParticipantMiner: reconnecting in 10s\n"); 4903 if (fGotWork) {
4148 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 }
4149 } 4932 }
4150 } 4933 }
4151 return true; 4934 return true;
@@ -4161,7 +4944,7 @@ bool BitcoinMiner(int nThreadId)
4161 // Build the fast (~2 GB dataset) RandomX mode once, up front, so we're 4944 // Build the fast (~2 GB dataset) RandomX mode once, up front, so we're
4162 // not stuck mining in the much slower light/cache-only mode forever. 4945 // not stuck mining in the much slower light/cache-only mode forever.
4163 if (!RandomXFastReady()) { 4946 if (!RandomXFastReady()) {
4164 unsigned int nThreads = std::thread::hardware_concurrency(); 4947 unsigned int nThreads = AvailableCoreCount();
4165 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); 4948 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
4166 } 4949 }
4167 4950
@@ -4247,8 +5030,40 @@ bool BitcoinMiner(int nThreadId)
4247 map<uint256, CTxIndex> mapTestPool; 5030 map<uint256, CTxIndex> mapTestPool;
4248 vector<char> vfAlreadyAdded(mapTransactions.size()); 5031 vector<char> vfAlreadyAdded(mapTransactions.size());
4249 bool fFoundSomething = true; 5032 bool fFoundSomething = true;
4250 unsigned int nBlockSize = 0; 5033 // Assembly must stop below the consensus cap that CheckBlock()
4251 while (fFoundSomething && nBlockSize < MAX_SIZE/2) 5034 // enforces, or we mine a block our own node then rejects.
5035 //
5036 // This used to read MAX_SIZE/2 (16 MB), which was consistent while
5037 // CheckBlock() also tested against MAX_SIZE (32 MB). Adding
5038 // MAX_BLOCK_SIZE inverted it: assembly could fill 16 MB and
5039 // validation would refuse anything over 1 MB, so a full mempool
5040 // meant solving a block and throwing it away -- with this pool at
5041 // ~90% of hashrate, that is the network stalling, not just us.
5042 //
5043 // The budget test then sat only on this outer while, and the
5044 // comment claimed that bounded the overshoot to a single
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)
4252 { 5067 {
4253 fFoundSomething = false; 5068 fFoundSomething = false;
4254 unsigned int n = 0; 5069 unsigned int n = 0;
@@ -4260,6 +5075,29 @@ bool BitcoinMiner(int nThreadId)
4260 if (tx.IsCoinBase() || !tx.IsFinal()) 5075 if (tx.IsCoinBase() || !tx.IsFinal())
4261 continue; 5076 continue;
4262 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
4263 // Transaction fee requirements, mainly only needed for flood control 5101 // Transaction fee requirements, mainly only needed for flood control
4264 // Under 10K (about 80 inputs) is free for first 100 transactions 5102 // Under 10K (about 80 inputs) is free for first 100 transactions
4265 // Base rate is 0.01 per KB 5103 // Base rate is 0.01 per KB
@@ -4271,7 +5109,8 @@ bool BitcoinMiner(int nThreadId)
4271 swap(mapTestPool, mapTestPoolTmp); 5109 swap(mapTestPool, mapTestPoolTmp);
4272 5110
4273 pblock->vtx.push_back(tx); 5111 pblock->vtx.push_back(tx);
4274 nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); 5112 nBlockSize += nTxSize;
5113 nBlockSigOps += nTxSigOps;
4275 vfAlreadyAdded[n] = true; 5114 vfAlreadyAdded[n] = true;
4276 fFoundSomething = true; 5115 fFoundSomething = true;
4277 } 5116 }
@@ -4320,7 +5159,7 @@ bool BitcoinMiner(int nThreadId)
4320 //// debug print 5159 //// debug print
4321 if (LogAcceptsCategory("net")) printf("BitcoinMiner:\n"); 5160 if (LogAcceptsCategory("net")) printf("BitcoinMiner:\n");
4322 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());
4323 if (LogAcceptsCategory("net")) pblock->print(); 5162 if (LogAcceptsCategory("relay")) pblock->print();
4324 5163
4325 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); 5164 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
4326 CRITICAL_BLOCK(cs_main) 5165 CRITICAL_BLOCK(cs_main)
@@ -4675,9 +5514,17 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
4675 5514
4676 5515
4677 5516
4678bool 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)
4679{ 5524{
4680 nFeeRequiredRet = 0; 5525 nFeeRequiredRet = 0;
5526 if (presultRet)
5527 *presultRet = WALLET_SEND_FAILED;
4681 CRITICAL_BLOCK(cs_main) 5528 CRITICAL_BLOCK(cs_main)
4682 { 5529 {
4683 // txdb must be opened before the mapWallet lock 5530 // txdb must be opened before the mapWallet lock
@@ -4708,10 +5555,21 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
4708 // fixed this in 107d9e288. 5555 // fixed this in 107d9e288.
4709 int64 nTotalValue = nValue + nFee; 5556 int64 nTotalValue = nValue + nFee;
4710 5557
4711 // 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.
4712 set<CWalletTx*> setCoins; 5566 set<CWalletTx*> setCoins;
4713 if (!SelectCoins(nTotalValue, setCoins)) 5567 if (!SelectCoins(nTotalValue, setCoins))
5568 {
5569 if (presultRet)
5570 *presultRet = WALLET_SEND_INSUFFICIENT_FUNDS;
4714 return false; 5571 return false;
5572 }
4715 int64 nValueIn = 0; 5573 int64 nValueIn = 0;
4716 foreach(CWalletTx* pcoin, setCoins) 5574 foreach(CWalletTx* pcoin, setCoins)
4717 nValueIn += pcoin->GetCredit(); 5575 nValueIn += pcoin->GetCredit();
@@ -4781,6 +5639,8 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
4781 } 5639 }
4782 } 5640 }
4783 } 5641 }
5642 if (presultRet)
5643 *presultRet = WALLET_SEND_OK;
4784 return true; 5644 return true;
4785} 5645}
4786 5646
@@ -4843,16 +5703,38 @@ bool CommitTransactionSpent(const CWalletTx& wtxNew)
4843 5703
4844 5704
4845 5705
4846bool 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)
4847{ 5713{
5714 outcomeRet.result = WALLET_SEND_FAILED;
5715 outcomeRet.nFeeRequired = 0;
4848 CRITICAL_BLOCK(cs_main) 5716 CRITICAL_BLOCK(cs_main)
4849 { 5717 {
4850 int64 nFeeRequired; 5718 int64 nFeeRequired;
4851 if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired)) 5719 WalletSendResult createResult = WALLET_SEND_FAILED;
5720 if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired, &createResult))
4852 { 5721 {
5722 outcomeRet.result = createResult;
5723 outcomeRet.nFeeRequired = nFeeRequired;
4853 string strError; 5724 string strError;
4854 if (nValue + nFeeRequired > GetBalance()) 5725 if (nValue + nFeeRequired > GetBalance())
4855 strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str()); 5726 // Not an oversized transaction -- this branch is simply "the
5727 // spendable balance is too small". Saying otherwise (with a fee
5728 // of 0.00, because CreateTransaction failed before setting one)
5729 // sends anyone reading the log after a failed payout hunting a
5730 // transaction-size problem that does not exist.
5731 strError = strprintf("Error: insufficient spendable balance:"
5732 " need %s (plus fee %s) but only %s is spendable"
5733 " -- mined coin counts as zero until it is %d deep",
5734 FormatMoney(nValue).c_str(),
5735 FormatMoney(nFeeRequired).c_str(),
5736 FormatMoney(GetBalance()).c_str(),
5737 COINBASE_SPENDABLE_DEPTH);
4856 else 5738 else
4857 strError = "Error: Transaction creation failed "; 5739 strError = "Error: Transaction creation failed ";
4858 return error("SendMoney() : %s\n", strError.c_str()); 5740 return error("SendMoney() : %s\n", strError.c_str());
@@ -4876,6 +5758,13 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
4876 } 5758 }
4877 wtxNew.RelayWalletTransaction(); 5759 wtxNew.RelayWalletTransaction();
4878 } 5760 }
5761 outcomeRet.result = WALLET_SEND_OK;
4879 MainFrameRepaint(); 5762 MainFrameRepaint();
4880 return true; 5763 return true;
4881} 5764}
5765
5766bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
5767{
5768 SendMoneyOutcome ignored;
5769 return SendMoney(scriptPubKey, nValue, wtxNew, ignored);
5770}