-
Notifications
You must be signed in to change notification settings - Fork 240
SIMD-0402: Finalization Cert in Block Footer #402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9c88a37
0a162f2
ecc8ac3
bbef21a
5695b36
ae23930
9f885d9
b928ef6
ec6ddb0
2a8c27a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,242 @@ | ||
| --- | ||
| simd: '0402' | ||
| title: Finalization Certificate in Block Footer | ||
| authors: | ||
| - Quentin Kniep (Anza) | ||
| - ksn6 (Anza) | ||
| - Wen Xu (Anza) | ||
| category: Standard | ||
| type: Core | ||
| status: Review | ||
| created: 2025-11-11 | ||
| feature: (fill in with feature key and github tracking issues once accepted) | ||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| This SIMD proposes giving each leader the option to add an Alpenglow | ||
| finalization certificate to the Block Footer for enhanced observability. This | ||
| way anyone who only observes blocks can understand that the blocks are | ||
| finalized without knowing the details of all-to-all communication between the | ||
| validators. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Before Alpenglow, validator votes were expressed as on-chain transactions that | ||
| updated vote accounts, each functioning as a state machine tracking the voting | ||
| activity of a staked validator. Alpenglow (SIMD-0326) moves away from this | ||
| model by having validators send vote messages directly to one another, | ||
| improving the speed and efficiency of consensus. | ||
|
|
||
| This shift removes the on-chain visibility previously provided by vote | ||
| transactions and vote account state, which zero-staked validators currently | ||
| rely on to infer validator delinquency. It also affects any party that depends | ||
| on the ability to observe votes on-chain. To address this, Alpenglow | ||
| proposed adding finalization certificate to the block footer. This certificate | ||
| consists of BLS-aggregated signatures representing votes from validators | ||
| collectively controlling a significant amount of stake. They offer a concise, | ||
| verifiable record that blocks have been finalized correctly, enabling reliable | ||
| RPC status reporting and supporting a broad range of downstream uses. | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - Alpenglow is specified in [SIMD 326](https://github.com/solana-foundation/solana-improvement-documents/pull/326) | ||
|
|
||
| - Block footer is specified in [SIMD 307](https://github.com/solana-foundation/solana-improvement-documents/pull/307) | ||
|
|
||
| ## New Terminology | ||
|
|
||
| - **Finalization Certificate for Observability**: is a proof that a specified | ||
| block is finalized by aggregating specific types of votes. Note that the data | ||
| format is slightly different from certificates in SIMD 326, because we are | ||
| combining a *slow-finalization* and corresponding *notarization* certificates | ||
| into one data structure when necessary. See details in *Finalization | ||
| Certificate for Observability data structure*. | ||
|
|
||
| ## Detailed Design | ||
|
|
||
| ### Data Layout in Block Footer | ||
|
|
||
| #### Finalization Certificate for Observability data structure | ||
|
|
||
| ```rust | ||
| pub struct VotesAggregate { | ||
| signature: BLSSignatureCompressed, | ||
| bitmap: Vec<u8>, | ||
| } | ||
|
|
||
| pub struct FinalCertificate { | ||
| pub slot: Slot, | ||
| pub block_id: Hash, | ||
| pub final_aggregate: VotesAggregate, | ||
| pub notar_aggregate: Option<VotesAggregate>, | ||
| } | ||
| ``` | ||
|
|
||
| Where `Slot` is u64, `Hash` is [u8; 32], and `BLSSignatureCompressed` is | ||
| specified in `bls-signatures`, it is a 96 byte array. | ||
|
|
||
| Please refer to `solana-signer-store` for bitmap format. We expect to be using | ||
| `solana-signer-store 0.1.0` for the Alpenglow launch. Only base2-encoding | ||
| will be used in either bitmap. When `notar` is `None`, this is a fast | ||
| finalization cert. Otherwise it’s a slow finalization cert. | ||
|
|
||
| Block Footer Extension | ||
|
|
||
| ```rust | ||
|
|
||
| pub struct BlockFooterV1 { | ||
| pub bank_hash: Hash, // Introduced in V1 (SIMD-0298) | ||
| pub block_producer_time_nanos: u64, // Introduced in V1 (SIMD-0307) | ||
| pub block_user_agent: Vec<u8>, // Introduced in V1 (SIMD-0307) | ||
| pub final_cert: Option<FinalCertificate>, // New, in this SIMD | ||
| } | ||
| ``` | ||
|
|
||
| **Note on Versioning and Field Ordering**: While adding fields to the footer | ||
| would typically warrant a version increment, we maintain `footer_version=1` | ||
| for simplicity. | ||
|
|
||
| We only make these atypical changes in light of the fact that, as of November | ||
| 2025, clients do not yet disseminate block footers or block markers, making | ||
| this an appropriate time to modify the version 1 format before widespread | ||
| adoption. | ||
|
|
||
| #### Serialization Format | ||
|
|
||
| The extended block footer serializes within a `BlockComponent` as follows: | ||
|
|
||
| ``` | ||
| +---------------------------------------+ | ||
| | Entry Count = 0 (8 bytes) | | ||
| +---------------------------------------+ | ||
| | Marker Version = 1 (2 bytes) | | ||
| +---------------------------------------+ | ||
| | Variant ID = 0 (1 byte) | | ||
| +---------------------------------------+ | ||
| | Length (2 bytes) | | ||
| +---------------------------------------+ | ||
| | Version = 1 (1 byte) | | ||
| +---------------------------------------+ | ||
| | bank_hash (32 bytes) | | ||
| +---------------------------------------+ | ||
| | block_producer_time_nanos (8 bytes) | | ||
| +---------------------------------------+ | ||
| | block_user_agent_len (1 byte) | | ||
| +---------------------------------------+ | ||
| | block_user_agent (0-255 bytes) | | ||
| +---------------------------------------+ | ||
| | final_cert_present (1 byte) | ← NEW | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to use this field to also indicate if the notar and skip reward certs are present. So we are not using additional bytes for them. Maybe we want to roll the reward certs into this SIMD?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is chosen this way because this is the natural translation of from Rust in bincode/wincode serialization.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like @akhi3030 's suggestion, though more strongly prefer avoiding custom serde. I'm checking with the |
||
| +---------------------------------------+ | ||
| | final_cert (variable) | ← NEW | ||
| +---------------------------------------+ | ||
| ``` | ||
|
|
||
| If the `final_cert_present` is 0, then there is no `final_cert` following it, | ||
| otherwise it is 1. | ||
|
|
||
| #### FinalCertificate Serialization | ||
|
|
||
| If `final_cert` is present, it is serialized as follows: | ||
|
|
||
| ``` | ||
| +---------------------------------------+ | ||
| | slot (8 bytes) | | ||
| +---------------------------------------+ | ||
| | block_id (32 bytes) | | ||
| +---------------------------------------+ | ||
| | final_aggregate_signature (96 bytes) | | ||
| +---------------------------------------+ | ||
| | final_aggregate_bitmap_len (2 bytes) | | ||
| +---------------------------------------+ | ||
| | final_aggregate_bitmap (variable) | | ||
| +---------------------------------------+ | ||
| | notar_aggregate_present (1 byte) | | ||
| +---------------------------------------+ | ||
| | notar_aggregate_signature (variable) | | ||
| +---------------------------------------+ | ||
| | notar_aggregate_bitmap_len (variable) | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrote variable basically because notar_aggregate is optional here. |
||
| +---------------------------------------+ | ||
| | notar_aggregate_bitmap (variable) | | ||
| +---------------------------------------+ | ||
| ``` | ||
|
|
||
| If the `notar_aggregate_present` is 0, then there are no | ||
| `notar_aggregate_signature`, `notar_aggregate_bitmap_len`, and | ||
| `notar_aggregate_bitmap` following it. Otherwise, we will have | ||
| `notar_aggregate_signature` as 96 bytes array, | ||
| `notar_aggregate_bitmap_len` as u16 in little endian, and | ||
| `notar_aggregate_bitmap` following that. | ||
|
|
||
| ### Field Population by leader | ||
|
|
||
| While producing a block at slot `s`, the leader should include the finalization | ||
| certificate corresponding to the highest slot available `t < s`. In the usual | ||
| case with no skipped slots, this will be the certificate for `s − 1`, though | ||
| the leader ultimately decides which certificates to include. | ||
|
|
||
| If a fast finalization certificate is available, the leader should include only | ||
| fast finalization cert in the `final` field. Otherwise, the leader should | ||
| include the slow finalization cert in the `final` field and the notarization | ||
| cert in the `notar` field. | ||
|
|
||
| ### Field Validation by non-leaders | ||
|
|
||
| Validators MUST enforce the following rules: | ||
|
|
||
| 1. Type Constraints: If the `notar` field is `None`, `final` field must be an | ||
| aggregate of notarization votes for `(slot, hash)`. Otherwise the `final` field | ||
| must be an aggregate of finalization votes for `slot`, and the `notar` field | ||
| must be an aggregate of notarization votes for `(slot, hash)`. | ||
|
|
||
| 2. BLS Validity: All certificates provided must pass BLS signature verification. | ||
|
|
||
| 3. Consensus Thresholds: Each certificate must meet the consensus thresholds | ||
| specified by the Alpenglow protocol (SIMD-0326, https://www.anza.xyz/alpenglow-1-1). | ||
| For a fast finalization certificate, the limit is 80%. For a slow finalization | ||
| certificate, the limit is 60% for both `final` and `notar` aggregates. | ||
|
|
||
| Any violation should cause the block to be invalidated, and the remainder of the | ||
| leader window should be skipped. | ||
|
|
||
| ### RPC change to Validator Delinquent status | ||
|
|
||
| The RPC layer will read the parsed certificates from the bank and use the | ||
| bitmaps embedded in those certificates to update each validator’s voting | ||
| status. | ||
|
|
||
| To interpret the bitmaps, the RPC can pull the BLS public keys from their vote | ||
| accounts and retrieve each account’s stake from the bank for the corresponding | ||
| epoch. Validators are then ranked by sorting first by stake in descending | ||
| order, and breaking ties by public keys in ascending order. See SIMD 357 for | ||
| how we pick staked validators if there are more than 2000 of them. This | ||
| deterministic ordering maps cleanly onto the bitmap positions, allowing the RPC | ||
| code to identify exactly which staked validators participated in a given vote. | ||
|
|
||
| ## Alternatives Considered | ||
|
|
||
| **Transaction-Based Distribution**: Rejected because the execution overhead was | ||
| too high. It may also increase consensus latency, bandwidth usage, and would | ||
| make the consensus implementation more complex and error prone. | ||
|
|
||
| **Directly using Certificate format in Consensus Pool**: Rejected because under | ||
| the new format it's easier to enforce the rule that `FinalizationCertificate` | ||
| contains either fast or slow finalization certificates. | ||
|
|
||
| **Use base-3 encoding in Certificate**: We can use base-3 encoding in `Skip` or | ||
| `NotarizeFallback` certs in Alpenglow consensus pool because the pool checks to | ||
| make sure there will never be a `(true, true)` combination where two different | ||
| votes are presented for any validator. This is not true in this case. In most | ||
| of the cases, validators will send both `Notarize` and `Finalize` for a block. | ||
|
|
||
| ## Impact | ||
|
|
||
| Invalid certificates will cause the block to be marked invalid. | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| N/A | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| Not backward compatible. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in previous SIMDs, the feedback was to keep this field in the end so that all fields have a fixed offset. This SIMD is introducing a new variable field. So at the least, we may want to swap things around so that all the fixed length fields come first and then all the variable length ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above, if we swap things around then natural bincode/wincode serialization doesn't work any more, we need to write customized serialization.