Skip to content

Commit

Permalink
Implement and enforce quorum commitment merkle roots in coinbases
Browse files Browse the repository at this point in the history
This bumps CCbTx::CURRENT_VERSION to 2 and enforces the new version after
DIP8 BIP9 activation.
  • Loading branch information
codablock committed Apr 4, 2019
1 parent 0762074 commit 8f7929b
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 6 deletions.
85 changes: 82 additions & 3 deletions src/evo/cbtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@

#include "cbtx.h"
#include "deterministicmns.h"
#include "llmq/quorums.h"
#include "llmq/quorums_blockprocessor.h"
#include "llmq/quorums_commitment.h"
#include "simplifiedmns.h"
#include "specialtx.h"

#include "chainparams.h"
#include "consensus/merkle.h"
#include "univalue.h"
#include "validation.h"

Expand All @@ -34,11 +38,18 @@ bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidatio
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-height");
}

if (pindexPrev) {
bool fDIP0008Active = VersionBitsState(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0008, versionbitscache) == THRESHOLD_ACTIVE;
if (fDIP0008Active && cbTx.nVersion < 2) {
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version");
}
}

return true;
}

// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list
bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state)
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValidationState& state)
{
if (block.vtx[0]->nType != TRANSACTION_COINBASE) {
return true;
Expand All @@ -57,6 +68,14 @@ bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, C
if (calculatedMerkleRoot != cbTx.merkleRootMNList) {
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot");
}
if (cbTx.nVersion >= 2) {
if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, calculatedMerkleRoot, state)) {
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-quorummerkleroot");
}
if (calculatedMerkleRoot != cbTx.merkleRootQuorums) {
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-quorummerkleroot");
}
}
}

return true;
Expand All @@ -78,10 +97,67 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev
return !mutated;
}

bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state)
{
auto quorums = llmq::quorumBlockProcessor->GetMinedAndActiveCommitmentsUntilBlock(pindexPrev);
std::map<Consensus::LLMQType, std::vector<uint256>> qcHashes;
size_t hashCount = 0;
for (const auto& p : quorums) {
auto& v = qcHashes[p.first];
v.reserve(p.second.size());
for (const auto& p2 : p.second) {
llmq::CFinalCommitment qc;
bool found = llmq::quorumBlockProcessor->GetMinedCommitment(p.first, p2->GetBlockHash(), qc);
assert(found);
v.emplace_back(::SerializeHash(qc));
hashCount++;
}
}

// now add the commitments from the current block, which are not returned by GetMinedAndActiveCommitmentsUntilBlock
// due to the use of pindexPrev (we don't have the tip index here)
for (size_t i = 1; i < block.vtx.size(); i++) {
auto& tx = block.vtx[i];

if (tx->nVersion == 3 && tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
llmq::CFinalCommitmentTxPayload qc;
if (!GetTxPayload(*tx, qc)) {
assert(false);
}
if (qc.commitment.IsNull()) {
continue;
}
auto qcHash = ::SerializeHash(qc.commitment);
const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.commitment.llmqType);
auto& v = qcHashes[params.type];
if (v.size() == params.signingActiveQuorumCount) {
v.pop_back();
}
v.emplace_back(qcHash);
hashCount++;
assert(v.size() <= params.signingActiveQuorumCount);
}
}

std::vector<uint256> qcHashesVec;
qcHashesVec.reserve(hashCount);

for (const auto& p : qcHashes) {
for (const auto& h : p.second) {
qcHashesVec.emplace_back(h);
}
}
std::sort(qcHashesVec.begin(), qcHashesVec.end());

bool mutated = false;
merkleRootRet = ComputeMerkleRoot(qcHashesVec, &mutated);
return !mutated;
}

std::string CCbTx::ToString() const
{
return strprintf("CCbTx(nHeight=%d, nVersion=%d, merkleRootMNList=%s)",
nVersion, nHeight, merkleRootMNList.ToString());
return strprintf("CCbTx(nHeight=%d, nVersion=%d, merkleRootMNList=%s, merkleRootQuorums=%s)",
nVersion, nHeight, merkleRootMNList.ToString(), merkleRootQuorums.ToString());
}

void CCbTx::ToJson(UniValue& obj) const
Expand All @@ -91,4 +167,7 @@ void CCbTx::ToJson(UniValue& obj) const
obj.push_back(Pair("version", (int)nVersion));
obj.push_back(Pair("height", (int)nHeight));
obj.push_back(Pair("merkleRootMNList", merkleRootMNList.ToString()));
if (nVersion >= 2) {
obj.push_back(Pair("merkleRootQuorums", merkleRootQuorums.ToString()));
}
}
10 changes: 8 additions & 2 deletions src/evo/cbtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ class UniValue;
class CCbTx
{
public:
static const uint16_t CURRENT_VERSION = 1;
static const uint16_t CURRENT_VERSION = 2;

public:
uint16_t nVersion{CURRENT_VERSION};
int32_t nHeight{0};
uint256 merkleRootMNList;
uint256 merkleRootQuorums;

public:
ADD_SERIALIZE_METHODS;
Expand All @@ -32,6 +33,10 @@ class CCbTx
READWRITE(nVersion);
READWRITE(nHeight);
READWRITE(merkleRootMNList);

if (nVersion >= 2) {
READWRITE(merkleRootQuorums);
}
}

std::string ToString() const;
Expand All @@ -40,7 +45,8 @@ class CCbTx

bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);

bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state);
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValidationState& state);
bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state);
bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state);

#endif //DASH_CBTX_H
2 changes: 1 addition & 1 deletion src/evo/specialtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV
return false;
}

if (!CheckCbTxMerkleRootMNList(block, pindex, state)) {
if (!CheckCbTxMerkleRoots(block, pindex, state)) {
return false;
}

Expand Down
13 changes: 13 additions & 0 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
LOCK2(cs_main, mempool.cs);

bool fDIP0003Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE;
bool fDIP0008Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0008, versionbitscache) == THRESHOLD_ACTIVE;

CBlockIndex* pindexPrev = chainActive.Tip();
nHeight = pindexPrev->nHeight + 1;
Expand Down Expand Up @@ -209,12 +210,24 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.nType = TRANSACTION_COINBASE;

CCbTx cbTx;

if (fDIP0008Active_context) {
cbTx.nVersion = 2;
} else {
cbTx.nVersion = 1;
}

cbTx.nHeight = nHeight;

CValidationState state;
if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state)) {
throw std::runtime_error(strprintf("%s: CalcSMLMerkleRootForNewBlock failed: %s", __func__, FormatStateMessage(state)));
}
if (fDIP0008Active_context) {
if (!CalcCbTxMerkleRootQuorums(*pblock, pindexPrev, cbTx.merkleRootQuorums, state)) {
throw std::runtime_error(strprintf("%s: CalcSMLMerkleRootForNewBlock failed: %s", __func__, FormatStateMessage(state)));
}
}

SetTxPayload(coinbaseTx, cbTx);
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/test_dash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns,
if (!CalcCbTxMerkleRootMNList(block, chainActive.Tip(), cbTx.merkleRootMNList, state)) {
BOOST_ASSERT(false);
}
if (!CalcCbTxMerkleRootQuorums(block, chainActive.Tip(), cbTx.merkleRootQuorums, state)) {
BOOST_ASSERT(false);
}
CMutableTransaction tmpTx = *block.vtx[0];
SetTxPayload(tmpTx, cbTx);
block.vtx[0] = MakeTransactionRef(tmpTx);
Expand Down

0 comments on commit 8f7929b

Please sign in to comment.