Lesson 2 of 4

Fetch a Specific Block from a Node

In 101.1 you connected to Dolos and asked "where is the chain now?" That question used ChainSync on a local socket. In this lesson you'll ask a different question — "give me this specific block" — and for that gOuroboros uses a different mini-protocol, BlockFetch, against a Node-to-Node peer.

By the end of this lesson, you will fetch one historical block by slot and hash, and print its era, transactions, and raw CBOR.


When You Use BlockFetch

BlockFetch is the right mini-protocol when:

  • You already know the (slot, hash) pair of the block you want (from an explorer, an event, a log)
  • You want to fetch it once and exit, not stream forward from a point
  • You're comfortable connecting Node-to-Node over TCP — BlockFetch is an NtN protocol

If you instead want to stream blocks forward from a starting point, that's ChainSync + BlockFetch chained together, which the chain-sync program in the starter-kit demonstrates.


Prerequisites

  • Completed 101.1 (starter-kit cloned, working)
  • A (slot, hash) pair for a preprod block you want to fetch — you can grab one from Cardanoscan preprod
  • A public preprod relay address (one is provided below)

The Program

Open cmd/block-fetch/main.go in the starter-kit. It reads seven environment variables, all prefixed with BLOCK_FETCH_:

Variable

Purpose

BLOCK_FETCH_ADDRESS

host:port of a Cardano relay

BLOCK_FETCH_NETWORK

Named network (preprod, preview, mainnet); fills in the magic

BLOCK_FETCH_NETWORK_MAGIC

Magic number (overrides the name)

BLOCK_FETCH_SLOT

Slot of the block

BLOCK_FETCH_HASH

Hex block hash

BLOCK_FETCH_RETURN_CBOR

true to dump raw CBOR bytes instead of human text

Defaults target the first mainnet Babbage block. You'll override that to hit a preprod block.

The interesting code path (paraphrased from the source):

o, err := ouroboros.NewConnection(
    ouroboros.WithNetworkMagic(cfg.NetworkMagic),
    ouroboros.WithErrorChan(errorChan),
    ouroboros.WithNodeToNode(true),          // NtN, not NtC
    ouroboros.WithKeepAlive(true),
)
// ...
o.Dial("tcp", cfg.Address)

blockHash, _ := hex.DecodeString(cfg.Hash)
block, err := o.BlockFetch().Client.GetBlock(
    ocommon.NewPoint(cfg.Slot, blockHash),
)

Two pieces to notice:

  • WithNodeToNode(true) — opposite of what chain-tip used. BlockFetch does not run on the NtC mux.
  • GetBlock(point) returns a ledger.Block, which the program then type-switches on to print era-specific fields.

Step 1: Pick a Block to Fetch

Visit Cardanoscan preprod, click any recent block, and note:

  • Block number (for your own reference)
  • Slot number → this is BLOCK_FETCH_SLOT
  • Block hash (64 hex chars) → BLOCK_FETCH_HASH

For this lesson the specific block doesn't matter — pick anything in the last ~epoch.


Step 2: Run the Program

export BLOCK_FETCH_NETWORK=preprod
export BLOCK_FETCH_ADDRESS=preprod-node.world.dev.cardano.org:3001
export BLOCK_FETCH_SLOT=<your slot>
export BLOCK_FETCH_HASH=<your hash>

go run ./cmd/block-fetch

Expected output (abbreviated):

Block: era = Babbage, slot = 56123456, block_no = 2834721, id = abc123...
Block CBOR: 85828a1a...
Minted by: pool1... (f1c9...)
Transactions:
- Hash: 0deadbeef...
- CBOR: a500...
  Inputs:
    - ...
  Outputs:
    - ...

If the block has no transactions you'll still see the era, slot, block number, and issuer.


Step 3: Try the CBOR Mode

Every block is ultimately a chunk of CBOR. The program can dump just the raw bytes — useful when you want to feed the block to another tool, or inspect it with a CBOR decoder:

BLOCK_FETCH_RETURN_CBOR=true go run ./cmd/block-fetch > block.cbor
file block.cbor
xxd block.cbor | head

xxd will show you the byte layout. The first byte of a Cardano block is a CBOR array marker; the second is the era tag. You'll see more of this in Module 204 when you learn to read CBOR by hand.


What Connecting to a Public Relay Means

Unlike chain-tip which talks to Dolos via a local socket, block-fetch reaches out across the internet:

  your Go program               public preprod relay
  ─────────────────             ────────────────────
  NewConnection(NtN=true)
        │
        │  TCP dial, port 3001
        ├────────────────────────────>│
        │  NtN handshake              │
        │<────────────────────────────┤
        │                             │
        │  BlockFetch.GetBlock(pt)    │
        ├────────────────────────────>│  reads block from its chain store
        │<────── block bytes ─────────┤
        │
        decode, print, exit

You do not need Dolos for this program. That's a feature — BlockFetch is useful even when you don't run local infrastructure. The trade-off is latency (TCP hop) and availability (the relay has to be up).


Common Issues

timeout connecting to preprod-node.world.dev.cardano.org:3001

Relay is unreachable. Try another preprod relay, or check your firewall. Ports 3000/3001 may be blocked on some networks.

block not found or empty block

The (slot, hash) pair is wrong. Make sure they came from the same row on the explorer.

handshake failed

BLOCK_FETCH_NETWORK=preprod is required — the default is mainnet, and preprod blocks won't resolve against mainnet magic.


You'll Know You're Successful When

  • You can fetch any preprod block by slot + hash
  • Era, slot, block number, and block hash in the output match the explorer
  • BLOCK_FETCH_RETURN_CBOR=true writes a binary file that's recognisably CBOR

Practice Tasks

  • Fetch the latest preprod block, then fetch the block 10 slots older. Compare their outputs — what fields change, what stays constant?
  • Fetch the same block twice and diff the CBOR outputs — they should be byte-identical.
  • Read cmd/block-fetch/main.go and find where the type switch decides between *ledger.ByronMainBlock and a generic ledger.Block. What does this tell you about Cardano's ledger type system?

What's Next

  • 101.3 — check how far a node has synced using chain tip queries
  • 101.4 — inspect pending transactions in the node's mempool