From 4615a071b7410601512250fc31138bf35626bef0 Mon Sep 17 00:00:00 2001 From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Date: Wed, 11 Jan 2023 21:24:29 -0600 Subject: [PATCH] feat(coinjoin): implement new serialization for dsq and dstx (#5136) ## Issue being fixed or feature implemented https://github.com/dashpay/dash/issues/4966 ## What was done? Serialization now depends on the protocol version, and we use an updated serialization that is spv friendly for the new version ## How Has This Been Tested? hasn't ## Breaking Changes this should be backwards compatible, but this likely should get some release notes. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation **For repository code-owners and collaborators only** - [x] I have assigned this pull request to a milestone Co-authored-by: UdjinM6 --- src/coinjoin/client.cpp | 21 +++++++++++++++++++++ src/coinjoin/coinjoin.cpp | 3 +++ src/coinjoin/coinjoin.h | 26 ++++++++++++++++++++++---- src/coinjoin/server.cpp | 36 +++++++++++++++++++++++++++++++++--- src/net_processing.cpp | 25 ++++++++++++++++++++----- src/version.h | 5 ++++- 6 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index 329cf7e2e4dc3..9ea0c36e3045b 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -53,6 +53,23 @@ void CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CDataStream& CCoinJoinQueue dsq; vRecv >> dsq; + if (dsq.masternodeOutpoint.IsNull() && dsq.m_protxHash.IsNull()) { + LOCK(cs_main); + Misbehaving(peer.GetId(), 100); + return; + } + + if (dsq.masternodeOutpoint.IsNull()) { + auto mnList = deterministicMNManager->GetListAtChainTip(); + if (auto dmn = mnList.GetValidMN(dsq.m_protxHash)) { + dsq.masternodeOutpoint = dmn->collateralOutpoint; + } else { + LOCK(cs_main); + Misbehaving(peer.GetId(), 10); + return; + } + } + { TRY_LOCK(cs_vecqueue, lockRecv); if (!lockRecv) return; @@ -78,6 +95,10 @@ void CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CDataStream& auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint); if (!dmn) return; + if (dsq.m_protxHash.IsNull()) { + dsq.m_protxHash = dmn->proTxHash; + } + if (!dsq.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) { LOCK(cs_main); Misbehaving(peer.GetId(), 10); diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index 59486fd6a8951..35edcbffe8380 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -130,6 +130,9 @@ bool CCoinJoinBroadcastTx::IsExpired(const CBlockIndex* pindex, const llmq::CCha bool CCoinJoinBroadcastTx::IsValidStructure() const { // some trivial checks only + if (masternodeOutpoint.IsNull() && m_protxHash.IsNull()) { + return false; + } if (tx->vin.size() != tx->vout.size()) { return false; } diff --git a/src/coinjoin/coinjoin.h b/src/coinjoin/coinjoin.h index 01eaf22b19993..d9e0ab123dffc 100644 --- a/src/coinjoin/coinjoin.h +++ b/src/coinjoin/coinjoin.h @@ -196,6 +196,7 @@ class CCoinJoinQueue public: int nDenom{0}; COutPoint masternodeOutpoint; + uint256 m_protxHash; int64_t nTime{0}; bool fReady{false}; //ready for submit std::vector vchSig; @@ -204,9 +205,10 @@ class CCoinJoinQueue CCoinJoinQueue() = default; - CCoinJoinQueue(int nDenom, const COutPoint& outpoint, int64_t nTime, bool fReady) : + CCoinJoinQueue(int nDenom, const COutPoint& outpoint, const uint256& proTxHash, int64_t nTime, bool fReady) : nDenom(nDenom), masternodeOutpoint(outpoint), + m_protxHash(proTxHash), nTime(nTime), fReady(fReady) { @@ -214,7 +216,14 @@ class CCoinJoinQueue SERIALIZE_METHODS(CCoinJoinQueue, obj) { - READWRITE(obj.nDenom, obj.masternodeOutpoint, obj.nTime, obj.fReady); + READWRITE(obj.nDenom); + + if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION || (s.GetType() & SER_GETHASH)) { + READWRITE(obj.masternodeOutpoint); + } else { + READWRITE(obj.m_protxHash); + } + READWRITE(obj.nTime, obj.fReady); if (!(s.GetType() & SER_GETHASH)) { READWRITE(obj.vchSig); } @@ -261,6 +270,7 @@ class CCoinJoinBroadcastTx public: CTransactionRef tx; COutPoint masternodeOutpoint; + uint256 m_protxHash; std::vector vchSig; int64_t sigTime{0}; @@ -269,16 +279,24 @@ class CCoinJoinBroadcastTx { } - CCoinJoinBroadcastTx(CTransactionRef _tx, const COutPoint& _outpoint, int64_t _sigTime) : + CCoinJoinBroadcastTx(CTransactionRef _tx, const COutPoint& _outpoint, const uint256& proTxHash, int64_t _sigTime) : tx(std::move(_tx)), masternodeOutpoint(_outpoint), + m_protxHash(proTxHash), sigTime(_sigTime) { } SERIALIZE_METHODS(CCoinJoinBroadcastTx, obj) { - READWRITE(obj.tx, obj.masternodeOutpoint); + READWRITE(obj.tx); + + if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION || (s.GetType() & SER_GETHASH)) { + READWRITE(obj.masternodeOutpoint); + } else { + READWRITE(obj.m_protxHash); + } + if (!(s.GetType() & SER_GETHASH)) { READWRITE(obj.vchSig); } diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index a1fe3b68687a7..649e1c4b4ead6 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -112,6 +112,23 @@ void CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv) CCoinJoinQueue dsq; vRecv >> dsq; + if (dsq.masternodeOutpoint.IsNull() && dsq.m_protxHash.IsNull()) { + LOCK(cs_main); + Misbehaving(peer.GetId(), 100); + return; + } + + if (dsq.masternodeOutpoint.IsNull()) { + auto mnList = deterministicMNManager->GetListAtChainTip(); + if (auto dmn = mnList.GetValidMN(dsq.m_protxHash)) { + dsq.masternodeOutpoint = dmn->collateralOutpoint; + } else { + LOCK(cs_main); + Misbehaving(peer.GetId(), 10); + return; + } + } + { TRY_LOCK(cs_vecqueue, lockRecv); if (!lockRecv) return; @@ -137,6 +154,10 @@ void CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, CDataStream& vRecv) auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint); if (!dmn) return; + if (dsq.m_protxHash.IsNull()) { + dsq.m_protxHash = dmn->proTxHash; + } + if (!dsq.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) { LOCK(cs_main); Misbehaving(peer.GetId(), 10); @@ -317,7 +338,10 @@ void CCoinJoinServer::CommitFinalTransaction() // create and sign masternode dstx transaction if (!CCoinJoin::GetDSTX(hashTx)) { - CCoinJoinBroadcastTx dstxNew(finalTransaction, WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), GetAdjustedTime()); + CCoinJoinBroadcastTx dstxNew(finalTransaction, + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.proTxHash), + GetAdjustedTime()); dstxNew.Sign(); CCoinJoin::AddDSTX(dstxNew); } @@ -483,7 +507,10 @@ void CCoinJoinServer::CheckForCompleteQueue() if (nState == POOL_STATE_QUEUE && IsSessionReady()) { SetState(POOL_STATE_ACCEPTING_ENTRIES); - CCoinJoinQueue dsq(nSessionDenom, WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), GetAdjustedTime(), true); + CCoinJoinQueue dsq(nSessionDenom, + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.proTxHash), + GetAdjustedTime(), true); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CheckForCompleteQueue -- queue is ready, signing and relaying (%s) " /* Continued */ "with %d participants\n", dsq.ToString(), vecSessionCollaterals.size()); dsq.Sign(); @@ -694,7 +721,10 @@ bool CCoinJoinServer::CreateNewSession(const CCoinJoinAccept& dsa, PoolMessage& if (!fUnitTest) { //broadcast that I'm accepting entries, only if it's the first entry through - CCoinJoinQueue dsq(nSessionDenom, WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), GetAdjustedTime(), false); + CCoinJoinQueue dsq(nSessionDenom, + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint), + WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.proTxHash), + GetAdjustedTime(), false); LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString()); dsq.Sign(); dsq.Relay(connman); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index d11f9acdbdb8c..da487cd0aba2c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2480,7 +2480,7 @@ std::string RejectCodeToString(const unsigned char code) return ""; } -std::pair static ValidateDSTX(CCoinJoinBroadcastTx dstx, uint256 hashTx) +std::pair static ValidateDSTX(CCoinJoinBroadcastTx& dstx, uint256 hashTx) { if (!dstx.IsValidStructure()) { LogPrint(BCLog::COINJOIN, "DSTX -- Invalid DSTX structure: %s\n", hashTx.ToString()); @@ -2499,11 +2499,26 @@ std::pair static ValidateDSTX(CCoinJoinBroadca } // It could be that a MN is no longer in the list but its DSTX is not yet mined. // Try to find a MN up to 24 blocks deep to make sure such dstx-es are relayed and processed correctly. - for (int i = 0; i < 24 && pindex; ++i) { - dmn = deterministicMNManager->GetListForBlock(pindex).GetMNByCollateral(dstx.masternodeOutpoint); - if (dmn) break; - pindex = pindex->pprev; + if (dstx.masternodeOutpoint.IsNull()) { + for (int i = 0; i < 24 && pindex; ++i) { + dmn = deterministicMNManager->GetListForBlock(pindex).GetMN(dstx.m_protxHash); + if (dmn) { + dstx.masternodeOutpoint = dmn->collateralOutpoint; + break; + } + pindex = pindex->pprev; + } + } else { + for (int i = 0; i < 24 && pindex; ++i) { + dmn = deterministicMNManager->GetListForBlock(pindex).GetMNByCollateral(dstx.masternodeOutpoint); + if (dmn) { + dstx.m_protxHash = dmn->proTxHash; + break; + } + pindex = pindex->pprev; + } } + if (!dmn) { LogPrint(BCLog::COINJOIN, "DSTX -- Can't find masternode %s to verify %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString()); return {false, true}; diff --git a/src/version.h b/src/version.h index 400a3341d0273..41c91f529d9b4 100644 --- a/src/version.h +++ b/src/version.h @@ -11,7 +11,7 @@ */ -static const int PROTOCOL_VERSION = 70225; +static const int PROTOCOL_VERSION = 70226; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -47,6 +47,9 @@ static const int COINJOIN_SU_PROTO_VERSION = 70224; //! BLS scheme was introduced in this version static const int BLS_SCHEME_PROTO_VERSION = 70225; +//! DSQ and DSTX started using protx hash in this version +static const int COINJOIN_PROTX_HASH_PROTO_VERSION = 70226; + // Make sure that none of the values above collide with `ADDRV2_FORMAT`. #endif // BITCOIN_VERSION_H