diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 631 |
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 | |||
| 73 | int fGenerateBitcoins; | 74 | int fGenerateBitcoins; |
| 74 | int64 nTransactionFee = 0; | 75 | int64 nTransactionFee = 0; |
| 75 | CAddress addrIncoming; | 76 | CAddress addrIncoming; |
| 76 | int nMineMode = MINE_SOLO; | 77 | int nMineMode = MINE_RELAY; |
| 77 | string strParticipantPool; // participant mode: pool .btf address | 78 | string strParticipantPool; // participant mode: pool .btf address |
| 78 | string strPoolName = "Bitflash Pool"; | 79 | string strPoolName = "Bitflash Pool"; |
| 79 | string strPoolDashboardUrl; | 80 | string strPoolDashboardUrl; |
| @@ -88,11 +89,10 @@ static std::string gParticipantStatus; | |||
| 88 | 89 | ||
| 89 | void SetParticipantMiningStatus(const std::string& status) | 90 | void 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 | ||
| 98 | std::string GetParticipantMiningStatus() | 98 | std::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 | ||
| 1038 | bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) | 1035 | bool 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) | |||
| 1789 | bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | 1786 | bool 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 | ||
| 2288 | using json = nlohmann::json; | 2226 | using 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 | |||
| 2234 | using json = nlohmann::json; | ||
| 2235 | |||
| 2290 | static std::string ToHexStr(const void* p, size_t n) | 2236 | static 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 | ||
| 2314 | static void UpdateParticipantHashRate(uint64 hashesSinceSample, int64 sampleStart) | 2260 | static 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. | ||
| 2324 | static bool StratumSendLine(SOCKET s, const json& j) | 2269 | static 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 | ||
| 2336 | static 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 | ||
| 2285 | static 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 | ||
| 2315 | static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120; | ||
| 2316 | |||
| 2354 | static bool PoolParticipantMiner() | 2317 | static 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 | ||
| 2538 | bool BitcoinMiner() | 2691 | bool 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. | ||
| 3125 | void 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 |
| 2957 | bool CommitTransactionSpent(const CWalletTx& wtxNew) | 3150 | bool 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 | } |