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.cpp631
1 files changed, 414 insertions, 217 deletions
diff --git a/src/main.cpp b/src/main.cpp
index f3de4d3..aff7bab 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -13,6 +13,7 @@
13#include "sha.h" 13#include "sha.h"
14#include <atomic> 14#include <atomic>
15#include <mutex> 15#include <mutex>
16#include <thread>
16#include "btfaddr.h" 17#include "btfaddr.h"
17#include "btftunnel.h" 18#include "btftunnel.h"
18#ifndef _WIN32 19#ifndef _WIN32
@@ -73,7 +74,7 @@ bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local t
73int fGenerateBitcoins; 74int fGenerateBitcoins;
74int64 nTransactionFee = 0; 75int64 nTransactionFee = 0;
75CAddress addrIncoming; 76CAddress addrIncoming;
76int nMineMode = MINE_SOLO; 77int nMineMode = MINE_RELAY;
77string strParticipantPool; // participant mode: pool .btf address 78string strParticipantPool; // participant mode: pool .btf address
78string strPoolName = "Bitflash Pool"; 79string strPoolName = "Bitflash Pool";
79string strPoolDashboardUrl; 80string strPoolDashboardUrl;
@@ -88,11 +89,10 @@ static std::string gParticipantStatus;
88 89
89void SetParticipantMiningStatus(const std::string& status) 90void SetParticipantMiningStatus(const std::string& status)
90{ 91{
92 // Updates the GUI status string only. Logging is done at each call site
93 // with full context; duplicating it here just adds noise to the log.
91 std::lock_guard<std::mutex> lk(gParticipantStatusMutex); 94 std::lock_guard<std::mutex> lk(gParticipantStatusMutex);
92 if (gParticipantStatus == status)
93 return;
94 gParticipantStatus = status; 95 gParticipantStatus = status;
95 printf("PoolParticipantMiner: status -> %s\n", status.c_str());
96} 96}
97 97
98std::string GetParticipantMiningStatus() 98std::string GetParticipantMiningStatus()
@@ -159,7 +159,7 @@ bool AddToWallet(const CWalletTx& wtxIn)
159 wtx.nTimeReceived = GetAdjustedTime(); 159 wtx.nTimeReceived = GetAdjustedTime();
160 160
161 //// debug print 161 //// debug print
162 printf("AddToWallet %s %s\n", wtxIn.GetHash().ToString().substr(0,6).c_str(), fInsertedNew ? "new" : "update"); 162 if (LogAcceptsCategory("net")) printf("AddToWallet %s %s\n", wtxIn.GetHash().ToString().substr(0,6).c_str(), fInsertedNew ? "new" : "update");
163 163
164 if (!fInsertedNew) 164 if (!fInsertedNew)
165 { 165 {
@@ -372,7 +372,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
372 { 372 {
373 vMerkleBranch.clear(); 373 vMerkleBranch.clear();
374 nIndex = -1; 374 nIndex = -1;
375 printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); 375 if (LogAcceptsCategory("net")) printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
376 return 0; 376 return 0;
377 } 377 }
378 378
@@ -433,7 +433,7 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
433 } 433 }
434 else 434 else
435 { 435 {
436 printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); 436 if (LogAcceptsCategory("net")) printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
437 continue; 437 continue;
438 } 438 }
439 439
@@ -519,7 +519,7 @@ bool CTransaction::AcceptTransaction(CTxDB& txdb, bool fCheckInputs, bool* pfMis
519 { 519 {
520 if (ptxOld) 520 if (ptxOld)
521 { 521 {
522 printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str()); 522 if (LogAcceptsCategory("net")) printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str());
523 mapTransactions.erase(ptxOld->GetHash()); 523 mapTransactions.erase(ptxOld->GetHash());
524 } 524 }
525 AddToMemoryPool(); 525 AddToMemoryPool();
@@ -530,7 +530,7 @@ bool CTransaction::AcceptTransaction(CTxDB& txdb, bool fCheckInputs, bool* pfMis
530 if (ptxOld) 530 if (ptxOld)
531 EraseFromWallet(ptxOld->GetHash()); 531 EraseFromWallet(ptxOld->GetHash());
532 532
533 printf("AcceptTransaction(): accepted %s\n", hash.ToString().substr(0,6).c_str()); 533 if (LogAcceptsCategory("net")) printf("AcceptTransaction(): accepted %s\n", hash.ToString().substr(0,6).c_str());
534 return true; 534 return true;
535} 535}
536 536
@@ -669,7 +669,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
669 uint256 hash = GetHash(); 669 uint256 hash = GetHash();
670 if (!txdb.ContainsTx(hash)) 670 if (!txdb.ContainsTx(hash))
671 { 671 {
672 printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str()); 672 if (LogAcceptsCategory("net")) printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str());
673 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); 673 RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
674 } 674 }
675 } 675 }
@@ -683,7 +683,7 @@ void RelayWalletTransactions()
683 nLastTime = GetTime(); 683 nLastTime = GetTime();
684 684
685 // Rebroadcast any of our txes that aren't in a block yet 685 // Rebroadcast any of our txes that aren't in a block yet
686 printf("RelayWalletTransactions()\n"); 686 if (LogAcceptsCategory("net")) printf("RelayWalletTransactions()\n");
687 CTxDB txdb("r"); 687 CTxDB txdb("r");
688 CRITICAL_BLOCK(cs_mapWallet) 688 CRITICAL_BLOCK(cs_mapWallet)
689 { 689 {
@@ -764,7 +764,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
764 764
765 // Limit adjustment step 765 // Limit adjustment step
766 unsigned int nActualTimespan = pindexLast->nTime - pindexFirst->nTime; 766 unsigned int nActualTimespan = pindexLast->nTime - pindexFirst->nTime;
767 printf(" nActualTimespan = %d before bounds\n", nActualTimespan); 767 if (LogAcceptsCategory("net")) printf(" nActualTimespan = %d before bounds\n", nActualTimespan);
768 if (nActualTimespan < nTargetTimespan/4) 768 if (nActualTimespan < nTargetTimespan/4)
769 nActualTimespan = nTargetTimespan/4; 769 nActualTimespan = nTargetTimespan/4;
770 if (nActualTimespan > nTargetTimespan*4) 770 if (nActualTimespan > nTargetTimespan*4)
@@ -780,10 +780,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
780 bnNew = bnProofOfWorkLimit; 780 bnNew = bnProofOfWorkLimit;
781 781
782 /// debug print 782 /// debug print
783 printf("\n\n\nGetNextWorkRequired RETARGET *****\n"); 783 if (LogAcceptsCategory("net")) { printf("\n\n\nGetNextWorkRequired RETARGET *****\n"); printf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan); printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); }
784 printf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan);
785 printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
786 printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
787 784
788 return bnNew.GetCompact(); 785 return bnNew.GetCompact();
789} 786}
@@ -1037,7 +1034,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
1037 1034
1038bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) 1035bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
1039{ 1036{
1040 printf("*** REORGANIZE ***\n"); 1037 LogPrint("net", "*** REORGANIZE ***\n");
1041 1038
1042 // Find the fork 1039 // Find the fork
1043 CBlockIndex* pfork = pindexBest; 1040 CBlockIndex* pfork = pindexBest;
@@ -1712,7 +1709,7 @@ bool ProcessMessages(CNode* pfrom)
1712 CDataStream& vRecv = pfrom->vRecv; 1709 CDataStream& vRecv = pfrom->vRecv;
1713 if (vRecv.empty()) 1710 if (vRecv.empty())
1714 return true; 1711 return true;
1715 printf("ProcessMessages(%d bytes)\n", vRecv.size()); 1712 LogPrint("net", "ProcessMessages(%d bytes)\n", vRecv.size());
1716 1713
1717 // 1714 //
1718 // Message format 1715 // Message format
@@ -1730,13 +1727,13 @@ bool ProcessMessages(CNode* pfrom)
1730 { 1727 {
1731 if (vRecv.size() > sizeof(CMessageHeader)) 1728 if (vRecv.size() > sizeof(CMessageHeader))
1732 { 1729 {
1733 printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); 1730 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n");
1734 vRecv.erase(vRecv.begin(), vRecv.end() - sizeof(CMessageHeader)); 1731 vRecv.erase(vRecv.begin(), vRecv.end() - sizeof(CMessageHeader));
1735 } 1732 }
1736 break; 1733 break;
1737 } 1734 }
1738 if (pstart - vRecv.begin() > 0) 1735 if (pstart - vRecv.begin() > 0)
1739 printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); 1736 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin());
1740 vRecv.erase(vRecv.begin(), pstart); 1737 vRecv.erase(vRecv.begin(), pstart);
1741 1738
1742 // Read header 1739 // Read header
@@ -1744,7 +1741,7 @@ bool ProcessMessages(CNode* pfrom)
1744 vRecv >> hdr; 1741 vRecv >> hdr;
1745 if (!hdr.IsValid()) 1742 if (!hdr.IsValid())
1746 { 1743 {
1747 printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); 1744 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str());
1748 continue; 1745 continue;
1749 } 1746 }
1750 string strCommand = hdr.GetCommand(); 1747 string strCommand = hdr.GetCommand();
@@ -1755,7 +1752,7 @@ bool ProcessMessages(CNode* pfrom)
1755 { 1752 {
1756 // Rewind and wait for rest of message 1753 // Rewind and wait for rest of message
1757 ///// need a mechanism to give up waiting for overlong message size error 1754 ///// need a mechanism to give up waiting for overlong message size error
1758 printf("MESSAGE-BREAK 2\n"); 1755 if (LogAcceptsCategory("net")) printf("MESSAGE-BREAK 2\n");
1759 vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr)); 1756 vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr));
1760 Sleep(100); 1757 Sleep(100);
1761 break; 1758 break;
@@ -1776,7 +1773,7 @@ bool ProcessMessages(CNode* pfrom)
1776 } 1773 }
1777 CATCH_PRINT_EXCEPTION("ProcessMessage()") 1774 CATCH_PRINT_EXCEPTION("ProcessMessage()")
1778 if (!fRet) 1775 if (!fRet)
1779 printf("ProcessMessage(%s, %d bytes) from %s to %s FAILED\n", strCommand.c_str(), nMessageSize, pfrom->addr.ToString().c_str(), addrLocalHost.ToString().c_str()); 1776 if (LogAcceptsCategory("net")) printf("ProcessMessage(%s, %d bytes) from %s to %s FAILED\n", strCommand.c_str(), nMessageSize, pfrom->addr.ToString().c_str(), addrLocalHost.ToString().c_str());
1780 } 1777 }
1781 1778
1782 vRecv.Compact(); 1779 vRecv.Compact();
@@ -1789,13 +1786,10 @@ bool ProcessMessages(CNode* pfrom)
1789bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 1786bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1790{ 1787{
1791 static map<unsigned int, vector<unsigned char> > mapReuseKey; 1788 static map<unsigned int, vector<unsigned char> > mapReuseKey;
1792 printf("received: %-12s (%d bytes) ", strCommand.c_str(), vRecv.size()); 1789 if (LogAcceptsCategory("net")) { printf("received: %-12s (%d bytes) ", strCommand.c_str(), vRecv.size()); for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) printf("%02x ", vRecv[i] & 0xff); printf("\n"); }
1793 for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++)
1794 printf("%02x ", vRecv[i] & 0xff);
1795 printf("\n");
1796 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) 1790 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0)
1797 { 1791 {
1798 printf("dropmessages DROPPING RECV MESSAGE\n"); 1792 if (LogAcceptsCategory("net")) printf("dropmessages DROPPING RECV MESSAGE\n");
1799 return true; 1793 return true;
1800 } 1794 }
1801 1795
@@ -1833,7 +1827,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1833 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0)); 1827 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0));
1834 } 1828 }
1835 1829
1836 printf("version addrMe = %s\n", addrMe.ToString().c_str()); 1830 if (LogAcceptsCategory("net")) printf("version addrMe = %s\n", addrMe.ToString().c_str());
1837 } 1831 }
1838 1832
1839 1833
@@ -1844,30 +1838,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1844 } 1838 }
1845 1839
1846 1840
1847 else if (strCommand == "addr")
1848 {
1849 vector<CAddress> vAddr;
1850 vRecv >> vAddr;
1851
1852 // Store the new addresses
1853 CAddrDB addrdb;
1854 foreach(const CAddress& addr, vAddr)
1855 {
1856 if (fShutdown)
1857 return true;
1858 if (AddAddress(addrdb, addr))
1859 {
1860 // Put on lists to send to other nodes
1861 pfrom->setAddrKnown.insert(addr);
1862 CRITICAL_BLOCK(cs_vNodes)
1863 foreach(CNode* pnode, vNodes)
1864 if (!pnode->setAddrKnown.count(addr))
1865 pnode->vAddrToSend.push_back(addr);
1866 }
1867 }
1868 }
1869
1870
1871 else if (strCommand == "inv") 1841 else if (strCommand == "inv")
1872 { 1842 {
1873 vector<CInv> vInv; 1843 vector<CInv> vInv;
@@ -1881,7 +1851,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1881 pfrom->AddInventoryKnown(inv); 1851 pfrom->AddInventoryKnown(inv);
1882 1852
1883 bool fAlreadyHave = AlreadyHave(txdb, inv); 1853 bool fAlreadyHave = AlreadyHave(txdb, inv);
1884 printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); 1854 if (LogAcceptsCategory("net")) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
1885 1855
1886 if (!fAlreadyHave) 1856 if (!fAlreadyHave)
1887 pfrom->AskFor(inv); 1857 pfrom->AskFor(inv);
@@ -1900,7 +1870,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1900 { 1870 {
1901 if (fShutdown) 1871 if (fShutdown)
1902 return true; 1872 return true;
1903 printf("received getdata for: %s\n", inv.ToString().c_str()); 1873 if (LogAcceptsCategory("net")) printf("received getdata for: %s\n", inv.ToString().c_str());
1904 1874
1905 if (inv.type == MSG_BLOCK) 1875 if (inv.type == MSG_BLOCK)
1906 { 1876 {
@@ -1940,12 +1910,12 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1940 // Send the rest of the chain 1910 // Send the rest of the chain
1941 if (pindex) 1911 if (pindex)
1942 pindex = pindex->pnext; 1912 pindex = pindex->pnext;
1943 printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,14).c_str()); 1913 if (LogAcceptsCategory("net")) printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,14).c_str());
1944 for (; pindex; pindex = pindex->pnext) 1914 for (; pindex; pindex = pindex->pnext)
1945 { 1915 {
1946 if (pindex->GetBlockHash() == hashStop) 1916 if (pindex->GetBlockHash() == hashStop)
1947 { 1917 {
1948 printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str()); 1918 if (LogAcceptsCategory("net")) printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str());
1949 break; 1919 break;
1950 } 1920 }
1951 1921
@@ -1997,7 +1967,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1997 1967
1998 if (tx.AcceptTransaction(true)) 1968 if (tx.AcceptTransaction(true))
1999 { 1969 {
2000 printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str()); 1970 if (LogAcceptsCategory("net")) printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str());
2001 AddToWalletIfMine(tx, NULL); 1971 AddToWalletIfMine(tx, NULL);
2002 RelayMessage(inv, vMsg); 1972 RelayMessage(inv, vMsg);
2003 mapAlreadyAskedFor.erase(inv); 1973 mapAlreadyAskedFor.erase(inv);
@@ -2011,7 +1981,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2011 } 1981 }
2012 else if (fMissingInputs) 1982 else if (fMissingInputs)
2013 { 1983 {
2014 printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str()); 1984 if (LogAcceptsCategory("net")) printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str());
2015 AddOrphanTx(vMsg); 1985 AddOrphanTx(vMsg);
2016 } 1986 }
2017 } 1987 }
@@ -2041,7 +2011,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2041 vRecv >> *pblock; 2011 vRecv >> *pblock;
2042 2012
2043 //// debug print 2013 //// debug print
2044 printf("received block:\n"); pblock->print(); 2014 if (LogAcceptsCategory("net")) { printf("received block:\n"); pblock->print(); }
2045 2015
2046 CInv inv(MSG_BLOCK, pblock->GetHash()); 2016 CInv inv(MSG_BLOCK, pblock->GetHash());
2047 pfrom->AddInventoryKnown(inv); 2017 pfrom->AddInventoryKnown(inv);
@@ -2051,25 +2021,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2051 } 2021 }
2052 2022
2053 2023
2054 else if (strCommand == "getaddr")
2055 {
2056 pfrom->vAddrToSend.clear();
2057 //// need to expand the time range if not enough found
2058 int64 nSince = GetAdjustedTime() - 60 * 60; // in the last hour
2059 CRITICAL_BLOCK(cs_mapAddresses)
2060 {
2061 foreach(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
2062 {
2063 if (fShutdown)
2064 return true;
2065 const CAddress& addr = item.second;
2066 if (addr.nTime > nSince)
2067 pfrom->vAddrToSend.push_back(addr);
2068 }
2069 }
2070 }
2071
2072
2073 else if (strCommand == "checkorder") 2024 else if (strCommand == "checkorder")
2074 { 2025 {
2075 uint256 hashReply; 2026 uint256 hashReply;
@@ -2134,12 +2085,12 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2134 else 2085 else
2135 { 2086 {
2136 // Ignore unknown commands for extensibility 2087 // Ignore unknown commands for extensibility
2137 printf("ProcessMessage(%s) : Ignored unknown message\n", strCommand.c_str()); 2088 if (LogAcceptsCategory("net")) printf("ProcessMessage(%s) : Ignored unknown message\n", strCommand.c_str());
2138 } 2089 }
2139 2090
2140 2091
2141 if (!vRecv.empty()) 2092 if (!vRecv.empty())
2142 printf("ProcessMessage(%s) : %d extra bytes\n", strCommand.c_str(), vRecv.size()); 2093 if (LogAcceptsCategory("net")) printf("ProcessMessage(%s) : %d extra bytes\n", strCommand.c_str(), vRecv.size());
2143 2094
2144 return true; 2095 return true;
2145} 2096}
@@ -2163,19 +2114,6 @@ bool SendMessages(CNode* pto)
2163 2114
2164 2115
2165 // 2116 //
2166 // Message: addr
2167 //
2168 vector<CAddress> vAddrToSend;
2169 vAddrToSend.reserve(pto->vAddrToSend.size());
2170 foreach(const CAddress& addr, pto->vAddrToSend)
2171 if (!pto->setAddrKnown.count(addr))
2172 vAddrToSend.push_back(addr);
2173 pto->vAddrToSend.clear();
2174 if (!vAddrToSend.empty())
2175 pto->PushMessage("addr", vAddrToSend);
2176
2177
2178 //
2179 // Message: inventory 2117 // Message: inventory
2180 // 2118 //
2181 vector<CInv> vInventoryToSend; 2119 vector<CInv> vInventoryToSend;
@@ -2204,7 +2142,7 @@ bool SendMessages(CNode* pto)
2204 while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) 2142 while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
2205 { 2143 {
2206 const CInv& inv = (*pto->mapAskFor.begin()).second; 2144 const CInv& inv = (*pto->mapAskFor.begin()).second;
2207 printf("sending getdata: %s\n", inv.ToString().c_str()); 2145 if (LogAcceptsCategory("net")) printf("sending getdata: %s\n", inv.ToString().c_str());
2208 if (!AlreadyHave(txdb, inv)) 2146 if (!AlreadyHave(txdb, inv))
2209 vAskFor.push_back(inv); 2147 vAskFor.push_back(inv);
2210 pto->mapAskFor.erase(pto->mapAskFor.begin()); 2148 pto->mapAskFor.erase(pto->mapAskFor.begin());
@@ -2280,13 +2218,21 @@ void BlockSHA256(const void* pin, unsigned int nBlocks, void* pout)
2280 2218
2281 2219
2282// --------------------------------------------------------------------------- 2220// ---------------------------------------------------------------------------
2283// Participant pool miner — connects to an external Stratum server, receives 2221// Participant pool miner -- connects to an external Stratum server, receives
2284// RandomX jobs, hashes with the local CPU, and submits shares. 2222// RandomX jobs, hashes with the local CPU, and submits shares.
2285// Called from BitcoinMiner() when nMineMode == MINE_PARTICIPANT. 2223// Called from BitcoinMiner() when nMineMode == MINE_PARTICIPANT.
2286// --------------------------------------------------------------------------- 2224// ---------------------------------------------------------------------------
2287 2225
2288using json = nlohmann::json; 2226using json = nlohmann::json;
2289 2227
2228
2229// ---------------------------------------------------------------------------
2230// Participant miner -- connects to a pool over a .btf tunnel, runs Stratum,
2231// hashes with RandomX, submits shares. Called from BitcoinMiner().
2232// ---------------------------------------------------------------------------
2233
2234using json = nlohmann::json;
2235
2290static std::string ToHexStr(const void* p, size_t n) 2236static std::string ToHexStr(const void* p, size_t n)
2291{ 2237{
2292 const unsigned char* b = (const unsigned char*)p; 2238 const unsigned char* b = (const unsigned char*)p;
@@ -2300,10 +2246,10 @@ static std::vector<unsigned char> FromHexStr(const std::string& s)
2300{ 2246{
2301 std::vector<unsigned char> v; 2247 std::vector<unsigned char> v;
2302 for (size_t i = 0; i+1 < s.size(); i += 2) { 2248 for (size_t i = 0; i+1 < s.size(); i += 2) {
2303 auto h=[](char c)->int{ 2249 auto h = [](char c)->int{
2304 if(c>='0'&&c<='9')return c-'0'; 2250 if(c>='0'&&c<='9') return c-'0';
2305 if(c>='a'&&c<='f')return c-'a'+10; 2251 if(c>='a'&&c<='f') return c-'a'+10;
2306 if(c>='A'&&c<='F')return c-'A'+10; 2252 if(c>='A'&&c<='F') return c-'A'+10;
2307 return 0; 2253 return 0;
2308 }; 2254 };
2309 v.push_back((unsigned char)((h(s[i])<<4)|h(s[i+1]))); 2255 v.push_back((unsigned char)((h(s[i])<<4)|h(s[i+1])));
@@ -2313,14 +2259,13 @@ static std::vector<unsigned char> FromHexStr(const std::string& s)
2313 2259
2314static void UpdateParticipantHashRate(uint64 hashesSinceSample, int64 sampleStart) 2260static void UpdateParticipantHashRate(uint64 hashesSinceSample, int64 sampleStart)
2315{ 2261{
2316 int64 now = GetTime(); 2262 int64 elapsed = GetTime() - sampleStart;
2317 int64 elapsed = now - sampleStart; 2263 if (elapsed <= 0) elapsed = 1;
2318 if (elapsed <= 0) 2264 gParticipantHashRateX1000.store(
2319 elapsed = 1; 2265 (uint64)((double)hashesSinceSample / (double)elapsed * 1000.0));
2320 double rate = (double)hashesSinceSample / (double)elapsed;
2321 gParticipantHashRateX1000.store((uint64)(rate * 1000.0));
2322} 2266}
2323 2267
2268// Blocking send of a JSON line to the pool.
2324static bool StratumSendLine(SOCKET s, const json& j) 2269static bool StratumSendLine(SOCKET s, const json& j)
2325{ 2270{
2326 std::string line = j.dump() + "\n"; 2271 std::string line = j.dump() + "\n";
@@ -2333,38 +2278,59 @@ static bool StratumSendLine(SOCKET s, const json& j)
2333 return true; 2278 return true;
2334} 2279}
2335 2280
2336static bool StratumRecvLine(SOCKET s, std::string& buf, std::string& line) 2281// Non-blocking recv into carry buffer. Returns true + fills line when a
2282// complete newline-terminated message is ready. Returns false in two cases:
2283// disconnected=true -- socket closed by peer; caller must stop
2284// disconnected=false -- no complete line yet; caller should select() and retry
2285static bool StratumRecvLine(SOCKET s, std::string& buf,
2286 std::string& line, bool& disconnected)
2337{ 2287{
2338 while (true) { 2288 disconnected = false;
2339 size_t pos = buf.find('\n'); 2289 char tmp[4096];
2340 if (pos != std::string::npos) { 2290#ifdef _WIN32
2341 line = buf.substr(0, pos); 2291 u_long avail = 0;
2342 if (!line.empty() && line.back()=='\r') line.pop_back(); 2292 ioctlsocket(s, FIONREAD, &avail);
2343 buf = buf.substr(pos+1); 2293 if (avail > 0) {
2344 return true; 2294 int r = recv(s, tmp, (int)std::min((u_long)(sizeof(tmp)-1), avail), 0);
2345 } 2295 if (r > 0) { tmp[r] = 0; buf += tmp; }
2346 char tmp[4096]; 2296 else if (r == 0) { disconnected = true; return false; }
2347 int r = recv(s, tmp, sizeof(tmp)-1, 0); 2297 }
2348 if (r <= 0) return false; 2298#else
2349 tmp[r] = 0; 2299 {
2350 buf += tmp; 2300 int r = recv(s, tmp, sizeof(tmp)-1, MSG_DONTWAIT);
2301 if (r > 0) { tmp[r] = 0; buf += tmp; }
2302 else if (r == 0) { disconnected = true; return false; }
2303 // r < 0: EAGAIN -- no data right now
2351 } 2304 }
2305#endif
2306 size_t pos = buf.find('\n');
2307 if (pos == std::string::npos) return false;
2308 line = buf.substr(0, pos);
2309 if (!line.empty() && line.back() == '\r') line.pop_back();
2310 buf = buf.substr(pos + 1);
2311 return true;
2352} 2312}
2353 2313
2314// How long to wait for a mining.notify after authorize before giving up
2315static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120;
2316
2354static bool PoolParticipantMiner() 2317static bool PoolParticipantMiner()
2355{ 2318{
2356 printf("PoolParticipantMiner: connecting to %s\n", strParticipantPool.c_str()); 2319 // ------------------------------------------------------------------
2357 SetParticipantMiningStatus("resolving pool address"); 2320 // Step 1: resolve .btf address -> meeting node
2358 2321 // ------------------------------------------------------------------
2359 if (strParticipantPool.empty()) { 2322 if (strParticipantPool.empty()) {
2360 printf("PoolParticipantMiner: no pool selected\n"); 2323 LogPrint("worker", "[worker] no pool selected\n");
2361 SetParticipantMiningStatus("no pool selected"); 2324 SetParticipantMiningStatus("no pool selected");
2362 return false; 2325 return false;
2363 } 2326 }
2364 2327
2328 LogPrint("worker", "[worker] connecting to pool %s\n", strParticipantPool.c_str());
2329 SetParticipantMiningStatus("resolving pool address");
2330
2365 unsigned char target_pubkey[32]; 2331 unsigned char target_pubkey[32];
2366 if (!btf::ParseAddress(strParticipantPool, target_pubkey)) { 2332 if (!btf::ParseAddress(strParticipantPool, target_pubkey)) {
2367 printf("PoolParticipantMiner: invalid .btf address %s\n", strParticipantPool.c_str()); 2333 LogPrint("worker", "[worker] invalid .btf address: %s\n", strParticipantPool.c_str());
2368 SetParticipantMiningStatus("invalid pool address"); 2334 SetParticipantMiningStatus("invalid pool address");
2369 return false; 2335 return false;
2370 } 2336 }
@@ -2372,35 +2338,48 @@ static bool PoolParticipantMiner()
2372 std::string meetingHostPort; 2338 std::string meetingHostPort;
2373 unsigned char service_enc_pub[32]; 2339 unsigned char service_enc_pub[32];
2374 if (!BtfResolve(strParticipantPool, meetingHostPort, service_enc_pub)) { 2340 if (!BtfResolve(strParticipantPool, meetingHostPort, service_enc_pub)) {
2375 printf("PoolParticipantMiner: failed to resolve %s\n", strParticipantPool.c_str()); 2341 LogPrint("worker", "[worker] failed to resolve pool %s"
2342 " (no Nostr descriptor found -- pool may be offline or relays down)\n",
2343 strParticipantPool.c_str());
2376 SetParticipantMiningStatus("failed to resolve pool"); 2344 SetParticipantMiningStatus("failed to resolve pool");
2377 return false; 2345 return false;
2378 } 2346 }
2347 LogPrint("worker", "[worker] pool resolved to meeting node %s\n", meetingHostPort.c_str());
2379 2348
2380 size_t colon = meetingHostPort.rfind(':'); 2349 size_t colon = meetingHostPort.rfind(':');
2381 if (colon == std::string::npos) { 2350 if (colon == std::string::npos) {
2382 printf("PoolParticipantMiner: bad meeting node %s\n", meetingHostPort.c_str()); 2351 LogPrint("worker", "[worker] bad meeting node format '%s'\n", meetingHostPort.c_str());
2383 SetParticipantMiningStatus("bad pool endpoint"); 2352 SetParticipantMiningStatus("bad pool endpoint");
2384 return false; 2353 return false;
2385 } 2354 }
2386 std::string host = meetingHostPort.substr(0, colon); 2355 std::string host = meetingHostPort.substr(0, colon);
2387 int port = atoi(meetingHostPort.substr(colon + 1).c_str()); 2356 int port = atoi(meetingHostPort.substr(colon + 1).c_str());
2388 if (port <= 0 || port > 65535) { 2357 if (port <= 0 || port > 65535) {
2389 printf("PoolParticipantMiner: bad meeting port %d\n", port); 2358 LogPrint("worker", "[worker] bad meeting port %d in '%s'\n",
2359 port, meetingHostPort.c_str());
2390 SetParticipantMiningStatus("bad pool port"); 2360 SetParticipantMiningStatus("bad pool port");
2391 return false; 2361 return false;
2392 } 2362 }
2393 2363
2394 btf_socket_t s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, target_pubkey, service_enc_pub); 2364 // ------------------------------------------------------------------
2365 // Step 2: open encrypted tunnel to pool
2366 // ------------------------------------------------------------------
2367 SetParticipantMiningStatus("opening tunnel");
2368 btf_socket_t s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
2369 target_pubkey, service_enc_pub);
2395 if (s == INVALID_SOCKET) { 2370 if (s == INVALID_SOCKET) {
2396 printf("PoolParticipantMiner: tunnel connect failed via %s\n", meetingHostPort.c_str()); 2371 LogPrint("worker", "[worker] tunnel connect FAILED via %s"
2372 " (relay unreachable or pool not listening)\n",
2373 meetingHostPort.c_str());
2397 SetParticipantMiningStatus("tunnel connect failed"); 2374 SetParticipantMiningStatus("tunnel connect failed");
2398 return false; 2375 return false;
2399 } 2376 }
2400 printf("PoolParticipantMiner: connected via %s\n", meetingHostPort.c_str()); 2377 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str());
2401 SetParticipantMiningStatus("tunnel connected"); 2378 SetParticipantMiningStatus("tunnel open");
2402 2379
2403 // Use our wallet address as mining username so payouts go to our wallet 2380 // ------------------------------------------------------------------
2381 // Step 3: determine our payout address (mining username)
2382 // ------------------------------------------------------------------
2404 std::string username; 2383 std::string username;
2405 { 2384 {
2406 std::vector<unsigned char> vchPubKey; 2385 std::vector<unsigned char> vchPubKey;
@@ -2408,102 +2387,260 @@ static bool PoolParticipantMiner()
2408 username = PubKeyToAddress(vchPubKey); 2387 username = PubKeyToAddress(vchPubKey);
2409 } 2388 }
2410 if (username.empty()) username = BtfLocalAddress(); 2389 if (username.empty()) username = BtfLocalAddress();
2411 if (username.empty()) username = "unknown"; 2390 if (username.empty()) {
2412 2391 LogPrint("worker", "[worker] WARNING: could not determine wallet address"
2392 " -- shares will not pay out\n");
2393 username = "unknown";
2394 }
2395 LogPrint("worker", "[worker] payout address: %s\n", username.c_str());
2396
2397 // ------------------------------------------------------------------
2398 // Step 4: build the fast (~2 GB dataset) RandomX mode if not already
2399 // built, then start our VM. Without this, RandomXCreateMinerVM() falls
2400 // back to "light" mode (cache only) forever, which is dramatically
2401 // slower per hash than fast mode -- it recomputes dataset items on the
2402 // fly for every single hash instead of a direct memory lookup. That
2403 // alone can turn a "few seconds per share" pool into "many minutes per
2404 // share" regardless of how easy the share target is.
2405 //
2406 // This MUST happen before we send mining.subscribe/authorize, not
2407 // after: it's a synchronous, single-threaded-per-call build that can
2408 // take anywhere from several seconds to a couple of minutes depending
2409 // on core count. If it runs after the handshake messages are sent,
2410 // the pool's replies (subscribe ack, authorize ack, set_difficulty,
2411 // and the first job) all land in the OS socket buffer while we're
2412 // not reading -- nothing is lost, but we sit there looking (and
2413 // logging) like we're "stuck waiting for pool response" for the
2414 // entire build duration, when really we're just not listening yet.
2415 // Building the dataset first means that by the time we say
2416 // "subscribing", we're genuinely ready to read the reply the instant
2417 // it arrives.
2418 // ------------------------------------------------------------------
2419 if (!RandomXFastReady()) {
2420 SetParticipantMiningStatus("building RandomX dataset (~2GB, one-time)");
2421 unsigned int nThreads = std::thread::hardware_concurrency();
2422 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
2423 }
2424 void* rxvm = RandomXCreateMinerVM();
2425 if (!rxvm) {
2426 LogPrint("worker", "[worker] failed to create RandomX VM\n");
2427 SetParticipantMiningStatus("RandomX init failed");
2428 closesocket(s); return false;
2429 }
2430 LogPrint("worker", "[worker] RandomX VM ready (%s)\n",
2431 RandomXFastReady() ? "fast 2GB" : "light 256MB");
2432
2433 // ------------------------------------------------------------------
2434 // Step 5: Stratum subscribe + authorize. The socket is read from
2435 // immediately after this, in the main loop below, so nothing sits
2436 // unread behind a blocking dataset build anymore.
2437 // ------------------------------------------------------------------
2413 std::string buf; 2438 std::string buf;
2414 int msgId = 1; 2439 int msgId = 1;
2415 std::set<int> pendingSubmitIds; 2440 std::set<int> pendingSubmitIds;
2416 2441
2417 // Subscribe
2418 SetParticipantMiningStatus("subscribing"); 2442 SetParticipantMiningStatus("subscribing");
2419 if (!StratumSendLine(s, json{{"id",msgId++},{"method","mining.subscribe"},{"params",json::array()}})) 2443 LogPrint("worker", "[worker->pool] sending mining.subscribe\n");
2420 { SetParticipantMiningStatus("subscribe failed"); closesocket(s); return false; } 2444 if (!StratumSendLine(s, json{{"id",msgId++},{"method","mining.subscribe"},
2445 {"params",json::array()}})) {
2446 LogPrint("worker", "[worker] subscribe send FAILED -- pool dropped connection\n");
2447 SetParticipantMiningStatus("subscribe send failed");
2448 RandomXDestroyMinerVM(rxvm);
2449 closesocket(s); return false;
2450 }
2421 2451
2422 // Authorize
2423 SetParticipantMiningStatus("authorizing"); 2452 SetParticipantMiningStatus("authorizing");
2453 LogPrint("worker", "[worker->pool] sending mining.authorize as %s\n", username.c_str());
2424 if (!StratumSendLine(s, json{{"id",msgId++},{"method","mining.authorize"}, 2454 if (!StratumSendLine(s, json{{"id",msgId++},{"method","mining.authorize"},
2425 {"params",json::array({username,"x"})}})) 2455 {"params",json::array({username,"x"})}})) {
2426 { SetParticipantMiningStatus("authorize failed"); closesocket(s); return false; } 2456 LogPrint("worker", "[worker] authorize send FAILED -- pool dropped connection\n");
2457 SetParticipantMiningStatus("authorize send failed");
2458 RandomXDestroyMinerVM(rxvm);
2459 closesocket(s); return false;
2460 }
2427 2461
2428 // Job state 2462 // ------------------------------------------------------------------
2463 // Step 6: main loop -- receive messages, hash, submit shares
2464 // ------------------------------------------------------------------
2429 std::string jobId; 2465 std::string jobId;
2430 unsigned char header[80]; 2466 unsigned char header[80] = {};
2431 uint256 target; 2467 uint256 target;
2432 bool haveJob = false; 2468 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool
2469 bool haveJob = false;
2433 bool gotSubscribeAck = false; 2470 bool gotSubscribeAck = false;
2434 bool gotAuthorizeAck = false; 2471 bool gotAuthorizeAck = false;
2435 int64 authWaitStart = 0; 2472 int64 authWaitStart = 0;
2436 unsigned int nNonce = 1; 2473 int64 lastStuckLog = 0;
2437 uint64 hashesSinceSample = 0; 2474 unsigned int nNonce = 1;
2438 int64 sampleStart = GetTime(); 2475 uint64 hashesSinceSample = 0;
2476 uint64 sessionSharesSent = 0; // this connection only
2477 uint64 sessionSharesAccepted = 0;
2478 int64 sampleStart = GetTime();
2439 2479
2440 void* rxvm = RandomXCreateMinerVM(); 2480 SetParticipantMiningStatus("waiting for pool response");
2441 if (!rxvm) { SetParticipantMiningStatus("failed to start RandomX miner"); closesocket(s); return false; }
2442 printf("PoolParticipantMiner: RandomX VM ready (%s)\n",
2443 RandomXFastReady() ? "fast 2GB" : "light 256MB");
2444 SetParticipantMiningStatus("authorized; asking pool for work");
2445 2481
2446 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) 2482 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown)
2447 { 2483 {
2448 // Non-blocking receive check 2484 // -- Receive: check buffer first, then select --
2449 { 2485 {
2450 fd_set fds; FD_ZERO(&fds); FD_SET(s, &fds); 2486 bool haveBuffered = buf.find('\n') != std::string::npos;
2451 struct timeval tv={0,50000}; // 50ms 2487 bool readable = false;
2452 if (select((int)s+1, &fds, NULL, NULL, &tv) > 0) { 2488 if (!haveBuffered) {
2489 fd_set fds; FD_ZERO(&fds); FD_SET(s, &fds);
2490 struct timeval tv = {0, 50000}; // 50ms
2491 readable = select((int)s+1, &fds, NULL, NULL, &tv) > 0;
2492 }
2493
2494 if (haveBuffered || readable) {
2453 std::string line; 2495 std::string line;
2454 if (!StratumRecvLine(s, buf, line)) break; 2496 bool disc = false;
2455 try { 2497 while (StratumRecvLine(s, buf, line, disc)) {
2456 json msg = json::parse(line); 2498 json msg;
2457 if (msg.contains("id") && msg["id"].is_number_integer() && msg.contains("result")) { 2499 try { msg = json::parse(line); } catch (...) {
2458 int id = msg["id"].get<int>(); 2500 LogPrint("worker", "[pool->worker] JSON parse error: %.80s\n",
2459 if (id == 1 && msg["result"].is_boolean() && msg["result"].get<bool>()) { 2501 line.c_str());
2460 gotSubscribeAck = true; 2502 continue;
2461 SetParticipantMiningStatus("subscribed; authorizing"); 2503 }
2462 } else if (id == 2 && msg["result"].is_boolean()) { 2504
2463 gotAuthorizeAck = msg["result"].get<bool>(); 2505 // -- Response to our subscribe (id=1) --
2464 authWaitStart = GetTime(); 2506 if (msg.contains("id") && msg["id"].is_number_integer()
2465 SetParticipantMiningStatus(gotAuthorizeAck ? "authorized; asking pool for work" : "authorization rejected"); 2507 && msg["id"].get<int>() == 1
2508 && msg.contains("result") && !msg["result"].is_null()) {
2509 gotSubscribeAck = true;
2510 LogPrint("worker", "[pool->worker] subscribe ack received\n");
2511 SetParticipantMiningStatus("subscribed; waiting for authorize ack");
2512 }
2513
2514 // -- Response to our authorize (id=2) --
2515 else if (msg.contains("id") && msg["id"].is_number_integer()
2516 && msg["id"].get<int>() == 2
2517 && msg.contains("result")) {
2518 bool ok = msg["result"].is_boolean() && msg["result"].get<bool>();
2519 authWaitStart = GetTime();
2520 if (ok) {
2521 gotAuthorizeAck = true;
2522 LogPrint("worker", "[pool->worker] authorize OK -- waiting for job\n");
2523 SetParticipantMiningStatus("authorized; waiting for job");
2524 } else {
2525 std::string reason = msg.value("error", json("unspecified")).is_string()
2526 ? msg["error"].get<std::string>() : "unspecified";
2527 LogPrint("worker", "[pool->worker] authorize REJECTED: %s\n",
2528 reason.c_str());
2529 SetParticipantMiningStatus("authorization rejected");
2530 // Don't break -- pool might still send a job (some pools
2531 // reject bad addresses but still send work)
2466 } 2532 }
2467 if (pendingSubmitIds.erase(id) > 0) { 2533 }
2468 if (msg["result"].is_boolean() && msg["result"].get<bool>()) { 2534
2535 // -- Share submission response --
2536 else if (msg.contains("id") && msg["id"].is_number_integer()) {
2537 int rid = msg["id"].get<int>();
2538 if (pendingSubmitIds.erase(rid) > 0) {
2539 bool accepted = msg.contains("result")
2540 && msg["result"].is_boolean()
2541 && msg["result"].get<bool>();
2542 if (accepted) {
2469 gParticipantSharesAccepted.fetch_add(1); 2543 gParticipantSharesAccepted.fetch_add(1);
2544 sessionSharesAccepted++;
2545 LogPrint("worker", "[pool->worker] share id=%d ACCEPTED\n", rid);
2470 SetParticipantMiningStatus("share accepted"); 2546 SetParticipantMiningStatus("share accepted");
2471 } else { 2547 } else {
2472 SetParticipantMiningStatus("share rejected"); 2548 std::string reason = (msg.contains("error")
2549 && msg["error"].is_string())
2550 ? msg["error"].get<std::string>() : "unspecified";
2551 LogPrint("worker", "[pool->worker] share id=%d REJECTED: %s\n",
2552 rid, reason.c_str());
2553 SetParticipantMiningStatus("share rejected: " + reason);
2473 } 2554 }
2474 } 2555 }
2475 } 2556 }
2476 std::string method = msg.value("method",""); 2557
2477 if (method == "mining.notify") { 2558 // -- Server notification --
2478 auto& p = msg["params"]; 2559 else {
2479 jobId = p[0].get<std::string>(); 2560 std::string method = msg.value("method", "");
2480 // p[1] = full 80-byte header hex 2561
2481 auto hdrBytes = FromHexStr(p[1].get<std::string>()); 2562 if (method == "mining.notify") {
2482 if (hdrBytes.size() >= 80) 2563 auto& p = msg["params"];
2483 memcpy(header, hdrBytes.data(), 80); 2564 if (!p.is_array() || p.size() < 4) {
2484 // p[3] = share target hex (little-endian uint256) 2565 LogPrint("worker", "[pool->worker] malformed mining.notify"
2485 auto tgtBytes = FromHexStr(p[3].get<std::string>()); 2566 " (params size=%zu)\n", p.is_array() ? p.size() : 0);
2486 if (tgtBytes.size() >= 32) 2567 } else {
2487 memcpy(&target, tgtBytes.data(), 32); 2568 std::string newJobId = p[0].get<std::string>();
2488 haveJob = true; 2569 auto hdrBytes = FromHexStr(p[1].get<std::string>());
2489 nNonce = 1; 2570 auto tgtBytes = FromHexStr(p[3].get<std::string>());
2490 SetParticipantMiningStatus("job received; hashing"); 2571
2491 printf("PoolParticipantMiner: new job %s\n", jobId.c_str()); 2572 if (hdrBytes.size() < 80) {
2492 } else if (method == "mining.set_difficulty") { 2573 LogPrint("worker", "[pool->worker] mining.notify:"
2493 // acknowledged, we use the target from notify 2574 " header too short (%zu bytes)\n",
2575 hdrBytes.size());
2576 } else if (tgtBytes.size() < 32) {
2577 LogPrint("worker", "[pool->worker] mining.notify:"
2578 " target too short (%zu bytes)\n",
2579 tgtBytes.size());
2580 } else {
2581 jobId = newJobId;
2582 memcpy(header, hdrBytes.data(), 80);
2583 memcpy(&target, tgtBytes.data(), 32);
2584 haveJob = true;
2585 nNonce = 1;
2586 LogPrint("worker", "[pool->worker] new job %s received\n",
2587 jobId.c_str());
2588 SetParticipantMiningStatus("hashing job " + jobId);
2589 }
2590 }
2591 } else if (method == "mining.set_difficulty") {
2592 // Pool adjusts our share difficulty via vardiff.
2593 // Apply it immediately so we start hashing at the new target.
2594 if (msg.contains("params") && msg["params"].is_array()
2595 && msg["params"].size() > 0
2596 && msg["params"][0].is_number()) {
2597 double newDiff = msg["params"][0].get<double>();
2598 if (newDiff > 0.0) {
2599 bool changed = (newDiff != currentDifficulty);
2600 currentDifficulty = newDiff;
2601 LogPrint("worker", "[pool->worker] set_difficulty %.6f%s\n",
2602 newDiff, changed ? " (updated)" : " (initial)");
2603 }
2604 } else {
2605 LogPrint("worker", "[pool->worker] set_difficulty"
2606 " received (no valid params)\n");
2607 }
2608 } else if (!method.empty()) {
2609 LogPrint("worker", "[pool->worker] unknown server method: %s\n",
2610 method.c_str());
2611 }
2494 } 2612 }
2495 } catch(...) {} 2613 }
2614
2615 if (disc) {
2616 LogPrint("worker", "[worker] pool closed the connection"
2617 " (gotSubscribeAck=%d gotAuthorizeAck=%d haveJob=%d)\n",
2618 gotSubscribeAck, gotAuthorizeAck, haveJob);
2619 break;
2620 }
2496 } 2621 }
2497 } 2622 }
2498 2623
2499 if (gotAuthorizeAck && !haveJob && authWaitStart > 0 && GetTime() - authWaitStart > 10) { 2624 // -- Stuck watchdog: authorized but no job arriving --
2500 SetParticipantMiningStatus("authorized; waiting for pool notify"); 2625 if (gotAuthorizeAck && !haveJob && authWaitStart > 0) {
2501 authWaitStart = 0; 2626 int64 waited = GetTime() - authWaitStart;
2627 if (waited > 10 && GetTime() - lastStuckLog >= 15) {
2628 LogPrint("worker", "[worker] authorized but no job after %llds"
2629 " -- pool tunnel is live, waiting on mining.notify\n",
2630 (long long)waited);
2631 lastStuckLog = GetTime();
2632 }
2633 if (waited > STUCK_NO_JOB_TIMEOUT_SECS) {
2634 LogPrint("worker", "[worker] no job received after %llds -- disconnecting"
2635 " and reconnecting\n", (long long)STUCK_NO_JOB_TIMEOUT_SECS);
2636 SetParticipantMiningStatus("timeout waiting for job; reconnecting");
2637 break;
2638 }
2502 } 2639 }
2503 2640
2504 if (!haveJob) { Sleep(100); continue; } 2641 if (!haveJob) { Sleep(100); continue; }
2505 2642
2506 // Hash one nonce 2643 // -- Hash one nonce --
2507 memcpy(header+76, &nNonce, 4); 2644 memcpy(header+76, &nNonce, 4);
2508 uint256 hash = RandomXHashWithVM(rxvm, header, 80); 2645 uint256 hash = RandomXHashWithVM(rxvm, header, 80);
2509 hashesSinceSample++; 2646 hashesSinceSample++;
@@ -2512,38 +2649,59 @@ static bool PoolParticipantMiner()
2512 UpdateParticipantHashRate(hashesSinceSample, sampleStart); 2649 UpdateParticipantHashRate(hashesSinceSample, sampleStart);
2513 2650
2514 if (hash <= target) { 2651 if (hash <= target) {
2515 printf("PoolParticipantMiner: share found! nonce=%u\n", nNonce); 2652 LogPrint("worker", "[worker] share found! nonce=%u job=%s difficulty=%.4f\n",
2653 nNonce, jobId.c_str(), currentDifficulty);
2516 SetParticipantMiningStatus("share found; submitting"); 2654 SetParticipantMiningStatus("share found; submitting");
2517 int submitId = msgId++; 2655 int submitId = msgId++;
2518 pendingSubmitIds.insert(submitId); 2656 pendingSubmitIds.insert(submitId);
2519 gParticipantSharesSent.fetch_add(1); 2657 gParticipantSharesSent.fetch_add(1);
2658 sessionSharesSent++;
2520 json submit = {{"id",submitId},{"method","mining.submit"}, 2659 json submit = {{"id",submitId},{"method","mining.submit"},
2521 {"params",json::array({username, jobId, 2660 {"params",json::array({username, jobId, ToHexStr(&nNonce,4)})}};
2522 ToHexStr(&nNonce,4)})}}; 2661 if (!StratumSendLine(s, submit)) {
2523 if (!StratumSendLine(s, submit)) break; 2662 LogPrint("worker", "[worker] share submit send FAILED"
2663 " -- pool connection lost\n");
2664 break;
2665 }
2524 } 2666 }
2525 2667
2526 nNonce++; 2668 nNonce++;
2527 if (nNonce == 0) { haveJob = false; } // exhausted, wait for new job 2669 if (nNonce == 0) {
2670 LogPrint("worker", "[worker] nonce space exhausted for job %s"
2671 " -- waiting for new job\n", jobId.c_str());
2672 haveJob = false;
2673 }
2528 } 2674 }
2529 2675
2676 // ------------------------------------------------------------------
2677 // Cleanup
2678 // ------------------------------------------------------------------
2530 RandomXDestroyMinerVM(rxvm); 2679 RandomXDestroyMinerVM(rxvm);
2531 closesocket(s); 2680 closesocket(s);
2532 UpdateParticipantHashRate(hashesSinceSample, sampleStart); 2681 UpdateParticipantHashRate(hashesSinceSample, sampleStart);
2533 SetParticipantMiningStatus("participant miner stopped"); 2682 SetParticipantMiningStatus("disconnected");
2534 printf("PoolParticipantMiner: disconnected\n"); 2683 LogPrint("worker", "[worker] disconnected from pool"
2684 " (hashes this session: %llu, shares sent: %llu, accepted: %llu)\n",
2685 (unsigned long long)hashesSinceSample,
2686 (unsigned long long)sessionSharesSent,
2687 (unsigned long long)sessionSharesAccepted);
2535 return true; 2688 return true;
2536} 2689}
2537 2690
2538bool BitcoinMiner() 2691bool BitcoinMiner()
2539{ 2692{
2693 // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here
2694 // since the GUI hides Start Mining for Relay, but guard it regardless.)
2695 if (nMineMode == MINE_RELAY)
2696 return true;
2697
2540 // Participant mode: connect to external pool instead of mining solo/operator 2698 // Participant mode: connect to external pool instead of mining solo/operator
2541 if (nMineMode == MINE_PARTICIPANT) 2699 if (nMineMode == MINE_PARTICIPANT)
2542 { 2700 {
2543 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 2701 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
2544 PoolParticipantMiner(); 2702 PoolParticipantMiner();
2545 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 2703 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
2546 printf("PoolParticipantMiner: reconnecting in 10s\n"); 2704 LogPrint("worker", "PoolParticipantMiner: reconnecting in 10s\n");
2547 for (int i = 0; i < 10 && !fShutdown; i++) Sleep(1000); 2705 for (int i = 0; i < 10 && !fShutdown; i++) Sleep(1000);
2548 } 2706 }
2549 } 2707 }
@@ -2554,9 +2712,16 @@ bool BitcoinMiner()
2554 if (nMineMode == MINE_OPERATOR) 2712 if (nMineMode == MINE_OPERATOR)
2555 return true; 2713 return true;
2556 2714
2557 printf("BitcoinMiner started\n"); 2715 LogPrint("net", "BitcoinMiner started\n");
2558 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); 2716 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
2559 2717
2718 // Build the fast (~2 GB dataset) RandomX mode once, up front, so we're
2719 // not stuck mining in the much slower light/cache-only mode forever.
2720 if (!RandomXFastReady()) {
2721 unsigned int nThreads = std::thread::hardware_concurrency();
2722 RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1);
2723 }
2724
2560 CKey key; 2725 CKey key;
2561 key.MakeNewKey(); 2726 key.MakeNewKey();
2562 CBigNum bnExtraNonce = 0; 2727 CBigNum bnExtraNonce = 0;
@@ -2639,7 +2804,7 @@ bool BitcoinMiner()
2639 } 2804 }
2640 pblock->nBits = nBits; 2805 pblock->nBits = nBits;
2641 pblock->vtx[0].vout[0].nValue = pblock->GetBlockValue(nFees); 2806 pblock->vtx[0].vout[0].nValue = pblock->GetBlockValue(nFees);
2642 printf("\n\nRunning BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); 2807 if (LogAcceptsCategory("net")) printf("\n\nRunning BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
2643 2808
2644 2809
2645 // 2810 //
@@ -2658,11 +2823,11 @@ bool BitcoinMiner()
2658 void* rxvm = RandomXCreateMinerVM(); 2823 void* rxvm = RandomXCreateMinerVM();
2659 if (!rxvm) 2824 if (!rxvm)
2660 { 2825 {
2661 printf("BitcoinMiner: failed to create RandomX VM\n"); 2826 LogPrint("net", "BitcoinMiner: failed to create RandomX VM\n");
2662 Sleep(1000); 2827 Sleep(1000);
2663 continue; 2828 continue;
2664 } 2829 }
2665 printf("BitcoinMiner: mining with RandomX (%s)\n", 2830 LogPrint("net", "BitcoinMiner: mining with RandomX (%s)\n",
2666 RandomXFastReady() ? "fast 2GB" : "light 256MB"); 2831 RandomXFastReady() ? "fast 2GB" : "light 256MB");
2667 2832
2668 unsigned int nStart = GetTime(); 2833 unsigned int nStart = GetTime();
@@ -2675,9 +2840,9 @@ bool BitcoinMiner()
2675 if (hash <= hashTarget) 2840 if (hash <= hashTarget)
2676 { 2841 {
2677 //// debug print 2842 //// debug print
2678 printf("BitcoinMiner:\n"); 2843 if (LogAcceptsCategory("net")) printf("BitcoinMiner:\n");
2679 printf("RandomX proof-of-work found \n powhash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); 2844 if (LogAcceptsCategory("net")) printf("RandomX proof-of-work found \n powhash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
2680 pblock->print(); 2845 if (LogAcceptsCategory("net")) pblock->print();
2681 2846
2682 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); 2847 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
2683 CRITICAL_BLOCK(cs_main) 2848 CRITICAL_BLOCK(cs_main)
@@ -2692,7 +2857,7 @@ bool BitcoinMiner()
2692 2857
2693 // Process this block the same as if we had received it from another node 2858 // Process this block the same as if we had received it from another node
2694 if (!ProcessBlock(NULL, pblock.release())) 2859 if (!ProcessBlock(NULL, pblock.release()))
2695 printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n"); 2860 LogPrint("net", "ERROR in BitcoinMiner, ProcessBlock, block not accepted\n");
2696 } 2861 }
2697 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); 2862 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
2698 2863
@@ -2860,11 +3025,11 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
2860 setCoinsRet.insert(vValue[i].second); 3025 setCoinsRet.insert(vValue[i].second);
2861 3026
2862 //// debug print 3027 //// debug print
2863 printf("SelectCoins() best subset: "); 3028 if (LogAcceptsCategory("net")) printf("SelectCoins() best subset: ");
2864 for (int i = 0; i < vValue.size(); i++) 3029 for (int i = 0; i < vValue.size(); i++)
2865 if (vfBest[i]) 3030 if (vfBest[i])
2866 printf("%s ", FormatMoney(vValue[i].first).c_str()); 3031 if (LogAcceptsCategory("net")) printf("%s ", FormatMoney(vValue[i].first).c_str());
2867 printf("total %s\n", FormatMoney(nBest).c_str()); 3032 if (LogAcceptsCategory("net")) printf("total %s\n", FormatMoney(nBest).c_str());
2868 } 3033 }
2869 3034
2870 return true; 3035 return true;
@@ -2953,6 +3118,34 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
2953 return true; 3118 return true;
2954} 3119}
2955 3120
3121// Undo what CommitTransactionSpent did: unmark the inputs it marked spent
3122// and erase the half-committed wallet entry. Used when a transaction is
3123// accepted into the wallet but then rejected by the mempool/chain, so we
3124// don't end up with a wallet that thinks money is spent that never left.
3125void RollbackTransactionSpent(const CWalletTx& wtxNew)
3126{
3127 CRITICAL_BLOCK(cs_main)
3128 CRITICAL_BLOCK(cs_mapWallet)
3129 {
3130 set<CWalletTx*> setCoins;
3131 foreach(const CTxIn& txin, wtxNew.vin)
3132 {
3133 map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
3134 if (mi != mapWallet.end())
3135 setCoins.insert(&mi->second);
3136 }
3137 foreach(CWalletTx* pcoin, setCoins)
3138 {
3139 pcoin->fSpent = false;
3140 pcoin->WriteToDisk();
3141 vWalletUpdated.push_back(make_pair(pcoin->GetHash(), false));
3142 }
3143
3144 EraseFromWallet(wtxNew.GetHash());
3145 }
3146 MainFrameRepaint();
3147}
3148
2956// Call after CreateTransaction unless you want to abort 3149// Call after CreateTransaction unless you want to abort
2957bool CommitTransactionSpent(const CWalletTx& wtxNew) 3150bool CommitTransactionSpent(const CWalletTx& wtxNew)
2958{ 3151{
@@ -3003,13 +3196,17 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
3003 return error("SendMoney() : Error finalizing transaction"); 3196 return error("SendMoney() : Error finalizing transaction");
3004 } 3197 }
3005 3198
3006 printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str()); 3199 if (LogAcceptsCategory("net")) printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str());
3007 3200
3008 // Broadcast 3201 // Broadcast
3009 if (!wtxNew.AcceptTransaction()) 3202 if (!wtxNew.AcceptTransaction())
3010 { 3203 {
3011 // This must not fail. The transaction has already been signed and recorded. 3204 // The transaction was already signed and committed to the wallet
3012 throw runtime_error("SendMoney() : wtxNew.AcceptTransaction() failed\n"); 3205 // above, but the chain/mempool just rejected it (e.g. a raced
3206 // input already spent by another transaction). Unwind the
3207 // wallet state we just wrote instead of crashing the app.
3208 RollbackTransactionSpent(wtxNew);
3209 return error("SendMoney() : wtxNew.AcceptTransaction() failed");
3013 } 3210 }
3014 wtxNew.RelayWalletTransaction(); 3211 wtxNew.RelayWalletTransaction();
3015 } 3212 }