Skip to content

Commit e19aadb

Browse files
Merge #6627: refactor: section off masternode network information to MnNetInfo, lay some groundwork for managing multiple entries
1d52678 refactor: track every `MnNetInfo` entry in the unique property map (Kittywhiskers Van Gogh) bcb8a7d refactor: impl `GetEntries()`, adapt to support multiple addresses (Kittywhiskers Van Gogh) ecc9368 refactor: implement `MnNetInfo::ToString()` for printing internal state (Kittywhiskers Van Gogh) 2e9bde0 refactor: move service validation to `MnNetInfo`, run during setting (Kittywhiskers Van Gogh) 03ec604 fix: correct `simplifiedmns_merkleroots` unit test (Kittywhiskers Van Gogh) bac4a27 refactor: move address lookup to `MnNetInfo::AddEntry()` (Kittywhiskers Van Gogh) e1783cb refactor: remove direct access to `MnNetInfo::addr` (Kittywhiskers Van Gogh) e868aff refactor: use const-ref when accessing `MnNetInfo::addr` if read-only (Kittywhiskers Van Gogh) aaabc35 refactor: section off masternode service to `MnNetInfo` (Kittywhiskers Van Gogh) 2c6dd05 fix: avoid potential "no user-provided default constructor" error (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Depends on #6626 * Depends on #6635 * Depends on #6636 * Dependency for #6629 * The `simplifiedmns_merkleroots` test constructs 15 `CSimplifiedMNListEntry` elements and populates them with a `CService`. So far, since we allowed direct assignment of the `CService` without validation, no checks were made to see if they would pass validation but if we start enforcing validation rules _when setting values_, two problems emerge. * Using non-default ports on mainnet (`BasicTestingSetup` is mainnet by default, [source](https://github.com/dashpay/dash/blob/bcd14b05cec7d94986f114ca17bbdadbee701d9b/src/test/util/setup_common.h#L100)), this has been resolved by using `RegTestingSetup` (which is based on regtest) instead. * As the index is used to generate the address, starting from 0, the first address is `0.0.0.0`, which is not a valid `CService` address ([source](https://github.com/dashpay/dash/blob/bcd14b05cec7d94986f114ca17bbdadbee701d9b/src/test/net_tests.cpp#L140)) and therefore, would fail validation ([source](https://github.com/dashpay/dash/blob/bcd14b05cec7d94986f114ca17bbdadbee701d9b/src/evo/deterministicmns.cpp#L1219)). This has been resolved by changing the index to start from 1. * To avoid a potential "no user-provided default constructor" compile-time error, we now explicitly request the default constructor <details> <summary>Compile error:</summary> ``` In file included from evo/deterministicmns.cpp:5: ./evo/deterministicmns.h:404:24: error: default initialization of an object of const type 'const ExampleType' without a user-provided default constructor 404 | static const T nullValue; | ^ | {} evo/deterministicmns.cpp:479:18: note: in instantiation of function template specialization 'CDeterministicMNList::AddUniqueProperty<ExampleType>' requested here 479 | if (!AddUniqueProperty(*dmn, domain)) { | ^ ``` </details> * The reason why we track individual entries _within_ `MnNetInfo` in the unique properties map instead of `MnNetInfo` is that while `MnNetInfo`-like objects (because `MnNetInfo` itself only stores one value) could check _internally_ for duplicates, the uniqueness requirement for addresses is _across_ ProTx'es and therefore, we are concerned not so much as _how_ the addresses are bundled (`MnNetInfo`) but if the individual addresses (`CService`) are unique _across_ all known addresses. * We cannot use `const auto&` when iterating through `GetEntries()` as it uses `std::reference_wrapper<const T>` and `auto` will take on the type of `const std::reference_wrapper<const T>&` instead of the underlying `const T&` as intended, to trigger the conversion to the underlying reference, we have to explicitly specify the type, hence the usage of `const T&`. ## Breaking Changes None expected. ## 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: knst: utACK 1d52678 UdjinM6: utACK 1d52678 Tree-SHA512: 0f13f51fff6c279e8c4e3f88ea4db925f4137e25b030d28afd48b5db9c073421d5bb3a3dc3e067ee4f559887bec9e8a981d2e6ae5f2d0a042e5817a3d59ea0bf
2 parents 29e2d43 + 1d52678 commit e19aadb

27 files changed

+416
-156
lines changed

src/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ BITCOIN_CORE_H = \
192192
evo/evodb.h \
193193
evo/mnauth.h \
194194
evo/mnhftx.h \
195+
evo/netinfo.h \
195196
evo/providertx.h \
196197
evo/simplifiedmns.h \
197198
evo/specialtx.h \
@@ -776,6 +777,7 @@ libbitcoin_common_a_SOURCES = \
776777
core_write.cpp \
777778
deploymentinfo.cpp \
778779
evo/core_write.cpp \
780+
evo/netinfo.cpp \
779781
governance/common.cpp \
780782
init/common.cpp \
781783
key.cpp \

src/Makefile.test.include

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ BITCOIN_TESTS =\
113113
test/evo_deterministicmns_tests.cpp \
114114
test/evo_islock_tests.cpp \
115115
test/evo_mnhf_tests.cpp \
116+
test/evo_netinfo_tests.cpp \
116117
test/evo_simplifiedmns_tests.cpp \
117118
test/evo_trivialvalidation.cpp \
118119
test/evo_utils_tests.cpp \

src/coinjoin/client.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ void CCoinJoinClientSession::ProcessMessage(CNode& peer, CChainState& active_cha
186186
if (!m_mn_sync.IsBlockchainSynced()) return;
187187

188188
if (!mixingMasternode) return;
189-
if (mixingMasternode->pdmnState->addr != peer.addr) return;
189+
if (mixingMasternode->pdmnState->netInfo.GetPrimary() != peer.addr) return;
190190

191191
if (msg_type == NetMsgType::DSSTATUSUPDATE) {
192192
CCoinJoinStatusUpdate psssup;
@@ -1106,7 +1106,7 @@ bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized,
11061106

11071107
m_clientman.AddUsedMasternode(dsq.masternodeOutpoint);
11081108

1109-
if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->addr)) {
1109+
if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo.GetPrimary())) {
11101110
WalletCJLogPrint(m_wallet, /* Continued */
11111111
"CCoinJoinClientSession::JoinExistingQueue -- skipping connection, masternode=%s\n", dmn->proTxHash.ToString());
11121112
continue;
@@ -1178,7 +1178,7 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon
11781178
continue;
11791179
}
11801180

1181-
if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->addr)) {
1181+
if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo.GetPrimary())) {
11821182
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::StartNewQueue -- skipping connection, masternode=%s\n",
11831183
dmn->proTxHash.ToString());
11841184
nTries++;
@@ -1218,7 +1218,7 @@ bool CCoinJoinClientSession::ProcessPendingDsaRequest(CConnman& connman)
12181218

12191219
CService mn_addr;
12201220
if (auto dmn = m_dmnman.GetListAtChainTip().GetMN(pendingDsaRequest.GetProTxHash())) {
1221-
mn_addr = Assert(dmn->pdmnState)->addr;
1221+
mn_addr = Assert(dmn->pdmnState)->netInfo.GetPrimary();
12221222
} else {
12231223
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::%s -- cannot find address to connect, masternode=%s\n", __func__,
12241224
pendingDsaRequest.GetProTxHash().ToString());
@@ -1820,7 +1820,7 @@ void CCoinJoinClientSession::RelayIn(const CCoinJoinEntry& entry, CConnman& conn
18201820
{
18211821
if (!mixingMasternode) return;
18221822

1823-
connman.ForNode(mixingMasternode->pdmnState->addr, [&entry, &connman, this](CNode* pnode) {
1823+
connman.ForNode(mixingMasternode->pdmnState->netInfo.GetPrimary(), [&entry, &connman, this](CNode* pnode) {
18241824
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::RelayIn -- found master, relaying message to %s\n",
18251825
pnode->addr.ToStringAddrPort());
18261826
CNetMsgMaker msgMaker(pnode->GetCommonVersion());
@@ -1876,7 +1876,7 @@ void CCoinJoinClientSession::GetJsonInfo(UniValue& obj) const
18761876
assert(mixingMasternode->pdmnState);
18771877
obj.pushKV("protxhash", mixingMasternode->proTxHash.ToString());
18781878
obj.pushKV("outpoint", mixingMasternode->collateralOutpoint.ToStringShort());
1879-
obj.pushKV("service", mixingMasternode->pdmnState->addr.ToStringAddrPort());
1879+
obj.pushKV("service", mixingMasternode->pdmnState->netInfo.GetPrimary().ToStringAddrPort());
18801880
}
18811881
obj.pushKV("denomination", ValueFromAmount(CoinJoin::DenominationToAmount(nSessionDenom)));
18821882
obj.pushKV("state", GetStateString());

src/evo/core_write.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <evo/assetlocktx.h>
77
#include <evo/cbtx.h>
88
#include <evo/mnhftx.h>
9+
#include <evo/netinfo.h>
910
#include <evo/providertx.h>
1011
#include <llmq/commitment.h>
1112

@@ -66,7 +67,7 @@
6667
ret.pushKV("type", ToUnderlying(nType));
6768
ret.pushKV("collateralHash", collateralOutpoint.hash.ToString());
6869
ret.pushKV("collateralIndex", (int)collateralOutpoint.n);
69-
ret.pushKV("service", addr.ToStringAddrPort());
70+
ret.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
7071
ret.pushKV("ownerAddress", EncodeDestination(PKHash(keyIDOwner)));
7172
ret.pushKV("votingAddress", EncodeDestination(PKHash(keyIDVoting)));
7273
if (CTxDestination dest; ExtractDestination(scriptPayout, dest)) {
@@ -113,7 +114,7 @@
113114
ret.pushKV("version", nVersion);
114115
ret.pushKV("type", ToUnderlying(nType));
115116
ret.pushKV("proTxHash", proTxHash.ToString());
116-
ret.pushKV("service", addr.ToStringAddrPort());
117+
ret.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
117118
if (CTxDestination dest; ExtractDestination(scriptOperatorPayout, dest)) {
118119
ret.pushKV("operatorPayoutAddress", EncodeDestination(dest));
119120
}

src/evo/deterministicmns.cpp

+65-47
Original file line numberDiff line numberDiff line change
@@ -470,10 +470,12 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
470470
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate collateralOutpoint=%s", __func__,
471471
dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
472472
}
473-
if (dmn->pdmnState->addr != CService() && !AddUniqueProperty(*dmn, dmn->pdmnState->addr)) {
474-
mnUniquePropertyMap = mnUniquePropertyMapSaved;
475-
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
476-
dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToStringAddrPort())));
473+
for (const CService& entry : dmn->pdmnState->netInfo.GetEntries()) {
474+
if (!AddUniqueProperty(*dmn, entry)) {
475+
mnUniquePropertyMap = mnUniquePropertyMapSaved;
476+
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
477+
dmn->proTxHash.ToString(), entry.ToStringAddrPort())));
478+
}
477479
}
478480
if (!AddUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
479481
mnUniquePropertyMap = mnUniquePropertyMapSaved;
@@ -511,10 +513,28 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s
511513
// Using this temporary map as a checkpoint to roll back to in case of any issues.
512514
decltype(mnUniquePropertyMap) mnUniquePropertyMapSaved = mnUniquePropertyMap;
513515

514-
if (!UpdateUniqueProperty(*dmn, oldState->addr, pdmnState->addr)) {
516+
const auto updateNetInfo = [&]() {
517+
if (oldState->netInfo != pdmnState->netInfo) {
518+
// We track each individual entry in netInfo as opposed to netInfo itself (preventing us from
519+
// using UpdateUniqueProperty()), so we need to successfully purge all old entries and insert
520+
// new entries to successfully update.
521+
for (const CService& old_entry : oldState->netInfo.GetEntries()) {
522+
if (!DeleteUniqueProperty(*dmn, old_entry)) {
523+
return strprintf("internal error"); // This shouldn't be possible
524+
}
525+
}
526+
for (const CService& new_entry : pdmnState->netInfo.GetEntries()) {
527+
if (!AddUniqueProperty(*dmn, new_entry)) {
528+
return strprintf("duplicate (%s)", new_entry.ToStringAddrPort());
529+
}
530+
}
531+
}
532+
return strprintf("");
533+
}();
534+
if (!updateNetInfo.empty()) {
515535
mnUniquePropertyMap = mnUniquePropertyMapSaved;
516-
throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate address=%s", __func__,
517-
oldDmn.proTxHash.ToString(), pdmnState->addr.ToStringAddrPort())));
536+
throw(std::runtime_error(strprintf("%s: Can't update masternode %s with addresses, reason=%s", __func__,
537+
oldDmn.proTxHash.ToString(), updateNetInfo)));
518538
}
519539
if (!UpdateUniqueProperty(*dmn, oldState->keyIDOwner, pdmnState->keyIDOwner)) {
520540
mnUniquePropertyMap = mnUniquePropertyMapSaved;
@@ -571,10 +591,12 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
571591
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a collateralOutpoint=%s", __func__,
572592
proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
573593
}
574-
if (dmn->pdmnState->addr != CService() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->addr)) {
575-
mnUniquePropertyMap = mnUniquePropertyMapSaved;
576-
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a address=%s", __func__,
577-
proTxHash.ToString(), dmn->pdmnState->addr.ToStringAddrPort())));
594+
for (const CService& entry : dmn->pdmnState->netInfo.GetEntries()) {
595+
if (!DeleteUniqueProperty(*dmn, entry)) {
596+
mnUniquePropertyMap = mnUniquePropertyMapSaved;
597+
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with an address=%s", __func__,
598+
proTxHash.ToString(), entry.ToStringAddrPort())));
599+
}
578600
}
579601
if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
580602
mnUniquePropertyMap = mnUniquePropertyMapSaved;
@@ -789,8 +811,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
789811
}
790812
}
791813

792-
if (newList.HasUniqueProperty(proTx.addr)) {
793-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr");
814+
for (const CService& entry : proTx.netInfo.GetEntries()) {
815+
if (newList.HasUniqueProperty(entry)) {
816+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
817+
}
794818
}
795819
if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
796820
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key");
@@ -800,7 +824,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
800824

801825
auto dmnState = std::make_shared<CDeterministicMNState>(proTx);
802826
dmnState->nRegisteredHeight = nHeight;
803-
if (proTx.addr == CService()) {
827+
if (proTx.netInfo.IsEmpty()) {
804828
// start in banned pdmnState as we need to wait for a ProUpServTx
805829
dmnState->BanIfNotBanned(nHeight);
806830
}
@@ -818,8 +842,11 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
818842
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
819843
}
820844

821-
if (newList.HasUniqueProperty(opt_proTx->addr) && newList.GetUniquePropertyMN(opt_proTx->addr)->proTxHash != opt_proTx->proTxHash) {
822-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr");
845+
for (const CService& entry : opt_proTx->netInfo.GetEntries()) {
846+
if (newList.HasUniqueProperty(entry) &&
847+
newList.GetUniquePropertyMN(entry)->proTxHash != opt_proTx->proTxHash) {
848+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
849+
}
823850
}
824851

825852
auto dmn = newList.GetMN(opt_proTx->proTxHash);
@@ -834,7 +861,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
834861
}
835862

836863
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
837-
newState->addr = opt_proTx->addr;
864+
newState->netInfo = opt_proTx->netInfo;
838865
newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout;
839866
if (opt_proTx->nType == MnType::Evo) {
840867
newState->platformNodeID = opt_proTx->platformNodeID;
@@ -1177,28 +1204,15 @@ void CDeterministicMNManager::CleanupCache(int nHeight)
11771204
template <typename ProTx>
11781205
static bool CheckService(const ProTx& proTx, TxValidationState& state)
11791206
{
1180-
if (!proTx.addr.IsValid()) {
1181-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
1182-
}
1183-
if (Params().RequireRoutableExternalIP() && !proTx.addr.IsRoutable()) {
1184-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
1185-
}
1186-
1187-
// TODO: use real args here
1188-
static int mainnetDefaultPort = CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)->GetDefaultPort();
1189-
if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
1190-
if (proTx.addr.GetPort() != mainnetDefaultPort) {
1191-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port");
1192-
}
1193-
} else if (proTx.addr.GetPort() == mainnetDefaultPort) {
1194-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port");
1195-
}
1196-
1197-
if (!proTx.addr.IsIPv4()) {
1198-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
1199-
}
1200-
1201-
return true;
1207+
switch (proTx.netInfo.Validate()) {
1208+
case NetInfoStatus::BadInput:
1209+
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo");
1210+
case NetInfoStatus::BadPort:
1211+
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-port");
1212+
case NetInfoStatus::Success:
1213+
return true;
1214+
} // no default case, so the compiler can warn about missing cases
1215+
assert(false);
12021216
}
12031217

12041218
template <typename ProTx>
@@ -1233,9 +1247,8 @@ static bool CheckPlatformFields(const ProTx& proTx, TxValidationState& state)
12331247
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
12341248
}
12351249

1236-
if (proTx.platformP2PPort == proTx.platformHTTPPort ||
1237-
proTx.platformP2PPort == proTx.addr.GetPort() ||
1238-
proTx.platformHTTPPort == proTx.addr.GetPort()) {
1250+
if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == proTx.netInfo.GetPrimary().GetPort() ||
1251+
proTx.platformHTTPPort == proTx.netInfo.GetPrimary().GetPort()) {
12391252
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports");
12401253
}
12411254

@@ -1300,7 +1313,7 @@ bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl:
13001313

13011314
// It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later
13021315
// If any of both is set, it must be valid however
1303-
if (opt_ptx->addr != CService() && !CheckService(*opt_ptx, state)) {
1316+
if (!opt_ptx->netInfo.IsEmpty() && !CheckService(*opt_ptx, state)) {
13041317
// pass the state returned by the function above
13051318
return false;
13061319
}
@@ -1360,8 +1373,11 @@ bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl:
13601373
auto mnList = dmnman.GetListForBlock(pindexPrev);
13611374

13621375
// only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
1363-
if (mnList.HasUniqueProperty(opt_ptx->addr) && mnList.GetUniquePropertyMN(opt_ptx->addr)->collateralOutpoint != collateralOutpoint) {
1364-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-addr");
1376+
for (const CService& entry : opt_ptx->netInfo.GetEntries()) {
1377+
if (mnList.HasUniqueProperty(entry) &&
1378+
mnList.GetUniquePropertyMN(entry)->collateralOutpoint != collateralOutpoint) {
1379+
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1380+
}
13651381
}
13661382

13671383
// never allow duplicate keys, even if this ProTx would replace an existing MN
@@ -1430,8 +1446,10 @@ bool CheckProUpServTx(CDeterministicMNManager& dmnman, const CTransaction& tx, g
14301446
}
14311447

14321448
// don't allow updating to addresses already used by other MNs
1433-
if (mnList.HasUniqueProperty(opt_ptx->addr) && mnList.GetUniquePropertyMN(opt_ptx->addr)->proTxHash != opt_ptx->proTxHash) {
1434-
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-addr");
1449+
for (const CService& entry : opt_ptx->netInfo.GetEntries()) {
1450+
if (mnList.HasUniqueProperty(entry) && mnList.GetUniquePropertyMN(entry)->proTxHash != opt_ptx->proTxHash) {
1451+
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1452+
}
14351453
}
14361454

14371455
// don't allow updating to platformNodeIds already used by other EvoNodes

src/evo/deterministicmns.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,17 @@ class CDeterministicMNList
394394
template <typename T>
395395
[[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const
396396
{
397-
static_assert(!std::is_same<T, CBLSPublicKey>(), "GetUniquePropertyHash cannot be templated against CBLSPublicKey");
397+
#define DMNL_NO_TEMPLATE(name) \
398+
static_assert(!std::is_same_v<std::decay_t<T>, name>, "GetUniquePropertyHash cannot be templated against " #name)
399+
DMNL_NO_TEMPLATE(CBLSPublicKey);
400+
DMNL_NO_TEMPLATE(MnNetInfo);
401+
#undef DMNL_NO_TEMPLATE
398402
return ::SerializeHash(v);
399403
}
400404
template <typename T>
401405
[[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v)
402406
{
403-
static const T nullValue;
407+
static const T nullValue{};
404408
if (v == nullValue) {
405409
return false;
406410
}
@@ -420,7 +424,7 @@ class CDeterministicMNList
420424
template <typename T>
421425
[[nodiscard]] bool DeleteUniqueProperty(const CDeterministicMN& dmn, const T& oldValue)
422426
{
423-
static const T nullValue;
427+
static const T nullValue{};
424428
if (oldValue == nullValue) {
425429
return false;
426430
}
@@ -443,7 +447,7 @@ class CDeterministicMNList
443447
if (oldValue == newValue) {
444448
return true;
445449
}
446-
static const T nullValue;
450+
static const T nullValue{};
447451

448452
if (oldValue != nullValue && !DeleteUniqueProperty(dmn, oldValue)) {
449453
return false;

src/evo/dmnstate.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,19 @@ std::string CDeterministicMNState::ToString() const
2727

2828
return strprintf("CDeterministicMNState(nVersion=%d, nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, "
2929
"nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
30-
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, addr=%s, payoutAddress=%s, "
31-
"operatorPayoutAddress=%s)",
30+
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s, "
31+
"operatorPayoutAddress=%s)\n"
32+
" %s",
3233
nVersion, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight,
3334
nRevocationReason, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(),
34-
EncodeDestination(PKHash(keyIDVoting)), addr.ToStringAddrPort(), payoutAddress,
35-
operatorPayoutAddress);
35+
EncodeDestination(PKHash(keyIDVoting)), payoutAddress, operatorPayoutAddress, netInfo.ToString());
3636
}
3737

3838
UniValue CDeterministicMNState::ToJson(MnType nType) const
3939
{
4040
UniValue obj(UniValue::VOBJ);
4141
obj.pushKV("version", nVersion);
42-
obj.pushKV("service", addr.ToStringAddrPort());
42+
obj.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
4343
obj.pushKV("registeredHeight", nRegisteredHeight);
4444
obj.pushKV("lastPaidHeight", nLastPaidHeight);
4545
obj.pushKV("consecutivePayments", nConsecutivePayments);
@@ -72,8 +72,8 @@ UniValue CDeterministicMNStateDiff::ToJson(MnType nType) const
7272
if (fields & Field_nVersion) {
7373
obj.pushKV("version", state.nVersion);
7474
}
75-
if (fields & Field_addr) {
76-
obj.pushKV("service", state.addr.ToStringAddrPort());
75+
if (fields & Field_netInfo) {
76+
obj.pushKV("service", state.netInfo.GetPrimary().ToStringAddrPort());
7777
}
7878
if (fields & Field_nRegisteredHeight) {
7979
obj.pushKV("registeredHeight", state.nRegisteredHeight);

0 commit comments

Comments
 (0)