diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index 540e0fd5713ca..c18c156d5f5e5 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -25,7 +25,6 @@ 'src/ctpl_stl.h', 'src/test/fuzz/FuzzedDataProvider.h', 'src/tinyformat.h', - 'src/util/expected.h', 'src/wallet/bip39.cpp', 'src/wallet/bip39.h', 'src/wallet/bip39_english.h', diff --git a/src/Makefile.am b/src/Makefile.am index fff2e0bbba192..18c318829dc59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -357,7 +357,6 @@ BITCOIN_CORE_H = \ util/enumerate.h \ util/epochguard.h \ util/error.h \ - util/expected.h \ util/fastrange.h \ util/fees.h \ util/golombrice.h \ diff --git a/src/chainlock/chainlock.h b/src/chainlock/chainlock.h index 69ba8130ae75b..3a41db671d725 100644 --- a/src/chainlock/chainlock.h +++ b/src/chainlock/chainlock.h @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -28,8 +29,6 @@ class CScheduler; class CSporkManager; class CTxMemPool; -using NodeId = int64_t; - namespace llmq { class CInstantSendManager; class CQuorumManager; diff --git a/src/chainlock/signing.h b/src/chainlock/signing.h index a07df18265cd8..dc483c46423e5 100644 --- a/src/chainlock/signing.h +++ b/src/chainlock/signing.h @@ -30,7 +30,7 @@ class ChainLockSignerParent virtual bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const = 0; virtual bool IsEnabled() const = 0; virtual bool IsTxSafeForMining(const uint256& txid) const = 0; - virtual MessageProcessingResult ProcessNewChainLock(NodeId from, const ChainLockSig& clsig, const uint256& hash) = 0; + [[nodiscard]] virtual MessageProcessingResult ProcessNewChainLock(NodeId from, const ChainLockSig& clsig, const uint256& hash) = 0; virtual void UpdateTxFirstSeenMap(const std::unordered_set& tx, const int64_t& time) = 0; }; diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index 35593152c8b2b..33fe73539c5ae 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -38,33 +38,27 @@ using wallet::CoinType; using wallet::CWallet; using wallet::ReserveDestination; -PeerMsgRet CCoinJoinClientQueueManager::ProcessMessage(const CNode& peer, CConnman& connman, PeerManager& peerman, - std::string_view msg_type, CDataStream& vRecv) +MessageProcessingResult CCoinJoinClientQueueManager::ProcessMessage(NodeId from, CConnman& connman, PeerManager& peerman, + std::string_view msg_type, CDataStream& vRecv) { + if (msg_type != NetMsgType::DSQUEUE) { + return {}; + } + if (m_is_masternode) return {}; if (!m_mn_sync.IsBlockchainSynced()) return {}; - if (msg_type == NetMsgType::DSQUEUE) { - return CCoinJoinClientQueueManager::ProcessDSQueue(peer, connman, peerman, vRecv); - } - return {}; -} - -PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnman& connman, PeerManager& peerman, - CDataStream& vRecv) -{ assert(m_mn_metaman.IsValid()); CCoinJoinQueue dsq; vRecv >> dsq; - { - LOCK(::cs_main); - peerman.EraseObjectRequest(peer.GetId(), CInv(MSG_DSQ, dsq.GetHash())); - } + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_DSQ, dsq.GetHash()}; if (dsq.masternodeOutpoint.IsNull() && dsq.m_protxHash.IsNull()) { - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } const auto tip_mn_list = m_dmnman.GetListAtChainTip(); @@ -72,7 +66,8 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnm if (auto dmn = tip_mn_list.GetValidMN(dsq.m_protxHash)) { dsq.masternodeOutpoint = dmn->collateralOutpoint; } else { - return tl::unexpected{10}; + ret.m_error = MisbehavingError{10}; + return ret; } } @@ -84,31 +79,32 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnm // process every dsq only once for (const auto &q: vecCoinJoinQueue) { if (q == dsq) { - return {}; + return ret; } if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) { // no way the same mn can send another dsq with the same readiness this soon LogPrint(BCLog::COINJOIN, /* Continued */ - "DSQUEUE -- Peer %s is sending WAY too many dsq messages for a masternode with collateral %s\n", - peer.GetLogString(), dsq.masternodeOutpoint.ToStringShort()); - return {}; + "DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with collateral %s\n", + from, dsq.masternodeOutpoint.ToStringShort()); + return ret; } } } // cs_vecqueue LogPrint(BCLog::COINJOIN, "DSQUEUE -- %s new\n", dsq.ToString()); - if (dsq.IsTimeOutOfBounds()) return {}; + if (dsq.IsTimeOutOfBounds()) return ret; auto dmn = tip_mn_list.GetValidMNByCollateral(dsq.masternodeOutpoint); - if (!dmn) return {}; + if (!dmn) return ret; if (dsq.m_protxHash.IsNull()) { dsq.m_protxHash = dmn->proTxHash; } if (!dsq.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) { - return tl::unexpected{10}; + ret.m_error = MisbehavingError{10}; + return ret; } // if the queue is ready, submit if we can @@ -117,7 +113,7 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnm return clientman->TrySubmitDenominate(dmn->proTxHash, connman); })) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- CoinJoin queue is ready, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString()); - return {}; + return ret; } else { int64_t nLastDsq = m_mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); int64_t nDsqThreshold = m_mn_metaman.GetDsqThreshold(dmn->proTxHash, tip_mn_list.GetValidMNsCount()); @@ -127,7 +123,7 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnm if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n", dmn->proTxHash.ToString()); - return {}; + return ret; } m_mn_metaman.AllowMixing(dmn->proTxHash); @@ -142,7 +138,7 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CConnm } } // cs_ProcessDSQueue peerman.RelayDSQ(dsq); - return {}; + return ret; } void CCoinJoinClientManager::ProcessMessage(CNode& peer, CChainState& active_chainstate, CConnman& connman, const CTxMemPool& mempool, std::string_view msg_type, CDataStream& vRecv) diff --git a/src/coinjoin/client.h b/src/coinjoin/client.h index bdeaccc7d9db6..7d403e33bd364 100644 --- a/src/coinjoin/client.h +++ b/src/coinjoin/client.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -252,9 +253,9 @@ class CCoinJoinClientQueueManager : public CCoinJoinBaseManager m_mn_sync(mn_sync), m_is_masternode{is_masternode} {}; - PeerMsgRet ProcessMessage(const CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, - CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue); - PeerMsgRet ProcessDSQueue(const CNode& peer, CConnman& connman, PeerManager& peerman, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, CConnman& connman, PeerManager& peerman, std::string_view msg_type, + CDataStream& vRecv) + EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue); void DoMaintenance(); }; diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index a90df185ef1a4..ad068dc59db1a 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -23,7 +23,7 @@ #include -PeerMsgRet CCoinJoinServer::ProcessMessage(CNode& peer, std::string_view msg_type, CDataStream& vRecv) +MessageProcessingResult CCoinJoinServer::ProcessMessage(CNode& peer, std::string_view msg_type, CDataStream& vRecv) { if (!m_mn_activeman) return {}; if (!m_mn_sync.IsBlockchainSynced()) return {}; @@ -31,7 +31,7 @@ PeerMsgRet CCoinJoinServer::ProcessMessage(CNode& peer, std::string_view msg_typ if (msg_type == NetMsgType::DSACCEPT) { ProcessDSACCEPT(peer, vRecv); } else if (msg_type == NetMsgType::DSQUEUE) { - return ProcessDSQUEUE(peer, vRecv); + return ProcessDSQUEUE(peer.GetId(), vRecv); } else if (msg_type == NetMsgType::DSVIN) { ProcessDSVIN(peer, vRecv); } else if (msg_type == NetMsgType::DSSIGNFINALTX) { @@ -109,20 +109,19 @@ void CCoinJoinServer::ProcessDSACCEPT(CNode& peer, CDataStream& vRecv) } } -PeerMsgRet CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv) +MessageProcessingResult CCoinJoinServer::ProcessDSQUEUE(NodeId from, CDataStream& vRecv) { assert(m_mn_metaman.IsValid()); CCoinJoinQueue dsq; vRecv >> dsq; - { - LOCK(::cs_main); - Assert(m_peerman)->EraseObjectRequest(peer.GetId(), CInv(MSG_DSQ, dsq.GetHash())); - } + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_DSQ, dsq.GetHash()}; if (dsq.masternodeOutpoint.IsNull() && dsq.m_protxHash.IsNull()) { - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } const auto tip_mn_list = m_dmnman.GetListAtChainTip(); @@ -130,40 +129,42 @@ PeerMsgRet CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv if (auto dmn = tip_mn_list.GetValidMN(dsq.m_protxHash)) { dsq.masternodeOutpoint = dmn->collateralOutpoint; } else { - return tl::unexpected{10}; + ret.m_error = MisbehavingError{10}; + return ret; } } { TRY_LOCK(cs_vecqueue, lockRecv); - if (!lockRecv) return {}; + if (!lockRecv) return ret; // process every dsq only once for (const auto& q : vecCoinJoinQueue) { if (q == dsq) { - return {}; + return ret; } if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) { // no way the same mn can send another dsq with the same readiness this soon - LogPrint(BCLog::COINJOIN, "DSQUEUE -- Peer %s is sending WAY too many dsq messages for a masternode with collateral %s\n", peer.GetLogString(), dsq.masternodeOutpoint.ToStringShort()); - return {}; + LogPrint(BCLog::COINJOIN, "DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with collateral %s\n", from, dsq.masternodeOutpoint.ToStringShort()); + return ret; } } } // cs_vecqueue LogPrint(BCLog::COINJOIN, "DSQUEUE -- %s new\n", dsq.ToString()); - if (dsq.IsTimeOutOfBounds()) return {}; + if (dsq.IsTimeOutOfBounds()) return ret; auto dmn = tip_mn_list.GetValidMNByCollateral(dsq.masternodeOutpoint); - if (!dmn) return {}; + if (!dmn) return ret; if (dsq.m_protxHash.IsNull()) { dsq.m_protxHash = dmn->proTxHash; } if (!dsq.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) { - return tl::unexpected{10}; + ret.m_error = MisbehavingError{10}; + return ret; } if (!dsq.fReady) { @@ -173,18 +174,18 @@ PeerMsgRet CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv //don't allow a few nodes to dominate the queuing process if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- node sending too many dsq messages, masternode=%s\n", dmn->proTxHash.ToString()); - return {}; + return ret; } m_mn_metaman.AllowMixing(dmn->proTxHash); LogPrint(BCLog::COINJOIN, "DSQUEUE -- new CoinJoin queue, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString()); TRY_LOCK(cs_vecqueue, lockRecv); - if (!lockRecv) return {}; + if (!lockRecv) return ret; vecCoinJoinQueue.push_back(dsq); m_peerman->RelayDSQ(dsq); } - return {}; + return ret; } void CCoinJoinServer::ProcessDSVIN(CNode& peer, CDataStream& vRecv) diff --git a/src/coinjoin/server.h b/src/coinjoin/server.h index c0611fc4c8825..eee182fb92307 100644 --- a/src/coinjoin/server.h +++ b/src/coinjoin/server.h @@ -7,6 +7,7 @@ #include +#include #include class CActiveMasternodeManager; @@ -85,7 +86,7 @@ class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager void RelayCompletedTransaction(PoolMessage nMessageID) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin); void ProcessDSACCEPT(CNode& peer, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue); - PeerMsgRet ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue); + [[nodiscard]] MessageProcessingResult ProcessDSQUEUE(NodeId from, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue); void ProcessDSVIN(CNode& peer, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin); void ProcessDSSIGNFINALTX(CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin); @@ -110,7 +111,7 @@ class CCoinJoinServer : public CCoinJoinBaseSession, public CCoinJoinBaseManager fUnitTest(false) {} - PeerMsgRet ProcessMessage(CNode& pfrom, std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& pfrom, std::string_view msg_type, CDataStream& vRecv); bool HasTimedOut() const; void CheckTimeout(); diff --git a/src/evo/mnauth.cpp b/src/evo/mnauth.cpp index 0358a2f0db99a..3c2b080059c1e 100644 --- a/src/evo/mnauth.cpp +++ b/src/evo/mnauth.cpp @@ -55,9 +55,9 @@ void CMNAuth::PushMNAUTH(CNode& peer, CConnman& connman, const CActiveMasternode connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::MNAUTH, mnauth)); } -PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman, - const CMasternodeSync& mn_sync, const CDeterministicMNList& tip_mn_list, - std::string_view msg_type, CDataStream& vRecv) +MessageProcessingResult CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, + const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync, + const CDeterministicMNList& tip_mn_list, std::string_view msg_type, CDataStream& vRecv) { assert(mn_metaman.IsValid()); @@ -71,22 +71,22 @@ PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CCon // only one MNAUTH allowed if (!peer.GetVerifiedProRegTxHash().IsNull()) { - return tl::unexpected{MisbehavingError{100, "duplicate mnauth"}}; + return MisbehavingError{100, "duplicate mnauth"}; } if ((~node_services) & (NODE_NETWORK | NODE_BLOOM)) { // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services - return tl::unexpected{MisbehavingError{100, "mnauth from a node with invalid services"}}; + return MisbehavingError{100, "mnauth from a node with invalid services"}; } if (mnauth.proRegTxHash.IsNull()) { - return tl::unexpected{MisbehavingError{100, "empty mnauth proRegTxHash"}}; + return MisbehavingError{100, "empty mnauth proRegTxHash"}; } if (!mnauth.sig.IsValid()) { LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- invalid mnauth for protx=%s with sig=%s\n", mnauth.proRegTxHash.ToString(), mnauth.sig.ToString(false)); - return tl::unexpected{MisbehavingError{100, "invalid mnauth signature"}}; + return MisbehavingError{100, "invalid mnauth signature"}; } const auto dmn = tip_mn_list.GetMN(mnauth.proRegTxHash); @@ -94,7 +94,7 @@ PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CCon // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a // low DoS score. - return tl::unexpected{MisbehavingError{10, "missing mnauth masternode"}}; + return MisbehavingError{10, "missing mnauth masternode"}; } uint256 signHash; @@ -114,7 +114,7 @@ PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CCon if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash, false)) { // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a // malicious node (DoSing us), it'll get banned soon. - return tl::unexpected{MisbehavingError{10, "mnauth signature verification failed"}}; + return MisbehavingError{10, "mnauth signature verification failed"}; } if (!peer.IsInboundConn()) { diff --git a/src/evo/mnauth.h b/src/evo/mnauth.h index d883f8900fd8f..9508cca595c8f 100644 --- a/src/evo/mnauth.h +++ b/src/evo/mnauth.h @@ -58,9 +58,9 @@ class CMNAuth * @pre CMasternodeMetaMan's database must be successfully loaded before * attempting to call this function regardless of sync state */ - static PeerMsgRet ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman, - const CMasternodeSync& mn_sync, const CDeterministicMNList& tip_mn_list, - std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] static MessageProcessingResult ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, + const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync, + const CDeterministicMNList& tip_mn_list, std::string_view msg_type, CDataStream& vRecv); static void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman); }; diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 06bb466e9de4d..16a32e0333673 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -140,7 +140,7 @@ bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream& return cmapVoteToObject.Get(nHash, pGovobj) && pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss); } -PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv) +MessageProcessingResult CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv) { if (!IsValid()) return {}; if (!m_mn_sync.IsBlockchainSynced()) return {}; @@ -155,17 +155,17 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe uint256 nProp; CBloomFilter filter; - vRecv >> nProp; - vRecv >> filter; LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing governance objects to our peer %s\n", peer.GetLogString()); if (nProp == uint256()) { - return SyncObjects(peer, peerman, connman); + return SyncObjects(peer, connman); } else { - SyncSingleObjVotes(peer, peerman, nProp, filter, connman); + return SyncSingleObjVotes(peer, nProp, filter, connman); } + + return {}; } // A NEW GOVERNANCE OBJECT HAS ARRIVED @@ -177,11 +177,12 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe uint256 nHash = govobj.GetHash(); - WITH_LOCK(::cs_main, peerman.EraseObjectRequest(peer.GetId(), CInv(MSG_GOVERNANCE_OBJECT, nHash))); + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_GOVERNANCE_OBJECT, nHash}; if (!m_mn_sync.IsBlockchainSynced()) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode list not synced\n"); - return {}; + return ret; } std::string strHash = nHash.ToString(); @@ -190,7 +191,7 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe if (!AcceptMessage(nHash)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash); - return {}; + return ret; } LOCK2(::cs_main, cs); @@ -198,13 +199,13 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || mapErasedGovernanceObjects.count(nHash)) { // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE? LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash); - return {}; + return ret; } bool fRateCheckBypassed = false; if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", strHash, nCachedBlockHeight); - return {}; + return ret; } std::string strError; @@ -215,7 +216,7 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current block height %d)\n", strHash, nCachedBlockHeight); - return {}; + return ret; } if (!fIsValid) { @@ -225,13 +226,15 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe } else { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError); // apply node's ban score - return tl::unexpected{20}; + ret.m_error = MisbehavingError{20}; + return ret; } - return {}; + return ret; } AddGovernanceObject(govobj, peerman, &peer); + return ret; } // A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED @@ -240,12 +243,14 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe vRecv >> vote; uint256 nHash = vote.GetHash(); - WITH_LOCK(::cs_main, peerman.EraseObjectRequest(peer.GetId(), CInv(MSG_GOVERNANCE_OBJECT_VOTE, nHash))); + + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash}; // Ignore such messages until masternode list is synced if (!m_mn_sync.IsBlockchainSynced()) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n"); - return {}; + return ret; } LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received vote: %s\n", vote.ToString(tip_mn_list)); @@ -255,7 +260,7 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe if (!AcceptMessage(nHash)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", vote.ToString(tip_mn_list), strHash, peer.GetId()); - return {}; + return ret; } CGovernanceException exception; @@ -266,11 +271,14 @@ PeerMsgRet CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, Pe } else { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what()); if ((exception.GetNodePenalty() != 0) && m_mn_sync.IsSynced()) { - return tl::unexpected{exception.GetNodePenalty()}; + ret.m_error = MisbehavingError{exception.GetNodePenalty()}; + return ret; } - return {}; + return ret; } + return ret; } + return {}; } @@ -911,12 +919,10 @@ bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) return true; } -void CGovernanceManager::SyncSingleObjVotes(CNode& peer, PeerManager& peerman, const uint256& nProp, const CBloomFilter& filter, CConnman& connman) +MessageProcessingResult CGovernanceManager::SyncSingleObjVotes(CNode& peer, const uint256& nProp, const CBloomFilter& filter, CConnman& connman) { // do not provide any data until our node is synced - if (!m_mn_sync.IsSynced()) return; - - int nVoteCount = 0; + if (!m_mn_sync.IsSynced()) return {}; // SYNC GOVERNANCE OBJECTS WITH OTHER CLIENT @@ -928,7 +934,7 @@ void CGovernanceManager::SyncSingleObjVotes(CNode& peer, PeerManager& peerman, c auto it = mapObjects.find(nProp); if (it == mapObjects.end()) { LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- no matching object for hash %s, peer=%d\n", __func__, nProp.ToString(), peer.GetId()); - return; + return {}; } const CGovernanceObject& govobj = it->second; std::string strHash = it->first.ToString(); @@ -938,12 +944,13 @@ void CGovernanceManager::SyncSingleObjVotes(CNode& peer, PeerManager& peerman, c if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- not syncing deleted/expired govobj: %s, peer=%d\n", __func__, strHash, peer.GetId()); - return; + return {}; } const auto& fileVotes = govobj.GetVoteFile(); const auto tip_mn_list = Assert(m_dmnman)->GetListAtChainTip(); + MessageProcessingResult ret{}; for (const auto& vote : fileVotes.GetVotes()) { uint256 nVoteHash = vote.GetHash(); @@ -952,16 +959,18 @@ void CGovernanceManager::SyncSingleObjVotes(CNode& peer, PeerManager& peerman, c if (filter.contains(nVoteHash) || !vote.IsValid(tip_mn_list, onlyVotingKeyAllowed)) { continue; } - peerman.PushInventory(peer.GetId(), CInv(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash)); - ++nVoteCount; + ret.m_inventory.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash); } CNetMsgMaker msgMaker(peer.GetCommonVersion()); - connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ_VOTE, nVoteCount)); - LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- sent %d votes to peer=%d\n", __func__, nVoteCount, peer.GetId()); + connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ_VOTE, + static_cast(ret.m_inventory.size()))); + LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- sent %d votes to peer=%d\n", __func__, ret.m_inventory.size(), + peer.GetId()); + return ret; } -PeerMsgRet CGovernanceManager::SyncObjects(CNode& peer, PeerManager& peerman, CConnman& connman) const +MessageProcessingResult CGovernanceManager::SyncObjects(CNode& peer, CConnman& connman) const { assert(m_netfulfilledman.IsValid()); @@ -971,12 +980,10 @@ PeerMsgRet CGovernanceManager::SyncObjects(CNode& peer, PeerManager& peerman, CC if (m_netfulfilledman.HasFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC)) { // Asking for the whole list multiple times in a short period of time is no good LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- peer already asked me for the list\n", __func__); - return tl::unexpected{20}; + return MisbehavingError{20}; } m_netfulfilledman.AddFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC); - int nObjCount = 0; - // SYNC GOVERNANCE OBJECTS WITH OTHER CLIENT LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- syncing all objects to peer=%d\n", __func__, peer.GetId()); @@ -984,6 +991,7 @@ PeerMsgRet CGovernanceManager::SyncObjects(CNode& peer, PeerManager& peerman, CC LOCK(cs); // all valid objects, no votes + MessageProcessingResult ret{}; for (const auto& objPair : mapObjects) { uint256 nHash = objPair.first; const CGovernanceObject& govobj = objPair.second; @@ -1012,14 +1020,15 @@ PeerMsgRet CGovernanceManager::SyncObjects(CNode& peer, PeerManager& peerman, CC // Push the inventory budget proposal message over to the other client LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- syncing govobj: %s, peer=%d\n", __func__, strHash, peer.GetId()); - peerman.PushInventory(peer.GetId(), CInv(MSG_GOVERNANCE_OBJECT, nHash)); - ++nObjCount; + ret.m_inventory.emplace_back(MSG_GOVERNANCE_OBJECT, nHash); } CNetMsgMaker msgMaker(peer.GetCommonVersion()); - connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ, nObjCount)); - LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- sent %d objects to peer=%d\n", __func__, nObjCount, peer.GetId()); - return {}; + connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ, + static_cast(ret.m_inventory.size()))); + LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- sent %d objects to peer=%d\n", __func__, ret.m_inventory.size(), + peer.GetId()); + return ret; } void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj) diff --git a/src/governance/governance.h b/src/governance/governance.h index adc25bb5431c1..ca77d19acafe2 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -14,6 +14,7 @@ #include #include +#include class CBloomFilter; class CBlockIndex; @@ -271,10 +272,10 @@ class CGovernanceManager : public GovernanceStore */ bool ConfirmInventoryRequest(const CInv& inv); - void SyncSingleObjVotes(CNode& peer, PeerManager& peerman, const uint256& nProp, const CBloomFilter& filter, CConnman& connman); - PeerMsgRet SyncObjects(CNode& peer, PeerManager& peerman, CConnman& connman) const; + [[nodiscard]] MessageProcessingResult SyncSingleObjVotes(CNode& peer, const uint256& nProp, const CBloomFilter& filter, CConnman& connman); + [[nodiscard]] MessageProcessingResult SyncObjects(CNode& peer, CConnman& connman) const; - PeerMsgRet ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv); private: void ResetVotedFundingTrigger(); diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index ea182b3dfb604..b84265d3bbc61 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -102,36 +102,34 @@ void CInstantSendManager::Stop() } } -PeerMsgRet CInstantSendManager::ProcessMessage(const CNode& pfrom, PeerManager& peerman, std::string_view msg_type, - CDataStream& vRecv) -{ - if (IsInstantSendEnabled() && msg_type == NetMsgType::ISDLOCK) { - const auto islock = std::make_shared(); - vRecv >> *islock; - return ProcessMessageInstantSendLock(pfrom, peerman, islock); - } - return {}; -} - bool ShouldReportISLockTiming() { return g_stats_client->active() || LogAcceptDebug(BCLog::INSTANTSEND); } -PeerMsgRet CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom, PeerManager& peerman, - const instantsend::InstantSendLockPtr& islock) +MessageProcessingResult CInstantSendManager::ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv) { + if (!IsInstantSendEnabled() || msg_type != NetMsgType::ISDLOCK) { + return {}; + } + + const auto islock = std::make_shared(); + vRecv >> *islock; + auto hash = ::SerializeHash(*islock); - WITH_LOCK(::cs_main, peerman.EraseObjectRequest(pfrom.GetId(), CInv(MSG_ISDLOCK, hash))); + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_ISDLOCK, hash}; if (!islock->TriviallyValid()) { - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } const auto blockIndex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(islock->cycleHash)); if (blockIndex == nullptr) { // Maybe we don't have the block yet or maybe some peer spams invalid values for cycleHash - return tl::unexpected{1}; + ret.m_error = MisbehavingError{1}; + return ret; } // Deterministic islocks MUST use rotation based llmq @@ -139,16 +137,17 @@ PeerMsgRet CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom const auto& llmq_params_opt = Params().GetLLMQ(llmqType); assert(llmq_params_opt); if (blockIndex->nHeight % llmq_params_opt->dkgInterval != 0) { - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } if (WITH_LOCK(cs_pendingLocks, return pendingInstantSendLocks.count(hash) || pendingNoTxInstantSendLocks.count(hash)) || db.KnownInstantSendLock(hash)) { - return {}; + return ret; } LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s: received islock, peer=%d\n", __func__, - islock->txid.ToString(), hash.ToString(), pfrom.GetId()); + islock->txid.ToString(), hash.ToString(), from); if (ShouldReportISLockTiming()) { auto time_diff = [&]() -> int64_t { @@ -168,8 +167,8 @@ PeerMsgRet CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom } LOCK(cs_pendingLocks); - pendingInstantSendLocks.emplace(hash, std::make_pair(pfrom.GetId(), islock)); - return {}; + pendingInstantSendLocks.emplace(hash, std::make_pair(from, islock)); + return ret; } bool CInstantSendManager::ProcessPendingInstantSendLocks(PeerManager& peerman) diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 645f33005a7aa..6a045952eee2a 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -25,7 +25,6 @@ class CBlockIndex; class CChainState; class CDataStream; class CMasternodeSync; -class CNode; class CSporkManager; class CTxMemPool; class PeerManager; @@ -36,8 +35,6 @@ namespace instantsend { class InstantSendSigner; } // namespace instantsend -using NodeId = int64_t; - namespace llmq { class CChainLocksHandler; class CQuorumManager; @@ -98,7 +95,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent void InterruptWorkerThread() { workInterrupt(); }; private: - PeerMsgRet ProcessMessageInstantSendLock(const CNode& pfrom, PeerManager& peerman, const instantsend::InstantSendLockPtr& islock); bool ProcessPendingInstantSendLocks(PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry); @@ -133,7 +129,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent bool IsWaitingForTx(const uint256& txHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); instantsend::InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const override; - PeerMsgRet ProcessMessage(const CNode& pfrom, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); void TransactionAddedToMempool(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry); diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index e554948a7429a..fdb2323fc23ba 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -147,7 +147,9 @@ MessageProcessingResult CQuorumBlockProcessor::ProcessMessage(const CNode& peer, LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- received commitment for quorum %s:%d, validMembers=%d, signers=%d, peer=%d\n", __func__, qc.quorumHash.ToString(), ToUnderlying(qc.llmqType), qc.CountValidMembers(), qc.CountSigners(), peer.GetId()); - ret.m_inventory = AddMineableCommitment(qc); + if (auto inv_opt = AddMineableCommitment(qc)) { + ret.m_inventory.emplace_back(inv_opt.value()); + } return ret; } diff --git a/src/llmq/blockprocessor.h b/src/llmq/blockprocessor.h index 170e0007f742d..f096b3b0ba8d3 100644 --- a/src/llmq/blockprocessor.h +++ b/src/llmq/blockprocessor.h @@ -53,7 +53,7 @@ class CQuorumBlockProcessor explicit CQuorumBlockProcessor(CChainState& chainstate, CDeterministicMNManager& dmnman, CEvoDB& evoDb, CQuorumSnapshotManager& qsnapman); - MessageProcessingResult ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv); bool ProcessBlock(const CBlock& block, gsl::not_null pindex, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); bool UndoBlock(const CBlock& block, gsl::not_null pindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index 7ef53456c8d49..306b74b91b079 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -58,7 +58,7 @@ CDKGSessionHandler::CDKGSessionHandler(CBLSWorker& _blsWorker, CChainState& chai CDKGSessionHandler::~CDKGSessionHandler() = default; -void CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv, PeerManager& peerman) +MessageProcessingResult CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv) { // this will also consume the data, even if we bail out early auto pm = std::make_shared(std::move(vRecv)); @@ -67,8 +67,9 @@ void CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv, Pe hw.write(AsWritableBytes(Span{*pm})); uint256 hash = hw.GetHash(); + MessageProcessingResult ret{}; if (from != -1) { - WITH_LOCK(::cs_main, peerman.EraseObjectRequest(from, CInv(invType, hash))); + ret.m_to_erase = CInv{invType, hash}; } LOCK(cs_messages); @@ -76,16 +77,17 @@ void CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv, Pe if (messagesPerNode[from] >= maxMessagesPerNode) { // TODO ban? LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- too many messages, peer=%d\n", __func__, from); - return; + return ret; } messagesPerNode[from]++; if (!seenMessages.emplace(hash).second) { LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d\n", __func__, hash.ToString(), from); - return; + return ret; } pendingMessages.emplace_back(std::make_pair(from, std::move(pm))); + return ret; } std::list CDKGPendingMessages::PopPendingMessages(size_t maxCount) @@ -150,19 +152,19 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, ToUnderlying(oldPhase), ToUnderlying(phase)); } -void CDKGSessionHandler::ProcessMessage(const CNode& pfrom, PeerManager& peerman, const std::string& msg_type, - CDataStream& vRecv) +MessageProcessingResult CDKGSessionHandler::ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv) { // We don't handle messages in the calling thread as deserialization/processing of these would block everything if (msg_type == NetMsgType::QCONTRIB) { - pendingContributions.PushPendingMessage(pfrom.GetId(), vRecv, peerman); + return pendingContributions.PushPendingMessage(from, vRecv); } else if (msg_type == NetMsgType::QCOMPLAINT) { - pendingComplaints.PushPendingMessage(pfrom.GetId(), vRecv, peerman); + return pendingComplaints.PushPendingMessage(from, vRecv); } else if (msg_type == NetMsgType::QJUSTIFICATION) { - pendingJustifications.PushPendingMessage(pfrom.GetId(), vRecv, peerman); + return pendingJustifications.PushPendingMessage(from, vRecv); } else if (msg_type == NetMsgType::QPCOMMITMENT) { - pendingPrematureCommitments.PushPendingMessage(pfrom.GetId(), vRecv, peerman); + return pendingPrematureCommitments.PushPendingMessage(from, vRecv); } + return {}; } void CDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index 5985242f5cc4e..faab21a69b17a 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -6,6 +6,7 @@ #define BITCOIN_LLMQ_DKGSESSIONHANDLER_H #include // for NodeId +#include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -24,7 +26,6 @@ class CChainState; class CConnman; class CDeterministicMNManager; class CMasternodeMetaMan; -class CNode; class CSporkManager; class PeerManager; @@ -64,7 +65,7 @@ class CDKGPendingMessages using BinaryMessage = std::pair>; private: - const int invType; + const uint32_t invType; const size_t maxMessagesPerNode; mutable Mutex cs_messages; std::list pendingMessages GUARDED_BY(cs_messages); @@ -72,10 +73,10 @@ class CDKGPendingMessages std::set seenMessages GUARDED_BY(cs_messages); public: - explicit CDKGPendingMessages(size_t _maxMessagesPerNode, int _invType) : + explicit CDKGPendingMessages(size_t _maxMessagesPerNode, uint32_t _invType) : invType(_invType), maxMessagesPerNode(_maxMessagesPerNode) {}; - void PushPendingMessage(NodeId from, CDataStream& vRecv, PeerManager& peerman); + [[nodiscard]] MessageProcessingResult PushPendingMessage(NodeId from, CDataStream& vRecv); std::list PopPendingMessages(size_t maxCount); bool HasSeen(const uint256& hash) const; void Misbehaving(NodeId from, int score, PeerManager& peerman); @@ -86,7 +87,7 @@ class CDKGPendingMessages { CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); ds << msg; - PushPendingMessage(from, ds, peerman); + peerman.PostProcessMessage(PushPendingMessage(from, ds), from); } // Might return nullptr messages, which indicates that deserialization failed for some reason @@ -165,7 +166,7 @@ class CDKGSessionHandler ~CDKGSessionHandler(); void UpdatedBlockTip(const CBlockIndex *pindexNew); - void ProcessMessage(const CNode& pfrom, PeerManager& peerman, const std::string& msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); void StartThread(CConnman& connman, PeerManager& peerman); void StopThread(); diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 49ca664a597fc..ead3e12568715 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -92,8 +92,8 @@ void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fIni } } -PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman, bool is_masternode, - const std::string& msg_type, CDataStream& vRecv) +MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, + CDataStream& vRecv) { static Mutex cs_indexedQuorumsCache; static std::map> indexedQuorumsCache GUARDED_BY(cs_indexedQuorumsCache); @@ -112,7 +112,7 @@ PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman if (msg_type == NetMsgType::QWATCH) { if (!is_masternode) { // non-masternodes should never receive this - return tl::unexpected{10}; + return MisbehavingError{10}; } pfrom.qwatch = true; return {}; @@ -120,11 +120,11 @@ PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman if ((!is_masternode && !IsWatchQuorumsEnabled())) { // regular non-watching nodes should never receive any of these - return tl::unexpected{10}; + return MisbehavingError{10}; } if (vRecv.empty()) { - return tl::unexpected{100}; + return MisbehavingError{100}; } Consensus::LLMQType llmqType; @@ -137,7 +137,7 @@ PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman const auto& llmq_params_opt = Params().GetLLMQ(llmqType); if (!llmq_params_opt.has_value()) { LogPrintf("CDKGSessionManager -- invalid llmqType [%d]\n", ToUnderlying(llmqType)); - return tl::unexpected{100}; + return MisbehavingError{100}; } const auto& llmq_params = llmq_params_opt.value(); @@ -158,12 +158,12 @@ PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman if (pQuorumBaseBlockIndex == nullptr) { LogPrintf("CDKGSessionManager -- unknown quorumHash %s\n", quorumHash.ToString()); // NOTE: do not insta-ban for this, we might be lagging behind - return tl::unexpected{10}; + return MisbehavingError{10}; } if (!IsQuorumTypeEnabled(llmqType, pQuorumBaseBlockIndex->pprev)) { LogPrintf("CDKGSessionManager -- llmqType [%d] quorums aren't active\n", ToUnderlying(llmqType)); - return tl::unexpected{100}; + return MisbehavingError{100}; } quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval; @@ -172,19 +172,18 @@ PeerMsgRet CDKGSessionManager::ProcessMessage(CNode& pfrom, PeerManager& peerman if (quorumIndex > quorumIndexMax) { LogPrintf("CDKGSessionManager -- invalid quorumHash %s\n", quorumHash.ToString()); - return tl::unexpected{100}; + return MisbehavingError{100}; } if (!dkgSessionHandlers.count(std::make_pair(llmqType, quorumIndex))) { LogPrintf("CDKGSessionManager -- no session handlers for quorumIndex [%d]\n", quorumIndex); - return tl::unexpected{100}; + return MisbehavingError{100}; } } assert(quorumIndex != -1); WITH_LOCK(cs_indexedQuorumsCache, indexedQuorumsCache[llmqType].insert(quorumHash, quorumIndex)); - dkgSessionHandlers.at(std::make_pair(llmqType, quorumIndex)).ProcessMessage(pfrom, peerman, msg_type, vRecv); - return {}; + return dkgSessionHandlers.at(std::make_pair(llmqType, quorumIndex)).ProcessMessage(pfrom.GetId(), msg_type, vRecv); } bool CDKGSessionManager::AlreadyHave(const CInv& inv) const diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 721b932d43a1e..51fb2fdbe8e9a 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -12,6 +12,7 @@ #include #include +#include template class CBLSIESMultiRecipientObjects; @@ -88,8 +89,8 @@ class CDKGSessionManager void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload); - PeerMsgRet ProcessMessage(CNode& pfrom, PeerManager& peerman, bool is_masternode, const std::string& msg_type, - CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, + CDataStream& vRecv); bool AlreadyHave(const CInv& inv) const; bool GetContribution(const uint256& hash, CDKGContribution& ret) const; bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const; diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp index 86e1f29c62408..711feb6e2dd59 100644 --- a/src/llmq/quorums.cpp +++ b/src/llmq/quorums.cpp @@ -703,13 +703,13 @@ size_t CQuorumManager::GetQuorumRecoveryStartOffset(const CQuorumCPtr pQuorum, c return nIndex % pQuorum->qc->validMembers.size(); } -PeerMsgRet CQuorumManager::ProcessMessage(CNode& pfrom, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) +MessageProcessingResult CQuorumManager::ProcessMessage(CNode& pfrom, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) { auto strFunc = __func__; - auto errorHandler = [&](const std::string& strError, int nScore = 10) -> PeerMsgRet { + auto errorHandler = [&](const std::string& strError, int nScore = 10) -> MessageProcessingResult { LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s: %s, from peer=%d\n", strFunc, msg_type, strError, pfrom.GetId()); if (nScore > 0) { - return tl::unexpected{nScore}; + return MisbehavingError{nScore}; } return {}; }; @@ -724,8 +724,8 @@ PeerMsgRet CQuorumManager::ProcessMessage(CNode& pfrom, CConnman& connman, const auto sendQDATA = [&](CQuorumDataRequest::Errors nError, bool request_limit_exceeded, - const CDataStream& body = CDataStream(SER_NETWORK, PROTOCOL_VERSION)) -> PeerMsgRet { - PeerMsgRet ret{}; + const CDataStream& body = CDataStream(SER_NETWORK, PROTOCOL_VERSION)) -> MessageProcessingResult { + MessageProcessingResult ret{}; switch (nError) { case (CQuorumDataRequest::Errors::NONE): case (CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID): diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h index 915b42aebf390..cc5d110dcd592 100644 --- a/src/llmq/quorums.h +++ b/src/llmq/quorums.h @@ -275,7 +275,7 @@ class CQuorumManager void UpdatedBlockTip(const CBlockIndex* pindexNew, CConnman& connman, bool fInitialDownload) const; - PeerMsgRet ProcessMessage(CNode& pfrom, CConnman& connman, const std::string& msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& pfrom, CConnman& connman, std::string_view msg_type, CDataStream& vRecv); static bool HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash); diff --git a/src/llmq/signing.cpp b/src/llmq/signing.cpp index b05375c63d620..810e543c7f788 100644 --- a/src/llmq/signing.cpp +++ b/src/llmq/signing.cpp @@ -380,18 +380,6 @@ bool CSigningManager::GetRecoveredSigForGetData(const uint256& hash, CRecoveredS return true; } -PeerMsgRet CSigningManager::ProcessMessage(const CNode& pfrom, PeerManager& peerman, const std::string& msg_type, - CDataStream& vRecv) -{ - if (msg_type == NetMsgType::QSIGREC) { - auto recoveredSig = std::make_shared(); - vRecv >> *recoveredSig; - - return ProcessMessageRecoveredSig(pfrom, peerman, recoveredSig); - } - return {}; -} - static bool PreVerifyRecoveredSig(const CQuorumManager& quorum_manager, const CRecoveredSig& recoveredSig, bool& retBan) { retBan = false; @@ -416,39 +404,46 @@ static bool PreVerifyRecoveredSig(const CQuorumManager& quorum_manager, const CR return true; } -PeerMsgRet CSigningManager::ProcessMessageRecoveredSig(const CNode& pfrom, PeerManager& peerman, - const std::shared_ptr& recoveredSig) +MessageProcessingResult CSigningManager::ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv) { - WITH_LOCK(::cs_main, - peerman.EraseObjectRequest(pfrom.GetId(), CInv(MSG_QUORUM_RECOVERED_SIG, recoveredSig->GetHash()))); + if (msg_type != NetMsgType::QSIGREC) { + return {}; + } + + auto recoveredSig = std::make_shared(); + vRecv >> *recoveredSig; + + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_QUORUM_RECOVERED_SIG, recoveredSig->GetHash()}; bool ban = false; if (!PreVerifyRecoveredSig(qman, *recoveredSig, ban)) { if (ban) { - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } - return {}; + return ret; } // It's important to only skip seen *valid* sig shares here. See comment for CBatchedSigShare // We don't receive recovered sigs in batches, but we do batched verification per node on these if (db.HasRecoveredSigForHash(recoveredSig->GetHash())) { - return {}; + return ret; } LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__, - recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), pfrom.GetId()); + recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from); LOCK(cs_pending); if (pendingReconstructedRecoveredSigs.count(recoveredSig->GetHash())) { // no need to perform full verification LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already pending reconstructed sig, signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__, - recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), pfrom.GetId()); - return {}; + recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from); + return ret; } - pendingRecoveredSigs[pfrom.GetId()].emplace_back(recoveredSig); - return {}; + pendingRecoveredSigs[from].emplace_back(recoveredSig); + return ret; } void CSigningManager::CollectPendingRecoveredSigsToVerify( diff --git a/src/llmq/signing.h b/src/llmq/signing.h index bb01087b60822..b36af4eb8d4a4 100644 --- a/src/llmq/signing.h +++ b/src/llmq/signing.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include +#include #include class CActiveMasternodeManager; @@ -23,12 +25,9 @@ class CDataStream; class CDBBatch; class CDBWrapper; class CInv; -class CNode; class PeerManager; class UniValue; -using NodeId = int64_t; - namespace llmq { class CQuorum; @@ -182,7 +181,7 @@ class CSigningManager bool AlreadyHave(const CInv& inv) const; bool GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret) const; - PeerMsgRet ProcessMessage(const CNode& pnode, PeerManager& peerman, const std::string& msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); // This is called when a recovered signature was was reconstructed from another P2P message and is known to be valid // This is the case for example when a signature appears as part of InstantSend or ChainLocks @@ -195,9 +194,6 @@ class CSigningManager void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id); private: - PeerMsgRet ProcessMessageRecoveredSig(const CNode& pfrom, PeerManager& peerman, - const std::shared_ptr& recoveredSig); - void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions, std::unordered_map>>& retSigShares, std::unordered_map, CQuorumCPtr, StaticSaltedHasher>& retQuorums); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 02b4d347d4889..9199874a4bc47 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -627,10 +627,10 @@ class PeerManagerImpl final : public PeerManager bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);; void PushInventory(NodeId nodeid, const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); - void RelayInv(CInv &inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); - void RelayInv(CInv &inv, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); - void RelayInvFiltered(CInv &inv, const CTransaction &relatedTx, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); - void RelayInvFiltered(CInv &inv, const uint256 &relatedTxHash, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + void RelayInv(const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + void RelayInv(const CInv& inv, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + void RelayInvFiltered(const CInv& inv, const CTransaction& relatedTx, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + void RelayInvFiltered(const CInv& inv, const uint256& relatedTxHash, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void RelayTransaction(const uint256& txid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void RelayRecoveredSig(const uint256& sigHash) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void RelayDSQ(const CCoinJoinQueue& queue) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); @@ -650,8 +650,7 @@ class PeerManagerImpl final : public PeerManager private: void _RelayTransaction(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - /** Helpers to process result of external handlers of message */ - void ProcessPeerMsgRet(const PeerMsgRet& ret, CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + /** Helper to process result of external handlers of message */ void PostProcessMessage(MessageProcessingResult&& ret, NodeId node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ @@ -2343,7 +2342,7 @@ void PeerManagerImpl::PushInventory(NodeId nodeid, const CInv& inv) PushInv(*peer, inv); } -void PeerManagerImpl::RelayInv(CInv &inv, const int minProtoVersion) +void PeerManagerImpl::RelayInv(const CInv& inv, const int minProtoVersion) { // TODO: Migrate to iteration through m_peer_map m_connman.ForEachNode([&](CNode* pnode) { @@ -2356,7 +2355,7 @@ void PeerManagerImpl::RelayInv(CInv &inv, const int minProtoVersion) }); } -void PeerManagerImpl::RelayInv(CInv &inv) +void PeerManagerImpl::RelayInv(const CInv& inv) { LOCK(m_peer_mutex); for (const auto& [_, peer] : m_peer_map) { @@ -2393,7 +2392,7 @@ void PeerManagerImpl::RelayDSQ(const CCoinJoinQueue& queue) } } -void PeerManagerImpl::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx, const int minProtoVersion) +void PeerManagerImpl::RelayInvFiltered(const CInv& inv, const CTransaction& relatedTx, const int minProtoVersion) { // TODO: Migrate to iteration through m_peer_map m_connman.ForEachNode([&](CNode* pnode) { @@ -2418,7 +2417,7 @@ void PeerManagerImpl::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx, }); } -void PeerManagerImpl::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash, const int minProtoVersion) +void PeerManagerImpl::RelayInvFiltered(const CInv& inv, const uint256& relatedTxHash, const int minProtoVersion) { // TODO: Migrate to iteration through m_peer_map m_connman.ForEachNode([&](CNode* pnode) { @@ -3492,11 +3491,6 @@ void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptrqueueman->ProcessMessage(pfrom, m_connman, *this, msg_type, vRecv), pfrom); + PostProcessMessage(m_cj_ctx->queueman->ProcessMessage(pfrom.GetId(), m_connman, *this, msg_type, vRecv), pfrom.GetId()); m_cj_ctx->walletman->ForEachCJClientMan([this, &pfrom, &msg_type, &vRecv](std::unique_ptr& clientman) { clientman->ProcessMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv); }); #endif // ENABLE_WALLET - ProcessPeerMsgRet(m_cj_ctx->server->ProcessMessage(pfrom, msg_type, vRecv), pfrom); - ProcessPeerMsgRet(m_sporkman.ProcessMessage(pfrom, m_connman, *this, msg_type, vRecv), pfrom); + PostProcessMessage(m_cj_ctx->server->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); + PostProcessMessage(m_sporkman.ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); m_mn_sync.ProcessMessage(pfrom, msg_type, vRecv); - ProcessPeerMsgRet(m_govman.ProcessMessage(pfrom, m_connman, *this, msg_type, vRecv), pfrom); - ProcessPeerMsgRet(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_mn_activeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom); + PostProcessMessage(m_govman.ProcessMessage(pfrom, m_connman, *this, msg_type, vRecv), pfrom.GetId()); + PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_mn_activeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); - ProcessPeerMsgRet(m_llmq_ctx->qdkgsman->ProcessMessage(pfrom, *this, is_masternode, msg_type, vRecv), pfrom); - ProcessPeerMsgRet(m_llmq_ctx->qman->ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom); + PostProcessMessage(m_llmq_ctx->qdkgsman->ProcessMessage(pfrom, is_masternode, msg_type, vRecv), pfrom.GetId()); + PostProcessMessage(m_llmq_ctx->qman->ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); m_llmq_ctx->shareman->ProcessMessage(pfrom, *this, m_sporkman, msg_type, vRecv); - ProcessPeerMsgRet(m_llmq_ctx->sigman->ProcessMessage(pfrom, *this, msg_type, vRecv), pfrom); + PostProcessMessage(m_llmq_ctx->sigman->ProcessMessage(pfrom.GetId(), msg_type, vRecv), pfrom.GetId()); if (msg_type == NetMsgType::CLSIG) { if (llmq::AreChainLocksEnabled(m_sporkman)) { @@ -5289,7 +5283,7 @@ void PeerManagerImpl::ProcessMessage( return; // CLSIG } - ProcessPeerMsgRet(m_llmq_ctx->isman->ProcessMessage(pfrom, *this, msg_type, vRecv), pfrom); + PostProcessMessage(m_llmq_ctx->isman->ProcessMessage(pfrom.GetId(), msg_type, vRecv), pfrom.GetId()); return; } diff --git a/src/net_processing.h b/src/net_processing.h index be19716117bdd..f9f297947f04f 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -98,16 +98,16 @@ class PeerManager : public CValidationInterface, public NetEventsInterface virtual void RelayDSQ(const CCoinJoinQueue& queue) = 0; /** Relay inventories to all peers */ - virtual void RelayInv(CInv &inv) = 0; - virtual void RelayInv(CInv &inv, const int minProtoVersion) = 0; - virtual void RelayInvFiltered(CInv &inv, const CTransaction &relatedTx, + virtual void RelayInv(const CInv& inv) = 0; + virtual void RelayInv(const CInv& inv, const int minProtoVersion) = 0; + virtual void RelayInvFiltered(const CInv& inv, const CTransaction& relatedTx, const int minProtoVersion = MIN_PEER_PROTO_VERSION) = 0; /** * This overload will not update node filters, use it only for the cases * when other messages will update related transaction data in filters */ - virtual void RelayInvFiltered(CInv &inv, const uint256 &relatedTxHash, + virtual void RelayInvFiltered(const CInv& inv, const uint256& relatedTxHash, const int minProtoVersion = MIN_PEER_PROTO_VERSION) = 0; /** Relay transaction to all peers. */ diff --git a/src/net_types.h b/src/net_types.h index 652962fdd90ca..6f334562b0fb2 100644 --- a/src/net_types.h +++ b/src/net_types.h @@ -40,6 +40,7 @@ class CBanEntry }; using banmap_t = std::map; +using NodeId = int64_t; /** * Convert a `banmap_t` object to a JSON array. @@ -58,5 +59,4 @@ UniValue BanMapToJson(const banmap_t& bans); */ void BanMapFromJson(const UniValue& bans_json, banmap_t& bans); - #endif // BITCOIN_NET_TYPES_H diff --git a/src/protocol.h b/src/protocol.h index 0c5161888a3fc..96a9ad5a61cb4 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -581,9 +580,6 @@ struct MisbehavingError {} }; -// TODO: replace usages of PeerMsgRet to MessageProcessingResult which is cover this one -using PeerMsgRet = tl::expected; - /** * This struct is a helper to return values from handlers that are processing * network messages but implemented outside of net_processing.cpp, @@ -600,8 +596,8 @@ struct MessageProcessingResult //! @m_error triggers Misbehaving error with score and optional message if not nullopt std::optional m_error; - //! @m_inventory will relay this inventory to connected peers if not nullopt - std::optional m_inventory; + //! @m_inventory will relay these inventories to connected peers + std::vector m_inventory; //! @m_transactions will relay transactions to peers which is ready to accept it (some peers does not accept transactions) std::vector m_transactions; @@ -614,7 +610,7 @@ struct MessageProcessingResult m_error(error) {} MessageProcessingResult(CInv inv) : - m_inventory(inv) + m_inventory({inv}) { } }; diff --git a/src/spork.cpp b/src/spork.cpp index 0c6e7fd0d09d2..021ab690e1392 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -126,39 +126,42 @@ void CSporkManager::CheckAndRemove() } } -PeerMsgRet CSporkManager::ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv) +MessageProcessingResult CSporkManager::ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) { if (msg_type == NetMsgType::SPORK) { - return ProcessSpork(peer, peerman, vRecv); + return ProcessSpork(peer.GetId(), vRecv); } else if (msg_type == NetMsgType::GETSPORKS) { ProcessGetSporks(peer, connman); } return {}; } -PeerMsgRet CSporkManager::ProcessSpork(const CNode& peer, PeerManager& peerman, CDataStream& vRecv) +MessageProcessingResult CSporkManager::ProcessSpork(NodeId from, CDataStream& vRecv) { CSporkMessage spork; vRecv >> spork; uint256 hash = spork.GetHash(); - WITH_LOCK(::cs_main, peerman.EraseObjectRequest(peer.GetId(), CInv(MSG_SPORK, hash))); - std::string strLogMsg{strprintf("SPORK -- hash: %s id: %d value: %10d peer=%d", hash.ToString(), spork.nSporkID, - spork.nValue, peer.GetId())}; + MessageProcessingResult ret{}; + ret.m_to_erase = CInv{MSG_SPORK, hash}; if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) { LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: too far into the future\n"); - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } auto opt_keyIDSigner = spork.GetSignerKeyID(); if (opt_keyIDSigner == std::nullopt || WITH_LOCK(cs, return !setSporkPubKeyIDs.count(*opt_keyIDSigner))) { LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: invalid signature\n"); - return tl::unexpected{100}; + ret.m_error = MisbehavingError{100}; + return ret; } + std::string strLogMsg{strprintf("SPORK -- hash: %s id: %d value: %10d peer=%d", hash.ToString(), spork.nSporkID, + spork.nValue, from)}; auto keyIDSigner = *opt_keyIDSigner; { @@ -167,7 +170,7 @@ PeerMsgRet CSporkManager::ProcessSpork(const CNode& peer, PeerManager& peerman, if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) { if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) { LogPrint(BCLog::SPORK, "%s seen\n", strLogMsg); - return {}; + return ret; } else { LogPrintf("%s updated\n", strLogMsg); } @@ -177,19 +180,16 @@ PeerMsgRet CSporkManager::ProcessSpork(const CNode& peer, PeerManager& peerman, } else { LogPrintf("%s new\n", strLogMsg); } - } - - { - LOCK(cs); // make sure to not lock this together with cs_main mapSporksByHash[hash] = spork; mapSporksActive[spork.nSporkID][keyIDSigner] = spork; // Clear cached values on new spork being processed WITH_LOCK(cs_mapSporksCachedActive, mapSporksCachedActive.erase(spork.nSporkID)); WITH_LOCK(cs_mapSporksCachedValues, mapSporksCachedValues.erase(spork.nSporkID)); } - spork.Relay(peerman); - return {}; + + ret.m_inventory.emplace_back(MSG_SPORK, hash); + return ret; } void CSporkManager::ProcessGetSporks(CNode& peer, CConnman& connman) diff --git a/src/spork.h b/src/spork.h index a9ed23424cc49..62b3433a518c9 100644 --- a/src/spork.h +++ b/src/spork.h @@ -257,7 +257,7 @@ class CSporkManager : public SporkStore /** * ProcessMessage is used to call ProcessSpork and ProcessGetSporks. See below */ - PeerMsgRet ProcessMessage(CNode& peer, CConnman& connman, PeerManager& peerman, std::string_view msg_type, CDataStream& vRecv); + [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv); /** * ProcessSpork is used to handle the 'spork' p2p message. @@ -265,7 +265,7 @@ class CSporkManager : public SporkStore * For 'spork', it validates the spork and adds it to the internal spork storage and * performs any necessary processing. */ - PeerMsgRet ProcessSpork(const CNode& peer, PeerManager& peerman, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs); + [[nodiscard]] MessageProcessingResult ProcessSpork(NodeId from, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs); /** * ProcessGetSporks is used to handle the 'getsporks' p2p message. diff --git a/src/util/expected.h b/src/util/expected.h deleted file mode 100644 index afee404d43ee5..0000000000000 --- a/src/util/expected.h +++ /dev/null @@ -1,2444 +0,0 @@ -/// -// expected - An implementation of std::expected with extensions -// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) -// -// Documentation available at http://tl.tartanllama.xyz/ -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to the -// public domain worldwide. This software is distributed without any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. If not, see -// . -/// - -#ifndef TL_EXPECTED_HPP -#define TL_EXPECTED_HPP - -#define TL_EXPECTED_VERSION_MAJOR 1 -#define TL_EXPECTED_VERSION_MINOR 1 -#define TL_EXPECTED_VERSION_PATCH 0 - -#include -#include -#include -#include - -#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) -#define TL_EXPECTED_EXCEPTIONS_ENABLED -#endif - -#if (defined(_MSC_VER) && _MSC_VER == 1900) -#define TL_EXPECTED_MSVC2015 -#define TL_EXPECTED_MSVC2015_CONSTEXPR -#else -#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr -#endif - -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC49 -#endif - -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC54 -#endif - -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC55 -#endif - -#if !defined(TL_ASSERT) -//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug -#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49) -#include -#define TL_ASSERT(x) assert(x) -#else -#define TL_ASSERT(x) -#endif -#endif - -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -// GCC < 5 doesn't support overloading on const&& for member functions - -#define TL_EXPECTED_NO_CONSTRR -// GCC < 5 doesn't support some standard C++11 type traits -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::has_trivial_copy_constructor -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::has_trivial_copy_assign - -// This one will be different for GCC 5.7 if it's ever supported -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible - -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks -// std::vector for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) -#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -namespace tl { -namespace detail { -template -struct is_trivially_copy_constructible - : std::is_trivially_copy_constructible {}; -#ifdef _GLIBCXX_VECTOR -template -struct is_trivially_copy_constructible> : std::false_type {}; -#endif -} // namespace detail -} // namespace tl -#endif - -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#else -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#endif - -#if __cplusplus > 201103L -#define TL_EXPECTED_CXX14 -#endif - -#ifdef TL_EXPECTED_GCC49 -#define TL_EXPECTED_GCC49_CONSTEXPR -#else -#define TL_EXPECTED_GCC49_CONSTEXPR constexpr -#endif - -#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ - defined(TL_EXPECTED_GCC49)) -#define TL_EXPECTED_11_CONSTEXPR -#else -#define TL_EXPECTED_11_CONSTEXPR constexpr -#endif - -namespace tl { -template class expected; - -#ifndef TL_MONOSTATE_INPLACE_MUTEX -#define TL_MONOSTATE_INPLACE_MUTEX -class monostate {}; - -struct in_place_t { - explicit in_place_t() = default; -}; -static constexpr in_place_t in_place{}; -#endif - -template class unexpected { -public: - static_assert(!std::is_same::value, "E must not be void"); - - unexpected() = delete; - constexpr explicit unexpected(const E &e) : m_val(e) {} - - constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} - - template ::value>::type * = nullptr> - constexpr explicit unexpected(Args &&...args) - : m_val(std::forward(args)...) {} - template < - class U, class... Args, - typename std::enable_if &, Args &&...>::value>::type * = nullptr> - constexpr explicit unexpected(std::initializer_list l, Args &&...args) - : m_val(l, std::forward(args)...) {} - - constexpr const E &value() const & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } - constexpr const E &&value() const && { return std::move(m_val); } - -private: - E m_val; -}; - -#ifdef __cpp_deduction_guides -template unexpected(E) -> unexpected; -#endif - -template -constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() == rhs.value(); -} -template -constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() != rhs.value(); -} -template -constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() < rhs.value(); -} -template -constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() <= rhs.value(); -} -template -constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() > rhs.value(); -} -template -constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() >= rhs.value(); -} - -template -unexpected::type> make_unexpected(E &&e) { - return unexpected::type>(std::forward(e)); -} - -struct unexpect_t { - unexpect_t() = default; -}; -static constexpr unexpect_t unexpect{}; - -namespace detail { -template -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(e); -#else - (void)e; -#ifdef _MSC_VER - __assume(0); -#else - __builtin_unreachable(); -#endif -#endif -} - -#ifndef TL_TRAITS_MUTEX -#define TL_TRAITS_MUTEX -// C++14-style aliases for brevity -template using remove_const_t = typename std::remove_const::type; -template -using remove_reference_t = typename std::remove_reference::type; -template using decay_t = typename std::decay::type; -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; - -// std::conjunction from C++17 -template struct conjunction : std::true_type {}; -template struct conjunction : B {}; -template -struct conjunction - : std::conditional, B>::type {}; - -#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L -#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -#endif - -// In C++11 mode, there's an issue in libc++'s std::mem_fn -// which results in a hard-error when using it in a noexcept expression -// in some cases. This is a check to workaround the common failing case. -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -template -struct is_pointer_to_non_const_member_func : std::false_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; - -template struct is_const_or_const_ref : std::false_type {}; -template struct is_const_or_const_ref : std::true_type {}; -template struct is_const_or_const_ref : std::true_type {}; -#endif - -// std::invoke from C++17 -// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template < - typename Fn, typename... Args, -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND - typename = enable_if_t::value && - is_const_or_const_ref::value)>, -#endif - typename = enable_if_t>::value>, int = 0> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { - return std::mem_fn(f)(std::forward(args)...); -} - -template >::value>> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} - -// std::invoke_result from C++17 -template struct invoke_result_impl; - -template -struct invoke_result_impl< - F, - decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = - decltype(detail::invoke(std::declval(), std::declval()...)); -}; - -template -using invoke_result = invoke_result_impl; - -template -using invoke_result_t = typename invoke_result::type; - -#if defined(_MSC_VER) && _MSC_VER <= 1900 -// TODO make a version which works with MSVC 2015 -template struct is_swappable : std::true_type {}; - -template struct is_nothrow_swappable : std::true_type {}; -#else -// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept -namespace swap_adl_tests { -// if swap ADL finds this then it would call std::swap otherwise (same -// signature) -struct tag {}; - -template tag swap(T &, T &); -template tag swap(T (&a)[N], T (&b)[N]); - -// helper functions to test if an unqualified swap is possible, and if it -// becomes std::swap -template std::false_type can_swap(...) noexcept(false); -template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); - -template std::false_type uses_std(...); -template -std::is_same(), std::declval())), tag> -uses_std(int); - -template -struct is_std_swap_noexcept - : std::integral_constant::value && - std::is_nothrow_move_assignable::value> {}; - -template -struct is_std_swap_noexcept : is_std_swap_noexcept {}; - -template -struct is_adl_swap_noexcept - : std::integral_constant(0))> {}; -} // namespace swap_adl_tests - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std( - 0))::value || - is_swappable::value)> {}; - -template -struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; -#endif -#endif - -// Trait for checking if a type is a tl::expected -template struct is_expected_impl : std::false_type {}; -template -struct is_expected_impl> : std::true_type {}; -template using is_expected = is_expected_impl>; - -template -using expected_enable_forward_value = detail::enable_if_t< - std::is_constructible::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value && - !std::is_same, detail::decay_t>::value>; - -template -using expected_enable_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value>; - -template -using is_void_or = conditional_t::value, std::true_type, U>; - -template -using is_copy_constructible_or_void = - is_void_or>; - -template -using is_move_constructible_or_void = - is_void_or>; - -template -using is_copy_assignable_or_void = is_void_or>; - -template -using is_move_assignable_or_void = is_void_or>; - -} // namespace detail - -namespace detail { -struct no_init_t {}; -static constexpr no_init_t no_init{}; - -// Implements the storage of the values, and ensures that the destructor is -// trivial if it can be. -// -// This specialization is for where neither `T` or `E` is trivially -// destructible, so the destructors must be called on destruction of the -// `expected` -template ::value, - bool = std::is_trivially_destructible::value> -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } else { - m_unexpect.~unexpected(); - } - } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// This specialization is for when both `T` and `E` are trivially-destructible, -// so the destructor of the `expected` can be trivial. -template struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() = default; - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// T is trivial, E is not. -template struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) - : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } - } - - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// E is trivial, T is not. -template struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } - } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// `T` is `void`, `E` is trivially-destructible -template struct expected_storage_base { - #if __GNUC__ <= 5 - //no constexpr for GCC 4/5 bug - #else - TL_EXPECTED_MSVC2015_CONSTEXPR - #endif - expected_storage_base() : m_has_val(true) {} - - constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} - - constexpr expected_storage_base(in_place_t) : m_has_val(true) {} - - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() = default; - struct dummy {}; - union { - unexpected m_unexpect; - dummy m_val; - }; - bool m_has_val; -}; - -// `T` is `void`, `E` is not trivially-destructible -template struct expected_storage_base { - constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} - - constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} - - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } - } - - union { - unexpected m_unexpect; - char m_dummy; - }; - bool m_has_val; -}; - -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template void construct(Args &&...args) noexcept { - new (std::addressof(this->m_val)) T(std::forward(args)...); - this->m_has_val = true; - } - - template void construct_with(Rhs &&rhs) noexcept { - new (std::addressof(this->m_val)) T(std::forward(rhs).get()); - this->m_has_val = true; - } - - template void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; - } - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - - // These assign overloads ensure that the most efficient assignment - // implementation is used while maintaining the strong exception guarantee. - // The problematic case is where rhs has a value, but *this does not. - // - // This overload handles the case where we can just copy-construct `T` - // directly into place without throwing. - template ::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); - } else { - assign_common(rhs); - } - } - - // This overload handles the case where we can attempt to create a copy of - // `T`, then no-throw move it into place if the copy was successful. - template ::value && - std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - T tmp = rhs.get(); - geterr().~unexpected(); - construct(std::move(tmp)); - } else { - assign_common(rhs); - } - } - - // This overload is the worst-case, where we have to move-construct the - // unexpected value into temporary storage, then try to copy the T into place. - // If the construction succeeds, then everything is fine, but if it throws, - // then we move the old unexpected value back into place before rethrowing the - // exception. - template ::value && - !std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - construct(rhs.get()); - } catch (...) { - geterr() = std::move(tmp); - throw; - } -#else - construct(rhs.get()); -#endif - } else { - assign_common(rhs); - } - } - - // These overloads do the same as above, but for rvalues - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); - } else { - assign_common(std::move(rhs)); - } - } - - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - construct(std::move(rhs).get()); - } catch (...) { - geterr() = std::move(tmp); - throw; - } -#else - construct(std::move(rhs).get()); -#endif - } else { - assign_common(std::move(rhs)); - } - } - -#else - - // If exceptions are disabled then we can just copy-construct - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); - } else { - assign_common(rhs); - } - } - - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); - } else { - assign_common(std::move(rhs)); - } - } - -#endif - - // The common part of move/copy assigning - template void assign_common(Rhs &&rhs) { - if (this->m_has_val) { - if (rhs.m_has_val) { - get() = std::forward(rhs).get(); - } else { - destroy_val(); - construct_error(std::forward(rhs).geterr()); - } - } else { - if (!rhs.m_has_val) { - geterr() = std::forward(rhs).geterr(); - } - } - } - - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } - constexpr const T &get() const & { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const T &&get() const && { return std::move(this->m_val); } -#endif - - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); - } -#endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } -}; - -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template void construct() noexcept { this->m_has_val = true; } - - // This function doesn't use its argument, but needs it so that code in - // levels above this can work independently of whether T is void - template void construct_with(Rhs &&) noexcept { - this->m_has_val = true; - } - - template void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; - } - - template void assign(Rhs &&rhs) noexcept { - if (!this->m_has_val) { - if (rhs.m_has_val) { - geterr().~unexpected(); - construct(); - } else { - geterr() = std::forward(rhs).geterr(); - } - } else { - if (!rhs.m_has_val) { - construct_error(std::forward(rhs).geterr()); - } - } - } - - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); - } -#endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { - // no-op - } -}; - -// This class manages conditionally having a trivial copy constructor -// This specialization is for when T and E are trivially copy constructible -template :: - value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; -}; - -// This specialization is for when T or E are not trivially copy constructible -template -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; - - expected_copy_base() = default; - expected_copy_base(const expected_copy_base &rhs) - : expected_operations_base(no_init) { - if (rhs.has_value()) { - this->construct_with(rhs); - } else { - this->construct_error(rhs.geterr()); - } - } - - expected_copy_base(expected_copy_base &&rhs) = default; - expected_copy_base &operator=(const expected_copy_base &rhs) = default; - expected_copy_base &operator=(expected_copy_base &&rhs) = default; -}; - -// This class manages conditionally having a trivial move constructor -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_constructible. We -// have to make do with a non-trivial move constructor even if T is trivially -// move constructible -#ifndef TL_EXPECTED_GCC49 -template >::value - &&std::is_trivially_move_constructible::value> -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; -}; -#else -template struct expected_move_base; -#endif -template -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; - - expected_move_base() = default; - expected_move_base(const expected_move_base &rhs) = default; - - expected_move_base(expected_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) - : expected_copy_base(no_init) { - if (rhs.has_value()) { - this->construct_with(std::move(rhs)); - } else { - this->construct_error(std::move(rhs.geterr())); - } - } - expected_move_base &operator=(const expected_move_base &rhs) = default; - expected_move_base &operator=(expected_move_base &&rhs) = default; -}; - -// This class manages conditionally having a trivial copy assignment operator -template >::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; -}; - -template -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; - - expected_copy_assign_base() = default; - expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; - - expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; - expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { - this->assign(rhs); - return *this; - } - expected_copy_assign_base & - operator=(expected_copy_assign_base &&rhs) = default; -}; - -// This class manages conditionally having a trivial move assignment operator -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_assignable. We have -// to make do with a non-trivial move assignment operator even if T is trivially -// move assignable -#ifndef TL_EXPECTED_GCC49 -template , - std::is_trivially_move_constructible, - std::is_trivially_move_assignable>>:: - value &&std::is_trivially_destructible::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; -}; -#else -template struct expected_move_assign_base; -#endif - -template -struct expected_move_assign_base - : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; - - expected_move_assign_base() = default; - expected_move_assign_base(const expected_move_assign_base &rhs) = default; - - expected_move_assign_base(expected_move_assign_base &&rhs) = default; - - expected_move_assign_base & - operator=(const expected_move_assign_base &rhs) = default; - - expected_move_assign_base & - operator=(expected_move_assign_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value - &&std::is_nothrow_move_assignable::value) { - this->assign(std::move(rhs)); - return *this; - } -}; - -// expected_delete_ctor_base will conditionally delete copy and move -// constructors depending on whether T is copy/move constructible -template ::value && - std::is_copy_constructible::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value)> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base & - operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base & - operator=(expected_delete_ctor_base &&) noexcept = default; -}; - -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base & - operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base & - operator=(expected_delete_ctor_base &&) noexcept = default; -}; - -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base & - operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base & - operator=(expected_delete_ctor_base &&) noexcept = default; -}; - -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base & - operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base & - operator=(expected_delete_ctor_base &&) noexcept = default; -}; - -// expected_delete_assign_base will conditionally delete copy and move -// constructors depending on whether T and E are copy/move constructible + -// assignable -template ::value && - std::is_copy_constructible::value && - is_copy_assignable_or_void::value && - std::is_copy_assignable::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value && - is_move_assignable_or_void::value && - std::is_move_assignable::value)> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base & - operator=(const expected_delete_assign_base &) = default; - expected_delete_assign_base & - operator=(expected_delete_assign_base &&) noexcept = default; -}; - -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base & - operator=(const expected_delete_assign_base &) = default; - expected_delete_assign_base & - operator=(expected_delete_assign_base &&) noexcept = delete; -}; - -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base & - operator=(const expected_delete_assign_base &) = delete; - expected_delete_assign_base & - operator=(expected_delete_assign_base &&) noexcept = default; -}; - -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base & - operator=(const expected_delete_assign_base &) = delete; - expected_delete_assign_base & - operator=(expected_delete_assign_base &&) noexcept = delete; -}; - -// This is needed to be able to construct the expected_default_ctor_base which -// follows, while still conditionally deleting the default constructor. -struct default_constructor_tag { - explicit constexpr default_constructor_tag() = default; -}; - -// expected_default_ctor_base will ensure that expected has a deleted default -// consturctor if T is not default constructible. -// This specialization is for when T is default constructible -template ::value || std::is_void::value> -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = default; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base & - operator=(expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base & - operator=(expected_default_ctor_base &&) noexcept = default; - - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; - -// This specialization is for when T is not default constructible -template struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = delete; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base & - operator=(expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base & - operator=(expected_default_ctor_base &&) noexcept = default; - - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; -} // namespace detail - -template class bad_expected_access : public std::exception { -public: - explicit bad_expected_access(E e) : m_val(std::move(e)) {} - - virtual const char *what() const noexcept override { - return "Bad expected access"; - } - - const E &error() const & { return m_val; } - E &error() & { return m_val; } - const E &&error() const && { return std::move(m_val); } - E &&error() && { return std::move(m_val); } - -private: - E m_val; -}; - -/// An `expected` object is an object that contains the storage for -/// another object and manages the lifetime of this contained object `T`. -/// Alternatively it could contain the storage for another unexpected object -/// `E`. The contained object may not be initialized after the expected object -/// has been initialized, and may not be destroyed before the expected object -/// has been destroyed. The initialization state of the contained object is -/// tracked by the expected object. -template -class expected : private detail::expected_move_assign_base, - private detail::expected_delete_ctor_base, - private detail::expected_delete_assign_base, - private detail::expected_default_ctor_base { - static_assert(!std::is_reference::value, "T must not be a reference"); - static_assert(!std::is_same::type>::value, - "T must not be in_place_t"); - static_assert(!std::is_same::type>::value, - "T must not be unexpect_t"); - static_assert( - !std::is_same>::type>::value, - "T must not be unexpected"); - static_assert(!std::is_reference::value, "E must not be a reference"); - - T *valptr() { return std::addressof(this->m_val); } - const T *valptr() const { return std::addressof(this->m_val); } - unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { - return std::addressof(this->m_unexpect); - } - - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &val() { - return this->m_val; - } - TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - - template ::value> * = nullptr> - constexpr const U &val() const { - return this->m_val; - } - constexpr const unexpected &err() const { return this->m_unexpect; } - - using impl_base = detail::expected_move_assign_base; - using ctor_base = detail::expected_default_ctor_base; - -public: - typedef T value_type; - typedef E error_type; - typedef unexpected unexpected_type; - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - return and_then_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - return and_then_impl(std::move(*this), std::forward(f)); - } - template constexpr auto and_then(F &&f) const & { - return and_then_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template constexpr auto and_then(F &&f) const && { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif - -#else - template - TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(std::declval(), - std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) && -> decltype(and_then_impl(std::declval(), - std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template constexpr auto map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#endif -#endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template constexpr auto transform_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template constexpr auto transform_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { - return or_else_impl(*this, std::forward(f)); - } - - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { - return or_else_impl(std::move(*this), std::forward(f)); - } - - template expected constexpr or_else(F &&f) const & { - return or_else_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template expected constexpr or_else(F &&f) const && { - return or_else_impl(std::move(*this), std::forward(f)); - } -#endif - constexpr expected() = default; - constexpr expected(const expected &rhs) = default; - constexpr expected(expected &&rhs) = default; - expected &operator=(const expected &rhs) = default; - expected &operator=(expected &&rhs) = default; - - template ::value> * = - nullptr> - constexpr expected(in_place_t, Args &&...args) - : impl_base(in_place, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&...args) - : impl_base(in_place, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value> * = - nullptr, - detail::enable_if_t::value> * = - nullptr> - explicit constexpr expected(const unexpected &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value> * = - nullptr> - constexpr explicit expected(unexpect_t, Args &&...args) - : impl_base(unexpect, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : impl_base(unexpect, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } - - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } - - template < - class U, class G, - detail::enable_if_t::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } - - template < - class U, class G, - detail::enable_if_t<(std::is_convertible::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - err().~unexpected(); - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } - - return *this; - } - - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; -#endif - } - - return *this; - } - - template ::value && - std::is_assignable::value> * = nullptr> - expected &operator=(const unexpected &rhs) { - if (!has_value()) { - err() = rhs; - } else { - this->destroy_val(); - ::new (errptr()) unexpected(rhs); - this->m_has_val = false; - } - - return *this; - } - - template ::value && - std::is_move_assignable::value> * = nullptr> - expected &operator=(unexpected &&rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } else { - this->destroy_val(); - ::new (errptr()) unexpected(std::move(rhs)); - this->m_has_val = false; - } - - return *this; - } - - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - } else { - err().~unexpected(); - this->m_has_val = true; - } - ::new (valptr()) T(std::forward(args)...); - } - - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - ::new (valptr()) T(std::forward(args)...); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; -#endif - } - } - - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - err().~unexpected(); - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } - } - - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; -#endif - } - } - -private: - using t_is_void = std::true_type; - using t_is_not_void = std::false_type; - using t_is_nothrow_move_constructible = std::true_type; - using move_constructing_t_can_throw = std::false_type; - using e_is_nothrow_move_constructible = std::true_type; - using move_constructing_e_can_throw = std::false_type; - - void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { - // swapping void is a no-op - } - - void swap_where_both_have_value(expected &rhs, t_is_not_void) { - using std::swap; - swap(val(), rhs.val()); - } - - void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( - std::is_nothrow_move_constructible::value) { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - std::swap(this->m_has_val, rhs.m_has_val); - } - - void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { - swap_where_only_one_has_value_and_t_is_not_void( - rhs, typename std::is_nothrow_move_constructible::type{}, - typename std::is_nothrow_move_constructible::type{}); - } - - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - e_is_nothrow_move_constructible) noexcept { - auto temp = std::move(val()); - val().~T(); - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } - - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - move_constructing_e_can_throw) { - auto temp = std::move(val()); - val().~T(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - val() = std::move(temp); - throw; - } -#else - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } - - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, move_constructing_t_can_throw, - e_is_nothrow_move_constructible) { - auto temp = std::move(rhs.err()); - rhs.err().~unexpected_type(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - rhs.err() = std::move(temp); - throw; - } -#else - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } - -public: - template - detail::enable_if_t::value && - detail::is_swappable::value && - (std::is_nothrow_move_constructible::value || - std::is_nothrow_move_constructible::value)> - swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value - &&std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - swap_where_both_have_value(rhs, typename std::is_void::type{}); - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - swap_where_only_one_has_value(rhs, typename std::is_void::type{}); - } else { - using std::swap; - swap(err(), rhs.err()); - } - } - - constexpr const T *operator->() const { - TL_ASSERT(has_value()); - return valptr(); - } - TL_EXPECTED_11_CONSTEXPR T *operator->() { - TL_ASSERT(has_value()); - return valptr(); - } - - template ::value> * = nullptr> - constexpr const U &operator*() const & { - TL_ASSERT(has_value()); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - TL_ASSERT(has_value()); - return val(); - } - template ::value> * = nullptr> - constexpr const U &&operator*() const && { - TL_ASSERT(has_value()); - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&operator*() && { - TL_ASSERT(has_value()); - return std::move(val()); - } - - constexpr bool has_value() const noexcept { return this->m_has_val; } - constexpr explicit operator bool() const noexcept { return this->m_has_val; } - - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } - - constexpr const E &error() const & { - TL_ASSERT(!has_value()); - return err().value(); - } - TL_EXPECTED_11_CONSTEXPR E &error() & { - TL_ASSERT(!has_value()); - return err().value(); - } - constexpr const E &&error() const && { - TL_ASSERT(!has_value()); - return std::move(err().value()); - } - TL_EXPECTED_11_CONSTEXPR E &&error() && { - TL_ASSERT(!has_value()); - return std::move(err().value()); - } - - template constexpr T value_or(U &&v) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy-constructible and convertible to from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move-constructible and convertible to from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } -}; - -namespace detail { -template using exp_t = typename detail::decay_t::value_type; -template using err_t = typename detail::decay_t::error_type; -template using ret_t = expected>; - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#else -template struct TC; -template (), - *std::declval())), - detail::enable_if_t>::value> * = nullptr> -auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template ())), - detail::enable_if_t>::value> * = nullptr> -constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto or_else_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#else -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto or_else_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#endif -} // namespace detail - -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); -} -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : true); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() == rhs.error() : false); -} - -template -constexpr bool operator==(const expected &x, const U &v) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator==(const U &v, const expected &x) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator!=(const expected &x, const U &v) { - return x.has_value() ? *x != v : true; -} -template -constexpr bool operator!=(const U &v, const expected &x) { - return x.has_value() ? *x != v : true; -} - -template -constexpr bool operator==(const expected &x, const unexpected &e) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator==(const unexpected &e, const expected &x) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator!=(const expected &x, const unexpected &e) { - return x.has_value() ? true : x.error() != e.value(); -} -template -constexpr bool operator!=(const unexpected &e, const expected &x) { - return x.has_value() ? true : x.error() != e.value(); -} - -template ::value || - std::is_move_constructible::value) && - detail::is_swappable::value && - std::is_move_constructible::value && - detail::is_swappable::value> * = nullptr> -void swap(expected &lhs, - expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { - lhs.swap(rhs); -} -} // namespace tl - -#endif diff --git a/test/lint/lint-include-guards.py b/test/lint/lint-include-guards.py index e8a287cf2a15d..6e59c83f7dbc6 100755 --- a/test/lint/lint-include-guards.py +++ b/test/lint/lint-include-guards.py @@ -30,8 +30,7 @@ 'src/ctpl_stl.h', 'src/dashbls', 'src/gsl', - 'src/immer', - 'src/util/expected.h'] + 'src/immer'] def _get_header_file_lst() -> List[str]: diff --git a/test/lint/lint-spelling.py b/test/lint/lint-spelling.py index 533bf8d63122b..204d297cedf05 100755 --- a/test/lint/lint-spelling.py +++ b/test/lint/lint-spelling.py @@ -12,7 +12,7 @@ from subprocess import check_output, STDOUT, CalledProcessError IGNORE_WORDS_FILE = 'test/lint/spelling.ignore-words.txt' -FILES_ARGS = ['git', 'ls-files', '--', ":(exclude)build-aux/m4/", ":(exclude)contrib/seeds/*.txt", ":(exclude)depends/", ":(exclude)doc/release-notes/", ":(exclude)src/bip39_english.h", ":(exclude)src/dashbls/", ":(exclude)src/crc32c/", ":(exclude)src/crypto/", ":(exclude)src/ctpl_stl.h", ":(exclude)src/cxxtimer.hpp", ":(exclude)src/util/expected.h", ":(exclude)src/immer/", ":(exclude)src/leveldb/", ":(exclude)src/qt/locale/", ":(exclude)src/qt/*.qrc", ":(exclude)src/secp256k1/", ":(exclude)src/minisketch/", ":(exclude)contrib/builder-keys/", ":(exclude)contrib/guix/patches"] +FILES_ARGS = ['git', 'ls-files', '--', ":(exclude)build-aux/m4/", ":(exclude)contrib/seeds/*.txt", ":(exclude)depends/", ":(exclude)doc/release-notes/", ":(exclude)src/bip39_english.h", ":(exclude)src/dashbls/", ":(exclude)src/crc32c/", ":(exclude)src/crypto/", ":(exclude)src/ctpl_stl.h", ":(exclude)src/cxxtimer.hpp", ":(exclude)src/immer/", ":(exclude)src/leveldb/", ":(exclude)src/qt/locale/", ":(exclude)src/qt/*.qrc", ":(exclude)src/secp256k1/", ":(exclude)src/minisketch/", ":(exclude)contrib/builder-keys/", ":(exclude)contrib/guix/patches"] def check_codespell_install(): diff --git a/test/lint/lint-whitespace.py b/test/lint/lint-whitespace.py index a0e1f0636a2b6..06b3adf52241f 100755 --- a/test/lint/lint-whitespace.py +++ b/test/lint/lint-whitespace.py @@ -24,7 +24,6 @@ "src/minisketch/", "src/dashbls/", "src/immer/", - "src/util/expected.h", "doc/release-notes/", "src/qt/locale"]