Skip to content

feat(verification): TSS Integration on verification path#2305

Open
AlfredoG87 wants to merge 9 commits intomainfrom
tss-integration
Open

feat(verification): TSS Integration on verification path#2305
AlfredoG87 wants to merge 9 commits intomainfrom
tss-integration

Conversation

@AlfredoG87
Copy link
Contributor

@AlfredoG87 AlfredoG87 commented Mar 2, 2026

Context

Starting with HAPI 0.72.0, Consensus Nodes sign block proofs using TSS (Threshold Signature Scheme) instead of the legacy hash of hash signature. Verifying these proofs requires three components: a ledger ID, an address book (node public keys, weights, and IDs), and a WRAPS verification key. All three are published together in block 0 via a LedgerIdPublicationTransactionBody embedded in a signed transaction.

This PR wires that bootstrap into the live verification path so that block nodes can verify TSS-signed blocks end-to-end.

How It Works

Block 0 bootstrap: When the session processes block 0, it scans SIGNED_TRANSACTION items for a LedgerIdPublicationTransactionBody. When found, it calls VerificationServicePlugin.initializeTssParameters(), which sets the native TSS state (address book, WRAPS VK) and stores the publication on the plugin. If block 0 verification then succeeds, the full publication is persisted to disk as a serialized protobuf.

Restart recovery: On startup, if the TSS parameters file exists, the plugin loads and restores the full publication — ledger ID, address book, and WRAPS VK — before any blocks are processed.

Retry semantics: If block 0 fails verification (e.g., tampered items), TSS parameters are not locked in. Any subsequent block 0 can overwrite them and try again. Once parameters are persisted after a successful verification, they are locked — initializeTssParameters() becomes a no-op, preventing a corrupt block 0 from poisoning verified state.

Session design: Each session is short-lived (one per block) and receives the current ledgerId via constructor. The plugin — which owns process-level state — is the right home for TSS parameters.

Key Changes

  • VerificationServicePlugin owns TSS state as public static fields (activeLedgerId, activeTssPublication, tssParametersPersisted)
  • ExtendedMerkleTreeSession receives ledgerId via constructor and calls VerificationServicePlugin.initializeTssParameters() when it discovers the publication in block 0
  • Persist/restore the full LedgerIdPublicationTransactionBody protobuf instead of just raw ledger ID bytes (fixes incomplete state restoration on restart)
  • Rename verification.ledgerIdFilePathverification.tssParametersFilePath to reflect that it stores more than just the ledger ID
  • Remove verification.ledgerId config property — a ledger ID alone is useless without the address book and WRAPS VK
  • Fix backfill handler that was persisting TSS parameters for block 0 without checking backfillNotification.success()
  • Add hedera-cryptography-wraps dependency for WRAPS VK initialization
  • Add real TssWraps block fixtures (block 0 and block 50) with unit and integration tests covering the full bootstrap-to-verify flow

Follow-up

TSS state currently lives as static fields on the plugin as a pragmatic starting point (the native TSS library is itself a singleton). Moving it into BlockNodeContext so other plugins can access it is tracked in #2321.

Fixes #1660
Fixes #1659
Fixes #2008

@AlfredoG87 AlfredoG87 self-assigned this Mar 2, 2026
@AlfredoG87 AlfredoG87 added this to the 0.29.0 milestone Mar 2, 2026
@AlfredoG87 AlfredoG87 changed the title Tss integration feat(verification): TSS Integration on verification path Mar 3, 2026
…tion

- Verify block proofs via TSS.verifyTSS() with full chain-of-trust
- Parse LedgerIdPublicationTransactionBody from block 0 to bootstrap address book, WRAPS VK, and ledger ID
- Add verification.ledgerId config property to pre-seed ledger ID for networks joined mid-stream
- Drop TSS-only (historyEnabled=false) mode; TssWraps only per expert guidance
- Add TssWraps block 0 fixture; settled-path test disabled pending fixture generation
- Export com.hedera.hapi.node.tss packages from protobuf-pbj module

Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
@AlfredoG87 AlfredoG87 added New Feature A new feature, service, or documentation. Major changes that are not backwards compatible. Block Node Issues/PR related to the Block Node. labels Mar 4, 2026
@AlfredoG87 AlfredoG87 marked this pull request as ready for review March 4, 2026 15:39
@AlfredoG87 AlfredoG87 requested review from a team as code owners March 4, 2026 15:39
@AlfredoG87 AlfredoG87 requested a review from jasperpotts March 4, 2026 15:39
…ck 0 instead of pre-configured

Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
- Add verification.ledgerId config to pre-seed ledger ID for nodes joining an established network mid-stream (block 0 will not be replayed)
- Add verification.ledgerIdFilePath config to persist ledger ID across restarts
- Block 0 is authoritative: always writes the ledger ID file on completion
- File takes priority over config string at startup; block 0 always overwrites both
- Add 5 bootstrap path tests covering all paths and combinations

Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Copy link
Contributor

@jsync-swirlds jsync-swirlds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good design, but there are some areas we could still improve.

Copy link
Contributor

@Nana-EC Nana-EC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Started, will circle back

// bootstraps TSS native state (address book + WRAPS VK) and sets ACTIVE_LEDGER_ID.
// Returns true if the publication was found and applied, false otherwise.
private static boolean loadLedgerId(Bytes signedTxBytes) throws ParseException {
if (signedTxBytes == null || signedTxBytes.length() == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Should we capture the block number and ensure that we're only ever setting assuming the block number is incrementing.
I'm curious about a backfill scenario that included the LedgerIdPublicationTransactionBody and overwrites the more recent values?

Copy link
Contributor Author

@AlfredoG87 AlfredoG87 Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. TSS initialization only triggers for block 0 (if (blockItemsMessage.blockNumber() == 0)), so backfill of other blocks won't attempt to overwrite. Additionally, the tssParametersPersisted guard on initializeTssParameters() prevents any overwrite once the parameters have been successfully
verified and persisted. A backfilled block 0 would only initialize TSS parameters if they haven't been persisted yet, which is the correct behavior for a node that doesn't have block 0 locally.
However unlikely since such cases most likely manually copy/pasted the /opt/hiero/block-node/verification/tss-parameters.bin file for that network, similar as we are planing to do with the TSS Cutover EPIC.

- Move TSS state (activeLedgerId, activeTssPublication) from ExtendedMerkleTreeSession to VerificationServicePlugin as public static fields
- Add public static initializeTssParameters() on plugin with tssParametersPersisted guard; session calls it during block 0 when LedgerIdPublicationTransactionBody is discovered
- TSS parameters can be overwritten by any block 0 until persisted after successful verification; once persisted, initializeTssParameters() is a no-op
- Split loadLedgerId() into findLedgerIdPublication() (pure parsing on session) and initializeTssParameters() (native init on plugin)
- Remove VK_LENGTH constant; let TSS.verifyTSS() handle validation internally
- Remove verification.ledgerId config string (useless without address book/WRAPS VK)
- Rename verification.ledgerIdFilePath to verification.tssParametersFilePath
- Persist/restore full LedgerIdPublicationTransactionBody protobuf (ledger ID + address book + WRAPS VK) instead of raw ledger ID bytes
- Fix backfill block 0 persisting TSS parameters without checking verification success
- Add hedera-cryptography-wraps dependency
- Add TssWraps test fixtures (block 0 and block 50) and verification tests
- Add plugin-level integration test verifying full TSS flow: block 0 bootstraps TSS parameters, subsequent block verifies with TSS

Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 81.57895% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...k/node/verification/VerificationServicePlugin.java 80.39% 8 Missing and 2 partials ⚠️
...cation/session/impl/ExtendedMerkleTreeSession.java 83.33% 3 Missing and 1 partial ⚠️
@@             Coverage Diff              @@
##               main    #2305      +/-   ##
============================================
+ Coverage     80.74%   80.81%   +0.06%     
- Complexity     1461     1472      +11     
============================================
  Files           139      139              
  Lines          6766     6837      +71     
  Branches        728      738      +10     
============================================
+ Hits           5463     5525      +62     
- Misses          984      990       +6     
- Partials        319      322       +3     
Files with missing lines Coverage Δ Complexity Δ
...lock/node/verification/AllBlocksHasherHandler.java 90.55% <100.00%> (ø) 29.00 <0.00> (ø)
...ro/block/node/verification/VerificationConfig.java 100.00% <ø> (ø) 1.00 <0.00> (ø)
...erification/session/HapiVersionSessionFactory.java 90.00% <ø> (ø) 9.00 <0.00> (ø)
...cation/session/impl/ExtendedMerkleTreeSession.java 81.48% <83.33%> (-0.19%) 22.00 <5.00> (+6.00) ⬇️
...k/node/verification/VerificationServicePlugin.java 85.22% <80.39%> (-2.08%) 24.00 <5.00> (+7.00) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Block Node Issues/PR related to the Block Node. New Feature A new feature, service, or documentation. Major changes that are not backwards compatible.

Projects

None yet

3 participants