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

247 lines
6.2 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package compression
import (
"fmt"
"math"
"runtime"
"testing"
"github.com/stretchr/testify/require"
_ "embed"
"github.com/luxfi/node/utils"
"github.com/luxfi/units"
)
const maxMessageSize = int64(2 * units.MiB) // Max message size. Can't import due to cycle.
var (
newCompressorFuncs = map[Type]func(maxSize int64) (Compressor, error){
TypeNone: func(int64) (Compressor, error) { //nolint:unparam // an error is needed to be returned to compile
return NewNoCompressor(), nil
},
TypeZstd: NewZstdCompressor,
}
//go:embed zstd_zip_bomb.bin
zstdZipBomb []byte
zipBombs = map[Type][]byte{
TypeZstd: zstdZipBomb,
}
)
func TestDecompressZipBombs(t *testing.T) {
for compressionType, zipBomb := range zipBombs {
// Make sure that the hardcoded zip bomb would be a valid message.
require.Less(t, len(zipBomb), int(maxMessageSize))
newCompressorFunc := newCompressorFuncs[compressionType]
t.Run(compressionType.String(), func(t *testing.T) {
require := require.New(t)
compressor, err := newCompressorFunc(maxMessageSize)
require.NoError(err)
var (
beforeDecompressionStats runtime.MemStats
afterDecompressionStats runtime.MemStats
)
runtime.ReadMemStats(&beforeDecompressionStats)
_, err = compressor.Decompress(zipBomb)
runtime.ReadMemStats(&afterDecompressionStats)
require.ErrorIs(err, ErrDecompressedMsgTooLarge)
// Make sure that we didn't allocate significantly more memory than
// the max message size.
bytesAllocatedDuringDecompression := afterDecompressionStats.TotalAlloc - beforeDecompressionStats.TotalAlloc
require.Less(bytesAllocatedDuringDecompression, uint64(10*maxMessageSize))
})
}
}
func TestCompressDecompress(t *testing.T) {
for compressionType, newCompressorFunc := range newCompressorFuncs {
t.Run(compressionType.String(), func(t *testing.T) {
require := require.New(t)
data := utils.RandomBytes(4096)
data2 := utils.RandomBytes(4096)
compressor, err := newCompressorFunc(maxMessageSize)
require.NoError(err)
dataCompressed, err := compressor.Compress(data)
require.NoError(err)
data2Compressed, err := compressor.Compress(data2)
require.NoError(err)
dataDecompressed, err := compressor.Decompress(dataCompressed)
require.NoError(err)
require.Equal(data, dataDecompressed)
data2Decompressed, err := compressor.Decompress(data2Compressed)
require.NoError(err)
require.Equal(data2, data2Decompressed)
dataDecompressed, err = compressor.Decompress(dataCompressed)
require.NoError(err)
require.Equal(data, dataDecompressed)
maxMessage := utils.RandomBytes(int(maxMessageSize))
maxMessageCompressed, err := compressor.Compress(maxMessage)
require.NoError(err)
maxMessageDecompressed, err := compressor.Decompress(maxMessageCompressed)
require.NoError(err)
require.Equal(maxMessage, maxMessageDecompressed)
})
}
}
func TestSizeLimiting(t *testing.T) {
for compressionType, compressorFunc := range newCompressorFuncs {
if compressionType == TypeNone {
continue
}
t.Run(compressionType.String(), func(t *testing.T) {
require := require.New(t)
compressor, err := compressorFunc(maxMessageSize)
require.NoError(err)
data := make([]byte, int(maxMessageSize)+1)
_, err = compressor.Compress(data) // should be too large
require.ErrorIs(err, ErrMsgTooLarge)
compressor2, err := compressorFunc(2 * maxMessageSize)
require.NoError(err)
dataCompressed, err := compressor2.Compress(data)
require.NoError(err)
_, err = compressor.Decompress(dataCompressed) // should be too large
require.ErrorIs(err, ErrDecompressedMsgTooLarge)
})
}
}
// Attempts to create a compressor with math.MaxInt64
// which leads to undefined decompress behavior due to integer overflow
// in limit reader creation.
func TestNewCompressorWithInvalidLimit(t *testing.T) {
for compressionType, compressorFunc := range newCompressorFuncs {
if compressionType == TypeNone {
continue
}
t.Run(compressionType.String(), func(t *testing.T) {
_, err := compressorFunc(math.MaxInt64)
require.ErrorIs(t, err, ErrInvalidMaxSizeCompressor)
})
}
}
func FuzzZstdCompressor(f *testing.F) {
fuzzHelper(f, TypeZstd)
}
func fuzzHelper(f *testing.F, compressionType Type) {
var (
compressor Compressor
err error
)
switch compressionType {
case TypeZstd:
compressor, err = NewZstdCompressor(maxMessageSize)
require.NoError(f, err)
default:
require.FailNow(f, "Unknown compression type")
}
f.Fuzz(func(t *testing.T, data []byte) {
require := require.New(t)
if len(data) > int(maxMessageSize) {
_, err := compressor.Compress(data)
require.ErrorIs(err, ErrMsgTooLarge)
return
}
compressed, err := compressor.Compress(data)
require.NoError(err)
decompressed, err := compressor.Decompress(compressed)
require.NoError(err)
require.Equal(data, decompressed)
})
}
func BenchmarkCompress(b *testing.B) {
sizes := []int{
0,
256,
int(units.KiB),
int(units.MiB),
int(maxMessageSize),
}
for compressionType, newCompressorFunc := range newCompressorFuncs {
if compressionType == TypeNone {
continue
}
for _, size := range sizes {
b.Run(fmt.Sprintf("%s_%d", compressionType, size), func(b *testing.B) {
require := require.New(b)
bytes := utils.RandomBytes(size)
compressor, err := newCompressorFunc(maxMessageSize)
require.NoError(err)
for n := 0; n < b.N; n++ {
_, err := compressor.Compress(bytes)
require.NoError(err)
}
})
}
}
}
func BenchmarkDecompress(b *testing.B) {
sizes := []int{
0,
256,
int(units.KiB),
int(units.MiB),
int(maxMessageSize),
}
for compressionType, newCompressorFunc := range newCompressorFuncs {
if compressionType == TypeNone {
continue
}
for _, size := range sizes {
b.Run(fmt.Sprintf("%s_%d", compressionType, size), func(b *testing.B) {
require := require.New(b)
bytes := utils.RandomBytes(size)
compressor, err := newCompressorFunc(maxMessageSize)
require.NoError(err)
compressedBytes, err := compressor.Compress(bytes)
require.NoError(err)
for n := 0; n < b.N; n++ {
_, err := compressor.Decompress(compressedBytes)
require.NoError(err)
}
})
}
}
}