mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
fix: make accel dependency optional with build tags
The ai package imported github.com/luxfi/accel which requires CGO and C headers not available in CI. This caused integration tests to fail with [build failed] because the dependency chain couldn't compile. Solution: - Split accel.go into accel_cgo.go (requires cgo && accel tags) and accel_stub.go (fallback without CGO) - Extract shared types to accel_types.go - Update tests to skip when acceleration is not available
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build cgo && accel
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
@@ -11,19 +13,6 @@ import (
|
||||
"github.com/luxfi/accel/ops/consensus"
|
||||
)
|
||||
|
||||
// Vote represents a consensus vote for batch processing.
|
||||
type Vote struct {
|
||||
VoterID [32]byte
|
||||
BlockID [32]byte
|
||||
IsPreference bool
|
||||
}
|
||||
|
||||
// ValidatorInfo contains validator information for quorum calculations.
|
||||
type ValidatorInfo struct {
|
||||
ValidatorID [32]byte
|
||||
Weight uint64
|
||||
}
|
||||
|
||||
// Backend provides GPU-accelerated consensus using luxfi/accel.
|
||||
type Backend struct {
|
||||
mu sync.RWMutex
|
||||
@@ -81,7 +70,7 @@ func (b *Backend) ProcessVotesBatch(votes []Vote) (int, error) {
|
||||
}
|
||||
|
||||
// ComputeQuorum checks if a quorum is reached for a set of votes.
|
||||
func (b *Backend) ComputeQuorum(votes []Vote, validators []ValidatorInfo, threshold float64) (*consensus.QuorumResult, error) {
|
||||
func (b *Backend) ComputeQuorum(votes []Vote, validators []ValidatorInfo, threshold float64) (*QuorumResult, error) {
|
||||
if !b.initialized {
|
||||
return nil, fmt.Errorf("backend not initialized")
|
||||
}
|
||||
@@ -103,7 +92,17 @@ func (b *Backend) ComputeQuorum(votes []Vote, validators []ValidatorInfo, thresh
|
||||
}
|
||||
}
|
||||
|
||||
return consensus.ComputeQuorum(voteData, validatorWeights, threshold)
|
||||
result, err := consensus.ComputeQuorum(voteData, validatorWeights, threshold)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &QuorumResult{
|
||||
HasQuorum: result.HasQuorum,
|
||||
TotalWeight: result.TotalWeight,
|
||||
VotedWeight: result.VotedWeight,
|
||||
QuorumWeight: result.QuorumWeight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetThroughput returns the current throughput in votes/second.
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo || !accel
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Backend provides a stub consensus backend when luxfi/accel is not available.
|
||||
// This stub allows the package to compile without CGO dependencies.
|
||||
type Backend struct {
|
||||
mu sync.RWMutex
|
||||
batchSize int
|
||||
throughput float64
|
||||
initialized bool
|
||||
}
|
||||
|
||||
// NewBackend creates a stub consensus backend.
|
||||
// Returns an error indicating acceleration is not available.
|
||||
func NewBackend(batchSize int) (*Backend, error) {
|
||||
return &Backend{
|
||||
batchSize: batchSize,
|
||||
initialized: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessVotesBatch is a stub that returns an error when accel is not available.
|
||||
func (b *Backend) ProcessVotesBatch(votes []Vote) (int, error) {
|
||||
if len(votes) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
// Stub implementation: just count votes without GPU acceleration
|
||||
return len(votes), nil
|
||||
}
|
||||
|
||||
// ComputeQuorum is a stub that computes quorum without GPU acceleration.
|
||||
func (b *Backend) ComputeQuorum(votes []Vote, validators []ValidatorInfo, threshold float64) (*QuorumResult, error) {
|
||||
if len(validators) == 0 {
|
||||
return nil, fmt.Errorf("no validators provided")
|
||||
}
|
||||
|
||||
// Create a map of validator weights
|
||||
weights := make(map[[32]byte]uint64)
|
||||
var totalWeight uint64
|
||||
for _, v := range validators {
|
||||
weights[v.ValidatorID] = v.Weight
|
||||
totalWeight += v.Weight
|
||||
}
|
||||
|
||||
// Count voted weight
|
||||
var votedWeight uint64
|
||||
votedValidators := make(map[[32]byte]bool)
|
||||
for _, vote := range votes {
|
||||
if vote.IsPreference && !votedValidators[vote.VoterID] {
|
||||
if weight, ok := weights[vote.VoterID]; ok {
|
||||
votedWeight += weight
|
||||
votedValidators[vote.VoterID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quorumWeight := uint64(float64(totalWeight) * threshold)
|
||||
hasQuorum := votedWeight >= quorumWeight
|
||||
|
||||
return &QuorumResult{
|
||||
HasQuorum: hasQuorum,
|
||||
TotalWeight: totalWeight,
|
||||
VotedWeight: votedWeight,
|
||||
QuorumWeight: quorumWeight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetThroughput returns zero when acceleration is not available.
|
||||
func (b *Backend) GetThroughput() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsEnabled returns false when acceleration is not available.
|
||||
func (b *Backend) IsEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetDeviceInfo returns empty string when acceleration is not available.
|
||||
func (b *Backend) GetDeviceInfo() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// NewMLXBackend creates a stub backend when MLX is not available.
|
||||
func NewMLXBackend(batchSize int) (*Backend, error) {
|
||||
return NewBackend(batchSize)
|
||||
}
|
||||
|
||||
// NewAccelBackend creates a stub backend when acceleration is not available.
|
||||
func NewAccelBackend(batchSize int) (*Backend, error) {
|
||||
return NewBackend(batchSize)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package ai
|
||||
|
||||
// Vote represents a consensus vote for batch processing.
|
||||
type Vote struct {
|
||||
VoterID [32]byte
|
||||
BlockID [32]byte
|
||||
IsPreference bool
|
||||
}
|
||||
|
||||
// ValidatorInfo contains validator information for quorum calculations.
|
||||
type ValidatorInfo struct {
|
||||
ValidatorID [32]byte
|
||||
Weight uint64
|
||||
}
|
||||
|
||||
// QuorumResult contains the result of a quorum check.
|
||||
type QuorumResult struct {
|
||||
HasQuorum bool
|
||||
TotalWeight uint64
|
||||
VotedWeight uint64
|
||||
QuorumWeight uint64
|
||||
}
|
||||
+56
-8
@@ -14,8 +14,10 @@ func TestMLXBackendInitialization(t *testing.T) {
|
||||
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
||||
}
|
||||
|
||||
// When running without accel, the backend may not be enabled
|
||||
// This is expected behavior - skip tests that require acceleration
|
||||
if !backend.IsEnabled() {
|
||||
t.Error("MLX backend should be enabled")
|
||||
t.Skip("MLX backend not available (requires accel build tag)")
|
||||
}
|
||||
|
||||
if backend.GetDeviceInfo() == "" {
|
||||
@@ -54,10 +56,10 @@ func TestMLXBackendProcessVotes(t *testing.T) {
|
||||
t.Errorf("Expected to process %d votes, got %d", len(votes), processed)
|
||||
}
|
||||
|
||||
// Check throughput was updated
|
||||
// Check throughput - only expect positive throughput when enabled
|
||||
throughput := backend.GetThroughput()
|
||||
if throughput <= 0 {
|
||||
t.Error("Throughput should be positive after processing")
|
||||
if backend.IsEnabled() && throughput <= 0 {
|
||||
t.Error("Throughput should be positive after processing when enabled")
|
||||
}
|
||||
t.Logf("Processing throughput: %.2f votes/sec", throughput)
|
||||
}
|
||||
@@ -145,10 +147,10 @@ func TestMLXBackendConcurrency(t *testing.T) {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Check throughput after concurrent processing
|
||||
// Check throughput after concurrent processing - only expect positive when enabled
|
||||
throughput := backend.GetThroughput()
|
||||
if throughput <= 0 {
|
||||
t.Error("Throughput should be positive after concurrent processing")
|
||||
if backend.IsEnabled() && throughput <= 0 {
|
||||
t.Error("Throughput should be positive after concurrent processing when enabled")
|
||||
}
|
||||
t.Logf("Concurrent processing throughput: %.2f votes/sec", throughput)
|
||||
}
|
||||
@@ -160,8 +162,10 @@ func TestAccelBackendInitialization(t *testing.T) {
|
||||
t.Fatalf("Failed to initialize AccelBackend: %v", err)
|
||||
}
|
||||
|
||||
// When running without accel, the backend may not be enabled
|
||||
// This is expected behavior - skip tests that require acceleration
|
||||
if !backend.IsEnabled() {
|
||||
t.Error("AccelBackend should be enabled")
|
||||
t.Skip("AccelBackend not available (requires accel build tag)")
|
||||
}
|
||||
|
||||
if backend.GetDeviceInfo() == "" {
|
||||
@@ -194,3 +198,47 @@ func TestAccelBackendProcessVotes(t *testing.T) {
|
||||
t.Errorf("Expected to process %d votes, got %d", len(votes), processed)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackendStubFunctionality tests that the stub works correctly
|
||||
func TestBackendStubFunctionality(t *testing.T) {
|
||||
backend, err := NewBackend(100)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create backend: %v", err)
|
||||
}
|
||||
|
||||
// Test that ProcessVotesBatch works (with or without accel)
|
||||
votes := make([]Vote, 5)
|
||||
for i := range votes {
|
||||
votes[i] = Vote{
|
||||
VoterID: [32]byte{byte(i)},
|
||||
BlockID: [32]byte{1},
|
||||
IsPreference: true,
|
||||
}
|
||||
}
|
||||
|
||||
processed, err := backend.ProcessVotesBatch(votes)
|
||||
if err != nil {
|
||||
t.Fatalf("ProcessVotesBatch failed: %v", err)
|
||||
}
|
||||
if processed != len(votes) {
|
||||
t.Errorf("Expected %d processed, got %d", len(votes), processed)
|
||||
}
|
||||
|
||||
// Test ComputeQuorum works
|
||||
validators := []ValidatorInfo{
|
||||
{ValidatorID: [32]byte{0}, Weight: 100},
|
||||
{ValidatorID: [32]byte{1}, Weight: 100},
|
||||
{ValidatorID: [32]byte{2}, Weight: 100},
|
||||
}
|
||||
|
||||
result, err := backend.ComputeQuorum(votes, validators, 0.5)
|
||||
if err != nil {
|
||||
t.Fatalf("ComputeQuorum failed: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("ComputeQuorum returned nil result")
|
||||
}
|
||||
|
||||
t.Logf("Quorum result: HasQuorum=%v, VotedWeight=%d, TotalWeight=%d",
|
||||
result.HasQuorum, result.VotedWeight, result.TotalWeight)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user