mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
169 lines
4.9 KiB
Go
169 lines
4.9 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package executor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/luxfi/accel"
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/node/vms/platformvm/block"
|
|
"github.com/luxfi/node/vms/platformvm/txs"
|
|
txexecutor "github.com/luxfi/node/vms/platformvm/txs/executor"
|
|
"github.com/luxfi/node/vms/platformvm/warp"
|
|
validators "github.com/luxfi/validators"
|
|
)
|
|
|
|
// VerifyWarpMessages verifies every warp message the block carries. If any of
|
|
// them is invalid, an error is returned.
|
|
//
|
|
// Every transaction means every transaction: the decision txs a block charges
|
|
// for, and — on a proposal block — the tx the chain emitted about itself. A
|
|
// warp message is an assertion about another chain no matter who put it in the
|
|
// block, so which of the two sets it arrived in cannot decide whether its
|
|
// signature is checked.
|
|
//
|
|
// When GPU acceleration is available and the block contains multiple BLS
|
|
// warp signatures, verification is batched through the GPU for throughput.
|
|
// Otherwise falls back to sequential CPU verification.
|
|
func VerifyWarpMessages(
|
|
ctx context.Context,
|
|
networkID uint32,
|
|
validatorState validators.State,
|
|
pChainHeight uint64,
|
|
b block.Block,
|
|
) error {
|
|
blockTxs := b.DecisionTxs()
|
|
if proposal, ok := b.(*block.ProposalBlock); ok {
|
|
blockTxs = append(blockTxs, proposal.Tx())
|
|
}
|
|
|
|
// Collect warp messages that need BLS verification.
|
|
type blsJob struct {
|
|
aggPubKey *bls.PublicKey
|
|
aggSig *bls.Signature
|
|
msgBytes []byte
|
|
txIdx int
|
|
}
|
|
|
|
var blsJobs []blsJob
|
|
|
|
for txIdx, tx := range blockTxs {
|
|
rawMsg, hasWarp := extractWarpMessageBytes(tx.Unsigned)
|
|
if !hasWarp {
|
|
continue
|
|
}
|
|
|
|
msg, err := warp.ParseMessage(rawMsg)
|
|
if err != nil {
|
|
// Return raw error to match sequential path behavior.
|
|
return err
|
|
}
|
|
|
|
// Only BitSetSignature uses BLS that can be batched.
|
|
// Other signature types (Corona, Hybrid) fall through to sequential.
|
|
bss, ok := msg.Signature.(*warp.BitSetSignature)
|
|
if !ok || !accel.Available() {
|
|
// Sequential path for non-BLS signatures or when GPU unavailable.
|
|
if err := txexecutor.VerifyWarpMessages(ctx, networkID, validatorState, pChainHeight, tx.Unsigned); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Verify non-crypto parts (quorum weight) and extract the aggregate key/sig.
|
|
if msg.NetworkID != networkID {
|
|
return fmt.Errorf("tx %d: %w", txIdx, warp.ErrWrongNetworkID)
|
|
}
|
|
|
|
cvs, err := warp.GetCanonicalValidatorSetFromChainID(ctx, validatorState, pChainHeight, msg.SourceChainID)
|
|
if err != nil {
|
|
return fmt.Errorf("tx %d: failed to get validators: %w", txIdx, err)
|
|
}
|
|
|
|
signerIndices := set.BitsFromBytes(bss.Signers)
|
|
if len(signerIndices.Bytes()) != len(bss.Signers) {
|
|
return fmt.Errorf("tx %d: %w", txIdx, warp.ErrInvalidBitSet)
|
|
}
|
|
|
|
signers, err := warp.FilterValidators(signerIndices, cvs.Validators)
|
|
if err != nil {
|
|
return fmt.Errorf("tx %d: %w", txIdx, err)
|
|
}
|
|
|
|
sigWeight, _ := warp.SumWeight(signers)
|
|
if err := warp.VerifyWeight(sigWeight, cvs.TotalWeight, txexecutor.WarpQuorumNumerator, txexecutor.WarpQuorumDenominator); err != nil {
|
|
return fmt.Errorf("tx %d: %w", txIdx, err)
|
|
}
|
|
|
|
aggSig, err := bls.SignatureFromBytes(bss.Signature[:])
|
|
if err != nil {
|
|
return fmt.Errorf("tx %d: %w", txIdx, err)
|
|
}
|
|
|
|
aggPubKey, err := warp.AggregatePublicKeys(signers)
|
|
if err != nil {
|
|
return fmt.Errorf("tx %d: %w", txIdx, err)
|
|
}
|
|
|
|
blsJobs = append(blsJobs, blsJob{
|
|
aggPubKey: aggPubKey,
|
|
aggSig: aggSig,
|
|
msgBytes: msg.UnsignedMessage.Bytes(),
|
|
txIdx: txIdx,
|
|
})
|
|
}
|
|
|
|
if len(blsJobs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Single BLS signature -- no batch benefit, verify directly.
|
|
if len(blsJobs) == 1 {
|
|
j := blsJobs[0]
|
|
if !bls.Verify(j.aggPubKey, j.aggSig, j.msgBytes) {
|
|
return fmt.Errorf("tx %d: %w", j.txIdx, warp.ErrInvalidSignature)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Batch BLS verification via GPU.
|
|
pks := make([]*bls.PublicKey, len(blsJobs))
|
|
msgs := make([][]byte, len(blsJobs))
|
|
sigs := make([]*bls.Signature, len(blsJobs))
|
|
for i, j := range blsJobs {
|
|
pks[i] = j.aggPubKey
|
|
msgs[i] = j.msgBytes
|
|
sigs[i] = j.aggSig
|
|
}
|
|
|
|
results, err := warp.BatchVerifyBLSSignatures(pks, msgs, sigs)
|
|
if err != nil {
|
|
return fmt.Errorf("batch BLS verify: %w", err)
|
|
}
|
|
|
|
for i, valid := range results {
|
|
if !valid {
|
|
return fmt.Errorf("tx %d: %w", blsJobs[i].txIdx, warp.ErrInvalidSignature)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// extractWarpMessageBytes returns the raw warp message bytes from a tx and
|
|
// whether the tx carries a warp message. A true second return value means
|
|
// the tx type requires warp verification (even if the message bytes are nil/empty).
|
|
func extractWarpMessageBytes(unsigned txs.UnsignedTx) ([]byte, bool) {
|
|
switch tx := unsigned.(type) {
|
|
case *txs.RegisterL1ValidatorTx:
|
|
return tx.Message(), true
|
|
case *txs.SetL1ValidatorWeightTx:
|
|
return tx.Message(), true
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|