Skip to main content
Technical proposal SIMD-0298 Idea

Add `bank_hash` to block footer.

  1. Idea
  2. Draft
  3. Review
  4. Accept
  5. Implement
  6. Active

Decision brief

Why this proposal matters

This proposal adds the bank hash to the block footer and introduces validity constraints to ensure the bank hash matches the expected value.

Source-backed reading aid · source language: EN

Proposal at a glance

What changes

  • footer_version: u16 - Version number for the footer structure (currently 1)
  • block_producer_time_nanos: u64 - Nanosecond UNIX timestamp when the block producer started constructing the block
  • block_user_agent_len: u8 - Length of the user agent string in bytes

Stakeholder map

Who is affected

Builders & client teams Medium impact

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.

Action requirement unknown
Validators & operators Medium impact

This change is not backwards compatible and requires a feature flag for activation. All validators must upgrade to support the new block footer format and validation rules before the feature is activated.

Action may be required
Users & stakers Medium impact

Blocks now contain slightly more metadata bytes which cannot be used for transactions.

Action requirement unknown
Governance & ecosystem Medium impact

This proposal adds the bank hash to the block footer and introduces validity constraints to ensure the bank hash matches the expected value.

Action requirement unknown

Exact source revision

Full proposal document

Source-language document. Technical identifiers and evidence remain unchanged. f1f6c8b05dc2

Summary

This proposal adds the bank hash to the block footer and introduces validity constraints to ensure the bank hash matches the expected value.

Motivation

This proposal is part of the changes required for Alpenglow. In Alpenglow, votes do not contain the bank_hash. Instead they are purely on block_id If we remove bank_hash from votes without any other changes, then execution would not be a part of consensus at all, meaning if there is a bug in the runtime which causes a bank_hash mismatch it could be difficult to identify quickly and resolve. This proposal moves consensus on the bank_hash to the block footer so that we still have consensus on execution state.

New Terminology

No new terminology but we recall the Block Footer structure and bank_hash

The block footer is implemented as a block marker variant that must occur once after the last entry batch in a block. The footer contains general block and producer metadata, along with any metrics that must be computed after the block has been produced.

Block Marker Header

The block footer uses a block marker system to differentiate it from regular entry batches. Entry batch data starts with an 8-byte value representing the number of entries in the batch, which cannot be zero. The block marker header begins with 8 zero bytes (the block_marker_flag), allowing replay parsers to distinguish block markers from regular entry batches.

The block marker header structure is:

        Block Marker Header Layout
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_marker_flag      (64 bits of 0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version=1                   (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| variant                      (8 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| length                      (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              --payload--              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The block footer follows this structure:

     Block Marker Variant -- Footer
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_marker_flag      (64 bits of 0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version=1                   (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| variant=0                    (8 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| length                      (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| footer_version=1            (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_producer_time_nanos   (64 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_user_agent_len         (8 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_user_agent        (0-255 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Existing Footer Fields:

  • footer_version: u16 - Version number for the footer structure (currently 1)
  • block_producer_time_nanos: u64 - Nanosecond UNIX timestamp when the block producer started constructing the block
  • block_user_agent_len: u8 - Length of the user agent string in bytes
  • block_user_agent: String - Variable length UTF-8 encoded string (up to 255 bytes) identifying the block producer

Bank Hash Structure

The bank_hash represents the cryptographic hash of the bank state after executing all transactions in a block. It serves as a commitment to the complete execution state and is calculated from several components:

           Bank Hash Components
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| parent_bank_hash           (32 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| signature_count            (64 bits)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| last_blockhash             (32 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| accounts_lt_hash_checksum  (32 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Bank Hash Components:

  • parent_bank_hash - Hash of the parent bank's state (32 bytes)
  • signature_count - Total number of signatures processed in the bank (64 bits)
  • last_blockhash - The blockhash used for transaction processing (32 bytes)
  • accounts_lt_hash_checksum - Checksum of the accounts ledger tree (32 bytes)
  • accounts - Detailed account state information (variable size)

Optional Component:

  • Hard Fork Data (when applicable) - If the current slot is a hard fork slot, additional hard fork data is hashed into the final result

The bank hash provides a cryptographic commitment to the complete execution state, ensuring that any change to the bank's state (accounts, balances, program state, etc.) will result in a different hash. This makes it a reliable mechanism for consensus on execution results.

Detailed Design

The block footer structure outlined in SIMD 0307 will be extended to include a new field. The updated footer structure will be:

     Block Footer (Extended)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_marker_flag      (64 bits of 0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version=1                   (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| variant=0                    (8 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| length                      (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| footer_version=1            (16 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| bank_hash                  (32 bytes) |  ←NEW
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_producer_time_nanos   (64 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_user_agent_len         (8 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| block_user_agent        (0-255 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Note on Versioning and Field Ordering: While adding a field to the footer would typically warrant a version increment, we maintain footer_version=1 for simplicity. Rather than appending bank_hash to the end of the footer, which would be the typical approach, we insert bank_hash towards the beginning to give it a fixed offset within the footer.

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.

Validity Constraints

In addition to the current validity constraints, a block will be considered invalid if:

  1. The bank_hash does not match the post execution state of the block
  2. The bank_hash is missing (for blocks after the feature is activated)

Implementation Details

  1. The bank_hash will be computed at the end of packing each block. The validator proposing the block will include it in the block footer.
  2. Replaying validators will verify the bank_hash in the block footer exists and matches their computed hash of the state after executing the block. This is similar to what they do with the bank_hash contained in votes today.

If the bank_hash does not match the post execution bank_hash then the block is marked invalid. Validators will treat this the same way they treat a protocol violatiing error. In particular, the block will be marked dead and the validator will not vote on it or any of its children. Post alpenglow the validator will vote skip on this block and any children.

Feature Activation

The feature MUST be actiavted before or the Alpenglow rollout.

This change will be implemented behind a feature flag and activated through the feature activation program. The activation will require:

  1. Implementation of the new block footer structure
  2. Updates to block production logic to include the bank_hash
  3. Updates to block replay logic to verify the bank_hash correctly corresponds to the post execution bank state

Alternatives Considered

  1. Remove Execution from Consensus Entirely: We could simply remove the bank_hash from votes and not include it anywhere in the block structure. This would mean that execution bugs could go undetected for longer periods, making it harder to identify and resolve runtime issues. Eventually we want to remove this completely but for now it seems useful to keep training wheels on, particularly as we rollout Firedancer's VM implementation.

  2. XOR bank_hash with block_id: Instead of adding a separate bank_hash field to the footer, we could XOR the bank_hash with the block_id to create a combined hash in votes. This would save space but would make verification more complex and less transparent. This approach was rejected because it adds unnecessary complexity to the validation process and makes debugging more difficult.

Impact

Blocks now contain slightly more metadata bytes which cannot be used for transactions.

Security Considerations

This change makes block validity more restrictive. No blocks would be valid under this proposal that would otherwise be invalid today.

Backwards Compatibility

This change is not backwards compatible and requires a feature flag for activation. All validators must upgrade to support the new block footer format and validation rules before the feature is activated.

Evidence graph

Related proposals and rollout

One or more sources are unavailable.

Upstream review record

Upstream discussion & review

GitHub review is editorial context, not evidence of on-chain support, voting, or outcome.

PR #298 · merged SIMD 0298: Add bankhash to block footer 15 comments and reviews · Jan 12, 2026
@bw-solana

I know we just plan on using the same bankhash that is included in votes today, but might not be a bad idea to spell out how the bank hash is generated in this SIMD

GitHub ↗
@bw-solana

We need this for Alpenglow, right? So I think it must be activated either before or as part of the overall Alpenglow feature activation

GitHub ↗
@MaxResnick

added to New Terminology section

GitHub ↗
@bw-solana

I wonder if we want to sneak this before the agent/agent length fields so that it's at a fixed offset and doesn't require reading prior fields to extract

GitHub ↗
@bw-solana

CC @topointon-jump

GitHub ↗
@brooksprumo

Hey folks, sorry for not seeing this SIMD until now. The accounts lt hash is meant to be a private implementation detail of how the bank hash is calculated. We should avoid making it public/standalone where possible, so that we retain the ability to change it more easily. (For background, read SIMD 223 on removing the Accounts Delta Hash). If the intent to have the hash of the final state for this bank, why not include the bank hash itself? Even if we have the accounts lt hash here, that may be insufficient to compute the bank hash, since the bank hash also includes hard forks. (And then as a nit, this value, as stated, is the accounts lt hash. It is not the accounts lt hash checksum. The ch

GitHub ↗
@bw-solana

that's right. I think I was the one that asked Max to include Bank Hash components just for background context. The Bank Hash is the only thing being put into the footer

GitHub ↗

simd.watch community discussion · SIMD-0298

Powered by Giscus · Sign in with GitHub to comment

Community comments load when this section approaches the viewport.

Provenance

Evidence & technical details

Rollout or chain data, source revisions, freshness and integrity.
2

SIMD

Deployment Status

Document lifecycleIdea
CategoryCore protocol

Feature Gate not yet created

Exact source revision

Sources & integrity

  • Proposal document pinned_commit_blob
    f1f6c8b05dc205552d3c290a854a438392701107
  • simd-document document · stale · Oct 17, 2026
    f1f6c8b05dc205552d3c290a854a438392701107

One or more sources are unavailable.