Skip to content

Bug: IPv6 relay addresses decoded with wrong byte order (yaci 0.4.0) #152

Description

@ducpm2303

Component: com.bloxbean.cardano:yaci:0.4.0 — CBOR relay decoder
Severity: Data correctness — wrong value returned, not crash
Discovered: 2026-04-17 during Blockfrost parity validation on preprod


Summary

When a pool registration certificate contains a SingleHostAddr relay with an IPv6 address, yaci stores the address with each 2-byte group byte-swapped relative to the correct network (big-endian) representation. The result is an IPv6 string that contains the same 8 non-zero nibble groups as the correct address, but in wrong order due to the endianness inversion and the resulting change in :: zero-compression placement.


Reproduction

Pool: pool19h8ak9gu37rgsdvmy5srf2v84km749fwalg4vts050tt6quzd9t
Hex: 2dcfdb151c8f8688359b252034a987adb7ea952eefd1562e0fa3d6bd
Registration tx: f131df3994201637f573ded80a24baa22c8d34eddf9585ea5f2635a223b69f44
Network: preprod

Source IPv6 value
Blockfrost (correct) 2504:4523:0:a12c:6705:0:b523:7356
yaci-store local 2345:425:2ca1:0:0:567:5673:23b5

All six registrations for this pool store the same wrong value, meaning the bug is present across the full history of this pool's relay updates.


Root-cause analysis

IPv6 relay addresses in Cardano CBOR are encoded as a 16-byte raw byte string. The correct decoding reads each of the 8 two-byte groups in big-endian order.

Actual raw bytes for the address above (hex):

25 04 45 23 00 00 a1 2c 67 05 00 00 b5 23 73 56

Correct big-endian groups → 2504:4523:0000:a12c:6705:0000:b523:7356

yaci 0.4.0 reads each 2-byte group in little-endian order:

04 25 → 0x0425   23 45 → 0x2345   00 00 → 0x0000   2c a1 → 0x2ca1
05 67 → 0x0567   00 00 → 0x0000   23 b5 → 0x23b5   56 73 → 0x5673

0425:2345:0000:2ca1:0567:0000:23b5:5673

After :: zero-compression (RFC 5952 — compress the first longest run):
425:2345:2ca1::567:5673:23b5 which formats as 2345:425:2ca1::567:5673:23b5

This matches exactly what yaci-store persists in pool_registration.relays.

Verification:

import socket
bf_bytes    = bytes.fromhex('250445230000a12c67050000b5237356')
local_bytes = bytes.fromhex('234504252ca1000000000567567323b5')

bf_groups_le = [int.from_bytes(bf_bytes[i:i+2], 'little') for i in range(0, 16, 2)]
local_groups = [int.from_bytes(local_bytes[i:i+2], 'big')  for i in range(0, 16, 2)]

# sorted non-zero groups are identical — same data, wrong byte order
assert sorted([g for g in bf_groups_le if g]) == sorted([g for g in local_groups if g])

Fix location

The fix must be made in the yaci library in the SingleHostAddr / relay CBOR decoder, changing the 2-byte group read from little-endian to big-endian.

Repo: https://github.com/bloxbean/yaci
Relevant area: CBOR deserialization of Relay / SingleHostAddr types

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions