Skip to content

Querying Chain and Irreversible Height⚓︎

BEGINNER

The /chain/height GET endpoint returns the current chain height.

The irreversible height is the highest block that can no longer be rolled back. On NEM, it is calculated by subtracting the rewrite limit from the current chain height.

This tutorial shows how to poll the chain height in a loop, calculate the irreversible height, and track how long ago the chain height last changed.

Prerequisites⚓︎

This tutorial uses the NEM REST API without requiring an SDK. You only need a way to make HTTP requests.

Full Code⚓︎

import json
import os
import time
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'http://libertalia.nemtest.net:7890')
print(f'Using node {NODE_URL}')

prev_height = None
height_changed_at = None

REWRITE_LIMIT = 360

try:
    while True:
        with urllib.request.urlopen(
            f'{NODE_URL}/chain/height'
        ) as response:
            chain_height = json.loads(response.read().decode())

        height = int(chain_height['height'])

        irreversible_height = max(0, height - REWRITE_LIMIT)

        now = time.time()
        if prev_height is not None and height != prev_height:
            height_changed_at = now

        if height_changed_at is not None:
            height_ago = f'{int(now - height_changed_at)}s ago'
        else:
            height_ago = '-'

        print(
            f'Height: {height:>10,}  (changed {height_ago})'
            f'  |  Irreversible: {irreversible_height:>10,}'
        )

        prev_height = height
        time.sleep(1)

except KeyboardInterrupt:
    pass
except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://libertalia.nemtest.net:7890';
console.log(`Using node ${NODE_URL}`);

let prevHeight = null;
let heightChangedAt = null;

const REWRITE_LIMIT = 360;

for (;;) {
    const response = await fetch(`${NODE_URL}/chain/height`);
    if (!response.ok)
        throw new Error(`HTTP error! status: ${response.status}`);

    const chainHeight = await response.json();

    const height = parseInt(chainHeight.height, 10);

    const irreversibleHeight = Math.max(0, height - REWRITE_LIMIT);

    const now = Date.now();
    if (null !== prevHeight && height !== prevHeight)
        heightChangedAt = now;

    const heightAgo = null !== heightChangedAt ?
        `${Math.floor((now - heightChangedAt) / 1000)}s ago` :
        '-';

    const heightLabel = height.toLocaleString().padStart(10);
    const irreversibleLabel =
        irreversibleHeight.toLocaleString().padStart(10);
    console.log(
        `Height: ${heightLabel}  (changed ${heightAgo})` +
        `  |  Irreversible: ${irreversibleLabel}`
    );

    prevHeight = height;
    await new Promise(resolve => { setTimeout(resolve, 1000); });

}

Download source

The snippet uses the NODE_URL environment variable to set the NEM API node. If no value is provided, a default one is used.

The program runs in an infinite loop, printing a status line every second. A keyboard interrupt (Ctrl+C) stops the loop.

Code Explanation⚓︎

Fetching Chain Height⚓︎

        with urllib.request.urlopen(
            f'{NODE_URL}/chain/height'
        ) as response:
            chain_height = json.loads(response.read().decode())

        height = int(chain_height['height'])
    const response = await fetch(`${NODE_URL}/chain/height`);
    if (!response.ok)
        throw new Error(`HTTP error! status: ${response.status}`);

    const chainHeight = await response.json();

    const height = parseInt(chainHeight.height, 10);

On each iteration, the code sends a GET request to the /chain/height GET endpoint. The response contains a single height field with the current chain height, the latest block known to the node.

The chain height increases each time a new block is produced (approximately every 60 seconds).

Calculating the Irreversible Height⚓︎

        irreversible_height = max(0, height - REWRITE_LIMIT)
    const irreversibleHeight = Math.max(0, height - REWRITE_LIMIT);

The rewrite limit is the maximum number of blocks a rollback can undo on NEM, set to 360 blocks (approximately six hours).

Subtracting the rewrite limit from the current chain height gives the irreversible height.

Any block at or below the irreversible height can no longer be rolled back.

See the Consensus textbook section for details on rollbacks and the rewrite limit.

Tracking Height Changes⚓︎

        now = time.time()
        if prev_height is not None and height != prev_height:
            height_changed_at = now

        if height_changed_at is not None:
            height_ago = f'{int(now - height_changed_at)}s ago'
        else:
            height_ago = '-'
    const now = Date.now();
    if (null !== prevHeight && height !== prevHeight)
        heightChangedAt = now;

    const heightAgo = null !== heightChangedAt ?
        `${Math.floor((now - heightChangedAt) / 1000)}s ago` :
        '-';

To show how long ago the chain height last changed, the code stores the previous height and the time at which it was last updated.

Whenever a new block arrives and the height changes, the timestamp is refreshed. The elapsed time is then displayed alongside the current chain height.

Polling Loop⚓︎

        print(
            f'Height: {height:>10,}  (changed {height_ago})'
            f'  |  Irreversible: {irreversible_height:>10,}'
        )

        prev_height = height
        time.sleep(1)
    const heightLabel = height.toLocaleString().padStart(10);
    const irreversibleLabel =
        irreversibleHeight.toLocaleString().padStart(10);
    console.log(
        `Height: ${heightLabel}  (changed ${heightAgo})` +
        `  |  Irreversible: ${irreversibleLabel}`
    );

    prevHeight = height;
    await new Promise(resolve => { setTimeout(resolve, 1000); });

Each iteration prints a single status line showing:

  • The current chain height and how many seconds have elapsed since it last changed.
  • The irreversible height.

The loop then sleeps for one second before querying the node again.

Output⚓︎

The following output shows a typical run monitoring the chain height and the irreversible height:

1
2
3
4
5
6
7
8
Using node http://libertalia.nemtest.net:7890
Height:    659,471  (changed -)  |  Irreversible:    659,111
Height:    659,471  (changed -)  |  Irreversible:    659,111
Height:    659,471  (changed -)  |  Irreversible:    659,111
Height:    659,472  (changed 0s ago)  |  Irreversible:    659,112
Height:    659,472  (changed 1s ago)  |  Irreversible:    659,112
Height:    659,472  (changed 2s ago)  |  Irreversible:    659,112
Height:    659,472  (changed 3s ago)  |  Irreversible:    659,112

Some highlights from the output:

  • Before a new block (lines 2 to 4): The chain height remains unchanged while the program continues polling.
  • A new block arrives (line 5): The chain height advances from 659,471 to 659,472. The irreversible height advances as well.

Chain height vs. irreversible height

The irreversible height always trails the chain height by the rewrite limit. Transactions near the chain tip may still be rolled back. Once their block falls below the rewrite limit, they become irreversible.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch chain height /chain/height GET

Next Steps⚓︎

For an event-driven approach to monitoring new blocks, see the Listening to New Blocks WebSocket tutorial.