mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
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.
248 lines
6.8 KiB
Go
248 lines
6.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package chains
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/ids"
|
|
)
|
|
|
|
// TestAcceptRejectDerivation tests the accept/reject derivation logic from the Chits handler
|
|
// This tests the core logic: Accept=true only if block exists AND verifies
|
|
func TestAcceptRejectDerivation(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
blockExists bool
|
|
verifyError error
|
|
expectedAccept bool
|
|
}{
|
|
{
|
|
name: "accept when block exists and verifies",
|
|
blockExists: true,
|
|
verifyError: nil,
|
|
expectedAccept: true,
|
|
},
|
|
{
|
|
name: "reject when block not found",
|
|
blockExists: false,
|
|
verifyError: nil,
|
|
expectedAccept: false,
|
|
},
|
|
{
|
|
name: "reject when block fails verification",
|
|
blockExists: true,
|
|
verifyError: errors.New("verification failed"),
|
|
expectedAccept: false,
|
|
},
|
|
{
|
|
name: "reject when block has invalid signature",
|
|
blockExists: true,
|
|
verifyError: errors.New("invalid signature"),
|
|
expectedAccept: false,
|
|
},
|
|
{
|
|
name: "reject when block has invalid parent",
|
|
blockExists: true,
|
|
verifyError: errors.New("unknown parent"),
|
|
expectedAccept: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// Simulate the accept/reject logic from HandleInbound
|
|
// This matches the logic in manager.go lines 1912-1920
|
|
accept := deriveAcceptFromVerification(
|
|
context.Background(),
|
|
tc.blockExists,
|
|
tc.verifyError,
|
|
)
|
|
|
|
require.Equal(tc.expectedAccept, accept, "Accept value mismatch")
|
|
})
|
|
}
|
|
}
|
|
|
|
// deriveAcceptFromVerification simulates the logic in blockHandler.HandleInbound
|
|
// for the Chits case. This is the core accept/reject derivation logic.
|
|
func deriveAcceptFromVerification(ctx context.Context, blockExists bool, verifyError error) bool {
|
|
// This matches the logic in manager.go:
|
|
// accept := false
|
|
// if b.vm != nil {
|
|
// if blk, err := b.vm.GetBlock(ctx, preferredID); err == nil {
|
|
// if err := blk.Verify(ctx); err == nil {
|
|
// accept = true
|
|
// }
|
|
// }
|
|
// }
|
|
if !blockExists {
|
|
return false
|
|
}
|
|
if verifyError != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// TestAcceptRejectWithNilVM tests that Accept=false when VM is nil
|
|
func TestAcceptRejectWithNilVM(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// When VM is nil, we can't get the block, so accept should be false
|
|
// This is the outer check: if b.vm != nil { ... }
|
|
vmIsNil := true
|
|
accept := false
|
|
if !vmIsNil {
|
|
// Would call vm.GetBlock and Verify
|
|
accept = true
|
|
}
|
|
|
|
require.False(accept, "Accept should be false when VM is nil")
|
|
}
|
|
|
|
// TestChitsMessageLength tests that messages shorter than 32 bytes are handled
|
|
func TestChitsMessageLength(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
messageLen int
|
|
shouldParse bool
|
|
}{
|
|
{
|
|
name: "empty message",
|
|
messageLen: 0,
|
|
shouldParse: false,
|
|
},
|
|
{
|
|
name: "message too short (16 bytes)",
|
|
messageLen: 16,
|
|
shouldParse: false,
|
|
},
|
|
{
|
|
name: "message too short (31 bytes)",
|
|
messageLen: 31,
|
|
shouldParse: false,
|
|
},
|
|
{
|
|
name: "message exactly 32 bytes",
|
|
messageLen: 32,
|
|
shouldParse: true,
|
|
},
|
|
{
|
|
name: "message longer than 32 bytes",
|
|
messageLen: 64,
|
|
shouldParse: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// This matches the logic in manager.go:
|
|
// if len(msg.Message) >= 32 && b.engine != nil { ... }
|
|
msg := make([]byte, tc.messageLen)
|
|
canParse := len(msg) >= 32
|
|
|
|
require.Equal(tc.shouldParse, canParse, "Parse decision mismatch")
|
|
|
|
// If we can parse, verify we can extract a block ID
|
|
if canParse {
|
|
var preferredID ids.ID
|
|
copy(preferredID[:], msg[:32])
|
|
require.Len(preferredID[:], 32, "Block ID should be 32 bytes")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestVoteCreation tests that Vote struct is created correctly with derived Accept value
|
|
func TestVoteCreation(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// Create a vote with Accept derived from verification
|
|
blockID := ids.GenerateTestID()
|
|
nodeID := ids.GenerateTestNodeID()
|
|
|
|
// Case 1: Block exists and verifies - accept should be true
|
|
accept1 := deriveAcceptFromVerification(context.Background(), true, nil)
|
|
require.True(accept1)
|
|
|
|
// Case 2: Block exists but fails verification - accept should be false
|
|
accept2 := deriveAcceptFromVerification(context.Background(), true, errors.New("bad block"))
|
|
require.False(accept2)
|
|
|
|
// Case 3: Block doesn't exist - accept should be false
|
|
accept3 := deriveAcceptFromVerification(context.Background(), false, nil)
|
|
require.False(accept3)
|
|
|
|
// Verify the block ID and node ID are preserved
|
|
require.NotEqual(ids.Empty, blockID)
|
|
require.NotEqual(ids.EmptyNodeID, nodeID)
|
|
}
|
|
|
|
// TestAcceptRejectOldVsNew tests that the new logic correctly rejects
|
|
// votes that would have been incorrectly accepted with hardcoded Accept=true
|
|
func TestAcceptRejectOldVsNew(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// OLD LOGIC (before fix): Accept was always true
|
|
// oldAccept := true // WRONG - this was hardcoded
|
|
|
|
// NEW LOGIC: Accept is derived from verification
|
|
// This ensures we don't accept votes for blocks we can't verify
|
|
|
|
testCases := []struct {
|
|
name string
|
|
blockExists bool
|
|
verifyError error
|
|
oldAccept bool // What old logic would have done (always true)
|
|
newAccept bool // What new logic does (derived from verification)
|
|
}{
|
|
{
|
|
name: "valid block - old and new agree",
|
|
blockExists: true,
|
|
verifyError: nil,
|
|
oldAccept: true,
|
|
newAccept: true,
|
|
},
|
|
{
|
|
name: "missing block - old accepted wrongly, new rejects",
|
|
blockExists: false,
|
|
verifyError: nil,
|
|
oldAccept: true, // BUG: Old logic would accept
|
|
newAccept: false, // CORRECT: New logic rejects
|
|
},
|
|
{
|
|
name: "invalid block - old accepted wrongly, new rejects",
|
|
blockExists: true,
|
|
verifyError: errors.New("invalid"),
|
|
oldAccept: true, // BUG: Old logic would accept
|
|
newAccept: false, // CORRECT: New logic rejects
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
newAccept := deriveAcceptFromVerification(context.Background(), tc.blockExists, tc.verifyError)
|
|
require.Equal(tc.newAccept, newAccept, "New accept value mismatch")
|
|
|
|
// Verify the new logic is an improvement over the old logic
|
|
if tc.blockExists && tc.verifyError == nil {
|
|
// Valid block: both should accept
|
|
require.Equal(tc.oldAccept, newAccept)
|
|
} else {
|
|
// Invalid or missing block: new logic should reject, old logic was wrong
|
|
require.NotEqual(tc.oldAccept, newAccept, "New logic should reject what old logic wrongly accepted")
|
|
}
|
|
})
|
|
}
|
|
}
|