Skip to content

Commit

Permalink
feat(coinjoin): implement new serialization for dsq and dstx (#5136)
Browse files Browse the repository at this point in the history
## Issue being fixed or feature implemented
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
#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 <[email protected]>
  • Loading branch information
PastaPastaPasta and UdjinM6 committed Jan 12, 2023
1 parent 78593c7 commit 4615a07
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 13 deletions.
21 changes: 21 additions & 0 deletions src/coinjoin/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/coinjoin/coinjoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
26 changes: 22 additions & 4 deletions src/coinjoin/coinjoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned char> vchSig;
Expand All @@ -204,17 +205,25 @@ 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)
{
}

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);
}
Expand Down Expand Up @@ -261,6 +270,7 @@ class CCoinJoinBroadcastTx
public:
CTransactionRef tx;
COutPoint masternodeOutpoint;
uint256 m_protxHash;
std::vector<unsigned char> vchSig;
int64_t sigTime{0};

Expand All @@ -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);
}
Expand Down
36 changes: 33 additions & 3 deletions src/coinjoin/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 20 additions & 5 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2480,7 +2480,7 @@ std::string RejectCodeToString(const unsigned char code)
return "";
}

std::pair<bool /*ret*/, bool /*do_return*/> static ValidateDSTX(CCoinJoinBroadcastTx dstx, uint256 hashTx)
std::pair<bool /*ret*/, bool /*do_return*/> static ValidateDSTX(CCoinJoinBroadcastTx& dstx, uint256 hashTx)
{
if (!dstx.IsValidStructure()) {
LogPrint(BCLog::COINJOIN, "DSTX -- Invalid DSTX structure: %s\n", hashTx.ToString());
Expand All @@ -2499,11 +2499,26 @@ std::pair<bool /*ret*/, bool /*do_return*/> 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};
Expand Down
5 changes: 4 additions & 1 deletion src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

0 comments on commit 4615a07

Please sign in to comment.