BlockSniper.ai implements a dual-output coinbase mechanism that automatically splits block rewards between miners and the pool operator. This document explains the mechanics, configuration, and verification.
When a solo miner finds a block, the pool creates a coinbase transaction with two outputs:
- Miner Output: Pays the found-block value to the miner's own BCH address — their stratum username — in solo mode (
-B). It falls back tobchaddressin two cases: a solo miner whose username is not address-shaped, and regular (non-solo) pool mode, where every block paysbchaddress - Fee Output: Pays the pool operator's fee to
pooladdress
Example with 6.25 BCH block and 2% fee:
Block Reward: 3.125 BCH
Pool Fee (2%): 0.0625 BCH (fee amount)
Miner Receives: 3.0625 BCH (remainder: 3.125 - 0.0625)
Coinbase Outputs:
- Output 1: 3.0625 BCH → Miner's address
- Output 2: 0.0625 BCH → Pool operator address
- Total: 3.125 BCH (sum invariant maintained)
The fee is calculated as: fee = block_reward * (poolfee / 100), then rounded down to the nearest satoshi.
Example calculations:
- 6.25 BCH, 2.0% fee → 0.125 BCH (12,500,000 sats)
- 3.125 BCH, 1.5% fee → 0.046875 BCH (4,687,500 sats) — exact
- 6.25 BCH, 1.0% fee → 0.0625 BCH (6,250,000 sats) — exact
- 1.0 BCH, 2.0% fee → 0.02 BCH (2,000,000 sats) — exact
- 0.5 BCH, 2.0% fee → 0.01 BCH (1,000,000 sats) — exact
If rounding down produces dust (< 546 sats), the fee output is omitted entirely:
- The entire block reward goes to the miner
- No fee output is created
- This avoids creating unspendable dust outputs
Dust omission example:
- 0.001 BCH block, 2% fee → fee would be 0.00002 BCH (2,000 sats)
- 2,000 sats > 546 sats → fee output is created ✓
- 0.0001 BCH block, 2% fee → fee would be 0.000002 BCH (200 sats)
- 200 sats < 546 sats → fee output omitted, miner gets full 0.0001 BCH
- Miner connects with username = their BCH address (e.g.,
bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy) - Miner finds a block (solves valid share at network difficulty)
- Pool creates coinbase with dual outputs:
- Miner's output: block_reward - fee → miner's address
- Fee output: fee amount → pooladdress
- Block is broadcast to the network
- On block confirmation, miner receives payment directly on-chain, non-custodial
No UTXO pooling: Each block creates its own distinct outputs; the pool takes no custody of funds.
{
"bchaddress": "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy",
"pooladdress": "bitcoincash:qregedwmg8tr2ymnp8j6f0tesuj4r9lqnqjfmlvj6w",
"poolfee": 2.0
}-
bchaddress(required for solo mode)- Receives payments when miner username is NOT a valid BCH address (fallback)
- Also used as coinbase message recipient if no
pooladdressconfigured - Supports CashAddr (with/without prefix:
bitcoincash:/bchtest:/bchreg:) and legacy Base58
-
pooladdress(required for dual-output fee splitting)- The pool operator's BCH address (receives the configured
poolfeepercentage) - Same address formats as
bchaddress - Should be distinct from
bchaddress— not enforced in code, but see the fallback note under Solo Mode with Smart Fallback for what happens when they are equal - Validated at stratifier startup. If bitcoind rejects it, the pool logs
Pool fee address ... is invalid, disabling pool fee, setspoolfeeto 0 and keeps running — collecting nothing. Grep the log forPool operator fee address validafter any config change
- The pool operator's BCH address (receives the configured
-
poolfee(configurable, default production: 2.0)- Fee percentage, expressed as a percent of the block reward
- Can be integer or decimal (0, 1, 1.5, 2.0, 10.5, etc.)
- Clamped to [0, 50] with a warning — a value above 50 is lowered to 50, not rejected, and the pool starts anyway
- Rounding: fees are always rounded down; dust outputs are omitted
- Example: 2.0 = 2% pool fee (miner receives 98%)
{
"btcaddress": "..."
}btcaddress is a deprecated alias for bchaddress. Both keys work identically. Use bchaddress in new configurations for clarity (BCH-specific naming).
{
"btcd": [{
"url": "127.0.0.1:8332",
"auth": "rpcuser",
"pass": "rpcpassword",
"notify": true,
"zmqnotify": "tcp://127.0.0.1:28333"
}],
"bchaddress": "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy",
"pooladdress": "bitcoincash:qregedwmg8tr2ymnp8j6f0tesuj4r9lqnqjfmlvj6w",
"poolfee": 2.0,
"btcsig": "YourPoolName.com",
"serverurl": ["0.0.0.0:3333"],
"mindiff": 500000,
"startdiff": 500000,
"maxdiff": 1000000
}BlockSniper.ai includes an end-to-end test suite at testing/regtest-e2e.sh that:
- Mines blocks in regtest mode
- Verifies dual-output coinbase creation
- Confirms fee calculations are correct
- Tests dust omission behavior
- Validates network-specific address handling
Run the full test:
cd /path/to/ckpool
./testing/regtest-e2e.shTo inspect a mined block and verify the fee split:
# Get a recent block hash
BLOCK_HASH=$(bitcoin-cli getblockhash BLOCK_HEIGHT)
# Inspect the block's coinbase outputs
bitcoin-cli getblock "$BLOCK_HASH" 2 | jq '.tx[0].vout'Example output (6.25 BCH block, 2% fee):
[
{
"value": 6.125,
"scriptPubKey": {
"address": "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"
}
},
{
"value": 0.125,
"scriptPubKey": {
"address": "bitcoincash:qregedwmg8tr2ymnp8j6f0tesuj4r9lqnqjfmlvj6w"
}
}
]- Output 0: 6.125 BCH → Miner's address
- Output 1: 0.125 BCH → Pool operator's address
These blocks on BCH testnet verify the system works:
| Block | Miner | Fee | Result |
|---|---|---|---|
| 1677558 | — | 1% | ✓ Dual-output confirmed |
| 1677572 | — | 2% | ✓ Dual-output confirmed |
To inspect these:
# Mainnet/testnet node
bitcoin-cli getblock 1677558 2 | jq '.tx[0].vout | length' # Should show 2 outputs
bitcoin-cli getblock 1677558 2 | jq '.tx[0].vout[] | .value'When a miner connects with a username:
-
Valid BCH address (any form: CashAddr with prefix, without prefix, or legacy Base58)
- Address is checksum-verified locally
- Must be correct network (mainnet/testnet/regtest)
- ✅ Accepted: Miner is paid to this address when finding a block
-
Invalid address (looks like an address but fails checksum/network)
- ❌ Rejected immediately with message: "Invalid BCH address (bad checksum or wrong network) - check your username for typos"
- Protects miners from accidentally donating a block
-
Non-address username (e.g.,
rig01,worker1)- ✅ Accepted: Miner is authorized and mines for the pool's
bchaddress - The user's payout script is copied from the pool's own at authorisation
(
stratifier.c,generate_user()→user->pool_fallback = true), so a block they find paysbchaddress— notpooladdress - The fee output is still cut. The split is built into the shared
workbase and is gated only on
poolvalid && poolfee > 0and the dust check — never onpool_fallback. A fallback block pays(100 - poolfee)%tobchaddressandpoolfee%topooladdress, exactly like an address-based miner's block. - This is why
bchaddressandpooladdressshould be distinct: set them to the same address and a fallback block carries two coinbase outputs to that one address, which coinbase parsers have to special-case.
- ✅ Accepted: Miner is authorized and mines for the pool's
# Correct address → mining starts
./cgminer -u bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy
# Typo in address (bad checksum) → rejected
./cgminer -u bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfui
# Error: "Invalid BCH address (bad checksum or wrong network) - check your username for typos"
# Non-address name → mines for pool fallback
./cgminer -u myrig01
# Mining for bchaddress (pool fallback)When NiceHash or MiningRigRentals connect, the pool auto-detects via useragent and applies configured difficulty (not overridable by mindiff_overrides pattern matching):
- NiceHash:
500000difficulty (configurable via config) - MiningRigRentals:
1000000difficulty (configurable via config)
Each rental rental service still uses their username (usually an address or username), and if an address, is paid directly when finding a block.
- Payments are made directly on-chain in the coinbase (block reward)
- The pool never holds or custodies miner funds
- Upon block confirmation, funds belong solely to the recipient address
- Coinbase outputs are spendable only after 100 block confirmations (Bitcoin/BCH consensus rule)
- No miner can claim the same block reward twice
- All addresses are validated locally using CashAddr checksum algorithms
- Network detection is automatic (mainnet/testnet/regtest from node)
- Case-insensitive CashAddr (normalized to lowercase after validation)
- Fees are calculated and rounded consistently
- Dust protection prevents creation of unspendable outputs
- Sum invariant: output1 + output2 = block reward (always)
- Check pool is running with
-Bflag for solo mode - Verify
pooladdressis configured and distinct frombchaddress - Verify
poolfeeis > 0 (and not rounded to dust) - Inspect block coinbase outputs:
bitcoin-cli getblock <hash> 2
- Verify address is valid for the network (mainnet/testnet/regtest)
- Check the rejection message: "Invalid BCH address" means checksum/network mismatch
- Test address locally: validate it with a BCH address tool or bitcoin-cli
- Confirm
poolfeeis a JSON number, not a string —2and2.0are both accepted (json_get_doubletakes anyjson_is_number), but"2.0"in quotes logsJson entry poolfee is not a numberand leaves the fee at 0 - Verify poolfee is in range [0, 50] — above 50 it is clamped down to 50 and
the pool logs
Poolfee ... out of range, clamping to 50at startup - Check if fee rounds to dust (< 546 sats) — if so, fee output is omitted
- Inspect the block with
bitcoin-cli getblock <hash> 2
BlockSniper.ai Pool Fee System — Non-custodial, dual-output, solo mining with automatic operator fee distribution