Files
zeekay 7cf87b230b node-unified is the node
The module already called itself github.com/luxfi/node while containing no node
in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and
the Go column of the matrix measured a VM plugin. A repo that fetches the thing
it claims to be has not replaced it, so the Go sources move in — 1515 files,
the daemon and everything under it — and the build reaches outside this module
for nothing.

Rust and C++ sit beside it unchanged and are still measured against it by the
differential. That is the point of one repo: the reference implementation and
the two ports are the same checkout, so parity is a thing you can run rather
than a thing you assert.

Verified: builds, and the closure carries no lux-private — checked at build time
rather than believed. The daemon starts twelve chains on a local network
(P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
2026-09-07 16:15:25 -07:00

459 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"encoding/binary"
"errors"
"fmt"
"github.com/luxfi/consensus/core/router"
"github.com/luxfi/math/set"
"github.com/luxfi/node/proto/p2p"
)
// Op is an opcode
type Op byte
// Types of messages that may be sent between nodes
// Note: If you add a new parseable Op below, you must add it to either
// [UnrequestedOps] or [FailedToResponseOps].
const (
// Handshake:
PingOp Op = iota
PongOp
HandshakeOp
GetPeerListOp
PeerListOp
// State sync:
GetStateSummaryFrontierOp
GetStateSummaryFrontierFailedOp
StateSummaryFrontierOp
GetAcceptedStateSummaryOp
GetAcceptedStateSummaryFailedOp
AcceptedStateSummaryOp
// Bootstrapping:
GetAcceptedFrontierOp
GetAcceptedFrontierFailedOp
AcceptedFrontierOp
GetAcceptedOp
GetAcceptedFailedOp
AcceptedOp
GetAncestorsOp
GetAncestorsFailedOp
AncestorsOp
// Consensus:
GetOp
GetFailedOp
PutOp
PushQueryOp
PullQueryOp
QueryFailedOp
QbitOp // Authenticated preference signal (formerly ChitsOp)
// Application:
RequestOp
ErrorOp
ResponseOp
GossipOp
// Internal:
ConnectedOp
DisconnectedOp
NotifyOp
GossipRequestOp
// BFT
BFTOp
)
var (
// UnrequestedOps are operations that are expected to be seen without having
// requested them. For example, a peer may receive a Get request for a block
// without having sent a message previously.
UnrequestedOps = set.Of(
GetAcceptedFrontierOp,
GetAcceptedOp,
GetAncestorsOp,
GetOp,
PushQueryOp,
PullQueryOp,
RequestOp,
GossipOp,
GetStateSummaryFrontierOp,
GetAcceptedStateSummaryOp,
BFTOp,
)
// FailedToResponseOps maps response failure messages to their successful
// counterparts.
FailedToResponseOps = map[Op]Op{
GetStateSummaryFrontierFailedOp: StateSummaryFrontierOp,
GetAcceptedStateSummaryFailedOp: AcceptedStateSummaryOp,
GetAcceptedFrontierFailedOp: AcceptedFrontierOp,
GetAcceptedFailedOp: AcceptedOp,
GetAncestorsFailedOp: AncestorsOp,
GetFailedOp: PutOp,
QueryFailedOp: QbitOp,
ErrorOp: ResponseOp,
}
errUnknownMessageType = errors.New("unknown message type")
)
func (op Op) String() string {
switch op {
// Handshake
case PingOp:
return "ping"
case PongOp:
return "pong"
case HandshakeOp:
return "handshake"
case GetPeerListOp:
return "get_peerlist"
case PeerListOp:
return "peerlist"
// State sync
case GetStateSummaryFrontierOp:
return "get_state_summary_frontier"
case GetStateSummaryFrontierFailedOp:
return "get_state_summary_frontier_failed"
case StateSummaryFrontierOp:
return "state_summary_frontier"
case GetAcceptedStateSummaryOp:
return "get_accepted_state_summary"
case GetAcceptedStateSummaryFailedOp:
return "get_accepted_state_summary_failed"
case AcceptedStateSummaryOp:
return "accepted_state_summary"
// Bootstrapping
case GetAcceptedFrontierOp:
return "get_accepted_frontier"
case GetAcceptedFrontierFailedOp:
return "get_accepted_frontier_failed"
case AcceptedFrontierOp:
return "accepted_frontier"
case GetAcceptedOp:
return "get_accepted"
case GetAcceptedFailedOp:
return "get_accepted_failed"
case AcceptedOp:
return "accepted"
case GetAncestorsOp:
return "get_ancestors"
case GetAncestorsFailedOp:
return "get_ancestors_failed"
case AncestorsOp:
return "ancestors"
// Consensus
case GetOp:
return "get"
case GetFailedOp:
return "get_failed"
case PutOp:
return "put"
case PushQueryOp:
return "push_query"
case PullQueryOp:
return "pull_query"
case QueryFailedOp:
return "query_failed"
case QbitOp:
return "qbit"
// Application
case RequestOp:
return "app_request"
case ErrorOp:
return "app_error"
case ResponseOp:
return "app_response"
case GossipOp:
return "app_gossip"
// Internal
case ConnectedOp:
return "connected"
case DisconnectedOp:
return "disconnected"
case NotifyOp:
return "notify"
case GossipRequestOp:
return "gossip_request"
// BFT
case BFTOp:
return "bft"
default:
return "unknown"
}
}
func Unwrap(m *p2p.Message) (fmt.Stringer, error) {
switch msg := m.GetMessage().(type) {
// Handshake:
case *p2p.Message_Ping:
return msg.Ping, nil
case *p2p.Message_Pong:
return msg.Pong, nil
case *p2p.Message_Handshake:
return msg.Handshake, nil
case *p2p.Message_GetPeerList:
return msg.GetPeerList, nil
case *p2p.Message_PeerList_:
return msg.PeerList_, nil
// State sync:
case *p2p.Message_GetStateSummaryFrontier:
return msg.GetStateSummaryFrontier, nil
case *p2p.Message_StateSummaryFrontier_:
return msg.StateSummaryFrontier_, nil
case *p2p.Message_GetAcceptedStateSummary:
return msg.GetAcceptedStateSummary, nil
case *p2p.Message_AcceptedStateSummary_:
return msg.AcceptedStateSummary_, nil
// Bootstrapping:
case *p2p.Message_GetAcceptedFrontier:
return msg.GetAcceptedFrontier, nil
case *p2p.Message_AcceptedFrontier_:
return msg.AcceptedFrontier_, nil
case *p2p.Message_GetAccepted:
return msg.GetAccepted, nil
case *p2p.Message_Accepted_:
return msg.Accepted_, nil
case *p2p.Message_GetAncestors:
return msg.GetAncestors, nil
case *p2p.Message_Ancestors_:
return msg.Ancestors_, nil
// Consensus:
case *p2p.Message_Get:
return msg.Get, nil
case *p2p.Message_Put:
return msg.Put, nil
case *p2p.Message_PushQuery:
return msg.PushQuery, nil
case *p2p.Message_PullQuery:
return msg.PullQuery, nil
case *p2p.Message_Chits:
return msg.Chits, nil
// Application:
case *p2p.Message_Request:
return msg.Request, nil
case *p2p.Message_Response:
return msg.Response, nil
case *p2p.Message_Error:
return msg.Error, nil
case *p2p.Message_Gossip:
return msg.Gossip, nil
// BFT
case *p2p.Message_BFT:
return extractBFT(msg), nil
default:
return nil, fmt.Errorf("%w: %T", errUnknownMessageType, msg)
}
}
// ToConsensusOp maps a wire message Op to the consensus router op the chain
// router (node/chain_router.go) dispatches on, returning false for ops that are
// not consensus-routed (the router drops those as "unhandled message op"). The
// returned values ARE the router constants, so they cannot drift in value; the
// mapping must also stay a bijection onto the router op space [0, router.NumOps)
// — proven by message/ops_test.go. A missing case silently drops that op: that
// is the α-of-K finality wedge, where GossipOp had no case and every vote
// vanished before reaching blockHandler.Gossip -> engine.HandleIncomingVote.
func ToConsensusOp(op Op) (byte, bool) {
switch op {
case GetAcceptedFrontierOp:
return byte(router.GetAcceptedFrontier), true
case AcceptedFrontierOp:
return byte(router.AcceptedFrontier), true
case GetAcceptedOp:
return byte(router.GetAccepted), true
case AcceptedOp:
return byte(router.Accepted), true
case GetOp:
return byte(router.Get), true
case PutOp:
return byte(router.Put), true
case PushQueryOp:
return byte(router.PushQuery), true
case PullQueryOp:
return byte(router.PullQuery), true
case QbitOp:
return byte(router.Vote), true // votes ride the Qbit wire op
case GetAncestorsOp:
return byte(router.GetContext), true // wire op is still GetAncestors
case AncestorsOp:
return byte(router.Context), true // wire op is still Ancestors
case GossipOp:
return byte(router.Gossip), true // α-of-K vote/cert transport
case GetStateSummaryFrontierOp:
return byte(router.GetStateSummaryFrontier), true
case StateSummaryFrontierOp:
return byte(router.StateSummaryFrontier), true
case GetAcceptedStateSummaryOp:
return byte(router.GetAcceptedStateSummary), true
case AcceptedStateSummaryOp:
return byte(router.AcceptedStateSummary), true
case RequestOp:
return byte(router.AppRequest), true // AppRequest — EVM trie-leaf/code fetch
case ResponseOp:
return byte(router.AppResponse), true // AppResponse
case ErrorOp:
return byte(router.AppError), true // AppError
default:
return 0, false
}
}
// GetContainerBytes extracts the container/body bytes from various message types
func GetContainerBytes(msg fmt.Stringer) []byte {
switch m := msg.(type) {
case *p2p.Put:
return m.Container
case *p2p.PushQuery:
return m.Container
case *p2p.Ancestors:
// Encode all containers with length prefix so handlers can decode
// multiple blocks from a single byte slice.
if len(m.Containers) == 0 {
return nil
}
totalLen := 0
for _, container := range m.Containers {
totalLen += 4 + len(container)
}
encoded := make([]byte, 0, totalLen)
for _, container := range m.Containers {
var lenBuf [4]byte
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(container)))
encoded = append(encoded, lenBuf[:]...)
encoded = append(encoded, container...)
}
return encoded
case *p2p.Gossip:
return m.AppBytes
case *p2p.Request:
return m.AppBytes
case *p2p.Response:
return m.AppBytes
// State sync. The frontier request carries no body — the requester is asking
// what the peer holds, not about anything in particular — so it flattens to
// nothing and the handler answers from the chain alone.
case *p2p.GetStateSummaryFrontier:
return nil
case *p2p.StateSummaryFrontier:
return m.Summary
case *p2p.GetAcceptedStateSummary:
// The heights asked about, fixed-width and in order. Fixed-width because
// the reader recovers the count by division and cannot disagree with the
// writer about where one ends.
if len(m.Heights) == 0 {
return nil
}
out := make([]byte, 0, len(m.Heights)*8)
var h [8]byte
for _, height := range m.Heights {
binary.BigEndian.PutUint64(h[:], height)
out = append(out, h[:]...)
}
return out
case *p2p.AcceptedStateSummary:
// The summaries the peer holds, named by id. Same shape as GetAccepted
// below: fixed 32-byte ids, concatenated.
if len(m.SummaryIds) == 0 {
return nil
}
out := make([]byte, 0, len(m.SummaryIds)*32)
for _, id := range m.SummaryIds {
out = append(out, id...)
}
return out
// Request messages with container IDs for P-Chain sync
case *p2p.GetAccepted:
containerIDs := m.GetContainerIds()
if len(containerIDs) == 0 {
return nil
}
result := make([]byte, 0, len(containerIDs)*32)
for _, id := range containerIDs {
result = append(result, id...)
}
return result
case *p2p.Get:
return m.GetContainerId()
case *p2p.GetAncestors:
return m.GetContainerId()
case *p2p.PullQuery:
return m.GetContainerId()
case *p2p.Chits:
// For Qbit messages (formerly Chits), return the PreferredId (the block being voted for)
return m.GetPreferredId()
case *p2p.AcceptedFrontier:
return m.GetContainerId()
case *p2p.Accepted:
containerIDs := m.GetContainerIds()
if len(containerIDs) == 0 {
return nil
}
result := make([]byte, 0, len(containerIDs)*32)
for _, id := range containerIDs {
result = append(result, id...)
}
return result
default:
return nil
}
}
func ToOp(m *p2p.Message) (Op, error) {
switch msg := m.GetMessage().(type) {
case *p2p.Message_Ping:
return PingOp, nil
case *p2p.Message_Pong:
return PongOp, nil
case *p2p.Message_Handshake:
return HandshakeOp, nil
case *p2p.Message_GetPeerList:
return GetPeerListOp, nil
case *p2p.Message_PeerList_:
return PeerListOp, nil
case *p2p.Message_GetStateSummaryFrontier:
return GetStateSummaryFrontierOp, nil
case *p2p.Message_StateSummaryFrontier_:
return StateSummaryFrontierOp, nil
case *p2p.Message_GetAcceptedStateSummary:
return GetAcceptedStateSummaryOp, nil
case *p2p.Message_AcceptedStateSummary_:
return AcceptedStateSummaryOp, nil
case *p2p.Message_GetAcceptedFrontier:
return GetAcceptedFrontierOp, nil
case *p2p.Message_AcceptedFrontier_:
return AcceptedFrontierOp, nil
case *p2p.Message_GetAccepted:
return GetAcceptedOp, nil
case *p2p.Message_Accepted_:
return AcceptedOp, nil
case *p2p.Message_GetAncestors:
return GetAncestorsOp, nil
case *p2p.Message_Ancestors_:
return AncestorsOp, nil
case *p2p.Message_Get:
return GetOp, nil
case *p2p.Message_Put:
return PutOp, nil
case *p2p.Message_PushQuery:
return PushQueryOp, nil
case *p2p.Message_PullQuery:
return PullQueryOp, nil
case *p2p.Message_Chits:
return QbitOp, nil
case *p2p.Message_Request:
return RequestOp, nil
case *p2p.Message_Response:
return ResponseOp, nil
case *p2p.Message_Error:
return ErrorOp, nil
case *p2p.Message_Gossip:
return GossipOp, nil
case *p2p.Message_BFT:
return BFTOp, nil
default:
return 0, fmt.Errorf("%w: %T", errUnknownMessageType, msg)
}
}