Files
vm/rpc/vm_server_zap_test.go
zeekayandHanzo Dev aaf53f6bad feat(rpc): close the ZAP caps handshake — plugin advertises CapQuasarExport + Quasar handlers; delete dead parallel scheme
The generic ZAP VM server never set InitializeResponse.Capabilities and had no MsgSetQuasarFinalized/
MsgQuasarHeight handlers, so CapQuasarExport was dead-on-arrival (every plugin reported Nova-only and
the node's Quasar-export bridge never wired for plugin VMs). newZAPVMServer now probes the wrapped VM
once for the quasarExporter interface (SetLastQuasarFinalized/LastQuasarHeight — the real C-Chain EVM
VM's method set), handleInitialize sets Capabilities accordingly, and the two Quasar handlers forward
to the VM (refuse loudly on a non-capable VM, no nil-panic). Deleted the parallel, never-wired
capabilities.go scheme (CurrentCapabilitiesVersion/VMKind/VMFeature — zero importers confirmed
repo-wide); InitializeResponse.Capabilities is the one caps mechanism.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-16 12:31:40 -07:00

107 lines
3.5 KiB
Go

//go:build !grpc
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package rpc
import (
"context"
"testing"
zapwire "github.com/luxfi/api/zap"
"github.com/luxfi/log"
"github.com/luxfi/vm/chain"
)
// baseVM embeds chain.ChainVM so it satisfies the full generic contract with no
// boilerplate. The probe + Quasar handlers under test never invoke the embedded
// (nil) methods, so leaving them unimplemented is safe for these unit tests.
type baseVM struct{ chain.ChainVM }
// quasarVMStub is a ChainVM that ALSO implements the OPTIONAL quasarExporter
// surface the server probes for at construction.
type quasarVMStub struct {
baseVM
finalized uint64
height uint64
}
func (q *quasarVMStub) SetLastQuasarFinalized(h uint64) { q.finalized = h }
func (q *quasarVMStub) LastQuasarHeight() uint64 { return q.height }
// Compile-time contracts: both stubs are ChainVMs; only the quasar stub is a
// quasarExporter (so the server's probe distinguishes them).
var (
_ chain.ChainVM = baseVM{}
_ chain.ChainVM = (*quasarVMStub)(nil)
_ quasarExporter = (*quasarVMStub)(nil)
)
func TestCapabilitiesProbe(t *testing.T) {
logger := log.NewTestLogger(log.DebugLevel)
// Generic VM: no optional add-ons → Capabilities 0 → node stays Nova-only.
if got := newZAPVMServer(baseVM{}, logger).capabilities(); got != 0 {
t.Fatalf("generic VM must advertise 0 capabilities, got %d", got)
}
// Quasar-capable VM: probe hits → CapQuasarExport advertised.
if got := newZAPVMServer(&quasarVMStub{}, logger).capabilities(); got != zapwire.CapQuasarExport {
t.Fatalf("quasar VM must advertise CapQuasarExport (%d), got %d", zapwire.CapQuasarExport, got)
}
}
func TestQuasarHandlersRoundTrip(t *testing.T) {
logger := log.NewTestLogger(log.DebugLevel)
stub := &quasarVMStub{height: 99}
s := newZAPVMServer(stub, logger)
ctx := context.Background()
// MsgSetQuasarFinalized forwards the height into the wrapped VM.
req := &zapwire.SetQuasarFinalizedRequest{Height: 42}
buf := zapwire.GetBuffer()
req.Encode(buf)
payload := append([]byte(nil), buf.Bytes()...)
zapwire.PutBuffer(buf)
mt, resp, err := s.handleSetQuasarFinalized(ctx, payload)
if err != nil {
t.Fatalf("handleSetQuasarFinalized: %v", err)
}
if mt != zapwire.MsgSetQuasarFinalized || resp != nil {
t.Fatalf("set-quasar-finalized reply: mt=%d resp=%v", mt, resp)
}
if stub.finalized != 42 {
t.Fatalf("SetLastQuasarFinalized not applied: got %d want 42", stub.finalized)
}
// MsgQuasarHeight returns the wrapped VM's clamped height.
mt, resp, err = s.handleQuasarHeight(ctx)
if err != nil {
t.Fatalf("handleQuasarHeight: %v", err)
}
if mt != zapwire.MsgQuasarHeight {
t.Fatalf("quasar-height reply type: got %d", mt)
}
var hr zapwire.QuasarHeightResponse
if err := hr.Decode(zapwire.NewReader(resp)); err != nil {
t.Fatalf("decode QuasarHeightResponse: %v", err)
}
if hr.Height != 99 {
t.Fatalf("quasar height: got %d want 99", hr.Height)
}
}
// TestQuasarHandlersRefusedWhenUnsupported: a stray Quasar message on a generic
// VM is refused with an error, not silently dropped or a nil-pointer panic.
func TestQuasarHandlersRefusedWhenUnsupported(t *testing.T) {
s := newZAPVMServer(baseVM{}, log.NewTestLogger(log.DebugLevel))
if _, _, err := s.handleQuasarHeight(context.Background()); err == nil {
t.Fatal("handleQuasarHeight on a non-quasar VM must error")
}
if _, _, err := s.handleSetQuasarFinalized(context.Background(), nil); err == nil {
t.Fatal("handleSetQuasarFinalized on a non-quasar VM must error")
}
}