protocol

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package protocol implements the Discovery v4 wire protocol.

The discv4 protocol uses UDP packets with the following structure:

  • MAC (32 bytes): Keccak256 hash of signature + packet data
  • Signature (65 bytes): ECDSA signature over packet hash
  • Packet type (1 byte): Message type identifier
  • RLP-encoded message data

Total header size: 97 bytes (32 + 65)

Index

Constants

View Source
const (
	PingPacket = iota + 1 // zero is reserved
	PongPacket
	FindnodePacket
	NeighborsPacket
	ENRRequestPacket
	ENRResponsePacket
)

Packet type constants

View Source
const MaxNeighbors = 12

MaxNeighbors is the maximum number of nodes in a Neighbors packet. This limit ensures packets fit within the 1280 byte MTU.

Variables

View Source
var (
	// ErrPacketTooSmall is returned when a packet is smaller than the minimum size
	ErrPacketTooSmall = errors.New("packet too small")

	// ErrBadHash is returned when the MAC hash doesn't match
	ErrBadHash = errors.New("bad hash")

	// ErrBadSignature is returned when signature recovery fails
	ErrBadSignature = errors.New("bad signature")

	// ErrUnknownPacket is returned for unknown packet types
	ErrUnknownPacket = errors.New("unknown packet type")

	// ErrExpired is returned when a packet has expired
	ErrExpired = errors.New("packet expired")
)

Functions

func Decode

func Decode(input []byte) (Packet, Pubkey, []byte, error)

Decode decodes a discv4 UDP packet.

Returns:

  • packet: The decoded packet message
  • fromKey: The sender's public key (64 bytes, uncompressed without 0x04)
  • hash: The packet hash (used as reply token)
  • error: Any decoding error

The packet structure is:

[MAC 32][Signature 65][Type 1][RLP Data...]

func DecodePubkey

func DecodePubkey(curve elliptic.Curve, p Pubkey) (*ecdsa.PublicKey, error)

DecodePubkey decodes a wire-format public key to ECDSA format.

func Encode

func Encode(priv *ecdsa.PrivateKey, req Packet) (packet, hash []byte, err error)

Encode encodes a discv4 packet with signature.

Returns:

  • packet: The complete encoded packet
  • hash: The packet hash (used as reply token)
  • error: Any encoding error

The packet structure is:

[MAC 32][Signature 65][Type 1][RLP Data...]

func EncodePacket

func EncodePacket(priv *ecdsa.PrivateKey, req Packet) ([]byte, error)

EncodePacket is a convenience function to encode a packet.

This is useful when you just need the packet bytes without the hash.

func Expired

func Expired(ts uint64) bool

Expired checks if a UNIX timestamp is in the past.

func MakeExpiration

func MakeExpiration(d time.Duration) uint64

MakeExpiration creates an expiration timestamp for the given duration from now.

Types

type ENRRequest

type ENRRequest struct {
	// Expiration is the UNIX timestamp when this packet expires
	Expiration uint64

	// Rest allows forward compatibility
	Rest []rlp.RawValue `rlp:"tail"`
}

ENRRequest represents an ENRREQUEST message.

ENRRequest queries for the node's current ENR record. This allows nodes to retrieve updated ENR information (added by EIP-868).

func (*ENRRequest) Kind

func (e *ENRRequest) Kind() byte

Kind returns the packet type byte.

func (*ENRRequest) Name

func (e *ENRRequest) Name() string

Name returns the packet name.

type ENRResponse

type ENRResponse struct {
	// ReplyTok is the hash of the ENRREQUEST packet we're responding to
	ReplyTok []byte

	// Record is the sender's ENR record
	Record *enr.Record

	// Rest allows forward compatibility
	Rest []rlp.RawValue `rlp:"tail"`
}

ENRResponse represents an ENRRESPONSE message (reply to ENRREQUEST).

ENRResponse contains the node's current ENR record.

func (*ENRResponse) Kind

func (e *ENRResponse) Kind() byte

Kind returns the packet type byte.

func (*ENRResponse) Name

func (e *ENRResponse) Name() string

Name returns the packet name.

type Endpoint

type Endpoint struct {
	// IP is the IP address (4 bytes for IPv4, 16 for IPv6)
	IP net.IP

	// UDP is the UDP port
	UDP uint16

	// TCP is the TCP port
	TCP uint16
}

Endpoint represents a network endpoint.

This is used in PING/PONG messages to communicate address information.

func NewEndpoint

func NewEndpoint(addr *net.UDPAddr, tcpPort uint16) Endpoint

NewEndpoint creates an endpoint from a UDP address.

func (Endpoint) TCPAddr

func (e Endpoint) TCPAddr() *net.TCPAddr

TCPAddr converts the endpoint to a TCPAddr.

func (Endpoint) UDPAddr

func (e Endpoint) UDPAddr() *net.UDPAddr

UDPAddr converts the endpoint to a UDPAddr.

type Findnode

type Findnode struct {
	// Target is the public key we're searching for neighbors of
	// The response should contain nodes close to this target
	Target Pubkey

	// Expiration is the UNIX timestamp when this packet expires
	Expiration uint64

	// Rest allows forward compatibility
	Rest []rlp.RawValue `rlp:"tail"`
}

Findnode represents a FINDNODE request.

Findnode queries for nodes close to a target in the Kademlia DHT. The sender must have an active bond (recent PING/PONG exchange) before the recipient will respond.

func (*Findnode) Kind

func (f *Findnode) Kind() byte

Kind returns the packet type byte.

func (*Findnode) Name

func (f *Findnode) Name() string

Name returns the packet name.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler handles incoming and outgoing discv4 protocol messages.

The handler is responsible for:

  • Encoding/decoding packets
  • Processing PING, PONG, FINDNODE, NEIGHBORS, ENRREQUEST, ENRRESPONSE
  • Bond tracking and validation
  • Request/response matching
  • Callbacks for application logic

func NewHandler

func NewHandler(ctx context.Context, config HandlerConfig, transport Transport) *Handler

NewHandler creates a new protocol handler.

func (*Handler) AllNodes

func (h *Handler) AllNodes() []*node.Node

AllNodes returns all known nodes.

func (*Handler) Findnode

func (h *Handler) Findnode(n *node.Node, target []byte) ([]*node.Node, error)

Findnode sends a FINDNODE request to a node.

func (*Handler) GetNode

func (h *Handler) GetNode(id node.ID) *node.Node

GetNode returns a node by ID.

func (*Handler) HandlePacket

func (h *Handler) HandlePacket(data []byte, from *net.UDPAddr, localAddr *net.UDPAddr) (err error)

HandlePacket processes an incoming UDP packet.

This is called by the transport layer when a packet is received. The localAddr parameter is the local address that received the packet.

func (*Handler) Ping

func (h *Handler) Ping(n *node.Node) (*Pong, error)

Ping sends a PING request to a node.

func (*Handler) RequestENR

func (h *Handler) RequestENR(n *node.Node) (*enr.Record, error)

RequestENR sends an ENRREQUEST to a node.

func (*Handler) Stats

func (h *Handler) Stats() map[string]interface{}

Stats returns current statistics.

type HandlerConfig

type HandlerConfig struct {
	// PrivateKey is our node's private key
	PrivateKey *ecdsa.PrivateKey

	// LocalENR is our node's ENR record (optional)
	LocalENR *enr.Record

	// LocalAddr is our listening address
	LocalAddr *net.UDPAddr

	// BondExpiration is how long bonds last (default 24 hours)
	BondExpiration time.Duration

	// RequestTimeout is how long to wait for responses (default 500ms)
	RequestTimeout time.Duration

	// ExpirationWindow is the acceptable time range for packet expiration (default 20s)
	ExpirationWindow time.Duration

	// Callbacks (all optional)
	OnPing         OnPingCallback
	OnPongReceived OnPongReceivedCallback
	OnFindnode     OnFindnodeCallback
	OnENRRequest   OnENRRequestCallback
	OnNodeSeen     OnNodeSeenCallback
}

HandlerConfig contains configuration for the protocol handler.

type Neighbors

type Neighbors struct {
	// Nodes is the list of nodes close to the target
	Nodes []NodeRecord

	// Expiration is the UNIX timestamp when this packet expires
	Expiration uint64

	// Rest allows forward compatibility
	Rest []rlp.RawValue `rlp:"tail"`
}

Neighbors represents a NEIGHBORS response (reply to FINDNODE).

Neighbors contains up to 12 node records. If more nodes need to be sent, multiple Neighbors packets are used.

func (*Neighbors) Kind

func (n *Neighbors) Kind() byte

Kind returns the packet type byte.

func (*Neighbors) Name

func (n *Neighbors) Name() string

Name returns the packet name.

type NodeRecord

type NodeRecord struct {
	// IP is the node's IP address (4 bytes for IPv4, 16 for IPv6)
	IP net.IP

	// UDP is the UDP port for discovery
	UDP uint16

	// TCP is the TCP port for RLPx
	TCP uint16

	// ID is the node's public key
	ID Pubkey
}

NodeRecord represents information about a node in the DHT.

This is used in NEIGHBORS responses to communicate node information.

type OnENRRequestCallback

type OnENRRequestCallback func(from *node.Node) error

OnENRRequestCallback is called when an ENRREQUEST is received.

type OnFindnodeCallback

type OnFindnodeCallback func(from *node.Node, target []byte, requester *net.UDPAddr) []*node.Node

OnFindnodeCallback is called when a FINDNODE request is received. Returns the list of nodes to include in the response.

type OnNodeSeenCallback

type OnNodeSeenCallback func(n *node.Node, timestamp time.Time)

OnNodeSeenCallback is called when we receive any valid packet from a node.

type OnPingCallback

type OnPingCallback func(from *node.Node, ping *Ping) error

OnPingCallback is called when a PING request is received.

type OnPongReceivedCallback

type OnPongReceivedCallback func(from *node.Node, ip net.IP, port uint16)

OnPongReceivedCallback is called when a PONG response is received. The ip and port parameters contain our external address as seen by the remote peer.

type Packet

type Packet interface {
	// Name returns the packet name for logging
	Name() string
	// Kind returns the packet type byte
	Kind() byte
}

Packet is the interface for all discv4 messages.

func DecodePacket

func DecodePacket(input []byte) (Packet, error)

DecodePacket is a convenience function to decode a packet.

This is useful when you don't need the sender key or hash.

type PendingNeighborsResponse

type PendingNeighborsResponse struct {
	// Nodes accumulated so far
	Nodes []*node.Node

	// CreatedAt is when we received the first packet
	CreatedAt time.Time

	// LastRecv is when we received the last packet
	LastRecv time.Time
}

PendingNeighborsResponse tracks a multi-packet NEIGHBORS response.

type PendingRequest

type PendingRequest struct {
	// RequestHash is the hash of the outgoing packet (used as reply token)
	RequestHash []byte

	// ToNode is the destination node
	ToNode *node.Node

	// PacketType is the type of request
	PacketType byte

	// CreatedAt is when the request was created
	CreatedAt time.Time

	// Timeout is when the request expires
	Timeout time.Time

	// ResponseChan receives the response (or nil on timeout)
	ResponseChan chan interface{}
}

PendingRequest tracks an outgoing request waiting for a response.

type Ping

type Ping struct {
	// Version is the discovery protocol version (currently 4)
	Version uint

	// From is the sender's endpoint information
	From Endpoint

	// To is the recipient's endpoint (as known by sender)
	To Endpoint

	// Expiration is the UNIX timestamp when this packet expires
	Expiration uint64

	// ENRSeq is the sender's ENR sequence number (optional, EIP-868)
	ENRSeq uint64 `rlp:"optional"`

	// Rest allows forward compatibility with future fields
	Rest []rlp.RawValue `rlp:"tail"`
}

Ping represents a PING message.

Ping is used for:

  1. Liveness check
  2. Endpoint verification (NAT detection)
  3. Establishing bonds before FINDNODE
  4. ENR sequence number exchange (EIP-868)

func (*Ping) Kind

func (p *Ping) Kind() byte

Kind returns the packet type byte.

func (*Ping) Name

func (p *Ping) Name() string

Name returns the packet name.

type Pong

type Pong struct {
	// To is the recipient's endpoint as seen by the sender
	// This allows the recipient to discover their external address
	To Endpoint

	// ReplyTok is the hash of the PING packet we're responding to
	// This allows the requester to match PONG to PING
	ReplyTok []byte

	// Expiration is the UNIX timestamp when this packet expires
	Expiration uint64

	// ENRSeq is the sender's ENR sequence number (optional, EIP-868)
	ENRSeq uint64 `rlp:"optional"`

	// Rest allows forward compatibility
	Rest []rlp.RawValue `rlp:"tail"`
}

Pong represents a PONG message (reply to PING).

Pong is used to:

  1. Confirm liveness
  2. Report the sender's external IP/port (for NAT traversal)
  3. Establish a bond with the requester
  4. Exchange ENR sequence numbers

func (*Pong) Kind

func (p *Pong) Kind() byte

Kind returns the packet type byte.

func (*Pong) Name

func (p *Pong) Name() string

Name returns the packet name.

type Pubkey

type Pubkey [64]byte

Pubkey represents an encoded 64-byte secp256k1 public key.

This is the uncompressed public key format (X and Y coordinates, 32 bytes each) without the 0x04 prefix.

func EncodePubkey

func EncodePubkey(key *ecdsa.PublicKey) Pubkey

EncodePubkey encodes an ECDSA public key to the wire format.

func (Pubkey) ID

func (p Pubkey) ID() []byte

ID returns the node ID (Keccak256 hash of the public key).

type Transport

type Transport interface {
	SendTo(data []byte, to *net.UDPAddr) error
	Send(data []byte, to *net.UDPAddr, from *net.UDPAddr) error
}

Transport interface for sending packets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL