Files
zeekay 787284d140 mpcvm/fhe: one device round trip, three kernels
BatchNTTForward, BatchNTTInverse and BatchPolyMul were three copies of one
loop — snapshot the session, allocate operand tensors, run a kernel, sync, read
back, copy out, and on any failure at any step close what is open and do the
operation on the CPU instead. ~45 statements each, differing in how many
polynomials an element reads, which kernel runs, and what the CPU does instead.
Three copies of a resource lifecycle are three places to forget a Close, and a
fourth kernel would have been a fourth copy.

Now one offload plus one element, and three wrappers that name their three
differences. 101 lines go, no behaviour with them.

The floors are deliberately NOT unified: forward NTT reaches the device at 64
polynomials and ring degree 8192, the other two at 4 and any degree, and the
reasoning written on the forward path applies to all three. That reads as
drift, but changing a performance threshold needs a device to measure on and
this host has none, so the numbers are carried through as parameters and the
disagreement is stated where the next reader will see it.

Also drops the count == 0 guard, which count < minBatch already subsumes for
every floor; reverting it changed no test result, which is what redundant
means.
2026-08-28 15:02:56 -07:00

95 lines
3.6 KiB
Go

// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package main
import (
"bytes"
"context"
"errors"
"strings"
"testing"
"github.com/luxfi/chains/mpcvm"
"github.com/luxfi/vm/rpc/runtime"
)
// The `version` subcommand prints the id the binary must be INSTALLED as, not
// just the software version.
//
// The two facts fail differently and an operator needs both. A wrong software
// version starts a chain that misbehaves; a wrong FILENAME starts nothing at
// all — the node's registry resolves a CreateChainTx's vmID by looking for a
// file with that name, so a correctly built plugin under the wrong name is
// invisible, and the symptom is a chain that never appears with no error
// anywhere. Printing the id here puts it where an operator already looks.
func TestVersionNamesTheIdTheBinaryMustBeInstalledAs(t *testing.T) {
var out, errOut bytes.Buffer
if code := run(context.Background(), []string{"version"}, &out, &errOut); code != 0 {
t.Fatalf("version exited %d, want 0; stderr=%q", code, errOut.String())
}
got := out.String()
if !strings.Contains(got, mpcvm.Version.String()) {
t.Errorf("version output %q does not name the software version %s", got, mpcvm.Version)
}
if !strings.Contains(got, mpcvm.VMID.String()) {
t.Errorf("version output %q does not name the vmID %s the plugin must be installed as",
got, mpcvm.VMID)
}
if errOut.Len() != 0 {
t.Errorf("version wrote to stderr: %q", errOut.String())
}
}
// Anything that is not `version` serves, and serving needs a node. Started by
// hand there is no engine address in the environment, so the plugin says why it
// is not serving and exits non-zero.
//
// The exit code is the load-bearing half. A plugin that fails to serve and
// exits 0 looks to the node's runtime like a plugin that ran and finished, so
// the chain never starts and nothing reports a failure — the same silent
// absence a misnamed binary produces.
func TestAPluginWithNoNodeToServeSaysSoAndFails(t *testing.T) {
t.Setenv(runtime.EngineAddressKey, "")
var out, errOut bytes.Buffer
if code := run(context.Background(), nil, &out, &errOut); code != 1 {
t.Fatalf("run with no engine address exited %d, want 1", code)
}
if !strings.Contains(errOut.String(), "mpcvm plugin:") {
t.Errorf("stderr %q does not name the plugin as the source of the failure", errOut.String())
}
if !strings.Contains(errOut.String(), runtime.EngineAddressKey) {
t.Errorf("stderr %q does not name the missing environment variable %q",
errOut.String(), runtime.EngineAddressKey)
}
}
// An empty argument list and an argument that is not `version` take the same
// path: there is one subcommand, and everything else is "serve".
func TestOnlyVersionIsASubcommand(t *testing.T) {
t.Setenv(runtime.EngineAddressKey, "")
for _, args := range [][]string{nil, {}, {"serve"}, {"Version"}, {"--version"}} {
var out, errOut bytes.Buffer
if code := run(context.Background(), args, &out, &errOut); code != 1 {
t.Errorf("run(%q) exited %d, want 1 (it must have tried to serve)", args, code)
}
if out.Len() != 0 {
t.Errorf("run(%q) wrote %q to stdout; only `version` reports on stdout", args, out.String())
}
}
}
// fail is the one place a refusal is reported, so the message names the plugin
// and the code is non-zero whatever went wrong.
func TestEveryRefusalIsAttributedAndNonZero(t *testing.T) {
var w bytes.Buffer
if code := fail(&w, errors.New("disk fell off")); code != 1 {
t.Fatalf("fail() = %d, want 1", code)
}
if got := w.String(); got != "mpcvm plugin: disk fell off\n" {
t.Fatalf("fail() wrote %q", got)
}
}