diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 90 |
1 files changed, 71 insertions, 19 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index a4b53ca..bd5fd8b 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -1732,7 +1732,9 @@ static void FlushMaturePayouts() | |||
| 1732 | } | 1732 | } |
| 1733 | int64 total = 0; | 1733 | int64 total = 0; |
| 1734 | for (auto& kv : it->amounts) total += kv.second; | 1734 | for (auto& kv : it->amounts) total += kv.second; |
| 1735 | LogPrint("payout", "[payout] block %d (%s) was orphaned -- voiding %zu" | 1735 | // Always: one line per voided entry, never a collapsed summary. |
| 1736 | // A reorg voids these in a burst from this one source line. | ||
| 1737 | LogPrintAlways("payout", "[payout] block %d (%s) was orphaned -- voiding %zu" | ||
| 1736 | " payment(s) totalling %s\n", | 1738 | " payment(s) totalling %s\n", |
| 1737 | it->blockHeight, it->blockHash.ToString().substr(0, 14).c_str(), | 1739 | it->blockHeight, it->blockHash.ToString().substr(0, 14).c_str(), |
| 1738 | it->amounts.size(), FormatMoney(total).c_str()); | 1740 | it->amounts.size(), FormatMoney(total).c_str()); |
| @@ -1931,7 +1933,7 @@ static void RecordPayoutTxid(const PayingRecord& r, const std::string& txid) | |||
| 1931 | // disagreeing. | 1933 | // disagreeing. |
| 1932 | static void ForfeitPayoutRecipient(const PayingRecord& r, const std::string& reason) | 1934 | static void ForfeitPayoutRecipient(const PayingRecord& r, const std::string& reason) |
| 1933 | { | 1935 | { |
| 1934 | LogPrint("payout", "[payout] writing off %s owed to %s for block %d: %s\n", | 1936 | LogPrintAlways("payout", "[payout] writing off %s owed to %s for block %d: %s\n", |
| 1935 | FormatMoney(r.amount).c_str(), r.address.c_str(), r.blockHeight, | 1937 | FormatMoney(r.amount).c_str(), r.address.c_str(), r.blockHeight, |
| 1936 | reason.c_str()); | 1938 | reason.c_str()); |
| 1937 | VersionedBlob blob; | 1939 | VersionedBlob blob; |
| @@ -2036,7 +2038,7 @@ static void PayoutThreadFn(void*) | |||
| 2036 | // funding block can land at any point in that window. Upstream has | 2038 | // funding block can land at any point in that window. Upstream has |
| 2037 | // no equivalent -- it decides once, when the entry matures. | 2039 | // no equivalent -- it decides once, when the entry matures. |
| 2038 | if (!PayoutBlockStillOnChain(r.blockHash)) { | 2040 | if (!PayoutBlockStillOnChain(r.blockHash)) { |
| 2039 | LogPrint("payout", "[payout] block %d (%s) orphaned while queued" | 2041 | LogPrintAlways("payout", "[payout] block %d (%s) orphaned while queued" |
| 2040 | " -- dropping %s to %s\n", | 2042 | " -- dropping %s to %s\n", |
| 2041 | r.blockHeight, r.blockHash.ToString().substr(0, 14).c_str(), | 2043 | r.blockHeight, r.blockHash.ToString().substr(0, 14).c_str(), |
| 2042 | FormatMoney(r.amount).c_str(), r.address.c_str()); | 2044 | FormatMoney(r.amount).c_str(), r.address.c_str()); |
| @@ -2080,7 +2082,7 @@ static void PayoutThreadFn(void*) | |||
| 2080 | const std::string reason = | 2082 | const std::string reason = |
| 2081 | strprintf("'%s' is not a valid address, so no transaction" | 2083 | strprintf("'%s' is not a valid address, so no transaction" |
| 2082 | " can pay it", r.address.c_str()); | 2084 | " can pay it", r.address.c_str()); |
| 2083 | LogPrint("payout", "[payout] SKIPPED bad address '%s' --" | 2085 | LogPrintAlways("payout", "[payout] SKIPPED bad address '%s' --" |
| 2084 | " %s owed for block %d cannot be paid\n", | 2086 | " %s owed for block %d cannot be paid\n", |
| 2085 | r.address.c_str(), FormatMoney(r.amount).c_str(), | 2087 | r.address.c_str(), FormatMoney(r.amount).c_str(), |
| 2086 | r.blockHeight); | 2088 | r.blockHeight); |
| @@ -2205,7 +2207,7 @@ static void PayoutThreadFn(void*) | |||
| 2205 | // described by at least one of them at every instant. | 2207 | // described by at least one of them at every instant. |
| 2206 | RecordPayoutTxid(r, wtx.GetHash().GetHex()); | 2208 | RecordPayoutTxid(r, wtx.GetHash().GetHex()); |
| 2207 | ForgetPayment(r); | 2209 | ForgetPayment(r); |
| 2208 | LogPrint("payout", "[payout] paid %s to %s (tx %s)\n", | 2210 | LogPrintAlways("payout", "[payout] paid %s to %s (tx %s)\n", |
| 2209 | FormatMoney(r.amount).c_str(), r.address.c_str(), | 2211 | FormatMoney(r.amount).c_str(), r.address.c_str(), |
| 2210 | wtx.GetHash().ToString().substr(0, 12).c_str()); | 2212 | wtx.GetHash().ToString().substr(0, 12).c_str()); |
| 2211 | } else { | 2213 | } else { |
| @@ -2263,7 +2265,7 @@ static void PayoutThreadFn(void*) | |||
| 2263 | // going away must not turn the log into one line a minute | 2265 | // going away must not turn the log into one line a minute |
| 2264 | // forever, and must not go quiet either. | 2266 | // forever, and must not go quiet either. |
| 2265 | if (r.retries <= 5 || r.retries % 60 == 0) | 2267 | if (r.retries <= 5 || r.retries % 60 == 0) |
| 2266 | LogPrint("payout", "[payout] FAILED to pay %s to %s" | 2268 | LogPrintAlways("payout", "[payout] FAILED to pay %s to %s" |
| 2267 | " (%s spendable, wallet unlocked, block %d on" | 2269 | " (%s spendable, wallet unlocked, block %d on" |
| 2268 | " the main chain) -- attempt %d, retrying in" | 2270 | " the main chain) -- attempt %d, retrying in" |
| 2269 | " 60s; this debt stands until it is paid\n", | 2271 | " 60s; this debt stands until it is paid\n", |
| @@ -4947,13 +4949,19 @@ static void AcceptOneFn(void* arg) | |||
| 4947 | std::string host = relay.substr(0, colon); | 4949 | std::string host = relay.substr(0, colon); |
| 4948 | int port = atoi(relay.substr(colon + 1).c_str()); | 4950 | int port = atoi(relay.substr(colon + 1).c_str()); |
| 4949 | 4951 | ||
| 4950 | LogPrint("pool", "[pool] accept thread started for relay %s\n", relay.c_str()); | 4952 | LogPrint("pool", "[pool] accept thread started for relay %s%s\n", relay.c_str(), |
| 4953 | fBtfRelayPinned ? " (/rvrelay pin)" : ""); | ||
| 4954 | |||
| 4955 | // Registration health for this relay. A pin has no fallback, so a pin that | ||
| 4956 | // never registers means the pool is simply unreachable -- and that has to | ||
| 4957 | // be loud. The 2026-07-30 incident went unnoticed because this loop logged | ||
| 4958 | // failures at one flat line and success not at all, so "which relay is this | ||
| 4959 | // pool actually registered at" could not be answered from the log. | ||
| 4960 | int nConsecFail = 0; | ||
| 4961 | int64 nFirstFail = 0; | ||
| 4962 | bool fEverRegistered = false; | ||
| 4951 | 4963 | ||
| 4952 | while (!fShutdown && gPoolRunning) { | 4964 | while (!fShutdown && gPoolRunning) { |
| 4953 | // Advertise this relay as the pool's meeting node BEFORE registering, | ||
| 4954 | // so that miners which resolve our descriptor find a relay where the | ||
| 4955 | // pool (not the node's peer-accept) holds the registration. | ||
| 4956 | BtfSetActiveRelay(relay); | ||
| 4957 | // Returns as soon as the relay lists us -- registered, not yet paired. | 4965 | // Returns as soon as the relay lists us -- registered, not yet paired. |
| 4958 | btf::RvSocket rv = btf::RvServiceRegister(host.c_str(), (unsigned short)port, pk); | 4966 | btf::RvSocket rv = btf::RvServiceRegister(host.c_str(), (unsigned short)port, pk); |
| 4959 | 4967 | ||
| @@ -4962,12 +4970,36 @@ static void AcceptOneFn(void* arg) | |||
| 4962 | break; | 4970 | break; |
| 4963 | } | 4971 | } |
| 4964 | if (rv == btf::RV_INVALID) { | 4972 | if (rv == btf::RV_INVALID) { |
| 4965 | LogPrint("pool", "[pool] relay %s: RvServiceRegister failed" | 4973 | nConsecFail++; |
| 4966 | " -- backing off 30s before retry\n", relay.c_str()); | 4974 | if (nFirstFail == 0) nFirstFail = GetTime(); |
| 4975 | LogPrint("pool", "[pool] relay %s: registration failed (attempt %d," | ||
| 4976 | " failing for %llds) -- backing off 30s before retry\n", | ||
| 4977 | relay.c_str(), nConsecFail, (long long)(GetTime() - nFirstFail)); | ||
| 4978 | if (fBtfRelayPinned && (nConsecFail == 3 || nConsecFail % 10 == 0)) | ||
| 4979 | LogPrint("pool", "[pool] WARNING: pinned relay %s refused registration" | ||
| 4980 | " %d times running (%llds); /rvrelay leaves no fallback, so" | ||
| 4981 | " miners cannot reach this pool%s\n", | ||
| 4982 | relay.c_str(), nConsecFail, (long long)(GetTime() - nFirstFail), | ||
| 4983 | fEverRegistered ? "" : " -- it has never registered once"); | ||
| 4967 | for (int i = 0; i < 30 && !fShutdown && gPoolRunning; i++) Sleep(1000); | 4984 | for (int i = 0; i < 30 && !fShutdown && gPoolRunning; i++) Sleep(1000); |
| 4968 | continue; | 4985 | continue; |
| 4969 | } | 4986 | } |
| 4970 | 4987 | ||
| 4988 | // Advertise THIS relay only now. Setting it before the call published a | ||
| 4989 | // meeting node we were not yet listed at, so a miner resolving the | ||
| 4990 | // descriptor inside that window burned its dial on a relay holding no | ||
| 4991 | // registration for us. net.cpp's peer-accept has always ordered it this | ||
| 4992 | // way round; the pool had not. | ||
| 4993 | BtfSetActiveRelay(relay); | ||
| 4994 | if (!fEverRegistered || nConsecFail > 0) | ||
| 4995 | LogPrint("pool", "[pool] registered at relay %s%s%s -- advertising it as" | ||
| 4996 | " the pool's meeting node\n", relay.c_str(), | ||
| 4997 | fBtfRelayPinned ? " (/rvrelay pin)" : "", | ||
| 4998 | nConsecFail > 0 ? ", recovered" : ""); | ||
| 4999 | fEverRegistered = true; | ||
| 5000 | nConsecFail = 0; | ||
| 5001 | nFirstFail = 0; | ||
| 5002 | |||
| 4971 | // Block for a miner to actually arrive. RvServiceRegister used to do | 5003 | // Block for a miner to actually arrive. RvServiceRegister used to do |
| 4972 | // this itself and returned with the relay's 0x01 pairing byte already | 5004 | // this itself and returned with the relay's 0x01 pairing byte already |
| 4973 | // consumed; it no longer does. Without waiting here, BtfServiceWrap | 5005 | // consumed; it no longer does. Without waiting here, BtfServiceWrap |
| @@ -5022,9 +5054,21 @@ static void AcceptLoopFn(void*) | |||
| 5022 | LogPrint("pool", "[pool] .btf identity ready after %ds\n", waitSecs * 2); | 5054 | LogPrint("pool", "[pool] .btf identity ready after %ds\n", waitSecs * 2); |
| 5023 | 5055 | ||
| 5024 | std::set<std::string> spawned; // relays we've already started a thread for | 5056 | std::set<std::string> spawned; // relays we've already started a thread for |
| 5057 | std::set<std::string> skipped; // discovered relays the pin excluded, logged once each | ||
| 5025 | 5058 | ||
| 5026 | while (!fShutdown && gPoolRunning) { | 5059 | while (!fShutdown && gPoolRunning) { |
| 5060 | // BtfAllRelays() already honours the pin: with /rvrelay set it returns | ||
| 5061 | // vBtfMeetingRelays alone, so no thread is ever spawned for a | ||
| 5062 | // discovered relay. Name the ones it drops -- a pool registering at a | ||
| 5063 | // stranger's relay is exactly the thing that should not be silent. | ||
| 5027 | vector<string> relays = BtfAllRelays(); | 5064 | vector<string> relays = BtfAllRelays(); |
| 5065 | if (fBtfRelayPinned) { | ||
| 5066 | for (const string& r : BtfDiscoveredRelays()) | ||
| 5067 | if (std::find(relays.begin(), relays.end(), r) == relays.end() && | ||
| 5068 | skipped.insert(r).second) | ||
| 5069 | LogPrint("pool", "[pool] not registering at discovered relay %s" | ||
| 5070 | " -- /rvrelay pins the relay list\n", r.c_str()); | ||
| 5071 | } | ||
| 5028 | if (relays.empty()) { | 5072 | if (relays.empty()) { |
| 5029 | LogPrint("pool", "[pool] no relays available -- miners cannot connect yet\n"); | 5073 | LogPrint("pool", "[pool] no relays available -- miners cannot connect yet\n"); |
| 5030 | } else { | 5074 | } else { |
| @@ -5040,8 +5084,9 @@ static void AcceptLoopFn(void*) | |||
| 5040 | relay.c_str()); | 5084 | relay.c_str()); |
| 5041 | } else { | 5085 | } else { |
| 5042 | spawned.insert(relay); | 5086 | spawned.insert(relay); |
| 5043 | LogPrint("pool", "[pool] accept thread spawned for relay %s" | 5087 | LogPrint("pool", "[pool] accept thread spawned for relay %s%s" |
| 5044 | " (%zu total)\n", relay.c_str(), spawned.size()); | 5088 | " (%zu total)\n", relay.c_str(), |
| 5089 | fBtfRelayPinned ? " (/rvrelay pin)" : "", spawned.size()); | ||
| 5045 | } | 5090 | } |
| 5046 | } | 5091 | } |
| 5047 | } | 5092 | } |
| @@ -5077,10 +5122,17 @@ void ThreadRPCServer(void*) | |||
| 5077 | // armed for its one wallet probe before it is allowed to send. | 5122 | // armed for its one wallet probe before it is allowed to send. |
| 5078 | ReconcilePayouts(); | 5123 | ReconcilePayouts(); |
| 5079 | 5124 | ||
| 5080 | _beginthread(AcceptLoopFn, 0, NULL); | 5125 | // _beginthread cannot throw (it reports -1), but an unchecked failure |
| 5081 | _beginthread(LocalAcceptFn, 0, NULL); | 5126 | // leaves the pool running deaf: nobody accepts, announces, or pays, and |
| 5082 | _beginthread(ThreadBtfPoolAnnouncer, 0, NULL); | 5127 | // nothing in the log says why. |
| 5083 | _beginthread(PayoutThreadFn, 0, NULL); | 5128 | if (_beginthread(AcceptLoopFn, 0, NULL) == (uintptr_t)-1) |
| 5129 | LogPrint("pool", "[pool] FATAL: accept loop thread failed to spawn -- miners cannot connect\n"); | ||
| 5130 | if (_beginthread(LocalAcceptFn, 0, NULL) == (uintptr_t)-1) | ||
| 5131 | LogPrint("pool", "[pool] local accept thread failed to spawn\n"); | ||
| 5132 | if (_beginthread(ThreadBtfPoolAnnouncer, 0, NULL) == (uintptr_t)-1) | ||
| 5133 | LogPrint("pool", "[pool] announcer thread failed to spawn -- pool will not be advertised\n"); | ||
| 5134 | if (_beginthread(PayoutThreadFn, 0, NULL) == (uintptr_t)-1) | ||
| 5135 | LogPrint("pool", "[pool] FATAL: payout thread failed to spawn -- no one gets paid\n"); | ||
| 5084 | 5136 | ||
| 5085 | PoolEventLoop(); // blocks until gPoolRunning = false or fShutdown | 5137 | PoolEventLoop(); // blocks until gPoolRunning = false or fShutdown |
| 5086 | 5138 | ||