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),
|
2023-02-14 19:48:33 +01:00
|
|
|
scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout),
|
|
|
|
nType(dmn.nType),
|
|
|
|
platformHTTPPort(dmn.pdmnState->platformHTTPPort),
|
|
|
|
platformNodeID(dmn.pdmnState->platformNodeID)
|
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);
|
|
|
|
}
|
|
|
|
|
2023-02-14 19:48:33 +01:00
|
|
|
return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d, platformNodeID=%s)",
|
2023-02-16 13:05:01 +01:00
|
|
|
static_cast<int>(nType), proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort, platformNodeID.ToString());
|
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());
|
2019-05-09 18:04:52 +02:00
|
|
|
obj.pushKV("votingAddress", EncodeDestination(PKHash(keyIDVoting)));
|
2020-06-18 11:17:23 +02:00
|
|
|
obj.pushKV("isValid", isValid);
|
2022-12-30 06:45:31 +01:00
|
|
|
obj.pushKV("nVersion", nVersion);
|
2023-02-16 13:05:01 +01:00
|
|
|
obj.pushKV("nType", static_cast<uint16_t>(nType));
|
|
|
|
if (nType == MnType::HighPerformance) {
|
2023-02-14 19:48:33 +01:00
|
|
|
obj.pushKV("platformHTTPPort", platformHTTPPort);
|
|
|
|
obj.pushKV("platformNodeID", platformNodeID.ToString());
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2023-02-14 19:48:33 +01:00
|
|
|
// TODO: Invistigate if we can delete this constructor
|
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
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-12 23:51:14 +01:00
|
|
|
CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList, bool isV19Active)
|
2018-04-09 10:35:43 +02:00
|
|
|
{
|
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;
|
2023-01-12 23:51:14 +01:00
|
|
|
dmnList.ForEachMN(false, [this, &i, isV19Active](auto& dmn) {
|
|
|
|
auto sme = std::make_unique<CSimplifiedMNListEntry>(dmn);
|
|
|
|
sme->nVersion = isV19Active ? CSimplifiedMNListEntry::BASIC_BLS_VERSION : CSimplifiedMNListEntry::LEGACY_BLS_VERSION;
|
|
|
|
mnList[i++] = std::move(sme);
|
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
|
|
|
|
2022-08-06 10:45:02 +02:00
|
|
|
bool CSimplifiedMNList::operator==(const CSimplifiedMNList& rhs) const
|
|
|
|
{
|
|
|
|
return mnList.size() == rhs.mnList.size() &&
|
|
|
|
std::equal(mnList.begin(), mnList.end(), rhs.mnList.begin(),
|
|
|
|
[](const std::unique_ptr<CSimplifiedMNListEntry>& left, const std::unique_ptr<CSimplifiedMNListEntry>& right)
|
|
|
|
{
|
|
|
|
return *left == *right;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-09-22 13:14:48 +02:00
|
|
|
bool CSimplifiedMNListDiff::BuildQuorumsDiff(const CBlockIndex* baseBlockIndex, const CBlockIndex* blockIndex,
|
|
|
|
const llmq::CQuorumBlockProcessor& quorum_block_processor)
|
2019-04-04 10:27:17 +02:00
|
|
|
{
|
2022-09-22 13:14:48 +02:00
|
|
|
auto baseQuorums = quorum_block_processor.GetMinedAndActiveCommitmentsUntilBlock(baseBlockIndex);
|
|
|
|
auto quorums = quorum_block_processor.GetMinedAndActiveCommitmentsUntilBlock(blockIndex);
|
2019-04-04 10:27:17 +02:00
|
|
|
|
|
|
|
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;
|
2022-09-22 13:14:48 +02:00
|
|
|
llmq::CFinalCommitmentPtr qc = quorum_block_processor.GetMinedCommitment(p.first, p.second, minedBlockHash);
|
2021-04-16 05:41:16 +02:00
|
|
|
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);
|
2022-12-30 06:45:31 +01:00
|
|
|
obj.pushKV("nVersion", nVersion);
|
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-09-22 13:14:48 +02:00
|
|
|
bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet,
|
|
|
|
const llmq::CQuorumBlockProcessor& quorum_block_processor, 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;
|
|
|
|
|
2022-09-22 13:14:48 +02:00
|
|
|
if (!mnListDiffRet.BuildQuorumsDiff(baseBlockIndex, blockIndex, quorum_block_processor)) {
|
2019-04-04 10:27:17 +02:00
|
|
|
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;
|
|
|
|
}
|