Tasks and dispatchers
Committing your difficulty, background tasks, thread pools, and the switch that disables protocol work.
Three blocks are left. state holds two switches: one that commits your difficulty on chain and one
that turns protocol work off. lithos-tasks configures the three background timers, and
lithos-contexts defines the thread pools everything runs on.
The dispatcher pools in particular encode a design decision rather than a preference, and the page says which ones you must not merge.
Protocol switches#
| Setting | Default | Range | What it does |
|---|---|---|---|
state.autoCommit | false | true or false | Register this miner and commit stratum.diff on chain, then keep the commitment equal to it |
state.disableTransforms | false | true or false | Disable every transaction and interaction with LFSM rollups |
disableTransforms is a blunt instrument. With it on the client follows the chain but submits nothing:
no NISPs, no phase transforms, no fraud proofs, no payouts, and no commitments. You mine and are not
paid. This can be useful for testing.
Committing your difficulty#
A miner is paid from a rollup only after two things are on chain: a registration in the Miner Dictionary, the on-chain registry of Lithos miners, and a difficulty commitment, the score your NISPs are judged against. Your first difficulty commitment also registers you.
You commit from the Commitment tab under Mining, Difficulty in the web interface, or with
POST /mining/commitment. autoCommit is the alternative: the client commits stratum.diff for you.
It ships off, so a new client never writes a difficulty you are still testing. While it is on, the
Commitment tab and POST /mining/commitment refuse to commit, so only one of them ever changes
your difficulty. While you are not registered, the client mines normally but cannot submit NISPs, and
the log says so at startup.
With autoCommit on, the dictionary task checks every minute, starting about a minute after startup:
- If you are not registered, the client registers you with
stratum.diffas your first commitment. Registration waits until the Miner Dictionary has synced. - If you are registered and your newest commitment differs from
stratum.diff, the client replaces it as soon as the contracts allow. - After sending either transaction, the client waits for it to confirm and for its own sync to reach that block, then checks again.
- Once the confirmed commitment equals
stratum.diff, the loop stops until the next restart.
The contracts decide how fast a difficulty commitment moves, however it is sent. The following counts blocks from the moment it is sent:
| Event | Blocks after sending | Testnet | Mainnet |
|---|---|---|---|
| The commitment binds, and NISPs are judged against it | 125 | about 1 hour 35 minutes | about 4 hours 10 minutes |
| The commitment may be replaced by a new one | 845 | about 10 hours 30 minutes | about 28 hours |
A registration or change has 5 blocks to be included. If it misses them it becomes invalid, and the client sends a new one on a later check.
While the loop is waiting, it logs the reason once, as Auto-commit waiting on ..., and again only when
the reason changes. autoCommit does nothing while disableTransforms is true or the dictionary task is
disabled.
Background tasks#
Three tasks run on timers. Each takes the same three keys, and all three are required for each task:
| Key | Range | What it does |
|---|---|---|
enabled | true or false | Whether the task runs |
startup | 1 to 604800000 ms | Delay before its first run |
interval | 1 to 604800000 ms | Gap between runs |
The three tasks and what they ship as:
| Task | Enabled | Startup | Interval | What it does |
|---|---|---|---|---|
lithos-tasks.stratum-server | true | 30 seconds | 30 seconds | Runs the stratum server that your mining software connects to |
lithos-tasks.rollup-sync-task | true | 10 seconds | 500 millis | Drives rollup phase work against current state |
lithos-tasks.dictionary-sync-task | true | 60 seconds | 60 seconds | Registers and commits when state.autoCommit is on, and warns before your registration expires |
Durations take any form the config library parses, such as 500 millis, 30 seconds or 5 minutes.
The dictionary task runs at most once a minute, so a shorter interval there is raised to one minute.
A missing task block stops the client rather than defaulting, because a task silently not running is worse than a refusal to start. The one exception is a lookup that only picks a log message, which reads an absent block as disabled.
Dispatchers#
lithos-contexts defines every thread pool the client uses. Startup checks that each named block
exists and leaves the contents to Akka, so a missing block is caught and a bad pool size is not.
The client requires all twenty-three of these blocks:
| Dispatcher | Pool | Serves |
|---|---|---|
wallet-io-dispatcher | 1 thread, queue 4 | Candidate requests, which change the node's cached work |
wallet-maintenance-dispatcher | 2 threads, queue 8 | Inventory scans and reward sweeps |
critical-wallet-dispatcher | 1 thread, queue 4 | Wallet work that must not queue behind optional work |
critical-tx-dispatcher | 2 threads, queue 4 | Transactions that must not be delayed |
engine-candidate-dispatcher | 2 threads, queue 4 | Engine work for candidate assembly |
engine-io-dispatcher | 1 thread, queue 4 | General engine node I/O |
mempool-io-dispatcher | 1 thread, queue 4 | Mempool reads |
candidate-io-dispatcher | 1 thread, queue 4 | Candidate node I/O |
mining-control-io-dispatcher | 2 threads, queue 4 | One chain observation and one solved-block submission, concurrently |
genesis-dispatcher | 1 thread, queue 4 | Collateral genesis signing |
batching-io-dispatcher | 2 threads, queue 4 | ErgoDEX order discovery, broadcasts and builds |
lithosdex-batching-io-dispatcher | 2 threads, queue 4 | The same for LithosDex |
snapshot-io-dispatcher | 4 threads, queue 4 | Snapshot save, restore and dictionary reconstruction |
stratum-dispatcher | fork-join, 4 to 16 | The stratum server |
polling-dispatcher | 32 threads | Node polling |
sync-dispatcher | fork-join, 2 to 10 | Synchronization |
tx-dispatcher | fork-join, 2 to 10 | General transaction work |
dex-dispatcher | 16 threads | The local LithosDex HTTP endpoints |
database-dispatcher | 2 threads | Local database work |
stats-dispatcher | 1 thread | The statistics collector behind the web panels |
stats-read-dispatcher | 1 thread, queue 1 | LithosDex statistics reads from the node |
stats-store-dispatcher | 1 thread, queue 1 | Writing LithosDex statistics to disk |
mining-stats-dispatcher | 1 thread, queue 1 | Mining statistics: node reads and their history on disk |
A top-level stats-mailbox block also bounds the statistics collector's mailbox at 256 messages, so
a slow statistics read drops observations rather than growing memory. See
Statistics and web panels.
Why they are separate#
The separation is the point, and three of these exist because merging them cost blocks.
A found block must never queue behind node I/O. mining-control-io-dispatcher has two threads so
that one chain observation and one solved-block submission can run at once, and neither waits for
candidate HTTP, wallet work or transaction preparation. An early version ran a rollup batch inside the
same mailbox that answers block assembly, and every block in flight silently lost its inserted
transactions with nothing in the log connecting the two.
Candidate requests are serialised on purpose. wallet-io-dispatcher has exactly one thread because
asking the node for a candidate replaces the node's cached work. Two concurrent requests would each
mutate a cache the other depends on. Optional transaction workers cannot occupy that thread.
Genesis signing has its own worker so that background collateral refresh and ordinary transaction work cannot occupy it while cached inputs are ready for a new block.
Snapshot work blocks in LevelDB, sometimes for a long time. Four threads cover the one save, one restore and two dictionary reconstructions the actor may own, and the bounded queue contains work left behind by an unexpected restart when a backend call cannot be interrupted.
Next steps#
- Configuration overview: how a missing block is reported at startup.
- Synchronization and storage: what
snapshot-io-dispatcheris doing. - Statistics and web panels: what the four statistics dispatchers serve.