fix!: use the same version along all protx special transactions#7302
fix!: use the same version along all protx special transactions#7302knst wants to merge 16 commits into
Conversation
|
✅ Review complete (commit 72bb63b) |
✅ No Merge Conflicts DetectedThis PR currently has no conflicts with other open PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed4d9c63ea
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ptx.nVersion = DeploymentToProtxVersion(WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()), chainman, | ||
| /*is_basic_override=*/!use_legacy); |
There was a problem hiding this comment.
Clamp update_registrar version for legacy masternodes
When DEPLOYMENT_V24 is active, this assignment makes protx update_registrar default to version 3, but legacy masternodes (dmn->pdmnState->nVersion == LegacyBLS) are still required by IsVersionChangeValid in src/evo/specialtxman.cpp to move to version 2 before any higher version. As a result, the RPC now constructs transactions that fail with bad-protx-version-upgrade for legacy nodes, which breaks the legacy→basic upgrade path (and related registrar updates) via the standard RPC. update_service and revoke already avoid this with a BasicBLS clamp, so update_registrar is now inconsistent and regresses behavior introduced by this commit.
Useful? React with 👍 / 👎.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (16)
💤 Files with no reviewable changes (1)
✅ Files skipped from review due to trivial changes (4)
🚧 Files skipped from review as they are similar to previous changes (9)
WalkthroughThis PR restructures ProTx version handling by introducing a shared Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant RPC as RPC (protx_register/update)
participant Validation as validation.cpp
participant SpecialTxMan as specialtxman.cpp
participant ProviderTx as CProRegTx/CProUpRegTx/CProUpServTx/CProUpRevTx
RPC->>Validation: DeploymentToProtxVersion(pindexPrev, chainman, override)
Validation-->>RPC: nVersion
RPC->>ProviderTx: construct payload with nVersion
RPC->>SpecialTxMan: submit transaction
SpecialTxMan->>SpecialTxMan: GetValidatedPayload<ProTx>(tx, pindexPrev, chainman, state)
SpecialTxMan->>Validation: DeploymentToProtxVersion(pindexPrev, chainman)
Validation-->>SpecialTxMan: max allowed version
SpecialTxMan->>ProviderTx: IsTriviallyValid(state)
ProviderTx-->>SpecialTxMan: valid/invalid + reject reason
SpecialTxMan-->>RPC: accepted payload or bad-protx-version error
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
ed4d9c6 to
f94f7cc
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/evo/providertx.h (1)
95-99: ⚡ Quick win
GetValidatedPayload<T>is not full validation.These comments overstate the helper’s contract. As implemented in
src/evo/specialtxman.cpp,GetValidatedPayloadonly covers payload decoding, deployment-gated version bounds, andIsTriviallyValid(...); callers still needCheckPro*Txfor collateral, masternode-list, signature, input-hash, and version-transition checks. Please reword this so future call sites don’t treat the helper as sufficient consensus validation.Also applies to: 160-164, 211-215, 265-269
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/evo/providertx.h` around lines 95 - 99, The comment for IsTriviallyValid overstresses GetValidatedPayload<T> — update the wording to clearly state that GetValidatedPayload<T> only performs payload decoding, deployment-gated version bounds checks and calls IsTriviallyValid, and that callers must still run full consensus checks (e.g., CheckProRegTx / CheckProUpServTx or other CheckPro*Tx functions) to validate collateral, masternode-list state, signatures, input-hash and version-transition rules; change the docstring near IsTriviallyValid and the similar comments at the other three locations to explicitly list those remaining checks and not present GetValidatedPayload<T> as full validation.src/test/evo_trivialvalidation.cpp (1)
61-64: ⚡ Quick winAdd a post-v24 fixture path here.
This runner still collapses the matrix to
"legacy"vs"basic", so none of the new version-3 behavior introduced in this PR gets exercised. Please add an"extaddr"/post-v24 branch and vectors for at leastCProUpRegTxandCProUpRevTx.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/evo_trivialvalidation.cpp` around lines 61 - 64, The test currently only selects between "basic" and "legacy" via test[2].get_str() when setting pindexPrev, so post-v24/extaddr vectors aren't exercised; update the selection logic (replace the two-way ternary around pindexPrev or add an if/else/switch) to handle a third value "extaddr" and pick an appropriate CBlockIndex from chainman.ActiveChain() for the post-v24 path, and add corresponding test vectors for CProUpRegTx and CProUpRevTx in the test matrix so the "extaddr" branch is executed during the trivial validation runner.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/evo/specialtxman.h`:
- Around line 101-103: The template function GetValidatedPayload is defined in a
.cpp which will cause linker errors because the tests instantiate it for
concrete types; fix by either moving the full template definition into the
header where it's declared (so the compiler can instantiate for CProRegTx,
CProUpServTx, CProUpRegTx, CProUpRevTx) or, if you keep the definition in
specialtxman.cpp, add explicit template instantiations for
GetValidatedPayload<CProRegTx>, GetValidatedPayload<CProUpServTx>,
GetValidatedPayload<CProUpRegTx>, and GetValidatedPayload<CProUpRevTx> in that
.cpp so the linker sees the generated symbols.
---
Nitpick comments:
In `@src/evo/providertx.h`:
- Around line 95-99: The comment for IsTriviallyValid overstresses
GetValidatedPayload<T> — update the wording to clearly state that
GetValidatedPayload<T> only performs payload decoding, deployment-gated version
bounds checks and calls IsTriviallyValid, and that callers must still run full
consensus checks (e.g., CheckProRegTx / CheckProUpServTx or other CheckPro*Tx
functions) to validate collateral, masternode-list state, signatures, input-hash
and version-transition rules; change the docstring near IsTriviallyValid and the
similar comments at the other three locations to explicitly list those remaining
checks and not present GetValidatedPayload<T> as full validation.
In `@src/test/evo_trivialvalidation.cpp`:
- Around line 61-64: The test currently only selects between "basic" and
"legacy" via test[2].get_str() when setting pindexPrev, so post-v24/extaddr
vectors aren't exercised; update the selection logic (replace the two-way
ternary around pindexPrev or add an if/else/switch) to handle a third value
"extaddr" and pick an appropriate CBlockIndex from chainman.ActiveChain() for
the post-v24 path, and add corresponding test vectors for CProUpRegTx and
CProUpRevTx in the test matrix so the "extaddr" branch is executed during the
trivial validation runner.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e2a718c8-ebeb-4c10-8f6b-eedbc74713c3
📒 Files selected for processing (17)
src/evo/deterministicmns.hsrc/evo/dmnstate.hsrc/evo/netinfo.cppsrc/evo/providertx.cppsrc/evo/providertx.hsrc/evo/simplifiedmns.hsrc/evo/smldiff.hsrc/evo/specialtxman.cppsrc/evo/specialtxman.hsrc/evo/types.hsrc/llmq/commitment.cppsrc/rpc/evo.cppsrc/test/data/trivially_invalid.jsonsrc/test/evo_trivialvalidation.cppsrc/validation.cppsrc/validation.htest/lint/lint-circular-dependencies.py
💤 Files with no reviewable changes (3)
- src/evo/dmnstate.h
- src/evo/smldiff.h
- src/evo/deterministicmns.h
✅ Files skipped from review due to trivial changes (5)
- src/llmq/commitment.cpp
- src/evo/types.h
- src/test/data/trivially_invalid.json
- test/lint/lint-circular-dependencies.py
- src/evo/simplifiedmns.h
🚧 Files skipped from review as they are similar to previous changes (5)
- src/evo/netinfo.cpp
- src/validation.cpp
- src/validation.h
- src/evo/specialtxman.cpp
- src/evo/providertx.cpp
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The refactor is sound but introduces a regression in the non-legacy protx update_registrar RPC: after V24 activates, it produces a v3 ProUpRegTx that consensus rejects when the masternode is still in LegacyBLS state, and also trips a CHECK_NONFATAL when the caller reuses the existing legacy operator key. Sibling RPCs (update_service, revoke) have the correct BasicBLS clamp and update_registrar should match. Test coverage for the newly-permitted v3 ProUpRegTx/ProUpRevTx path is also missing.
Reviewed commit: f94f7cc
🔴 1 blocking | 🟡 1 suggestion(s)
1 additional finding
🟡 suggestion: No regression coverage for the newly-permitted v3 ProUpRegTx/ProUpRevTx path
src/test/evo_trivialvalidation.cpp (lines 60-64)
This PR removes the bad-protx-version-tx-type consensus check that previously rejected CProUpRegTx/CProUpRevTx at version 3, and routes the test harness through the same GetValidatedPayload path consensus uses. However, the harness still only accepts "basic" and "legacy" vectors and leaves a TODO for extended addresses, so none of the new acceptance rules are actually exercised at a post-V24 height. Adding a v3 ProUpRegTx/ProUpRevTx vector validated against a post-V24 pindexPrev would lock in the new contract and protect against accidental re-introduction of the dropped check.
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/rpc/evo.cpp`:
- [BLOCKING] lines 1139-1140: `protx update_registrar` produces an invalid v3 payload for legacy-state masternodes after V24
After V24 activates, `DeploymentToProtxVersion(tip, chainman, /*is_basic_override=*/!use_legacy)` returns `ProTxVersion::ExtAddr` (3) for the non-legacy path. Previously, `GetMaxFromDeployment<CProUpRegTx>` capped this at `BasicBLS` (2) for registrar updates. Two consequences:
1. If the underlying masternode is still at state version `LegacyBLS` (1), `IsVersionChangeValid` (specialtxman.cpp:907-909) rejects the v3 jump with `bad-protx-version-upgrade`. There is no longer a working non-legacy RPC path to migrate a legacy masternode via `update_registrar`.
2. If the caller leaves the operator key unchanged, `ptx.pubKeyOperator` is reused from `dmn->pdmnState`, which is legacy. The `CHECK_NONFATAL(ptx.pubKeyOperator.IsLegacy() == (ptx.nVersion == ProTxVersion::LegacyBLS))` at line 1159 then trips (true == false).
The sibling wrappers `protx update_service` (1014-1018) and `protx revoke` (1268-1272) already clamp to BasicBLS in this case. `update_registrar` needs the same clamp. Real-world impact is limited because V24 is `NEVER_ACTIVE` on mainnet/testnet, but the regression is real and silently breaks devnet/regtest setups.
In `src/test/evo_trivialvalidation.cpp`:
- [SUGGESTION] lines 60-64: No regression coverage for the newly-permitted v3 ProUpRegTx/ProUpRevTx path
This PR removes the `bad-protx-version-tx-type` consensus check that previously rejected `CProUpRegTx`/`CProUpRevTx` at version 3, and routes the test harness through the same `GetValidatedPayload` path consensus uses. However, the harness still only accepts `"basic"` and `"legacy"` vectors and leaves a TODO for extended addresses, so none of the new acceptance rules are actually exercised at a post-V24 height. Adding a v3 ProUpRegTx/ProUpRevTx vector validated against a post-V24 `pindexPrev` would lock in the new contract and protect against accidental re-introduction of the dropped check.
| ptx.nVersion = DeploymentToProtxVersion(WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()), chainman, | ||
| /*is_basic_override=*/!use_legacy); |
There was a problem hiding this comment.
🔴 Blocking: protx update_registrar produces an invalid v3 payload for legacy-state masternodes after V24
After V24 activates, DeploymentToProtxVersion(tip, chainman, /*is_basic_override=*/!use_legacy) returns ProTxVersion::ExtAddr (3) for the non-legacy path. Previously, GetMaxFromDeployment<CProUpRegTx> capped this at BasicBLS (2) for registrar updates. Two consequences:
-
If the underlying masternode is still at state version
LegacyBLS(1),IsVersionChangeValid(specialtxman.cpp:907-909) rejects the v3 jump withbad-protx-version-upgrade. There is no longer a working non-legacy RPC path to migrate a legacy masternode viaupdate_registrar. -
If the caller leaves the operator key unchanged,
ptx.pubKeyOperatoris reused fromdmn->pdmnState, which is legacy. TheCHECK_NONFATAL(ptx.pubKeyOperator.IsLegacy() == (ptx.nVersion == ProTxVersion::LegacyBLS))at line 1159 then trips (true == false).
The sibling wrappers protx update_service (1014-1018) and protx revoke (1268-1272) already clamp to BasicBLS in this case. update_registrar needs the same clamp. Real-world impact is limited because V24 is NEVER_ACTIVE on mainnet/testnet, but the regression is real and silently breaks devnet/regtest setups.
source: ['claude', 'codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/rpc/evo.cpp`:
- [BLOCKING] lines 1139-1140: `protx update_registrar` produces an invalid v3 payload for legacy-state masternodes after V24
After V24 activates, `DeploymentToProtxVersion(tip, chainman, /*is_basic_override=*/!use_legacy)` returns `ProTxVersion::ExtAddr` (3) for the non-legacy path. Previously, `GetMaxFromDeployment<CProUpRegTx>` capped this at `BasicBLS` (2) for registrar updates. Two consequences:
1. If the underlying masternode is still at state version `LegacyBLS` (1), `IsVersionChangeValid` (specialtxman.cpp:907-909) rejects the v3 jump with `bad-protx-version-upgrade`. There is no longer a working non-legacy RPC path to migrate a legacy masternode via `update_registrar`.
2. If the caller leaves the operator key unchanged, `ptx.pubKeyOperator` is reused from `dmn->pdmnState`, which is legacy. The `CHECK_NONFATAL(ptx.pubKeyOperator.IsLegacy() == (ptx.nVersion == ProTxVersion::LegacyBLS))` at line 1159 then trips (true == false).
The sibling wrappers `protx update_service` (1014-1018) and `protx revoke` (1268-1272) already clamp to BasicBLS in this case. `update_registrar` needs the same clamp. Real-world impact is limited because V24 is `NEVER_ACTIVE` on mainnet/testnet, but the regression is real and silently breaks devnet/regtest setups.
kwvg
left a comment
There was a problem hiding this comment.
Concept NACK
From commit d86d571
CProRegTx and CProUpServTx used to be the only type of protx that have
a different version. It is theoretically acceptable in assumption that
there is no new features or version will ever be introduced for protx
special transaction.
The versioning system made an assumption that any change in ProTx structures will affect all fields uniformly, leaving certain fields v2 and others v3 allows us to prevent unexpected upgrades based on changes to fields that were never modified and thus prevents the propagation of serialisation directives based on fields never changed.
Even if the RPC sets the version we intend, removing a way to distinguish what txType is allowed to set the version at the consensus level means a patch in RPC could allow corruption of the masternode list as it'll see a v3 transaction, assume v3 serialization and then on client restart, cannot recognise the bytes on disk as it's v2 bytes but v3 version.
A theoretical v4 would follow the same pattern, set the version where the fields could be updated (always at creation OR for existing nodes, when the relevant ProTx is submitted) and then version-solve accordingly. The current system doesn't block that.
Let's assume that revocation is now at v4, future rules could force that you first upgrade to v3 by submitting a new ProUpRegTx (explicit user action that submits the new ser format) to then use v4 ProUpRevTx (to avoid getting a v2 to v4 not allowed error).
| return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-upgrade"); | ||
| } | ||
|
|
||
| if (tx_type != TRANSACTION_PROVIDER_UPDATE_SERVICE && tx_version == ProTxVersion::ExtAddr) { |
There was a problem hiding this comment.
The reason we have version transition rules is to prevent the node from being classed as ExtAddr without actually opting in to it at creation or by explicitly updating the service.
Especially since once the version is upgraded the serialisation format changes. By classifying all transactions ExtAddr, a revoke transaction could indicate that the node intends to follow ExtAddr serialization when that was a) not the user's intention and b) will cause serialization mismatches which are very dependent on the version (see below)
Lines 78 to 79 in 4a0fd85
|
|
||
|
|
||
| /** | ||
| * This helper does some trivial validations that doesn't depends on collateral and |
There was a problem hiding this comment.
These changes are valuable but must be decoupled from changes in consensus behavior
| /** Get highest permissible ProTx version based on deployment status | ||
| * Note: The override is needed because some RPCs need to use deployment status information for everything *except* | ||
| * the BLS version upgrade since they are specializations for a specific BLS version. This is a one-off. | ||
| * TODO: Resolve this oddity. Consider deprecating legacy BLS-only RPCs so we can remove them eventually. |
There was a problem hiding this comment.
| * TODO: Resolve this oddity. Consider deprecating legacy BLS-only RPCs so we can remove them eventually. |
We can probably drop this TODO since the legacy RPCs won't be going anywhere even after deprecation due to hard requirements in functional tests, we already have network rules to enforce the deprecation at a consensus level but the RPCs themselves remain indispensible.
No, they would not, because:
|
The issue isn't the new node creation path, it's the upgrade path, say
But without at a minimum restricting the upgrade path, a specially crafted transaction could update the expected serialization to v3 with a ProUpRevTx (despite holding no service data but now allowed to mark itself as v3) and then on restart, when reading the masternode list and encountering a v3, find the v2 bytes unrecognizable and emit an error. |
I will write some extra tests; PR is draft temporary |
|
This pull request has conflicts, please rebase. |
f94f7cc to
a016390
Compare
|
This pull request has conflicts, please rebase. |
a016390 to
ff6151b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff6151b1a7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (obj.nVersion >= ProTxVersion::ExtAddr) { | ||
| READWRITE(obj.payouts); |
There was a problem hiding this comment.
Backfill payouts when service updates promote v2 states
Because v3 states now take the payouts branch, a basic v2 masternode upgraded by a post-v24 ProUpServTx loses its owner payee: the service-update path in CSpecialTxProcessor::RebuildListFromBlock can raise nVersion from 2 to 3 while only converting netInfo, and v2 states have an empty payouts vector because they serialized scriptPayout. After that, GetOwnerPayouts(state) returns an empty list, so masternode payment construction omits the owner payout (and callers that use .front() can fail) until a registrar update backfills it. Please migrate scriptPayout to LegacyPayoutAsList when any non-registrar version bump crosses ExtAddr.
Useful? React with 👍 / 👎.
ff6151b to
19464a5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19464a5adb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (obj.nVersion >= ProTxVersion::ExtAddr) { | ||
| READWRITE(obj.payouts); |
There was a problem hiding this comment.
Preserve decoding of existing v3 state snapshots
For nodes that have already written deterministic MN snapshots containing v3 states with the previous encoding, this switch makes CDeterministicMNState read those same dmn_S3 snapshot records as payouts instead of the formerly serialized scriptPayout; DB_LIST_SNAPSHOT is still dmn_S3 in src/evo/deterministicmns.cpp, and GetListForBlockInternal reads it directly before applying diffs. In that upgrade scenario the old script bytes are interpreted as a vector of payout shares, which can fail deserialization or corrupt the reconstructed list, so this needs a snapshot key bump or legacy decoder/migration alongside the format change.
Useful? React with 👍 / 👎.
|
|
||
| CProUpRegTx proTx; | ||
| proTx.nVersion = ProTxVersion::MultiPayout; | ||
| proTx.nVersion = ProTxVersion::ExtAddr; |
There was a problem hiding this comment.
Update the stale legacy-upgrade expectation
With this transaction now using ExtAddr, the test below still expects CheckProUpRegTx to reject it with bad-protx-version-upgrade, but this commit removed that rejection from IsVersionChangeValid and the new functional test explicitly exercises legacy-to-v3 migration as accepted. As a result, the proupreg_v4_on_legacy_rejected Boost test will fail once it reaches the BOOST_CHECK(!CheckProUpRegTx(...)) assertion; update or remove this legacy rejection case to match the new versioning rules.
Useful? React with 👍 / 👎.
19464a5 to
72bb63b
Compare
There was a problem hiding this comment.
Code Review
Blocking issues remain in the payout migration paths: ProUpServTx and ProUpRevTx now bump a legacy v1/v2 deterministic MN state to v3 (ExtAddr) after V24 without migrating scriptPayout into payouts, silently losing the owner payout on snapshot roundtrip and in payment construction. Additionally, one unit test still asserts the now-removed v1→v3 rejection and will break make check. The previously flagged ProUpRevTx version-downgrade is resolved.
Source: reviewers: claude-general (opus), codex-general (gpt-5.5), claude-dash-core-commit-history (opus), codex-dash-core-commit-history (gpt-5.5); verifier: claude (opus).
🔴 3 blocking | 🟡 2 suggestion(s) | 💬 1 nitpick(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/evo/specialtxman.cpp`:
- [BLOCKING] src/evo/specialtxman.cpp:420-446: ProUpServTx v3 upgrade drops legacy scriptPayout without populating payouts
After V24 activation, the RPC path emits a v3 ProUpServTx regardless of the target MN's stored version (`src/rpc/evo.cpp:1107` uses `DeploymentToProtxVersion`). In the apply path here, `newState->nVersion` is unconditionally set to `opt_proTx->nVersion` (v3 = ExtAddr) and `SetStateVersion` is invoked, but `SetStateVersion` only converts `netInfo` — it never migrates the pre-existing `scriptPayout` into the new `payouts` vector. Because `CDeterministicMNState` serialization (`src/evo/dmnstate.h:100-104`) writes `payouts` and skips `scriptPayout` once `nVersion >= ExtAddr`, and `GetOwnerPayouts(state)` (`src/evo/providertx_util.cpp:25-27`) returns `state.payouts` in that regime, a legacy/basic MN that only had `scriptPayout` ends up as version 3 with an empty owner payout list. `masternode/payments.cpp:141-151` then produces zero owner-payment outputs for that MN, effectively burning the owner reward, and any snapshot roundtrip loses the payout permanently. Apply the same `LegacyPayoutAsList(scriptPayout)` migration the registrar path does at `src/evo/specialtxman.cpp:491-498` whenever `target_version >= ProTxVersion::ExtAddr` and the previous state was below ExtAddr, and clear `scriptPayout` after the copy.
In `src/evo/specialtxman.cpp`:
- [BLOCKING] src/evo/specialtxman.cpp:517-529: ProUpRevTx v3 upgrade drops legacy scriptPayout without populating payouts
The new `target_version = std::max<uint16_t>(old_version, opt_proTx->nVersion)` correctly preserves the state version on a v3 ProUpRevTx against a v1/v2 state, but `SetStateVersion` — like on the service path — only touches `netInfo`, not payouts. After the state is bumped to ExtAddr, `state.payouts` is still empty while `state.scriptPayout` holds the real payee, so both serialization (`src/evo/dmnstate.h:100-104`) and `GetOwnerPayouts(state)` see no owner payouts. The MN then produces no owner payment output, and a subsequent `protx update_registrar` that reuses the current state via `ptx.payouts = GetOwnerPayouts(*dmn->pdmnState)` (`src/rpc/evo.cpp:1240`) will operate on an empty list. Migrate `scriptPayout` to `LegacyPayoutAsList(scriptPayout)` (and clear `scriptPayout`) whenever `target_version >= ProTxVersion::ExtAddr` and the previous state was below ExtAddr — mirroring the registrar path.
In `src/test/evo_deterministicmns_tests.cpp`:
- [BLOCKING] src/test/evo_deterministicmns_tests.cpp:568-575: Unit test still asserts the removed v1→v3 rejection and will break `make check`
This test (`proupreg_v4_on_legacy_rejected`) constructs a v3 (ExtAddr) `CProUpRegTx` against a still-legacy v1 masternode after V24, then asserts `!CheckProUpRegTx(...)` with reject reason `bad-protx-version-upgrade`. Commit 4368317609 explicitly removed the `state_version == LegacyBLS && tx_version > BasicBLS` branch from `IsVersionChangeValid` (`src/evo/specialtxman.cpp:1014-1028`), so this transaction now passes the version-change check and there is no other rejection to trip on the constructed inputs — `CheckProUpRegTx` returns true and both `BOOST_CHECK(!...)` and the reject-reason equality assertion fail. Either rework this test to cover the newly-accepted v1→v3 migration path (and the resulting `nVersion`/payout state), or delete the case; running `make check` on develop will fail at HEAD.
In `<commit:19464a5a>`:
- [SUGGESTION] <commit:19464a5a>:1: Split the unrelated Python 3.15 tooling fix out of this PR
The final commit `fix: run of circular-dependencies with python3.15` rewrites `contrib/devtools/circular-dependencies.py` to swap `multiprocess`/`dill` for stdlib `multiprocessing` with a fork context. That is a standalone CI/tooling fix with no relationship to the stated ProTx-versioning goal of this PR. Landing it inside PR #7302 mixes unrelated topics in `git log`/`git blame` and makes bisecting a future Python-3.15 tooling regression to this SHA misleading. Please split it into its own PR, or — if it is genuinely required to unblock CI here — reword the subject (e.g. `build: fix circular-dependencies check under Python 3.15`) so future readers understand why a tooling commit sits on a ProTx stack.
In `<commit:49d30e96>`:
- [SUGGESTION] <commit:49d30e96>:1: Squash or reword review-feedback commit 49d30e9664
Commit 49d30e9664 `test: address review comments about preserving v2 / v3 version for dmnstate` is a review-process fixup — the subject advertises it as such and the diff only adds coverage that logically belongs to the earlier commit that introduced the dmnstate versioning behavior. Since this stack lands unsquashed on `develop`, `address review comments` becomes permanent noise in `git log` and splits related test coverage across two SHAs for `git bisect` / `git blame`. Squash it into the substantive commit whose behavior it validates (e.g. `git rebase -i --autosquash` after prefixing with `fixup!`), or at minimum reword the subject to describe the durable behavior being tested (e.g. `test: cover v24 ProTx dmnstate version preservation`). This is the same class of commit that was flagged on the prior review SHA (93d764816b) — please handle it the same way.
In `<commit:2e1ec941cc>`:
- [NITPICK] <commit:2e1ec941cc>:1: Fold the ProUpRevTx corrective fix into the versioning change
Commit 2e1ec941cc `fix: preserve masternode state version on ProUpRevTx after v24` corrects consensus-state behavior introduced by earlier commits in this same PR (the ProTx versioning changes), and 86f86a462f adds the matching regression coverage. Since the stack lands unsquashed, develop history will permanently contain a known-bad intermediate ProUpRevTx state-version behavior for a few commits in a consensus-critical area — avoidable noise for `git bisect`. Consider squashing 2e1ec941cc and 86f86a462f into the earlier logical ProTx versioning commit so each committed step is self-contained.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72bb63bd2d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Code Review
Incremental review of PR #7302 at 72bb63b. Prior blocking bug (v3 ProUpRevTx downgrading v2 state) is FIXED by 17411b8. Revert pair prior-ff-2 was dropped in rebase. Two commit-hygiene suggestions remain from prior review (carried forward as 49d30e9 and 9e365b4 at new SHAs), and Codex adds one related new nitpick asking to fold the ProUpRevTx fix+test into the logical versioning commit rather than landing unsquashed.
Source: reviewers claude-general (opus), codex-general (gpt-5.5), claude-dash-core-commit-history (opus), codex-dash-core-commit-history (gpt-5.5); verifier claude (opus).
🟡 1 suggestion(s) | 💬 2 nitpick(s)
Prior Reconciliation
- prior-ff-1 — FIXED: 17411b8 applies exactly the suggested target_version + SetStateVersion pattern at specialtxman.cpp:518-529; f53455f adds the missing v2→v3 revoke regression test.
- prior-ff-2 — FIXED: The feat/Revert pair (2900455 + d5ec8d1) is no longer present in the develop..HEAD stack after rebase.
- prior-ff-3 — STILL_VALID: The review-feedback commit persists at new SHA 49d30e9 with the exact same subject (
test: address review comments about preserving v2 / v3 version for dmnstate). Carried forward as a finding. - prior-ff-4 — STILL_VALID: Old SHA 75ca23b is gone, but the same issue reappears at 9e365b4: subject still says
CMasternodePayoutSharewhile its topological parent 18eefe2 already renamed the type toMasternodePayoutShare. Carried forward as a finding.
Carried-Forward Prior Findings
- [SUGGESTION] commit:49d30e9664:1: Squash or reword review-feedback commit 49d30e9
Commit 49d30e9 (test: address review comments about preserving v2 / v3 version for dmnstate) is a rebased carry-over of the prior review-response fixup (previously 93d7648). The subject describes the review process rather than the durable behavior being added, and the diff (35+ lines added to feature_protx_version.py covering V24 dmnstate version preservation) logically belongs with fedcc68 (fix!: use the same version along all protx special transactions), which introduced the dmnstate versioning behavior it patches. Since this stack lands unsquashed, eithergit rebase -i --autosquashit into its parent or reword to something liketest: cover v24 ProTx dmnstate version preservation. - [NITPICK] commit:9e365b479b:1: Commit subject 9e365b4 references pre-rename class name
9e365b4 (refactor: let compiler to auto-generate operator== and != for CMasternodePayoutShare) is applied topologically after 18eefe2 (refactor: rename CMasternodePayoutShare to MasternodePayoutShare accordingly code style), so the subject references a class name that no longer exists in the tree at that point. Reads oddly ingit log. Consider updating the subject toMasternodePayoutShare, or squashing 9e365b4 into the rename or ctor-removal commit (d999f4e / 18eefe2) since all three are trivial tidying of the same type. (This is the rebased successor of prior 75ca23b.)
New Findings In Latest Delta
- [NITPICK] commit:17411b830a:1: Fold the ProUpRevTx corrective fix and its test into the versioning commit
17411b8 (fix: preserve masternode state version on ProUpRevTx after v24) fixes consensus-state behavior introduced earlier in this same PR by fedcc68 (fix!: use the same version along all protx special transactions), and f53455f adds the matching regression coverage. Because the stack lands unsquashed,develophistory would contain a known-bad intermediate ProUpRevTx state-version behavior for several commits between fedcc68 and 17411b8, which is agit bisecthazard for anything touching consensus/state versioning in that window. Fold both the fix and its test into fedcc68 so each committed step is self-contained.
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `<commit:49d30e9664>`:
- [SUGGESTION] <commit:49d30e9664>:1: Squash or reword review-feedback commit 49d30e9664
Commit 49d30e9664 (`test: address review comments about preserving v2 / v3 version for dmnstate`) is a rebased carry-over of the prior review-response fixup (previously 93d764816b). The subject describes the review process rather than the durable behavior being added, and the diff (35+ lines added to feature_protx_version.py covering V24 dmnstate version preservation) logically belongs with fedcc68115 (`fix!: use the same version along all protx special transactions`), which introduced the dmnstate versioning behavior it patches. Since this stack lands unsquashed, either `git rebase -i --autosquash` it into its parent or reword to something like `test: cover v24 ProTx dmnstate version preservation`.
In `<commit:9e365b479b>`:
- [NITPICK] <commit:9e365b479b>:1: Commit subject 9e365b479b references pre-rename class name
9e365b479b (`refactor: let compiler to auto-generate operator== and != for CMasternodePayoutShare`) is applied topologically after 18eefe23a7 (`refactor: rename CMasternodePayoutShare to MasternodePayoutShare accordingly code style`), so the subject references a class name that no longer exists in the tree at that point. Reads oddly in `git log`. Consider updating the subject to `MasternodePayoutShare`, or squashing 9e365b479b into the rename or ctor-removal commit (d999f4e686 / 18eefe23a7) since all three are trivial tidying of the same type. (This is the rebased successor of prior 75ca23bee8.)
In `<commit:17411b830a>`:
- [NITPICK] <commit:17411b830a>:1: Fold the ProUpRevTx corrective fix and its test into the versioning commit
17411b830a (`fix: preserve masternode state version on ProUpRevTx after v24`) fixes consensus-state behavior introduced earlier in this same PR by fedcc68115 (`fix!: use the same version along all protx special transactions`), and f53455f8ad adds the matching regression coverage. Because the stack lands unsquashed, `develop` history would contain a known-bad intermediate ProUpRevTx state-version behavior for several commits between fedcc68115 and 17411b830a, which is a `git bisect` hazard for anything touching consensus/state versioning in that window. Fold both the fix and its test into fedcc68115 so each committed step is self-contained.
fcd2588 to
58e6cb7
Compare
…rdingly code style
…dation This commit replaces usage of helpers GetPayload+IsTrivialValid to GetValidatedPayload It unifies validation between regression tests and production code and useful for the next commits in PR
CProRegTx and CProUpServTx used to be the only type of protx that have a different version. It is theoretically acceptable in assumption that there is no new features or version will ever be introduced for protx special transaction. Though, for better compatibility for futher version, unification, simplicity of documentation and to reduce user's confusions for after-v24 version of CProUpRegTx and CProUpRevTx are allowed to be "ext addresses" even they don't have any network related fields _at the moment_ So, since now: - version 1: legacy BLS, extended addresses disallowed (pre v19 fork) - version 2: basic BLS, extended addresses disallowed (since v19 fork) - version 3: basic BLS, extended addresses allowed, multi-payouts allowed (since v24 fork) NOTE: there are also classes CSimplifiedMNListEntry and CDeterministicMNState use the same enum for its version; moreover CDeterministicMNState inherits version directly from CProRegTx. This refactoring doesn't contradict or conflict this behavior
It helps to drop multiple circular dependencies for providertx <-> validation.h as a side effect
58e6cb7 to
2d62450
Compare
Issue being fixed or feature implemented
Until now, CProRegTx and CProUpServTx were the only ProTx subtypes that could carry a higher version (v3, ext-addresses). With v4 introduced with masternode payout this gap is even more strange.
What was done?
For better forward-compatibility, uniform documentation, and less user confusion, after v24 CProUpRegTx and CProUpRevTx are also allowed to use the "ext-addresses" version, even though they don't carry any network-related fields at the moment.
Functionality of "multi-payout" is merged to v3 protx; v4 protx are removed.
They both activated by v24 fork and that's possible that multiple-payout masternode is using external address; it's completely find situation and existing v4 separation is artificial.
From now on:
NOTE: CSimplifiedMNListEntry and CDeterministicMNState use the same enum for its version; moreover CDeterministicMNState inherits version directly from CProRegTx. This refactoring is possible due already existing versioning of state's object.
It also simplifies the implementation, drops the dependency of evo/providertx.h on validation.h and reduces the number of circular dependencies over evo/providertx.
The regression test now goes through the same GetValidatedPayload helper that consensus uses, instead of calling GetTxPayload + IsTriviallyValid directly.
How Has This Been Tested?
Run unit / functional tests.
Breaking Changes
After v24, CProUpRegTx and CProUpRevTx may now be serialized at version 3, which they were previously not eligible for. The bad-protx-version-tx-type consensus check is removed accordingly so these txes are accepted at version 3.
v4 protx is removed by merging functionality to v3. They are activated by the same fork and this diversion is not required.
Checklist: