LithosDocs
ProtocolPayoutsTokenStatsDocs
Start mining
Documentation home
Protocol
  • Lithos Protocol
  • LFSM
    • Rollups
    • NISPs
    • Emissions
    • Mining on Lithos
    • Candidate building
    • Ergo Voting
  • LithosDex
    • Trading with orders
    • Providing liquidity
Run Lithos
  • Startup guide
    • Setting up an Ergo testnet node
  • Configuration
    • Node connection and security
    • Stratum and difficulty
    • Block packages
    • Batching and orders
    • Collateral and emission
    • Wallet and consolidation
    • Statistics and web panels
    • Synchronization and storage
    • Tasks and dispatchers
    • Failure modes
    • Reporting issues
Client API
  • Overview
    • getLithos client information
    • getList UTXO ids for blocks
    • getUTXO ids by block height
    • getBlock info by UTXO id
    • postBlock info for multiple UTXO ids
    • getGet a block's miner set
    • getList all pool blocks
    • getGet Lithos payouts
    • getStatistics overview
    • getMining and payment totals
    • getMining statistics by hour or day
    • getEstimate Lithos hashrate
    • getNetwork difficulty by epoch
    • getThis client's own hashrate
    • getThis client's hashrate over time
    • getThis client's payouts and claims
    • getCollateral inventory
    • getLocal mining activity
    • getCollateral market snapshot
    • getList queue boxes
    • getList the active set
    • getPermit price history
    • getYour locked block rewards
    • postClaim unlocked block rewards
    • postQuote joining the queue
    • postJoin the collateral queue
    • getLithosDex pool state
    • getLithosDex fee vault state
    • getList your outstanding orders
    • postQuote a swap order
    • postPlace a swap order
    • postQuote a deposit order
    • postPlace a deposit order
    • postQuote a redeem order
    • postPlace a redeem order
    • postCancel an order
    • postQuote a swap
    • postQuote a swap by desired output
    • postExecute a swap
    • postQuote a liquidity deposit
    • postDeposit liquidity
    • postQuote a liquidity redemption
    • postRedeem liquidity
    • getList liquidity provisions
    • postClaim settled fees for a provision
    • postQuote a provision resize
    • postResize a provision
    • postCheck a pending fee flush
    • postFlush pending fees to the vault
    • getPool-wide fee history
    • getPool price history
    • getRecent pool activity
    • getFee history for one provision
    • getWallet balances and lending status
    • getGet Stratum information
    • getGet candidate settings
    • getGet difficulty commitment
    • postCommit a difficulty
    • getGet best NISP at height
Lithos

A decentralized mining pool protocol on Ergo. Miners build their own blocks; contracts pay them for proven work.

Live on Ergo mainnet
Protocol
  • How Lithos works
  • Mining on Lithos
  • NISPs
  • Emissions
  • LFSM contracts
Run Lithos
  • Startup guide
  • Configuration
  • LithosDex
  • Client API
  • Failure modes
Network
  • Live stats
  • Public stats API
  • Ergo Platform↗
  • SOAT Miner↗
Community
  • Telegram↗
  • X / Twitter↗
  • GitHub↗
Lithos
© 2026 Lithos Protocol. Open source under the MIT licence.λίθος — stone
Docs/Run Lithos/Configuration

Block packages

How the client assembles the blocks it mines, and what each transaction source may contribute.

Advanced13 min readAssumes you know how Ergo blocks are assembled and what a mempool is. Start with Stratum and difficulty if not.

Everything under stratum.candidate controls the blocks this client builds: which collateral box it draws, how long it waits for the genesis transaction, and what other transactions it inserts. This page covers that block and the five transaction sources beneath it.

Inserting transactions is on by default, from every source except storage rent. The settings that control it are in Transaction sources. What the client does with them, and where the revenue they earn goes, is explained in Candidate building.

Collateral selection#

The following settings decide which collateral box your block spends:

SettingDefaultRangeWhat it does
stratum.candidate.collateralPoolSize1001 to 100Collateral boxes kept pre-loaded in memory
stratum.candidate.collateralRefreshInterval60000100 to 3600000 msHow often the collateral set is refreshed. It also refreshes on every new block
stratum.candidate.collateralStrategyhighestFeehighestFee or randomHow a box is drawn from the set
stratum.candidate.clearanceAge7200100 to 14400 blocksWith highestFee, how long a box may stay live before it is drawn ahead of every priority fee

The active set never holds more than 100 boxes protocol-wide, which is why the pool size stops there. Fetching a box at mining time would put an indexer round trip on the critical path of every block, so the whole set is held in memory instead.

The refresh interval is a floor rather than a schedule. Every new block triggers one anyway, since that is the only moment a collateral box can be spent.

Which box your block spends#

The box is chosen afresh at every block height and kept for the rest of that height. With the default highestFee strategy, the client draws one box from the set by the following rules:

  1. If any box has been live for clearanceAge blocks or more, the oldest of those is drawn, whatever the others offer.
  2. Otherwise the box offering the finder the largest priority fee is drawn, and the oldest breaks a tie.

The default of 7200 blocks is about 10 days on mainnet. Until a box reaches it, lenders compete on priority fee alone, which is what makes paying one worth anything. The age rule then clears boxes that offer nothing, so no box waits in the active set forever.

A lower value clears idle boxes sooner and weakens priority fees. At the minimum of 100 blocks, about 3 hours 20 minutes on mainnet, a box that bids nothing outranks every bid after waiting only that long. The maximum of 14400 blocks, about 20 days, lets bids decide for longest. The rule is this client's own choice, not a contract rule, so different miners may run different values.

random draws uniformly from every usable box, ignoring priority fees and age. Because it ignores the bids, it usually earns you a smaller finder's share than highestFee.

Boxes your block never spends#

The client leaves a box out of the draw in the following cases:

CaseWhat happens
The box pays into a rollup chain this release does not buildSkipped with a warning. The chain is named by the box's R8, stamped from the emission config when the box was activated
No box is left after thatBlocks are mined solo, with a warning, until you update the client
The node left the box's genesis transaction out of its candidateSkipped with a warning for up to three blocks, and the next build uses another box. At most three rebuilds per block
The box was spent in a recent blockSkipped for up to three blocks, because the indexer can still report it unspent for a few seconds

A box skipped for the first reason stays skipped, because this release cannot pay into its chain at all. Updating to a release that builds that chain brings it back.

Building the genesis transaction#

SettingDefaultRangeWhat it does
stratum.candidate.genesisWaitMs15000 to 60000 msHow long to wait for candidate sources before a normal genesis transaction is sent
stratum.candidate.waitForBlockPackagetruetrue or falseWait for other sources to contribute before sending the genesis transaction, up to genesisWaitMs

The genesis transaction is what makes a block a Lithos block. The candidate builder will wait for the genesis and other sources to arrive, but on a new height build, it will wait at-most genesisWaitMs before it sends a solo-candidate. If waitForBlockPackage is off, the client will first create the genesis transaction and send it out as a new job, before sending a following job containing package sources. This can potentially slow down mining, especially if the two jobs are sent in quick succession.

Refreshing mid-block#

SettingDefaultRangeWhat it does
stratum.candidate.mempoolRefreshMs200000 to 3600000 msMilliseconds between mid-block package rebuilds. 0 mines the block's initial transaction set and never rebuilds
stratum.candidate.minCandidateChangeRevenue10000000 to 9223372036854775807 nanoERGExtra package revenue required before a mid-block job change is worth making
stratum.candidate.refreshForProtocolTxstruetrue or falseAlso change the job when a rebuilt package adds rollup or emission transactions
stratum.candidate.minNewProtocolTxs11 to 1000Rollup or emission transactions a rebuilt package must add before the job changes
stratum.candidate.blockTxTimeout200000 to 600000 msHow long to wait for sources to build. Not validated separately from its range

A rebuild picks up transactions that arrived since the last one, which can raise what the block earns. Rebuilding too often can slow down your rig's hashrate, so feel free to adjust mempoolRefreshMs in order to control how often rebuilds happen.

minCandidateChangeRevenue is what stops a trivial gain from being worth that window. The client compares a rebuilt package against the published one and keeps the old job unless the difference clears this threshold. Tokens are not valued, so a package whose only gain is tokens never clears it. Initial packages and required fallbacks bypass the threshold entirely.

Rollup and emission transactions earn no ERG, so minCandidateChangeRevenue alone never changes the job for them. With refreshForProtocolTxs on, the client also changes the job when the rebuilt package carries at least minNewProtocolTxs rollup or emission transactions that the current job does not. Unconfirmed transactions carried ahead of them do not count. Each change is a new job for your mining software, and at most one happens per mempoolRefreshMs.

The client keeps the current job, whatever the rebuild adds, when a source that contributed to the current job did not answer the rebuild in time. Switching would drop that source's work from the block.

blockTxTimeout tells you how long the client will wait before sending its best effort candidate to the stratum. In some cases, transaction sources can take a while, so before blockTxTimeout is hit, the candidate builder will collect transactions from the sources that did respond in time, and send just those. Later sources will still show up in refreshes once they respond.

The package budget#

SettingDefaultRangeWhat it does
stratum.candidate.blockShare0.50.0 to 1.0Fraction of the node's block byte and cost limits this client's whole package may claim

The default is well under 1 on purpose. The node adds transactions this client never sees and picks a remainder you have no visibility into, and an unconfirmed ancestor carried into the package reports no execution cost at all. Claiming the whole block would mean the node dropping part of what you built.

Keeping this below 0.8 is recommended. The genesis transaction is charged against the budget first, so it is never the thing that gets dropped.

Note
The share is measured against the node's live limits

A chain sitting at Ergo's floor block cost of 1,000,000 leaves far less room than mainnet's, which is roughly eight times higher. The same blockShare therefore admits very different amounts of work on different chains.

Transaction sources#

SettingDefaultRangeWhat it does
stratum.candidate.blockTransactionstruetrue or false. Not validatedThe master switch. With it off, no source contributes anything

Each source is bounded on its own before the package is bounded as a whole, so a busy source cannot crowd out a quieter one that matters more. A source that is off is never asked, so it costs no build and no node read.

Revenue the package earns goes to the pool, not to you alone. Executor fees and collected storage rent are added to the holding box your block's genesis transaction creates, so every miner who submits to that rollup is paid a share of them by score. You submit to it too.

Every source takes the same four keys under stratum.candidate.sources.<name>:

KeyRangeWhat it does
enabledtrue or falseWhether this source is asked to contribute
maxTxs0 to 100Transactions it may place in one block
maxBytes0 to 8388608Serialized bytes it may claim, counting unconfirmed ancestors it carries
maxCost0 to 100000000Execution cost it may claim

The five sources and what they ship as:

SourceShipsmaxTxsWhat it inserts
rollupson5Non-Interactive Share Proof (NISP) submissions, phase transforms, fraud proofs and payouts
emissionson20Clear and Activate on the collateral queue. Activates alone stop at emission.candidateActivates
lithosdexon20LithosDex order executions, their placements and the closing flush
ergodexon20Native ErgoDEX swaps, deposits and redemptions, including ancestors
rentoff1Storage rent collection

maxBytes and maxCost ship at 262144 and 1000000 for all five.

Warning
Every slot is block space not earning a fee

Transactions you insert pay no transaction fee, because they are yours. That is the point for rollups and emissions, which are protocol work you want in the block regardless. It also means each slot is space that could have held a fee-paying transaction, so keep the counts small. Although fees do not directly pay Lithos miners, failing to include fee paying transactions can reflect in lower fee priority values on collateral boxes, which may reduce your total reward over time.

Rollup work#

The rollups source takes one extra key:

SettingDefaultRangeWhat it does
stratum.candidate.sources.rollups.maxAncestorTxs40 to 64Unconfirmed transactions one rollup transaction may carry into your block ahead of itself

A rollup transaction often spends a box created by a transaction still in the mempool, usually another miner's NISP submission to the same rollup. Your block has to carry that parent too, and every parent counts towards maxTxs. Work that needs more parents than maxAncestorTxs stays out of your block until they confirm, and still reaches the mempool as usual. 0 offers only work that spends a confirmed rollup box.

Rollup work your block carries is not also sent with a fee while that block is mined. Unless a block lands the work first, the client sends every rollup transaction to the mempool with a fee. The following rules decide when:

  • If your block carries the transaction, the fee-paying copy waits while that block is mined.
  • If that block passes without the work landing, the fee-paying copy goes out at the client's next chain check. Checks run once a minute, so this is about one block later.
  • A rollup transaction that appears mid-block waits up to mempoolRefreshMs for the next rebuild, so your block gets it first.
  • NISP submissions and fraud proofs with 10 blocks or fewer left in their window are always sent at once. That is about 20 minutes at Ergo's 2-minute block time.

A transaction that appears mid-block waits only if a rebuild can still bring it into the job. That needs mempoolRefreshMs above 0, and either refreshForProtocolTxs on or minCandidateChangeRevenue at 0. While your client is not building candidates, nothing waits.

A NISP submission in your block posts its bond from one confirmed wallet box worth at least the bond plus 0.001 ERG. If no such box is free, the submission is left to the mempool for that block.

Note
The wait costs one block when you do not find the block

Whenever someone else finds the block, the fee-paying copy goes out about one block later than it otherwise would. With the stratum running and no mining software connected, every rollup transaction waits that block for nothing.

Setting boxes aside for fraud proofs#

One setting funds fraud proofs inside your own block:

SettingDefaultRangeWhat it does
stratum.candidate.pinnedInputs10 to 16Wallet boxes set aside to fund fraud proofs inside your own block

A fraud proof needs one wallet input of yours, and its reward, the slashed bond, is paid to that input's address. In your own block that input is a pinned box: a wallet box the client sets aside and never sends anywhere. Each fraud proof returns the pinned box whole as change, so one pinned box funds every fraud proof a block can carry.

The client pins the smallest boxes that qualify, which are confirmed plain boxes of yours with no tokens and at least 0.001 ERG. It never pins your last such box, so the mempool path always has one. A pinned box stays out of the client's other transactions and out of your reported spendable balance until one of your blocks spends it. The client then pins a replacement. If your wallet cannot supply enough boxes, the client logs a warning once.

pinnedInputs applies only while all four of the following are on:

  • The stratum task, lithos-tasks.stratum-server
  • Rollup processing, which state.disableTransforms = true turns off
  • stratum.candidate.blockTransactions
  • The rollups source

0 turns pinning off. Fraud proofs then reach the chain through the mempool alone.

Storage rent#

Storage rent collects boxes nobody has touched for four years, which Ergo permits anyone to spend. It ships off, and it has three settings of its own:

SettingDefaultRangeWhat it does
stratum.candidate.sources.rent.startHeight8000000 to 100000000Inclusion height the walk begins at
stratum.candidate.sources.rent.scanIntervalMs300001000 to 3600000 msGap between scan passes
stratum.candidate.sources.rent.blocksPerScan2001 to 10000Blocks one pass reads. Two node calls each

Discovery walks the chain by inclusion height rather than reading the whole UTXO set. A box created at height h becomes collectable at h + 1051200, so the walk trails four years behind the tip and every box it meets there is old enough by construction.

Set startHeight to roughly where collection has already caught up, not to zero. The early chain was swept long ago and walking it costs node reads for nothing.

Note
Storage rent cannot be tested on testnet

Testnet tops out well under a storage period, so no box on it is ever collectable. Exercising this source at all needs a private devnet with a shortened storage period.

Intermediate outputs#

SettingDefaultRangeWhat it does
stratum.candidate.useTruePropCollectionfalsetrue or falseGuard the intermediate outputs a candidate creates for its own later transactions with TrueProp instead of your own key

A package often creates an output that a later transaction in the same package spends. Guarding those with TrueProp rather than your P2PK is cheaper in both validation cost and byte size, which lets more transactions fit in the block.

Danger
An exposed intermediate output can be taken by anyone

TrueProp means exactly what it says: anything can spend it. That is safe only while every intermediate is consumed inside the same block by a transaction that definitely lands. If a package fails part way, an intermediate output guarded this way is free for anyone to take.

Logging#

SettingDefaultRangeWhat it does
stratum.candidate.logTimingsfalsetrue or falseAppend how long each stage took to the candidate log lines
stratum.candidate.logBudgetsfalsetrue or falseLog package and source bytes and cost against their budgets

Both are diagnostic and ship off. logTimings breaks a candidate down into building the genesis transaction, collecting the extra transactions, and the wait before miners are given the job, which is what to turn on when a block seems slow to reach your rigs. logBudgets shows which source is using the package budget, and what was refused for want of room.

Next steps#

  • Candidate building: how a package is assembled, and what happens when part of it fails.
  • Batching and orders: the two sources that fill other people's orders.
  • Collateral and emission: what the emissions source is doing.
Edit this page on GitHubsynced from e0ca86a
← PreviousStratum and difficultyNext →Batching and orders
On this page
  • Collateral selection
  • Which box your block spends
  • Boxes your block never spends
  • Building the genesis transaction
  • Refreshing mid-block
  • The package budget
  • Transaction sources
  • Rollup work
  • Setting boxes aside for fraud proofs
  • Storage rent
  • Intermediate outputs
  • Logging
  • Next steps