Files
node/chains/quorum_pubkey_encoding_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

83 lines
2.5 KiB
Go

// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package chains
import (
"testing"
"github.com/luxfi/crypto/bls"
)
// A validator's registered BLS key must verify in EITHER G1 encoding.
//
// Hanzo L1 36963 halted on exactly this: every validator in its set carries a
// 96-byte UNCOMPRESSED G1 key, the verifier parsed only 48-byte COMPRESSED, so
// key parsing failed for every voter and no vote could ever be counted. The
// chain sat at one height forever while every other signal looked healthy.
func TestValidatorPublicKeyParsesInBothG1Encodings(t *testing.T) {
sk, err := bls.NewSecretKey()
if err != nil {
t.Fatalf("keygen: %v", err)
}
pub := sk.PublicKey()
compressed := bls.PublicKeyToCompressedBytes(pub)
uncompressed := bls.PublicKeyToUncompressedBytes(pub)
if len(compressed) != bls.PublicKeyLen {
t.Fatalf("compressed len = %d, want %d", len(compressed), bls.PublicKeyLen)
}
if len(uncompressed) != 2*bls.PublicKeyLen {
t.Fatalf("uncompressed len = %d, want %d", len(uncompressed), 2*bls.PublicKeyLen)
}
// The real test: BOTH encodings must parse, and both must verify the SAME
// signature. Parsing alone is not enough — a mis-parsed key would still fail
// bls.Verify and strand the chain the same way.
msg := []byte("canonical vote message")
sig, err := sk.Sign(msg)
if err != nil {
t.Fatalf("sign: %v", err)
}
for name, raw := range map[string][]byte{
"compressed(48)": compressed,
"uncompressed(96)": uncompressed,
} {
pk, err := blsPublicKeyFromValidatorBytes(raw)
if err != nil || pk == nil {
t.Errorf("%s: parse failed: %v", name, err)
continue
}
if !bls.Verify(pk, sig, msg) {
t.Errorf("%s: parsed but did not verify a valid signature", name)
}
}
}
// Widening what can be PARSED must not widen what is ACCEPTED.
func TestBadValidatorPublicKeyIsStillRejected(t *testing.T) {
for name, raw := range map[string][]byte{
"empty": {},
"short": make([]byte, 47),
"between": make([]byte, 64),
"too long": make([]byte, 128),
"all zeros": make([]byte, bls.PublicKeyLen),
} {
pk, err := blsPublicKeyFromValidatorBytes(raw)
if err == nil && pk != nil {
// A zero-length-correct blob may parse; it must NOT verify anything.
if sig, sErr := bls.SignatureFromBytes(make([]byte, bls.SignatureLen)); sErr == nil && sig != nil {
if bls.Verify(pk, sig, []byte("m")) {
t.Errorf("%s: garbage key verified a signature", name)
}
}
continue
}
if err == nil {
t.Errorf("%s: expected an error", name)
}
}
}