Repository navigation
Expand file tree
/
Copy pathproxy.conf.example
More file actions
379 lines (356 loc) · 19.8 KB
/
Copy pathproxy.conf.example
File metadata and controls
379 lines (356 loc) · 19.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
listen_addr = 0.0.0.0
listen_port = 3334
# Maximum concurrent stratum connections, across every listener. A connection
# past this is closed immediately at accept. Each one costs a thread and an fd.
max_conns = 500
# listen() backlog for every stratum listener: completed handshakes the kernel
# holds until the pool accepts them. Default 1024 (when unset or 0). A full
# queue drops connections silently -- watch TcpExt:ListenOverflows. The kernel
# caps it at net.core.somaxconn. Negative values are refused.
# listen_backlog = 1024
bitcoind_url = http://127.0.0.1:18443
# bitcoind_user / bitcoind_pass are optional. Omit BOTH for a block-template
# backend that doesn't require authentication; the RPC call is then made
# without a basic-auth header. Set them for a stock bitcoind using rpcuser/
# rpcpassword (cookie auth is not supported).
bitcoind_user = drivepool
bitcoind_pass = drivepool
# How often to re-fetch the block template. On a drivechain pool this is also
# the worst-case delay before a sidechain's BMM request reaches a stratum job:
# a sidechain publishes its BMM transaction only after it sees the new tip, so
# any block found within one poll of that cannot carry the commitment. Lower it
# (5000-10000) if sidechains need to merge-mine reliably; the cost is one extra
# getblocktemplate call per interval.
bitcoind_poll_interval_ms = 30000
# Miners are paid directly: the stratum username must be the miner's
# bitcoin address (`<address>` or `<address>.<rig_label>`). The configured
# operator_address below receives a fee_bps cut of every block reward.
operator_address = bc1qREPLACEME # REPLACE with a real address for your network (bc1 mainnet / tb1 testnet / bcrt1 regtest)
fee_bps = 100 # 100 = 1%; valid range 0..1000 (max 10%)
coinbase_tag = /simplepool/
# vardiff — auto-adjust each connection's difficulty to keep the share
# rate near `target_spm` shares/minute. Set vardiff_enabled = 0 to pin
# every connection to initial_diff (legacy behaviour).
#
# These are the settings for listen_port, which is the port home miners are
# pointed at. Rented hashrate does not belong here — give it its own listener
# below, with its own floor.
initial_diff = 1
vardiff_enabled = 1
vardiff_target_spm = 12 # ~1 share every 5 seconds per connection
vardiff_min = 1
vardiff_max = 1e12
vardiff_window_sec = 30 # how often to retarget
# How many accepted shares a window must hold before its rate is trusted
# enough to act on. At the defaults above an ON-TARGET connection produces
# six shares a window, and Poisson noise on six samples is +/-41%, so the
# measured rate leaves the retarget deadband on noise alone and the
# difficulty oscillates around the right answer instead of settling on it.
#
# Under the floor the window stays OPEN and keeps accumulating, for up to
# vardiff_max_window_mult windows -- after which it acts on what it has, so a
# connection whose difficulty really is far too high still ratchets down.
#
# 0 restores the previous behaviour exactly.
vardiff_min_samples = 20
vardiff_max_window_mult = 8
# Largest difficulty step a window that never met the sample floor may make.
# A window that met it keeps the historical 4x. This is what stops a quiet
# connection being driven to vardiff_min: one rig behind a proxy arrives as
# many connections, each going quiet between bursts, and at 4x two near-empty
# windows cut its difficulty 16x.
vardiff_idle_step = 2
# Extra stratum ports, each with its own difficulty policy. Repeatable —
# unlike every other key here, a second `listener` line adds a port rather
# than replacing the first. listen_port above is always served too, on the
# settings above.
# At most 7 listener lines: listen_port takes the eighth port slot.
#
# port the port to bind (required)
# min_diff vardiff floor for this port, and its starting difficulty
# initial_diff starting difficulty, if it should differ from min_diff
# max_diff vardiff ceiling for this port
# label shown in the log and on the dashboard; [A-Za-z0-9_-]
#
# Why a second port at all: one difficulty cannot serve both a home ASIC and
# rented hashrate. Rented hashrate does not arrive as many small miners — a
# marketplace aggregates a whole fleet behind one connection, and 1 PH/s at
# difficulty 1024 is ~227 shares per SECOND down that single socket. The
# marketplaces know this and refuse to deliver to a pool below their floor:
# Braiins wants at least 1024 and recommends 65536, NiceHash requires 500000.
#
# Vardiff cannot bridge the gap. It moves by at most 4x per window, so at the
# 30s default it takes eight windows — four minutes — to climb from 1 to
# 65536, and the reject flood on the way there is what gets an order
# cancelled. The miner has to arrive at the right difficulty, which is what a
# dedicated port is for.
#
# listener = port=3335 min_diff=65536 label=braiins
# listener = port=3336 min_diff=500000 label=nicehash
#
# IMPORTANT — min_diff is KEPT even when the chain is easier than it, and
# that costs blocks. Share difficulty is otherwise capped at the network
# difficulty, because a miner filters locally against the stratum target and
# a harder share target makes it discard valid blocks before the pool ever
# sees them. min_diff overrides that cap, on purpose: a marketplace measures
# the difficulty on the wire, so a 500000 port that quietly served 1200 would
# have its order cancelled and the pool would never learn why.
#
# The trade is therefore explicit and confined to the ports that ask for it.
# On a 500000 port over a chain at 1200, miners discard roughly 416 of every
# 417 blocks they solve. simplepool warns at startup for every listener in
# that position, and the dashboard reports it too (the "Stratum ports can
# hold their difficulty" health check). If keeping every block matters more
# to you than serving rented hashrate on that port, drop its min_diff — the
# cap then applies as it always has.
#
# listen_port and any listener without a min_diff are untouched by all of
# this: no floor, cap as before.
# Ceiling on mining.submit per second, per connection. 0 disables it.
#
# A connection's share rate is its hashrate divided by the difficulty it was
# assigned, and nothing stops those from being badly mismatched — an
# aggregated fleet pointed at a home-miner port is 1 PH/s against difficulty
# 1, which is ~232,000 submits per second down one socket. Validating a submit
# costs about 9 microseconds, so that one connection asks for more than two
# cores, and every share it lands passes through the store's ring, which drops
# events once full: work a miner was told was accepted, never credited.
#
# The default is far above anything a correctly configured miner reaches. With
# vardiff on, the steady state is vardiff_target_spm — 0.2/s at the default —
# for a connection of ANY size, because that is what vardiff converges on. The
# only rates that come near this ceiling are transients while vardiff climbs,
# and they only get near it when the difficulty is already badly wrong. For
# scale, the largest plausible legitimate burst — 1 EH/s at difficulty
# 65536, before vardiff has raised it — is about 3,500/s.
#
# Raise it if you pin a low difficulty for a very large miner
# (vardiff_enabled = 0), since then a high sustained rate is the correct
# behaviour rather than a symptom.
max_submits_per_sec = 20000
# Budget for mining.authorize. A failed authorize is the cheapest write on the
# pool -- a reject observation and a log line -- and until this existed nothing
# bounded how many of them one client could buy before authenticating at all.
#
# The third failure on a connection is answered and the connection is then
# closed. An address that has failed three times inside the lockout window is
# refused before its next attempt is even parsed, until the window passes. A
# successful authorize forgives the address, so a miner that fixes a typo is
# not made to wait.
#
# 0 disables both halves. A correct miner never reaches either.
auth_max_failures = 3
auth_fail_lockout_sec = 60
# Idle-connection reaper, in seconds. Two budgets, because the two states are
# not the same risk:
#
# idle_timeout_sec a socket that has not authorized. It has told
# us nothing and costs an fd for nothing.
# idle_timeout_authorized_sec a miner that HAS authorized. Having nothing to
# say is its normal resting state — the pool
# asks it nothing between shares — so a small
# rig that has not cleared its assigned
# difficulty yet is silent but perfectly
# healthy. Reaping it on the short budget
# disconnects working hashrate, which is one of
# the behaviours marketplaces delist pools for.
#
# TCP keepalive (2 min idle + 3x30s probes) already reaps a socket whose peer
# is genuinely gone, so the long budget only has to catch a peer answering
# keepalives while doing no work. Negative disables either one.
idle_timeout_sec = 600
idle_timeout_authorized_sec = 7200
db_path = ./data/shares.db
commit_window_ms = 100
commit_max_shares = 100
# Days of template history to keep on the /templates page. One row is kept per
# materially distinct template, so this is roughly one row per block plus one
# per source/commitment change — set 0 to keep everything. Only the dashboard
# reads this table; the ledger lives in shares and rate_history.
templates_retention_days = 30
# Redis broadcast (optional). When set, accepted shares / rejects / blocks /
# tip changes are PUBLISHed on channels pool:shares / pool:rejects /
# pool:blocks / pool:tip as JSON. SQLite remains the source of truth — the
# broadcast is fire-and-forget. Leave empty to disable.
# redis_url = redis://127.0.0.1:6379
redis_url =
redis_publish_timeout_ms = 200
redis_reconnect_backoff_ms = 2000
# pool_mode controls the coinbase shape and the username validation:
# solo default. Coinbase pays the miner (stratum username is a
# BTC address) minus the operator fee. No PPS accrual.
# pplns-thunder / pplns-btc
# Pay Per Last N Shares. The coinbase pays pool_btc_address, as
# in pps-classic, but nothing is credited when a share arrives.
# When a block matures, the reward and its transaction fees are
# split across everyone whose shares fall inside the last-N
# window, in proportion to the difficulty each contributed.
#
# The difference that matters to an operator is who carries the
# variance. PPS guarantees a price per share, so a run of bad
# luck is the operator's problem and has to be absorbed by a
# reserve measured in block rewards. PPLNS never owes more than
# it has just been paid, so there is no reserve to size and
# operator ruin is not a failure mode. The miners carry the
# variance instead, which is why the fee is normally set lower
# than under PPS -- there is no risk premium to charge.
#
# The suffix picks the payout rail, and it is one knob rather
# than two because a pool runs one or the other: the rail
# decides what a stratum username IS. pplns-thunder takes
# Thunder addresses and pays over Thunder, exactly as
# pps-classic does. pplns-btc takes Bitcoin addresses and pays
# on L1.
#
# pplns-btc needs the bip300301_enforcer running with
# --enable-wallet, and pool_btc_address set to an address from
# that wallet (WalletService/CreateNewAddress). The pool holds
# no keys and builds no transactions: the coinbase pays into
# the enforcer's wallet, and paying miners is one RPC to
# WalletService/SendTransaction, which selects the inputs,
# signs and broadcasts. The payout worker needs PAYOUT_RAIL=btc
# and ENFORCER_RPC_ADDR to match — see payout/README.md.
#
# pps-classic Coinbase pays a pool BTC address (pool_btc_address below)
# as a normal P2WPKH/P2PKH output. Miners authorize with a
# Thunder address (base58 of a 20-byte hash) and accrue in
# pps_credits. The operator batches accumulated BTC into
# Thunder from the admin dashboard, and the payout worker
# drains that reserve to miners.
pool_mode = solo
# pps-classic and both pplns modes — required
# pool_btc_address = REPLACE_WITH_POOL_BTC_ADDRESS
# pplns — the window, as a multiple of the CURRENT network difficulty.
# 2.0 means "the last two blocks' worth of expected work".
#
# A multiple rather than an absolute share count or difficulty sum, because it
# self-scales across retargets. An absolute window silently changes meaning
# every time the chain retargets: on a forknet moving 4x it becomes four times
# longer or shorter than the operator chose, with nothing in the config having
# changed to say so.
#
# Below 1.0 the window covers less work than a block is expected to take, so a
# block pays out across less work than it took to find. That rewards whoever
# happened to be connected at the moment over the work that actually produced
# the block -- which is the pool-hopping incentive PPLNS exists to remove. The
# proxy warns if you set it there.
# pplns_window_diff_multiple = 2.0
# pplns-coinbase — the byte budget for the WHOLE serialized coinbase, which is
# what actually limits how many miners one block can pay.
#
# Not a consensus limit. Consensus bounds the coinbase by block weight and
# would allow far more; the real constraint is that rented-hashrate
# marketplaces verify the coinbase and refuse a job whose coinbase they
# consider oversized, and a delisting costs more than paying a few small
# miners a block later.
#
# Counted in bytes rather than in outputs because outputs are not the only
# thing spending them. On a drivechain pool the binding term is the BIP300/301
# commitment OP_RETURNs the enforcer's template already carries: a
# coinbase-direct pool in production reports the same 16 payouts costing 817
# bytes against four of them and 769 against three. A cap counted in outputs
# cannot see that; this can, because the commitments are simply part of what
# has already been spent.
#
# Whatever does not fit is redistributed across the miners it could pay — see
# pplns_payout_floor_sats below.
#
# Settable PER LISTENER, and usually should be. The ceiling that actually binds
# is a marketplace rule enforced on the port rented hashrate connects to, and
# every byte of it costs a payout: at 400 bytes a 100-miner window pays 9, at
# 3000 it pays 93. There is no reason to make your own miners live under a
# limit their port is not measured against:
#
# coinbase_max_bytes = 3000
# listener = port=3335 label=rental min_diff=500000 initial_diff=500000 max_coinbase_bytes=900
#
# A listener that sets none uses the server-wide value below.
# coinbase_max_bytes = 1000
# pplns-coinbase — the payout floor, in satoshis. A miner whose share of a
# block is worth less than this gets no output in THAT block.
#
# What it was owed is not lost and does not go to you: it is shared out among
# the miners that block could pay, and the skipped miner goes first in the
# queue for the next one. You take your fee and nothing else, at every byte
# budget.
#
# That queue is `pplns_fractions` in the database — a signed fraction of one
# block reward per worker, positive if skipped and negative if paid early out
# of someone else's skipped share, summing to zero. A quarter of every
# coinbase's payout slots are reserved for whoever has waited longest, because
# a miner's share of the window tracks its hashrate: without that, the largest
# claims take the same slots in every block and the same addresses are never
# paid at all.
#
# It is NOT a balance and you hold nothing against it. Nothing is ever withheld
# from a coinbase and released later — that would need a block paying less than
# the reward followed by one paying more, and the second is invalid. Delete the
# table and nobody is owed a payment; the pool just forgets whose turn it was.
# Rows are staged when a block is found and applied only once it confirms, so
# an orphaned block rotates nobody. Confirmed means one block deep, so a block
# reorged out AFTER that keeps its rotation: one turn out of order, never a
# satoshi, and corrected by the next block found.
#
# So being small here costs a miner FREQUENCY, not money. Say that on your
# pool page — the proxy states the floor at startup, per template, per block,
# and on the dashboard, but only you can put it where a miner looks first.
#
# Clamped up to 546 (the dust limit) — no smaller output is relayable anyway.
# pplns_payout_floor_sats = 546
# pps-classic — OPTIONAL rate override, sats credited per unit of share
# difficulty. Leave it commented out and the proxy derives the rate from each
# block template as (coinbasevalue / network_difficulty) * (1 - fee_bps/1e4).
# That is the recommended setup: fee_bps becomes the single knob controlling
# the fee, and the rate follows difficulty instead of going stale.
#
# If you do set it, the value is used verbatim and treated as ALREADY NET of
# fee — fee_bps is NOT applied on top. A fixed rate drifts as difficulty
# moves and can invert into paying miners more than each share earns, so the
# proxy logs the fee your value actually implies and warns when it disagrees
# with fee_bps. Prefer leaving it unset.
# pps_sats_per_diff = 1000
# pps-classic — READ THIS BEFORE RUNNING PPS ON A NEW CHAIN.
#
# The derived rate is block_value / network_difficulty, which is a share's
# expected value. That is correct only while every share the pool produces has
# a real chance of becoming a block — which needs difficulty to be calibrated
# to hashrate. On a young chain it is not: difficulty starts at 1 and climbs,
# and until it catches up the pool produces solutions far faster than the chain
# can accept blocks. The formula does not know that, and prices every share as
# though it were worth a whole block.
#
# This is not theoretical. A 40 TH/s pool on a forknet that began at difficulty
# 1 accrued 15,561,471 BTC of liability in under four hours, against 943.60 BTC
# it had actually mined. Nothing was paid out only because the payout worker
# had not run yet.
#
# Set the floor to the difficulty at which YOUR pool alone would find one block
# per block interval:
#
# pps_min_network_difficulty = hashrate_in_H/s * block_interval_sec / 2^32
#
# 10 TH/s -> ~1,400,000 100 TH/s -> ~14,000,000
# 40 TH/s -> ~5,600,000 1 PH/s -> ~140,000,000
#
# That is a FLOOR, not a target — you share the chain with other miners, so the
# genuinely safe difficulty is higher. Below it the proxy credits nothing and,
# by default, refuses new miners rather than taking work it will not pay for.
# It resumes on its own once the chain retargets, and logs the difficulty it
# observes to be necessary either way.
#
# 0 (the default) disables the check. That is only safe on a chain whose
# difficulty is already calibrated: mainnet, testnet, signet. The proxy also
# applies an automatic ceiling that caps accrual at what the chain can mint,
# but that needs a minute of hashrate history — it cannot protect the first
# shares after a restart. This floor is what does.
# pps_min_network_difficulty = 5600000
# Target seconds between blocks. 600 for Bitcoin and every chain forked from
# it. Only change this for a chain that genuinely retargets to a different
# interval — it feeds both the floor above and the issuance ceiling.
# block_interval_sec = 600
# While accrual is suspended by the floor, refuse mining.authorize and reject
# submits, so miners point elsewhere instead of hashing uncredited. Default on.
# Turn it off only if the miners are yours and you know they are working for
# nothing.
# pps_refuse_shares_below_min = 1
log_level = info