mirror of
https://github.com/luxfi/session.git
synced 2026-10-30 15:17:14 +00:00
344 lines
8.3 KiB
Go
344 lines
8.3 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// Package network provides P2P networking abstractions for the session layer.
|
|
// Supports post-quantum secure transport using hybrid key encapsulation.
|
|
package network
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/luxfi/session/core"
|
|
)
|
|
|
|
// MessageType identifies the type of network message.
|
|
type MessageType uint8
|
|
|
|
const (
|
|
MessageTypeUnknown MessageType = iota
|
|
MessageTypeHandshake
|
|
MessageTypeSessionCreate
|
|
MessageTypeSessionStart
|
|
MessageTypeOracleRequest
|
|
MessageTypeOracleRecord
|
|
MessageTypeOracleCommit
|
|
MessageTypeAttestation
|
|
MessageTypeReceipt
|
|
MessageTypeHeartbeat
|
|
MessageTypePing
|
|
MessageTypePong
|
|
)
|
|
|
|
func (t MessageType) String() string {
|
|
switch t {
|
|
case MessageTypeHandshake:
|
|
return "handshake"
|
|
case MessageTypeSessionCreate:
|
|
return "session_create"
|
|
case MessageTypeSessionStart:
|
|
return "session_start"
|
|
case MessageTypeOracleRequest:
|
|
return "oracle_request"
|
|
case MessageTypeOracleRecord:
|
|
return "oracle_record"
|
|
case MessageTypeOracleCommit:
|
|
return "oracle_commit"
|
|
case MessageTypeAttestation:
|
|
return "attestation"
|
|
case MessageTypeReceipt:
|
|
return "receipt"
|
|
case MessageTypeHeartbeat:
|
|
return "heartbeat"
|
|
case MessageTypePing:
|
|
return "ping"
|
|
case MessageTypePong:
|
|
return "pong"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// Message represents a network message.
|
|
type Message struct {
|
|
// Type identifies the message type
|
|
Type MessageType `json:"type"`
|
|
|
|
// From is the sender's node ID
|
|
From core.ID `json:"from"`
|
|
|
|
// To is the recipient's node ID (empty for broadcast)
|
|
To core.ID `json:"to,omitempty"`
|
|
|
|
// Payload is the message data
|
|
Payload []byte `json:"payload"`
|
|
|
|
// Timestamp when the message was created
|
|
Timestamp time.Time `json:"timestamp"`
|
|
|
|
// Signature from the sender
|
|
Signature []byte `json:"signature,omitempty"`
|
|
}
|
|
|
|
// Transport is the core networking interface.
|
|
type Transport interface {
|
|
// Start starts the transport.
|
|
Start(ctx context.Context) error
|
|
|
|
// Stop stops the transport.
|
|
Stop() error
|
|
|
|
// Send sends a message to a specific node.
|
|
Send(ctx context.Context, to core.ID, msg *Message) error
|
|
|
|
// Broadcast sends a message to all known peers.
|
|
Broadcast(ctx context.Context, msg *Message) error
|
|
|
|
// Receive returns a channel for receiving messages.
|
|
Receive() <-chan *Message
|
|
|
|
// Connect connects to a peer.
|
|
Connect(ctx context.Context, addr string) error
|
|
|
|
// Disconnect disconnects from a peer.
|
|
Disconnect(nodeID core.ID) error
|
|
|
|
// Peers returns the list of connected peer IDs.
|
|
Peers() []core.ID
|
|
|
|
// LocalID returns the local node ID.
|
|
LocalID() core.ID
|
|
}
|
|
|
|
// Connection represents a connection to a peer.
|
|
type Connection interface {
|
|
// Read reads data from the connection.
|
|
Read(p []byte) (n int, err error)
|
|
|
|
// Write writes data to the connection.
|
|
Write(p []byte) (n int, err error)
|
|
|
|
// Close closes the connection.
|
|
Close() error
|
|
|
|
// RemoteID returns the remote node ID.
|
|
RemoteID() core.ID
|
|
|
|
// RemoteAddr returns the remote address.
|
|
RemoteAddr() string
|
|
|
|
// LocalAddr returns the local address.
|
|
LocalAddr() string
|
|
}
|
|
|
|
// Listener listens for incoming connections.
|
|
type Listener interface {
|
|
// Accept accepts a new connection.
|
|
Accept() (Connection, error)
|
|
|
|
// Close closes the listener.
|
|
Close() error
|
|
|
|
// Addr returns the listener address.
|
|
Addr() string
|
|
}
|
|
|
|
// Dialer dials outgoing connections.
|
|
type Dialer interface {
|
|
// Dial dials a connection to the address.
|
|
Dial(ctx context.Context, addr string) (Connection, error)
|
|
}
|
|
|
|
// Handler processes incoming messages.
|
|
type Handler interface {
|
|
// Handle processes a message and optionally returns a response.
|
|
Handle(ctx context.Context, msg *Message) (*Message, error)
|
|
}
|
|
|
|
// HandlerFunc is a function that implements Handler.
|
|
type HandlerFunc func(ctx context.Context, msg *Message) (*Message, error)
|
|
|
|
// Handle implements Handler.
|
|
func (f HandlerFunc) Handle(ctx context.Context, msg *Message) (*Message, error) {
|
|
return f(ctx, msg)
|
|
}
|
|
|
|
// Router routes messages to handlers.
|
|
type Router struct {
|
|
handlers map[MessageType]Handler
|
|
}
|
|
|
|
// NewRouter creates a new message router.
|
|
func NewRouter() *Router {
|
|
return &Router{
|
|
handlers: make(map[MessageType]Handler),
|
|
}
|
|
}
|
|
|
|
// Register registers a handler for a message type.
|
|
func (r *Router) Register(msgType MessageType, handler Handler) {
|
|
r.handlers[msgType] = handler
|
|
}
|
|
|
|
// RegisterFunc registers a handler function for a message type.
|
|
func (r *Router) RegisterFunc(msgType MessageType, f HandlerFunc) {
|
|
r.handlers[msgType] = f
|
|
}
|
|
|
|
// Route routes a message to its handler.
|
|
func (r *Router) Route(ctx context.Context, msg *Message) (*Message, error) {
|
|
handler, ok := r.handlers[msg.Type]
|
|
if !ok {
|
|
return nil, errors.New("no handler for message type")
|
|
}
|
|
return handler.Handle(ctx, msg)
|
|
}
|
|
|
|
// StreamConn wraps a connection with framed message reading/writing.
|
|
type StreamConn struct {
|
|
conn Connection
|
|
}
|
|
|
|
// NewStreamConn creates a new stream connection wrapper.
|
|
func NewStreamConn(conn Connection) *StreamConn {
|
|
return &StreamConn{conn: conn}
|
|
}
|
|
|
|
// WriteMessage writes a framed message.
|
|
func (s *StreamConn) WriteMessage(msg *Message) error {
|
|
// Simple length-prefixed framing
|
|
// In production, use proper serialization
|
|
data := encodeMessage(msg)
|
|
lenBuf := make([]byte, 4)
|
|
lenBuf[0] = byte(len(data) >> 24)
|
|
lenBuf[1] = byte(len(data) >> 16)
|
|
lenBuf[2] = byte(len(data) >> 8)
|
|
lenBuf[3] = byte(len(data))
|
|
|
|
_, err := s.conn.Write(lenBuf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = s.conn.Write(data)
|
|
return err
|
|
}
|
|
|
|
// ReadMessage reads a framed message.
|
|
func (s *StreamConn) ReadMessage() (*Message, error) {
|
|
lenBuf := make([]byte, 4)
|
|
_, err := io.ReadFull(s.conn, lenBuf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
length := uint32(lenBuf[0])<<24 | uint32(lenBuf[1])<<16 | uint32(lenBuf[2])<<8 | uint32(lenBuf[3])
|
|
if length > 1<<20 { // 1MB max
|
|
return nil, errors.New("message too large")
|
|
}
|
|
|
|
data := make([]byte, length)
|
|
_, err = io.ReadFull(s.conn, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return decodeMessage(data)
|
|
}
|
|
|
|
// Close closes the underlying connection.
|
|
func (s *StreamConn) Close() error {
|
|
return s.conn.Close()
|
|
}
|
|
|
|
// encodeMessage encodes a message to bytes using length-prefixed binary format.
|
|
func encodeMessage(msg *Message) []byte {
|
|
buf := make([]byte, 1+32+32+4+len(msg.Payload)+8+4+len(msg.Signature))
|
|
offset := 0
|
|
|
|
buf[offset] = byte(msg.Type)
|
|
offset++
|
|
|
|
copy(buf[offset:], msg.From[:])
|
|
offset += 32
|
|
|
|
copy(buf[offset:], msg.To[:])
|
|
offset += 32
|
|
|
|
buf[offset] = byte(len(msg.Payload) >> 24)
|
|
buf[offset+1] = byte(len(msg.Payload) >> 16)
|
|
buf[offset+2] = byte(len(msg.Payload) >> 8)
|
|
buf[offset+3] = byte(len(msg.Payload))
|
|
offset += 4
|
|
|
|
copy(buf[offset:], msg.Payload)
|
|
offset += len(msg.Payload)
|
|
|
|
ts := msg.Timestamp.UnixNano()
|
|
buf[offset] = byte(ts >> 56)
|
|
buf[offset+1] = byte(ts >> 48)
|
|
buf[offset+2] = byte(ts >> 40)
|
|
buf[offset+3] = byte(ts >> 32)
|
|
buf[offset+4] = byte(ts >> 24)
|
|
buf[offset+5] = byte(ts >> 16)
|
|
buf[offset+6] = byte(ts >> 8)
|
|
buf[offset+7] = byte(ts)
|
|
offset += 8
|
|
|
|
buf[offset] = byte(len(msg.Signature) >> 24)
|
|
buf[offset+1] = byte(len(msg.Signature) >> 16)
|
|
buf[offset+2] = byte(len(msg.Signature) >> 8)
|
|
buf[offset+3] = byte(len(msg.Signature))
|
|
offset += 4
|
|
|
|
copy(buf[offset:], msg.Signature)
|
|
|
|
return buf
|
|
}
|
|
|
|
// decodeMessage decodes a message from bytes.
|
|
func decodeMessage(data []byte) (*Message, error) {
|
|
if len(data) < 1+32+32+4 {
|
|
return nil, errors.New("data too short")
|
|
}
|
|
|
|
msg := &Message{}
|
|
offset := 0
|
|
|
|
msg.Type = MessageType(data[offset])
|
|
offset++
|
|
|
|
copy(msg.From[:], data[offset:])
|
|
offset += 32
|
|
|
|
copy(msg.To[:], data[offset:])
|
|
offset += 32
|
|
|
|
payloadLen := int(data[offset])<<24 | int(data[offset+1])<<16 | int(data[offset+2])<<8 | int(data[offset+3])
|
|
offset += 4
|
|
|
|
if len(data) < offset+payloadLen+8+4 {
|
|
return nil, errors.New("data too short for payload")
|
|
}
|
|
|
|
msg.Payload = make([]byte, payloadLen)
|
|
copy(msg.Payload, data[offset:])
|
|
offset += payloadLen
|
|
|
|
ts := int64(data[offset])<<56 | int64(data[offset+1])<<48 | int64(data[offset+2])<<40 | int64(data[offset+3])<<32 |
|
|
int64(data[offset+4])<<24 | int64(data[offset+5])<<16 | int64(data[offset+6])<<8 | int64(data[offset+7])
|
|
msg.Timestamp = time.Unix(0, ts)
|
|
offset += 8
|
|
|
|
sigLen := int(data[offset])<<24 | int(data[offset+1])<<16 | int(data[offset+2])<<8 | int(data[offset+3])
|
|
offset += 4
|
|
|
|
if sigLen > 0 && len(data) >= offset+sigLen {
|
|
msg.Signature = make([]byte, sigLen)
|
|
copy(msg.Signature, data[offset:])
|
|
}
|
|
|
|
return msg, nil
|
|
}
|