mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
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.
148 lines
3.8 KiB
Go
148 lines
3.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package rpcchainvm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/luxfi/log"
|
|
)
|
|
|
|
// Message represents a notification message type
|
|
type Message uint32
|
|
|
|
const (
|
|
_ Message = iota
|
|
// PendingTxs indicates pending transactions notification
|
|
PendingTxs
|
|
)
|
|
|
|
func (m Message) String() string {
|
|
switch m {
|
|
case PendingTxs:
|
|
return "PendingTxs"
|
|
default:
|
|
return fmt.Sprintf("Unknown(%d)", m)
|
|
}
|
|
}
|
|
|
|
// Subscription is a function that blocks until either the given context is cancelled, or a message is returned.
|
|
// It is used to receive messages from a VM such as Pending transactions, state sync completion, etc.
|
|
// The function returns the message received, or an error if the context is cancelled.
|
|
type Subscription func(ctx context.Context) (Message, error)
|
|
|
|
type Notifier interface {
|
|
Notify(context.Context, Message) error
|
|
}
|
|
|
|
// NotificationForwarder is a component that listens for notifications from a Subscription,
|
|
// and forwards them to a Notifier.
|
|
// When CheckForEvent is called mid-subscription, it retries the subscription.
|
|
// After Notify is called, it waits for CheckForEvent to be called before subscribing again.
|
|
type NotificationForwarder struct {
|
|
Engine Notifier
|
|
Subscribe Subscription
|
|
Log log.Logger
|
|
|
|
lock sync.Mutex
|
|
executing sync.WaitGroup
|
|
execCtx context.Context
|
|
haltExecution context.CancelFunc
|
|
abortContext context.CancelFunc
|
|
}
|
|
|
|
func NewNotificationForwarder(
|
|
engine Notifier,
|
|
subscribe Subscription,
|
|
log log.Logger,
|
|
) *NotificationForwarder {
|
|
nf := &NotificationForwarder{
|
|
Engine: engine,
|
|
Subscribe: subscribe,
|
|
Log: log,
|
|
}
|
|
nf.start()
|
|
return nf
|
|
}
|
|
|
|
func (nf *NotificationForwarder) start() {
|
|
// The loop below logs before it does anything else, and it runs on a
|
|
// goroutine whose panic no caller can recover. Log is exported and the
|
|
// constructor also takes one, so there are two ways to leave it unset;
|
|
// answering it here covers both, because this is the one place the loop
|
|
// begins. Noop discards what it is given, so a caller that wanted silence
|
|
// still gets it.
|
|
if nf.Log == nil {
|
|
nf.Log = log.Noop()
|
|
}
|
|
|
|
nf.executing.Add(1)
|
|
nf.execCtx, nf.haltExecution = context.WithCancel(context.Background())
|
|
go nf.run()
|
|
}
|
|
|
|
func (nf *NotificationForwarder) run() {
|
|
defer nf.executing.Done()
|
|
for nf.execCtx.Err() == nil {
|
|
nf.forwardNotification()
|
|
}
|
|
}
|
|
|
|
func (nf *NotificationForwarder) forwardNotification() {
|
|
ctx := nf.setAndGetContext()
|
|
defer nf.cancelContext()
|
|
|
|
nf.Log.Debug("Subscribing to notifications")
|
|
|
|
msg, err := nf.Subscribe(ctx)
|
|
if err != nil {
|
|
nf.Log.Debug("Failed subscribing to notifications", "error", err)
|
|
return
|
|
}
|
|
|
|
nf.Log.Debug("Received notification", "msg", msg)
|
|
|
|
if err := nf.Engine.Notify(ctx, msg); err != nil {
|
|
nf.Log.Debug("Failed notifying engine", "error", err)
|
|
return
|
|
}
|
|
|
|
// Wait for the context to be cancelled before proceeding to the next subscription,
|
|
// in order to subscribe after a block was accepted or a state sync was completed.
|
|
<-ctx.Done()
|
|
}
|
|
|
|
// CheckForEvent cancels any outstanding WaitForEvent calls and schedules a new WaitForEvent call.
|
|
func (nf *NotificationForwarder) CheckForEvent() {
|
|
nf.cancelContext()
|
|
}
|
|
|
|
func (nf *NotificationForwarder) cancelContext() {
|
|
nf.lock.Lock()
|
|
defer nf.lock.Unlock()
|
|
|
|
if nf.abortContext != nil {
|
|
nf.abortContext()
|
|
}
|
|
}
|
|
|
|
func (nf *NotificationForwarder) setAndGetContext() context.Context {
|
|
ctx, cancel := context.WithCancel(nf.execCtx)
|
|
|
|
nf.lock.Lock()
|
|
defer nf.lock.Unlock()
|
|
nf.abortContext = cancel
|
|
return ctx
|
|
}
|
|
|
|
// Close cancels any outstanding WaitForEvent calls and waits for them to return.
|
|
// After Close returns, no future WaitForEvent calls will be made by the notification forwarder.
|
|
func (nf *NotificationForwarder) Close() {
|
|
defer nf.executing.Wait()
|
|
|
|
nf.haltExecution()
|
|
}
|