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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions