Files
consensus/pkg/python/lux_consensus/__init__.py
T
zeekay f5da5ba21d refactor: clean package structure with single-import SDKs
BREAKING CHANGE: Package paths have been reorganized for clarity

## Major Changes

### Package Structure
- Collapsed engine/core → engine (removed unnecessary nesting)
- Moved core/types → types (top-level types package)
- Added root facade for single-import experience
- Removed redundant factory.go

### SDK Improvements
All SDKs now provide single-import interfaces:
- Go: import "github.com/luxfi/consensus"
- Python: import lux_consensus as consensus
- Rust: use lux_consensus::*

### Documentation
- Added comprehensive introduction and architecture docs
- Created AI/ML integration guide for researchers
- Added research section with papers and experiments
- Improved sidebar organization and discoverability

### Benefits
- Shallow, obvious import paths
- One responsibility per package
- DRY, composable, orthogonal design
- Idiomatic patterns for each language
- Consistent API across all SDKs

This refactor provides a clean, intuitive developer experience where
one import is enough and things work one way only.
2025-11-12 21:40:00 -08:00

348 lines
8.3 KiB
Python

"""
Lux Consensus Python SDK
A clean, single-import interface to the Lux consensus system.
This is the main SDK surface for Python applications using Lux consensus.
Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
"""
from typing import List, Optional, Dict, Any
from dataclasses import dataclass
from datetime import datetime
from enum import IntEnum
import time
# Version info
__version__ = '1.0.0'
__all__ = [
# Main classes
'Chain', 'Config', 'Block', 'Vote', 'Certificate',
# Enums
'Status', 'Decision', 'VoteType',
# Constants
'GENESIS_ID',
# Factory functions
'default_config', 'new_chain', 'new_block', 'new_vote', 'quick_start',
# Errors
'ConsensusError', 'BlockNotFoundError', 'InvalidBlockError', 'NoQuorumError'
]
# ============= TYPES =============
class Status(IntEnum):
"""Block status"""
UNKNOWN = 0
PROCESSING = 1
REJECTED = 2
ACCEPTED = 3
class Decision(IntEnum):
"""Consensus decision outcome"""
UNDECIDED = 0
ACCEPT = 1
REJECT = 2
class VoteType(IntEnum):
"""Vote type"""
PREFERENCE = 0
COMMIT = 1
CANCEL = 2
@dataclass
class ID:
"""Identifier type (simplified for Python)"""
value: bytes
def __init__(self, *args):
if len(args) == 1 and isinstance(args[0], bytes):
self.value = args[0]
else:
# Convert list of ints to bytes
self.value = bytes(args)
def __str__(self):
return self.value.hex()
def __eq__(self, other):
return isinstance(other, ID) and self.value == other.value
def __hash__(self):
return hash(self.value)
# Genesis block ID
GENESIS_ID = ID(b'\x00' * 32)
@dataclass
class Block:
"""Block in the blockchain"""
id: ID
parent_id: ID
height: int
payload: bytes
time: Optional[datetime] = None
def __post_init__(self):
if self.time is None:
self.time = datetime.now()
@dataclass
class Vote:
"""Vote on a block"""
block_id: ID
vote_type: VoteType
voter: ID # NodeID
signature: bytes = b''
@dataclass
class Certificate:
"""Consensus certificate"""
block_id: ID
height: int
votes: List[Vote]
timestamp: datetime
signatures: List[bytes]
@dataclass
class Config:
"""Consensus configuration"""
# Consensus parameters
alpha: int = 20 # Quorum size
k: int = 20 # Sample size
max_outstanding: int = 10 # Max outstanding polls
max_poll_delay: float = 1.0 # Max delay between polls (seconds)
# Network parameters
network_timeout: float = 5.0 # Network timeout (seconds)
max_message_size: int = 2 * 1024 * 1024 # 2MB
# Security parameters
security_level: int = 5 # NIST security level
quantum_resistant: bool = True # Use PQ crypto
gpu_acceleration: bool = True # Use GPU acceleration
# ============= ERRORS =============
class ConsensusError(Exception):
"""Base consensus error"""
pass
class BlockNotFoundError(ConsensusError):
"""Block not found error"""
pass
class InvalidBlockError(ConsensusError):
"""Invalid block error"""
pass
class InvalidVoteError(ConsensusError):
"""Invalid vote error"""
pass
class NoQuorumError(ConsensusError):
"""No quorum error"""
pass
class AlreadyVotedError(ConsensusError):
"""Already voted error"""
pass
class NotValidatorError(ConsensusError):
"""Not a validator error"""
pass
class TimeoutError(ConsensusError):
"""Operation timeout error"""
pass
class NotInitializedError(ConsensusError):
"""Engine not initialized error"""
pass
# ============= ENGINE =============
class Engine:
"""Base consensus engine interface"""
def add(self, block: Block) -> None:
"""Add a new block to consensus"""
raise NotImplementedError
def record_vote(self, vote: Vote) -> None:
"""Record a vote for a block"""
raise NotImplementedError
def is_accepted(self, block_id: ID) -> bool:
"""Check if a block has been accepted"""
raise NotImplementedError
def get_status(self, block_id: ID) -> Status:
"""Get the status of a block"""
raise NotImplementedError
def start(self) -> None:
"""Start the consensus engine"""
raise NotImplementedError
def stop(self) -> None:
"""Stop the consensus engine"""
raise NotImplementedError
class Chain(Engine):
"""Linear blockchain consensus engine"""
def __init__(self, config: Config):
self.config = config
self.blocks: Dict[ID, Block] = {}
self.votes: Dict[ID, List[Vote]] = {}
self.status: Dict[ID, Status] = {}
self.last_accepted = GENESIS_ID
self.height = 0
self.validators: List[ID] = []
self._started = False
def add(self, block: Block) -> None:
"""Add a new block to the chain"""
if not self._started:
raise NotInitializedError("Engine not started")
# Store the block
self.blocks[block.id] = block
self.status[block.id] = Status.PROCESSING
# Initialize vote tracking
if block.id not in self.votes:
self.votes[block.id] = []
def record_vote(self, vote: Vote) -> None:
"""Record a vote for a block"""
if not self._started:
raise NotInitializedError("Engine not started")
# Check if block exists
if vote.block_id not in self.blocks:
raise BlockNotFoundError(f"Block {vote.block_id} not found")
# Add vote
self.votes[vote.block_id].append(vote)
# Check if we have quorum
if len(self.votes[vote.block_id]) >= self.config.alpha:
self._accept_block(vote.block_id)
def is_accepted(self, block_id: ID) -> bool:
"""Check if a block has been accepted"""
return self.status.get(block_id) == Status.ACCEPTED
def get_status(self, block_id: ID) -> Status:
"""Get the status of a block"""
return self.status.get(block_id, Status.UNKNOWN)
def start(self) -> None:
"""Start the consensus engine"""
if self._started:
return
# Initialize genesis block
genesis = Block(
id=GENESIS_ID,
parent_id=ID(b'\x00' * 32),
height=0,
payload=b'',
time=datetime.now()
)
self.blocks[genesis.id] = genesis
self.status[genesis.id] = Status.ACCEPTED
self.last_accepted = genesis.id
self._started = True
def stop(self) -> None:
"""Stop the consensus engine"""
self._started = False
def _accept_block(self, block_id: ID) -> None:
"""Mark a block as accepted"""
self.status[block_id] = Status.ACCEPTED
block = self.blocks.get(block_id)
if block and block.height > self.height:
self.height = block.height
self.last_accepted = block_id
# ============= FACTORY FUNCTIONS =============
def default_config() -> Config:
"""Returns the default consensus configuration"""
return Config()
def new_chain(config: Optional[Config] = None) -> Chain:
"""Creates a new chain consensus engine"""
if config is None:
config = default_config()
return Chain(config)
def new_block(id: ID, parent_id: ID, height: int, payload: bytes) -> Block:
"""Creates a new block with default values"""
return Block(
id=id,
parent_id=parent_id,
height=height,
payload=payload
)
def new_vote(block_id: ID, vote_type: VoteType, voter: ID) -> Vote:
"""Creates a new vote"""
return Vote(
block_id=block_id,
vote_type=vote_type,
voter=voter
)
def quick_start() -> Chain:
"""Quick start a consensus engine with default config"""
chain = new_chain()
chain.start()
return chain
# ============= CONVENIENCE =============
# Re-export common constants for convenience
VOTE_PREFERENCE = VoteType.PREFERENCE
VOTE_COMMIT = VoteType.COMMIT
VOTE_CANCEL = VoteType.CANCEL
STATUS_UNKNOWN = Status.UNKNOWN
STATUS_PROCESSING = Status.PROCESSING
STATUS_REJECTED = Status.REJECTED
STATUS_ACCEPTED = Status.ACCEPTED
DECIDE_UNDECIDED = Decision.UNDECIDED
DECIDE_ACCEPT = Decision.ACCEPT
DECIDE_REJECT = Decision.REJECT