Files
node/vms/xvm/block/executor/executionroot_test.go
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

190 lines
5.8 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package executor
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/node/vms/xvm/state/xvmroot"
)
// randUTXOLeaves builds n UTXO leaves with random fields and a random occupancy
// mix (roughly half unoccupied) so the parallel compaction is exercised against
// the serial one over a non-trivial occupied subset.
func randUTXOLeaves(rng *rand.Rand, n int) []xvmroot.UTXOLeaf {
us := make([]xvmroot.UTXOLeaf, n)
for i := range us {
rng.Read(us[i].UTXOID[:])
rng.Read(us[i].AssetID[:])
rng.Read(us[i].OwnerRoot[:])
us[i].AmountLo = rng.Uint64()
us[i].AmountHi = rng.Uint64()
us[i].Locktime = rng.Uint64()
us[i].Threshold = rng.Uint32()
if rng.Intn(2) == 0 {
us[i].Status = xvmroot.UTXOOccupied
}
}
return us
}
func randAssetLeaves(rng *rand.Rand, n int) []xvmroot.AssetLeaf {
as := make([]xvmroot.AssetLeaf, n)
for i := range as {
rng.Read(as[i].AssetID[:])
rng.Read(as[i].MintAuthorityRoot[:])
as[i].TotalSupplyLo = rng.Uint64()
as[i].TotalSupplyHi = rng.Uint64()
as[i].FreezeFlag = rng.Uint32()
as[i].Denomination = rng.Uint32()
if rng.Intn(2) == 0 {
as[i].Occupied = 1
}
}
return as
}
func randTxLeaves(rng *rand.Rand, n int) []xvmroot.TxLeaf {
ts := make([]xvmroot.TxLeaf, n)
for i := range ts {
rng.Read(ts[i].TxID[:])
rng.Read(ts[i].ProofDigest[:])
ts[i].Kind = rng.Uint32()
ts[i].Status = rng.Uint32()
ts[i].RejectReason = rng.Uint32()
}
return ts
}
// TestParallelFamilyRootsMatchSerial pins each parallel family fold byte-for-byte
// against the serial xvmroot family root, across a range of sizes that includes
// 0, 1, odd, even, and sizes straddling the inline/worker threshold — the leaf
// counts where lone-right Merkle promotion and worker sharding edges live.
func TestParallelFamilyRootsMatchSerial(t *testing.T) {
require := require.New(t)
rng := rand.New(rand.NewSource(0xC0FFEE))
sizes := []int{0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 33, 63, 64, 65,
127, 255, 256, 257, 511, 1000, 1023, 1024, 4097}
for _, n := range sizes {
us := randUTXOLeaves(rng, n)
require.Equalf(xvmroot.UTXORoot(us), parallelUTXORoot(us), "utxo root n=%d", n)
as := randAssetLeaves(rng, n)
require.Equalf(xvmroot.AssetRoot(as), parallelAssetRoot(as), "asset root n=%d", n)
ts := randTxLeaves(rng, n)
require.Equalf(xvmroot.TxRoot(ts), parallelTxRoot(ts), "tx root n=%d", n)
}
}
// TestParallelExecutionRootMatchesSerial is the property test the deliverable
// requires: the full parallel execution_root equals the serial
// xvmroot.ExecutionRoot over random state of random sizes (including odd
// per-family counts), so the parallel build is a drop-in for the serial oracle.
func TestParallelExecutionRootMatchesSerial(t *testing.T) {
require := require.New(t)
rng := rand.New(rand.NewSource(0x5EED))
for iter := 0; iter < 200; iter++ {
var parent [xvmroot.Size]byte
rng.Read(parent[:])
height := rng.Uint64()
// Independent, frequently-odd per-family sizes.
nU := rng.Intn(600)
nA := rng.Intn(600)
nT := rng.Intn(600)
us := randUTXOLeaves(rng, nU)
as := randAssetLeaves(rng, nA)
ts := randTxLeaves(rng, nT)
wantExec, wantU, wantA, wantT := xvmroot.ExecutionRoot(parent, us, as, ts, height)
gotExec, gotU, gotA, gotT := xvmExecutionRoot(parent, us, as, ts, height)
require.Equalf(wantU, gotU, "utxo iter=%d nU=%d", iter, nU)
require.Equalf(wantA, gotA, "asset iter=%d nA=%d", iter, nA)
require.Equalf(wantT, gotT, "tx iter=%d nT=%d", iter, nT)
require.Equalf(wantExec, gotExec, "exec iter=%d", iter)
}
}
// TestParallelExecutionRootEmpty confirms the all-empty state reduces to the
// same composed root the serial path yields (empty family roots are
// keccak256(""), not zero).
func TestParallelExecutionRootEmpty(t *testing.T) {
require := require.New(t)
var parent [xvmroot.Size]byte
wantExec, _, _, _ := xvmroot.ExecutionRoot(parent, nil, nil, nil, 0)
gotExec, _, _, _ := xvmExecutionRoot(parent, nil, nil, nil, 0)
require.Equal(wantExec, gotExec)
}
const benchUTXOCount = 65536
func benchUTXOSet() []xvmroot.UTXOLeaf {
rng := rand.New(rand.NewSource(1))
us := make([]xvmroot.UTXOLeaf, benchUTXOCount)
for i := range us {
rng.Read(us[i].UTXOID[:])
rng.Read(us[i].AssetID[:])
rng.Read(us[i].OwnerRoot[:])
us[i].AmountLo = rng.Uint64()
us[i].Threshold = 1
us[i].Status = xvmroot.UTXOOccupied // all occupied: worst-case fold width
}
return us
}
// BenchmarkUTXORootSerial folds a 65536-leaf UTXO set with the serial
// xvmroot.UTXORoot.
func BenchmarkUTXORootSerial(b *testing.B) {
us := benchUTXOSet()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = xvmroot.UTXORoot(us)
}
}
// BenchmarkUTXORootParallel folds the same 65536-leaf set with the parallel
// worker-pool fold. Compare against BenchmarkUTXORootSerial to see the
// leaf-hashing speedup.
func BenchmarkUTXORootParallel(b *testing.B) {
us := benchUTXOSet()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = parallelUTXORoot(us)
}
}
// BenchmarkExecutionRootSerial / Parallel compare the full execution_root build
// over a 65536-UTXO post-block state (with a modest asset + tx set), serial
// xvmroot.ExecutionRoot vs the concurrent xvmExecutionRoot.
func BenchmarkExecutionRootSerial(b *testing.B) {
var parent [xvmroot.Size]byte
us := benchUTXOSet()
rng := rand.New(rand.NewSource(2))
as := randAssetLeaves(rng, 4096)
ts := randTxLeaves(rng, 4096)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _, _ = xvmroot.ExecutionRoot(parent, us, as, ts, 100)
}
}
func BenchmarkExecutionRootParallel(b *testing.B) {
var parent [xvmroot.Size]byte
us := benchUTXOSet()
rng := rand.New(rand.NewSource(2))
as := randAssetLeaves(rng, 4096)
ts := randTxLeaves(rng, 4096)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _, _ = xvmExecutionRoot(parent, us, as, ts, 100)
}
}