Files
zeekay 7cf87b230b node-unified is the node
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.
2026-09-07 16:15:25 -07:00

99 lines
2.8 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package executor
import (
"github.com/luxfi/accel"
accelcrypto "github.com/luxfi/accel/ops/crypto"
"github.com/luxfi/crypto/hash"
"github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/node/vms/xvm/txs"
"github.com/luxfi/utxo/secp256k1fx"
)
// batchVerifyBlockSignatures performs GPU-accelerated batch verification of all
// secp256k1 signatures in the block's transactions. When the GPU is unavailable
// or the block has fewer than 2 signatures, this is a no-op and returns nil
// (the sequential verification path handles it).
//
// On success, returns nil indicating all signatures are cryptographically valid.
// The caller still performs semantic checks (address matching, timelocks, etc.)
// through the normal verification path.
func batchVerifyBlockSignatures(blockTxs []*txs.Tx) error {
if !accel.Available() {
return nil
}
// Collect all (hash, sig) pairs across all txs.
type sigEntry struct {
hash []byte
sig []byte
pubkey []byte
}
var entries []sigEntry
for _, tx := range blockTxs {
txBytes := tx.Unsigned.Bytes()
if len(txBytes) == 0 {
continue
}
txHash := hash.ComputeHash256(txBytes)
for _, fxCred := range tx.Creds {
cred, ok := fxCred.Credential.(*secp256k1fx.Credential)
if !ok {
continue
}
for _, sig := range cred.Sigs {
// Recover the public key from the signature (CPU, cached).
// This is the same operation the sequential path does.
pk, err := secp256k1.RecoverPublicKeyFromHash(txHash, sig[:])
if err != nil {
// Recovery failure means invalid sig; let sequential path
// handle the error with proper context.
return nil
}
entries = append(entries, sigEntry{
hash: txHash,
sig: sig[:secp256k1.SignatureLen-1], // r||s (64 bytes, no recovery byte)
pubkey: pk.CompressedBytes(), // 33 bytes compressed secp256k1
})
}
}
}
if len(entries) < 2 {
// Not enough signatures to benefit from batch GPU verification.
return nil
}
// Pack into slices for accelcrypto.BatchVerify.
msgs := make([][]byte, len(entries))
sigs := make([][]byte, len(entries))
pks := make([][]byte, len(entries))
for i, e := range entries {
msgs[i] = e.hash
sigs[i] = e.sig
pks[i] = e.pubkey
}
results, err := accelcrypto.BatchVerify(accelcrypto.SigECDSA, sigs, msgs, pks)
if err != nil {
// GPU error; fall through to sequential CPU verification.
return nil
}
for _, valid := range results {
if !valid {
// At least one signature is invalid. Let the sequential path
// identify which tx/credential is bad and return proper error.
return nil
}
}
// All signatures verified. Sequential path still runs for semantic
// checks but signature recovery will hit cache (fast).
return nil
}