Cross-Chain Bridge

ThresholdVM (T-Chain)

T-Chain MPC-as-a-service VM, distributed key generation, signing, and resharing

ThresholdVM (T-Chain)

Specification: LP-6015 MPC Bridge Protocol

Source: github.com/luxfi/node/vms/thresholdvm

The T-Chain (Threshold Chain) provides MPC-as-a-service for all Lux chains, handling distributed key generation, threshold signing, and key management.

Implementation Status

ComponentFileStatus
ThresholdVM Corevm.goComplete
Protocol Registryprotocols.goComplete
Block Structureblock.goComplete
RPC Handlersrpc.goComplete
Codeccodec.goComplete

VM Architecture

// Source: vms/thresholdvm/vm.go

// VM implements the Threshold VM for MPC-as-a-service
type VM struct {
    ctx      *consensusctx.Context
    db       database.Database
    config   ThresholdConfig
    toEngine chan<- common.Message
    log      log.Logger

    // Protocol Registry - supports multiple threshold protocols
    protocolRegistry *ProtocolRegistry

    // LSS MPC Protocol Components (default protocol)
    lssConfig   *lssconfig.Config // LSS config for this party
    partyID     party.ID          // This party's ID
    partyIDs    []party.ID        // All party IDs in the MPC group
    pool        *pool.Pool        // Worker pool for MPC operations
    mpcReady    bool              // Whether MPC is ready for signing

    // Key Management
    keys           map[string]*ManagedKey // KeyID -> Key configuration
    activeKeyID    string                 // Currently active key for signing
    keygenSessions map[string]*KeygenSession

    // Signing Sessions
    signingSessions map[string]*SigningSession
    sessionsByChain map[string][]string // ChainID -> SessionIDs

    // Quota Tracking
    dailySigningCount map[string]uint64
    quotaResetTime    time.Time

    // Block Management
    preferred      ids.ID
    lastAcceptedID ids.ID
    pendingBlocks  map[ids.ID]*Block
    heightIndex    map[uint64]ids.ID

    mu sync.RWMutex
}

Configuration

// Source: vms/thresholdvm/vm.go

// ThresholdConfig contains VM configuration
type ThresholdConfig struct {
    // MPC Configuration
    Threshold    int `json:"threshold"`    // t: Threshold (t+1 parties needed)
    TotalParties int `json:"totalParties"` // n: Total number of MPC nodes

    // Session Configuration
    SessionTimeout      time.Duration `json:"sessionTimeout"`
    MaxActiveSessions   int           `json:"maxActiveSessions"`
    MaxSessionsPerChain int           `json:"maxSessionsPerChain"`

    // Quota Configuration (daily limits)
    DailySigningQuota map[string]uint64 `json:"dailySigningQuota"`

    // Authorized Chains that can request MPC services
    AuthorizedChains map[string]*ChainPermissions `json:"authorizedChains"`

    // Key Management
    KeyRotationPeriod time.Duration `json:"keyRotationPeriod"`
    MaxKeyAge         time.Duration `json:"maxKeyAge"`
}

Chain Permissions

// ChainPermissions defines what a chain can do with MPC services
type ChainPermissions struct {
    ChainID           string   `json:"chainId"`
    ChainName         string   `json:"chainName"`
    CanSign           bool     `json:"canSign"`
    CanKeygen         bool     `json:"canKeygen"`
    CanReshare        bool     `json:"canReshare"`
    AllowedKeyTypes   []string `json:"allowedKeyTypes"`
    MaxSigningSize    int      `json:"maxSigningSize"`
    RequirePreHash    bool     `json:"requirePreHash"`
    DailySigningLimit uint64   `json:"dailySigningLimit"`
}

Default Authorized Chains

ChainSignKeygenReshareKey TypesDaily Limit
B-ChainYesYesYessecp256k1100,000
C-ChainYesNoNosecp256k150,000
P-ChainYesYesYessecp256k1, bls10,000
X-ChainYesNoNosecp256k110,000
Q-ChainYesYesYessecp256k1, dilithium10,000

Key Management

ManagedKey Structure

// Source: vms/thresholdvm/vm.go

// ManagedKey represents a threshold key managed by the T-Chain
type ManagedKey struct {
    KeyID        string            `json:"keyId"`
    KeyType      string            `json:"keyType"`      // secp256k1, ed25519
    PublicKey    []byte            `json:"publicKey"`    // Compressed public key
    Address      []byte            `json:"address"`      // Ethereum-style address
    Threshold    int               `json:"threshold"`    // t value
    TotalParties int               `json:"totalParties"` // n value
    Generation   uint64            `json:"generation"`   // Key generation number
    CreatedAt    time.Time         `json:"createdAt"`
    LastUsedAt   time.Time         `json:"lastUsedAt"`
    SignCount    uint64            `json:"signCount"`
    Status       string            `json:"status"`       // active, rotating, expired
    Config       *lssconfig.Config `json:"-"`            // LSS config (not serialized)
    PartyIDs     []party.ID        `json:"partyIds"`
}

Key Lifecycle

    +----------+
    |  Create  |
    +----+-----+
         |
         v
    +----+-----+     +----------+
    |  Keygen  | --> |  Active  |
    +----+-----+     +----+-----+
                          |
         +----------------+----------------+
         |                |                |
         v                v                v
    +----+-----+     +----+-----+     +----+-----+
    |  Refresh |     |  Reshare |     |  Expire  |
    +----+-----+     +----+-----+     +----+-----+
         |                |                |
         +----------------+                |
                |                          |
                v                          v
           +----+-----+              +----+-----+
           |  Active  |              |  Expired |
           +----------+              +----------+

Protocol Registry

The T-Chain supports multiple threshold protocols:

// Source: vms/thresholdvm/protocols.go

// Protocol identifies a threshold signature protocol
type Protocol string

const (
    ProtocolLSS      Protocol = "lss"      // Lux Signature Scheme
    ProtocolCGGMP21  Protocol = "cggmp21"  // Standard CGGMP21
    ProtocolBLS      Protocol = "bls"      // BLS signatures
    ProtocolRingtail Protocol = "ringtail" // Post-quantum
)

// ProtocolHandler interface for threshold protocols
type ProtocolHandler interface {
    Keygen(ctx context.Context, partyID party.ID, partyIDs []party.ID, threshold int) (KeyShare, error)
    Sign(ctx context.Context, share KeyShare, message []byte, signers []party.ID) (Signature, error)
    Refresh(ctx context.Context, share KeyShare) (KeyShare, error)
    Reshare(ctx context.Context, share KeyShare, newPartyIDs []party.ID, threshold int) (KeyShare, error)
    SupportedCurves() []string
}

// KeyShare interface for protocol-specific key shares
type KeyShare interface {
    PublicKey() []byte
    Generation() uint64
}

// Signature interface for protocol-specific signatures
type Signature interface {
    R() *big.Int
    S() *big.Int
    V() byte
}

Core Operations

Key Generation

// Source: vms/thresholdvm/vm.go

// StartKeygen initiates distributed key generation
func (vm *VM) StartKeygen(keyID, keyType, requestedBy string) (*KeygenSession, error) {
    vm.mu.Lock()
    defer vm.mu.Unlock()

    // Check authorization
    perms, ok := vm.config.AuthorizedChains[requestedBy]
    if !ok || !perms.CanKeygen {
        return nil, ErrUnauthorizedChain
    }

    // Validate key type
    allowed := false
    for _, kt := range perms.AllowedKeyTypes {
        if kt == keyType {
            allowed = true
            break
        }
    }
    if !allowed {
        return nil, fmt.Errorf("key type %s not allowed", keyType)
    }

    // Create session
    session := &KeygenSession{
        SessionID:    ids.GenerateTestID().String(),
        KeyID:        keyID,
        KeyType:      keyType,
        Threshold:    vm.config.Threshold,
        TotalParties: vm.config.TotalParties,
        PartyIDs:     vm.partyIDs,
        Status:       "pending",
        RequestedBy:  requestedBy,
        StartedAt:    time.Now(),
    }

    vm.keygenSessions[session.SessionID] = session

    // Start keygen in background
    go vm.runKeygen(session)

    return session, nil
}

Threshold Signing

// Source: vms/thresholdvm/vm.go

// RequestSignature requests a threshold signature
func (vm *VM) RequestSignature(
    requestingChain string,
    keyID string,
    messageHash []byte,
    messageType string,
) (*SigningSession, error) {
    vm.mu.Lock()
    defer vm.mu.Unlock()

    // Check authorization
    perms, ok := vm.config.AuthorizedChains[requestingChain]
    if !ok || !perms.CanSign {
        return nil, ErrUnauthorizedChain
    }

    // Check message size
    if len(messageHash) > perms.MaxSigningSize {
        return nil, fmt.Errorf("message too large")
    }

    // Check quota
    vm.checkQuotaReset()
    if vm.dailySigningCount[requestingChain] >= perms.DailySigningLimit {
        return nil, ErrQuotaExceeded
    }

    // Get key
    key, ok := vm.keys[keyID]
    if !ok {
        if keyID == "" && vm.activeKeyID != "" {
            key = vm.keys[vm.activeKeyID]
            keyID = vm.activeKeyID
        } else {
            return nil, ErrKeyNotFound
        }
    }

    // Create session
    session := &SigningSession{
        SessionID:       ids.GenerateTestID().String(),
        KeyID:           keyID,
        RequestingChain: requestingChain,
        MessageHash:     messageHash,
        MessageType:     messageType,
        Status:          "pending",
        CreatedAt:       time.Now(),
        ExpiresAt:       time.Now().Add(vm.config.SessionTimeout),
    }

    vm.signingSessions[session.SessionID] = session
    vm.sessionsByChain[requestingChain] = append(
        vm.sessionsByChain[requestingChain], session.SessionID)

    // Start signing in background
    go vm.runSigning(session, key)

    return session, nil
}

Key Resharing

// Source: vms/thresholdvm/vm.go

// ReshareKey triggers key resharing for adding/removing parties
func (vm *VM) ReshareKey(keyID string, newPartyIDs []party.ID, requestedBy string) (*KeygenSession, error) {
    vm.mu.Lock()
    defer vm.mu.Unlock()

    // Check authorization
    perms, ok := vm.config.AuthorizedChains[requestedBy]
    if !ok || !perms.CanReshare {
        return nil, ErrUnauthorizedChain
    }

    key, ok := vm.keys[keyID]
    if !ok {
        return nil, ErrKeyNotFound
    }

    if len(newPartyIDs) < vm.config.Threshold+1 {
        return nil, ErrInsufficientParties
    }

    // Create reshare session
    session := &KeygenSession{
        SessionID:    ids.GenerateTestID().String(),
        KeyID:        keyID,
        KeyType:      key.KeyType,
        Threshold:    vm.config.Threshold,
        TotalParties: len(newPartyIDs),
        PartyIDs:     newPartyIDs,
        Status:       "pending",
        RequestedBy:  requestedBy,
        StartedAt:    time.Now(),
    }

    vm.keygenSessions[session.SessionID] = session

    // Start resharing in background
    go vm.runReshare(session, key)

    return session, nil
}

Key Refresh

// Source: vms/thresholdvm/vm.go

// RefreshKey refreshes key shares without changing public key
func (vm *VM) RefreshKey(keyID string, requestedBy string) (*KeygenSession, error) {
    vm.mu.Lock()
    defer vm.mu.Unlock()

    perms, ok := vm.config.AuthorizedChains[requestedBy]
    if !ok || !perms.CanReshare {
        return nil, ErrUnauthorizedChain
    }

    key, ok := vm.keys[keyID]
    if !ok {
        return nil, ErrKeyNotFound
    }

    session := &KeygenSession{
        SessionID:    ids.GenerateTestID().String(),
        KeyID:        keyID,
        KeyType:      key.KeyType,
        Threshold:    key.Threshold,
        TotalParties: key.TotalParties,
        PartyIDs:     key.PartyIDs,
        Status:       "pending",
        RequestedBy:  requestedBy,
        StartedAt:    time.Now(),
    }

    vm.keygenSessions[session.SessionID] = session

    go vm.runRefresh(session, key)

    return session, nil
}

Cross-Chain Interface

CrossChainAppRequest Handler

// Source: vms/thresholdvm/vm.go

// CrossChainAppRequest handles MPC requests from other chains
func (vm *VM) CrossChainAppRequest(
    ctx context.Context,
    chainID ids.ID,
    requestID uint32,
    deadline time.Time,
    request []byte,
) error {
    var req CrossChainMPCRequest
    if _, err := Codec.Unmarshal(request, &req); err != nil {
        return err
    }

    switch req.Type {
    case "sign":
        session, err := vm.RequestSignature(
            req.RequestingChain,
            req.KeyID,
            req.MessageHash,
            req.MessageType,
        )
        if err != nil {
            return err
        }
        // Response sent when signing completes

    case "keygen":
        _, err := vm.StartKeygen(req.KeyID, req.KeyType, req.RequestingChain)
        if err != nil {
            return err
        }

    case "reshare":
        _, err := vm.ReshareKey(req.KeyID, nil, req.RequestingChain)
        if err != nil {
            return err
        }
    }

    return nil
}

Request/Response Types

// CrossChainMPCRequest is the request format for cross-chain MPC operations
type CrossChainMPCRequest struct {
    Type            string   `json:"type"` // sign, keygen, reshare
    RequestingChain string   `json:"requestingChain"`
    KeyID           string   `json:"keyId"`
    KeyType         string   `json:"keyType,omitempty"`
    MessageHash     []byte   `json:"messageHash,omitempty"`
    MessageType     string   `json:"messageType,omitempty"`
}

Block Structure

Operation Types

// Source: vms/thresholdvm/block.go

const (
    OpTypeKeygen  = "keygen"
    OpTypeSign    = "sign"
    OpTypeReshare = "reshare"
    OpTypeRefresh = "refresh"
)

// Operation represents an MPC operation in a block
type Operation struct {
    Type            string `serialize:"true"`
    SessionID       string `serialize:"true"`
    KeyID           string `serialize:"true"`
    RequestingChain string `serialize:"true"`
    Timestamp       int64  `serialize:"true"`
}

// Block represents a T-Chain block
type Block struct {
    ParentID_      ids.ID       `serialize:"true"`
    ID_            ids.ID       `serialize:"false"`
    BlockHeight    uint64       `serialize:"true"`
    BlockTimestamp int64        `serialize:"true"`
    Operations     []*Operation `serialize:"true"`
    vm             *VM          `serialize:"false"`
}

Statistics and Monitoring

// vmStats tracks internal T-Chain statistics
type vmStats struct {
    TotalSignatures    uint64
    TotalKeygens       uint64
    ActiveSessions     int
    SignaturesByChain  map[string]uint64
    AverageSigningTime time.Duration
    SuccessRate        float64
    mu                 sync.RWMutex
}

Health Check

// HealthCheck implements the common.VM interface
func (vm *VM) HealthCheck(ctx context.Context) (interface{}, error) {
    vm.mu.RLock()
    defer vm.mu.RUnlock()

    return map[string]interface{}{
        "status":         "healthy",
        "mpcReady":       vm.mpcReady,
        "activeKey":      vm.activeKeyID,
        "activeSessions": len(vm.signingSessions),
        "totalKeys":      len(vm.keys),
    }, nil
}

RPC API

threshold_requestSignature

curl -X POST -H "Content-Type: application/json" \
    --data '{
        "jsonrpc": "2.0",
        "method": "threshold_requestSignature",
        "params": {
            "requestingChain": "B-Chain",
            "keyId": "bridge-key-001",
            "messageHash": "0xabc123...",
            "messageType": "eth_sign"
        },
        "id": 1
    }' \
    http://localhost:9650/ext/bc/T/rpc

threshold_getSignature

curl -X POST -H "Content-Type: application/json" \
    --data '{
        "jsonrpc": "2.0",
        "method": "threshold_getSignature",
        "params": {
            "sessionId": "session-123"
        },
        "id": 1
    }' \
    http://localhost:9650/ext/bc/T/rpc

Response:

{
    "jsonrpc": "2.0",
    "result": {
        "sessionId": "session-123",
        "status": "completed",
        "signature": {
            "r": "0x...",
            "s": "0x...",
            "v": 27
        }
    },
    "id": 1
}

threshold_startKeygen

curl -X POST -H "Content-Type: application/json" \
    --data '{
        "jsonrpc": "2.0",
        "method": "threshold_startKeygen",
        "params": {
            "keyId": "new-bridge-key",
            "keyType": "secp256k1",
            "requestingChain": "B-Chain"
        },
        "id": 1
    }' \
    http://localhost:9650/ext/bc/T/rpc

threshold_getPublicKey

curl -X POST -H "Content-Type: application/json" \
    --data '{
        "jsonrpc": "2.0",
        "method": "threshold_getPublicKey",
        "params": {
            "keyId": "bridge-key-001"
        },
        "id": 1
    }' \
    http://localhost:9650/ext/bc/T/rpc

Integration with B-Chain

The T-Chain receives requests from B-Chain via CrossChainAppRequest:

B-Chain                                  T-Chain
+-------------+                         +-------------+
|             |  CrossChainAppRequest   |             |
| BridgeVM    | ----------------------> | ThresholdVM |
|             |  (sign request)         |             |
|             |                         |             |
|             |                         | Run MPC     |
|             |                         | Protocol    |
|             |                         |             |
|             |  CrossChainAppResponse  |             |
|             | <---------------------- |             |
|             |  (signature)            |             |
+-------------+                         +-------------+

Error Handling

var (
    ErrNotInitialized       = errors.New("MPC not initialized")
    ErrKeygenInProgress     = errors.New("keygen already in progress")
    ErrSigningInProgress    = errors.New("signing session already in progress")
    ErrInvalidThreshold     = errors.New("invalid threshold configuration")
    ErrInsufficientParties  = errors.New("insufficient parties for operation")
    ErrSessionNotFound      = errors.New("session not found")
    ErrSessionExpired       = errors.New("session expired")
    ErrUnauthorizedChain    = errors.New("unauthorized chain")
    ErrQuotaExceeded        = errors.New("signing quota exceeded")
    ErrInvalidSignature     = errors.New("invalid signature")
    ErrKeyNotFound          = errors.New("key not found")
)