Skip to content

fix(framework): count pq_auth_sig in multisign fee and reverify checks#40

Open
bladehan1 wants to merge 3 commits into
release_post_quantumfrom
feature/pq_multifee
Open

fix(framework): count pq_auth_sig in multisign fee and reverify checks#40
bladehan1 wants to merge 3 commits into
release_post_quantumfrom
feature/pq_multifee

Conversation

@bladehan1

@bladehan1 bladehan1 commented Jun 23, 2026

Copy link
Copy Markdown
Owner

What does this PR do

Fixes three places where PQ multisig (pq_auth_sig) was not accounted for alongside ECDSA signatures, even though the existing permission/weight validation already treats the two as contributing independently to the same threshold:

  • Manager.consumeMultiSignFee only checked getSignatureCount() > 1, so PQ-only and hybrid ECDSA+PQ multisig transactions escaped the multisign fee entirely.
  • Manager.isSameSig (used by getVerifyTxs to skip re-verifying transactions already verified in the pending pool) compared only the ECDSA signature list. Since txId hashes raw_data only, a block transaction with a substituted pq_auth_sig but the same txId could be wrongly marked verified=true and skip real signature validation in processTransaction — a signature-verification bypass.
  • Wallet.getTransactionApprovedList built the approved list from ECDSA signatures only, missing the pq_auth_sig branch already present in its sibling TransactionUtil.getTransactionSignWeight, so PQ signers never showed up in the approval status response.

Why required

These are correctness/security gaps introduced when PQ multisig support (pq_auth_sig) was added: the fee, re-verification optimization, and approval-status reporting paths weren't updated to match the combined ECDSA+PQ signature semantics used everywhere else (e.g. TransactionCapsule.validateSignature, TransactionUtil.getTransactionSignWeight).

Tests

  • ManagerTest: consumeMultiSignFeePqOnlyCharged, consumeMultiSignFeeHybridCharged, consumeMultiSignFeeSingleEcdsaNotCharged, consumeMultiSignFeeSinglePqNotCharged, getVerifyTxsPqAuthSigContentDifferRequiresReverify, getVerifyTxsPqAuthSigCountDifferRequiresReverify, getVerifyTxsPqAuthSigIdenticalSkipsReverify
  • WalletTest: testApprovedListPqOnlySigner, testApprovedListHybridEcdsaAndPq, testApprovedListPqNotActivated
  • All three suites pass locally (ManagerTest 42/42, WalletTest 51/51 with 1 pre-existing unrelated skip, ProposalUtilTest 5/5).

Follow up

None.

Extra details

Also wraps two pre-existing >100-character lines in ProposalUtilTest (unrelated to this fix) to keep checkstyleTest passing.


Summary by cubic

Count pq_auth_sig with ECDSA for multisign fee, re-verify checks, and approved lists. Fixes a verification bypass and makes PQ-only and hybrid multisig work as expected.

  • Bug Fixes

    • Multi-sign fee: charge when total signatures (ECDSA + PQ) > 1 in Manager.consumeMultiSignFee.
    • Re-verify: Manager.isSameSig also checks PQ signature count and content to avoid skipping verification when txId matches but pq_auth_sig differs.
    • Approved list: Wallet.getTransactionApprovedList merges ECDSA and PQ signers and rejects pq_auth_sig when no PQ scheme is activated.
  • Refactors

    • Tests: made Manager.isSameSig @VisibleForTesting and added focused unit tests; consolidated getVerifyTxs PQ cases into a helper and hardened the inactive-PQ test by disabling all PQ schemes and asserting the global gate.

Written for commit 9094a1b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added support for post-quantum cryptographic signatures in transactions alongside traditional signature types.
  • Bug Fixes

    • Fixed multi-signature fee calculation to properly account for both cryptographic signature types.
    • Improved transaction signature matching for mixed-signature scenarios.
  • Tests

    • Added comprehensive test coverage for post-quantum signature validation, including single post-quantum, hybrid, and activation verification scenarios.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Wallet.getTransactionApprovedList is extended to validate post-quantum (pqAuthSig) signatures into a shared approve list alongside legacy ECDSA signatures. Manager.consumeMultiSignFee adds PQ auth sig counts to the multi-sign fee threshold check, and Manager.isSameSig gains PQ sig count and content equality checks. Tests cover all three paths.

Changes

Post-Quantum Signature Integration

Layer / File(s) Summary
PQ support in getTransactionApprovedList
framework/src/main/java/org/tron/core/Wallet.java
approveList is created before both signature branches; ECDSA validation runs when signatureCount > 0; PQ validation runs when pqAuthSigCount > 0 and isAnyPqSchemeAllowed(); addAllApprovedList is called once after both paths.
PQ-aware multi-sign fee and isSameSig
framework/src/main/java/org/tron/core/db/Manager.java
consumeMultiSignFee charges the fee when signatureCount + pqAuthSigCount > 1; isSameSig short-circuits on PQ count mismatch and compares PQ auth signature bytes when ECDSA signatures match.
WalletTest PQ approved list tests
framework/src/test/java/org/tron/core/WalletTest.java
Adds PQ/protobuf imports, txIdOf hash helper, and buildApprovedListTransferTx; three new tests assert PQ-only success, hybrid ECDSA+PQ success, and PQ rejection when no scheme is activated.
ManagerTest PQ fee and isSameSig tests
framework/src/test/java/org/tron/core/db/ManagerTest.java, framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java
Adds dummyPqAuthSig and transaction builder helpers; four tests cover consumeMultiSignFee (charged and not-charged cases); three tests cover getVerifyTxs for PQ content mismatch, count mismatch, and identical-sig skip paths. ProposalUtilTest has cosmetic hardForkTime expression reformatting only.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hop, hop, through quantum gates so bright,
Two signature flavors now share the light.
ECDSA and PQ in one approve list,
Multi-sign fees counted, none are missed.
The isSameSig checks each byte with care—
Post-quantum bunnies are everywhere! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately describes the main fix: counting PQ auth signatures in multisign fee calculations and reverification checks.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/pq_multifee

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bladehan1

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
framework/src/test/java/org/tron/core/db/ManagerTest.java (1)

255-260: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make charged-fee assertions non-vacuous.

These “charged” tests can still pass if multiSignFee is 0, even when charging is accidentally skipped. Pin a positive fee in test setup (and restore it) so the condition regression is always detected.

Also applies to: 276-281

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/src/test/java/org/tron/core/db/ManagerTest.java` around lines 255 -
260, The assertions for multiSignFee charging at lines 255-260 can vacuously
pass if the fee value is zero, even when the charging logic is broken. Before
running the test that calls consumeMultiSignFee, explicitly set the multiSignFee
to a positive hardcoded value using
dbManager.getDynamicPropertiesStore().setMultiSignFee() in the test setup, and
ensure it is restored to its original value after the test completes. This
ensures the balance reduction and receipt assertions will actually validate that
charging occurred rather than passing when fee equals zero. Apply the same
pattern to the other charged-fee test referenced at lines 276-281.
framework/src/test/java/org/tron/core/WalletTest.java (1)

1675-1679: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the actual hybrid signer addresses.

Line 1678 only checks the count, so this test could still pass if the PQ branch appended the wrong address. Assert both ECDSA and PQ approvals are present.

Suggested assertion tightening
     assertEquals(GrpcAPI.TransactionApprovedList.Result.response_code.SUCCESS,
         reply.getResult().getCode());
     assertEquals(2, reply.getApprovedListCount());
+    Assert.assertTrue(reply.getApprovedListList()
+        .contains(ByteString.copyFrom(ecKey.getAddress())));
+    Assert.assertTrue(reply.getApprovedListList()
+        .contains(ByteString.copyFrom(pqSignerAddr)));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/src/test/java/org/tron/core/WalletTest.java` around lines 1675 -
1679, The test method currently only asserts that the approved list count equals
2, but does not validate the actual addresses contained in that list. After the
assertEquals check for getApprovedListCount() returning 2, add additional
assertions to verify that both the ECDSA and PQ hybrid signer addresses are
present in the reply's approved list by iterating through the approved list
items and confirming the expected addresses match the actual returned addresses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/src/test/java/org/tron/core/WalletTest.java`:
- Around line 1682-1683: The test method testApprovedListPqNotActivated()
currently only disables the FN_DSA_512 scheme via saveAllowFnDsa512(0L), but
since Wallet gates on isAnyPqSchemeAllowed(), other enabled schemes like
ML_DSA_44 will cause the test to not properly exercise the "no post-quantum
scheme activated" path. Disable all registered post-quantum schemes in this test
by calling the appropriate save methods on
chainBaseManager.getDynamicPropertiesStore() for each PQ scheme (not just
FN_DSA_512) to ensure no post-quantum scheme is enabled before testing the
inactive gate scenario.

---

Nitpick comments:
In `@framework/src/test/java/org/tron/core/db/ManagerTest.java`:
- Around line 255-260: The assertions for multiSignFee charging at lines 255-260
can vacuously pass if the fee value is zero, even when the charging logic is
broken. Before running the test that calls consumeMultiSignFee, explicitly set
the multiSignFee to a positive hardcoded value using
dbManager.getDynamicPropertiesStore().setMultiSignFee() in the test setup, and
ensure it is restored to its original value after the test completes. This
ensures the balance reduction and receipt assertions will actually validate that
charging occurred rather than passing when fee equals zero. Apply the same
pattern to the other charged-fee test referenced at lines 276-281.

In `@framework/src/test/java/org/tron/core/WalletTest.java`:
- Around line 1675-1679: The test method currently only asserts that the
approved list count equals 2, but does not validate the actual addresses
contained in that list. After the assertEquals check for getApprovedListCount()
returning 2, add additional assertions to verify that both the ECDSA and PQ
hybrid signer addresses are present in the reply's approved list by iterating
through the approved list items and confirming the expected addresses match the
actual returned addresses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 175bc10d-5126-40af-9d14-78bd040626d1

📥 Commits

Reviewing files that changed from the base of the PR and between c2d4477 and f0431d9.

📒 Files selected for processing (5)
  • framework/src/main/java/org/tron/core/Wallet.java
  • framework/src/main/java/org/tron/core/db/Manager.java
  • framework/src/test/java/org/tron/core/WalletTest.java
  • framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java
  • framework/src/test/java/org/tron/core/db/ManagerTest.java

Comment on lines +1682 to +1683
public void testApprovedListPqNotActivated() throws Exception {
chainBaseManager.getDynamicPropertiesStore().saveAllowFnDsa512(0L);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable every PQ scheme for the inactive-gate test.

Line 1683 disables only FN_DSA_512, but Wallet gates on isAnyPqSchemeAllowed(). If another registered scheme such as ML_DSA_44 is enabled by default or a prior test, this no longer exercises the “no post-quantum scheme is activated” path.

Suggested fix
   public void testApprovedListPqNotActivated() throws Exception {
     chainBaseManager.getDynamicPropertiesStore().saveAllowFnDsa512(0L);
+    chainBaseManager.getDynamicPropertiesStore().saveAllowMlDsa44(0L);
     FNDSA512 kp = new FNDSA512();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/src/test/java/org/tron/core/WalletTest.java` around lines 1682 -
1683, The test method testApprovedListPqNotActivated() currently only disables
the FN_DSA_512 scheme via saveAllowFnDsa512(0L), but since Wallet gates on
isAnyPqSchemeAllowed(), other enabled schemes like ML_DSA_44 will cause the test
to not properly exercise the "no post-quantum scheme activated" path. Disable
all registered post-quantum schemes in this test by calling the appropriate save
methods on chainBaseManager.getDynamicPropertiesStore() for each PQ scheme (not
just FN_DSA_512) to ensure no post-quantum scheme is enabled before testing the
inactive gate scenario.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 5 files

Re-trigger cubic

PQ multisig support added pq_auth_sig but several signature-count
checks kept summing ECDSA signatures only:

- consumeMultiSignFee only checked getSignatureCount(), so PQ-only and
  hybrid ECDSA+PQ multisig transactions escaped the multisign fee.
- isSameSig (used by getVerifyTxs to skip re-verifying transactions
  already verified in the pending pool) compared only the ECDSA
  signature list, so a block transaction with a substituted pq_auth_sig
  but the same txId (txId hashes raw_data only) could be wrongly marked
  verified and skip real signature validation.
- Wallet.getTransactionApprovedList built the approved list from ECDSA
  signatures only, missing the pq_auth_sig branch already present in
  its sibling TransactionUtil.getTransactionSignWeight, so PQ signers
  never showed up in the approval status.

Add unit tests covering PQ-only, hybrid, and regression cases for all
three paths. Also wraps two pre-existing >100-char lines in
ProposalUtilTest to keep checkstyleTest passing.
@bladehan1 bladehan1 force-pushed the feature/pq_multifee branch from f0431d9 to 9dcb21f Compare June 23, 2026 07:51
@bladehan1 bladehan1 changed the title fix(db): count pq_auth_sig in multisign fee and reverify checks fix(framework): count pq_auth_sig in multisign fee and reverify checks Jun 23, 2026
Collapse the three getVerifyTxs pq_auth_sig tests onto one shared
helper instead of repeating the same transaction/pending-pool/
try-finally scaffolding in each.

testApprovedListPqNotActivated only disabled FN_DSA_512, but Wallet
gates on isAnyPqSchemeAllowed() across every registered scheme. Disable
ML_DSA_44 too and assert isAnyPqSchemeAllowed() is false before
building the transaction, so the test still exercises the intended
path if another scheme is enabled by default or by a prior test.
Mark isSameSig package-private with @VisibleForTesting (precedented in
this codebase by TransactionCapsule.logSlowSigVerify and several other
@VisibleForTesting members) instead of exercising it only indirectly
through getVerifyTxs's pending-pool/BlockCapsule scaffolding.

Add 5 direct isSameSig tests covering null, pq_auth_sig content/count
mismatch, identical pq_auth_sig, and the exact bug scenario (matching
ECDSA list masking a differing pq_auth_sig list). Keep one
getVerifyTxs-level integration test (getVerifyTxsClosesPqAuthSigBypass)
as proof the fix is actually wired into the real bypass path, since a
unit test of isSameSig alone can't prove that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant