Files
chains/thresholdvm/fhe/handler.go
T
zeekay 3e8821a3d5 decomplect(warp): one warp (luxfi/warp v1.23.0) + one signed-message helper across bridgevm/zkvm/thresholdvm; deps evm v1.99.48 + precompile v0.16.0
Standardize all of chains on github.com/luxfi/warp v1.23.0 (the standalone
ZAP-native warp that precompile/evm/bridgevm already use). zkvm/fhe and
thresholdvm/fhe are migrated OFF github.com/luxfi/node/vms/platformvm/warp;
there are now ZERO platformvm/warp imports in chains.

The build->sign->wrap sequence (previously copy-pasted in three places and
split across two warp libraries) now lives in exactly one place:
internal/warpmsg.BuildSigned(signer, networkID, sourceChainID, payload)
-> (*warp.Envelope, error). It builds a content Message (NewMessage), signs
it over the Beam domain with the node BLS key, and wraps the signature at
validator bit index 0 into a single-signer Envelope. All three send sites
call it:
  - bridgevm/vm.go        reshare-request gossip
  - zkvm/fhe/coprocessor   FHE task-result callback
  - thresholdvm/fhe/relayer decryption fulfillment

v1.23.0 split the signature from the Message: the signed/transmittable unit
is now *warp.Envelope (Message + Beam BitSetSignature + PQ lanes), not a
combined Message. The receive path moves with it: onMessage callbacks and
WarpHandler.HandleMessage now carry *warp.Envelope and read authenticated
content from env.Message. MPC/BLS single-signer semantics are preserved
exactly (bit-0 signer, Beam-domain BLS, receiver aggregates/verifies the
BitSetSignature against the canonical validator set via warp.VerifyEnvelope).

CGO_ENABLED=0 build/vet clean; bridgevm/zkvm/thresholdvm test suites green.
2026-06-27 21:49:08 -07:00

200 lines
5.3 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package fhe
import (
"context"
"encoding/binary"
"errors"
"fmt"
"sync"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/warp"
)
const (
// Function selectors (first 4 bytes of keccak256 hash)
SelectorRequestDecryption = 0x5a6d3af9 // requestDecryption(bytes32,uint8)
SelectorRequestDecryptionCallback = 0x7b8c4d12 // requestDecryptionWithCallback(bytes32,uint8,address,bytes4)
)
var (
ErrInvalidSelector = errors.New("invalid function selector")
ErrInvalidPayload = errors.New("invalid payload format")
)
// WarpHandler processes FHE-related Warp messages from C-Chain
type WarpHandler struct {
logger log.Logger
relayer *Relayer
}
// NewWarpHandler creates a new Warp message handler for FHE decryption
func NewWarpHandler(logger log.Logger, relayer *Relayer) *WarpHandler {
return &WarpHandler{
logger: logger,
relayer: relayer,
}
}
// HandleMessage processes an incoming signed Warp envelope for FHE decryption.
// The envelope's Beam BitSetSignature is aggregated and verified against the
// canonical validator set by the node warp backend before delivery; this
// handler reads the authenticated content from env.Message.
func (h *WarpHandler) HandleMessage(ctx context.Context, env *warp.Envelope) error {
if env == nil {
return errors.New("nil message")
}
msg := &env.Message
payload := msg.Payload
if len(payload) < 4 {
return ErrInvalidPayload
}
// Extract function selector
selector := binary.BigEndian.Uint32(payload[:4])
switch selector {
case SelectorRequestDecryption:
return h.handleDecryptionRequest(ctx, msg, payload[4:])
case SelectorRequestDecryptionCallback:
return h.handleDecryptionWithCallback(ctx, msg, payload[4:])
default:
h.logger.Debug("Unknown FHE selector",
"selector", fmt.Sprintf("0x%08x", selector),
)
return ErrInvalidSelector
}
}
// handleDecryptionRequest handles a simple decryption request
func (h *WarpHandler) handleDecryptionRequest(ctx context.Context, msg *warp.Message, data []byte) error {
// Parse: ciphertextHash (32 bytes) + decryptionType (1 byte)
if len(data) < 33 {
return ErrInvalidPayload
}
ciphertextHash := common.BytesToHash(data[:32])
decryptionType := data[32]
h.logger.Info("Received decryption request",
"ciphertextHash", ciphertextHash.Hex(),
"type", decryptionType,
"sourceChain", msg.SourceChainID,
)
// Create decryption request
req := &DecryptionRequest{
RequestID: ciphertextHash,
CiphertextHash: ciphertextHash,
DecryptionType: decryptionType,
SourceChainID: msg.SourceChainID,
}
return h.relayer.SubmitRequest(ctx, req)
}
// handleDecryptionWithCallback handles a decryption request with callback
func (h *WarpHandler) handleDecryptionWithCallback(ctx context.Context, msg *warp.Message, data []byte) error {
// Parse: ciphertextHash (32 bytes) + decryptionType (1 byte) + callbackAddress (20 bytes) + callbackSelector (4 bytes)
if len(data) < 57 {
return ErrInvalidPayload
}
ciphertextHash := common.BytesToHash(data[:32])
decryptionType := data[32]
callbackAddress := common.BytesToAddress(data[33:53])
callbackSelector := binary.BigEndian.Uint32(data[53:57])
h.logger.Info("Received decryption request with callback",
"ciphertextHash", ciphertextHash.Hex(),
"type", decryptionType,
"callback", callbackAddress.Hex(),
"selector", fmt.Sprintf("0x%08x", callbackSelector),
"sourceChain", msg.SourceChainID,
)
req := &DecryptionRequest{
RequestID: ciphertextHash,
CiphertextHash: ciphertextHash,
DecryptionType: decryptionType,
SourceChainID: msg.SourceChainID,
CallbackAddress: callbackAddress,
CallbackSelector: callbackSelector,
HasCallback: true,
}
return h.relayer.SubmitRequest(ctx, req)
}
// FHEDecryptionService manages FHE decryption operations
type FHEDecryptionService struct {
logger log.Logger
// Active handlers
handlers map[ids.ID]*WarpHandler
handlersMu sync.RWMutex
// Service state
running bool
cancel context.CancelFunc
}
// NewFHEDecryptionService creates a new FHE decryption service
func NewFHEDecryptionService(logger log.Logger) *FHEDecryptionService {
return &FHEDecryptionService{
logger: logger,
handlers: make(map[ids.ID]*WarpHandler),
}
}
// Start begins the FHE decryption service
func (s *FHEDecryptionService) Start(ctx context.Context) error {
if s.running {
return errors.New("service already running")
}
_, cancel := context.WithCancel(ctx)
s.cancel = cancel
s.running = true
s.logger.Info("FHE Decryption Service started")
return nil
}
// Stop shuts down the FHE decryption service
func (s *FHEDecryptionService) Stop() error {
if !s.running {
return nil
}
if s.cancel != nil {
s.cancel()
}
s.running = false
s.logger.Info("FHE Decryption Service stopped")
return nil
}
// RegisterHandler registers a Warp handler for a specific chain
func (s *FHEDecryptionService) RegisterHandler(chainID ids.ID, handler *WarpHandler) {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
s.handlers[chainID] = handler
}
// GetHandler returns the handler for a specific chain
func (s *FHEDecryptionService) GetHandler(chainID ids.ID) (*WarpHandler, bool) {
s.handlersMu.RLock()
defer s.handlersMu.RUnlock()
handler, ok := s.handlers[chainID]
return handler, ok
}