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

102 lines
2.7 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package rpcchainvm
import (
"context"
"fmt"
"os"
zapwire "github.com/luxfi/api/zap"
"github.com/luxfi/log"
"github.com/luxfi/metric"
"github.com/luxfi/node/vms"
"github.com/luxfi/node/vms/rpcchainvm/runtime"
"github.com/luxfi/node/vms/rpcchainvm/runtime/subprocess"
rpcchainvmzap "github.com/luxfi/node/vms/rpcchainvm/zap"
"github.com/luxfi/resource"
)
var _ vms.Factory = (*factory)(nil)
type factory struct {
path string
processTracker resource.ProcessTracker
runtimeTracker runtime.Tracker
metricsGatherer metric.MultiGatherer
}
// NewFactory creates a new factory. VM subprocesses are spawned and
// communication uses ZAP — the one and only wire protocol for rpcchainvm.
func NewFactory(
path string,
processTracker resource.ProcessTracker,
runtimeTracker runtime.Tracker,
metricsGatherer metric.MultiGatherer,
) vms.Factory {
return &factory{
path: path,
processTracker: processTracker,
runtimeTracker: runtimeTracker,
metricsGatherer: metricsGatherer,
}
}
func (f *factory) New(logger log.Logger) (interface{}, error) {
logger.Info("creating VM client", "path", f.path)
config := &subprocess.Config{
Stderr: os.Stderr,
Stdout: os.Stdout,
HandshakeTimeout: runtime.DefaultHandshakeTimeout,
Log: logger,
}
// Create TCP listener for ZAP handshake
listener, err := rpcchainvmzap.NewListener()
if err != nil {
return nil, fmt.Errorf("failed to create ZAP listener: %w", err)
}
// Tell subprocess to use ZAP transport.
// IMPORTANT: Start with os.Environ() so the subprocess inherits PATH, HOME, etc.
// Setting cmd.Env to a non-nil value prevents exec.Cmd from inheriting the parent env.
cmd := subprocess.NewCmd(f.path)
cmd.Env = append(os.Environ(), "VM_TRANSPORT=zap")
status, stopper, err := subprocess.Bootstrap(
context.TODO(),
listener,
cmd,
config,
)
if err != nil {
return nil, err
}
// Connect via ZAP with no read timeout (WaitForEvent blocks indefinitely)
zapCfg := zapwire.DefaultConfig()
zapCfg.ReadTimeout = 0
ctx, cancel := context.WithTimeout(context.Background(), runtime.DefaultHandshakeTimeout)
defer cancel()
zapConn, err := rpcchainvmzap.Dial(ctx, status.Addr, zapCfg)
if err != nil {
stopper.Stop(context.Background())
logger.Error("failed to dial VM ZAP service", "error", err)
return nil, err
}
f.processTracker.TrackProcess(status.Pid)
f.runtimeTracker.TrackRuntime(stopper)
logger.Info("VM client connected via ZAP",
"addr", status.Addr,
"pid", status.Pid,
)
return rpcchainvmzap.NewClient(zapConn, logger), nil
}