diff --git a/src/Makefile.am b/src/Makefile.am index 74db920908d0..daa4a2568587 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -365,6 +365,7 @@ BITCOIN_CORE_H = \ util/moneystr.h \ util/overflow.h \ util/overloaded.h \ + util/pointer.h \ util/ranges.h \ util/readwritefile.h \ util/underlying.h \ diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index 025823f9b9bd..d5f5185e4b28 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -193,7 +193,7 @@ void CCoinJoinClientSession::ProcessMessage(CNode& peer, CChainState& active_cha if (!m_mn_sync.IsBlockchainSynced()) return; if (!mixingMasternode) return; - if (mixingMasternode->pdmnState->netInfo.GetPrimary() != peer.addr) return; + if (mixingMasternode->pdmnState->netInfo->GetPrimary() != peer.addr) return; if (msg_type == NetMsgType::DSSTATUSUPDATE) { CCoinJoinStatusUpdate psssup; @@ -1113,7 +1113,7 @@ bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, m_clientman.AddUsedMasternode(dsq.masternodeOutpoint); - if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo.GetPrimary())) { + if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo->GetPrimary())) { WalletCJLogPrint(m_wallet, /* Continued */ "CCoinJoinClientSession::JoinExistingQueue -- skipping connection, masternode=%s\n", dmn->proTxHash.ToString()); continue; @@ -1185,7 +1185,7 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon continue; } - if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo.GetPrimary())) { + if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->netInfo->GetPrimary())) { WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::StartNewQueue -- skipping connection, masternode=%s\n", dmn->proTxHash.ToString()); nTries++; @@ -1225,7 +1225,7 @@ bool CCoinJoinClientSession::ProcessPendingDsaRequest(CConnman& connman) CService mn_addr; if (auto dmn = m_dmnman.GetListAtChainTip().GetMN(pendingDsaRequest.GetProTxHash())) { - mn_addr = Assert(dmn->pdmnState)->netInfo.GetPrimary(); + 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()); @@ -1827,7 +1827,7 @@ void CCoinJoinClientSession::RelayIn(const CCoinJoinEntry& entry, CConnman& conn { if (!mixingMasternode) return; - connman.ForNode(mixingMasternode->pdmnState->netInfo.GetPrimary(), [&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()); @@ -1883,7 +1883,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->netInfo.GetPrimary().ToStringAddrPort()); + obj.pushKV("service", mixingMasternode->pdmnState->netInfo->GetPrimary().ToStringAddrPort()); } obj.pushKV("denomination", ValueFromAmount(CoinJoin::DenominationToAmount(nSessionDenom))); obj.pushKV("state", GetStateString()); diff --git a/src/evo/core_write.cpp b/src/evo/core_write.cpp index 55af0bf50352..91ffc5c2270f 100644 --- a/src/evo/core_write.cpp +++ b/src/evo/core_write.cpp @@ -67,7 +67,7 @@ ret.pushKV("type", ToUnderlying(nType)); ret.pushKV("collateralHash", collateralOutpoint.hash.ToString()); ret.pushKV("collateralIndex", (int)collateralOutpoint.n); - ret.pushKV("service", netInfo.GetPrimary().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)) { @@ -114,7 +114,7 @@ ret.pushKV("version", nVersion); ret.pushKV("type", ToUnderlying(nType)); ret.pushKV("proTxHash", proTxHash.ToString()); - ret.pushKV("service", netInfo.GetPrimary().ToStringAddrPort()); + ret.pushKV("service", netInfo->GetPrimary().ToStringAddrPort()); if (CTxDestination dest; ExtractDestination(scriptOperatorPayout, dest)) { ret.pushKV("operatorPayoutAddress", EncodeDestination(dest)); } diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index e38e699f5270..7eecfd6fbbb2 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -409,7 +410,7 @@ 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()))); } - for (const NetInfoEntry& entry : dmn->pdmnState->netInfo.GetEntries()) { + for (const NetInfoEntry& entry : dmn->pdmnState->netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (!AddUniqueProperty(*dmn, service)) { @@ -459,13 +460,13 @@ 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; - auto updateNetInfo = [this](const CDeterministicMN& dmn, const MnNetInfo& oldInfo, - const MnNetInfo& newInfo) -> std::string { - if (oldInfo != newInfo) { + auto updateNetInfo = [this](const CDeterministicMN& dmn, const std::shared_ptr& oldInfo, + const std::shared_ptr& newInfo) -> std::string { + if (util::shared_ptr_not_equal(oldInfo, newInfo)) { // 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 NetInfoEntry& old_entry : oldInfo.GetEntries()) { + for (const NetInfoEntry& old_entry : oldInfo->GetEntries()) { if (const auto& service_opt{old_entry.GetAddrPort()}) { const CService& service{service_opt.value()}; if (!DeleteUniqueProperty(dmn, service)) { @@ -475,7 +476,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s return "invalid address"; } } - for (const NetInfoEntry& new_entry : newInfo.GetEntries()) { + for (const NetInfoEntry& new_entry : newInfo->GetEntries()) { if (const auto& service_opt{new_entry.GetAddrPort()}) { const CService& service{service_opt.value()}; if (!AddUniqueProperty(dmn, service)) { @@ -489,6 +490,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s return ""; }; + assert(oldState->netInfo && pdmnState->netInfo); if (auto err = updateNetInfo(*dmn, oldState->netInfo, pdmnState->netInfo); !err.empty()) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't update masternode %s with addresses, reason=%s", __func__, @@ -549,7 +551,7 @@ 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()))); } - for (const NetInfoEntry& entry : dmn->pdmnState->netInfo.GetEntries()) { + for (const NetInfoEntry& entry : dmn->pdmnState->netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (!DeleteUniqueProperty(*dmn, service)) { @@ -730,6 +732,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no newList.DecreaseScores(); const bool isMNRewardReallocation{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR)}; + const bool is_v23_deployed{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_V23)}; // we skip the coinbase for (int i = 1; i < (int)block.vtx.size(); i++) { @@ -777,7 +780,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no } } - for (const NetInfoEntry& entry : proTx.netInfo.GetEntries()) { + for (const NetInfoEntry& entry : proTx.netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (newList.HasUniqueProperty(service)) { @@ -795,7 +798,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no auto dmnState = std::make_shared(proTx); dmnState->nRegisteredHeight = nHeight; - if (proTx.netInfo.IsEmpty()) { + if (proTx.netInfo->IsEmpty()) { // start in banned pdmnState as we need to wait for a ProUpServTx dmnState->BanIfNotBanned(nHeight); } @@ -813,7 +816,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } - for (const NetInfoEntry& entry : opt_proTx->netInfo.GetEntries()) { + for (const NetInfoEntry& entry : opt_proTx->netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (newList.HasUniqueProperty(service) && @@ -837,6 +840,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no } auto newState = std::make_shared(*dmn->pdmnState); + if (is_v23_deployed) { + // Extended addresses support in v23 means that the version can be updated + newState->nVersion = opt_proTx->nVersion; + } newState->netInfo = opt_proTx->netInfo; newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout; if (opt_proTx->nType == MnType::Evo) { @@ -877,7 +884,9 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no newState->ResetOperatorFields(); newState->BanIfNotBanned(nHeight); // we update pubKeyOperator here, make sure state version matches - newState->nVersion = opt_proTx->nVersion; + // Make sure we don't accidentally downgrade the state version if using version after basic BLS + newState->nVersion = newState->nVersion > ProTxVersion::BasicBLS ? newState->nVersion : opt_proTx->nVersion; + newState->netInfo = NetInfoInterface::MakeNetInfo(newState->nVersion); newState->pubKeyOperator = opt_proTx->pubKeyOperator; } newState->keyIDVoting = opt_proTx->keyIDVoting; @@ -1175,7 +1184,7 @@ void CDeterministicMNManager::CleanupCache(int nHeight) template static bool CheckService(const ProTx& proTx, TxValidationState& state) { - switch (proTx.netInfo.Validate()) { + switch (proTx.netInfo->Validate()) { case NetInfoStatus::BadAddress: return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr"); case NetInfoStatus::BadPort: @@ -1228,8 +1237,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.netInfo.GetPrimary().GetPort() || - proTx.platformHTTPPort == proTx.netInfo.GetPrimary().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"); } @@ -1276,8 +1285,7 @@ static std::optional GetValidatedPayload(const CTransaction& tx, gsl::not state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload"); return std::nullopt; } - const bool is_basic_scheme_active{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}; - if (!opt_ptx->IsTriviallyValid(is_basic_scheme_active, state)) { + if (!opt_ptx->IsTriviallyValid(pindexPrev, state)) { // pass the state returned by the function above return std::nullopt; } @@ -1294,7 +1302,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->netInfo.IsEmpty() && !CheckService(*opt_ptx, state)) { + if (!opt_ptx->netInfo->IsEmpty() && !CheckService(*opt_ptx, state)) { // pass the state returned by the function above return false; } @@ -1354,7 +1362,7 @@ 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) - for (const NetInfoEntry& entry : opt_ptx->netInfo.GetEntries()) { + for (const NetInfoEntry& entry : opt_ptx->netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (mnList.HasUniqueProperty(service) && @@ -1432,7 +1440,7 @@ bool CheckProUpServTx(CDeterministicMNManager& dmnman, const CTransaction& tx, g } // don't allow updating to addresses already used by other MNs - for (const NetInfoEntry& entry : opt_ptx->netInfo.GetEntries()) { + for (const NetInfoEntry& entry : opt_ptx->netInfo->GetEntries()) { if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { const CService& service{service_opt.value()}; if (mnList.HasUniqueProperty(service) && mnList.GetUniquePropertyMN(service)->proTxHash != opt_ptx->proTxHash) { diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 5b0c246a0242..3b0eee1bfbba 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -62,47 +62,26 @@ class CDeterministicMN // only non-initial values assert(_internalId != std::numeric_limits::max()); } - template - CDeterministicMN(deserialize_type, Stream& s, const uint8_t format_version) - { - SerializationOp(s, CSerActionUnserialize(), format_version); - } + CDeterministicMN(deserialize_type, Stream& s) { s >> *this; } - template - inline void SerializationOp(Stream& s, Operation ser_action, const uint8_t format_version) + SERIALIZE_METHODS(CDeterministicMN, obj) { - // We no longer support EvoDB formats below MN_VERSION_FORMAT - if (format_version < MN_VERSION_FORMAT) { - throw std::ios_base::failure("EvoDb too old, run Dash Core with -reindex to rebuild"); - } - READWRITE(proTxHash); - READWRITE(VARINT(internalId)); - READWRITE(collateralOutpoint); - READWRITE(nOperatorReward); - READWRITE(pdmnState); + READWRITE(obj.proTxHash); + READWRITE(VARINT(obj.internalId)); + READWRITE(obj.collateralOutpoint); + READWRITE(obj.nOperatorReward); + READWRITE(obj.pdmnState); // We can't know if we are serialising for the Disk or for the Network here (s.GetType() is not accessible) // Therefore if s.GetVersion() == CLIENT_VERSION -> Then we know we are serialising for the Disk // Otherwise, we can safely check with protocol versioning logic so we won't break old clients if (s.GetVersion() == CLIENT_VERSION || s.GetVersion() >= DMN_TYPE_PROTO_VERSION) { - READWRITE(nType); + READWRITE(obj.nType); } else { - nType = MnType::Regular; + SER_READ(obj, obj.nType = MnType::Regular); } } - template - void Serialize(Stream& s) const - { - const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_CURRENT_FORMAT); - } - - template - void Unserialize(Stream& s, const uint8_t format_version = MN_CURRENT_FORMAT) - { - SerializationOp(s, CSerActionUnserialize(), format_version); - } - [[nodiscard]] uint64_t GetInternalId() const; [[nodiscard]] std::string ToString() const; @@ -193,27 +172,36 @@ class CDeterministicMNList void Serialize(Stream& s) const { const_cast(this)->SerializationOpBase(s, CSerActionSerialize()); + // Serialize the map as a vector WriteCompactSize(s, mnMap.size()); - for (const auto& p : mnMap) { - s << *p.second; + for (const auto& [_, dmn] : mnMap) { + s << *dmn; } } - template - void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) { - mnMap = MnMap(); - mnUniquePropertyMap = MnUniquePropertyMap(); - mnInternalIdMap = MnInternalIdMap(); + template + void Unserialize(Stream& s) + { + Clear(); SerializationOpBase(s, CSerActionUnserialize()); - size_t cnt = ReadCompactSize(s); - for (size_t i = 0; i < cnt; i++) { - AddMN(std::make_shared(deserialize, s, format_version), false); + for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) { + AddMN(std::make_shared(deserialize, s), /*fBumpTotalCount=*/false); } } + void Clear() + { + blockHash = uint256{}; + nHeight = -1; + nTotalRegisteredCount = 0; + mnMap = MnMap(); + mnUniquePropertyMap = MnUniquePropertyMap(); + mnInternalIdMap = MnInternalIdMap(); + } + [[nodiscard]] size_t GetAllMNsCount() const { return mnMap.size(); @@ -397,6 +385,8 @@ class CDeterministicMNList DMNL_NO_TEMPLATE(CBLSPublicKey); DMNL_NO_TEMPLATE(MnNetInfo); DMNL_NO_TEMPLATE(NetInfoEntry); + DMNL_NO_TEMPLATE(NetInfoInterface); + DMNL_NO_TEMPLATE(std::shared_ptr); #undef DMNL_NO_TEMPLATE return ::SerializeHash(v); } @@ -483,46 +473,39 @@ class CDeterministicMNListDiff void Serialize(Stream& s) const { s << addedMNs; + WriteCompactSize(s, updatedMNs.size()); - for (const auto& p : updatedMNs) { - WriteVarInt(s, p.first); - s << p.second; + for (const auto& [internalId, pdmnState] : updatedMNs) { + WriteVarInt(s, internalId); + s << pdmnState; } + WriteCompactSize(s, removedMns.size()); - for (const auto& p : removedMns) { - WriteVarInt(s, p); + for (const auto& internalId : removedMns) { + WriteVarInt(s, internalId); } } template - void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) + void Unserialize(Stream& s) { updatedMNs.clear(); removedMns.clear(); - size_t tmp; - uint64_t tmp2; - tmp = ReadCompactSize(s); - for (size_t i = 0; i < tmp; i++) { - CDeterministicMN mn(0); - mn.Unserialize(s, format_version); - auto dmn = std::make_shared(mn); - addedMNs.push_back(dmn); + for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) { + addedMNs.push_back(std::make_shared(deserialize, s)); } - tmp = ReadCompactSize(s); - for (size_t i = 0; i < tmp; i++) { - CDeterministicMNStateDiff diff; - // CDeterministicMNState hold new fields {nConsecutivePayments, platformNodeID, platformP2PPort, platformHTTPPort} but no migration is needed here since: - // CDeterministicMNStateDiff is always serialised using a bitmask. - // Because the new field have a new bit guide value then we are good to continue - tmp2 = ReadVarInt(s); - s >> diff; - updatedMNs.emplace(tmp2, std::move(diff)); + + for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) { + uint64_t internalId = ReadVarInt(s); + // CDeterministicMNState can have newer fields but doesn't need migration logic here as CDeterministicMNStateDiff + // is always serialised using a bitmask and new fields have a new bit guide value, so we are good to continue. + updatedMNs.emplace(internalId, CDeterministicMNStateDiff(deserialize, s)); } - tmp = ReadCompactSize(s); - for (size_t i = 0; i < tmp; i++) { - tmp2 = ReadVarInt(s); - removedMns.emplace(tmp2); + + for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) { + uint64_t internalId = ReadVarInt(s); + removedMns.emplace(internalId); } } diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index df09e2d4611c..dd95a8b5ee4d 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -32,14 +32,14 @@ std::string CDeterministicMNState::ToString() const " %s", nVersion, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), - EncodeDestination(PKHash(keyIDVoting)), payoutAddress, operatorPayoutAddress, netInfo.ToString()); + EncodeDestination(PKHash(keyIDVoting)), payoutAddress, operatorPayoutAddress, netInfo->ToString()); } UniValue CDeterministicMNState::ToJson(MnType nType) const { UniValue obj(UniValue::VOBJ); obj.pushKV("version", nVersion); - obj.pushKV("service", netInfo.GetPrimary().ToStringAddrPort()); + obj.pushKV("service", netInfo->GetPrimary().ToStringAddrPort()); obj.pushKV("registeredHeight", nRegisteredHeight); obj.pushKV("lastPaidHeight", nLastPaidHeight); obj.pushKV("consecutivePayments", nConsecutivePayments); @@ -73,7 +73,7 @@ UniValue CDeterministicMNStateDiff::ToJson(MnType nType) const obj.pushKV("version", state.nVersion); } if (fields & Field_netInfo) { - obj.pushKV("service", state.netInfo.GetPrimary().ToStringAddrPort()); + obj.pushKV("service", state.netInfo->GetPrimary().ToStringAddrPort()); } if (fields & Field_nRegisteredHeight) { obj.pushKV("registeredHeight", state.nRegisteredHeight); diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index 49a3b4745d55..9d2f45f3eb9d 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -11,6 +11,7 @@ #include #include #include