2021-04-20 21:33:02 +02:00
|
|
|
// Copyright (c) 2018-2021 The Dash Core developers
|
2018-11-23 15:42:09 +01:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2021-10-02 19:32:24 +02:00
|
|
|
#include <llmq/blockprocessor.h>
|
|
|
|
#include <llmq/commitment.h>
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2021-04-16 05:41:16 +02:00
|
|
|
#include <evo/evodb.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <evo/specialtx.h>
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <chain.h>
|
|
|
|
#include <chainparams.h>
|
2021-04-16 05:41:16 +02:00
|
|
|
#include <consensus/params.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <consensus/validation.h>
|
|
|
|
#include <net.h>
|
|
|
|
#include <net_processing.h>
|
|
|
|
#include <primitives/block.h>
|
2021-04-16 05:41:16 +02:00
|
|
|
#include <primitives/transaction.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <validation.h>
|
2021-04-16 05:41:16 +02:00
|
|
|
#include <saltedhasher.h>
|
|
|
|
#include <sync.h>
|
|
|
|
|
|
|
|
#include <map>
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
namespace llmq
|
|
|
|
{
|
|
|
|
|
|
|
|
CQuorumBlockProcessor* quorumBlockProcessor;
|
|
|
|
|
|
|
|
static const std::string DB_MINED_COMMITMENT = "q_mc";
|
2019-04-02 12:51:13 +02:00
|
|
|
static const std::string DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT = "q_mcih";
|
|
|
|
|
2019-04-05 13:39:29 +02:00
|
|
|
static const std::string DB_BEST_BLOCK_UPGRADE = "q_bbu2";
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2021-01-25 10:22:28 +01:00
|
|
|
CQuorumBlockProcessor::CQuorumBlockProcessor(CEvoDB &_evoDb) :
|
|
|
|
evoDb(_evoDb)
|
|
|
|
{
|
|
|
|
CLLMQUtils::InitQuorumsCache(mapHasMinedCommitmentCache);
|
|
|
|
}
|
|
|
|
|
2021-01-11 04:28:37 +01:00
|
|
|
void CQuorumBlockProcessor::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
if (strCommand == NetMsgType::QFCOMMITMENT) {
|
|
|
|
CFinalCommitment qc;
|
|
|
|
vRecv >> qc;
|
|
|
|
|
2019-02-27 14:10:12 +01:00
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
2021-08-06 23:55:51 +02:00
|
|
|
EraseObjectRequest(pfrom->GetId(), CInv(MSG_QUORUM_FINAL_COMMITMENT, ::SerializeHash(qc)));
|
2019-02-27 14:10:12 +01:00
|
|
|
}
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
if (qc.IsNull()) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- null commitment from peer=%d\n", __func__, pfrom->GetId());
|
2021-09-28 23:40:32 +02:00
|
|
|
LOCK(cs_main);
|
2017-05-07 09:59:42 +02:00
|
|
|
Misbehaving(pfrom->GetId(), 100);
|
2018-11-23 15:42:09 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-10 19:36:18 +01:00
|
|
|
if (!Params().HasLLMQ(qc.llmqType)) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- invalid commitment type %d from peer=%d\n", __func__,
|
2021-10-15 12:28:19 +02:00
|
|
|
static_cast<uint8_t>(qc.llmqType), pfrom->GetId());
|
2021-09-28 23:40:32 +02:00
|
|
|
LOCK(cs_main);
|
2017-05-07 09:59:42 +02:00
|
|
|
Misbehaving(pfrom->GetId(), 100);
|
2018-11-23 15:42:09 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-06-26 15:10:53 +02:00
|
|
|
auto type = qc.llmqType;
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
// Verify that quorumHash is part of the active chain and that it's the first block in the DKG interval
|
2021-10-26 18:08:38 +02:00
|
|
|
const CBlockIndex* pQuorumBaseBlockIndex;
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
2021-10-26 18:08:38 +02:00
|
|
|
pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
|
|
|
|
if (!pQuorumBaseBlockIndex) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- unknown block %s in commitment, peer=%d\n", __func__,
|
2017-05-07 09:59:42 +02:00
|
|
|
qc.quorumHash.ToString(), pfrom->GetId());
|
2018-11-23 15:42:09 +01:00
|
|
|
// can't really punish the node here, as we might simply be the one that is on the wrong chain or not
|
|
|
|
// fully synced
|
|
|
|
return;
|
|
|
|
}
|
2021-10-26 18:08:38 +02:00
|
|
|
if (::ChainActive().Tip()->GetAncestor(pQuorumBaseBlockIndex->nHeight) != pQuorumBaseBlockIndex) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s not in active chain, peer=%d\n", __func__,
|
2017-05-07 09:59:42 +02:00
|
|
|
qc.quorumHash.ToString(), pfrom->GetId());
|
2018-11-23 15:42:09 +01:00
|
|
|
// same, can't punish
|
|
|
|
return;
|
|
|
|
}
|
2021-10-26 18:08:38 +02:00
|
|
|
int quorumHeight = pQuorumBaseBlockIndex->nHeight - (pQuorumBaseBlockIndex->nHeight % GetLLMQParams(type).dkgInterval);
|
|
|
|
if (quorumHeight != pQuorumBaseBlockIndex->nHeight) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s is not the first block in the DKG interval, peer=%d\n", __func__,
|
2017-05-07 09:59:42 +02:00
|
|
|
qc.quorumHash.ToString(), pfrom->GetId());
|
|
|
|
Misbehaving(pfrom->GetId(), 100);
|
2018-11-23 15:42:09 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check if we already got a better one locally
|
|
|
|
// We do this before verifying the commitment to avoid DoS
|
|
|
|
LOCK(minableCommitmentsCs);
|
|
|
|
auto k = std::make_pair(type, qc.quorumHash);
|
|
|
|
auto it = minableCommitmentsByQuorum.find(k);
|
|
|
|
if (it != minableCommitmentsByQuorum.end()) {
|
|
|
|
auto jt = minableCommitments.find(it->second);
|
2021-06-26 15:10:53 +02:00
|
|
|
if (jt != minableCommitments.end() && jt->second.CountSigners() <= qc.CountSigners()) {
|
|
|
|
return;
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:08:38 +02:00
|
|
|
if (!qc.Verify(pQuorumBaseBlockIndex, true)) {
|
2020-01-28 11:04:47 +01:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- commitment for quorum %s:%d is not valid, peer=%d\n", __func__,
|
2021-10-15 12:28:19 +02:00
|
|
|
qc.quorumHash.ToString(), static_cast<uint8_t>(qc.llmqType), pfrom->GetId());
|
2021-09-28 23:40:32 +02:00
|
|
|
LOCK(cs_main);
|
2017-05-07 09:59:42 +02:00
|
|
|
Misbehaving(pfrom->GetId(), 100);
|
2018-11-23 15:42:09 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- received commitment for quorum %s:%d, validMembers=%d, signers=%d, peer=%d\n", __func__,
|
2021-10-15 12:28:19 +02:00
|
|
|
qc.quorumHash.ToString(), static_cast<uint8_t>(qc.llmqType), qc.CountValidMembers(), qc.CountSigners(), pfrom->GetId());
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2021-04-15 20:19:03 +02:00
|
|
|
AddMineableCommitment(qc);
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 04:01:26 +01:00
|
|
|
bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, bool fJustCheck)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2019-04-25 17:39:04 +02:00
|
|
|
bool fDIP0003Active = pindex->nHeight >= Params().GetConsensus().DIP0003Height;
|
2018-11-23 15:42:09 +01:00
|
|
|
if (!fDIP0003Active) {
|
2019-04-04 17:58:51 +02:00
|
|
|
evoDb.Write(DB_BEST_BLOCK_UPGRADE, block.GetHash());
|
2018-11-23 15:42:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::map<Consensus::LLMQType, CFinalCommitment> qcs;
|
2019-04-25 17:39:04 +02:00
|
|
|
if (!GetCommitmentsFromBlock(block, pindex, qcs, state)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The following checks make sure that there is always a (possibly null) commitment while in the mining phase
|
|
|
|
// until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
|
|
|
|
// allowed, including null commitments.
|
2020-12-10 00:08:05 +01:00
|
|
|
// Note: must only check quorums that were enabled at the _previous_ block height to match mining logic
|
2021-10-28 21:10:43 +02:00
|
|
|
for (const Consensus::LLMQParams& params : CLLMQUtils::GetEnabledQuorumParams(pindex->pprev)) {
|
2020-05-11 14:33:21 +02:00
|
|
|
// skip these checks when replaying blocks after the crash
|
2021-10-16 12:54:22 +02:00
|
|
|
if (!::ChainActive().Tip()) {
|
2020-05-11 14:33:21 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
// does the currently processed block contain a (possibly null) commitment for the current session?
|
2021-10-28 21:10:43 +02:00
|
|
|
bool hasCommitmentInNewBlock = qcs.count(params.type) != 0;
|
|
|
|
bool isCommitmentRequired = IsCommitmentRequired(params, pindex->nHeight);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
if (hasCommitmentInNewBlock && !isCommitmentRequired) {
|
|
|
|
// If we're either not in the mining phase or a non-null commitment was mined already, reject the block
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-not-allowed");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hasCommitmentInNewBlock && isCommitmentRequired) {
|
|
|
|
// If no non-null commitment was mined for the mining phase yet and the new block does not include
|
|
|
|
// a (possibly null) commitment, the block should be rejected.
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-missing");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 17:58:51 +02:00
|
|
|
auto blockHash = block.GetHash();
|
|
|
|
|
2021-06-26 15:10:53 +02:00
|
|
|
for (const auto& p : qcs) {
|
|
|
|
const auto& qc = p.second;
|
2021-02-04 04:01:26 +01:00
|
|
|
if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2019-04-04 17:58:51 +02:00
|
|
|
|
|
|
|
evoDb.Write(DB_BEST_BLOCK_UPGRADE, blockHash);
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-02 12:51:13 +02:00
|
|
|
// We store a mapping from minedHeight->quorumHeight in the DB
|
|
|
|
// minedHeight is inversed so that entries are traversable in reversed order
|
2019-05-28 15:34:41 +02:00
|
|
|
static std::tuple<std::string, Consensus::LLMQType, uint32_t> BuildInversedHeightKey(Consensus::LLMQType llmqType, int nMinedHeight)
|
2019-04-02 12:51:13 +02:00
|
|
|
{
|
2019-04-05 13:39:29 +02:00
|
|
|
// nMinedHeight must be converted to big endian to make it comparable when serialized
|
2019-05-28 15:34:41 +02:00
|
|
|
return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT, llmqType, htobe32(std::numeric_limits<uint32_t>::max() - nMinedHeight));
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
|
|
|
|
2021-02-04 04:01:26 +01:00
|
|
|
bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, CValidationState& state, bool fJustCheck)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2021-09-08 00:33:02 +02:00
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2021-07-15 22:28:58 +02:00
|
|
|
const auto& llmq_params = GetLLMQParams(qc.llmqType);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
uint256 quorumHash = GetQuorumBlockHash(llmq_params, nHeight);
|
2020-05-11 14:33:21 +02:00
|
|
|
|
|
|
|
// skip `bad-qc-block` checks below when replaying blocks after the crash
|
2021-10-16 12:54:22 +02:00
|
|
|
if (!::ChainActive().Tip()) {
|
2020-05-11 14:33:21 +02:00
|
|
|
quorumHash = qc.quorumHash;
|
|
|
|
}
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
if (quorumHash.IsNull()) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
|
|
|
}
|
|
|
|
if (quorumHash != qc.quorumHash) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qc.IsNull()) {
|
2018-11-27 08:04:08 +01:00
|
|
|
if (!qc.VerifyNull()) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null");
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-15 22:28:58 +02:00
|
|
|
if (HasMinedCommitment(llmq_params.type, quorumHash)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
// should not happen as it's already handled in ProcessBlock
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup");
|
|
|
|
}
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
if (!IsMiningPhase(llmq_params, nHeight)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
// should not happen as it's already handled in ProcessBlock
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-height");
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:08:38 +02:00
|
|
|
auto pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2021-10-26 18:08:38 +02:00
|
|
|
if (!qc.Verify(pQuorumBaseBlockIndex, true)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid");
|
|
|
|
}
|
|
|
|
|
2021-02-04 04:01:26 +01:00
|
|
|
if (fJustCheck) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
// Store commitment in DB
|
2021-07-15 22:28:58 +02:00
|
|
|
auto cacheKey = std::make_pair(llmq_params.type, quorumHash);
|
2021-01-25 10:22:28 +01:00
|
|
|
evoDb.Write(std::make_pair(DB_MINED_COMMITMENT, cacheKey), std::make_pair(qc, blockHash));
|
2021-10-26 18:08:38 +02:00
|
|
|
evoDb.Write(BuildInversedHeightKey(llmq_params.type, nHeight), pQuorumBaseBlockIndex->nHeight);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2019-02-26 07:20:47 +01:00
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
2021-01-25 10:22:28 +01:00
|
|
|
mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash);
|
|
|
|
minableCommitmentsByQuorum.erase(cacheKey);
|
|
|
|
minableCommitments.erase(::SerializeHash(qc));
|
2019-02-26 07:20:47 +01:00
|
|
|
}
|
|
|
|
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- processed commitment from block. type=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s\n", __func__,
|
2021-10-15 12:28:19 +02:00
|
|
|
static_cast<uint8_t>(qc.llmqType), quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString());
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
|
|
|
std::map<Consensus::LLMQType, CFinalCommitment> qcs;
|
|
|
|
CValidationState dummy;
|
2019-04-25 17:39:04 +02:00
|
|
|
if (!GetCommitmentsFromBlock(block, pindex, qcs, dummy)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& p : qcs) {
|
|
|
|
auto& qc = p.second;
|
|
|
|
if (qc.IsNull()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)));
|
2021-06-26 15:10:53 +02:00
|
|
|
evoDb.Erase(BuildInversedHeightKey(qc.llmqType, pindex->nHeight));
|
2019-02-26 07:20:47 +01:00
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
2021-01-25 10:22:28 +01:00
|
|
|
mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash);
|
2019-02-26 07:20:47 +01:00
|
|
|
}
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2019-02-02 01:08:51 +01:00
|
|
|
// if a reorg happened, we should allow to mine this commitment later
|
2021-04-15 20:19:03 +02:00
|
|
|
AddMineableCommitment(qc);
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
2019-04-04 17:58:51 +02:00
|
|
|
evoDb.Write(DB_BEST_BLOCK_UPGRADE, pindex->pprev->GetBlockHash());
|
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-02 12:51:13 +02:00
|
|
|
// TODO remove this with 0.15.0
|
2020-09-25 18:19:58 +02:00
|
|
|
bool CQuorumBlockProcessor::UpgradeDB()
|
2019-04-02 12:51:13 +02:00
|
|
|
{
|
2019-04-04 17:58:51 +02:00
|
|
|
LOCK(cs_main);
|
2020-09-25 18:19:58 +02:00
|
|
|
|
2021-10-16 12:54:22 +02:00
|
|
|
if (::ChainActive().Tip() == nullptr) {
|
2020-09-25 18:19:58 +02:00
|
|
|
// should have no records
|
|
|
|
return evoDb.IsEmpty();
|
|
|
|
}
|
|
|
|
|
2019-04-04 17:58:51 +02:00
|
|
|
uint256 bestBlock;
|
2021-10-16 12:54:22 +02:00
|
|
|
if (evoDb.GetRawDB().Read(DB_BEST_BLOCK_UPGRADE, bestBlock) && bestBlock == ::ChainActive().Tip()->GetBlockHash()) {
|
2020-09-25 18:19:58 +02:00
|
|
|
return true;
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LogPrintf("CQuorumBlockProcessor::%s -- Upgrading DB...\n", __func__);
|
|
|
|
|
2021-10-16 12:54:22 +02:00
|
|
|
if (::ChainActive().Height() >= Params().GetConsensus().DIP0003EnforcementHeight) {
|
|
|
|
auto pindex = ::ChainActive()[Params().GetConsensus().DIP0003EnforcementHeight];
|
2019-04-04 17:58:51 +02:00
|
|
|
while (pindex) {
|
2019-06-08 13:16:41 +02:00
|
|
|
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
|
|
|
// Too late, we already pruned blocks we needed to reprocess commitments
|
2020-09-25 18:19:58 +02:00
|
|
|
return false;
|
2019-06-08 13:16:41 +02:00
|
|
|
}
|
2019-04-04 17:58:51 +02:00
|
|
|
CBlock block;
|
|
|
|
bool r = ReadBlockFromDisk(block, pindex, Params().GetConsensus());
|
|
|
|
assert(r);
|
|
|
|
|
|
|
|
std::map<Consensus::LLMQType, CFinalCommitment> qcs;
|
|
|
|
CValidationState dummyState;
|
2019-04-25 17:39:04 +02:00
|
|
|
GetCommitmentsFromBlock(block, pindex, qcs, dummyState);
|
2019-04-04 17:58:51 +02:00
|
|
|
|
|
|
|
for (const auto& p : qcs) {
|
|
|
|
const auto& qc = p.second;
|
|
|
|
if (qc.IsNull()) {
|
|
|
|
continue;
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
2021-10-26 18:08:38 +02:00
|
|
|
auto pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
|
2019-05-28 15:34:41 +02:00
|
|
|
evoDb.GetRawDB().Write(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)), std::make_pair(qc, pindex->GetBlockHash()));
|
2021-10-26 18:08:38 +02:00
|
|
|
evoDb.GetRawDB().Write(BuildInversedHeightKey(qc.llmqType, pindex->nHeight), pQuorumBaseBlockIndex->nHeight);
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
2019-04-04 17:58:51 +02:00
|
|
|
|
|
|
|
evoDb.GetRawDB().Write(DB_BEST_BLOCK_UPGRADE, pindex->GetBlockHash());
|
|
|
|
|
2021-10-16 12:54:22 +02:00
|
|
|
pindex = ::ChainActive().Next(pindex);
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LogPrintf("CQuorumBlockProcessor::%s -- Upgrade done...\n", __func__);
|
2020-09-25 18:19:58 +02:00
|
|
|
return true;
|
2019-04-02 12:51:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 17:39:04 +02:00
|
|
|
bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::map<Consensus::LLMQType, CFinalCommitment>& ret, CValidationState& state)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2021-08-06 23:55:51 +02:00
|
|
|
const auto& consensus = Params().GetConsensus();
|
2019-04-25 17:39:04 +02:00
|
|
|
bool fDIP0003Active = pindex->nHeight >= consensus.DIP0003Height;
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
ret.clear();
|
|
|
|
|
|
|
|
for (const auto& tx : block.vtx) {
|
|
|
|
if (tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
|
2018-11-27 08:04:08 +01:00
|
|
|
CFinalCommitmentTxPayload qc;
|
2018-11-23 15:42:09 +01:00
|
|
|
if (!GetTxPayload(*tx, qc)) {
|
2018-11-27 08:04:08 +01:00
|
|
|
// should not happen as it was verified before processing the block
|
2020-06-08 04:57:57 +02:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// only allow one commitment per type and per block
|
2021-06-26 15:10:53 +02:00
|
|
|
if (ret.count(qc.commitment.llmqType)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup");
|
|
|
|
}
|
|
|
|
|
2021-06-26 15:10:53 +02:00
|
|
|
ret.emplace(qc.commitment.llmqType, std::move(qc.commitment));
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fDIP0003Active && !ret.empty()) {
|
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-premature");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
bool CQuorumBlockProcessor::IsMiningPhase(const Consensus::LLMQParams& llmqParams, int nHeight)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2021-10-28 21:10:43 +02:00
|
|
|
int phaseIndex = nHeight % llmqParams.dkgInterval;
|
|
|
|
if (phaseIndex >= llmqParams.dkgMiningWindowStart && phaseIndex <= llmqParams.dkgMiningWindowEnd) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
bool CQuorumBlockProcessor::IsCommitmentRequired(const Consensus::LLMQParams& llmqParams, int nHeight) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2021-09-08 00:33:02 +02:00
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
uint256 quorumHash = GetQuorumBlockHash(llmqParams, nHeight);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
// perform extra check for quorumHash.IsNull as the quorum hash is unknown for the first block of a session
|
|
|
|
// this is because the currently processed block's hash will be the quorumHash of this session
|
2021-10-28 21:10:43 +02:00
|
|
|
bool isMiningPhase = !quorumHash.IsNull() && IsMiningPhase(llmqParams, nHeight);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
|
|
|
// did we already mine a non-null commitment for this session?
|
2021-10-28 21:10:43 +02:00
|
|
|
bool hasMinedCommitment = !quorumHash.IsNull() && HasMinedCommitment(llmqParams.type, quorumHash);
|
2018-11-23 15:42:09 +01:00
|
|
|
|
2018-12-13 13:55:46 +01:00
|
|
|
return isMiningPhase && !hasMinedCommitment;
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: This method returns uint256() on the first block of the DKG interval (because the block hash is not known yet)
|
2021-10-28 21:10:43 +02:00
|
|
|
uint256 CQuorumBlockProcessor::GetQuorumBlockHash(const Consensus::LLMQParams& llmqParams, int nHeight)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2018-12-13 09:04:08 +01:00
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
int quorumStartHeight = nHeight - (nHeight % llmqParams.dkgInterval);
|
2018-12-13 09:04:08 +01:00
|
|
|
uint256 quorumBlockHash;
|
|
|
|
if (!GetBlockHash(quorumBlockHash, quorumStartHeight)) {
|
2020-08-09 23:35:02 +02:00
|
|
|
return {};
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
2018-12-13 09:04:08 +01:00
|
|
|
return quorumBlockHash;
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
2021-09-08 00:33:02 +02:00
|
|
|
bool CQuorumBlockProcessor::HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2021-01-25 10:22:28 +01:00
|
|
|
bool fExists;
|
2019-02-26 07:20:47 +01:00
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
2021-01-25 10:22:28 +01:00
|
|
|
if (mapHasMinedCommitmentCache[llmqType].get(quorumHash, fExists)) {
|
|
|
|
return fExists;
|
2019-02-26 07:20:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-25 10:22:28 +01:00
|
|
|
fExists = evoDb.Exists(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash)));
|
2019-02-26 07:20:47 +01:00
|
|
|
|
|
|
|
LOCK(minableCommitmentsCs);
|
2021-01-25 10:22:28 +01:00
|
|
|
mapHasMinedCommitmentCache[llmqType].insert(quorumHash, fExists);
|
|
|
|
|
|
|
|
return fExists;
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
2021-09-08 00:33:02 +02:00
|
|
|
CFinalCommitmentPtr CQuorumBlockProcessor::GetMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash, uint256& retMinedBlockHash) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
2019-05-28 15:34:41 +02:00
|
|
|
auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash));
|
2019-04-04 11:11:25 +02:00
|
|
|
std::pair<CFinalCommitment, uint256> p;
|
|
|
|
if (!evoDb.Read(key, p)) {
|
2021-04-16 05:41:16 +02:00
|
|
|
return nullptr;
|
2019-04-04 11:11:25 +02:00
|
|
|
}
|
|
|
|
retMinedBlockHash = p.second;
|
2021-11-29 06:12:09 +01:00
|
|
|
return std::make_unique<CFinalCommitment>(p.first);
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
2019-06-18 13:34:16 +02:00
|
|
|
// The returned quorums are in reversed order, so the most recent one is at index 0
|
2021-09-08 00:33:02 +02:00
|
|
|
std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t maxCount) const
|
2019-02-02 01:08:51 +01:00
|
|
|
{
|
2020-03-27 15:11:42 +01:00
|
|
|
LOCK(evoDb.cs);
|
|
|
|
|
2019-04-04 10:18:31 +02:00
|
|
|
auto dbIt = evoDb.GetCurTransaction().NewIteratorUniquePtr();
|
|
|
|
|
|
|
|
auto firstKey = BuildInversedHeightKey(llmqType, pindex->nHeight);
|
|
|
|
auto lastKey = BuildInversedHeightKey(llmqType, 0);
|
|
|
|
|
|
|
|
dbIt->Seek(firstKey);
|
|
|
|
|
|
|
|
std::vector<const CBlockIndex*> ret;
|
|
|
|
ret.reserve(maxCount);
|
|
|
|
|
|
|
|
while (dbIt->Valid() && ret.size() < maxCount) {
|
|
|
|
decltype(firstKey) curKey;
|
|
|
|
int quorumHeight;
|
|
|
|
if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
|
|
|
|
break;
|
|
|
|
}
|
2019-05-28 15:34:41 +02:00
|
|
|
if (std::get<0>(curKey) != DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT || std::get<1>(curKey) != llmqType) {
|
2019-04-04 10:18:31 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-04-05 13:39:29 +02:00
|
|
|
uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh(std::get<2>(curKey));
|
2022-02-11 17:15:26 +01:00
|
|
|
if (nMinedHeight > uint32_t(pindex->nHeight)) {
|
2019-04-04 10:18:31 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dbIt->GetValue(quorumHeight)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:08:38 +02:00
|
|
|
auto pQuorumBaseBlockIndex = pindex->GetAncestor(quorumHeight);
|
|
|
|
assert(pQuorumBaseBlockIndex);
|
|
|
|
ret.emplace_back(pQuorumBaseBlockIndex);
|
2019-04-04 10:18:31 +02:00
|
|
|
|
|
|
|
dbIt->Next();
|
2019-02-02 01:08:51 +01:00
|
|
|
}
|
2019-04-04 10:18:31 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-18 13:34:16 +02:00
|
|
|
// The returned quorums are in reversed order, so the most recent one is at index 0
|
2021-09-08 00:33:02 +02:00
|
|
|
std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> CQuorumBlockProcessor::GetMinedAndActiveCommitmentsUntilBlock(const CBlockIndex* pindex) const
|
2019-04-04 10:18:31 +02:00
|
|
|
{
|
|
|
|
std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> ret;
|
|
|
|
|
2022-01-10 19:36:18 +01:00
|
|
|
for (const auto& params : Params().GetConsensus().llmqs) {
|
|
|
|
auto& v = ret[params.type];
|
|
|
|
v.reserve(params.signingActiveQuorumCount);
|
|
|
|
auto commitments = GetMinedCommitmentsUntilBlock(params.type, pindex, params.signingActiveQuorumCount);
|
2021-08-06 23:55:51 +02:00
|
|
|
std::copy(commitments.begin(), commitments.end(), std::back_inserter(v));
|
2019-04-04 10:18:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2019-02-02 01:08:51 +01:00
|
|
|
}
|
|
|
|
|
2021-09-08 00:33:02 +02:00
|
|
|
bool CQuorumBlockProcessor::HasMineableCommitment(const uint256& hash) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
|
|
|
return minableCommitments.count(hash) != 0;
|
|
|
|
}
|
|
|
|
|
2021-04-15 20:19:03 +02:00
|
|
|
void CQuorumBlockProcessor::AddMineableCommitment(const CFinalCommitment& fqc)
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
bool relay = false;
|
|
|
|
uint256 commitmentHash = ::SerializeHash(fqc);
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
|
|
|
|
2021-06-26 15:10:53 +02:00
|
|
|
auto k = std::make_pair(fqc.llmqType, fqc.quorumHash);
|
2018-11-23 15:42:09 +01:00
|
|
|
auto ins = minableCommitmentsByQuorum.emplace(k, commitmentHash);
|
|
|
|
if (ins.second) {
|
|
|
|
minableCommitments.emplace(commitmentHash, fqc);
|
|
|
|
relay = true;
|
|
|
|
} else {
|
2021-06-26 15:10:53 +02:00
|
|
|
const auto& oldFqc = minableCommitments.at(ins.first->second);
|
2018-11-23 15:42:09 +01:00
|
|
|
if (fqc.CountSigners() > oldFqc.CountSigners()) {
|
|
|
|
// new commitment has more signers, so override the known one
|
|
|
|
ins.first->second = commitmentHash;
|
|
|
|
minableCommitments.erase(ins.first->second);
|
|
|
|
minableCommitments.emplace(commitmentHash, fqc);
|
|
|
|
relay = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We only relay the new commitment if it's new or better then the old one
|
|
|
|
if (relay) {
|
|
|
|
CInv inv(MSG_QUORUM_FINAL_COMMITMENT, commitmentHash);
|
2020-08-14 13:42:15 +02:00
|
|
|
g_connman->RelayInv(inv);
|
2018-11-23 15:42:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-08 00:33:02 +02:00
|
|
|
bool CQuorumBlockProcessor::GetMineableCommitmentByHash(const uint256& commitmentHash, llmq::CFinalCommitment& ret) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
LOCK(minableCommitmentsCs);
|
|
|
|
auto it = minableCommitments.find(commitmentHash);
|
|
|
|
if (it == minableCommitments.end()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ret = it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Will return false if no commitment should be mined
|
2021-04-15 20:19:03 +02:00
|
|
|
// Will return true and a null commitment if no mineable commitment is known and none was mined yet
|
2021-10-28 21:10:43 +02:00
|
|
|
bool CQuorumBlockProcessor::GetMineableCommitment(const Consensus::LLMQParams& llmqParams, int nHeight, CFinalCommitment& ret) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
if (!IsCommitmentRequired(llmqParams, nHeight)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
// no commitment required
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
uint256 quorumHash = GetQuorumBlockHash(llmqParams, nHeight);
|
2018-11-23 15:42:09 +01:00
|
|
|
if (quorumHash.IsNull()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOCK(minableCommitmentsCs);
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
auto k = std::make_pair(llmqParams.type, quorumHash);
|
2018-11-23 15:42:09 +01:00
|
|
|
auto it = minableCommitmentsByQuorum.find(k);
|
|
|
|
if (it == minableCommitmentsByQuorum.end()) {
|
|
|
|
// null commitment required
|
2021-10-28 21:10:43 +02:00
|
|
|
ret = CFinalCommitment(llmqParams, quorumHash);
|
2018-11-23 15:42:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = minableCommitments.at(it->second);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-28 21:10:43 +02:00
|
|
|
bool CQuorumBlockProcessor::GetMineableCommitmentTx(const Consensus::LLMQParams& llmqParams, int nHeight, CTransactionRef& ret) const
|
2018-11-23 15:42:09 +01:00
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
2018-11-27 08:04:08 +01:00
|
|
|
CFinalCommitmentTxPayload qc;
|
2021-10-28 21:10:43 +02:00
|
|
|
if (!GetMineableCommitment(llmqParams, nHeight, qc.commitment)) {
|
2018-11-23 15:42:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-13 09:04:08 +01:00
|
|
|
qc.nHeight = nHeight;
|
2018-11-27 08:04:08 +01:00
|
|
|
|
2018-11-23 15:42:09 +01:00
|
|
|
CMutableTransaction tx;
|
|
|
|
tx.nVersion = 3;
|
|
|
|
tx.nType = TRANSACTION_QUORUM_COMMITMENT;
|
|
|
|
SetTxPayload(tx, qc);
|
|
|
|
|
|
|
|
ret = MakeTransactionRef(tx);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-15 20:55:01 +02:00
|
|
|
} // namespace llmq
|