Files
zeekay ede37547f6 refactor(platformvm): consolidate block code, drop atomic_block, add v0 lift
- Drop atomic_block.go + legacy *_block_test.go (consolidated)
- Add block/v0/ subdir + lift_v0.go for old version handling
- Copyright header: 'Lux Industries, Inc.' → 'Lux Industries Inc' (no comma)
- block_adapter.go: new adapter layer
2026-06-01 15:22:27 -07:00

158 lines
4.4 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"
validators "github.com/luxfi/validators"
"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"
)
// VerifyWarpMessages verifies all warp messages in the block. If any of the
// warp messages are invalid, an error is returned.
//
// 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 {
// 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 b.Txs() {
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
}
}