Concepts
Confirmations and finality
An operation passes three marks. It is fast-confirmed within seconds, on Bitcoin once its block’s data is in a Bitcoin block, and final after six Bitcoin confirmations. This page says what each mark protects against and how long to wait.
On this page
Every operation you send passes three marks, and you choose which one to wait for by what is at stake. A fast confirmation is protected only by the fast lane’s own proof of work; a final block can be undone only by reorganizing Bitcoin. Mainnet has not launched, and the public signet, where you can watch the marks today, has too little hashrate for its fast confirmations to protect much (see below).
The three marks
- ≈ 5 s Fast-confirmed Executed on the fast lane: balances have moved and contracts see the new state.
- next Bitcoin block On Bitcoin The block’s data is in a Bitcoin block, where anyone can read it.
- 6 Bitcoin confirmations Final Decided from Bitcoin: no fast-lane chain can replace it, and undoing it takes a Bitcoin reorganization.
The three marks of an operation: fast-confirmed in about 5 seconds, on Bitcoin with the next Bitcoin block after its settlement, final after 6 Bitcoin confirmations.
| Mark | What has happened | Typical time |
|---|---|---|
| Fast-confirmed | The node’s main chain accepted the operation, and it executed successfully. | About 5 seconds |
| On Bitcoin | The data of the block that accepted it, and of every block before it, is in a Bitcoin block. | The next Bitcoin block after its settlement goes out |
| Final | That data has 6 Bitcoin confirmations, on the chain that every node and indexer decides is final. | From about an hour |
Statuses in the RPC
ksana_getOpStatus, which ksana-cli op and the SDK’s getOpStatus call, answers with one of five statuses:
| Status | Meaning |
|---|---|
pending |
In this node’s entry pool, not in a block yet. |
fast_confirmed |
Included and executed successfully: the fast-confirmed mark. |
failed |
Included, but it failed, and its changes were rolled back. The fee is still paid, and the nonce is used, except by a path C operation that failed its checks (version, chain ID, length, fee mode, signature or nonce). |
skipped |
Included only by merged blocks that could not execute it (a duplicate, a nonce already used, a balance already spent). No fee is charged, and the status can still change. |
btc_final |
Executed successfully, and the main-chain block that accepted it is final. |
What to watch for when you read them:
- On Bitcoin is not an op status. Ask for the settlement of the op’s block with
ksana_getSettlement:settledturns true once the data of the main-chain block that accepted it, and of everything before it, is on Bitcoin, andbtc_finalonce it is final. A block with no settlement of its own, an empty block or one holding only path C operations, answers-32004. Once a main-chain block is settled or final, so is every block below it, so read the marks from the next block up that has a settlement. - A failed op stays
failed, even after its block is final. Checkbtc_finalof its block’s settlement to know that the failure itself is final. skippedis not the end. A skipped op that is still valid returns to the entry pool and can be included again, so it may readpendingand thenfast_confirmed.- An unknown op is an error, code
-32004. A path C operation is unknown to the nodes until a block includes it, and an op that left the entry pool (replaced by one with a higher fee, or evicted from a full pool) returns the same error. blockgives the hash of the block that included the op and, asheight, the height of the main-chain block that accepted it: for a merged block, the height where its operations took effect.
What each mark guarantees
Fast-confirmed
Your operation has executed on the node’s main chain: balances and contract state include it, and later operations build on it. Only the fast lane’s proof of work protects it. Until its block is final, a heavier fast-lane chain can replace the block, and a payment is reversed if the sender gets a conflicting operation (another one with the same nonce) into that chain.
A Bitcoin reorganization can also take a fast-confirmed block back. One of at most ANCHOR_DEPTH blocks (1 on signet, 2 on mainnet) orphans no anchor and leaves the main chain as it is, and a deeper one takes the blocks anchored on the orphaned Bitcoin blocks off the main chain. Their path B operations return to the entry pool and are included again with the same op ID, so an honest payment comes back by itself; only a sender who gets a different operation with the same nonce in first turns such a reorganization into a reversal.
On Bitcoin
The data of the block that accepted your operation, and of every block before it, is in a Bitcoin block. From now on anyone with a Bitcoin node can read the operation and recompute its effect without any Ksana node, and an indexer executes the block once it is 2 blocks deep in the fast lane or final. The rewards and fees of the block can now be paid out.
This mark is about availability, not finality. A heavier fast-lane chain can still replace the block until it is final, and a Bitcoin reorganization can take the transaction that carries the data out of its block until it confirms again.
Final
The main-chain block that accepted your operation is on the final chain: its data and the data of the blocks it merges have 6 Bitcoin confirmations, its execution was checked, and every node and indexer reaches the same decision from Bitcoin data alone. No fast-lane chain can replace it, however much hashrate is behind it: a competing chain counts only if its headers reached Bitcoin by the Bitcoin block that gave your block’s data its sixth confirmation. Undoing it takes a Bitcoin reorganization, and a final block grows safer with every Bitcoin block after it, as a Bitcoin transaction does (Security).
How long each mark takes
- Fast-confirmed: about 5 seconds for path B on signet and mainnet alike. A path C operation is fast-confirmed only once its Bitcoin transaction has
NATIVE_CONFIRMATIONS+ANCHOR_DEPTHconfirmations: 2 on signet, 5 on mainnet. - On Bitcoin: a settlement goes out once the block is 2 fast-lane blocks deep, and its data lands in the next Bitcoin block that takes it, about 10 minutes on average on both networks. While an earlier settlement is still waiting to confirm, your block’s data waits for the next one, which adds about one Bitcoin block.
- Final: five more Bitcoin blocks after the data lands, so from about an hour after you send the operation.
On a quiet chain the 2 blocks after yours are usually empty blocks, which take minutes (Mining explains why), so the settlement leaves a few minutes later than on a busy chain. Here is one transfer on the signet on 14 October 2026:
| Time (UTC) | What happened | Mark |
|---|---|---|
| 03:29:04 | Block 127 includes a KSA transfer, while the settlement of block 124 still waits for a signet block. | Fast-confirmed |
| 03:40:29 | Signet block 326,271 confirms block 124’s settlement. | |
| 03:41:56 | Block 130, an empty block, carries a settlement with block 127’s body. | |
| 04:01:27 | Signet block 326,273, the next one after that settlement went out, confirms it. | On Bitcoin, after 32 minutes |
| 04:31:34 | Signet block 326,278 gives it 6 confirmations. | Final, after 62 minutes |
How long to wait
How safe a fast confirmation is depends on the attacker’s share of the fast lane’s hashrate and on whether the chain is busy. In the model of SPEC §10.4, an attacker with a share α of the hashrate mines a private branch from the parent of the block that holds your payment and publishes it if it pulls ahead. These tables give the chance that a payment you accepted after the given wait is reversed (the best of three attacker strategies, 50,000 simulated races per cell).
A busy chain, where blocks carry other users’ operations:
| Wait | α = 5% | α = 10% | α = 20% |
|---|---|---|---|
| 30 seconds | 2.0% | 7.5% | 27% |
| 1 minute | 1.5% | 6.5% | 25% |
| 5 minutes | 0.06% | 0.91% | 9.3% |
| 10 minutes | 0.03% | 0.37% | 5.7% |
| 30 minutes | 0.002% | 0.05% | 2.3% |
An idle chain, where only empty blocks follow yours:
| Wait | α = 5% | α = 10% | α = 20% |
|---|---|---|---|
| 30 seconds | 26% | 43% | 69% |
| 1 minute | 20% | 33% | 58% |
| 5 minutes | 4.3% | 13% | 37% |
| 10 minutes | 1.8% | 7.7% | 28% |
| 30 minutes | 0.38% | 2.6% | 13% |
On an idle chain the honest work arrives in empty blocks of weight 12 to 120, each taking minutes, so for minutes your payment is protected by little more than its own block. On a busy chain an attacker’s lucky empty block weighs as much as 12 honest blocks and counts as soon as it is found, which is why even there short waits carry some risk.
So choose by what is at stake:
- Small amounts: fast-confirmed is enough.
- Moderate amounts on a busy chain: wait about 5 minutes.
- Large amounts, or anything that matters on an idle chain: wait for final.
A miner who can reorganize Bitcoin is a different threat: against such a miner a fast confirmation is worth only its anchor’s Bitcoin confirmations. Reversing a block made m Bitcoin blocks ago takes a reorganization of at least ANCHOR_DEPTH + 1 + m blocks, 3 + m on mainnet; such a miner could double-spend bitcoin as well. Undoing a final block also takes a Bitcoin reorganization, as Final explains.
On the signet
The signet’s difficulty was set for its two seed nodes, which mine on one CPU core each, so a computer with a few cores can outpace them. Use the signet to see how the marks behave, not to judge how safe they are; signet coins have no value anyway.
Reading the marks
Ask a node for the op’s status, then for the settlement of the block that included it:
import { KsanaRpcClient, RpcError, SIGNET } from '@ksana/sdk';
const rpc = new KsanaRpcClient('https://signet.ksana.network/rpc');
await rpc.getParams(SIGNET); // throws ChainMismatchError for a node on another network
const opId = '7796031150fd930f07cc99dac446343106456583197d3acc5a15c1be3dba2114';
const status = await rpc.getOpStatus(opId); // throws RpcError -32004 for an unknown op
let mark = 'none yet';
if (status.block !== null && (status.status === 'fast_confirmed' || status.status === 'btc_final')) {
mark = 'fast-confirmed';
// From the op's height up, the first main-chain block with a settlement of its own
const tip = (await rpc.getChainInfo()).tip.height;
for (let height = status.block.height; height <= tip; height++) {
try {
const settlement = await rpc.getSettlement(height);
if (settlement.settled) mark = 'on Bitcoin';
if (settlement.btc_final) mark = 'final';
break;
} catch (e) {
if (!(e instanceof RpcError && e.code === -32004)) throw e; // none of its own
}
}
}
console.log(status.status, mark);export KSANA_RPC=https://signet.ksana.network/rpc
ksana-cli --chain signet op 7796031150fd930f07cc99dac446343106456583197d3acc5a15c1be3dba2114
ksana-cli --chain signet settlement \
7586db3476895fa540484c578469b78fb1ce99f4ad38968aac905c71682ee5c3 \
| jq '{status, settled, btc_final}'op=7796031150fd930f07cc99dac446343106456583197d3acc5a15c1be3dba2114
curl -s https://signet.ksana.network/rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"ksana_getOpStatus","params":["'$op'"]}' \
| jq .resultThe op status, as ksana-cli printed it for the transfer in the timeline above:
{
"block": {
"hash": "7586db3476895fa540484c578469b78fb1ce99f4ad38968aac905c71682ee5c3",
"height": "127"
},
"index": 0,
"receipt": {
"error": null,
"events": [],
"gas_used": "10000",
"op_id": "7796031150fd930f07cc99dac446343106456583197d3acc5a15c1be3dba2114",
"output": "",
"success": true
},
"status": "btc_final"
}And the settlement of block 127, which a later block carried:
{
"status": "confirmed",
"settled": true,
"btc_final": true
}status here is the settlement’s own progress: pending before it is broadcast (the block is not 2 blocks deep yet, or no block carries its data yet), broadcast while it waits for a Bitcoin block, and confirmed once the data is in one. For the chain as a whole, ksana_getChainInfo reports the newest final block as finalized: an operation accepted at or below finalized.height on the node’s main chain is final. The indexer’s indexer_status reports finalized from Bitcoin alone, so comparing the two checks the node. The signet wallet shows the same marks for every operation it sends.