Skip to content

Commit a5b230a

Browse files
committed
doc: explain Stratum v2 design, testing and usage
1 parent 2dc454a commit a5b230a

File tree

2 files changed

+335
-1
lines changed

2 files changed

+335
-1
lines changed

doc/stratum-v2.md

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
# Stratum v2
2+
3+
## Design
4+
5+
The Stratum v2 protocol specification can be found here: https://stratumprotocol.org/specification
6+
7+
Bitcoin Core (or a sidecar application connected to it via IPC) performs the
8+
Template Provider role, and for that it implements the Template Distribution
9+
Protocol. When launched with `-sv2` we listen for connections from either a
10+
Job Declarator client (JDF) or a Pool (pool default template or for solo mining).
11+
12+
A JDC probably runs on the same machine. A different possible use case is where
13+
a miner relies on a node run by someone else to provide the templates. This is
14+
currently not safe for the node operator, see the section on DoS.
15+
16+
The Template Provider send the JDC (or Pool) a new block template whenever our
17+
tip is updated, or when mempool fees have increased sufficiently. If the pool
18+
finds a block, we attempt to broadcast it based on a cached template.
19+
20+
Communication with other roles uses the Noise Protocol, which has been implemented
21+
to the extent necessary.
22+
23+
### Advantage over getblocktemplate RPC
24+
25+
Although under the hood the Template Provider uses `CreateNewBlock()` just like
26+
the `getblocktemplate` RPC, there's a number of advantages in running a
27+
server with a stateful connection, and avoiding JSON RPC in general.
28+
29+
1. Stateful, so we can have back-and-forth, e.g. requesting transaction data,
30+
processing a block solution.
31+
2. Less (de)serializing and data sent over the wire, compared to plain text JSON
32+
3. Encrypted, safer (for now: less unsafe) to expose on the public internet
33+
4. Push based: new template is sent immediately when a new block is found rather
34+
than at the next poll interval. Combined with Cluster Mempool this can
35+
hopefully be done for higher fee templates too.
36+
5. Low friction deployment with other Stratum v2 software / devices
37+
38+
### Message flow(s)
39+
40+
See the [Message Types](https://stratumprotocol.org/specification/08-Message-Types/)
41+
and [Protocol Overview](https://stratumprotocol.org/specification/03-Protocol-Overview/)
42+
section of the spec for all messages and their details.
43+
44+
When a Job Declarator client connects to us, it first sends a `SetupConnection`
45+
message. We reply with `SetupConnection.Success` unless something went wrong,
46+
e.g. version mismatch, in which case we reply with `SetupConnection.Error`.
47+
48+
Next the client sends us their `CoinbaseOutputConstraints`. If this is invalid
49+
we disconnect. Otherwise we start the cycle below that repeats with every block.
50+
51+
We send a `NewTemplate` message with `future_template` set `true`, immedidately
52+
followed by `SetNewPrevHash`. We _don't_ send any transaction information
53+
at this point. The Job Declarator client uses this to announce upstream that
54+
it wants to declare a new template.
55+
56+
In the simplest setup with SRI the Job Declarator client doubles as a proxy and
57+
sends these two messages to all connected mining devices. They will keep
58+
working on their previous job until the `SetNewPrevHash` message arrives.
59+
Future implementations could provide an empty or speculative template before
60+
a new block is found.
61+
62+
Meanwhile the pool will request, via the Job Declarator client, the transaction
63+
lists belonging to the template: `RequestTransactionData`. In case of a problem
64+
we reply with `RequestTransactionData.Error`. Otherwise we reply with the full[0]
65+
transaction data in `RequestTransactionData.Success`.
66+
67+
When we find a template with higher fees, we send a `NewTemplate` message
68+
with `future_template` set to `false`. This is _not_ followed by `SetNewPrevHash`.
69+
70+
Finally, if we find an actual block, the client sends us `SubmitSolution`.
71+
We then lookup the template (may not be the most recent one), reconstruct
72+
the block and broadcast it. The pool will do the same.
73+
74+
`[0]`: When the Job Declarator client communicates with the Job Declarator
75+
server there is an intermediate message which sends short transaction ids
76+
first, followed by a `ProvideMissingTransactions` message. The spec could be
77+
modified to introduce a similar message here. This is especially useful when
78+
the Template Provider runs on a different machine than the Job Declarator
79+
client. Erlay might be useful here too, in a later stage.
80+
81+
### Noise Protocol
82+
83+
As detailed in the [Protocol Security](https://stratumprotocol.org/specification/04-Protocol-Security/)
84+
section of the spec, Stratum v2 roles use the Noise Protocol to communicate.
85+
86+
We only implement the parts needed for inbound connections, although not much
87+
code would be needed to support outbound connections as well if this is required later.
88+
89+
The spec was written before BIP 324 peer-to-peer encryption was introduced. It
90+
has much in common with Noise, but for the purposes of Stratum v2 it currently
91+
lacks authentication. Perhaps a future version of Stratum will use this. Since
92+
we only communicate with the Job Declarator role, a transition to BIP 324 would
93+
not require waiting for the entire mining ecosystem to adopt it.
94+
95+
An alternative to implementing the Noise Protocol in Bitcoin Core is to use a
96+
unix socket instead and rely on the user to install a separate tool to convert
97+
to this protocol. This approach is implemented in https://github.com/Sjors/bitcoin/pull/48,
98+
but could also be provided as part of SRI.
99+
100+
### Mempool monitoring
101+
102+
The current design calls `CreateNewBlock()` internally every `-sv2interval` seconds.
103+
We then broadcast the resulting block template if fees have increased enough to make
104+
it worth the overhead (`-sv2feedelta`). A pool may have additional rate limiting in
105+
place.
106+
107+
This is better than the Stratum v1 model of a polling call to the `getblocktemplate` RPC.
108+
It avoids (de)serializing JSON, uses an encrypted connection and only sends data
109+
over the wire if fees increased.
110+
111+
But it's still a poll based model, as opposed to the push based approach
112+
whenever a new block arrives. It would be better if a new template is generated
113+
as soon as a potentially revenue-increasing transaction is added to the mempool.
114+
The Cluster Mempool project might enable that.
115+
116+
### DoS and privacy
117+
118+
The current Template Provider should not be run on the public internet with
119+
unlimited access. It is not hardened against DoS attacks, nor against mempool probing.
120+
121+
There's currently no limit to the number of Job Declarator clients that can connect,
122+
which could exhaust memory. There's also no limit to the amount of raw transaction
123+
data that can be requested.
124+
125+
Templates reveal what is in the mempool without any delay or randomization.
126+
127+
Future improvements should aim to reduce or eliminate the above concerns such
128+
that any node can run a Template Provider as a public service.
129+
130+
## Usage
131+
132+
Using this in a production environment is not yet recommended, but see the testing guide below.
133+
134+
### Build
135+
136+
Follow the instructions in [build-unix.md](build-unix.md), [build-osx.md](build-osx.md).
137+
138+
### Parameters
139+
140+
See also `bitcoind --help`.
141+
142+
Start Bitcoin Core with `-sv2` to start a Template Provider server with default settings.
143+
The listening port can be changed with `-sv2port`.
144+
145+
By default it only accepts connections from localhost. This can be changed
146+
using `-sv2bind`. See DoS and Privacy above.
147+
148+
Use `-debug=sv2` to see Stratum v2 related log messages. Set `-loglevel=sv2:trace`
149+
to see which messages are exchanged with the Job Declarator client.
150+
151+
The frequency at which new templates are generated can be controlled with
152+
`-sv2interval`. The new templates are only submitted to connected clients if
153+
they are for a new block, or if fees have increased by at least `-sv2feedelta`.
154+
155+
You may increase `-sv2interval`` to something your node can handle, and then
156+
adjust `-sv2feedelta` to limit back and forth with the pool.
157+
158+
## Testing Guide
159+
160+
Unfortunately testing still requires quite a few moving parts, and each setup has
161+
its own merits and issues.
162+
163+
To get help with the stratum side of things, this Discord may be useful: https://discord.gg/fsEW23wFYs
164+
165+
The Stratum Reference Implementation (SRI) provides example implementations of
166+
the various (other) Stratum v2 roles: https://github.com/stratum-mining/stratum
167+
168+
You can set up an entire pool on your own machine. You can also connect to an
169+
existing pool and only run a limited set of roles on your machine, e.g. the
170+
Job Declarator client and Translator (v1 to v2).
171+
172+
SRI includes a v1 and v2 CPU miner, but at the time of writing neither seems to work.
173+
Another CPU miner that does work, when used with the Translator: https://github.com/pooler/cpuminer
174+
175+
### Regtest
176+
177+
TODO
178+
179+
This is also needed for functional test suite coverage. It's also the only test
180+
network doesn't need a standalone CPU miner or ASIC.
181+
182+
Perhaps a mock Job Declarator client can be added. We also need a way mine a given
183+
block template, akin to `generate`.
184+
185+
To make testing easier it should be possible to use a connection without Noise Protocol.
186+
187+
### Testnet
188+
189+
The difficulty on testnet4 varies wildly, but typically much too high for CPU mining.
190+
Even when using a relatively cheap second hand miner, e.g. an S9, it could take
191+
weeks to find a block.
192+
193+
The above means it's difficult to test the `SubmitSolution` message.
194+
195+
#### Bring your own ASIC, use external testnet pool
196+
197+
This uses an existing testnet pool. There's no need to create an account anywhere.
198+
The pool does not pay out the testnet coins it generates. It also currently
199+
doesn't censor anything, so you can't test the (solo mining) fallback behavior.
200+
201+
First start the node:
202+
203+
```
204+
build/bin/bitcoind -testnet4 -sv2 -debug=sv2
205+
```
206+
207+
Build and run a Job Declarator client: [stratum-mining/stratum/tree/main/roles/jd-client](https://github.com/stratum-mining/stratum/tree/main/roles/jd-client
208+
209+
This client connects to your node to receive new block templates and then "declares"
210+
them to a Job Declarator server. Additionally it connects to the pool itself.
211+
212+
Copy [jdc-config-hosted-example.toml](https://github.com/stratum-mining/stratum/blob/main/roles/jd-client/config-examples/jdc-config-hosted-example.toml)
213+
to e.g. `~/.stratum/testnet4-jdc.toml`, change `tp_address` to `127.0.0.1:48336` and comment out `tp_authority_public_key`.
214+
215+
The `coinbase_outputs` is used for fallback to solo mining. Generate an address
216+
of any type and then use the `getaddressinfo` RPC to find its public key.
217+
218+
Finally you most likely need to use the v1 to v2 translator: [stratum-mining/stratum/tree/main/roles/translator](https://github.com/stratum-mining/stratum/tree/main/roles/translator),
219+
even when you have a stratum v2 capable miner (see notes on ASIC's and Firmware below).
220+
221+
You need to point the translator to your job declarator client, which in turn takes
222+
care of connecting to the pool. Try [tproxy-config-local-jdc-example.toml](https://github.com/stratum-mining/stratum/blob/main/roles/translator/tproxy-config-local-jdc-example.toml).
223+
224+
As soon as you turn on the translator, the Bitcoin Core log should show a `SetupConnection` [message](https://github.com/stratum-mining/sv2-spec/blob/main/08-Message-Types.md).
225+
226+
Now point your ASIC to the translator. At this point you should be seeing
227+
`NewTemplate`, `SetNewPrevHash` and `SetNewPrevHash` messages.
228+
229+
If the pool is down, notify someone on the above mentioned Discord.
230+
231+
### SRI Signet
232+
233+
Unlike testnet4, signet(s) use the regular difficulty adjustment mechanism.
234+
Although the default signet has very low difficulty, you can't mine on it,
235+
because to do so requires signing blocks using a private key that only two people have.
236+
237+
The SRI team operates a signet that does not require signatures:
238+
239+
```ini
240+
[signet]
241+
signetchallenge=51 # OP_TRUE
242+
connect=75.119.150.111
243+
```
244+
245+
Unlike regtest this network does have difficulty (adjustment). This allows you to
246+
test if e.g. pool software correctly sets and adjusts the share difficulty for each
247+
participant. Although for the Template Provider role this is not relevant.
248+
249+
You can also create your own custom unsigned signet using the above configuration,
250+
just make to _not_ connect to the SRI signet because it might reorg your own chain.
251+
252+
See [signet/README.md](../contrib/signet/README.md).
253+
254+
#### Mining
255+
256+
The cleanest setup involves two connected nodes, each with their own data
257+
directory: one for the pool and one for the miner. By selectively breaking the
258+
connection you can inspect how unknown transactions in the template are requested
259+
by the pool, and how a newly found block is submitted is submitted both by the
260+
pool and the miner.
261+
262+
However things should work fine with just one node.
263+
264+
Start the miner node first, with a GUI for convenience:
265+
266+
```sh
267+
build/bin/qt/bitcoin-qt -datadir=$HOME/.stratum/bitcoin -signet
268+
```
269+
270+
Suggested config for the pool node:
271+
272+
```ini
273+
[signet]
274+
signetchallenge=51 # OP_TRUE
275+
server=0
276+
listen=0
277+
connect=75.119.150.111 # SRI
278+
```
279+
280+
The above disables its RPC server and p2p listening to avoid a port conflict.
281+
282+
Start the pool node:
283+
284+
```sh
285+
build/bin/bitcoind -datadir=$HOME/.stratum/bitcoin-pool -signet
286+
```
287+
288+
Configure an SRI pool:
289+
290+
```
291+
cd roles/pool
292+
mkdir -p ~/.stratum
293+
cp
294+
```
295+
296+
Start the SRI pool:
297+
298+
```sh
299+
cargo run -p pool_sv2 -- -c ~/.stratum/signet-pool.toml
300+
```
301+
302+
For the Job Declarator _client_ and Translator, see Testnet above.
303+
304+
Now use the [CPU miner](https://github.com/pooler/cpuminer) and point it to the translator:
305+
306+
```
307+
./minerd -a sha256d -o stratum+tcp://localhost:34255 -q -D -P
308+
```
309+
310+
You can also a BitAxe, but please do so only for a few minutes at a time
311+
to prevent increasing the difficulty too much for others.
312+
313+
### Mainnet
314+
315+
See testnet for how to use an external pool. See signet for how to configure your own pool.
316+
317+
Pools that support Stratum v2 on mainnet:
318+
319+
* Braiins: unclear if they are currently compatible with latest spec. URL's are
320+
listed [here](https://academy.braiins.com/en/braiins-pool/stratum-v2-manual/#servers-and-ports). There's no Job Declarator server.
321+
* DEMAND : No account needed for solo mining. Both the pool and Job Declarator
322+
server are at `dmnd.work:2000`. Requires a custom SRI branch, see [instructions](https://dmnd.work/#solo-mine).
323+
324+
### Notes on ASIC's and Firmware:
325+
326+
#### BraiinsOS
327+
328+
* v22.08.1 uses an (incompatible) older version of Stratum v2
329+
* v23.12 is untested (and not available on S9)
330+
* v22.08.1 when used in Stratum v1 mode, does not work with the SRI Translator
331+
332+
#### Antminer stock OS
333+
334+
This should work with the Translator, but has not been tested.

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
681681
argsman.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
682682
argsman.AddArg("-coinbaselocktime", strprintf("Set nLockTime to the current block height and nSequence to enforce it (default: %d)", DEFAULT_COINBASE_LOCKTIME), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
683683
#ifdef WITH_SV2
684-
argsman.AddArg("-sv2", "Bitcoind will act as a Stratum v2 Template Provider (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
684+
argsman.AddArg("-sv2", "Bitcoind will act as a Stratum v2 Template Provider, see doc/stratum-v2.md (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
685685
argsman.AddArg("-sv2interval", strprintf("Template Provider block template update interval (default: %d seconds)", Sv2TemplateProviderOptions().fee_check_interval.count()), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
686686
argsman.AddArg("-sv2feedelta", strprintf("Minimum fee delta for Template Provider to send update upstream (default: %d sat)", uint64_t(Sv2TemplateProviderOptions().fee_delta)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
687687
#endif

0 commit comments

Comments
 (0)