Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ BITCOIN_CORE_H = \
evo/evodb.h \
evo/mnauth.h \
evo/mnhftx.h \
evo/netinfo.h \
Comment thread
knst marked this conversation as resolved.
evo/providertx.h \
evo/simplifiedmns.h \
evo/specialtx.h \
Expand Down Expand Up @@ -776,6 +777,7 @@ libbitcoin_common_a_SOURCES = \
core_write.cpp \
deploymentinfo.cpp \
evo/core_write.cpp \
evo/netinfo.cpp \
Comment thread
knst marked this conversation as resolved.
Outdated
governance/common.cpp \
init/common.cpp \
key.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ BITCOIN_TESTS =\
test/evo_deterministicmns_tests.cpp \
test/evo_islock_tests.cpp \
test/evo_mnhf_tests.cpp \
test/evo_netinfo_tests.cpp \
Comment thread
knst marked this conversation as resolved.
Outdated
test/evo_simplifiedmns_tests.cpp \
test/evo_trivialvalidation.cpp \
test/evo_utils_tests.cpp \
Expand Down
12 changes: 6 additions & 6 deletions src/coinjoin/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ void CCoinJoinClientSession::ProcessMessage(CNode& peer, CChainState& active_cha
if (!m_mn_sync.IsBlockchainSynced()) return;

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

if (msg_type == NetMsgType::DSSTATUSUPDATE) {
CCoinJoinStatusUpdate psssup;
Expand Down Expand Up @@ -1106,7 +1106,7 @@ bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized,

m_clientman.AddUsedMasternode(dsq.masternodeOutpoint);

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

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

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

connman.ForNode(mixingMasternode->pdmnState->addr, [&entry, &connman, this](CNode* pnode) {
connman.ForNode(mixingMasternode->pdmnState->netInfo.GetPrimary(), [&entry, &connman, this](CNode* pnode) {
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::RelayIn -- found master, relaying message to %s\n",
pnode->addr.ToStringAddrPort());
CNetMsgMaker msgMaker(pnode->GetCommonVersion());
Expand Down Expand Up @@ -1876,7 +1876,7 @@ void CCoinJoinClientSession::GetJsonInfo(UniValue& obj) const
assert(mixingMasternode->pdmnState);
obj.pushKV("protxhash", mixingMasternode->proTxHash.ToString());
obj.pushKV("outpoint", mixingMasternode->collateralOutpoint.ToStringShort());
obj.pushKV("service", mixingMasternode->pdmnState->addr.ToStringAddrPort());
obj.pushKV("service", mixingMasternode->pdmnState->netInfo.GetPrimary().ToStringAddrPort());
}
obj.pushKV("denomination", ValueFromAmount(CoinJoin::DenominationToAmount(nSessionDenom)));
obj.pushKV("state", GetStateString());
Expand Down
5 changes: 3 additions & 2 deletions src/evo/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <evo/assetlocktx.h>
#include <evo/cbtx.h>
#include <evo/mnhftx.h>
#include <evo/netinfo.h>
#include <evo/providertx.h>
#include <llmq/commitment.h>

Expand Down Expand Up @@ -66,7 +67,7 @@
ret.pushKV("type", ToUnderlying(nType));
ret.pushKV("collateralHash", collateralOutpoint.hash.ToString());
ret.pushKV("collateralIndex", (int)collateralOutpoint.n);
ret.pushKV("service", addr.ToStringAddrPort());
ret.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
ret.pushKV("ownerAddress", EncodeDestination(PKHash(keyIDOwner)));
ret.pushKV("votingAddress", EncodeDestination(PKHash(keyIDVoting)));
if (CTxDestination dest; ExtractDestination(scriptPayout, dest)) {
Expand Down Expand Up @@ -113,7 +114,7 @@
ret.pushKV("version", nVersion);
ret.pushKV("type", ToUnderlying(nType));
ret.pushKV("proTxHash", proTxHash.ToString());
ret.pushKV("service", addr.ToStringAddrPort());
ret.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
if (CTxDestination dest; ExtractDestination(scriptOperatorPayout, dest)) {
ret.pushKV("operatorPayoutAddress", EncodeDestination(dest));
}
Expand Down
112 changes: 65 additions & 47 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,12 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate collateralOutpoint=%s", __func__,
dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
}
if (dmn->pdmnState->addr != CService() && !AddUniqueProperty(*dmn, dmn->pdmnState->addr)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToStringAddrPort())));
for (const CService& entry : dmn->pdmnState->netInfo.GetEntries()) {
if (!AddUniqueProperty(*dmn, entry)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
dmn->proTxHash.ToString(), entry.ToStringAddrPort())));
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
if (!AddUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
Expand Down Expand Up @@ -511,10 +513,28 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s
// Using this temporary map as a checkpoint to roll back to in case of any issues.
decltype(mnUniquePropertyMap) mnUniquePropertyMapSaved = mnUniquePropertyMap;

if (!UpdateUniqueProperty(*dmn, oldState->addr, pdmnState->addr)) {
const auto updateNetInfo = [&]() {
if (oldState->netInfo != pdmnState->netInfo) {
// We track each individual entry in netInfo as opposed to netInfo itself (preventing us from
// using UpdateUniqueProperty()), so we need to successfully purge all old entries and insert
// new entries to successfully update.
for (const CService& old_entry : oldState->netInfo.GetEntries()) {
if (!DeleteUniqueProperty(*dmn, old_entry)) {
return strprintf("internal error"); // This shouldn't be possible
}
}
for (const CService& new_entry : pdmnState->netInfo.GetEntries()) {
if (!AddUniqueProperty(*dmn, new_entry)) {
return strprintf("duplicate (%s)", new_entry.ToStringAddrPort());
}
}
}
return strprintf("");
}();
if (!updateNetInfo.empty()) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate address=%s", __func__,
oldDmn.proTxHash.ToString(), pdmnState->addr.ToStringAddrPort())));
throw(std::runtime_error(strprintf("%s: Can't update masternode %s with addresses, reason=%s", __func__,
oldDmn.proTxHash.ToString(), updateNetInfo)));
}
if (!UpdateUniqueProperty(*dmn, oldState->keyIDOwner, pdmnState->keyIDOwner)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
Expand Down Expand Up @@ -571,10 +591,12 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a collateralOutpoint=%s", __func__,
proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
}
if (dmn->pdmnState->addr != CService() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->addr)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a address=%s", __func__,
proTxHash.ToString(), dmn->pdmnState->addr.ToStringAddrPort())));
for (const CService& entry : dmn->pdmnState->netInfo.GetEntries()) {
if (!DeleteUniqueProperty(*dmn, entry)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with an address=%s", __func__,
proTxHash.ToString(), entry.ToStringAddrPort())));
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
Expand Down Expand Up @@ -789,8 +811,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
}
}

if (newList.HasUniqueProperty(proTx.addr)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr");
for (const CService& entry : proTx.netInfo.GetEntries()) {
if (newList.HasUniqueProperty(entry)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
}
}
if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key");
Expand All @@ -800,7 +824,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no

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

if (newList.HasUniqueProperty(opt_proTx->addr) && newList.GetUniquePropertyMN(opt_proTx->addr)->proTxHash != opt_proTx->proTxHash) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr");
for (const CService& entry : opt_proTx->netInfo.GetEntries()) {
if (newList.HasUniqueProperty(entry) &&
newList.GetUniquePropertyMN(entry)->proTxHash != opt_proTx->proTxHash) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
}
}

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

auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->addr = opt_proTx->addr;
newState->netInfo = opt_proTx->netInfo;
newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout;
if (opt_proTx->nType == MnType::Evo) {
newState->platformNodeID = opt_proTx->platformNodeID;
Expand Down Expand Up @@ -1177,28 +1204,15 @@ void CDeterministicMNManager::CleanupCache(int nHeight)
template <typename ProTx>
static bool CheckService(const ProTx& proTx, TxValidationState& state)
{
if (!proTx.addr.IsValid()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
}
if (Params().RequireRoutableExternalIP() && !proTx.addr.IsRoutable()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
}

// TODO: use real args here
static int mainnetDefaultPort = CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)->GetDefaultPort();
if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
if (proTx.addr.GetPort() != mainnetDefaultPort) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port");
}
} else if (proTx.addr.GetPort() == mainnetDefaultPort) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port");
}

if (!proTx.addr.IsIPv4()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr");
}

return true;
switch (proTx.netInfo.Validate()) {
case NetInfoStatus::BadInput:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo");
case NetInfoStatus::BadPort:
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-port");
case NetInfoStatus::Success:
return true;
} // no default case, so the compiler can warn about missing cases
assert(false);
}

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

if (proTx.platformP2PPort == proTx.platformHTTPPort ||
proTx.platformP2PPort == proTx.addr.GetPort() ||
proTx.platformHTTPPort == proTx.addr.GetPort()) {
if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == proTx.netInfo.GetPrimary().GetPort() ||
proTx.platformHTTPPort == proTx.netInfo.GetPrimary().GetPort()) {
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports");
}

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

// 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
// If any of both is set, it must be valid however
if (opt_ptx->addr != CService() && !CheckService(*opt_ptx, state)) {
if (!opt_ptx->netInfo.IsEmpty() && !CheckService(*opt_ptx, state)) {
// pass the state returned by the function above
return false;
}
Expand Down Expand Up @@ -1360,8 +1373,11 @@ bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl:
auto mnList = dmnman.GetListForBlock(pindexPrev);

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

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

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

// don't allow updating to platformNodeIds already used by other EvoNodes
Expand Down
12 changes: 8 additions & 4 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,17 @@ class CDeterministicMNList
template <typename T>
[[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const
{
static_assert(!std::is_same<T, CBLSPublicKey>(), "GetUniquePropertyHash cannot be templated against CBLSPublicKey");
#define DMNL_NO_TEMPLATE(name) \
static_assert(!std::is_same_v<std::decay_t<T>, name>, "GetUniquePropertyHash cannot be templated against " #name)
DMNL_NO_TEMPLATE(CBLSPublicKey);
DMNL_NO_TEMPLATE(MnNetInfo);
#undef DMNL_NO_TEMPLATE
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: I'd personally prefer copy-paste here rather than using define

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Blocklist expands in later PRs (see below, WIP code) and would also include ExtNetInfo since we only want to allow storing individual CServices and DomainPorts and therefore need to prohibit higher-level components.

https://github.com/dashpay/dash/blob/7a9bfa2873fcb53b9f5f61b709f95adbc4c5f666/src/evo/deterministicmns.h#L397-L402

return ::SerializeHash(v);
}
template <typename T>
[[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v)
{
static const T nullValue;
static const T nullValue{};
if (v == nullValue) {
return false;
}
Expand All @@ -420,7 +424,7 @@ class CDeterministicMNList
template <typename T>
[[nodiscard]] bool DeleteUniqueProperty(const CDeterministicMN& dmn, const T& oldValue)
{
static const T nullValue;
static const T nullValue{};
if (oldValue == nullValue) {
return false;
}
Expand All @@ -443,7 +447,7 @@ class CDeterministicMNList
if (oldValue == newValue) {
return true;
}
static const T nullValue;
static const T nullValue{};

if (oldValue != nullValue && !DeleteUniqueProperty(dmn, oldValue)) {
return false;
Expand Down
14 changes: 7 additions & 7 deletions src/evo/dmnstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ std::string CDeterministicMNState::ToString() const

return strprintf("CDeterministicMNState(nVersion=%d, nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, "
"nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, addr=%s, payoutAddress=%s, "
"operatorPayoutAddress=%s)",
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s, "
"operatorPayoutAddress=%s)\n"
" %s",
nVersion, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight,
nRevocationReason, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(),
EncodeDestination(PKHash(keyIDVoting)), addr.ToStringAddrPort(), payoutAddress,
operatorPayoutAddress);
EncodeDestination(PKHash(keyIDVoting)), payoutAddress, operatorPayoutAddress, netInfo.ToString());
}

UniValue CDeterministicMNState::ToJson(MnType nType) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", nVersion);
obj.pushKV("service", addr.ToStringAddrPort());
obj.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
obj.pushKV("registeredHeight", nRegisteredHeight);
obj.pushKV("lastPaidHeight", nLastPaidHeight);
Comment on lines 40 to 44
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

netInfo.GetPrimary() can crash when netInfo is empty

GetPrimary() dereferences the first element without a guard. While normal
flows should guarantee at least one address, defensive code is advisable for
RPC output. Returning "unknown" (as is already done for payoutAddress) keeps
RPC stable instead of terminating the process in edge/fuzz cases.

-    obj.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
+    obj.pushKV("service",
+               netInfo.HasEntries() ? netInfo.GetPrimary().ToStringAddrPort()
+                                    : "unknown");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", nVersion);
obj.pushKV("service", addr.ToStringAddrPort());
obj.pushKV("service", netInfo.GetPrimary().ToStringAddrPort());
obj.pushKV("registeredHeight", nRegisteredHeight);
obj.pushKV("lastPaidHeight", nLastPaidHeight);
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", nVersion);
obj.pushKV("service",
netInfo.HasEntries() ? netInfo.GetPrimary().ToStringAddrPort()
: "unknown");
obj.pushKV("registeredHeight", nRegisteredHeight);
obj.pushKV("lastPaidHeight", nLastPaidHeight);

obj.pushKV("consecutivePayments", nConsecutivePayments);
Expand Down Expand Up @@ -72,8 +72,8 @@ UniValue CDeterministicMNStateDiff::ToJson(MnType nType) const
if (fields & Field_nVersion) {
obj.pushKV("version", state.nVersion);
}
if (fields & Field_addr) {
obj.pushKV("service", state.addr.ToStringAddrPort());
if (fields & Field_netInfo) {
obj.pushKV("service", state.netInfo.GetPrimary().ToStringAddrPort());
}
if (fields & Field_nRegisteredHeight) {
obj.pushKV("registeredHeight", state.nRegisteredHeight);
Expand Down
Loading
Loading