-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: introduce basic extended addresses implementation, stop using platform{HTTP,P2P}Port
#6666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
This pull request has conflicts, please rebase. |
0f230bf
to
e2f1a8f
Compare
This pull request has conflicts, please rebase. |
…nfo`) in functional tests, implement helpers, add type annotations 0532baf refactor: add helper for finding collateral vout (Kittywhiskers Van Gogh) 89b2548 refactor: add helper for transaction burial (Kittywhiskers Van Gogh) f3777c5 refactor: introduce address generation helper, make constructor minimal (Kittywhiskers Van Gogh) fa56126 refactor: add type annotations for `MasternodeInfo` members (Kittywhiskers Van Gogh) dfca0ee refactor: use `MasternodeInfo` in feature_dip3_deterministicmns.py (Kittywhiskers Van Gogh) cd7ffcf refactor: store only the node index in `MasternodeInfo` (Kittywhiskers Van Gogh) 07c7319 refactor: use `nodeIdx` instead of `node.index` with `MasternodeInfo` (Kittywhiskers Van Gogh) 08e81d8 refactor: store P2P port instead of full address in `MasternodeInfo` (Kittywhiskers Van Gogh) 3e25b30 refactor: store the funding address in `MasternodeInfo` (Kittywhiskers Van Gogh) 08d18d7 refactor: annotate usage of `MasternodeInfo` (Kittywhiskers Van Gogh) Pull request description: ## Motivation While working on functional tests for [dash#6666](#6666) (which in turn, relies on the foundation built by [dash#6674](#6674), see `rpc_netinfo.py`, [source](https://github.com/dashpay/dash/blob/c788531d35c48820af951799ff8b67ee2dadb8b3/test/functional/rpc_netinfo.py)), the existing test infrastructure, while well optimized for testing interactions _between_ masternodes, didn't offer enough flexibility for testing _creation_ of masternodes. The tests that need to be implemented for extended addresses mimic `feature_dip3_deterministicmns.py` ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/feature_dip3_deterministicmns.py)) in objective and while taking cues from it, it was found that instead of the `MasternodeInfo` used in most of the codebase ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1138-L1153)), `feature_dip3_deterministicmns.py` implements its own object, `Masternode` ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/feature_dip3_deterministicmns.py#L223-L227)). In a similar vein, `rpc_netinfo.py`, as implemented in c788531, implemented its own variant, `Node` ([source](https://github.com/dashpay/dash/blob/c788531d35c48820af951799ff8b67ee2dadb8b3/test/functional/rpc_netinfo.py#L31-L194)) to address the testing needs for extended addresses. It became clear that without additional intervention, the test suite would have three different ways of representing masternode information (`MasternodeInfo`, `Masternode`, `Node`) and that it would be more beneficial to consolidate all three approaches together. So, taking cue from `Node` (from `rpc_netinfo.py`, not included in `develop` as of this writing, [source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/rpc_netinfo.py)), this pull request aims to clean up, consolidate and improve upon `MasternodeInfo` in an attempt to remove the need for `Node`. ## Additional Information * PEP 526 ("Syntax for Variable Annotations") prohibit annotation of variables defined by a `for` or `with` statement ([source](https://peps.python.org/pep-0526/#where-annotations-aren-t-allowed)), to get around that, the syntax for type comments ([source](https://peps.python.org/pep-0484)) as defined in PEP 484 ("Type Hints") have been used instead. * The decision to remove `node` from `MasternodeInfo` despite its convenience was rooted in the observation that there are cases where `nodeIdx` and `node` are assigned separately ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1480), [source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1499)), which highlighted the fact that `MasternodeInfo` has no control over the lifetime of the `TestNode` instance as it is ultimately controlled by `BitcoinTestFramework`. To avoid potential error and to make it syntactically clear that ownership is vests with `BitcoinTestFramework`, `node` has been removed and replaced with `get_node()`, which alongside some basic sanity checks, is just an alias for `self.nodes[mn.nodeIdx]` but is still less tedious to type or read. * The reason why functions like `generate_addresses()` accept a separate `TestNode` ([source](https://github.com/dashpay/dash/blob/c5444b3f7c06aa5b060fd542773df6dd5a9bae1f/test/functional/test_framework/test_framework.py#L1160-L1174)) is due to the practice of using a single node to instantiate all masternodes instead of keeping them as self-contained instances that mine their own blocks and fund their own collaterals (for instance, `DashTestFramework` uses `nodes[0]` to setup all the masternodes even if the masternodes themselves had a different index). * The decision to replace `addr` with `nodePort` has been inspired by the need to register masternodes with non-loopback addresses (like in `feature_dip3_deterministicmns.py` or `rpc_netinfo.py`). As a node that is intended to be used will always need the same standardized loopback address, we can safely resort to only storing the port number and save a few `p2p_port()` calls along the way. ## Breaking Changes None expected. Affects only functional tests. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK 0532baf UdjinM6: utACK 0532baf Tree-SHA512: c2b5b0f9200c2714159a5e0f6f52174e38eae426c01620cf15e9adb8b8e67bbb078d658edb21b052a1fc9896ba098cffa02248aaa980be5d5cb145e9929c4568
This pull request has conflicts, please rebase. |
This pull request has conflicts, please rebase. |
…addresses` field, deprecate `service` field, allow submitting multiple addresses 7fdd642 docs: add documentation for deprecation, new field and renamed input (Kittywhiskers Van Gogh) 5c564c2 test: check pre-fork conditions for ProRegTx and ProUpServTx preparation (Kittywhiskers Van Gogh) 859ce5c test: validate common conditions for input validation (Kittywhiskers Van Gogh) a60c39a test: add functional test for `addresses` and deprecated `service` field (Kittywhiskers Van Gogh) e0d2a81 rpc: deprecate key "service" replaced by "addresses" (Kittywhiskers Van Gogh) d9247ad rpc: add new key "addresses" to allow reporting multiple entries (Kittywhiskers Van Gogh) 33e5f6a refactor: s/ipAndPort/coreP2PAddrs/g (Kittywhiskers Van Gogh) 4fd4e0e rpc: allow `ipAndPort` to accept multiple entries with arrays (Kittywhiskers Van Gogh) 8c28f30 rpc: spin-off `ipAndPort` and `platform{HTTP,P2P}Port` setters to util (Kittywhiskers Van Gogh) Pull request description: ## Motivation To enable including functional tests with the extended addresses (ExtAddr) implementation (i.e. [dash#6666](#6666)), we need the ability to _specify_ and _retrieve_ multiple addresses. The current RPCs do not allow for that and this pull request aims to remedy it. This _particular_ pull request does not include: * Compatibility logic to allow reporting Platform-specific addresses using legacy fields (will be submitted after ExtAddr impl) * Compatibility logic to allow deriving Platform ports from extended address entries to populate deprecated fields (will be submitted after ExtAddr impl) * The ability to submit multiple Platform addresses (done in conjunction with ExtAddr impl) ## Additional Information * Depends on #6665 * Depends on #6720 * Dependency for #6666 *⚠️ The format for the field replacing `service`, `addresses` is not stable and is changed in [dash#6666](#6666) to allow reporting Platform-specific addresses. This should be acceptable as the whole extended addresses changeset across multiple PRs is expected to be part of a major release but is mentioned here for the sake of posterity. * As `IsDeprecatedRPCEnabled()` is defined in `libbitcoin_node` (see `rpc/server.cpp`, [source](https://github.com/dashpay/dash/blob/da8a475dfac9ad80d566eef8cf119c8036d6ebcc/src/rpc/server.cpp#L370-L375)), it is not available to `libbitcoin_common`, where special transaction and network information source files are included. To get around this, a practically identical function, `IsServiceDeprecatedRPCEnabled()` is defined and used in non-RPC code. * It is located in `evo/netinfo.cpp` instead of a more natural `rpc/evo_util.cpp` to avoid unnecessary circular dependencies. ## Breaking Changes * The input field `ipAndPort` has been renamed to `coreP2PAddrs`. * `coreP2PAddrs` can now, in addition to accepting a string, accept an array of strings, subject to validation rules. * The key `service` has been deprecated for some RPCs (`decoderawtransaction`, `decodepsbt`, `getblock`, `getrawtransaction`, `gettransaction`, `masternode status` (only for the `dmnState` key), `protx diff`, `protx listdiff`) and has been replaced with the field `addresses`. * The deprecated field can be re-enabled using `-deprecatedrpc=service` but is liable to be removed in future versions of Dash Core. * This change does not affect `masternode status` (except for the `dmnState` key) as `service` does not represent a payload value but the external address advertised by the active masternode. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK 7fdd642 UdjinM6: utACK 7fdd642 Tree-SHA512: 75331a467e7170542349c94408b71f5d25ebd2bf418cb6bdf77866a342b9079ec795638535f67c6487739b37651bf8265dce66ef3b06e0732748a173441695ef
This pull request has conflicts, please rebase. |
MnNetInfo cannot store duplicate entries as it can store only one entry, so it cannot emit NetInfoStatus::Duplicate.
The current name is now a misnomer as we accept (multiple) addr:port pairs, the name has been updated to reflect that. We still support submitting only ports but that is transitional support that may be removed in a future release.
…2p}` We also update the type annotations to reflect the three different types of valid input (port, single address, array of addresses).
`coreP2PAddrs` is the name given to the input field in Dash Core but the test suite follows a different naming convention, let's harmonize it for good.
MnNetInfo doesn't actually store this information so we are pulling the data from platform{HTTP,P2P}Port to make it appear like it does. This is to ensure reporting parity with ExtNetInfo, which stores platform fields and should allow us to deprecate the platform{HTTP,P2P}Port when we need to.
…iff` `CDeterministicMNStateDiff` is special because not all of its fields may be populated, so we may not have all the data we need to report on the diff.
This is to ensure that even when the fields have been deprecated, they can still return values by reading from `netInfo`. This will require us to constrain ExtNetInfo's validation rules to continue under legacy assumptions *until* we remove the legacy reporting fields altogether.
`or` will treat a blank string as a bad value and will go for the default, which creates problems when we need to test for blank value behaviors. This isn't a problem with `platform_{p2p,http}_port` since we check against None explicitly.
We are unlikely to create regular masternodes in this test as we need to specifically validate shims for platform-specific fields, which only come into play with EvoNodes. This should let us trim down the tests a little.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
test/functional/rpc_netinfo.py (1)
73-81
: set_active_state: correct use of original extra_args snapshotCopying node.extra_args is correct in this framework (restart_node doesn’t mutate it). No risk of duplicating -masternodeblsprivkey across restarts.
🧹 Nitpick comments (9)
src/test/evo_netinfo_tests.cpp (1)
60-77
: Optional: also assert purpose-filtered GetEntries()You validate flattened GetEntries() results; consider an extra assertion for GetEntries(purpose) to exercise the optional filter path.
- ValidateGetEntries(netInfo.GetEntries(), /*expected_size=*/1); + ValidateGetEntries(netInfo.GetEntries(), /*expected_size=*/1); + // Filtered by purpose should match too + ValidateGetEntries(netInfo.GetEntries(purpose), /*expected_size=*/1);src/evo/netinfo.cpp (4)
169-174
: Guard against version 0 with assert message or graceful handlingassert(nVersion > 0) is fine for internal callers, but consider a more descriptive assert message for easier debugging.
- assert(nVersion > 0); + assert(nVersion > 0 && "NetInfoInterface::MakeNetInfo requires non-zero ProTx version");
324-327
: Typo in comment: “succesfully” → “successfully”Minor spelling fix.
- // Candidate succesfully added, update cache + // Candidate successfully added, update cache
386-399
: Simplify redundant size check in GetPrimary()After ASSERT_IF_DEBUG(!entries.empty()), the size() >= 1 guard is redundant. Optional micro-simplification.
- // If a purpose code is in the map, there should be at least one entry - ASSERT_IF_DEBUG(!entries.empty()); - if (entries.size() >= 1) { - if (const auto& service_opt{entries[0].GetAddrPort()}) { - return *service_opt; - } - } + // If a purpose code is in the map, there should be at least one entry + ASSERT_IF_DEBUG(!entries.empty()); + if (const auto& service_opt{entries[0].GetAddrPort()}) { + return *service_opt; + }
459-475
: Address clang-format diff in ToString() and nested lambdasCI reported clang-format differences in this file. Please run clang-format to normalize indentation/line breaks, especially around nested lambdas in ToString().
Run:
clang-format -i src/evo/netinfo.cpptest/functional/rpc_netinfo.py (2)
161-166
: Usein dict
instead ofin dict.keys()
Simplify membership checks per Ruff SIM118.
- for arr in rpc_output['updatedMNs']: - if proregtx_hash in arr.keys(): - return arr[proregtx_hash] + for arr in rpc_output['updatedMNs']: + if proregtx_hash in arr: + return arr[proregtx_hash]
415-420
: Clean up dict membership checks (SIM118): drop.keys()
Replace multiple occurrences of
"key" in d.keys()
with"key" in d
and corresponding negative checks.- assert "addresses" in proregtx_rpc['proRegTx'].keys() - assert "addresses" in masternode_status['dmnState'].keys() - assert "addresses" in proupservtx_rpc['proUpServTx'].keys() - assert "addresses" in protx_diff_rpc['mnList'][0].keys() - assert "addresses" in protx_listdiff_rpc.keys() + assert "addresses" in proregtx_rpc['proRegTx'] + assert "addresses" in masternode_status['dmnState'] + assert "addresses" in proupservtx_rpc['proUpServTx'] + assert "addresses" in protx_diff_rpc['mnList'][0] + assert "addresses" in protx_listdiff_rpc- assert "core_p2p" not in protx_listdiff_rpc_pl['addresses'].keys() + assert "core_p2p" not in protx_listdiff_rpc_pl['addresses']- assert "service" not in proregtx_rpc['proRegTx'].keys() - assert "service" not in masternode_status['dmnState'].keys() - assert "service" not in proupservtx_rpc['proUpServTx'].keys() - assert "service" not in protx_diff_rpc['mnList'][0].keys() - assert "service" not in protx_listdiff_rpc.keys() + assert "service" not in proregtx_rpc['proRegTx'] + assert "service" not in masternode_status['dmnState'] + assert "service" not in proupservtx_rpc['proUpServTx'] + assert "service" not in protx_diff_rpc['mnList'][0] + assert "service" not in protx_listdiff_rpc- assert "service" in proregtx_rpc['proRegTx'].keys() - assert "service" in masternode_status['dmnState'].keys() - assert "service" in proupservtx_rpc['proUpServTx'].keys() - assert "service" in protx_diff_rpc['mnList'][0].keys() - assert "service" in protx_listdiff_rpc.keys() + assert "service" in proregtx_rpc['proRegTx'] + assert "service" in masternode_status['dmnState'] + assert "service" in proupservtx_rpc['proUpServTx'] + assert "service" in protx_diff_rpc['mnList'][0] + assert "service" in protx_listdiff_rpc- assert "platformP2PPort" not in protx_listdiff_rpc.keys() - assert "platformHTTPPort" not in protx_listdiff_rpc.keys() + assert "platformP2PPort" not in protx_listdiff_rpc + assert "platformHTTPPort" not in protx_listdiff_rpcAlso applies to: 430-433, 435-440, 459-464, 546-548
src/evo/netinfo.h (2)
299-309
: clang-format the file to satisfy CICI flagged formatting diffs around this region. Please run clang-format on this header.
Run:
clang-format -i src/evo/netinfo.h
361-441
: Serialization wrapper: magic-based disambiguation for MNStateDiff is pragmaticThe 0x23… magic to distinguish extended vs legacy, plus fixed-size fallback for MnNetInfo, is a reasonable compromise given stream constraints. Consider documenting the extremely low-probability collision scenario in a comment.
- static constexpr std::array<uint8_t, 4> EXTADDR_MAGIC{0x23, 0x23, 0x23, 0x23}; + // Magic used to detect extended netinfo in CDeterministicMNStateDiff streams. + // Collision with the first 4 bytes of legacy MnNetInfo serialization is extremely unlikely. + static constexpr std::array<uint8_t, 4> EXTADDR_MAGIC{0x23, 0x23, 0x23, 0x23};
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
contrib/seeds/makeseeds.py
(1 hunks)doc/release-notes-6666.md
(1 hunks)src/coinjoin/client.cpp
(2 hunks)src/evo/core_write.cpp
(5 hunks)src/evo/deterministicmns.cpp
(8 hunks)src/evo/deterministicmns.h
(1 hunks)src/evo/dmnstate.cpp
(4 hunks)src/evo/dmnstate.h
(2 hunks)src/evo/netinfo.cpp
(6 hunks)src/evo/netinfo.h
(11 hunks)src/evo/providertx.cpp
(5 hunks)src/evo/providertx.h
(3 hunks)src/evo/simplifiedmns.cpp
(1 hunks)src/evo/simplifiedmns.h
(1 hunks)src/evo/specialtxman.cpp
(3 hunks)src/rpc/coinjoin.cpp
(1 hunks)src/rpc/evo.cpp
(7 hunks)src/rpc/evo_util.cpp
(4 hunks)src/rpc/evo_util.h
(1 hunks)src/rpc/masternode.cpp
(3 hunks)src/rpc/quorums.cpp
(2 hunks)src/test/block_reward_reallocation_tests.cpp
(1 hunks)src/test/evo_deterministicmns_tests.cpp
(6 hunks)src/test/evo_netinfo_tests.cpp
(4 hunks)src/test/evo_simplifiedmns_tests.cpp
(1 hunks)test/functional/feature_dip3_deterministicmns.py
(1 hunks)test/functional/rpc_netinfo.py
(8 hunks)test/functional/rpc_quorum.py
(1 hunks)test/functional/test_framework/test_framework.py
(12 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/coinjoin/client.cpp
🚧 Files skipped from review as they are similar to previous changes (23)
- contrib/seeds/makeseeds.py
- src/evo/deterministicmns.h
- test/functional/rpc_quorum.py
- src/test/evo_simplifiedmns_tests.cpp
- src/test/block_reward_reallocation_tests.cpp
- test/functional/feature_dip3_deterministicmns.py
- src/evo/simplifiedmns.h
- src/evo/providertx.h
- src/evo/simplifiedmns.cpp
- src/evo/core_write.cpp
- src/rpc/quorums.cpp
- src/rpc/coinjoin.cpp
- src/evo/dmnstate.h
- src/test/evo_deterministicmns_tests.cpp
- src/rpc/masternode.cpp
- src/evo/specialtxman.cpp
- src/evo/providertx.cpp
- src/evo/dmnstate.cpp
- src/evo/deterministicmns.cpp
- src/rpc/evo_util.h
- doc/release-notes-6666.md
- src/rpc/evo.cpp
- src/rpc/evo_util.cpp
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit Inference Engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}
: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/test/evo_netinfo_tests.cpp
src/evo/netinfo.cpp
src/evo/netinfo.h
src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Unit tests for C++ code should be placed in src/test/, src/wallet/test/, or src/qt/test/ and use Boost::Test or Qt 5 for GUI tests
Files:
src/test/evo_netinfo_tests.cpp
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Masternode lists must use immutable data structures (Immer library) for thread safety
Files:
src/evo/netinfo.cpp
src/evo/netinfo.h
test/functional/**/*.py
📄 CodeRabbit Inference Engine (CLAUDE.md)
Functional tests should be written in Python and placed in test/functional/
Files:
test/functional/rpc_netinfo.py
test/functional/test_framework/test_framework.py
🧠 Learnings (4)
📚 Learning: 2025-05-10T00:54:15.597Z
Learnt from: kwvg
PR: dashpay/dash#6629
File: src/evo/deterministicmns.cpp:473-485
Timestamp: 2025-05-10T00:54:15.597Z
Learning: In the current implementation of `NetInfoEntry`, only `CService` is a valid type and `std::monostate` represents an invalid state. Entries that don't provide a valid `CService` through `GetAddrPort()` should be rejected with an exception, not silently skipped.
Applied to files:
src/test/evo_netinfo_tests.cpp
src/evo/netinfo.cpp
src/evo/netinfo.h
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
PR: dashpay/dash#6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Applied to files:
test/functional/rpc_netinfo.py
test/functional/test_framework/test_framework.py
📚 Learning: 2025-08-10T13:52:46.260Z
Learnt from: kwvg
PR: dashpay/dash#6666
File: test/functional/rpc_netinfo.py:73-81
Timestamp: 2025-08-10T13:52:46.260Z
Learning: In the Bitcoin/Dash test framework, `self.node.extra_args` contains the original startup arguments for a TestNode and is not modified by `test.restart_node()`. Each restart with new `extra_args` doesn't update the stored `self.node.extra_args`, so copying from it always provides a clean slate without accumulated modifications from previous restarts.
Applied to files:
test/functional/rpc_netinfo.py
📚 Learning: 2025-06-16T17:59:55.669Z
Learnt from: kwvg
PR: dashpay/dash#6665
File: src/evo/deterministicmns.cpp:1284-1287
Timestamp: 2025-06-16T17:59:55.669Z
Learning: In Dash masternode ProRegTx validation, platform ports (platformHTTPPort and platformP2PPort) are mandatory and must be non-zero, while netInfo (ipAndPort) is optional. This means that even if an empty netInfo returns 0 from GetPrimary().GetPort(), it won't cause false positives in port duplication checks since platform ports cannot be 0.
Applied to files:
test/functional/rpc_netinfo.py
test/functional/test_framework/test_framework.py
🧬 Code Graph Analysis (1)
src/test/evo_netinfo_tests.cpp (1)
src/evo/netinfo.h (2)
MAX_ENTRIES_EXTNETINFO
(20-20)CService
(193-203)
🪛 GitHub Actions: Clang Diff Format Check
src/evo/netinfo.cpp
[error] 257-270: Clang format differences found. Code formatting does not match the expected style.
src/evo/netinfo.h
[error] 299-309: Clang format differences found. Code formatting does not match the expected style.
🪛 Ruff (0.12.2)
test/functional/rpc_netinfo.py
163-163: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
415-415: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
416-416: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
417-417: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
418-418: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
419-419: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
430-430: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
435-435: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
436-436: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
437-437: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
438-438: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
439-439: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
442-442: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
459-459: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
460-460: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
461-461: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
462-462: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
463-463: Use key in dict
instead of key in dict.keys()
Remove .keys()
(SIM118)
546-546: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
547-547: Use key not in dict
instead of key not in dict.keys()
Remove .keys()
(SIM118)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: mac-build / Build source
- GitHub Check: linux64_sqlite-build / Build source
- GitHub Check: linux64_ubsan-build / Build source
- GitHub Check: linux64_fuzz-build / Build source
- GitHub Check: linux64-build / Build source
- GitHub Check: arm-linux-build / Build source
- GitHub Check: win64-build / Build source
- GitHub Check: Lint / Run linters
- GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
🔇 Additional comments (16)
src/test/evo_netinfo_tests.cpp (7)
21-50
: Good coverage and purpose-aware expectations match implementationThe mainnet vector captures default-port implication for MnNetInfo, explicit-port requirement for ExtNetInfo, invalid purpose handling, IPv6 allowance only for ExtNetInfo, and platform P2P acceptance only for ExtNetInfo. These align with ValidateService/AddEntry semantics.
79-96
: LGTM: ExtNetInfo tests mirror Mn and handle malformed vs success states correctlyThe separation of expected return codes for Mn vs Ext and malformed checks for empty states is precise.
102-119
: Correct enforcement: single-entry MnNetInfo and purpose restrictionSingle-entry overwrite rejection and restricting to CORE_P2P are tested and match MnNetInfo::AddEntry logic.
142-157
: Entry limit and per-purpose semantics verified correctlyPer-purpose limit (MAX_ENTRIES_EXTNETINFO) and flattened-size expectations are asserted properly. Nice touch verifying a different purpose does not trip the limit.
159-179
: Duplicate rules validated comprehensivelyWithin-list exact and partial duplicates prohibited; across-lists exact duplicates prohibited and partial allowed. This matches ExtNetInfo::ProcessCandidate and duplicate helpers.
286-324
: CService compatibility tests updated appropriatelyUsing purpose-aware AddEntry calls keeps this compatibility test relevant. Assertions for failed lookups and invalid types leaving MnNetInfo empty are sound.
326-361
: Shared_ptr equality semantics well coveredGood exercise of type-sensitive equality across MnNetInfo/ExtNetInfo implementations and state differences.
src/evo/netinfo.cpp (2)
197-220
: MnNetInfo AddEntry: behavior matches tests, fast-fail and no DNS lookupsIPv4-only safe char filter, default port implication, and fAllowLookup=false conform to intended behavior and tests.
329-345
: Validation excludes Tor/other types; explicit zero/bad ports check is correctIPv4/IPv6-only and routable checks plus IsBadPort() || port==0 align with policy and tests.
test/functional/rpc_netinfo.py (1)
154-160
: Assertion helper covers expected optional fields correctlyUsing truthy checks to gate platform entries is fine since tests don’t pass zero here. Keep as-is.
test/functional/test_framework/test_framework.py (3)
1223-1243
: validate_inputs centralizes Evo-specific checks — good extractionThe helper enforces presence of platform params for EvoNodes and pairs error code/message usage. This removes repetition in register/update paths.
1248-1266
: Default core addr behavior remains intact with new paramsFalling back to ['127.0.0.1:{nodePort}'] when addrs_core_p2p is None preserves legacy behavior while allowing extended inputs.
1413-1458
: update_service wiring aligns with refactored RPC argumentsParameter order and submission booleans mirror register paths; Evo and legacy codepaths are cleanly separated.
src/evo/netinfo.h (3)
19-26
: Public API additions look coherentMAX_ENTRIES_EXTNETINFO and Duplicate status are sensible, matching tests and implementation.
63-75
: Purpose codes and comments clarify ordering/contiguity constraintsClear documentation that changes require a format version bump. Good.
169-176
: GetAddrPort returns by value — consistent with variant storageReturning std::optional by value avoids dangling references and matches netinfo.cpp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is really big! I reviewed half of commits, please consider my comments
src/evo/netinfo.cpp
Outdated
@@ -244,13 +244,18 @@ NetInfoStatus MnNetInfo::Validate() const | |||
|
|||
UniValue MnNetInfo::ToJson() const | |||
{ | |||
if (IsEmpty()) { | |||
return UniValue(UniValue::VARR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: UniValue{UniValue::VARR};
is better here I think
src/test/evo_netinfo_tests.cpp
Outdated
// Exact duplicates are prohibited | ||
BOOST_CHECK_EQUAL(netInfo.AddEntry("1.1.1.1:9998"), NetInfoStatus::Duplicate); | ||
// Partial duplicates (same address, different port) are also prohibited | ||
BOOST_CHECK_EQUAL(netInfo.AddEntry("1.1.1.1:9997"), NetInfoStatus::Duplicate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: may you add tests with duplicates of ipv6 address too?
@@ -311,8 +323,12 @@ NetInfoStatus ExtNetInfo::ValidateService(const CService& service) | |||
return NetInfoStatus::Success; | |||
} | |||
|
|||
NetInfoStatus ExtNetInfo::AddEntry(const std::string& input) | |||
NetInfoStatus ExtNetInfo::AddEntry(const uint8_t purpose, const std::string& input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: purpose here should have a type of enum, not uint8_t
// entries must be contiguous and cannot be changed once set without a format version | ||
// update, which will necessitate a hard-fork. | ||
namespace Purpose { | ||
enum : uint8_t { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: enum class
for all new code is preferable.
} | ||
|
||
// Warning: Used in RPC code, altering existing values is a breaking change | ||
constexpr std::string_view PurposeToString(const uint8_t purpose, const bool lower = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here should be enum instead uint8_t
constexpr std::string_view PurposeToString(const Purpose purpose, const bool lower = false)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems as capitals are used only in logs. If it is true, consider to drop an argument lower=false
and just always output it (in RPC and in logs) as lowercase without capital option. It doesn't seems much useful for me
@@ -440,6 +440,16 @@ static RPCHelpMan getcoinjoininfo() | |||
{RPCResult::Type::STR, "address", ""}, | |||
} | |||
}, | |||
{RPCResult::Type::ARR, "platform_p2p", /*optional=*/true, "Addresses used for Platform P2P", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if you will keep it here, consider mentioning evo nodes here;
maybe something like
{RPCResult::Type::ARR, "platform_p2p", /*optional=*/true, "Addresses used for Platform P2P (EvoNodes only)",
@@ -440,6 +440,16 @@ static RPCHelpMan getcoinjoininfo() | |||
{RPCResult::Type::STR, "address", ""}, | |||
} | |||
}, | |||
{RPCResult::Type::ARR, "platform_p2p", /*optional=*/true, "Addresses used for Platform P2P", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO coinjoin related RPC getcoinjoininfo
should not return platform_https
and platform_p2p
; consider just removing these fields comletely.
Probably you need to add helper "get p2p addreses only" for NetInfo, something like that:
- obj.pushKV("addresses", mixingMasternode->pdmnState->netInfo->ToJson());
+ obj.pushKV("addresses", mixingMasternode->pdmnState->netInfo->FilterBy(NetInfoPurpose::P2P_CORE)->ToJson());
} | ||
|
||
// TODO: use real args here | ||
static int mainnetPlatformHTTPPort = CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)->GetDefaultPlatformHTTPPort(); | ||
const auto main_params{CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait, why here CreateChainParams()
?
I haven't noticed it before in prior PRs... Why can't you just use here Params()
from header chainparams.h
?
Calling CreateChainParams
is not good at least from performance point of view. It's not a very heavy object, but it's not cheap at all
"platformNodeID=%s%s)\n" | ||
" %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider merging 2 lines for simplicity
- "platformNodeID=%s%s)\n"
- " %s",
+ "platformNodeID=%s%s)\n %s",
src/rpc/evo.cpp
Outdated
@@ -184,12 +184,14 @@ static RPCArg GetRpcArg(const std::string& strParamName) | |||
"Platform P2P node ID, derived from P2P public key."} | |||
}, | |||
{"platformP2PPort", | |||
{"platformP2PPort", RPCArg::Type::NUM, RPCArg::Optional::NO, | |||
"TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."} | |||
{"platformP2PPort", RPCArg::Type::STR, RPCArg::Optional::NO, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not more platormP2PPort
; especially if it's string.
I'd suggest to create new field if you really need to return string for any reason; otherwise just return PORT.
Also I see further you requires platform address to has port only (not address), so, I'd suggest to don't change this one.
See:
-8, "ProTx version disallows storing addresses in platformP2PPort (must specify port number only)")
} | ||
} | ||
if (!netInfoObj.empty()) { | ||
obj.pushKV("addresses", netInfoObj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: IMO should not re-order fields if there's no reason to do it.
addresses
will be now the last fields
@@ -246,7 +246,7 @@ class MnNetInfo final : public NetInfoInterface | |||
} | |||
|
|||
NetInfoStatus AddEntry(const uint8_t purpose, const std::string& service) override; | |||
NetInfoList GetEntries() const override; | |||
NetInfoList GetEntries(std::optional<uint8_t> purpose_opt = std::nullopt) const override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::optional here looks a bit too complicated; maybe better to add one more fake value to enum such as Purpose::ALL
?
@@ -1242,7 +1242,7 @@ def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str] | |||
args = [ | |||
collateral_txid or self.collateral_txid, | |||
collateral_vout or self.collateral_vout, | |||
addrs_core_p2p or [f'127.0.0.1:{self.nodePort}'], | |||
[f'127.0.0.1:{self.nodePort}'] if addrs_core_p2p is None else addrs_core_p2p, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[f'127.0.0.1:{self.nodePort}'] if addrs_core_p2p is None else addrs_core_p2p,
[f'127.0.0.1:{self.nodePort}'] if addrs_core_p2p is None else addrs_core_p2p,
[f'127.0.0.1:{self.nodePort}'] if addrs_core_p2p is None else addrs_core_p2p,
nit: consider create a helper here
|
||
def skip_test_if_missing_module(self): | ||
self.skip_if_no_wallet() | ||
|
||
def activate_v23(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider using here activate_by_name
instead self-written helper
Motivation
Currently, we store an address and port pair for all masternodes and two port numbers for Evonodes. The first pair is used by Dash Core and the latter two port numbers are paired with the address from the first pair and are used by Dash Platform.
This arrangement has allowed the network to grow and sustain its current operations but proves to be rigid as it imposes the constraint that all advertised activity (Core P2P, Platform P2P and the Platform HTTPS API) happen only on one network (IPv4), from the same public IP (as we can only register one address).
This prevents specifying different networks (like IPv6), alternate addresses (e.g. on privacy-oriented networks), expanding to advertise other purposes or deferring resolution of the underlying address (e.g. specifying domain names). To allow for these use cases, the changes made to transaction, storage and state formats alongside changes made to RPC input and output fields are collectively referred to as "extended addresses".
This pull request includes the following:
A basic extended addresses implementation that allows storing 4 addresses per purpose code, recognizing the following purpose codes,
CORE_P2P
,PLATFORM_P2P
andPLATFORM_HTTPS
.Support for specifying (arrays of) addr:port pairs to
platformP2PAddrs
(formerly known asplatformP2PPort
) andplatformHTTPSAddrs
(formerly known asplatformHTTPPort
).Compatibility code to allow
Reporting
platformP2PPort
andplatformHTTPPort
for extended address payloads even though they have been subsumed intonetInfo
Reporting
platformP2PPort
andplatformHTTPPort
data from legacy address payloads throughaddresses
even though they aren't stored innetInfo
Specifying only ports in
platformP2PAddrs
andplatformHTTPSAddrs
when usingprotx register{,_evo}
,protx register_fund{,_evo}
andprotx update_service{,_evo}
to create/update an extended addresses eligible masternode by reading the address of the firstcoreP2PAddrs
entry and pairing it with the supplied port.This pull request does not include the the full set of validation rules applicable on extended addresses as they have been reserved for a subsequent pull request. This pull request's scope is to lay down the base implementation, its surrounding compatibility code and tests to ensure its sanity.
Additional Information
Depends on feat(rpc): allow reporting multiple addresses with new
addresses
field, deprecateservice
field, allow submitting multiple addresses #6674The adoption of
std::reference_wrapper
(starting with dash#6627) has been reverted as while it there were performance considerations that led to its adoption, the risk of dangling references due to a race condition (e.g. iterating throughGetEntries()
whileClear()
is called) are more pronounced for extended addresses.The underlying structures (
NetInfoEntry
, which will predominantly hold aCService
) are not heavy enough to justify the usage of locking (i.e. mutexes). Making copies are deemed affordable enough for the safety that it provides.CDeterministicMNStateDiff
is an append-only structure populated based on flags, which has made it a source of attention throughout work on extended addresses (see dash#6636). It is the reasonNetInfoSerWrapper
was introduced, asnVersion
is placed afternetInfo
(source), which means, we cannot determine what format to use when deserializing based on the version of the format.To work around this, extended address payloads are prepended with the magic
0x23232323
(source) when serializing and deserialization will read the first four bytes to determine if the payload is extended or legacy.As we require a flattened list of all addresses associated with a masternode in order to check it against the mempool or unique properties map (example), it would be inefficient to regenerate that list every time
GetEntries()
is called. To get around that, we use a memory-only cache,m_all_entries
is used.It is populated when deserialized or added to when new entries are successful. This proves to be sufficient as
ExtNetInfo
is an append-only structure (i.e. they can either be added to withAddEntry()
orClear()
ed).As
rpc_netinfo.py
is unlikely to use regular masternodes (as Platform-related fields can only be tested with Evonodes), non-Evonode code paths were removed and the following additional changes were madeImplementing the helper functions
reconnect_nodes()
andset_active_state()
, the former to reconnect restarted nodes to their peers (which is not done automatically by the test framework) and the latter to restart the node to enable it in active masternode state (and/or optionally define extra arguments).Fix a minor bug where
destroy_mn()
overwrote the ProTx hash of the destroyed masternode before checking for removal from the masternode list and logging it.Breaking Changes
Refer to release notes.
Checklist: