The threat
To reverse a payment, a miner builds a competing chain in secret, one that leaves the payment out. Once that chain is longer than the public one, the miner publishes it. Nodes always follow the chain with the most work, so they switch to it, and the payment is gone, along with every confirmation it had. Each confirmation is one more block the secret chain must overtake. A pool counts as one miner, because it decides what all its miners work on. What matters is the share of blocks found by the largest pool, not the number of individual miners.How the wait is worked out
The status page works out the wait against the largest miner of the last 7 days, from its share of those 7 days’ blocks.Below half the hashrate
A miner with less than half the hashrate falls behind on average, so it can only catch up by luck, and its odds shrink with every confirmation. The status page works them out exactly, as in Rosenfeld (2014), not with the approximation in section 11 of the Bitcoin paper, which understates them: against 30% of the hashrate, 99.9% safety takes 32 blocks, not the paper’s 24. Those odds assume the miner was mining in secret the whole time the payment was being confirmed, which gives it a head start. But hashrate cannot be in two places at once. If the miner’s blocks keep arriving on the public chain at the rate its share says they should, it was not mining in secret. It could only start after the payment is accepted, from as far behind as the payment is deep, and that needs far fewer confirmations to defend against. It could still hide part of its hashrate and pass the gap off as bad luck, up to about a quarter of it, so the shorter waits allow for that. They hold for deposits credited while the miner is seen mining in the open (see Hidden mining). Confirmations needed against the largest miner’s share, worked out with the status page’s own code:
At 10 minutes a block, 6 confirmations is about an hour, 32 about 5 hours,
and 133 about a day. Below a 25% share the two columns agree, because the
open-mining check only watches a miner with 25% of the blocks or more.
Half the hashrate or more
A miner with a majority finds blocks faster than everyone else together, so it always catches up eventually, however long the wait. Watching it does not help either: it can start a secret chain after the payment is accepted, beginning below it. What protects a payment then is cost. The blocks the miner replaces are mostly its own, and their rewards are lost with them: about its share of each block’s pay, for every confirmation. Wait until reversing the deposit would cost the miner more than the deposit is worth:Not counted
Neither method sees hashrate that is rented or brought in from elsewhere for an attack, because it never shows on the chain beforehand, nor pools that work together. Large deposits deserve a margin above what the status page says. As a rough guide to rented hashrate: matching the whole network’s hashrate costs about what miners earn, which the status page reports assecurityBudget.laxPerDay. Divided by 144, that is roughly what an
attacker pays per block it mines. A deposit worth more than your
confirmation count times that figure is worth renting hashrate to attack,
and should wait longer or be reviewed by hand.
Majority attacks are not only theory. Ethereum Classic lost about 219,500
ETC to repeated reorganizations in January 2019, and attackers double-spent
about 388,000 BTG at exchanges on Bitcoin Gold in May 2018. In both cases
it was exchanges crediting deposits that lost the funds.
Hidden mining
Hashrate spent on a secret chain is missing from the public one. So a miner mining in secret stops publishing blocks, or publishes fewer than its share says it should. The status page watches the largest miner’s last 144 blocks, once it has 25% of the blocks or more, and asks: how likely is it that a miner mining openly, at this share, would publish this few blocks in this long? The answer sets a status dot and theconfirmations.watch
object in the API.
How quickly the dot changes depends on the share. At 60% of the blocks it
turns amber after about 2.6 hours without one of the miner’s blocks and red
after about 3.8 hours. At 94% it takes about 1 hour 20 minutes and 2 hours.
What a quiet miner does and does not mean
A hidden-mining signal is not, by itself, an attack. A pool that is offline looks exactly the same: hashrate spent on nothing is as absent from the public chain as hashrate spent on a secret chain. Pools go down for maintenance, lose their node, have network trouble, or have miners move elsewhere for a while. And at 1 in 100, the grey dot shows up by chance from time to time even when nothing at all is wrong. A quiet stretch that ends with the miner simply resuming, and no block replaced, was not an attack. What the signal tells you is where a reversal would come from, and which blocks it could reach: those afterwatch.fromHeight. That makes the
right response narrow. Hold the deposits it could affect, and let the rest
of the data decide whether anything happened:
1
Hold only what is exposed
While
watch.level is "critical", hold deposits in blocks above
watch.fromHeight. Deposits in earlier blocks are not exposed by this
stretch and follow your normal rules. At "warn", stop using the shorter
open-mining waits and hold large deposits.2
Wait for the miner to come back
The quiet stretch ends when the miner’s blocks appear again. Either they
extend the public chain, or they replace part of it.
3
Check whether anything was replaced
Look at
staleBlocks.lastReorg and staleBlocks.reorgEvents on the
status page, and at your own scanner’s reorganization log. If there is
no reorganization reaching down to fromHeight or below, the blocks
after fromHeight stood: the miner was offline or unlucky, not mining a
secret chain. Release the held deposits; they have their full count of
confirmations. If a reorganization did reach that far, the blocks were
replaced: rescan, and treat it as an incident.4
Look for corroboration while you wait
The other figures help tell an outage from an attack before it resolves.
A pool that is visibly down (its own website or dashboard, which the
status page links from
decentralization.topMiners[].website, or an
announcement) points to an outage. So does a propagation or
slow-miner alert, which points to network trouble rather than
deliberate withholding. Unexplained silence from a pool that otherwise
looks healthy, in a period where a large deposit is pending, is the
case that deserves the most caution.withholding incident, kept for 90 days. Comparing
those incidents with staleBlocks.reorgEvents over the same periods shows
how often a quiet miner has been followed by a reorganization on Parallax,
and how long its quiet stretches usually last. That record, together with your own
reorganization log, is what lets you hold briefly and precisely instead of
pausing all deposits whenever a pool hiccups.
Building a policy
A workable policy has four parts. 1. A required count per deposit, from live data. Choose a confidence by deposit size, for example 99.9% for ordinary deposits and 99.99% for large ones, and read the count fromconfirmations.levels, or from
openLevels when watch.open is true at the moment you credit. While
majority is true, use the cost formula instead, with a safety margin.
Never go below a floor of your choosing.
2. Exposure limits. Cap the value credited per deposit, per account and
per hour, lower while decentralization.grade is concentrated or
critical. While a majority exists, the cap is what actually bounds your
loss.
3. Halt conditions. A short list of signals that hold or pause
deposits, below. The fields they name are described in
Fields for a deposit policy.
4. Withdrawals stay open. These risks are to deposits. A reorganization
can also drop a withdrawal you sent, but that is recovered by rebroadcasting
the same signed transaction (see Withdrawals).
Halt conditions
A policy in code puts these rules together
in Python.

