Files
netrunner/multinet/manager.go
T
2026-01-14 19:56:11 -08:00

475 lines
12 KiB
Go

// Package multinet provides support for running multiple heterogeneous networks
// in parallel with shared BadgerDB for cross-chain ACID transactions
package multinet
import (
"context"
"fmt"
"sync"
"time"
"github.com/dgraph-io/badger/v4"
"github.com/luxfi/ids"
log "github.com/luxfi/log"
"github.com/luxfi/netrunner/network"
)
// NetworkType represents the type of network
type NetworkType string
const (
NetworkTypePrimary NetworkType = "primary" // Mainnet or Testnet
NetworkTypeChain NetworkType = "chain" // L1/L2 chains
)
// NetworkConfig defines configuration for a single network
type NetworkConfig struct {
NetworkID uint32 `json:"networkID"`
Name string `json:"name"`
Type NetworkType `json:"type"`
ParentID uint32 `json:"parentID,omitempty"` // For chains
HTTPPort int `json:"httpPort"`
StakingPort int `json:"stakingPort"`
DataDir string `json:"dataDir"`
Validators int `json:"validators"`
ChainConfig []ChainConfig `json:"chains,omitempty"`
}
// ChainConfig defines configuration for a chain within a network
type ChainConfig struct {
ChainID string `json:"chainID"`
VMID string `json:"vmID"`
PChainID string `json:"pChainID,omitempty"`
Genesis []byte `json:"genesis,omitempty"`
IsEVM bool `json:"isEVM"`
}
// MultiNetworkManager manages multiple networks running in parallel
type MultiNetworkManager struct {
mu sync.RWMutex
// Shared database for cross-chain ACID transactions
sharedDB *badger.DB
// Network instances
networks map[uint32]*NetworkInstance
// Consensus managers for parallel validation
consensusManagers map[uint32]*ConsensusManager
// Cross-chain transaction coordinator
txCoordinator *CrossChainTxCoordinator
// Logger
logger log.Logger
// Context for lifecycle management
ctx context.Context
cancel context.CancelFunc
}
// NetworkInstance represents a running network
type NetworkInstance struct {
Config NetworkConfig
Network network.Network
Status NetworkStatus
Chains map[string]*ChainInstance
StartTime time.Time
}
// ChainInstance represents a running chain
type ChainInstance struct {
ChainID string
PChainID string
ParentNetID uint32
Validators []ids.NodeID
Status NetworkStatus
}
// NetworkStatus represents the status of a network
type NetworkStatus string
const (
NetworkStatusStopped NetworkStatus = "stopped"
NetworkStatusStarting NetworkStatus = "starting"
NetworkStatusRunning NetworkStatus = "running"
NetworkStatusStopping NetworkStatus = "stopping"
NetworkStatusError NetworkStatus = "error"
)
// ConsensusManager handles consensus for a network
type ConsensusManager struct {
NetworkID uint32
Type string // "snowman" or "avalanche"
Params ConsensusParams
mu sync.RWMutex
}
// ConsensusParams defines consensus parameters
type ConsensusParams struct {
K int `json:"k"`
Alpha int `json:"alpha"`
BetaVirtuous int `json:"betaVirtuous"`
BetaRogue int `json:"betaRogue"`
ConcurrentPolls int `json:"concurrentPolls"`
OptimalProcessing int `json:"optimalProcessing"`
MaxProcessing int `json:"maxProcessing"`
MaxTimeProcessing time.Duration `json:"maxTimeProcessing"`
}
// CrossChainTxCoordinator coordinates transactions across chains
type CrossChainTxCoordinator struct {
sharedDB *badger.DB
mu sync.RWMutex
// Pending cross-chain transactions
pendingTxs map[string]*CrossChainTx
// Transaction channels for each network
txChannels map[uint32]chan *CrossChainTx
}
// CrossChainTx represents a cross-chain transaction
type CrossChainTx struct {
ID string
SourceNet uint32
SourceChain string
DestNet uint32
DestChain string
Amount uint64
Asset string
Status TxStatus
Timestamp time.Time
}
// TxStatus represents transaction status
type TxStatus string
const (
TxStatusPending TxStatus = "pending"
TxStatusValidating TxStatus = "validating"
TxStatusCommitted TxStatus = "committed"
TxStatusFailed TxStatus = "failed"
)
// NewMultiNetworkManager creates a new multi-network manager
func NewMultiNetworkManager(logger log.Logger, sharedDBPath string) (*MultiNetworkManager, error) {
// Open shared BadgerDB for cross-chain ACID transactions
opts := badger.DefaultOptions(sharedDBPath)
opts.Logger = nil // Disable BadgerDB's internal logging
sharedDB, err := badger.Open(opts)
if err != nil {
return nil, fmt.Errorf("failed to open shared database: %w", err)
}
ctx, cancel := context.WithCancel(context.Background())
return &MultiNetworkManager{
sharedDB: sharedDB,
networks: make(map[uint32]*NetworkInstance),
consensusManagers: make(map[uint32]*ConsensusManager),
txCoordinator: NewCrossChainTxCoordinator(sharedDB),
logger: logger,
ctx: ctx,
cancel: cancel,
}, nil
}
// NewCrossChainTxCoordinator creates a new cross-chain transaction coordinator
func NewCrossChainTxCoordinator(db *badger.DB) *CrossChainTxCoordinator {
return &CrossChainTxCoordinator{
sharedDB: db,
pendingTxs: make(map[string]*CrossChainTx),
txChannels: make(map[uint32]chan *CrossChainTx),
}
}
// AddNetwork adds a network configuration
func (m *MultiNetworkManager) AddNetwork(config NetworkConfig) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, exists := m.networks[config.NetworkID]; exists {
return fmt.Errorf("network %d already exists", config.NetworkID)
}
instance := &NetworkInstance{
Config: config,
Status: NetworkStatusStopped,
Chains: make(map[string]*ChainInstance),
}
m.networks[config.NetworkID] = instance
// Create consensus manager for this network
m.consensusManagers[config.NetworkID] = &ConsensusManager{
NetworkID: config.NetworkID,
Type: "snowman", // Default to Snowman consensus
Params: DefaultConsensusParams(),
}
// Create transaction channel for this network
m.txCoordinator.txChannels[config.NetworkID] = make(chan *CrossChainTx, 100)
m.logger.Info("Added network configuration",
"networkID", config.NetworkID,
"name", config.Name,
"type", config.Type,
)
return nil
}
// DefaultConsensusParams returns default consensus parameters
func DefaultConsensusParams() ConsensusParams {
return ConsensusParams{
K: 1, // For single validator testing
Alpha: 1,
BetaVirtuous: 1,
BetaRogue: 2,
ConcurrentPolls: 4,
OptimalProcessing: 10,
MaxProcessing: 1000,
MaxTimeProcessing: 120 * time.Second,
}
}
// StartNetwork starts a specific network
func (m *MultiNetworkManager) StartNetwork(networkID uint32) error {
m.mu.Lock()
defer m.mu.Unlock()
instance, exists := m.networks[networkID]
if !exists {
return fmt.Errorf("network %d not found", networkID)
}
if instance.Status == NetworkStatusRunning {
return fmt.Errorf("network %d is already running", networkID)
}
instance.Status = NetworkStatusStarting
instance.StartTime = time.Now()
// Start consensus manager for this network
go m.runConsensus(networkID)
// Start transaction processor for this network
go m.processCrossChainTxs(networkID)
instance.Status = NetworkStatusRunning
m.logger.Info("Started network",
"networkID", networkID,
"name", instance.Config.Name,
"httpPort", instance.Config.HTTPPort,
"stakingPort", instance.Config.StakingPort,
)
return nil
}
// StartAll starts all configured networks in parallel
func (m *MultiNetworkManager) StartAll() error {
m.mu.RLock()
networkIDs := make([]uint32, 0, len(m.networks))
for id := range m.networks {
networkIDs = append(networkIDs, id)
}
m.mu.RUnlock()
var wg sync.WaitGroup
errCh := make(chan error, len(networkIDs))
for _, networkID := range networkIDs {
wg.Add(1)
go func(id uint32) {
defer wg.Done()
if err := m.StartNetwork(id); err != nil {
errCh <- fmt.Errorf("failed to start network %d: %w", id, err)
}
}(networkID)
}
wg.Wait()
close(errCh)
// Check for errors
for err := range errCh {
if err != nil {
return err
}
}
m.logger.Info("All networks started successfully")
return nil
}
// runConsensus runs consensus for a network
func (m *MultiNetworkManager) runConsensus(networkID uint32) {
consensus := m.consensusManagers[networkID]
ticker := time.NewTicker(100 * time.Millisecond) // Fast consensus for testing
defer ticker.Stop()
m.logger.Info("Starting consensus engine",
"networkID", networkID,
"type", consensus.Type,
"k", consensus.Params.K,
)
for {
select {
case <-m.ctx.Done():
return
case <-ticker.C:
// Simulate consensus round
// In production, this would run actual Snowman/Avalanche consensus
}
}
}
// processCrossChainTxs processes cross-chain transactions for a network
func (m *MultiNetworkManager) processCrossChainTxs(networkID uint32) {
txChan := m.txCoordinator.txChannels[networkID]
for {
select {
case <-m.ctx.Done():
return
case tx := <-txChan:
if tx == nil {
continue
}
// Process cross-chain transaction with ACID guarantees
err := m.processACIDTransaction(tx)
if err != nil {
m.logger.Error("Failed to process cross-chain transaction",
"txID", tx.ID,
"error", err,
)
tx.Status = TxStatusFailed
} else {
tx.Status = TxStatusCommitted
}
}
}
}
// processACIDTransaction processes a transaction with ACID guarantees
func (m *MultiNetworkManager) processACIDTransaction(tx *CrossChainTx) error {
// Use BadgerDB transaction for ACID guarantees
return m.sharedDB.Update(func(txn *badger.Txn) error {
// 1. Debit from source chain
sourceKey := fmt.Sprintf("balance:%d:%s:%s", tx.SourceNet, tx.SourceChain, tx.Asset)
sourceItem, err := txn.Get([]byte(sourceKey))
if err != nil {
return fmt.Errorf("failed to get source balance: %w", err)
}
var sourceBalance uint64
err = sourceItem.Value(func(val []byte) error {
// Parse balance (simplified - would use proper encoding)
return nil
})
if err != nil {
return err
}
if sourceBalance < tx.Amount {
return fmt.Errorf("insufficient balance")
}
// 2. Credit to destination chain
_ = fmt.Sprintf("balance:%d:%s:%s", tx.DestNet, tx.DestChain, tx.Asset) // destKey - TODO: implement credit
// 3. Record transaction
txKey := fmt.Sprintf("tx:%s", tx.ID)
txValue := fmt.Sprintf("%d:%s:%d:%s:%d:%s",
tx.SourceNet, tx.SourceChain,
tx.DestNet, tx.DestChain,
tx.Amount, tx.Asset,
)
// All operations happen atomically
if err := txn.Set([]byte(txKey), []byte(txValue)); err != nil {
return err
}
return nil
})
}
// GetNetworkStatus returns the status of all networks
func (m *MultiNetworkManager) GetNetworkStatus() map[uint32]NetworkStatus {
m.mu.RLock()
defer m.mu.RUnlock()
status := make(map[uint32]NetworkStatus)
for id, instance := range m.networks {
status[id] = instance.Status
}
return status
}
// SubmitCrossChainTx submits a cross-chain transaction
func (m *MultiNetworkManager) SubmitCrossChainTx(tx *CrossChainTx) error {
m.mu.RLock()
defer m.mu.RUnlock()
// Validate networks exist
if _, exists := m.networks[tx.SourceNet]; !exists {
return fmt.Errorf("source network %d not found", tx.SourceNet)
}
if _, exists := m.networks[tx.DestNet]; !exists {
return fmt.Errorf("destination network %d not found", tx.DestNet)
}
// Add to pending transactions
tx.Status = TxStatusPending
tx.Timestamp = time.Now()
m.txCoordinator.mu.Lock()
m.txCoordinator.pendingTxs[tx.ID] = tx
m.txCoordinator.mu.Unlock()
// Send to source network's transaction channel
m.txCoordinator.txChannels[tx.SourceNet] <- tx
m.logger.Info("Submitted cross-chain transaction",
"txID", tx.ID,
"source", tx.SourceNet,
"dest", tx.DestNet,
"amount", tx.Amount,
)
return nil
}
// Shutdown gracefully shuts down all networks
func (m *MultiNetworkManager) Shutdown() error {
m.logger.Info("Shutting down multi-network manager")
// Cancel context to stop all goroutines
m.cancel()
// Stop all networks
m.mu.Lock()
for id, instance := range m.networks {
instance.Status = NetworkStatusStopping
m.logger.Info("Stopping network", "networkID", id)
// Actual network shutdown would happen here
instance.Status = NetworkStatusStopped
}
m.mu.Unlock()
// Close shared database
if err := m.sharedDB.Close(); err != nil {
return fmt.Errorf("failed to close shared database: %w", err)
}
m.logger.Info("Multi-network manager shutdown complete")
return nil
}