Files
node/vms/mpcvm/mpcvm_gpu_test.go
T
zeekayandHanzo Dev d4323da799 node: rename thresholdvm → mpcvm; split M/F registries (LP-0130)
- vms/thresholdvm/ → vms/mpcvm/ (dir + inner files + package decl).
- node/vms.go optional-VM registry: drop ThresholdVMID row, add
  MPCVMID and FHEVMID rows (matching the new split).
- genesis/builder/registry.go: T-Chain entry → M-Chain (MPCVMID) +
  F-Chain (FHEVMID); registry_test.go parity assertions updated.
- genesis/builder/builder.go: TChainAliases → MChainAliases +
  FChainAliases; ThresholdVMID switch case split to MPCVMID / FHEVMID;
  optIn chain slice uses config.MChainGenesis + config.FChainGenesis
  (T-Chain slot retired).
- LLM.md: point at LP-0130 as canonical topology + fee spec; correct
  the quantumvm posture to service-only per LP-0130 §6 (Q-Chain has
  no user-payable blockspace).

Build + vet clean on chains/mpcvm/... + node/{vms/mpcvm,genesis,node}/...

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 01:23:03 -07:00

108 lines
3.9 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package mpcvm
import (
"errors"
"testing"
"unsafe"
)
// TestNodeGPULayoutSizes pins the re-exported wire structs to the same
// device-side __align__(16) values declared in
// the GPU plugin install tree ops/mpcvm/cuda/mpcvm_kernels_common.cuh.
//
// Even though the structs are type aliases to chains/mpcvm, this
// test sits at the node import boundary — if upstream sizes drift the
// node-side surface MUST also fail loud rather than miscompile.
func TestNodeGPULayoutSizes(t *testing.T) {
cases := []struct {
name string
want uintptr
got uintptr
}{
{"GPUCeremony", 128, unsafe.Sizeof(GPUCeremony{})},
{"GPUKeyShare", 368, unsafe.Sizeof(GPUKeyShare{})},
{"GPUContribution", 432, unsafe.Sizeof(GPUContribution{})},
{"GPUMPCVMState", 160, unsafe.Sizeof(GPUMPCVMState{})},
{"GPUMPCVMRoundDescriptor", 96, unsafe.Sizeof(GPUMPCVMRoundDescriptor{})},
{"GPUCeremonyOp", 96, unsafe.Sizeof(GPUCeremonyOp{})},
{"GPUContributionOp", 416, unsafe.Sizeof(GPUContributionOp{})},
{"GPUMPCVMTransitionResult", 176, unsafe.Sizeof(GPUMPCVMTransitionResult{})},
}
for _, c := range cases {
if c.got != c.want {
t.Errorf("%s: sizeof=%d want=%d", c.name, c.got, c.want)
}
}
}
// TestNodeGPUBackendRoundTrip is the dlopen round-trip required by the
// task spec. Two acceptable outcomes:
//
// 1. Plugin resolved: GPUBackendInstance() != nil && IsAvailable(); a
// zero-fixture CeremonyApply against the best backend returns rc=0
// (no ops processed) with no error.
//
// 2. Plugin absent: GPUBackendInstance() == nil; nil-receiver methods
// return ErrGPUNotAvailable. SelectGPUBackend reports the CPU-only
// diagnostic string.
//
// Both outcomes count as passing — the bridge correctly surfaces GPU
// state through the public node API.
func TestNodeGPUBackendRoundTrip(t *testing.T) {
b, diag := SelectGPUBackend()
t.Logf("SelectGPUBackend: %s", diag)
if b == nil {
// Plugin-absent path. The nil receiver contract must hold —
// every method returns ErrGPUNotAvailable, no panic.
_, err := (*GPUBackend)(nil).CeremonyApply(nil, nil, nil)
if !errors.Is(err, ErrGPUNotAvailable) {
t.Fatalf("nil receiver CeremonyApply: want ErrGPUNotAvailable, got %v", err)
}
return
}
// Plugin-resolved path. Zero fixture: 1-slot ceremony arena, no ops.
// The kernel walks zero ops and returns applied=0 with rc=0. Any
// non-zero rc means a real launcher failure.
desc := &GPUMPCVMRoundDescriptor{
ChainID: 0xCAFEBABE,
Round: 1,
TimestampNs: 1700000000_000000000,
Epoch: 1,
}
ceremonies := make([]GPUCeremony, 1)
applied, err := b.CeremonyApply(desc, nil, ceremonies)
if err != nil {
t.Fatalf("CeremonyApply(zero): %v", err)
}
if applied != 0 {
t.Errorf("CeremonyApply(zero): applied=%d want=0", applied)
}
}
// TestNodeGPUStubContract verifies the nocgo-equivalent contract on a
// zero-value GPUBackend: IsAvailable()==false and every state-machine
// method returns ErrGPUNotAvailable. The same contract holds under cgo
// when no plugin is dlopened — making this test build-flavor-independent.
func TestNodeGPUStubContract(t *testing.T) {
var b GPUBackend
if b.IsAvailable() {
t.Fatal("zero GPUBackend.IsAvailable() must be false")
}
if _, err := b.CeremonyApply(nil, nil, nil); !errors.Is(err, ErrGPUNotAvailable) {
t.Errorf("CeremonyApply: want ErrGPUNotAvailable, got %v", err)
}
if _, _, _, err := b.KeyShareApply(nil, nil, nil, nil, 0); !errors.Is(err, ErrGPUNotAvailable) {
t.Errorf("KeyShareApply: want ErrGPUNotAvailable, got %v", err)
}
if _, err := b.ContributionApply(nil, nil, nil, nil, 0); !errors.Is(err, ErrGPUNotAvailable) {
t.Errorf("ContributionApply: want ErrGPUNotAvailable, got %v", err)
}
if _, err := b.MPCTransition(nil, nil, nil, nil, nil); !errors.Is(err, ErrGPUNotAvailable) {
t.Errorf("MPCTransition: want ErrGPUNotAvailable, got %v", err)
}
}