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
- Variables
- func Decode(input []byte) (Packet, Pubkey, []byte, error)
- func DecodePubkey(curve elliptic.Curve, p Pubkey) (*ecdsa.PublicKey, error)
- func Encode(priv *ecdsa.PrivateKey, req Packet) (packet, hash []byte, err error)
- func EncodePacket(priv *ecdsa.PrivateKey, req Packet) ([]byte, error)
- func Expired(ts uint64) bool
- func MakeExpiration(d time.Duration) uint64
- type ENRRequest
- type ENRResponse
- type Endpoint
- type Findnode
- type Handler
- func (h *Handler) AllNodes() []*node.Node
- func (h *Handler) Findnode(n *node.Node, target []byte) ([]*node.Node, error)
- func (h *Handler) GetNode(id node.ID) *node.Node
- func (h *Handler) GetStats() HandlerStats
- func (h *Handler) HandlePacket(data []byte, from *net.UDPAddr, localAddr *net.UDPAddr) (err error)
- func (h *Handler) LocalRecord() *enr.Record
- func (h *Handler) Ping(n *node.Node) (*Pong, error)
- func (h *Handler) RequestENR(n *node.Node) (*enr.Record, error)
- func (h *Handler) SetLocalENR(record *enr.Record)
- type HandlerConfig
- type HandlerStats
- type Neighbors
- type NodeRecord
- type OnENRRequestCallback
- type OnFindnodeCallback
- type OnNodeSeenCallback
- type OnPingCallback
- type OnPongReceivedCallback
- type Packet
- type PendingNeighborsResponse
- type PendingRequest
- type Ping
- type Pong
- type Pubkey
- type Transport
Constants ¶
const ( PingPacket = iota + 1 // zero is reserved PongPacket FindnodePacket NeighborsPacket ENRRequestPacket ENRResponsePacket )
Packet type constants
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 ¶
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 ¶
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 ¶
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 MakeExpiration ¶
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).
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.
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 ¶
NewEndpoint creates an endpoint from a UDP address.
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.
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) GetStats ¶ added in v0.0.4
func (h *Handler) GetStats() HandlerStats
GetStats returns current statistics.
func (*Handler) HandlePacket ¶
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) LocalRecord ¶ added in v0.0.4
LocalRecord returns the ENR the handler currently advertises.
func (*Handler) RequestENR ¶
RequestENR sends an ENRREQUEST to a node.
func (*Handler) SetLocalENR ¶ added in v0.0.4
SetLocalENR replaces the advertised ENR in place, so a fork transition or an IP-discovery update does not have to rebuild the handler and lose its bonds, known nodes, pending requests and stats.
type HandlerConfig ¶
type HandlerConfig struct {
// PrivateKey is our node's private key
PrivateKey *ecdsa.PrivateKey
// LocalENR is our node's ENR record (optional). It seeds the handler's
// record; SetLocalENR replaces it while running, so handler code must read
// LocalRecord() rather than this field.
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
// MaxNodes is the maximum number of nodes to track (default 50000).
// Once reached, new nodes are handled but not retained until a slot frees up,
// keeping memory bounded under floods of distinct node IDs.
MaxNodes int
// NodeTTL is how long an unbonded node is retained since it was last seen
// before it becomes eligible for eviction (default 5 minutes). Bonded nodes
// are kept until their bond expires.
NodeTTL time.Duration
// Callbacks (all optional)
OnPing OnPingCallback
OnPongReceived OnPongReceivedCallback
OnFindnode OnFindnodeCallback
OnENRRequest OnENRRequestCallback
OnNodeSeen OnNodeSeenCallback
}
HandlerConfig contains configuration for the protocol handler.
type HandlerStats ¶ added in v0.0.4
type HandlerStats struct {
PacketsReceived uint64
PacketsSent uint64
InvalidPackets uint64
ExpiredPackets uint64
UnbondedFindnode uint64
FindnodeRequestsRecv uint64
FindnodeResponsesRecv uint64
KnownNodes int
PendingRequests int
PendingNeighbors int
}
HandlerStats is a snapshot of the handler's counters.
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.
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 ¶
OnENRRequestCallback is called when an ENRREQUEST is received.
type OnFindnodeCallback ¶
OnFindnodeCallback is called when a FINDNODE request is received. Returns the list of nodes to include in the response.
type OnNodeSeenCallback ¶
OnNodeSeenCallback is called when we receive any valid packet from a node.
type OnPingCallback ¶
OnPingCallback is called when a PING request is received.
type OnPongReceivedCallback ¶
OnPongReceivedCallback is called when a PONG response is received. The ip and port parameters contain our external address as seen by the remote peer.
provenAddr is the address the answered PING was sent to. Callers must use it rather than from.Addr(), which any later inbound packet rewrites, including a spoofed one; attributing a report to that address would undo the endpoint proof this callback is gated on.
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 ¶
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
// Reserved counts nodes a handler has claimed room for but may not have
// appended yet. Reserving before decoding makes the persistence cap exact
// even when packets are dispatched concurrently.
Reserved int
// Closed marks a delivered entry. Packets processed after delivery must
// not reserve against it; cleanup evicts the tombstone by CreatedAt.
Closed bool
// CreatedAt is when we received the first packet
CreatedAt 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
// DestIP is the IP the request was sent to, snapshotted at send time; see
// lookupOrCreateNode. ToNode.Addr() cannot serve here because promoteAddr can
// move it between send and response.
DestIP net.IP
// 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:
- Liveness check
- Endpoint verification (NAT detection)
- Establishing bonds before FINDNODE
- ENR sequence number exchange (EIP-868)
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:
- Confirm liveness
- Report the sender's external IP/port (for NAT traversal)
- Establish a bond with the requester
- Exchange ENR sequence numbers
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 ¶
EncodePubkey encodes an ECDSA public key to the wire format.