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

215 lines
4.9 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package pubsub
import (
"errors"
"fmt"
"sync/atomic"
"time"
"github.com/go-json-experiment/json"
"github.com/gorilla/websocket"
"github.com/luxfi/log"
"github.com/luxfi/node/pubsub/bloom"
)
var (
ErrFilterNotInitialized = errors.New("filter not initialized")
ErrAddressLimit = errors.New("address limit exceeded")
ErrInvalidFilterParam = errors.New("invalid bloom filter params")
ErrInvalidCommand = errors.New("invalid command")
_ Filter = (*connection)(nil)
)
type Filter interface {
Check(addr []byte) bool
}
// connection is a representation of the websocket connection.
type connection struct {
s *Server
// The websocket connection.
conn *websocket.Conn
// Buffered channel of outbound messages.
send chan interface{}
fp *FilterParam
active uint32
}
func (c *connection) Check(addr []byte) bool {
return c.fp.Check(addr)
}
func (c *connection) isActive() bool {
active := atomic.LoadUint32(&c.active)
return active != 0
}
func (c *connection) deactivate() {
atomic.StoreUint32(&c.active, 0)
}
func (c *connection) Send(msg interface{}) bool {
if !c.isActive() {
return false
}
select {
case c.send <- msg:
return true
default:
}
return false
}
// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *connection) readPump() {
defer func() {
c.deactivate()
c.s.removeConnection(c)
// close is called by both the writePump and the readPump so one of them
// will always error
_ = c.conn.Close()
}()
c.conn.SetReadLimit(maxMessageSize)
// SetReadDeadline returns an error if the connection is corrupted
if err := c.conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
return
}
c.conn.SetPongHandler(func(string) error {
return c.conn.SetReadDeadline(time.Now().Add(pongWait))
})
for {
err := c.readMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
c.s.log.Debug("unexpected close in websockets",
log.Err(err),
)
}
break
}
}
}
// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *connection) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
c.deactivate()
ticker.Stop()
c.s.removeConnection(c)
// close is called by both the writePump and the readPump so one of them
// will always error
_ = c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
if err := c.conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
c.s.log.Debug("closing the connection",
log.String("reason", "failed to set the write deadline"),
log.Err(err),
)
return
}
if !ok {
// The hub closed the channel. Attempt to close the connection
// gracefully.
_ = c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := c.conn.WriteJSON(message); err != nil {
return
}
case <-ticker.C:
if err := c.conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
c.s.log.Debug("closing the connection",
log.String("reason", "failed to set the write deadline"),
log.Err(err),
)
return
}
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
func (c *connection) readMessage() error {
_, r, err := c.conn.NextReader()
if err != nil {
return err
}
cmd := &Command{}
err = json.UnmarshalRead(r, cmd)
if err != nil {
return err
}
switch {
case cmd.NewBloom != nil:
err = c.handleNewBloom(cmd.NewBloom)
case cmd.NewSet != nil:
c.handleNewSet(cmd.NewSet)
case cmd.AddAddresses != nil:
err = c.handleAddAddresses(cmd.AddAddresses)
default:
err = ErrInvalidCommand
}
if err != nil {
c.Send(&errorMsg{
Error: err.Error(),
})
}
return err
}
func (c *connection) handleNewBloom(cmd *NewBloom) error {
if !cmd.IsParamsValid() {
return ErrInvalidFilterParam
}
filter, err := bloom.New(int(cmd.MaxElements), float64(cmd.CollisionProb), MaxBytes)
if err != nil {
return fmt.Errorf("bloom filter creation failed %w", err)
}
c.fp.SetFilter(filter)
return nil
}
func (c *connection) handleNewSet(_ *NewSet) {
c.fp.NewSet()
}
func (c *connection) handleAddAddresses(cmd *AddAddresses) error {
if err := cmd.parseAddresses(); err != nil {
return fmt.Errorf("address parse failed %w", err)
}
err := c.fp.Add(cmd.addressIds...)
if err != nil {
return fmt.Errorf("address append failed %w", err)
}
c.s.subscribedConnections.Add(c)
return nil
}