Docs
v1

Guide

Run a mining pool

Set up tenrai-pool, the Tenrai reference pool, step by step with a node and a wallet daemon. Every option, fees, payouts and the API.

On this page

tenrai-pool is the Tenrai reference pool. It runs PPLNS and SOLO ports over stratum, credits rewards from the node's finalized cuts, pays miners through the wallet daemon and serves a website with a page per payout address.

What runs where

All three programs run on one machine.

ProgramRole
tenraidSynced node. The pool uses its mining listener 127.0.0.1:28702 and API 127.0.0.1:28701
tenrai-wallet daemonThe pool wallet. Its first address receives every block reward and pays the miners
tenrai-poolStratum ports, accounting, payouts, website
A pool server. Miners connect to the stratum ports of tenrai-pool. Visitors and web miners reach the pool through nginx on port 443. tenrai-pool takes work from tenraid and submits blocks through it, and pays the miners through the wallet daemon. Only the stratum ports and 443 are open; everything else listens on 127.0.0.1. Miners mining rigs Visitors site, web miners POOL SERVER stratum ports nginx :443 tenrai-pool stratum, shares, payouts, website tenraid API :28701, work :28702 Wallet daemon :28703, pays the miners Open to the internet: the stratum ports and 443. The rest listens on 127.0.0.1.
A pool server once it is public. Only the stratum ports and 443 face the internet.

tenrai-pool is built from its own repository with cargo build --release. It needs Rust and a C compiler, like the node.

On mainnet the three programs find each other by their defaults: started in the order below, none of them needs an option. The options are for what differs from that, such as ports that miners reach from outside.

1. Run a synced node

Set up a node as in Run a node and wait until GET /v1/node/sync answers "synced": true.

2. Create the pool wallet

Shell
tenrai-wallet init --generate-password

Write down the recovery_phrase it prints. This wallet holds every block reward until it is paid out.

The wallet file lies in the wallet folder of this machine, ~/.tenrai/wallet/mainnet/wallet.vault on Linux and macOS and %LOCALAPPDATA%\Tenrai\wallet\mainnet\wallet.vault on Windows. --generate-password writes a random password to wallet.vault.pass beside it, readable by this user only, so the daemon opens the wallet unattended.

3. Start the wallet daemon

Shell
tenrai-wallet daemon

It listens on 127.0.0.1:28703. On its first start the daemon writes wallet.vault.token beside the wallet file, the key the pool uses to talk to it. Keep the daemon on 127.0.0.1.

4. Start the pool

Shell
tenrai-pool

The pool reads the daemon's token from the wallet folder and listens on 127.0.0.1: PPLNS on port 28740 and SOLO on 28744. To let miners in from outside and name the pool:

Shell
tenrai-pool --pplns-listen 0.0.0.0 --solo-listen 0.0.0.0 \
  --pool-name "Example Pool" --public-host pool.example.org

A pool wallet kept somewhere else is named on both sides: tenrai-wallet daemon --file pool.vault --password-file pool.pass and tenrai-pool --wallet-token-file pool.vault.token.

On a testnet, start the daemon and the pool with --network testnet, and the pool also with the testnet's --chain-id. The pool checks at start that the daemon serves the node's chain. It refuses to start if the daemon serves another wallet than the one its database was created with.

5. Test it

Shell
tenrai-miner -a <address> --stratum stratum+tcp://127.0.0.1:28740 -w test --pool

The website on http://127.0.0.1:28772/ shows the worker once its first shares arrive; /miner/<address> shows the address in detail. A worker that has been offline for an hour no longer shows.

6. Go public

TaskHow
Websitenginx with TLS in front of --web-bind (default 127.0.0.1:28772), which must stay on loopback. Set proxy_set_header X-Real-IP $remote_addr; so rate limits count each visitor, and do not pass /metrics on. The pool repository ships a site file in deploy/nginx, including the WebSocket ports for web miners
Web miners--pplns-listen-ws 127.0.0.1:28743 and --solo-listen-ws 127.0.0.1:28747 behind the same nginx, with --ws-origin set to the pages that may mine. The WebSocket location sets X-Real-IP again: nginx drops the server's proxy_set_header lines in a location that sets its own, and without that header the pool counts every web miner as 127.0.0.1, under one limit and never banned
Encrypted stratum--pplns-listen-tls 0.0.0.0:28741 or --pplns-listen-v2-tls 0.0.0.0:28742; with --tls-cert and --tls-key for a certificate of your own
FirewallOpen the stratum ports, 443 and the node's P2P port 28700. Keep 28701, 28702, 28703 and 28772 closed
Abuse--autoban bans addresses that send mostly invalid shares or bad requests
ServiceStart node, daemon and pool with your service manager, in this order. tenrai-pool.json next to the program keeps the options

The connect instructions on the website show the listener ports. --pplns-url and --solo-url replace them, for example when a proxy uses other ports.

Options

Every option also works in a JSON file, with the long option name as key. The pool reads tenrai-pool.json next to the program and creates it with the default settings on the first start; --config names another file. The command line wins.

Node and wallet

OptionDefaultWhat it does
--network <NAME>mainnetmainnet, testnet or development. Sets the default ports, see Default ports
--node <HOST[:PORT]>127.0.0.1:28702Mining listener of the node; a URL works too
--node-api <HOST[:PORT]>127.0.0.1:28701API of the same node; a URL works too
--chain-id <HEX>mainnetExpected chain ID. Required for a testnet or development node
--node-identity <HEX>Expected node identity. Required with a remote node, which also needs HTTPS
--wallet <HOST[:PORT]>127.0.0.1:28703Wallet daemon; a URL works too
--wallet-token-file <FILE>wallet.vault.token in this machine's wallet folder for the networkToken of the daemon. Without it and without that file the pool does not start, unless --no-payouts
--no-wallet-checkoffStarts even when the daemon does not answer
--pool-address <ADDR>the daemon'sPool address for a start without the daemon. Must be the daemon's receive address

Stratum

OptionDefaultWhat it does
--pplns-listen <IP[:PORT]>127.0.0.1:28740PPLNS port, version 1, unencrypted. Repeatable
--solo-listen <IP[:PORT]>127.0.0.1:28744SOLO port, version 1, unencrypted. Repeatable
--pplns-listen-tls, --solo-listen-tlsVersion 1 over TLS, ports 28741 and 28745 unless given
--pplns-listen-v2-tls, --solo-listen-v2-tlsVersion 2, always TLS, ports 28742 and 28746 unless given
--pplns-listen-ws, --solo-listen-wsVersion 1 over WebSocket for browser miners, behind a TLS proxy, ports 28743 and 28747 unless given
--ws-origin <ORIGIN>any pageWeb pages allowed to mine over WebSocket. Repeatable
--no-v1offNo version 1 port at all; needs a version 2 port
--tls-cert <FILE>, --tls-key <FILE>stratum.crt and stratum.key beside the databaseCertificate and key for the TLS and version 2 ports. Without them the pool creates a self-signed pair at its first start, for localhost, the listeners' addresses, --public-host and the connect URLs; miners pass the .crt as --tls-ca
--max-clients <N>8192Connections in total
--max-clients-per-ip <N>256Connections per IPv4 address or IPv6 /64
--idle-timeout <S>600Closes a connection that sends nothing for this long
--tag <TEXT>the miner'sTag written into every block, e.g. the pool name
--autobanoffBans addresses for bad requests or mostly invalid shares
--ban-time <S>600Length of a ban

Share difficulty

OptionDefaultWhat it does
--start-difficulty <D>65536Share difficulty of a new worker
--min-difficulty <D>50Lowest share difficulty, also for d= requests
--max-difficulty <D>network difficultyHighest share difficulty
--shares-per-block <N>16Keeps the share difficulty at most 1/N of the network difficulty
--share-interval <S>20Seconds between two shares of one worker that vardiff aims for
--vardiff-tolerance <%>25How far a worker may be off the aim before vardiff acts
--vardiff-max-step <F>4Largest factor of one change
--vardiff-history <N>64Shares the hashrate estimate uses at full weight

TenQ

OptionDefaultWhat it does
--tenq-mode full|lightfulllight checks shares from the 32 MiB cache, far slower
--no-tenq-prefetchoffBuilds the next epoch's dataset only when the epoch starts
--verify-threads <N>CPU countShares checked at the same time

Rewards and payouts

OptionDefaultWhat it does
--pplns-window <F>2PPLNS window in multiples of the network difficulty of the block
--pplns-lookback <MIN>60Oldest share a window may reach
--pplns-fee <%>, --solo-fee <%>2.5, 3Pool fee per block reward
--fee-recipient <ADDR>=<%>Pays this share of the fees to an address. Repeatable; the rest stays in the pool wallet
--no-payoutsoffCredits balances but sends nothing
--payout-interval <MIN>15Minutes between payout rounds
--min-payout <TRAI>1Smallest payout
--payout-fee <FEE>normalminimum, normal, priority or a total in TRAI
--payout-fee-max <TRAI>Skips a round while a payout transaction would cost more
--payout-fee-payer pool|minerspoolminers takes the fee from the payments in equal parts
--payout-batch-size <N>7Recipients per transaction
--payout-batches <N>20Transactions per round
--compound-above <N>64Compounds the pool wallet above this many outputs; 0 turns it off

Storage, website and logs

OptionDefaultWhat it does
--db <FILE>data/pool.sqliteDatabase
--keep-blocks <DAYS>2Keeps closed blocks for the lists; 0 forever
--keep-blocks-max <N>10000Keeps at most this many closed blocks; 0 no limit
--keep-block-splits <DAYS>0Keeps a block's PPLNS split for its block page; 0 drops it once the reward is credited
--web-bind <IP[:PORT]>127.0.0.1:28772Website, API and metrics
--no-web, --no-metricsoffNo website, or no /metrics
--web-rate-limit <N>300Requests per minute and client; 0 turns the limit off
--pool-name <TEXT>Tenrai PoolName on the website
--public-host <HOST>the page's hostHost in the connect instructions
--pplns-url, --solo-url <URL>from the portsStratum URLs the connect instructions show instead. Repeatable
--explorer-url <URL>Tenrai ExplorerExplorer the website links to; empty for no links
--stats-interval <S>60Seconds between status lines in the log
-d, --log-file <FILE>, --no-colorDebug log, log file (moved to .old at 64 MiB), plain console

Blocks

StatusMeaning
foundStored, on its way to the node
pendingTaken by the node, waiting for registration and its reward
confirmedPaid by a finalized cut; credited to the miners
expiredNot registered before its window closed
rejectedRefused by the node

A finalized cut never changes, so a confirmed block is credited at once, without a maturity period.

Payouts

Each round pays the largest balances of at least --min-payout, --payout-batch-size miners per transaction. Every payout gets a request ID before it is sent; if the daemon's answer is unclear, the next round asks again under the same ID, so nothing is paid twice. Each address is checked again before a send.

API

RouteContent
/api/v1/poolPool, address, network, fees, ports, top miners
/api/v1/blocks, /api/v1/payments25 blocks or payouts per page
/api/v1/miners/{address}Balance, workers, blocks and payouts of one miner
/api/pools, /api/blocks, /api/miners/{address}Miningcore format, for pool listing sites

Type to search guides, concepts and reference pages.