Skip to content

fix: reject invalid extended masternode net info#7385

Draft
thepastaclaw wants to merge 2 commits into
dashpay:developfrom
thepastaclaw:fix/extnetinfo-validation
Draft

fix: reject invalid extended masternode net info#7385
thepastaclaw wants to merge 2 commits into
dashpay:developfrom
thepastaclaw:fix/extnetinfo-validation

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jun 27, 2026

Copy link
Copy Markdown

Summary

Issue being fixed or feature implemented

Extended masternode net info loaded from serialized transaction payloads can
contain semantic states that the in-memory builder rejects during normal
construction. Validation should reject those states explicitly instead of
relying on internal assertions.

What was done?

  • Added a validation backstop for per-purpose extended net info entry limits.
  • Mapped semantically invalid extended net info states to stable transaction
    validation failures.
  • Added primitive coverage for deserialized duplicate entries, oversized
    per-purpose lists, and domain entries under non-HTTPS purposes.
  • Added transaction-level CheckProRegTx regression coverage for the same
    invalid deserialized net info states.

How Has This Been Tested?

Tested locally on macOS:

git diff HEAD~1..HEAD --check
make -j6 src/test/test_dash
./src/test/test_dash --run_test=evo_netinfo_tests/extnetinfo_validate_deser
./src/test/test_dash --run_test=evo_netinfo_tests
./src/test/test_dash --run_test=evo_dip3_activation_tests/proregtx_rejects_invalid_deserialized_extnetinfo
./src/test/test_dash --run_test=evo_dip3_activation_tests

Pre-PR automated code review was also run for
upstream/develop..fix/extnetinfo-validation after the transaction-level
coverage was added and returned ship.

Breaking Changes

None.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone
    (for repository code-owners and collaborators only)

`ExtNetInfo::Validate()` can return `BadInput`, `Duplicate`, and
`MaxLimit` for states reachable via deserialization that bypass the
in-memory builder's per-entry checks. `CheckService` previously
asserted on those statuses; map them to graceful `TX_BAD_SPECIAL`
rejections with stable reason strings instead.

Also enforce the per-purpose `MAX_ENTRIES_EXTNETINFO` cap inside
`ExtNetInfo::Validate()` so an oversized deserialized list is
rejected rather than silently accepted.

Adds unit coverage for the three deserialization paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

⚠️ Potential Merge Conflicts Detected

This PR has potential conflicts with the following open PRs:

Please coordinate with the authors of these PRs to avoid merge conflicts.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 27, 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.

@thepastaclaw

thepastaclaw commented Jun 27, 2026

Copy link
Copy Markdown
Author

✅ Review complete (commit d818922)

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

ExtNetInfo::Validate() now rejects per-purpose lists that exceed MAX_ENTRIES_EXTNETINFO with NetInfoStatus::MaxLimit. CheckService now converts BadInput, Duplicate, and MaxLimit statuses into explicit TX_BAD_SPECIAL rejections with specific reason strings. New tests deserialize ExtNetInfo data from streams and verify duplicate, oversized, and invalid-purpose cases, including ProRegTx rejection handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • knst
  • UdjinM6
🚥 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
Title check ✅ Passed The title clearly matches the main change: rejecting invalid extended masternode net info.
Description check ✅ Passed The description accurately summarizes the validation fixes and added tests in this PR.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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.

🧹 Nitpick comments (1)
src/test/evo_netinfo_tests.cpp (1)

500-561: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for the CheckService reject mapping.

These cases validate ExtNetInfo::Validate(), but the behavior change in src/evo/specialtxman.cpp is the TX_BAD_SPECIAL/reason-string translation. A typo or missing case there would still pass this suite, so please add one validation test that feeds deserialized bad netinfo through the provider-tx path and asserts the new reject reason.

🤖 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 `@src/test/evo_netinfo_tests.cpp` around lines 500 - 561, The current
`extnetinfo_validate_deser` test only checks `ExtNetInfo::Validate()` and does
not cover the `CheckService` reject mapping in the provider-tx path. Add one
focused test that deserializes an invalid `ExtNetInfo`, sends it through the
special-tx validation flow in `specialtxman`/`CheckService`, and asserts the
`TX_BAD_SPECIAL` reject reason string. Use the existing `ExtNetInfo`,
`Validate()`, and provider-tx validation symbols to locate the right path.
🤖 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.

Nitpick comments:
In `@src/test/evo_netinfo_tests.cpp`:
- Around line 500-561: The current `extnetinfo_validate_deser` test only checks
`ExtNetInfo::Validate()` and does not cover the `CheckService` reject mapping in
the provider-tx path. Add one focused test that deserializes an invalid
`ExtNetInfo`, sends it through the special-tx validation flow in
`specialtxman`/`CheckService`, and asserts the `TX_BAD_SPECIAL` reject reason
string. Use the existing `ExtNetInfo`, `Validate()`, and provider-tx validation
symbols to locate the right path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0bd68d96-c614-44c3-a829-1adc6c815bee

📥 Commits

Reviewing files that changed from the base of the PR and between ebfdb8b and 5ba9ace.

📒 Files selected for processing (3)
  • src/evo/netinfo.cpp
  • src/evo/specialtxman.cpp
  • src/test/evo_netinfo_tests.cpp

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Code Review

Small, well-scoped consensus-safety fix. Three previously unreachable assert(false) arms in CheckService are converted into stable TX_BAD_SPECIAL rejections, and ExtNetInfo::Validate() now enforces the per-purpose MAX_ENTRIES_EXTNETINFO cap that the in-memory builder enforces at insertion. Unit coverage exercises the three deserialization paths (duplicate entries, oversized list, domain on non-HTTPS purpose). No in-scope issues.

Note: posted as a COMMENT review because GitHub does not allow approving my own PR.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 27, 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.

🧹 Nitpick comments (1)
src/test/evo_deterministicmns_tests.cpp (1)

724-733: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Also assert TX_BAD_SPECIAL in this helper.

Right now this only checks state.GetRejectReason(). A regression that keeps the same string but changes the validation class would still pass, even though this PR is also locking down the TX_BAD_SPECIAL mapping.

Suggested test tightening
     auto check_reject_reason = [&](std::shared_ptr<NetInfoInterface> net_info, const std::string& reject_reason) {
         TxValidationState state;
         {
             LOCK(cs_main);
             BOOST_CHECK(!CheckProRegTx(BuildExtNetInfoProRegTx(std::move(net_info)), chainman.ActiveChain().Tip(),
                                        dmnman, chainman.ActiveChainstate().CoinsTip(), chainman, state,
                                        /*check_sigs=*/false));
         }
+        BOOST_CHECK_EQUAL(state.GetResult(), TxValidationResult::TX_BAD_SPECIAL);
         BOOST_CHECK_EQUAL(state.GetRejectReason(), reject_reason);
     };
🤖 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 `@src/test/evo_deterministicmns_tests.cpp` around lines 724 - 733, The test
helper check_reject_reason currently only verifies state.GetRejectReason(), so
it can miss regressions where the reject string stays the same but the
validation class changes. Update this helper in evo_deterministicmns_tests.cpp
to also assert the TxValidationState maps to TX_BAD_SPECIAL, using the existing
state object from CheckProRegTx alongside the reject reason check.
🤖 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.

Nitpick comments:
In `@src/test/evo_deterministicmns_tests.cpp`:
- Around line 724-733: The test helper check_reject_reason currently only
verifies state.GetRejectReason(), so it can miss regressions where the reject
string stays the same but the validation class changes. Update this helper in
evo_deterministicmns_tests.cpp to also assert the TxValidationState maps to
TX_BAD_SPECIAL, using the existing state object from CheckProRegTx alongside the
reject reason check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c42647b0-4c12-4901-9713-b4f014910ca6

📥 Commits

Reviewing files that changed from the base of the PR and between 5ba9ace and 0f5fd82.

📒 Files selected for processing (1)
  • src/test/evo_deterministicmns_tests.cpp

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Code Review

Test-only delta (0f5fd825) adds CheckProRegTx-level regression coverage for the three new TX_BAD_SPECIAL reject paths introduced by 5ba9ace (BadInput, Duplicate, MaxLimit deserialization escapes). Implementation and test coverage are correct and in scope. Only a minor test-hardening nit from CodeRabbit is worth surfacing; no carried-forward prior findings (prior PastaClaw review was clean).

💬 1 nitpick(s)

Note: GitHub does not allow me to approve my own PR, so this is posted as a COMMENT while preserving the verified nonblocking finding.

Comment on lines +724 to +733
auto check_reject_reason = [&](std::shared_ptr<NetInfoInterface> net_info, const std::string& reject_reason) {
TxValidationState state;
{
LOCK(cs_main);
BOOST_CHECK(!CheckProRegTx(BuildExtNetInfoProRegTx(std::move(net_info)), chainman.ActiveChain().Tip(),
dmnman, chainman.ActiveChainstate().CoinsTip(), chainman, state,
/*check_sigs=*/false));
}
BOOST_CHECK_EQUAL(state.GetRejectReason(), reject_reason);
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

💬 Nitpick: Also assert TxValidationResult::TX_BAD_SPECIAL in check_reject_reason

The fix in 5ba9ace deliberately maps BadInput/Duplicate/MaxLimit to TX_BAD_SPECIAL with stable reason strings. The new helper only checks the reject reason, so a future regression that keeps the same reason string but degrades the result class (e.g. TX_CONSENSUS or TX_RESULT_UNSET) would slip through. Since this test exists specifically to lock down both halves of that mapping, adding a result-class assertion costs one line and tightens the guarantee.

Suggested change
auto check_reject_reason = [&](std::shared_ptr<NetInfoInterface> net_info, const std::string& reject_reason) {
TxValidationState state;
{
LOCK(cs_main);
BOOST_CHECK(!CheckProRegTx(BuildExtNetInfoProRegTx(std::move(net_info)), chainman.ActiveChain().Tip(),
dmnman, chainman.ActiveChainstate().CoinsTip(), chainman, state,
/*check_sigs=*/false));
}
BOOST_CHECK_EQUAL(state.GetRejectReason(), reject_reason);
};
auto check_reject_reason = [&](std::shared_ptr<NetInfoInterface> net_info, const std::string& reject_reason) {
TxValidationState state;
{
LOCK(cs_main);
BOOST_CHECK(!CheckProRegTx(BuildExtNetInfoProRegTx(std::move(net_info)), chainman.ActiveChain().Tip(),
dmnman, chainman.ActiveChainstate().CoinsTip(), chainman, state,
/*check_sigs=*/false));
}
BOOST_CHECK_EQUAL(state.GetResult(), TxValidationResult::TX_BAD_SPECIAL);
BOOST_CHECK_EQUAL(state.GetRejectReason(), reject_reason);
};

source: ['coderabbit']

@thepastaclaw thepastaclaw force-pushed the fix/extnetinfo-validation branch from 0f5fd82 to d818922 Compare June 27, 2026 18:31
@thepastaclaw

Copy link
Copy Markdown
Author

Addressed the latest CodeRabbit test-hardening nit by adding the TX_BAD_SPECIAL result assertion to check_reject_reason and amended the test commit.

Validation:

  • git diff --check
  • make -C src -j6 test/test_dash
  • ./src/test/test_dash --run_test=evo_dip3_activation_tests/proregtx_rejects_invalid_deserialized_extnetinfo

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Code Review

Cumulative review of PR #7385 at head d818922. The single prior nitpick (assert TxValidationResult::TX_BAD_SPECIAL in check_reject_reason) is resolved at src/test/evo_deterministicmns_tests.cpp:732. No new findings in the latest delta; the change is a one-line test hardening that strengthens coverage of deserialized ExtNetInfo rejection paths.

Note: GitHub does not allow me to approve my own PR, so this is posted as a COMMENT while preserving the verified clean result.

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