2022-06-08 01:36:46 +02:00
|
|
|
// Copyright (c) 2017-2022 The Dash Core developers
|
2018-04-09 10:35:43 +02:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <evo/simplifiedmns.h>
|
|
|
|
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <evo/cbtx.h>
|
|
|
|
#include <core_io.h>
|
|
|
|
#include <evo/deterministicmns.h>
|
2021-10-02 19:32:24 +02:00
|
|
|
#include <llmq/blockprocessor.h>
|
|
|
|
#include <llmq/commitment.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <evo/specialtx.h>
|
2018-04-09 10:35:43 +02:00
|
|
|
|
2021-04-16 05:41:16 +02:00
|
|
|
#include <pubkey.h>
|
|
|
|
#include <serialize.h>
|
|
|
|
#include <version.h>
|
|
|
|
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <base58.h>
|
|
|
|
#include <chainparams.h>
|
|
|
|
#include <consensus/merkle.h>
|
|
|
|
#include <univalue.h>
|
|
|
|
#include <validation.h>
|
2022-01-24 15:20:50 +01:00
|
|
|
#include <key_io.h>
|
2018-04-09 10:35:43 +02:00
|
|
|
|
|
|
|
CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) :
|
|
|
|
proRegTxHash(dmn.proTxHash),
|
2018-11-23 10:40:46 +01:00
|
|
|
confirmedHash(dmn.pdmnState->confirmedHash),
|
2018-04-09 10:35:43 +02:00
|
|
|
service(dmn.pdmnState->addr),
|
2018-10-21 21:45:16 +02:00
|
|
|
pubKeyOperator(dmn.pdmnState->pubKeyOperator),
|
2018-04-09 10:35:43 +02:00
|
|
|
keyIDVoting(dmn.pdmnState->keyIDVoting),
|
2022-06-24 20:35:23 +02:00
|
|
|
isValid(!dmn.pdmnState->IsBanned()),
|
|
|
|
scriptPayout(dmn.pdmnState->scriptPayout),
|
|
|
|
scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout)
|
2018-04-09 10:35:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 CSimplifiedMNListEntry::CalcHash() const
|
|
|
|
{
|
|
|
|
CHashWriter hw(SER_GETHASH, CLIENT_VERSION);
|
|
|
|
hw << *this;
|
|
|
|
return hw.GetHash();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CSimplifiedMNListEntry::ToString() const
|
|
|
|
{
|
2022-06-24 20:35:23 +02:00
|
|
|
CTxDestination dest;
|
|
|
|
std::string payoutAddress = "unknown";
|
|
|
|
std::string operatorPayoutAddress = "none";
|
|
|
|
if (ExtractDestination(scriptPayout, dest)) {
|
|
|
|
payoutAddress = EncodeDestination(dest);
|
|
|
|
}
|
|
|
|
if (ExtractDestination(scriptOperatorPayout, dest)) {
|
|
|
|
operatorPayoutAddress = EncodeDestination(dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
return strprintf("CSimplifiedMNListEntry(proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s)",
|
|
|
|
proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(keyIDVoting), isValid, payoutAddress, operatorPayoutAddress);
|
2018-04-09 10:35:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-24 20:35:23 +02:00
|
|
|
void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const
|
2018-04-09 10:35:43 +02:00
|
|
|
{
|
|
|
|
obj.clear();
|
|
|
|
obj.setObject();
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("proRegTxHash", proRegTxHash.ToString());
|
|
|
|
obj.pushKV("confirmedHash", confirmedHash.ToString());
|
|
|
|
obj.pushKV("service", service.ToString(false));
|
|
|
|
obj.pushKV("pubKeyOperator", pubKeyOperator.Get().ToString());
|
|
|
|
obj.pushKV("votingAddress", EncodeDestination(keyIDVoting));
|
|
|
|
obj.pushKV("isValid", isValid);
|
2022-06-24 20:35:23 +02:00
|
|
|
|
|
|
|
if (!extended) return;
|
|
|
|
|
|
|
|
CTxDestination dest;
|
|
|
|
if (ExtractDestination(scriptPayout, dest)) {
|
|
|
|
obj.pushKV("payoutAddress", EncodeDestination(dest));
|
|
|
|
}
|
|
|
|
if (ExtractDestination(scriptOperatorPayout, dest)) {
|
|
|
|
obj.pushKV("operatorPayoutAddress", EncodeDestination(dest));
|
|
|
|
}
|
2018-04-09 10:35:43 +02:00
|
|
|
}
|
|
|
|
|
2018-08-22 12:39:41 +02:00
|
|
|
CSimplifiedMNList::CSimplifiedMNList(const std::vector<CSimplifiedMNListEntry>& smlEntries)
|
|
|
|
{
|
2019-07-02 22:46:03 +02:00
|
|
|
mnList.resize(smlEntries.size());
|
|
|
|
for (size_t i = 0; i < smlEntries.size(); i++) {
|
|
|
|
mnList[i] = std::make_unique<CSimplifiedMNListEntry>(smlEntries[i]);
|
|
|
|
}
|
2018-08-22 12:39:41 +02:00
|
|
|
|
2019-07-02 22:46:03 +02:00
|
|
|
std::sort(mnList.begin(), mnList.end(), [&](const std::unique_ptr<CSimplifiedMNListEntry>& a, const std::unique_ptr<CSimplifiedMNListEntry>& b) {
|
|
|
|
return a->proRegTxHash.Compare(b->proRegTxHash) < 0;
|
2018-08-22 12:39:41 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-04-09 10:35:43 +02:00
|
|
|
CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList)
|
|
|
|
{
|
2019-07-02 22:46:03 +02:00
|
|
|
mnList.resize(dmnList.GetAllMNsCount());
|
2018-04-09 10:35:43 +02:00
|
|
|
|
2019-07-02 22:46:03 +02:00
|
|
|
size_t i = 0;
|
refactor: numerous changes to avoid passing around a const ref to shared_ptr of CDeterministicMNC when not needed. (#4653)
* refactor: numerous changes to avoid passing around a const ref to shared_ptr of CDeterministicMNC when not needed.
Introduces ForEachMNShared, a version of ForEachMN that uses a shared_ptr, and may extend the lifetime of the underlying shared_ptr. This is not preferred, should prefer ForEachMN. See docs.
Adjusts ForEachMN to pass a reference. This is preferred for use over ForEachMNShared. See docs. A reference should be used since in usage we assume it's non-null anyway. Additionally, it allows us to know that the lifespan of the dmn is not being being extended (if lifespan needs to be extended, should use ForEachMNShared.
IsMNValid, IsMNPoSeBanned, UpdateMN, UpdateMN, AddUniqueProperty, DeleteUniqueProperty, UpdateUniqueProperty now take a const reference to CDeterministicMN instead of a const reference to shared_ptr<CDeterministicMN>. All of these functions previously assumed (or would've crashed) a non-null ptr, and non extended lifetime, as such converting to ref is appropriate.
CompareByLastPaid ptr overload now takes raw ptr instead of a const ref to shared. Since we simply dereference them, a raw ptr makes the most sense. This also avoids a potential expensive and implicit raw ptr -> shared ptr conversion if the function was called with raw ptrs.
rpcevo BuildDMNListEntry now takes a const ref for reasons as stated above
Signed-off-by: Pasta <pasta@dashboost.org>
* make stuff const
Signed-off-by: Pasta <pasta@dashboost.org>
* refactor/llmq: use ranges count_if
Signed-off-by: Pasta <pasta@dashboost.org>
2022-01-04 17:13:38 +01:00
|
|
|
dmnList.ForEachMN(false, [this, &i](auto& dmn) {
|
|
|
|
mnList[i++] = std::make_unique<CSimplifiedMNListEntry>(dmn);
|
2018-10-02 11:03:05 +02:00
|
|
|
});
|
2018-04-09 10:35:43 +02:00
|
|
|
|
2019-07-02 22:46:03 +02:00
|
|
|
std::sort(mnList.begin(), mnList.end(), [&](const std::unique_ptr<CSimplifiedMNListEntry>& a, const std::unique_ptr<CSimplifiedMNListEntry>& b) {
|
|
|
|
return a->proRegTxHash.Compare(b->proRegTxHash) < 0;
|
2018-04-09 10:35:43 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-06 09:54:23 +01:00
|
|
|
uint256 CSimplifiedMNList::CalcMerkleRoot(bool* pmutated) const
|
2018-04-09 10:35:43 +02:00
|
|
|
{
|
|
|
|
std::vector<uint256> leaves;
|
|
|
|
leaves.reserve(mnList.size());
|
|
|
|
for (const auto& e : mnList) {
|
2019-07-02 22:46:03 +02:00
|
|
|
leaves.emplace_back(e->CalcHash());
|
2018-04-09 10:35:43 +02:00
|
|
|
}
|
|
|
|
return ComputeMerkleRoot(leaves, pmutated);
|
|
|
|
}
|
2018-04-09 14:49:34 +02:00
|
|
|
|
2020-08-09 23:34:26 +02:00
|
|
|
CSimplifiedMNListDiff::CSimplifiedMNListDiff() = default;
|
2019-04-04 10:27:17 +02:00
|
|
|
|
2020-08-09 23:34:26 +02:00
|
|
|
CSimplifiedMNListDiff::~CSimplifiedMNListDiff() = default;
|
2019-04-04 10:27:17 +02:00
|
|
|
|
|
|
|
bool CSimplifiedMNListDiff::BuildQuorumsDiff(const CBlockIndex* baseBlockIndex, const CBlockIndex* blockIndex)
|
|
|
|
{
|
|
|
|
auto baseQuorums = llmq::quorumBlockProcessor->GetMinedAndActiveCommitmentsUntilBlock(baseBlockIndex);
|
|
|
|
auto quorums = llmq::quorumBlockProcessor->GetMinedAndActiveCommitmentsUntilBlock(blockIndex);
|
|
|
|
|
|
|
|
std::set<std::pair<Consensus::LLMQType, uint256>> baseQuorumHashes;
|
|
|
|
std::set<std::pair<Consensus::LLMQType, uint256>> quorumHashes;
|
2021-07-31 20:29:12 +02:00
|
|
|
for (const auto& p : baseQuorums) {
|
|
|
|
for (const auto& p2 : p.second) {
|
2019-04-04 10:27:17 +02:00
|
|
|
baseQuorumHashes.emplace(p.first, p2->GetBlockHash());
|
|
|
|
}
|
|
|
|
}
|
2021-07-31 20:29:12 +02:00
|
|
|
for (const auto& p : quorums) {
|
|
|
|
for (const auto& p2 : p.second) {
|
2019-04-04 10:27:17 +02:00
|
|
|
quorumHashes.emplace(p.first, p2->GetBlockHash());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& p : baseQuorumHashes) {
|
|
|
|
if (!quorumHashes.count(p)) {
|
|
|
|
deletedQuorums.emplace_back((uint8_t)p.first, p.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto& p : quorumHashes) {
|
|
|
|
if (!baseQuorumHashes.count(p)) {
|
2019-04-04 11:11:25 +02:00
|
|
|
uint256 minedBlockHash;
|
2021-04-16 05:41:16 +02:00
|
|
|
llmq::CFinalCommitmentPtr qc = llmq::quorumBlockProcessor->GetMinedCommitment(p.first, p.second, minedBlockHash);
|
|
|
|
if (qc == nullptr) {
|
2019-04-04 10:27:17 +02:00
|
|
|
return false;
|
|
|
|
}
|
2021-04-16 05:41:16 +02:00
|
|
|
newQuorums.emplace_back(*qc);
|
2019-04-04 10:27:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-24 20:35:23 +02:00
|
|
|
void CSimplifiedMNListDiff::ToJson(UniValue& obj, bool extended) const
|
2018-04-09 14:49:34 +02:00
|
|
|
{
|
|
|
|
obj.setObject();
|
|
|
|
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("baseBlockHash", baseBlockHash.ToString());
|
|
|
|
obj.pushKV("blockHash", blockHash.ToString());
|
2018-04-09 14:49:34 +02:00
|
|
|
|
2018-10-30 10:59:32 +01:00
|
|
|
CDataStream ssCbTxMerkleTree(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssCbTxMerkleTree << cbTxMerkleTree;
|
2021-05-18 19:17:10 +02:00
|
|
|
obj.pushKV("cbTxMerkleTree", HexStr(ssCbTxMerkleTree));
|
2018-10-30 10:59:32 +01:00
|
|
|
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("cbTx", EncodeHexTx(*cbTx));
|
2018-10-30 10:59:32 +01:00
|
|
|
|
2018-04-09 14:49:34 +02:00
|
|
|
UniValue deletedMNsArr(UniValue::VARR);
|
|
|
|
for (const auto& h : deletedMNs) {
|
|
|
|
deletedMNsArr.push_back(h.ToString());
|
|
|
|
}
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("deletedMNs", deletedMNsArr);
|
2018-04-09 14:49:34 +02:00
|
|
|
|
|
|
|
UniValue mnListArr(UniValue::VARR);
|
|
|
|
for (const auto& e : mnList) {
|
|
|
|
UniValue eObj;
|
2022-06-24 20:35:23 +02:00
|
|
|
e.ToJson(eObj, extended);
|
2018-04-09 14:49:34 +02:00
|
|
|
mnListArr.push_back(eObj);
|
|
|
|
}
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("mnList", mnListArr);
|
2018-04-09 14:49:34 +02:00
|
|
|
|
2019-04-04 10:27:17 +02:00
|
|
|
UniValue deletedQuorumsArr(UniValue::VARR);
|
|
|
|
for (const auto& e : deletedQuorums) {
|
|
|
|
UniValue eObj(UniValue::VOBJ);
|
2020-06-18 11:17:23 +02:00
|
|
|
eObj.pushKV("llmqType", e.first);
|
|
|
|
eObj.pushKV("quorumHash", e.second.ToString());
|
2019-04-04 10:27:17 +02:00
|
|
|
deletedQuorumsArr.push_back(eObj);
|
|
|
|
}
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("deletedQuorums", deletedQuorumsArr);
|
2019-04-04 10:27:17 +02:00
|
|
|
|
|
|
|
UniValue newQuorumsArr(UniValue::VARR);
|
|
|
|
for (const auto& e : newQuorums) {
|
|
|
|
UniValue eObj;
|
|
|
|
e.ToJson(eObj);
|
|
|
|
newQuorumsArr.push_back(eObj);
|
|
|
|
}
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("newQuorums", newQuorumsArr);
|
2019-04-04 10:27:17 +02:00
|
|
|
|
2018-04-09 14:49:34 +02:00
|
|
|
CCbTx cbTxPayload;
|
|
|
|
if (GetTxPayload(*cbTx, cbTxPayload)) {
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("merkleRootMNList", cbTxPayload.merkleRootMNList.ToString());
|
2019-04-04 10:27:17 +02:00
|
|
|
if (cbTxPayload.nVersion >= 2) {
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("merkleRootQuorums", cbTxPayload.merkleRootQuorums.ToString());
|
2019-04-04 10:27:17 +02:00
|
|
|
}
|
2018-04-09 14:49:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-24 20:35:23 +02:00
|
|
|
bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet, bool extended)
|
2018-04-09 14:49:34 +02:00
|
|
|
{
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
mnListDiffRet = CSimplifiedMNListDiff();
|
|
|
|
|
2021-10-16 12:54:22 +02:00
|
|
|
const CBlockIndex* baseBlockIndex = ::ChainActive().Genesis();
|
2018-04-09 14:49:34 +02:00
|
|
|
if (!baseBlockHash.IsNull()) {
|
2020-12-12 20:04:30 +01:00
|
|
|
baseBlockIndex = LookupBlockIndex(baseBlockHash);
|
2018-03-13 19:04:28 +01:00
|
|
|
if (!baseBlockIndex) {
|
2019-02-12 20:52:06 +01:00
|
|
|
errorRet = strprintf("block %s not found", baseBlockHash.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
2018-04-09 14:49:34 +02:00
|
|
|
}
|
2018-03-13 19:04:28 +01:00
|
|
|
|
|
|
|
const CBlockIndex* blockIndex = LookupBlockIndex(blockHash);
|
|
|
|
if (!blockIndex) {
|
2018-04-09 14:49:34 +02:00
|
|
|
errorRet = strprintf("block %s not found", blockHash.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-16 12:54:22 +02:00
|
|
|
if (!::ChainActive().Contains(baseBlockIndex) || !::ChainActive().Contains(blockIndex)) {
|
2018-04-09 14:49:34 +02:00
|
|
|
errorRet = strprintf("block %s and %s are not in the same chain", baseBlockHash.ToString(), blockHash.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
2019-02-12 20:52:06 +01:00
|
|
|
if (baseBlockIndex->nHeight > blockIndex->nHeight) {
|
2018-04-09 14:49:34 +02:00
|
|
|
errorRet = strprintf("base block %s is higher then block %s", baseBlockHash.ToString(), blockHash.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOCK(deterministicMNManager->cs);
|
|
|
|
|
2019-07-09 07:59:57 +02:00
|
|
|
auto baseDmnList = deterministicMNManager->GetListForBlock(baseBlockIndex);
|
|
|
|
auto dmnList = deterministicMNManager->GetListForBlock(blockIndex);
|
2022-06-24 20:35:23 +02:00
|
|
|
mnListDiffRet = baseDmnList.BuildSimplifiedDiff(dmnList, extended);
|
2018-04-09 14:49:34 +02:00
|
|
|
|
2019-07-09 07:59:57 +02:00
|
|
|
// We need to return the value that was provided by the other peer as it otherwise won't be able to recognize the
|
|
|
|
// response. This will usually be identical to the block found in baseBlockIndex. The only difference is when a
|
|
|
|
// null block hash was provided to get the diff from the genesis block.
|
|
|
|
mnListDiffRet.baseBlockHash = baseBlockHash;
|
|
|
|
|
2019-04-04 10:27:17 +02:00
|
|
|
if (!mnListDiffRet.BuildQuorumsDiff(baseBlockIndex, blockIndex)) {
|
|
|
|
errorRet = strprintf("failed to build quorums diff");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-09 14:49:34 +02:00
|
|
|
// TODO store coinbase TX in CBlockIndex
|
|
|
|
CBlock block;
|
2019-02-12 20:52:06 +01:00
|
|
|
if (!ReadBlockFromDisk(block, blockIndex, Params().GetConsensus())) {
|
2018-04-09 14:49:34 +02:00
|
|
|
errorRet = strprintf("failed to read block %s from disk", blockHash.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mnListDiffRet.cbTx = block.vtx[0];
|
|
|
|
|
|
|
|
std::vector<uint256> vHashes;
|
|
|
|
std::vector<bool> vMatch(block.vtx.size(), false);
|
|
|
|
for (const auto& tx : block.vtx) {
|
|
|
|
vHashes.emplace_back(tx->GetHash());
|
|
|
|
}
|
|
|
|
vMatch[0] = true; // only coinbase matches
|
|
|
|
mnListDiffRet.cbTxMerkleTree = CPartialMerkleTree(vHashes, vMatch);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|