mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
batchVerifyBlockSignatures had two answers to "is this signature valid", chosen
by accel.Available():
- the CPU loop verified against blockHash[:] (accel_verify.go:69) while the GPU
layout verified against sha256(blockHash[:]) (accel_verify.go:96,107). Same
signature set, two different signed messages, so a node with an accelerator
and a node without reached opposite verdicts on the same block —
bridgevm/block.go:219 turns validSignatures into accept or reject, so this is
an accept/reject split decided by host hardware.
- a signature shorter than 64 bytes, or a public key that would not marshal,
was silently zero-filled (`if len >= sigSize`, `if mErr != nil { continue }`)
while the entry kept its row in the result vector, and validCount was taken
straight from the kernel's output for that row. The CPU path rejects those
entries outright via deserializeSignature. Correctness of the count therefore
rested on an out-of-tree kernel refusing degenerate input, and one
spuriously-true row is enough to admit a block.
The predicate is now verifyBlockSig, and the CPU loop is its only evaluator.
packBlockSigBatch lays out the accelerator's rows over the same message bytes and
returns an error for any row it cannot represent exactly, which sends the batch to
that one authority instead of guessing.
Tests: TestBatchSignsTheBlockHashItself, TestBatchRefusesRowsItCannotRepresent,
TestVerifyBlockSigIsTheOneAuthority, TestBatchVerifyCountsNoSignatureWithoutConfig.
Co-authored-by: Hanzo Dev <[email protected]>