Files
chains/bridgevm
zeekayandHanzo Dev bcf93b97db bridgevm: one acceptance predicate for a block MPC signature
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]>
2026-07-29 16:48:10 -07:00
..