Files
chains/quantumvm/vm.go
T
zeekayandHanzo Dev 9ec41cf150 Native-ZAP struct-is-wire for the four straggler VMs (aivm/keyvm/quantumvm/dexvm)
Completes the "one and only one way" wire migration: every VM in this repo now
serializes blocks and txs through luxfi/zap struct-is-wire at fixed field
offsets — no encoding/json, no hand-rolled big-endian, no cursor codec.

- aivm: Block + AIVertex + nested CIntent native-encoded. Fixes a latent
  data-loss bug — AIVertex had unexported fields, so encoding/json silently
  marshalled it as {}; the native encoder round-trips every field.
- keyvm: Transaction (signing preimage = SigningBytes(), excludes Auth/Sig;
  full wire = signing || sig object, so the signed bytes stay a genuine prefix
  and authenticate() re-derives a byte-identical preimage) + Block.
- quantumvm: Block + BaseTransaction. Block wire is the quantum-signature
  preimage (deterministic fixed offsets, ordered tx list).
- dexvm: block envelope (txs were already ZAP). Preserves UnixNano timestamps,
  deterministic tx order, and content-addressed carried fills.

Hardening from the adversarial crypto review (must-holds all verified: no
keyvm signature malleability/replay, no aivm CIntent id-forgery/escrow tamper,
no dexvm ordering/fill tamper, all parsers fail closed):

- keyvm (M1): make the wire strictly canonical. zap follows the root offset
  and ignores unreferenced padding inside a message's declared size, so a twin
  buffer could decode to identical fields with a different hash (id-malleability).
  ParseTransaction now binds the id to the re-serialized (canonical) form and
  rejects any non-canonical input at the door — exactly one byte-string
  authenticates per logical tx. Regression test: the padded twin is rejected.
- quantumvm (M2): derive block + tx ids as sha256(Bytes()), matching the other
  three VMs. The prior ids.ToID(parent||height) over 40 bytes (and ids.ToID over
  the >=32-byte tx wire) silently yielded ids.Empty for every block/tx,
  collapsing the block store and tx pool to a single slot. The block id is no
  longer stored in the wire, so the invariant id == sha256(Bytes()) holds
  unconditionally. Regression tests: ids are non-Empty and collision-distinct.

Not in scope (tracked separately, fail-closed today): aivm RewardPerOperator is
escrowed but not bound by ComputeIntentID — a pre-existing design gap that needs
a coordinated A-side + C-side intent-id preimage bump before any non-fail-closed
CCommitVerifier ships.

Builds + tests green under the canonical CGO=0 -mod=readonly recipe; go.mod/go.sum
are the go mod tidy canonical set (indirect deps resolved forward, all within
major). Cryptographer verdict: SAFE TO PUBLISH.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-16 13:44:24 -07:00

736 lines
21 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package quantumvm
import (
"context"
"encoding/binary"
"errors"
"fmt"
"net/http"
"sync"
"github.com/gorilla/rpc/v2"
"github.com/luxfi/chains/quantumvm/config"
"github.com/luxfi/chains/quantumvm/quantum"
consensuschain "github.com/luxfi/consensus/engine/chain"
"github.com/luxfi/consensus/protocol/quasar"
"github.com/luxfi/database"
"github.com/luxfi/database/versiondb"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/metric"
"github.com/luxfi/node/cache"
"github.com/luxfi/node/utils/json"
"github.com/luxfi/node/vms/types/fee"
"github.com/luxfi/timer/mockable"
"github.com/luxfi/version"
luxvm "github.com/luxfi/vm"
"github.com/luxfi/vm/chain"
)
const (
// Version of the QVM
Version = "1.0.0"
// MaxParallelVerifications is the maximum number of parallel verifications
MaxParallelVerifications = 100
// DefaultBatchSize is the default batch size for parallel processing
DefaultBatchSize = 10
)
var (
errNotImplemented = errors.New("not implemented")
errNoPendingTxs = errors.New("no pending transactions")
errVMShutdown = errors.New("VM is shutting down")
errInvalidQuantumStamp = errors.New("invalid quantum stamp")
errParallelProcessingFailed = errors.New("parallel transaction processing failed")
// Compile-time check that *VM satisfies chain.ChainVM (= block.ChainVM)
// AND the consensus engine's BlockBuilder. Together these prove Q-Chain
// takes the LINEAR ⅔-stake cert path in the chain manager (buildChain →
// consensuschain.NewRuntime), not the DAG path. Do NOT add a
// GetEngine() consensusdag.Engine method: that would route the manager's
// type switch back to createDAG and bypass the certificate.
_ chain.ChainVM = (*VM)(nil)
_ consensuschain.BlockBuilder = (*VM)(nil)
)
// BCLookup provides blockchain alias lookup
type BCLookup interface {
Lookup(string) (ids.ID, error)
PrimaryAlias(ids.ID) (string, error)
}
// SharedMemory provides cross-chain shared memory
type SharedMemory interface {
Get(peerChainID ids.ID, keys [][]byte) ([][]byte, error)
Apply(map[ids.ID]interface{}, ...interface{}) error
}
// VM implements the Q-chain Virtual Machine with quantum features
type VM struct {
config.Config
// Core components
// consensusRuntime *runtime.Runtime
log log.Logger
db database.Database
versiondb *versiondb.Database
blockchainID ids.ID
ChainAlias string
NetworkID uint32
// Quantum components
quantumSigner *quantum.QuantumSigner
quantumCache *cache.LRU[ids.ID, *quantum.QuantumSignature]
// Hybrid P/Q consensus bridge (connects P-Chain BLS + Q-Chain Corona)
// Uses Quasar consensus for dual BLS+Corona threshold signatures
quasarBridge *QuasarBridge
// Consensus and validation
// validators validators.Manager
// versionManager consensusversion.Manager
// consensusEngine consensus.Consensus
// Metrics and monitoring
metrics metric.Registry
// State management
state database.Database
shuttingDown bool
shuttingDownMu sync.RWMutex
// Transaction processing
txPool *TransactionPool
parallelWorkers int
workerPool *sync.Pool
// Clock and timing
clock mockable.Clock
// Network communication
bcLookup BCLookup
sharedMemory SharedMemory
// HTTP service
httpServer *http.Server
rpcServer *rpc.Server
// Fee policy gating user-submitted tx admission (IssueTx).
// FlatPolicy at MinTxFeeFloor on Q-Chain; consensus-internal
// callers bypass via txPool.AddTransaction.
feePolicy fee.Policy
// Synchronization
lock sync.RWMutex
}
// Initialize initializes the VM. Implements chain.ChainVM.
func (vm *VM) Initialize(ctx context.Context, init luxvm.Init) error {
vm.lock.Lock()
defer vm.lock.Unlock()
_ = ctx
vm.db = init.DB
if init.Log != nil {
vm.log = init.Log
}
genesisBytes := init.Genesis
// vm.blockchainID = chainRuntime.ChainID
// vm.NetworkID = chainRuntime.NetworkID
// Initialize logger
// if vm.log.IsZero() {
// vm.log = chainRuntime.Log
// }
// vm.log.Info("initializing QVM",
// "version", Version,
// "chainID", vm.blockchainID,
// "networkID", vm.NetworkID,
// )
// Initialize quantum components
vm.quantumSigner = quantum.NewQuantumSigner(
vm.log,
vm.Config.QuantumAlgorithmVersion,
vm.Config.CoronaKeySize,
vm.Config.QuantumStampWindow,
vm.Config.QuantumSigCacheSize,
)
// Initialize transaction pool
vm.txPool = NewTransactionPool(
vm.Config.MaxParallelTxs,
vm.Config.ParallelBatchSize,
vm.log,
)
// Pin fee policy. Q-Chain accepts user-submitted txs through
// IssueTx so attach the canonical FlatPolicy at MinTxFeeFloor;
// fee.Validate refuses zero-fee user-facing chains at boot.
if init.Runtime != nil {
vm.NetworkID = init.Runtime.NetworkID
}
vm.feePolicy = newFeePolicy(vm.NetworkID)
if err := fee.Validate(vm.feePolicy); err != nil {
return fmt.Errorf("quantumvm: fee policy: %w", err)
}
// Set up worker pool for parallel processing
vm.parallelWorkers = vm.Config.MaxParallelTxs
if vm.parallelWorkers <= 0 {
vm.parallelWorkers = MaxParallelVerifications
}
vm.workerPool = &sync.Pool{
New: func() interface{} {
return &TransactionWorker{
vm: vm,
quantumSigner: vm.quantumSigner,
}
},
}
// Initialize version database
vm.versiondb = versiondb.New(vm.db)
// Metrics are not yet available via chainRuntime; the quantumvm
// operates without per-chain metrics until runtime exposes them.
// Parse genesis if provided
if len(genesisBytes) > 0 {
if err := vm.parseGenesis(genesisBytes); err != nil {
return fmt.Errorf("failed to parse genesis: %w", err)
}
}
// Initialize state
vm.state = vm.versiondb
// Set up HTTP handlers
if err := vm.initializeHTTPHandlers(); err != nil {
return fmt.Errorf("failed to initialize HTTP handlers: %w", err)
}
// Initialize Quasar hybrid consensus bridge (BLS + Corona)
quasarCfg := QuasarBridgeConfig{
ValidatorID: vm.blockchainID.String(),
Threshold: 0, // Will be set to 2/3+1 based on total nodes
TotalNodes: 3, // Default 3-node network, can be updated
Logger: vm.log,
}
quasarBridge, err := NewQuasarBridge(quasarCfg, vm.quantumSigner)
if err != nil {
return fmt.Errorf("failed to initialize Quasar bridge: %w", err)
}
vm.quasarBridge = quasarBridge
vm.log.Info("═══════════════════════════════════════════════════════════════════")
vm.log.Info("║ QVM INITIALIZED with Quasar PQ-BFT Consensus ║")
vm.log.Info("───────────────────────────────────────────────────────────────────")
vm.log.Info("║ Quantum Signatures: ML-DSA (NIST PQC)", log.Bool("enabled", vm.Config.QuantumStampEnabled))
vm.log.Info("║ Corona Threshold: Ring-LWE PQ", log.Bool("enabled", vm.Config.CoronaEnabled))
vm.log.Info("║ BLS Threshold: Classical fast path", log.Bool("enabled", true))
vm.log.Info("║ Quasar Hybrid: BLS + Corona dual signing", log.Bool("enabled", true))
vm.log.Info("║ Parallel TX Processing:", log.Int("maxParallel", vm.Config.MaxParallelTxs))
vm.log.Info("═══════════════════════════════════════════════════════════════════")
return nil
}
// BuildBlock builds a new block with pending transactions. Implements chain.ChainVM.
func (vm *VM) BuildBlock(ctx context.Context) (chain.Block, error) {
vm.lock.Lock()
defer vm.lock.Unlock()
// Check if VM is shutting down
if vm.isShuttingDown() {
return nil, errVMShutdown
}
// Get pending transactions from pool
pendingTxs := vm.txPool.GetPendingTransactions(vm.Config.ParallelBatchSize)
if len(pendingTxs) == 0 {
return nil, errNoPendingTxs
}
// Process transactions in parallel
validTxs, err := vm.processTransactionsParallel(pendingTxs)
if err != nil {
return nil, fmt.Errorf("failed to process transactions: %w", err)
}
// Create new block with valid transactions. The id is the content hash of
// the canonical wire (computeID = sha256(Bytes())); it is derived AFTER the
// structural fields are set, and the wire does not carry the id, so
// Bytes()/id are independent of ordering.
block := &Block{
timestamp: vm.clock.Time(),
height: vm.getHeight() + 1,
parentID: vm.getLastAcceptedID(),
transactions: validTxs,
vm: vm,
}
block.id = block.computeID()
// Sign block with quantum signature if enabled
if vm.Config.QuantumStampEnabled {
if err := vm.signBlockWithQuantum(block); err != nil {
return nil, fmt.Errorf("failed to sign block with quantum stamp: %w", err)
}
}
vm.log.Debug("built block",
"blockID", block.ID(),
"height", block.Height(),
"txCount", len(validTxs),
)
return block, nil
}
// ParseBlock parses a block from bytes. Implements chain.ChainVM.
func (vm *VM) ParseBlock(ctx context.Context, blockBytes []byte) (chain.Block, error) {
vm.lock.RLock()
defer vm.lock.RUnlock()
block, err := vm.parseBlock(blockBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse block: %w", err)
}
// Verify quantum signature if enabled
if vm.Config.QuantumStampEnabled {
if err := vm.verifyBlockQuantumSignature(block); err != nil {
return nil, fmt.Errorf("quantum signature verification failed: %w", err)
}
}
return block, nil
}
// GetBlock retrieves a block by its ID. Implements chain.ChainVM.
func (vm *VM) GetBlock(ctx context.Context, blockID ids.ID) (chain.Block, error) {
vm.lock.RLock()
defer vm.lock.RUnlock()
blockBytes, err := vm.state.Get(blockID[:])
if err != nil {
return nil, fmt.Errorf("failed to get block %s: %w", blockID, err)
}
return vm.parseBlock(blockBytes)
}
// SetState sets the VM state. Implements chain.ChainVM.
func (vm *VM) SetState(ctx context.Context, state uint32) error {
vm.lock.Lock()
defer vm.lock.Unlock()
// Q-Chain uses quantum state management - log state transitions generically
vm.log.Info("QVM state transition", "state", state)
return nil
}
// Shutdown gracefully shuts down the VM
func (vm *VM) Shutdown(ctx context.Context) error {
vm.shuttingDownMu.Lock()
vm.shuttingDown = true
vm.shuttingDownMu.Unlock()
vm.log.Info("shutting down QVM")
// Stop HTTP server
if vm.httpServer != nil {
if err := vm.httpServer.Shutdown(ctx); err != nil {
vm.log.Error("failed to shutdown HTTP server", "error", err)
}
}
// Close transaction pool
if vm.txPool != nil {
vm.txPool.Close()
}
// Close database
if vm.versiondb != nil {
if err := vm.versiondb.Close(); err != nil {
vm.log.Error("failed to close versiondb", "error", err)
}
}
vm.log.Info("QVM shutdown complete")
return nil
}
// processTransactionsParallel processes transactions in parallel batches
func (vm *VM) processTransactionsParallel(txs []Transaction) ([]Transaction, error) {
if len(txs) == 0 {
return nil, nil
}
// Determine batch size
batchSize := vm.Config.ParallelBatchSize
if batchSize <= 0 {
batchSize = DefaultBatchSize
}
var validTxs []Transaction
var mu sync.Mutex
var wg sync.WaitGroup
// Process in batches
for i := 0; i < len(txs); i += batchSize {
end := i + batchSize
if end > len(txs) {
end = len(txs)
}
batch := txs[i:end]
wg.Add(1)
go func(batch []Transaction) {
defer wg.Done()
// Get worker from pool
worker := vm.workerPool.Get().(*TransactionWorker)
defer vm.workerPool.Put(worker)
// Process batch
validBatch, err := worker.ProcessBatch(batch)
if err != nil {
vm.log.Error("batch processing failed", "error", err)
return
}
// Add valid transactions
mu.Lock()
validTxs = append(validTxs, validBatch...)
mu.Unlock()
}(batch)
}
wg.Wait()
if len(validTxs) == 0 {
return nil, errParallelProcessingFailed
}
return validTxs, nil
}
// signBlockWithQuantum signs a block with quantum signature using Quasar hybrid consensus
func (vm *VM) signBlockWithQuantum(block *Block) error {
ctx := context.Background()
blockData := block.Bytes()
// Use Quasar bridge for dual BLS+Corona threshold signing
if vm.quasarBridge != nil {
_, err := vm.quasarBridge.SignBlock(ctx, block.ID(), blockData, block.Height())
if err != nil {
vm.log.Warn("Quasar signing failed, falling back to ML-DSA", "error", err)
} else {
vm.log.Debug("Block signed with Quasar BLS threshold",
"blockID", block.ID(),
"height", block.Height(),
)
}
}
// Also sign with ML-DSA for quantum resistance (standalone signature)
key, err := vm.quantumSigner.GenerateCoronaKey()
if err != nil {
return fmt.Errorf("failed to generate corona key: %w", err)
}
sig, err := vm.quantumSigner.Sign(blockData, key)
if err != nil {
return fmt.Errorf("failed to sign block with ML-DSA: %w", err)
}
block.quantumSignature = sig
return nil
}
// verifyBlockQuantumSignature verifies a block's quantum signature
func (vm *VM) verifyBlockQuantumSignature(block *Block) error {
if block.quantumSignature == nil {
return errInvalidQuantumStamp
}
blockData := block.Bytes()
return vm.quantumSigner.Verify(blockData, block.quantumSignature)
}
// parseGenesis parses genesis data
func (vm *VM) parseGenesis(genesisBytes []byte) error {
// Genesis parsing for quantumvm is a no-op; initial state is derived
// from the quantum signer configuration and the empty state trie.
vm.log.Info("genesis loaded", "size", len(genesisBytes))
return nil
}
// parseBlock parses a block from its ZAP wire (see wire.go). The transaction set
// is not reconstructed — the concrete Transaction is an interface with no
// on-chain deserializer; the raw wire is retained for signature re-verification.
func (vm *VM) parseBlock(blockBytes []byte) (*Block, error) {
return parseBlockBytes(vm, blockBytes)
}
// initializeHTTPHandlers sets up HTTP handlers
func (vm *VM) initializeHTTPHandlers() error {
vm.rpcServer = rpc.NewServer()
// Register QVM service
service := &Service{vm: vm}
vm.rpcServer.RegisterCodec(json.NewCodec(), "application/json")
vm.rpcServer.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
return vm.rpcServer.RegisterService(service, "quantumvm")
}
// isShuttingDown returns true if VM is shutting down
func (vm *VM) isShuttingDown() bool {
vm.shuttingDownMu.RLock()
defer vm.shuttingDownMu.RUnlock()
return vm.shuttingDown
}
// getHeight returns current blockchain height
func (vm *VM) getHeight() uint64 {
heightBytes, err := vm.state.Get([]byte("height"))
if err != nil {
return 0
}
if len(heightBytes) != 8 {
return 0
}
return binary.BigEndian.Uint64(heightBytes)
}
// getLastAcceptedID returns the last accepted block ID
func (vm *VM) getLastAcceptedID() ids.ID {
lastAcceptedBytes, err := vm.state.Get([]byte("lastAccepted"))
if err != nil {
return ids.Empty
}
if len(lastAcceptedBytes) != 32 {
return ids.Empty
}
var id ids.ID
copy(id[:], lastAcceptedBytes)
return id
}
// Version returns the version of the VM
func (vm *VM) Version(ctx context.Context) (string, error) {
return Version, nil
}
// Connected notifies the VM that a validator has connected
func (vm *VM) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion *version.Application) error {
vm.log.Debug("node connected", "nodeID", nodeID, "version", nodeVersion)
return nil
}
// Disconnected notifies the VM that a validator has disconnected
func (vm *VM) Disconnected(ctx context.Context, nodeID ids.NodeID) error {
vm.log.Debug("node disconnected", "nodeID", nodeID)
return nil
}
// HealthCheck returns the health status of the VM
func (vm *VM) HealthCheck(ctx context.Context) (chain.HealthResult, error) {
vm.lock.RLock()
defer vm.lock.RUnlock()
return chain.HealthResult{
Healthy: !vm.isShuttingDown(),
Details: map[string]string{
"version": Version,
"quantumEnabled": fmt.Sprintf("%v", vm.Config.QuantumStampEnabled),
"coronaEnabled": fmt.Sprintf("%v", vm.Config.CoronaEnabled),
"pendingTxs": fmt.Sprintf("%d", vm.txPool.PendingCount()),
},
}, nil
}
// CreateHandlers returns HTTP handlers for the VM
func (vm *VM) CreateHandlers(ctx context.Context) (map[string]http.Handler, error) {
handlers := map[string]http.Handler{
"/rpc": vm.rpcServer,
}
return handlers, nil
}
// CreateStaticHandlers returns static HTTP handlers
func (vm *VM) CreateStaticHandlers(ctx context.Context) (map[string]http.Handler, error) {
return nil, nil
}
// NewHTTPHandler returns the VM's HTTP handler. Implements chain.ChainVM.
func (vm *VM) NewHTTPHandler(ctx context.Context) (http.Handler, error) {
handlers, err := vm.CreateHandlers(ctx)
if err != nil {
return nil, err
}
mux := http.NewServeMux()
for path, h := range handlers {
if path == "" {
path = "/"
}
mux.Handle(path, h)
}
return mux, nil
}
// SetPreference sets the preferred block. Implements chain.ChainVM.
// Q-Chain uses BLS+Corona threshold finality rather than preference,
// so this is a no-op until preference-based fork choice is wired in.
func (vm *VM) SetPreference(ctx context.Context, blockID ids.ID) error {
return nil
}
// LastAccepted returns the last accepted block ID. Implements chain.ChainVM.
func (vm *VM) LastAccepted(ctx context.Context) (ids.ID, error) {
vm.lock.RLock()
defer vm.lock.RUnlock()
return vm.getLastAcceptedID(), nil
}
// GetBlockIDAtHeight returns the block ID at the given height. Implements chain.ChainVM.
// Q-Chain does not yet maintain a height index, so this returns errNotImplemented
// until indexer integration lands.
func (vm *VM) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error) {
return ids.Empty, errNotImplemented
}
// WaitForEvent blocks until an event triggers block building. Implements chain.ChainVM.
// CRITICAL: must block on ctx.Done() to avoid the notification flood loop in
// node/chains/manager.go (matches the relayvm contract).
func (vm *VM) WaitForEvent(ctx context.Context) (luxvm.Message, error) {
<-ctx.Done()
return luxvm.Message{}, ctx.Err()
}
// GetQuasarBridge returns the Quasar hybrid consensus bridge
// This provides BLS + Corona dual threshold signatures for PQ finality
func (vm *VM) GetQuasarBridge() *QuasarBridge {
vm.lock.RLock()
defer vm.lock.RUnlock()
return vm.quasarBridge
}
// GetHybridBridge returns the hybrid finality bridge for P/Q chain consensus
// This connects P-Chain BLS signatures with Q-Chain Corona for quantum finality
// Deprecated: Use GetQuasarBridge() for proper type safety
func (vm *VM) GetHybridBridge() interface{} {
return vm.GetQuasarBridge()
}
// SetHybridBridge sets the hybrid finality bridge (called by chain manager)
// Deprecated: Bridge is now auto-initialized in VM.Initialize()
func (vm *VM) SetHybridBridge(bridge interface{}) {
vm.lock.Lock()
defer vm.lock.Unlock()
if qb, ok := bridge.(*QuasarBridge); ok {
vm.quasarBridge = qb
}
}
// StampBlock implements QChainStamper interface for hybrid finality
// Uses Quasar BLS+Corona for dual post-quantum threshold signatures
func (vm *VM) StampBlock(blockID interface{}, pChainHeight uint64, message []byte) (interface{}, error) {
vm.lock.RLock()
defer vm.lock.RUnlock()
ctx := context.Background()
// Convert blockID to ids.ID if possible
var blkID ids.ID
switch v := blockID.(type) {
case ids.ID:
blkID = v
case string:
parsed, err := ids.FromString(v)
if err == nil {
blkID = parsed
}
}
// Use Quasar bridge for BLS threshold signature if available
if vm.quasarBridge != nil && blkID != ids.Empty {
hybridSig, err := vm.quasarBridge.SignBlock(ctx, blkID, message, pChainHeight)
if err != nil {
vm.log.Warn("Quasar BLS stamp failed, using ML-DSA fallback", "error", err)
} else {
vm.log.Info("Quasar BLS stamp created",
"blockID", blkID,
"pChainHeight", pChainHeight,
"threshold", vm.quasarBridge.GetThreshold(),
)
// Return hybrid signature for BLS finality
return hybridSig, nil
}
}
// Fallback: Generate quantum stamp using ML-DSA signer
key, err := vm.quantumSigner.GenerateCoronaKey()
if err != nil {
return nil, fmt.Errorf("failed to generate key for stamp: %w", err)
}
sig, err := vm.quantumSigner.Sign(message, key)
if err != nil {
return nil, fmt.Errorf("failed to create quantum stamp: %w", err)
}
vm.log.Debug("ML-DSA quantum stamp created",
"pChainHeight", pChainHeight,
"sigLen", len(sig.Signature),
)
return sig, nil
}
// VerifyStamp implements QChainStamper interface for quasar finality
// Supports both Quasar QuasarSignature and ML-DSA QuantumSignature
func (vm *VM) VerifyStamp(stamp interface{}) error {
switch s := stamp.(type) {
case *quasar.QuasarSignature:
// Quasar BLS + Corona threshold signature
if s.BLS == nil || len(s.BLS.Signature) == 0 {
return errors.New("invalid Quasar BLS signature")
}
vm.log.Debug("Verified Quasar stamp",
"validatorID", s.BLS.ValidatorID,
"threshold", s.BLS.IsThreshold,
)
return nil
case *quasar.AggregatedSignature:
// Aggregated threshold signature
if len(s.BLSAggregated) == 0 || s.SignerCount < vm.quasarBridge.GetThreshold() {
return errors.New("insufficient aggregated signature")
}
vm.log.Debug("Verified aggregated Quasar stamp",
"signerCount", s.SignerCount,
"threshold", s.IsThreshold,
)
return nil
case *quantum.QuantumSignature:
// ML-DSA quantum signature
if len(s.Signature) == 0 || len(s.QuantumStamp) == 0 {
return errors.New("invalid quantum stamp structure")
}
return nil
default:
return errors.New("unsupported stamp type")
}
}