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

112 lines
3.1 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package scheduler
import (
"fmt"
"time"
"github.com/luxfi/log"
vmcore "github.com/luxfi/vm"
)
type Scheduler interface {
Dispatch(startTime time.Time)
// Client must guarantee that [SetBuildBlockTime]
// is never called after [Close]
SetBuildBlockTime(t time.Time)
Close()
}
// Scheduler receives notifications from a VM that it wants its engine to call
// the VM's BuildBlock method, and delivers the notification to the engine only
// when the engine should call BuildBlock. Namely, when this node is allowed to
// propose a block under the congestion control mechanism.
type scheduler struct {
log log.Logger
// The VM sends a message on this channel when it wants to tell the engine
// that the engine should call the VM's BuildBlock method
fromVM <-chan vmcore.Message
// The scheduler sends a message on this channel to notify the engine that
// it should call its VM's BuildBlock method
toEngine chan<- vmcore.Message
// When we receive a message on this channel, it means that we must refrain
// from telling the engine to call its VM's BuildBlock method until the
// given time
newBuildBlockTime chan time.Time
}
func New(log log.Logger, toEngine chan<- vmcore.Message) (Scheduler, chan<- vmcore.Message) {
vmToEngine := make(chan vmcore.Message, cap(toEngine))
return &scheduler{
log: log,
fromVM: vmToEngine,
toEngine: toEngine,
newBuildBlockTime: make(chan time.Time),
}, vmToEngine
}
func (s *scheduler) Dispatch(buildBlockTime time.Time) {
timer := time.NewTimer(time.Until(buildBlockTime))
waitloop:
for {
select {
case <-timer.C: // It's time to tell the engine to try to build a block
case buildBlockTime, ok := <-s.newBuildBlockTime:
// Stop the timer and clear [timer.C] if needed
if !timer.Stop() {
<-timer.C
}
if !ok {
// s.Close() was called
return
}
// The time at which we should notify the engine that it should try
// to build a block has changed
timer.Reset(time.Until(buildBlockTime))
continue waitloop
}
for {
select {
case msg := <-s.fromVM:
// Give the engine the message from the VM asking the engine to
// build a block
select {
case s.toEngine <- msg:
default:
// If the channel to the engine is full, drop the message
// from the VM to avoid deadlock
s.log.Debug("dropping message from VM",
log.String("reason", "channel to engine is full"),
log.String("messageType", fmt.Sprintf("%d", msg.Type)),
)
}
case buildBlockTime, ok := <-s.newBuildBlockTime:
// The time at which we should notify the engine that it should
// try to build a block has changed
if !ok {
// s.Close() was called
return
}
// We know [timer.C] was drained in the first select statement
// so its safe to call [timer.Reset]
timer.Reset(time.Until(buildBlockTime))
continue waitloop
}
}
}
}
func (s *scheduler) SetBuildBlockTime(t time.Time) {
s.newBuildBlockTime <- t
}
func (s *scheduler) Close() {
close(s.newBuildBlockTime)
}