mirror of
https://github.com/luxfi/netrunner.git
synced 2026-07-27 00:04:23 +00:00
475 lines
12 KiB
Go
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
|
|
}
|