go-p2p/node/levin/header.go
Claude 7089e0990c
feat(levin): header encode/decode (33-byte Levin packet framing)
Co-Authored-By: Charon <charon@lethean.io>
2026-02-20 19:23:24 +00:00

95 lines
2.9 KiB
Go

// Copyright (c) 2024-2026 Lethean Contributors
// SPDX-License-Identifier: EUPL-1.2
// Package levin implements the CryptoNote Levin binary protocol.
// It is a standalone package with no imports from the parent node package.
package levin
import (
"encoding/binary"
"errors"
)
// HeaderSize is the exact byte length of a serialised Levin header.
const HeaderSize = 33
// Signature is the magic value that opens every Levin packet.
const Signature uint64 = 0x0101010101012101
// MaxPayloadSize is the upper bound we accept for a single payload (100 MB).
const MaxPayloadSize uint64 = 100 * 1024 * 1024
// Return-code constants carried in every Levin response.
const (
ReturnOK int32 = 0
ReturnErrConnection int32 = -1
ReturnErrFormat int32 = -7
ReturnErrSignature int32 = -13
)
// Command IDs for the CryptoNote P2P layer.
const (
CommandHandshake uint32 = 1001
CommandTimedSync uint32 = 1002
CommandPing uint32 = 1003
CommandNewBlock uint32 = 2001
CommandNewTransactions uint32 = 2002
CommandRequestObjects uint32 = 2003
CommandResponseObjects uint32 = 2004
CommandRequestChain uint32 = 2006
CommandResponseChain uint32 = 2007
)
// Sentinel errors returned by DecodeHeader.
var (
ErrBadSignature = errors.New("levin: bad signature")
ErrPayloadTooBig = errors.New("levin: payload exceeds maximum size")
)
// Header is the 33-byte packed header that prefixes every Levin message.
type Header struct {
Signature uint64
PayloadSize uint64
ExpectResponse bool
Command uint32
ReturnCode int32
Flags uint32
ProtocolVersion uint32
}
// EncodeHeader serialises h into a fixed-size 33-byte array (little-endian).
func EncodeHeader(h *Header) [HeaderSize]byte {
var buf [HeaderSize]byte
binary.LittleEndian.PutUint64(buf[0:8], h.Signature)
binary.LittleEndian.PutUint64(buf[8:16], h.PayloadSize)
if h.ExpectResponse {
buf[16] = 0x01
} else {
buf[16] = 0x00
}
binary.LittleEndian.PutUint32(buf[17:21], h.Command)
binary.LittleEndian.PutUint32(buf[21:25], uint32(h.ReturnCode))
binary.LittleEndian.PutUint32(buf[25:29], h.Flags)
binary.LittleEndian.PutUint32(buf[29:33], h.ProtocolVersion)
return buf
}
// DecodeHeader deserialises a 33-byte array into a Header, validating
// the magic signature.
func DecodeHeader(buf [HeaderSize]byte) (Header, error) {
var h Header
h.Signature = binary.LittleEndian.Uint64(buf[0:8])
if h.Signature != Signature {
return Header{}, ErrBadSignature
}
h.PayloadSize = binary.LittleEndian.Uint64(buf[8:16])
if h.PayloadSize > MaxPayloadSize {
return Header{}, ErrPayloadTooBig
}
h.ExpectResponse = buf[16] == 0x01
h.Command = binary.LittleEndian.Uint32(buf[17:21])
h.ReturnCode = int32(binary.LittleEndian.Uint32(buf[21:25]))
h.Flags = binary.LittleEndian.Uint32(buf[25:29])
h.ProtocolVersion = binary.LittleEndian.Uint32(buf[29:33])
return h, nil
}