An XDC epoch holds at most 900 blocks. In XDPoS V2 an epoch is 900 rounds, and a round that times out makes no block. On net2040, 554 of 933 complete epochs hold exactly 900 blocks and 379 hold fewer.

The numbers below are an xOne team measurement, 2026-11-15. For what an epoch switch is and what its header carries, read how epoch switches work. For how timeouts work, read QC, TC and timeouts. This post does not repeat them.

How many blocks does a net2040 epoch hold?

Most epochs hold 900 blocks. The mean is 895.4 blocks. The shortest epoch holds 699 blocks and the longest holds 900. No epoch holds more than 900.

The measurement covers net2040 from block 10,801 to head 847,075. It found 934 epoch-switch blocks (10,801 to 846,234), so 933 epochs are complete. An epoch length is the number of blocks between two consecutive epoch-switch blocks.

REFERENCE TABLEScroll to explore all columns →
blocks per epoch epochs
900 554
895-899 241
880-894 78
850-879 37
800-849 20
0-799 3
  • Exactly 900 blocks: 554 epochs.
  • Fewer than 900 blocks: 379 epochs.
  • More than 900 blocks: 0 epochs.

The ten shortest epochs, as start block (length in blocks): 530,054 (699), 588,543 (739), 619,491 (772), 530,753 (802), 621,141 (803), 693,797 (803), 555,498 (807), 554,683 (815), 567,043 (820), 17,999 (824).

Why can an epoch hold fewer than 900 blocks?

An epoch is counted in rounds, and a round makes at most one block. The epoch-switch spec page says:

An epoch is epoch rounds, not epoch blocks: a round that times out has no block, so a timeout makes an epoch hold fewer than epoch blocks (and last longer in time, because the round waits timeoutPeriod).

The retention spec page says the same thing from the other side:

An epoch holds at most E blocks. V2 epochs are counted in rounds (IsEpochSwitch, engine.go:1952) and a round makes at most one block, so the walk to the k-th epoch switch below n is at most k * E blocks.

On net2040, E is 900. The timeouts spec page says the timeout schedule is flat on this network: 60 s on Apothem and net2040. Round parameters follow rounds too. The config-by-round page says "the entry in force at round r is the one with the highest key <= r".

The spec pages agree on the rule. They quote different Apothem samples, and this post uses neither. Every number above is from net2040.

What does a timeout round cost?

Each missing block is one round that made no block. The measurement gives these totals for the same 933 epochs:

  • The 933 epochs hold 835,433 blocks.
  • 933 x 900 = 839,700 rounds.
  • 839,700 - 835,433 = 4,267 rounds that produced no block.
  • Over the 379 short epochs, that is 11.3 block-less rounds per short epoch on average.
  • The largest single gap is 201: the 699-block epoch at 530,054 (900 - 699 = 201).

This arithmetic assumes the rounds rule that both pages state: an epoch is 900 rounds and each round makes at most one block. The RPC call lists switch blocks only. It does not say why a round made no block. The spec says a timeout is one cause, and that a round that times out waits timeoutPeriod before the next one.

How do I repeat the count with XDPoS_getEpochNumbersBetween?

Call XDPoS_getEpochNumbersBetween with a begin block and an end block. It returns the epoch-switch block numbers in the range. The XDPoS namespace page lists three rules:

  • A begin block at or below the V2 switch block gives -32000 not supported in the v1 consensus. On net2040 the V2 switch block is 10,800, so start at 10,801.
  • A range of more than 1000 epochs gives a -32000 error. Use chunks of 100,000 blocks.
  • begin == end returns [].

This script is an example, not a tool that ships with xOne. It uses only the Python standard library. Replace <rpc-url> with the RPC URL of a net2040 node.

# Example only: count epoch lengths on net2040.
import collections, json, urllib.request

URL = "<rpc-url>"
START, HEAD, CHUNK = 10_801, 847_075, 100_000  # set HEAD to your node's head

def call(begin, end):
    body = json.dumps({"jsonrpc": "2.0", "id": 1,
                       "method": "XDPoS_getEpochNumbersBetween",
                       "params": [hex(begin), hex(end)]}).encode()
    req = urllib.request.Request(URL, body, {"Content-Type": "application/json"})
    return json.load(urllib.request.urlopen(req))["result"]

switches = set()
for begin in range(START, HEAD + 1, CHUNK):
    switches.update(call(begin, min(begin + CHUNK - 1, HEAD)))

s = sorted(switches)
lengths = [b - a for a, b in zip(s, s[1:])]
print(len(s), "switch blocks,", len(lengths), "complete epochs")
print("shortest", min(lengths), "longest", max(lengths),
      "mean", round(sum(lengths) / len(lengths), 1))
print(sorted(collections.Counter(lengths).items())[-5:])

The head moves, so your totals grow past the figures above. The team ran the same call on a geth-XDC net2040 node for blocks 10,801 to 120,001. It returned the same 122 switch blocks as the xOneRS node. See RPC methods and networks for the node setup.

Frequently asked questions

How many blocks are in an XDC epoch?

At most 900. An epoch is 900 rounds, and a round that times out makes no block. On net2040 the mean was 895.4 blocks over 933 complete epochs.

Is a block number that is a multiple of 900 an epoch switch?

Usually not. The epoch-switch spec page says a switch can be at any block number, and a multiple of epoch is usually not one.

How many net2040 epochs hold fewer than 900 blocks?

379 of 933. 554 hold exactly 900 blocks, and none hold more. The shortest holds 699 blocks.

Why does XDPoS_getEpochNumbersBetween fail at block 10,800?

Block 10,800 is the V2 switch block on net2040. A begin block at or below it gives -32000 not supported in the v1 consensus. Start at 10,801.

Sources