|
53 | 53 | #include "pool_standup.hpp" |
54 | 54 | #include "coin/embedded_daemon.hpp" |
55 | 55 | #include "stratum/work_source.hpp" |
| 56 | +#include "config_pool.hpp" // bch::PoolConfig::get_donation_script |
| 57 | +#include "share_check.hpp" // bch::create_local_share (transitive via node.hpp) |
| 58 | +#include "share_types.hpp" // bch::StaleInfo |
| 59 | +#include "coin/block.hpp" // bch::coin::SmallBlockHeaderType |
| 60 | + |
| 61 | +#include <core/pack_types.hpp> // BaseScript |
56 | 62 |
|
57 | 63 | #include <core/log.hpp> |
58 | 64 | #include <core/stratum_server.hpp> |
|
62 | 68 | #include <boost/asio/io_context.hpp> |
63 | 69 |
|
64 | 70 | #include <cstdint> |
| 71 | +#include <cstring> |
| 72 | +#include <shared_mutex> |
| 73 | +#include <type_traits> |
65 | 74 | #include <memory> |
66 | 75 | #include <stdexcept> |
67 | 76 | #include <string> |
@@ -132,6 +141,141 @@ inline void standup_pool_run(boost::asio::io_context& ioc, |
132 | 141 | return r.any(); |
133 | 142 | }); |
134 | 143 |
|
| 144 | + // ── Sharechain WRITE path: local-share author wiring ───────────────── |
| 145 | + // Without these callbacks BCHWorkSource accepts miner submissions that |
| 146 | + // clear the sharechain target but DROPS them ("accepted (no-tracker)", |
| 147 | + // work_source.cpp:686) because create_share_fn_ is null -- c2pool-bch runs |
| 148 | + // as a bare stratum proxy and Stored shares stay stuck at 0. This block |
| 149 | + // closes that gap (mirrors main_btc.cpp:863/1118), wiring the three |
| 150 | + // callbacks the local-author path needs: |
| 151 | + // - best_share_hash_fn : new-job prev_share = our sharechain tip |
| 152 | + // - donation_script : version-gated coinbase residual recipient |
| 153 | + // - create_share_fn : mining_submit -> bch::create_local_share -> |
| 154 | + // tracker.add -> broadcast_share + notify_local_share |
| 155 | + // BCH is a STANDALONE SHA256d parent: no segwit, no merged/aux dimension |
| 156 | + // (merged_addrs = {}, segwit_active=false, witness_* empty). ref_hash_fn / |
| 157 | + // pplns_fn are a FOLLOW-UP slice -- they gate peer-verifiable ref_hash + |
| 158 | + // PPLNS payout distribution, NOT local Stored accumulation; until wired, |
| 159 | + // build_connection_coinbase falls back to a single-output coinbase and |
| 160 | + // create_local_share computes its own share target via |
| 161 | + // tracker.compute_share_target (has_frozen=false). |
| 162 | + work_source->set_best_share_hash_fn( |
| 163 | + [&node]() -> uint256 { return node.best_share_hash(); }); |
| 164 | + |
| 165 | + // Initial donation matches the cold-start create version (35 -> P2PK). A |
| 166 | + // ratchet-driven refresh to the COMBINED P2SH on v36 activation is the same |
| 167 | + // follow-up slice as pplns_fn/ref_hash_fn. |
| 168 | + work_source->set_donation_script(PoolConfig::get_donation_script(35)); |
| 169 | + |
| 170 | + work_source->set_create_share_fn( |
| 171 | + [&node](const std::vector<unsigned char>& full_coinbase, |
| 172 | + const std::vector<uint8_t>& header_80b, |
| 173 | + const core::stratum::JobSnapshot& job, |
| 174 | + const std::vector<unsigned char>& payout_script) -> uint256 |
| 175 | + { |
| 176 | + if (header_80b.size() != 80) { |
| 177 | + LOG_WARNING << "[BCH-CREATE-SHARE] bad header size=" << header_80b.size(); |
| 178 | + return uint256::ZERO; |
| 179 | + } |
| 180 | + |
| 181 | + // Parse the 80-byte BCH block header -> SmallBlockHeaderType. The |
| 182 | + // merkle_root (bytes 36..67) is reconstructible from coinbase + |
| 183 | + // frozen branches, so it is not stored in min_header. |
| 184 | + auto read_le32 = [](const uint8_t* p) -> uint32_t { |
| 185 | + return uint32_t(p[0]) | (uint32_t(p[1]) << 8) |
| 186 | + | (uint32_t(p[2]) << 16) | (uint32_t(p[3]) << 24); |
| 187 | + }; |
| 188 | + coin::SmallBlockHeaderType min_header; |
| 189 | + min_header.m_version = read_le32(header_80b.data() + 0); |
| 190 | + std::memcpy(min_header.m_previous_block.data(), header_80b.data() + 4, 32); |
| 191 | + min_header.m_timestamp = read_le32(header_80b.data() + 68); |
| 192 | + min_header.m_bits = read_le32(header_80b.data() + 72); |
| 193 | + min_header.m_nonce = read_le32(header_80b.data() + 76); |
| 194 | + |
| 195 | + BaseScript coinbase_bs(std::vector<unsigned char>( |
| 196 | + full_coinbase.begin(), full_coinbase.end())); |
| 197 | + |
| 198 | + // Stratum branches are hex of LE-internal bytes -> ParseHex+memcpy |
| 199 | + // (SetHex would byte-reverse and break the merkle root the miner used). |
| 200 | + std::vector<uint256> merkle_branches; |
| 201 | + merkle_branches.reserve(job.merkle_branches.size()); |
| 202 | + for (const auto& bhex : job.merkle_branches) { |
| 203 | + uint256 b; |
| 204 | + auto bb = ParseHex(bhex); |
| 205 | + if (bb.size() == 32) std::memcpy(b.begin(), bb.data(), 32); |
| 206 | + merkle_branches.push_back(b); |
| 207 | + } |
| 208 | + |
| 209 | + // Exclusive tracker lock (non-blocking): defer to the next submit if |
| 210 | + // the compute thread is mid-think rather than freezing the io_context. |
| 211 | + std::unique_lock<std::shared_mutex> lk(node.tracker_mutex(), std::try_to_lock); |
| 212 | + if (!lk.owns_lock()) { |
| 213 | + LOG_INFO << "[BCH-CREATE-SHARE] tracker busy -- share deferred"; |
| 214 | + return uint256::ZERO; |
| 215 | + } |
| 216 | + |
| 217 | + // Author at the current tip's version (35 on an empty chain), voting |
| 218 | + // desired=36. Same-version authoring never trips the 60%-by-work |
| 219 | + // accept gate; the ratchet-driven upgrade to v36 is a follow-up slice. |
| 220 | + int64_t create_ver = 35; |
| 221 | + if (!job.prev_share_hash.IsNull() && |
| 222 | + node.tracker().chain.contains(job.prev_share_hash)) { |
| 223 | + node.tracker().chain.get_share(job.prev_share_hash).invoke( |
| 224 | + [&](auto* s) { |
| 225 | + using ST = std::remove_pointer_t<decltype(s)>; |
| 226 | + create_ver = ST::version; |
| 227 | + }); |
| 228 | + } |
| 229 | + |
| 230 | + uint256 share_hash; |
| 231 | + try { |
| 232 | + share_hash = bch::create_local_share( |
| 233 | + node.tracker(), min_header, coinbase_bs, |
| 234 | + /* subsidy */ job.subsidy, |
| 235 | + /* prev_share */ job.prev_share_hash, |
| 236 | + merkle_branches, payout_script, |
| 237 | + /* donation bps */ 50, |
| 238 | + /* merged_addrs */ {}, |
| 239 | + /* stale_info */ StaleInfo::none, |
| 240 | + /* segwit_active */ false, // BCH: no segwit |
| 241 | + /* witness_commitment */ {}, |
| 242 | + /* message_data */ {}, |
| 243 | + /* actual_coinbase_bytes */ full_coinbase, |
| 244 | + /* witness_root */ uint256(), |
| 245 | + /* override_max_bits */ job.share_max_bits, // pin share target to |
| 246 | + /* override_bits */ job.share_bits, // what the miner was issued |
| 247 | + /* frozen_absheight */ 0, |
| 248 | + /* frozen_abswork */ uint128(), |
| 249 | + /* frozen_far_share_hash */ uint256(), |
| 250 | + /* frozen_timestamp */ 0, |
| 251 | + /* frozen_merged_payout */ uint256(), |
| 252 | + /* has_frozen */ false, |
| 253 | + /* frozen_merkle_branches*/ {}, |
| 254 | + /* frozen_witness_root */ uint256(), |
| 255 | + /* frozen_merged_cb_info */ {}, |
| 256 | + /* share_version */ create_ver, |
| 257 | + /* desired_version */ 36); |
| 258 | + } catch (const std::exception& e) { |
| 259 | + LOG_WARNING << "[BCH-CREATE-SHARE] threw: " << e.what(); |
| 260 | + return uint256::ZERO; |
| 261 | + } |
| 262 | + |
| 263 | + // Drop the exclusive lock BEFORE broadcast/notify (they take their |
| 264 | + // own locks / post to the io_context). |
| 265 | + lk.unlock(); |
| 266 | + if (!share_hash.IsNull()) { |
| 267 | + node.broadcast_share(share_hash); |
| 268 | + node.notify_local_share(share_hash); |
| 269 | + LOG_INFO << "[BCH-CREATE-SHARE] OK + broadcast v" << create_ver |
| 270 | + << " hash=" << share_hash.GetHex().substr(0, 16); |
| 271 | + } |
| 272 | + return share_hash; |
| 273 | + }); |
| 274 | + |
| 275 | + LOG_INFO << "[BCH-POOL] sharechain WRITE path wired (mining_submit ->" |
| 276 | + << " create_local_share -> broadcast_share + notify_local_share);" |
| 277 | + << " ref_hash/pplns are a follow-up slice."; |
| 278 | + |
135 | 279 | std::unique_ptr<core::StratumServer> stratum_server; |
136 | 280 | if (stratum_port != 0) { |
137 | 281 | stratum_server = std::make_unique<core::StratumServer>( |
|
0 commit comments