Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

cbtx.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2019 The Dash Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <evo/cbtx.h>
6 #include <evo/deterministicmns.h>
7 #include <llmq/quorums.h>
10 #include <evo/simplifiedmns.h>
11 #include <evo/specialtx.h>
12 
13 #include <chainparams.h>
14 #include <consensus/merkle.h>
15 #include <univalue.h>
16 #include <validation.h>
17 
18 bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
19 {
20  if (tx.nType != TRANSACTION_COINBASE) {
21  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-type");
22  }
23 
24  if (!tx.IsCoinBase()) {
25  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-invalid");
26  }
27 
28  CCbTx cbTx;
29  if (!GetTxPayload(tx, cbTx)) {
30  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-payload");
31  }
32 
33  if (cbTx.nVersion == 0 || cbTx.nVersion > CCbTx::CURRENT_VERSION) {
34  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version");
35  }
36 
37  if (pindexPrev && pindexPrev->nHeight + 1 != cbTx.nHeight) {
38  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-height");
39  }
40 
41  if (pindexPrev) {
42  bool fDIP0008Active = VersionBitsState(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0008, versionbitscache) == THRESHOLD_ACTIVE;
43  if (fDIP0008Active && cbTx.nVersion < 2) {
44  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version");
45  }
46  }
47 
48  return true;
49 }
50 
51 // This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list
52 bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValidationState& state)
53 {
54  if (block.vtx[0]->nType != TRANSACTION_COINBASE) {
55  return true;
56  }
57 
58  static int64_t nTimePayload = 0;
59  static int64_t nTimeMerkleMNL = 0;
60  static int64_t nTimeMerkleQuorum = 0;
61 
62  int64_t nTime1 = GetTimeMicros();
63 
64  CCbTx cbTx;
65  if (!GetTxPayload(*block.vtx[0], cbTx)) {
66  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-payload");
67  }
68 
69  int64_t nTime2 = GetTimeMicros(); nTimePayload += nTime2 - nTime1;
70  LogPrint(BCLog::BENCHMARK, " - GetTxPayload: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimePayload * 0.000001);
71 
72  if (pindex) {
73  uint256 calculatedMerkleRoot;
74  if (!CalcCbTxMerkleRootMNList(block, pindex->pprev, calculatedMerkleRoot, state)) {
75  // pass the state returned by the function above
76  return false;
77  }
78  if (calculatedMerkleRoot != cbTx.merkleRootMNList) {
79  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot");
80  }
81 
82  int64_t nTime3 = GetTimeMicros(); nTimeMerkleMNL += nTime3 - nTime2;
83  LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootMNList: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeMerkleMNL * 0.000001);
84 
85  if (cbTx.nVersion >= 2) {
86  if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, calculatedMerkleRoot, state)) {
87  // pass the state returned by the function above
88  return false;
89  }
90  if (calculatedMerkleRoot != cbTx.merkleRootQuorums) {
91  return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-quorummerkleroot");
92  }
93  }
94 
95  int64_t nTime4 = GetTimeMicros(); nTimeMerkleQuorum += nTime4 - nTime3;
96  LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeMerkleQuorum * 0.000001);
97 
98  }
99 
100  return true;
101 }
102 
103 bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state)
104 {
106 
107  static int64_t nTimeDMN = 0;
108  static int64_t nTimeSMNL = 0;
109  static int64_t nTimeMerkle = 0;
110 
111  int64_t nTime1 = GetTimeMicros();
112 
113  try {
114  CDeterministicMNList tmpMNList;
115  if (!deterministicMNManager->BuildNewListFromBlock(block, pindexPrev, state, tmpMNList, false)) {
116  // pass the state returned by the function above
117  return false;
118  }
119 
120  int64_t nTime2 = GetTimeMicros(); nTimeDMN += nTime2 - nTime1;
121  LogPrint(BCLog::BENCHMARK, " - BuildNewListFromBlock: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeDMN * 0.000001);
122 
123  CSimplifiedMNList sml(tmpMNList);
124 
125  int64_t nTime3 = GetTimeMicros(); nTimeSMNL += nTime3 - nTime2;
126  LogPrint(BCLog::BENCHMARK, " - CSimplifiedMNList: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeSMNL * 0.000001);
127 
128  static CSimplifiedMNList smlCached;
129  static uint256 merkleRootCached;
130  static bool mutatedCached{false};
131 
132  if (sml.mnList == smlCached.mnList) {
133  merkleRootRet = merkleRootCached;
134  if (mutatedCached) {
135  return state.DoS(100, false, REJECT_INVALID, "mutated-cached-calc-cb-mnmerkleroot");
136  }
137  return true;
138  }
139 
140  bool mutated = false;
141  merkleRootRet = sml.CalcMerkleRoot(&mutated);
142 
143  int64_t nTime4 = GetTimeMicros(); nTimeMerkle += nTime4 - nTime3;
144  LogPrint(BCLog::BENCHMARK, " - CalcMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeMerkle * 0.000001);
145 
146  smlCached = std::move(sml);
147  merkleRootCached = merkleRootRet;
148  mutatedCached = mutated;
149 
150  if (mutated) {
151  return state.DoS(100, false, REJECT_INVALID, "mutated-calc-cb-mnmerkleroot");
152  }
153 
154  return true;
155  } catch (const std::exception& e) {
156  LogPrintf("%s -- failed: %s\n", __func__, e.what());
157  return state.DoS(100, false, REJECT_INVALID, "failed-calc-cb-mnmerkleroot");
158  }
159 }
160 
161 bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state)
162 {
163  static int64_t nTimeMinedAndActive = 0;
164  static int64_t nTimeMined = 0;
165  static int64_t nTimeLoop = 0;
166  static int64_t nTimeMerkle = 0;
167 
168  int64_t nTime1 = GetTimeMicros();
169 
170  static std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> quorumsCached;
171  static std::map<Consensus::LLMQType, std::vector<uint256>> qcHashesCached;
172 
173  // The returned quorums are in reversed order, so the most recent one is at index 0
175  std::map<Consensus::LLMQType, std::vector<uint256>> qcHashes;
176  size_t hashCount = 0;
177 
178  int64_t nTime2 = GetTimeMicros(); nTimeMinedAndActive += nTime2 - nTime1;
179  LogPrint(BCLog::BENCHMARK, " - GetMinedAndActiveCommitmentsUntilBlock: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeMinedAndActive * 0.000001);
180 
181  if (quorums == quorumsCached) {
182  qcHashes = qcHashesCached;
183  } else {
184  for (const auto& p : quorums) {
185  auto& v = qcHashes[p.first];
186  v.reserve(p.second.size());
187  for (const auto& p2 : p.second) {
189  uint256 minedBlockHash;
190  bool found = llmq::quorumBlockProcessor->GetMinedCommitment(p.first, p2->GetBlockHash(), qc, minedBlockHash);
191  if (!found) return state.DoS(100, false, REJECT_INVALID, "commitment-not-found");
192  v.emplace_back(::SerializeHash(qc));
193  hashCount++;
194  }
195  }
196  quorumsCached = quorums;
197  qcHashesCached = qcHashes;
198  }
199 
200  int64_t nTime3 = GetTimeMicros(); nTimeMined += nTime3 - nTime2;
201  LogPrint(BCLog::BENCHMARK, " - GetMinedCommitment: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeMined * 0.000001);
202 
203  // now add the commitments from the current block, which are not returned by GetMinedAndActiveCommitmentsUntilBlock
204  // due to the use of pindexPrev (we don't have the tip index here)
205  for (size_t i = 1; i < block.vtx.size(); i++) {
206  auto& tx = block.vtx[i];
207 
208  if (tx->nVersion == 3 && tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
210  if (!GetTxPayload(*tx, qc)) {
211  return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload-calc-cbtx-quorummerkleroot");
212  }
213  if (qc.commitment.IsNull()) {
214  continue;
215  }
216  auto qcHash = ::SerializeHash(qc.commitment);
217  const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.commitment.llmqType);
218  auto& v = qcHashes[params.type];
219  if (v.size() == params.signingActiveQuorumCount) {
220  // we pop the last entry, which is actually the oldest quorum as GetMinedAndActiveCommitmentsUntilBlock
221  // returned quorums in reversed order. This pop and later push can only work ONCE, but we rely on the
222  // fact that a block can only contain a single commitment for one LLMQ type
223  v.pop_back();
224  }
225  v.emplace_back(qcHash);
226  hashCount++;
227  if (v.size() > params.signingActiveQuorumCount) {
228  return state.DoS(100, false, REJECT_INVALID, "excess-quorums-calc-cbtx-quorummerkleroot");
229  }
230  }
231  }
232 
233  std::vector<uint256> qcHashesVec;
234  qcHashesVec.reserve(hashCount);
235 
236  for (const auto& p : qcHashes) {
237  for (const auto& h : p.second) {
238  qcHashesVec.emplace_back(h);
239  }
240  }
241  std::sort(qcHashesVec.begin(), qcHashesVec.end());
242 
243  int64_t nTime4 = GetTimeMicros(); nTimeLoop += nTime4 - nTime3;
244  LogPrint(BCLog::BENCHMARK, " - Loop: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeLoop * 0.000001);
245 
246  bool mutated = false;
247  merkleRootRet = ComputeMerkleRoot(qcHashesVec, &mutated);
248 
249  int64_t nTime5 = GetTimeMicros(); nTimeMerkle += nTime5 - nTime4;
250  LogPrint(BCLog::BENCHMARK, " - ComputeMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);
251 
252  if (mutated) {
253  return state.DoS(100, false, REJECT_INVALID, "mutated-calc-cbtx-quorummerkleroot");
254  }
255 
256  return true;
257 }
258 
259 std::string CCbTx::ToString() const
260 {
261  return strprintf("CCbTx(nHeight=%d, nVersion=%d, merkleRootMNList=%s, merkleRootQuorums=%s)",
263 }
bool CalcCbTxMerkleRootMNList(const CBlock &block, const CBlockIndex *pindexPrev, uint256 &merkleRootRet, CValidationState &state)
Definition: cbtx.cpp:103
bool GetTxPayload(const std::vector< unsigned char > &payload, T &obj)
Definition: specialtx.h:21
static const uint16_t CURRENT_VERSION
Definition: cbtx.h:19
bool CheckCbTx(const CTransaction &tx, const CBlockIndex *pindexPrev, CValidationState &state)
Definition: cbtx.cpp:18
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:177
bool CheckCbTxMerkleRoots(const CBlock &block, const CBlockIndex *pindex, CValidationState &state)
Definition: cbtx.cpp:52
Definition: block.h:72
Definition: cbtx.h:16
#define strprintf
Definition: tinyformat.h:1066
CQuorumBlockProcessor * quorumBlockProcessor
int64_t GetTimeMicros()
Returns the system time (not mockable)
Definition: utiltime.cpp:63
bool DoS(int level, bool ret=false, unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="", bool corruptionIn=false, const std::string &strDebugMessageIn="")
Definition: validation.h:36
Consensus::LLMQType llmqType
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
static const unsigned char REJECT_INVALID
Definition: validation.h:13
bool IsCoinBase() const
Definition: transaction.h:272
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object&#39;s serialization.
Definition: hash.h:254
LLMQType
Definition: params.h:48
#define LogPrintf(...)
Definition: util.h:203
std::map< Consensus::LLMQType, std::vector< const CBlockIndex * > > GetMinedAndActiveCommitmentsUntilBlock(const CBlockIndex *pindex)
#define LOCK(cs)
Definition: sync.h:178
uint256 merkleRootMNList
Definition: cbtx.h:24
uint256 merkleRootQuorums
Definition: cbtx.h:25
std::string ToString() const
Definition: uint256.cpp:62
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:189
VersionBitsCache versionbitscache
int32_t nHeight
Definition: cbtx.h:23
#define LogPrint(category,...)
Definition: util.h:214
Capture information about block/transaction validation.
Definition: validation.h:22
bool GetMinedCommitment(Consensus::LLMQType llmqType, const uint256 &quorumHash, CFinalCommitment &ret, uint256 &retMinedBlockHash)
256-bit opaque blob.
Definition: uint256.h:123
uint256 ComputeMerkleRoot(std::vector< uint256 > hashes, bool *mutated)
Definition: merkle.cpp:46
std::vector< CTransactionRef > vtx
Definition: block.h:76
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:170
const CChainParams & Params()
Return the currently selected parameters.
uint256 CalcMerkleRoot(bool *pmutated=nullptr) const
std::vector< std::unique_ptr< CSimplifiedMNListEntry > > mnList
Definition: simplifiedmns.h:77
uint16_t nVersion
Definition: cbtx.h:22
bool CalcCbTxMerkleRootQuorums(const CBlock &block, const CBlockIndex *pindexPrev, uint256 &merkleRootRet, CValidationState &state)
Definition: cbtx.cpp:161
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: transaction.h:198
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:183
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:54
ThresholdState VersionBitsState(const CBlockIndex *pindexPrev, const Consensus::Params &params, Consensus::DeploymentPos pos, VersionBitsCache &cache)
std::string ToString() const
Definition: cbtx.cpp:259
const int16_t nType
Definition: transaction.h:218
Released under the MIT license