Byte Codec
API | Type Rules | Examples | RLP Comparison
Overview
The Sigilaris byte codec provides deterministic binary encoding and decoding for blockchain applications. When signing transactions or computing block hashes, data must first be converted to a deterministic byte sequence—the same data must always produce the same bytes to ensure hash and signature correctness.
Why Deterministic Encoding?
In blockchain systems:
- Transaction Signing: Users sign the byte representation of transactions
- Block Hashing: Block headers are hashed to create block IDs
- Merkle Trees: Data structures require consistent byte ordering
- Consensus: All nodes must agree on byte representations
Any non-deterministic encoding (e.g., random collection ordering) breaks consensus.
Key Features
- Variable-Length Encoding: Space-efficient encoding similar to RLP but with distinct design choices
- Deterministic Collections: Sets and Maps are lexicographically sorted after encoding
- Type-Safe API: Built on Scala 3 + cats ecosystem with contravariant/covariant functors
- Automatic Derivation: Case classes and sealed traits via
Mirror.ProductOf
Quick Start (30 seconds)
import org.sigilaris.core.codec.byte.*
import scodec.bits.ByteVector
case class Transaction(from: Long, to: Long, amount: Long)
val tx = Transaction(from = 1L, to = 2L, amount = 100L)
val encoded: ByteVector = ByteEncoder[Transaction].encode(tx)
val decoded = ByteDecoder[Transaction].decode(encoded)
That's it! The codec automatically derives instances for case classes.
Documentation
- API Reference:
ByteEncoder
,ByteDecoder
,ByteCodec
details - Type Rules: Encoding/decoding rules for
BigNat
,BigInt
, collections - Practical Examples: Blockchain data structures
- RLP Comparison: Differences from Ethereum RLP
What's Included
Basic Types
Unit
,Byte
,Long
,Instant
: Direct encodingBigInt
: Sign-aware variable-length encoding
Collections
List[A]
: Size prefix + ordered elementsOption[A]
: Encoded asList[A]
(0 or 1 element)Set[A]
: Lexicographically sorted after encodingMap[K, V]
: Treated asSet[(K, V)]
for determinism
Automatic Derivation
import org.sigilaris.core.codec.byte.*
case class Block(height: Long, txCount: Long)
// Instances automatically derived
val block = Block(height = 1L, txCount = 10L)
ByteEncoder[Block].encode(block)
Design Philosophy
Separation of Concerns
This codec library handles byte encoding only. Hashing and signing are separate modules:
- Codec:
Data → ByteVector
- Crypto:
ByteVector → Hash/Signature
(future module)
Determinism Guarantee
- Collections are sorted by encoded byte representation
- Same input always produces same output
- Independent of platform, JVM version, or execution order
Performance
- Small integers (0-128) use single bytes
- Variable-length encoding minimizes space
- Tail-recursive implementations for stack safety
Example: Transaction Encoding
import org.sigilaris.core.codec.byte.*
import scodec.bits.ByteVector
case class Address(id: Long)
case class Transaction(
from: Address,
to: Address,
amount: Long,
nonce: Long
)
val tx = Transaction(
from = Address(100L),
to = Address(200L),
amount = 5000L,
nonce = 42L
)
val bytes = ByteEncoder[Transaction].encode(tx)
val roundtrip = ByteDecoder[Transaction].decode(bytes)
The encoded bytes can now be hashed or signed (using future crypto modules).
Next Steps
- API Reference: Learn about
contramap
,emap
,flatMap
combinators - Type Rules: Understand
BigNat
/BigInt
variable-length encoding - Examples: See complete blockchain data structures
- RLP Comparison: Compare with Ethereum's RLP encoding
Limitations and Scope
- No Hashing/Signing: This module only handles byte encoding
- No Compression: Data is not compressed (use separate compression library if needed)
- Not RLP Compatible: Similar design but different byte layout
Performance Characteristics
- Encoding: O(n) where n is the size of the data structure
- Decoding: O(n) with early exit on errors
- Space: Variable-length encoding minimizes bytes for small values
- Collections: Sorting overhead is O(k log k) where k is collection size