Skip to content

fix(db): re-verify block signature during fork replay#6777

Merged
lvs0075 merged 1 commit into
tronprotocol:release_v4.8.2from
xxo1shine:fix/fork-replay-signature-recheck
May 21, 2026
Merged

fix(db): re-verify block signature during fork replay#6777
lvs0075 merged 1 commit into
tronprotocol:release_v4.8.2from
xxo1shine:fix/fork-replay-signature-recheck

Conversation

@xxo1shine
Copy link
Copy Markdown
Collaborator

What does this PR do?

  • Manager.switchFork now re-validates each replayed block's witness signature before applying. The witness account's permission can change between forks (via permission-update transactions), so a signature that was valid on the original chain may no longer be valid on the replay path.
  • Use if (!validateSignature(...)) throw new ValidateSignatureException rather than discarding the boolean return: validateSignature only throws on malformed signature bytes; an attacker-supplied valid-format signature with a wrong-signer address returns false. Discarding the return would let that attack through.
  • The existing switchFork catch list already includes ValidateSignatureException, so the new throw is wired into the existing switchback path with no additional handling.
  • Add three BlockCapsule.validateSignature contract tests pinning the two failure modes the fix relies on: signer-mismatch returns false, signer-match returns true, and a 65-byte malformed signature throws ValidateSignatureException.

Why are these changes required?

This PR has been tested by:

  • Unit Tests
  • Manual Testing

Follow up

Extra details

@github-actions github-actions Bot requested a review from halibobo1205 May 18, 2026 07:11
- Manager.switchFork now re-validates each replayed block's witness signature
  before applying. The witness account's permission can change between forks
  (via permission-update transactions), so a signature that was valid on the
  original chain may no longer be valid on the replay path.
- Use `if (!validateSignature(...)) throw new ValidateSignatureException`
  rather than discarding the boolean return: validateSignature only throws
  on malformed signature bytes; an attacker-supplied valid-format signature
  with a wrong-signer address returns false. Discarding the return would let
  that attack through.
- The existing switchFork catch list already includes
  ValidateSignatureException, so the new throw is wired into the existing
  switchback path with no additional handling.
- Add three BlockCapsule.validateSignature contract tests pinning the two
  failure modes the fix relies on: signer-mismatch returns false, signer-match
  returns true, and a 65-byte malformed signature throws
  ValidateSignatureException.
@xxo1shine xxo1shine force-pushed the fix/fork-replay-signature-recheck branch from 2d44164 to 70e5b2e Compare May 18, 2026 09:10
@lvs0075 lvs0075 requested a review from waynercheung May 19, 2026 03:09
Exception exception = null;
// todo process the exception carefully later
try (ISession tmpSession = revokingStore.buildSession()) {
if (!item.getBlk().validateSignature(
Copy link
Copy Markdown
Collaborator

@waynercheung waynercheung May 20, 2026

Choose a reason for hiding this comment

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

[MUST] In normal chain state a witness account should exist, but fork replay re-validates the block under a different state from the one used when the block was first accepted into KhaosDB. If the witness account/permission only existed on the old branch and is gone after rollback to the common ancestor, accountStore.get(witnessAccountAddress) can return null and validateSignature() throws NPE. This unchecked exception skips the existing switchback path. Please convert this case to false / ValidateSignatureException or otherwise route it through the same rollback handling.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

  1. When a block arrives from the network, it first goes through TronNetDelegate.validBlock — which already invokes block.validateSignature(dynamicPropertiesStore, accountStore) once.
  2. If accountStore.get(witnessAccountAddress) had returned null at that point, that validateSignature call would return false due to address mismatch before any NullPointerException could fire, and validBlock would raise it as BLOCK_SIGN_INVALID and reject the block — so it could never have entered KhaosDB, and would never reach switchFork.

// The throw fires before the finally's switchback runs. Switchback's applyBlock
// may surface another exception due to partial mocks; we tolerate any throwable
// here because the new code's throw has already been executed (line covered).
try {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[MUST] This test still passes if production code calls validateSignature() but ignores the false return value, because it catches any Throwable and only verifies the method was invoked. Please assert the reflected invocation fails specifically with ValidateSignatureException, and verify setSwitch(true) / apply path is not reached for the invalid block.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the heads-up. This was considered — the boolean return contract of validateSignature is already pinned independently in BlockCapsuleTest by three contract tests; this ManagerMockTest test is here to pin that the switchFork replay path actually reaches validateSignature. The two layers together already cover the regression. Adding a cause assertion plus verify(never()).setSwitch(true) here would couple the test to the implementation detail that applyBlock calls setSwitch via its argument expression — a future refactor would break this test for the wrong reason. Prefer to keep the current form.

Copy link
Copy Markdown
Collaborator

@waynercheung waynercheung left a comment

Choose a reason for hiding this comment

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

LGTM

@halibobo1205 halibobo1205 added topic:DB Database topic:net p2p net work, synchronization labels May 21, 2026
@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.2 milestone May 21, 2026
@lvs0075 lvs0075 merged commit 78bc75d into tronprotocol:release_v4.8.2 May 21, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:DB Database topic:net p2p net work, synchronization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants