Consensus Finality Signed Message and Quorum Certificate
Abstract
This LP specifies the exact bytes a validator signs to attest finality on a Lux
chain, and the byte layout of the quorum certificate that aggregates those
signatures. It is the authoritative, implementation-independent description of
the LUX/chain/vote/v2 signed message and the QuorumCert wire format. An
independent node built to this document produces byte-identical signed messages
and interoperable certificates.
Motivation
The signed finality message is the object a validator's key commits to when it votes a block final. Forging it is forging finality. It was defined only in the comments and vectors of one implementation, so any second implementation would have had to reverse-engineer it, and a Final LP (LP-182) described a different, signature-incompatible certificate that the shipped code never produced. This LP records the format that actually ships, taken from the code, so the spec and the signed bytes cannot disagree.
The certificate bytes are a frozen external contract: bridges, the DEX settlement receipt, and light clients consume them (LP-305 §7.6). A change to either layout below is a coordinated protocol change across every implementation, never an edit on one side.
Specification
The signed message: LUX/chain/vote/v2
A vote signs, and a verifier recomputes, exactly these bytes, in order, big-endian:
| field | width | notes |
|---|---|---|
| domain tag | 18 | the ASCII LUX/chain/vote/v2 followed by one NUL byte |
| version | 2 | QuorumCertVersion |
| qc_type | 1 | QCFinality |
| chain_id | 32 | |
| height | 8 | |
| round | 4 | |
| canonical_block_id | 32 | the inner execution commitment — the PRIMARY signed object |
| parent_canonical_id | 32 | the inner parent commitment |
| execution_state_root | 32 | post-execution state root |
| payload_root | 32 | transaction/payload root |
| validator_set_root | 32 | epoch / weighted-set commitment; all-zero means unbound |
| accept | 1 | 0x01 accept, 0x00 reject |
Two rules carry the safety of the scheme:
-
The signature commits to the inner canonical identity, never the outer envelope. The proposervm wrapper — proposer, timestamp — is excluded, so two honest wrappers of one execution produce byte-identical vote messages and interoperate. A block with no inner/outer split (a bare in-process VM block, or a fixed-set chain) binds its own id in the
canonical_block_idslot; this degrade is resolved in exactly one place so every producer signs the same bytes for the same block. -
validator_set_rootis bound before the accept byte, so a vote is committed to the weighted validator set it was cast under. A cert gathered under set-root R cannot be re-verified as certifying under a different set R′.
The domain tag carries its own version. A v2 (canonical-commitment) signature
can never be confused with a signature over an outer id.
The certificate: QuorumCert
The certificate is stored and transported with this layout, big-endian. It carries both the signed canonical identity and the outer transport ids; the outer ids are a lookup/gossip key and are not part of the signed message above.
| field | width | signed? |
|---|---|---|
| version | 2 | — |
| type | 1 | — |
| tier | 1 | — (Nova or Quasar, the accept/export rung this cert attests) |
| chain_id | 32 | yes |
| height | 8 | yes |
| round | 4 | yes |
| block_id | 32 | no — outer envelope, transport cache key |
| parent_id | 32 | no — outer envelope parent |
| canonical_block_id | 32 | yes |
| parent_canonical_id | 32 | yes |
| execution_state_root | 32 | yes |
| payload_root | 32 | yes |
| validator_set_root | 32 | yes |
| threshold | 4 | — |
| vote_count | 4 | — |
then vote_count records | ||
| node_id | 20 | |
| accept | 1 | |
| sig_len | 4 | |
| sig | sig_len |
A decoder MUST reject a frame it could not itself produce:
- the accept byte in each vote record is
0x00or0x01; any other value is corrupt (framing malleability — two byte strings must not name one cert); vote_count × (20 + 1 + 4)must not exceed the remaining buffer, and eachsig_lenmust not exceed what remains, before any allocation (a peer-supplied count must never size an allocation);- no trailing bytes remain after the last vote record.
Tier semantics
tier records the rung the cert attests, not a weaker verification path. Both
tiers verify against the live committee's threshold:
- Quasar is export finality: two-thirds of stake at the epoch.
- Nova is local-execution finality at the committee majority, deliberately sub-Byzantine. It is sound because nothing exportable reads a Nova-only height; the accept threshold for either tier is the live committee's, never a value the cert declares about itself.
Rationale
The layout is a hand-written fixed-width encoding rather than a schema with optional fields, so a field cannot silently drop out of the signature: adding one is a visible change to this table and to the domain-tag version. Signing the inner canonical identity rather than the outer envelope is what let a single fix resolve a class of finality-halt and interoperability defects — two wrappers of one execution are one decision.
Backwards Compatibility
This LP documents the shipped v2 format; it introduces no change. It supersedes
the certificate description in LP-182, which specifies a ZAP-schema cert the code
does not produce. Any node built to LP-182's certificate would be
signature-incompatible with the network. LP-182 should be reduced to the parts
that ship, or retired.
Reference Implementation
github.com/luxfi/consensus, engine/chain/cert.go (canonicalVoteMessageFor,
the signed message) and engine/chain/cert_codec.go (MarshalQuorumCert /
UnmarshalQuorumCert, the certificate wire). The byte vectors in
engine/chain/cert_wire_vector_test.go are hand-derived from this layout and
pin it against regression.
Security Considerations
The signed message is the finality authority. Every field the finality decision depends on is inside it, and the outer envelope — which an attacker can vary without changing the decision — is deliberately outside it. A conformance suite across implementations MUST assert byte-equality of the signed message for a shared corpus of positions; a divergence there is a fork or a forgery surface, not a compatibility nuisance.