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
Component:
com.bloxbean.cardano:yaci:0.4.0— CBOR relay decoderSeverity: 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
SingleHostAddrrelay with an IPv6 address,yacistores 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:
pool19h8ak9gu37rgsdvmy5srf2v84km749fwalg4vts050tt6quzd9tHex:
2dcfdb151c8f8688359b252034a987adb7ea952eefd1562e0fa3d6bdRegistration tx:
f131df3994201637f573ded80a24baa22c8d34eddf9585ea5f2635a223b69f44Network: preprod
2504:4523:0:a12c:6705:0:b523:73562345:425:2ca1:0:0:567:5673:23b5All 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):
Correct big-endian groups →
2504:4523:0000:a12c:6705:0000:b523:7356yaci 0.4.0 reads each 2-byte group in little-endian order:
→
0425:2345:0000:2ca1:0567:0000:23b5:5673After
::zero-compression (RFC 5952 — compress the first longest run):→
425:2345:2ca1::567:5673:23b5which formats as2345:425:2ca1::567:5673:23b5This matches exactly what yaci-store persists in
pool_registration.relays.Verification:
Fix location
The fix must be made in the
yacilibrary in theSingleHostAddr/ 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/SingleHostAddrtypes