fix: Resolve mainnet v19 fork issues (#5403)

same as  #5392, alternative solution

~based on #5402 atm, will rebase later~

pls see individual commits

reorg mainnet around forkpoint with a patched client (to allow low
difficulty), run tests

Another evodb migration is required. Going back to an older version or
migrating after the fork requires reindexing.

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation
- [x] I have assigned this pull request to a milestone _(for repository
code-owners and collaborators only)_
This commit is contained in:
UdjinM6 2023-06-04 23:45:56 +03:00 committed by pasta
parent 35ccc8500c
commit e0175b28d7
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
29 changed files with 519 additions and 148 deletions

View File

@ -418,10 +418,7 @@ public:
CBLSLazyWrapper() :
vecBytes(BLSObject::SerSize, 0),
bufLegacyScheme(bls::bls_legacy_scheme.load())
{
// the all-zero buf is considered a valid buf, but the resulting object will return false for IsValid
bufValid = true;
}
{}
explicit CBLSLazyWrapper(const CBLSLazyWrapper& r)
{
@ -459,12 +456,8 @@ public:
{
std::unique_lock<std::mutex> l(mutex);
if (!objInitialized && !bufValid) {
// the all-zero buf is considered a valid buf
std::fill(vecBytes.begin(), vecBytes.end(), 0);
bufLegacyScheme = specificLegacyScheme;
bufValid = true;
}
if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) {
} else if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) {
vecBytes = obj.ToByteVector(specificLegacyScheme);
bufValid = true;
bufLegacyScheme = specificLegacyScheme;
@ -476,7 +469,7 @@ public:
template<typename Stream>
inline void Serialize(Stream& s) const
{
Serialize(s, bls::bls_legacy_scheme.load());
Serialize(s, bufLegacyScheme);
}
template<typename Stream>
@ -493,13 +486,14 @@ public:
template<typename Stream>
inline void Unserialize(Stream& s) const
{
Unserialize(s, bls::bls_legacy_scheme.load());
Unserialize(s, bufLegacyScheme);
}
void Set(const BLSObject& _obj)
void Set(const BLSObject& _obj, const bool specificLegacyScheme)
{
std::unique_lock<std::mutex> l(mutex);
bufValid = false;
bufLegacyScheme = specificLegacyScheme;
objInitialized = true;
obj = _obj;
hash.SetNull();
@ -549,13 +543,15 @@ public:
return !(*this == r);
}
uint256 GetHash(const bool specificLegacyScheme = bls::bls_legacy_scheme.load()) const
uint256 GetHash() const
{
std::unique_lock<std::mutex> l(mutex);
if (!bufValid || bufLegacyScheme != specificLegacyScheme) {
vecBytes = obj.ToByteVector(specificLegacyScheme);
if (!objInitialized && !bufValid) {
std::fill(vecBytes.begin(), vecBytes.end(), 0);
hash.SetNull();
} else if (!bufValid) {
vecBytes = obj.ToByteVector(bufLegacyScheme);
bufValid = true;
bufLegacyScheme = specificLegacyScheme;
hash.SetNull();
}
if (hash.IsNull()) {
@ -565,6 +561,11 @@ public:
}
return hash;
}
std::string ToString() const
{
return Get().ToString(bufLegacyScheme);
}
};
using CBLSLazySignature = CBLSLazyWrapper<CBLSSignature>;
using CBLSLazyPublicKey = CBLSLazyWrapper<CBLSPublicKey>;

View File

@ -25,8 +25,8 @@
#include <memory>
static const std::string DB_LIST_SNAPSHOT = "dmn_S2";
static const std::string DB_LIST_DIFF = "dmn_D2";
static const std::string DB_LIST_SNAPSHOT = "dmn_S3";
static const std::string DB_LIST_DIFF = "dmn_D3";
std::unique_ptr<CDeterministicMNManager> deterministicMNManager;
@ -463,48 +463,6 @@ CDeterministicMNList CDeterministicMNList::ApplyDiff(const CBlockIndex* pindex,
return result;
}
// RepopulateUniquePropertyMap clears internal mnUniquePropertyMap, and repopulate it with currently MNs unique properties.
// This is needed when the v19 fork activates, we need to store again pubKeyOperator in the mnUniquePropertyMap.
// pubKeyOperator don't differ between the two schemes (legacy and basic(v19)) but their serialisation do: hence their hash.
// And because mnUniquePropertyMap store only hashes, then we need to re-calculate hashes and repopulate.
void CDeterministicMNList::RepopulateUniquePropertyMap() {
decltype(mnUniquePropertyMap) mnUniquePropertyMapEmpty;
mnUniquePropertyMap = mnUniquePropertyMapEmpty;
for (const auto &p: mnMap) {
auto dmn = p.second;
if (!AddUniqueProperty(*dmn, dmn->collateralOutpoint)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate collateralOutpoint=%s", __func__,
dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
}
if (dmn->pdmnState->addr != CService() && !AddUniqueProperty(*dmn, dmn->pdmnState->addr)) {
throw (std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
dmn->proTxHash.ToString(),
dmn->pdmnState->addr.ToStringIPPort(false))));
}
if (!AddUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate keyIDOwner=%s", __func__,
dmn->proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() &&
!AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString())));
}
}
}
}
void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
{
assert(dmn != nullptr);
@ -538,7 +496,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
@ -580,7 +538,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s
if (!UpdateUniqueProperty(*dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__,
oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString())));
oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) {
@ -639,7 +597,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a pubKeyOperator=%s", __func__,
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
@ -687,17 +645,14 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde
newList.SetBlockHash(block.GetHash());
// If the fork is active for pindex block, then we need to repopulate property map
// (Check documentation of CDeterministicMNList::RepopulateUniquePropertyMap()).
// This is needed only when base list is pre-v19 fork and pindex is post-v19 fork.
bool v19_just_activated = pindex == llmq::utils::V19ActivationIndex(pindex);
if (v19_just_activated) {
newList.RepopulateUniquePropertyMap();
}
oldList = GetListForBlock(pindex->pprev);
diff = oldList.BuildDiff(newList);
// NOTE: The block next to the activation is the one that is using new rules.
// If the fork was activated at pindex->prev block then current one is the first one
// using new rules. Save mn list snapsot for this block.
bool v19_just_activated = pindex->pprev == llmq::utils::V19ActivationIndex(pindex);
m_evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff);
if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1 || v19_just_activated) {
m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList);
@ -940,12 +895,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (newState->pubKeyOperator != proTx.pubKeyOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
}
newState->pubKeyOperator.Set(proTx.pubKeyOperator);
newState->pubKeyOperator = proTx.pubKeyOperator;
newState->keyIDVoting = proTx.keyIDVoting;
newState->scriptPayout = proTx.scriptPayout;
@ -1266,6 +1221,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S";
static const std::string DB_OLD_LIST_DIFF = "dmn_D";
static const std::string DB_OLD_BEST_BLOCK = "b_b2";
static const std::string DB_OLD_BEST_BLOCK2 = "b_b3";
LOCK(cs_main);
@ -1277,11 +1233,17 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
return m_evoDb.IsEmpty();
}
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) {
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK) || m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK2)) {
LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__);
return true;
}
if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) {
// too late
LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__);
return false;
}
// Removing the old EVODB_BEST_BLOCK value early results in older version to crash immediately, even if the upgrade
// process is cancelled in-between. But if the new version sees that the old EVODB_BEST_BLOCK is already removed,
// then we must assume that the upgrade process was already running before but was interrupted.
@ -1304,15 +1266,15 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) {
auto pindex = ::ChainActive()[nHeight];
// Unserialise CDeterministicMNListDiff using CURRENT_MN_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_TYPE_FORMAT which includes the type field.
// Unserialise CDeterministicMNListDiff using MN_OLD_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_CURRENT_FORMAT which includes the type field and MN state bls version.
CDataStream diff_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) {
LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight);
return false;
}
CDeterministicMNListDiff mndiff;
mndiff.Unserialize(diff_data, CDeterministicMN::CURRENT_MN_FORMAT);
mndiff.Unserialize(diff_data, CDeterministicMN::MN_OLD_FORMAT);
batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff);
CDataStream snapshot_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) {
@ -1320,7 +1282,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
continue;
}
CDeterministicMNList mnList;
mnList.Unserialize(snapshot_data, CDeterministicMN::CURRENT_MN_FORMAT);
mnList.Unserialize(snapshot_data, CDeterministicMN::MN_OLD_FORMAT);
batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList);
m_evoDb.GetRawDB().WriteBatch(batch);
batch.Clear();
@ -1329,7 +1291,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
m_evoDb.GetRawDB().WriteBatch(batch);
// Writing EVODB_BEST_BLOCK (which is b_b3 now) marks the DB as upgraded
// Writing EVODB_BEST_BLOCK (which is b_b4 now) marks the DB as upgraded
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
@ -1345,6 +1307,111 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__);
// flush it to disk
if (!m_evoDb.CommitRootTransaction()) {
LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__);
return false;
}
return true;
}
bool CDeterministicMNManager::MigrateDBIfNeeded2()
{
static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S2";
static const std::string DB_OLD_LIST_DIFF = "dmn_D2";
static const std::string DB_OLD_BEST_BLOCK = "b_b3";
LOCK(cs_main);
LogPrintf("CDeterministicMNManager::%s -- upgrading DB to migrate MN state bls version\n", __func__);
if (::ChainActive().Tip() == nullptr) {
// should have no records
LogPrintf("CDeterministicMNManager::%s -- Chain empty. evoDB:%d.\n", __func__, m_evoDb.IsEmpty());
return m_evoDb.IsEmpty();
}
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) {
LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__);
return true;
}
if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) {
// too late
LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__);
return false;
}
// Removing the old EVODB_BEST_BLOCK value early results in older version to crash immediately, even if the upgrade
// process is cancelled in-between. But if the new version sees that the old EVODB_BEST_BLOCK is already removed,
// then we must assume that the upgrade process was already running before but was interrupted.
if (::ChainActive().Height() > 1 && !m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK)) {
LogPrintf("CDeterministicMNManager::%s -- previous migration attempt failed.\n", __func__);
return false;
}
m_evoDb.GetRawDB().Erase(DB_OLD_BEST_BLOCK);
if (::ChainActive().Height() < Params().GetConsensus().DIP0003Height) {
// not reached DIP3 height yet, so no upgrade needed
LogPrintf("CDeterministicMNManager::%s -- migration not needed. dip3 not reached\n", __func__);
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
return true;
}
CDBBatch batch(m_evoDb.GetRawDB());
for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) {
auto pindex = ::ChainActive()[nHeight];
// Unserialise CDeterministicMNListDiff using MN_TYPE_FORMAT and set MN state bls version to LEGACY_BLS_VERSION.
// It will be later written with format MN_CURRENT_FORMAT which includes the type field.
CDataStream diff_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) {
LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight);
return false;
}
CDeterministicMNListDiff mndiff;
mndiff.Unserialize(diff_data, CDeterministicMN::MN_TYPE_FORMAT);
batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff);
CDataStream snapshot_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) {
// it's ok, we write snapshots every DISK_SNAPSHOT_PERIOD blocks only
continue;
}
CDeterministicMNList mnList;
mnList.Unserialize(snapshot_data, CDeterministicMN::MN_TYPE_FORMAT);
batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList);
m_evoDb.GetRawDB().WriteBatch(batch);
batch.Clear();
LogPrintf("CDeterministicMNManager::%s -- wrote snapshot at height %d\n", __func__, nHeight);
}
m_evoDb.GetRawDB().WriteBatch(batch);
// Writing EVODB_BEST_BLOCK (which is b_b4 now) marks the DB as upgraded
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
LogPrintf("CDeterministicMNManager::%s -- done migrating\n", __func__);
m_evoDb.GetRawDB().Erase(DB_OLD_LIST_DIFF);
m_evoDb.GetRawDB().Erase(DB_OLD_LIST_SNAPSHOT);
LogPrintf("CDeterministicMNManager::%s -- done cleaning old data\n", __func__);
m_evoDb.GetRawDB().CompactFull();
LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__);
// flush it to disk
if (!m_evoDb.CommitRootTransaction()) {
LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__);
return false;
}
return true;
}

View File

@ -43,8 +43,10 @@ private:
uint64_t internalId{std::numeric_limits<uint64_t>::max()};
public:
static constexpr uint16_t CURRENT_MN_FORMAT = 0;
static constexpr uint16_t MN_OLD_FORMAT = 0;
static constexpr uint16_t MN_TYPE_FORMAT = 1;
static constexpr uint16_t MN_VERSION_FORMAT = 2;
static constexpr uint16_t MN_CURRENT_FORMAT = MN_VERSION_FORMAT;
CDeterministicMN() = delete; // no default constructor, must specify internalId
explicit CDeterministicMN(uint64_t _internalId, MnType mnType = MnType::Regular) :
@ -74,12 +76,16 @@ public:
READWRITE(VARINT(internalId));
READWRITE(collateralOutpoint);
READWRITE(nOperatorReward);
// We need to read CDeterministicMNState using the old format only when called with CURRENT_MN_FORMAT on Unserialize()
// We need to read CDeterministicMNState using the old format only when called with MN_OLD_FORMAT or MN_TYPE_FORMAT on Unserialize()
// Serialisation (writing) will be done always using new format
if (ser_action.ForRead() && format_version == CURRENT_MN_FORMAT) {
if (ser_action.ForRead() && format_version == MN_OLD_FORMAT) {
CDeterministicMNState_Oldformat old_state;
READWRITE(old_state);
pdmnState = std::make_shared<const CDeterministicMNState>(old_state);
} else if (ser_action.ForRead() && format_version == MN_TYPE_FORMAT) {
CDeterministicMNState_mntype_format old_state;
READWRITE(old_state);
pdmnState = std::make_shared<const CDeterministicMNState>(old_state);
} else {
READWRITE(pdmnState);
}
@ -98,11 +104,11 @@ public:
template<typename Stream>
void Serialize(Stream& s) const
{
const_cast<CDeterministicMN*>(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT);
const_cast<CDeterministicMN*>(this)->SerializationOp(s, CSerActionSerialize(), MN_CURRENT_FORMAT);
}
template <typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = MN_TYPE_FORMAT)
void Unserialize(Stream& s, const uint8_t format_version = MN_CURRENT_FORMAT)
{
SerializationOp(s, CSerActionUnserialize(), format_version);
}
@ -204,14 +210,14 @@ public:
}
template<typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) {
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) {
mnMap = MnMap();
mnUniquePropertyMap = MnUniquePropertyMap();
mnInternalIdMap = MnInternalIdMap();
SerializationOpBase(s, CSerActionUnserialize());
bool evodb_migration = (format_version == CDeterministicMN::CURRENT_MN_FORMAT);
bool evodb_migration = (format_version == CDeterministicMN::MN_OLD_FORMAT || format_version == CDeterministicMN::MN_TYPE_FORMAT);
size_t cnt = ReadCompactSize(s);
for (size_t i = 0; i < cnt; i++) {
if (evodb_migration) {
@ -385,8 +391,6 @@ public:
[[nodiscard]] CSimplifiedMNListDiff BuildSimplifiedDiff(const CDeterministicMNList& to, bool extended) const;
[[nodiscard]] CDeterministicMNList ApplyDiff(const CBlockIndex* pindex, const CDeterministicMNListDiff& diff) const;
void RepopulateUniquePropertyMap();
void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true);
void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr<const CDeterministicMNState>& pdmnState);
void UpdateMN(const uint256& proTxHash, const std::shared_ptr<const CDeterministicMNState>& pdmnState);
@ -396,12 +400,12 @@ public:
template <typename T>
[[nodiscard]] bool HasUniqueProperty(const T& v) const
{
return mnUniquePropertyMap.count(::SerializeHash(v)) != 0;
return mnUniquePropertyMap.count(GetUniquePropertyHash(v)) != 0;
}
template <typename T>
[[nodiscard]] CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const
{
auto p = mnUniquePropertyMap.find(::SerializeHash(v));
auto p = mnUniquePropertyMap.find(GetUniquePropertyHash(v));
if (!p) {
return nullptr;
}
@ -409,6 +413,14 @@ public:
}
private:
template <typename T>
[[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const
{
if constexpr (std::is_same<T, CBLSPublicKey>()) {
assert(false);
}
return ::SerializeHash(v);
}
template <typename T>
[[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v)
{
@ -417,7 +429,7 @@ private:
return false;
}
auto hash = ::SerializeHash(v);
auto hash = GetUniquePropertyHash(v);
auto oldEntry = mnUniquePropertyMap.find(hash);
if (oldEntry != nullptr && oldEntry->first != dmn.proTxHash) {
return false;
@ -437,7 +449,7 @@ private:
return false;
}
auto oldHash = ::SerializeHash(oldValue);
auto oldHash = GetUniquePropertyHash(oldValue);
auto p = mnUniquePropertyMap.find(oldHash);
if (p == nullptr || p->first != dmn.proTxHash) {
return false;
@ -466,6 +478,16 @@ private:
}
return true;
}
friend bool operator==(const CDeterministicMNList& a, const CDeterministicMNList& b)
{
return a.blockHash == b.blockHash &&
a.nHeight == b.nHeight &&
a.nTotalRegisteredCount == b.nTotalRegisteredCount &&
a.mnMap == b.mnMap &&
a.mnInternalIdMap == b.mnInternalIdMap &&
a.mnUniquePropertyMap == b.mnUniquePropertyMap;
}
};
class CDeterministicMNListDiff
@ -494,7 +516,7 @@ public:
}
template <typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT)
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT)
{
updatedMNs.clear();
removedMns.clear();
@ -504,8 +526,6 @@ public:
tmp = ReadCompactSize(s);
for (size_t i = 0; i < tmp; i++) {
CDeterministicMN mn(0);
// Unserialise CDeterministicMN using CURRENT_MN_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_TYPE_FORMAT which includes the type field.
mn.Unserialize(s, format_version);
auto dmn = std::make_shared<CDeterministicMN>(mn);
addedMNs.push_back(dmn);
@ -594,6 +614,7 @@ public:
bool IsDIP3Enforced(int nHeight = -1);
bool MigrateDBIfNeeded();
bool MigrateDBIfNeeded2();
void DoMaintenance();

View File

@ -29,7 +29,7 @@ std::string CDeterministicMNState::ToString() const
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)",
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress);
EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress);
}
void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const
@ -56,7 +56,7 @@ void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.Get().ToString());
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
if (ExtractDestination(scriptOperatorPayout, dest)) {
obj.pushKV("operatorPayoutAddress", EncodeDestination(dest));
}

View File

@ -72,6 +72,62 @@ public:
}
};
// TODO: To remove this in the future
class CDeterministicMNState_mntype_format
{
private:
int nPoSeBanHeight{-1};
friend class CDeterministicMNStateDiff;
friend class CDeterministicMNState;
public:
int nRegisteredHeight{-1};
int nLastPaidHeight{0};
int nConsecutivePayments{0};
int nPoSePenalty{0};
int nPoSeRevivedHeight{-1};
uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
uint256 confirmedHash;
uint256 confirmedHashWithProRegTxHash;
CKeyID keyIDOwner;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
CService addr;
CScript scriptPayout;
CScript scriptOperatorPayout;
uint160 platformNodeID{};
uint16_t platformP2PPort{0};
uint16_t platformHTTPPort{0};
public:
CDeterministicMNState_mntype_format() = default;
SERIALIZE_METHODS(CDeterministicMNState_mntype_format, obj)
{
READWRITE(
obj.nRegisteredHeight,
obj.nLastPaidHeight,
obj.nConsecutivePayments,
obj.nPoSePenalty,
obj.nPoSeRevivedHeight,
obj.nPoSeBanHeight,
obj.nRevocationReason,
obj.confirmedHash,
obj.confirmedHashWithProRegTxHash,
obj.keyIDOwner,
obj.pubKeyOperator,
obj.keyIDVoting,
obj.addr,
obj.scriptPayout,
obj.scriptOperatorPayout,
obj.platformNodeID,
obj.platformP2PPort,
obj.platformHTTPPort);
}
};
class CDeterministicMNState
{
private:
@ -80,6 +136,8 @@ private:
friend class CDeterministicMNStateDiff;
public:
int nVersion{CProRegTx::LEGACY_BLS_VERSION};
int nRegisteredHeight{-1};
int nLastPaidHeight{0};
int nConsecutivePayments{0};
@ -107,7 +165,9 @@ public:
public:
CDeterministicMNState() = default;
explicit CDeterministicMNState(const CProRegTx& proTx) :
nVersion(proTx.nVersion),
keyIDOwner(proTx.keyIDOwner),
pubKeyOperator(proTx.pubKeyOperator),
keyIDVoting(proTx.keyIDVoting),
addr(proTx.addr),
scriptPayout(proTx.scriptPayout),
@ -115,7 +175,6 @@ public:
platformP2PPort(proTx.platformP2PPort),
platformHTTPPort(proTx.platformHTTPPort)
{
pubKeyOperator.Set(proTx.pubKeyOperator);
}
explicit CDeterministicMNState(const CDeterministicMNState_Oldformat& s) :
nPoSeBanHeight(s.nPoSeBanHeight),
@ -132,6 +191,27 @@ public:
addr(s.addr),
scriptPayout(s.scriptPayout),
scriptOperatorPayout(s.scriptOperatorPayout) {}
explicit CDeterministicMNState(const CDeterministicMNState_mntype_format& s) :
nPoSeBanHeight(s.nPoSeBanHeight),
nRegisteredHeight(s.nRegisteredHeight),
nLastPaidHeight(s.nLastPaidHeight),
nConsecutivePayments(s.nConsecutivePayments),
nPoSePenalty(s.nPoSePenalty),
nPoSeRevivedHeight(s.nPoSeRevivedHeight),
nRevocationReason(s.nRevocationReason),
confirmedHash(s.confirmedHash),
confirmedHashWithProRegTxHash(s.confirmedHashWithProRegTxHash),
keyIDOwner(s.keyIDOwner),
pubKeyOperator(s.pubKeyOperator),
keyIDVoting(s.keyIDVoting),
addr(s.addr),
scriptPayout(s.scriptPayout),
scriptOperatorPayout(s.scriptOperatorPayout),
platformNodeID(s.platformNodeID),
platformP2PPort(s.platformP2PPort),
platformHTTPPort(s.platformHTTPPort) {}
template <typename Stream>
CDeterministicMNState(deserialize_type, Stream& s)
{
@ -141,6 +221,7 @@ public:
SERIALIZE_METHODS(CDeterministicMNState, obj)
{
READWRITE(
obj.nVersion,
obj.nRegisteredHeight,
obj.nLastPaidHeight,
obj.nConsecutivePayments,
@ -151,7 +232,9 @@ public:
obj.confirmedHash,
obj.confirmedHashWithProRegTxHash,
obj.keyIDOwner,
obj.pubKeyOperator,
obj.keyIDOwner);
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), obj.nVersion == CProRegTx::LEGACY_BLS_VERSION));
READWRITE(
obj.keyIDVoting,
obj.addr,
obj.scriptPayout,
@ -163,7 +246,7 @@ public:
void ResetOperatorFields()
{
pubKeyOperator.Set(CBLSPublicKey());
pubKeyOperator.Set(CBLSPublicKey(), bls::bls_legacy_scheme.load());
addr = CService();
scriptOperatorPayout = CScript();
nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
@ -225,6 +308,7 @@ public:
Field_platformNodeID = 0x8000,
Field_platformP2PPort = 0x10000,
Field_platformHTTPPort = 0x20000,
Field_nVersion = 0x40000,
};
#define DMN_STATE_DIFF_ALL_FIELDS \
@ -245,7 +329,8 @@ public:
DMN_STATE_DIFF_LINE(nConsecutivePayments) \
DMN_STATE_DIFF_LINE(platformNodeID) \
DMN_STATE_DIFF_LINE(platformP2PPort) \
DMN_STATE_DIFF_LINE(platformHTTPPort)
DMN_STATE_DIFF_LINE(platformHTTPPort) \
DMN_STATE_DIFF_LINE(nVersion)
public:
uint32_t fields{0};
@ -266,8 +351,7 @@ public:
READWRITE(VARINT(obj.fields));
#define DMN_STATE_DIFF_LINE(f) \
if (strcmp(#f, "pubKeyOperator") == 0 && (obj.fields & Field_pubKeyOperator)) {\
/* TODO: implement migration to Basic BLS after the fork */ \
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), true)); \
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), obj.state.nVersion == CProRegTx::LEGACY_BLS_VERSION)); \
} else if (obj.fields & Field_##f) READWRITE(obj.state.f);
DMN_STATE_DIFF_ALL_FIELDS

View File

@ -12,7 +12,8 @@
// "b_b" was used in the initial version of deterministic MN storage
// "b_b2" was used after compact diffs were introduced
// "b_b3" was used after masternode type introduction in evoDB
static const std::string EVODB_BEST_BLOCK = "b_b3";
// "b_b4" was used after storing protx version for each masternode in evoDB
static const std::string EVODB_BEST_BLOCK = "b_b4";
class CEvoDB;

View File

@ -23,7 +23,7 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
}
if (keyIDOwner.IsNull() || !pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
}
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
@ -81,7 +81,7 @@ std::string CProRegTx::ToString() const
}
return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)",
nVersion, ToUnderlying(nType), collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort);
nVersion, ToUnderlying(nType), collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort);
}
maybe_error CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
@ -114,7 +114,7 @@ maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
}
if (!pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
}
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
@ -132,7 +132,7 @@ std::string CProUpRegTx::ToString() const
}
return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
nVersion, proTxHash.ToString(), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee);
nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee);
}
maybe_error CProUpRevTx::IsTriviallyValid(bool is_bls_legacy_scheme) const

View File

@ -43,7 +43,7 @@ public:
uint16_t platformP2PPort{0};
uint16_t platformHTTPPort{0};
CKeyID keyIDOwner;
CBLSPublicKey pubKeyOperator;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
uint16_t nOperatorReward{0};
CScript scriptPayout;
@ -65,7 +65,7 @@ public:
obj.collateralOutpoint,
obj.addr,
obj.keyIDOwner,
CBLSPublicKeyVersionWrapper(const_cast<CBLSPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
obj.keyIDVoting,
obj.nOperatorReward,
obj.scriptPayout,
@ -104,7 +104,7 @@ public:
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION));
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
obj.pushKV("operatorReward", (double)nOperatorReward / 100);
if (nType == MnType::HighPerformance) {
obj.pushKV("platformNodeID", platformNodeID.ToString());
@ -212,7 +212,7 @@ public:
uint16_t nVersion{LEGACY_BLS_VERSION}; // message version
uint256 proTxHash;
uint16_t nMode{0}; // only 0 supported for now
CBLSPublicKey pubKeyOperator;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
CScript scriptPayout;
uint256 inputsHash; // replay protection
@ -230,7 +230,7 @@ public:
READWRITE(
obj.proTxHash,
obj.nMode,
CBLSPublicKeyVersionWrapper(const_cast<CBLSPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
obj.keyIDVoting,
obj.scriptPayout,
obj.inputsHash
@ -255,7 +255,7 @@ public:
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION));
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
obj.pushKV("inputsHash", inputsHash.ToString());
}

View File

@ -13,6 +13,7 @@
#include <hash.h>
#include <llmq/blockprocessor.h>
#include <llmq/commitment.h>
#include <llmq/utils.h>
#include <primitives/block.h>
#include <validation.h>
@ -152,6 +153,13 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll
int64_t nTime5 = GetTimeMicros();
nTimeMerkle += nTime5 - nTime4;
LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);
if (llmq::utils::V19ActivationIndex(pindex) == pindex) {
// NOTE: The block next to the activation is the one that is using new rules.
// V19 activated just activated, so we must switch to the new rules here.
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
} catch (const std::exception& e) {
LogPrintf("%s -- failed: %s\n", __func__, e.what());
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-procspectxsinblock");
@ -164,7 +172,16 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq:
{
AssertLockHeld(cs_main);
auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
try {
if (llmq::utils::V19ActivationIndex(pindex) == pindex) {
// NOTE: The block next to the activation is the one that is using new rules.
// Removing the activation block here, so we must switch back to the old rules.
bls::bls_legacy_scheme.store(true);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
for (int i = (int)block.vtx.size() - 1; i >= 0; --i) {
const CTransaction& tx = *block.vtx[i];
if (!UndoSpecialTx(tx, pindex)) {
@ -180,6 +197,8 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq:
return false;
}
} catch (const std::exception& e) {
bls::bls_legacy_scheme.store(bls_legacy_scheme);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
return error(strprintf("%s -- failed: %s\n", __func__, e.what()).c_str());
}

View File

@ -501,7 +501,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
// Check that we have a valid MN signature
if (!CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) {
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.Get().ToString();
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.ToString();
return false;
}

View File

@ -1702,7 +1702,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
activeMasternodeInfo.blsKeyOperator = std::make_unique<CBLSSecretKey>(keyOperator);
activeMasternodeInfo.blsPubKeyOperator = std::make_unique<CBLSPublicKey>(keyOperator.GetPublicKey());
}
LogPrintf("MASTERNODE:\n blsPubKeyOperator: %s\n", activeMasternodeInfo.blsPubKeyOperator->ToString());
// We don't know the actual scheme at this point, print both
LogPrintf("MASTERNODE:\n blsPubKeyOperator legacy: %s\n blsPubKeyOperator basic: %s\n",
activeMasternodeInfo.blsPubKeyOperator->ToString(true),
activeMasternodeInfo.blsPubKeyOperator->ToString(false));
} else {
LOCK(activeMasternodeInfoCs);
activeMasternodeInfo.blsKeyOperator = std::make_unique<CBLSSecretKey>();
@ -2168,6 +2171,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
strLoadError = _("Error upgrading evo database");
break;
}
if (!deterministicMNManager->MigrateDBIfNeeded2()) {
strLoadError = _("Error upgrading evo database");
break;
}
if (!llmq::quorumBlockProcessor->UpgradeDB()) {
strLoadError = _("Error upgrading evo database");
@ -2192,8 +2199,11 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
break;
}
if (llmq::utils::IsV19Active(tip))
bool v19active = llmq::utils::IsV19Active(tip);
if (llmq::utils::IsV19Active(tip)) {
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
// Only verify the DB of the active chainstate. This is fixed in later
// work when we allow VerifyDB to be parameterized by chainstate.
@ -2207,6 +2217,14 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
failed_verification = true;
break;
}
// VerifyDB() disconnects blocks which might result in us switching back to legacy.
// Make sure we use the right scheme.
if (v19active && bls::bls_legacy_scheme.load()) {
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
} else {
// TODO: CEvoDB instance should probably be a part of CChainState
// (for multiple chainstates to actually work in parallel)

View File

@ -146,9 +146,6 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex*
return true;
}
if (utils::IsV19Active(pindex->pprev))
bls::bls_legacy_scheme.store(false);
llmq::utils::PreComputeQuorumMembers(pindex);
std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
@ -307,9 +304,6 @@ bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pi
{
AssertLockHeld(cs_main);
if (!utils::IsV19Active(pindex->pprev))
bls::bls_legacy_scheme.store(true);
llmq::utils::PreComputeQuorumMembers(pindex, true);
std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;

View File

@ -74,7 +74,7 @@ public:
CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSLazySignature& _sig) :
CSigBase(_llmqType, _quorumHash, _id, _msgHash), sig(_sig) {UpdateHash();};
CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSSignature& _sig) :
CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast<CBLSLazySignature&>(sig).Set(_sig); UpdateHash();};
CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast<CBLSLazySignature&>(sig).Set(_sig, bls::bls_legacy_scheme.load()); UpdateHash();};
private:
// only in-memory

View File

@ -1514,7 +1514,7 @@ std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorumCPtr& qu
CSigShare sigShare(quorum->params.type, quorum->qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
uint256 signHash = sigShare.buildSignHash();
sigShare.sigShare.Set(skShare.Sign(signHash));
sigShare.sigShare.Set(skShare.Sign(signHash), bls::bls_legacy_scheme.load());
if (!sigShare.sigShare.Get().IsValid()) {
LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count());

View File

@ -131,6 +131,7 @@ void CActiveMasternodeManager::Init(const CBlockIndex* pindex)
activeMasternodeInfo.proTxHash = dmn->proTxHash;
activeMasternodeInfo.outpoint = dmn->collateralOutpoint;
activeMasternodeInfo.legacy = dmn->pdmnState->nVersion == CProRegTx::LEGACY_BLS_VERSION;
state = MASTERNODE_READY;
}

View File

@ -28,6 +28,7 @@ struct CActiveMasternodeInfo {
uint256 proTxHash;
COutPoint outpoint;
CService service;
bool legacy{true};
};

View File

@ -661,7 +661,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
}
ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address");
CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", specific_legacy_bls_scheme && !isHPMNrequested);
ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION);
CKeyID keyIDVoting = ptx.keyIDOwner;
if (request.params[paramIdx + 3].get_str() != "") {
@ -703,7 +703,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
paramIdx += 3;
}
ptx.pubKeyOperator = pubKeyOperator;
ptx.keyIDVoting = keyIDVoting;
ptx.scriptPayout = GetScriptForDestination(payoutDest);
@ -1041,12 +1040,12 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co
if (!dmn) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString()));
}
ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator.Get();
ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator;
ptx.keyIDVoting = dmn->pdmnState->keyIDVoting;
ptx.scriptPayout = dmn->pdmnState->scriptPayout;
if (request.params[1].get_str() != "") {
ptx.pubKeyOperator = ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", specific_legacy_bls_scheme);
ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", ptx.nVersion == CProUpRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION);
}
if (request.params[2].get_str() != "") {
ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[2].get_str(), "voting address");

View File

@ -323,7 +323,7 @@ static UniValue gobject_submit(const JSONRPCRequest& request)
bool fMnFound = WITH_LOCK(activeMasternodeInfoCs, return mnList.HasValidMNByCollateral(activeMasternodeInfo.outpoint));
LogPrint(BCLog::GOBJECT, "gobject_submit -- pubKeyOperator = %s, outpoint = %s, params.size() = %lld, fMnFound = %d\n",
(WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString() : "N/A")),
(WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString(activeMasternodeInfo.legacy) : "N/A")),
WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint.ToStringShort()), request.params.size(), fMnFound);
// ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS

View File

@ -675,7 +675,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)) << " " <<
EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)) << " " <<
collateralAddressStr << " " <<
dmn.pdmnState->pubKeyOperator.Get().ToString();
dmn.pdmnState->pubKeyOperator.ToString();
std::string strInfo = streamInfo.str();
if (strFilter !="" && strInfo.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
@ -697,7 +697,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
objMN.pushKV("votingaddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)));
objMN.pushKV("collateraladdress", collateralAddressStr);
objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.Get().ToString());
objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.ToString());
obj.pushKV(strOutpoint, objMN);
} else if (strMode == "lastpaidblock") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
@ -714,7 +714,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
obj.pushKV(strOutpoint, EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
} else if (strMode == "pubkeyoperator") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.Get().ToString());
obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.ToString());
} else if (strMode == "status") {
std::string strStatus = dmnToStatus(dmn);
if (strFilter !="" && strStatus.find(strFilter) == std::string::npos &&

View File

@ -210,7 +210,7 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMem
UniValue mo(UniValue::VOBJ);
mo.pushKV("proTxHash", dmn->proTxHash.ToString());
mo.pushKV("service", dmn->pdmnState->addr.ToString());
mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.Get().ToString());
mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.ToString());
mo.pushKV("valid", quorum->qc->validMembers[i]);
if (quorum->qc->validMembers[i]) {
CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);

View File

@ -125,7 +125,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM
proTx.collateralOutpoint.n = 0;
proTx.addr = LookupNumeric("1.1.1.1", port);
proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID();
proTx.pubKeyOperator = operatorKeyRet.GetPublicKey();
proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load());
proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID();
proTx.scriptPayout = scriptPayout;

View File

@ -21,6 +21,7 @@
#include <evo/specialtx.h>
#include <evo/providertx.h>
#include <evo/deterministicmns.h>
#include <llmq/utils.h>
#include <boost/test/unit_test.hpp>
@ -103,7 +104,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM
proTx.collateralOutpoint.n = 0;
proTx.addr = LookupNumeric("1.1.1.1", port);
proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID();
proTx.pubKeyOperator = operatorKeyRet.GetPublicKey();
proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load());
proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID();
proTx.scriptPayout = scriptPayout;
@ -143,7 +144,7 @@ static CMutableTransaction CreateProUpRegTx(const CTxMemPool& mempool, SimpleUTX
CProUpRegTx proTx;
proTx.nVersion = CProUpRegTx::GetVersion(!bls::bls_legacy_scheme);
proTx.proTxHash = proTxHash;
proTx.pubKeyOperator = pubKeyOperator;
proTx.pubKeyOperator.Set(pubKeyOperator, bls::bls_legacy_scheme.load());
proTx.keyIDVoting = keyIDVoting;
proTx.scriptPayout = scriptPayout;
@ -264,6 +265,136 @@ void FuncDIP3Activation(TestChainSetup& setup)
BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
};
void FuncV19Activation(TestChainSetup& setup)
{
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
// create
auto utxos = BuildSimpleUtxoMap(setup.m_coinbase_txns);
CKey owner_key;
CBLSSecretKey operator_key;
CKey collateral_key;
collateral_key.MakeNewKey(false);
auto collateralScript = GetScriptForDestination(PKHash(collateral_key.GetPubKey()));
auto tx_reg = CreateProRegTx(*(setup.m_node.mempool), utxos, 1, collateralScript, setup.coinbaseKey, owner_key, operator_key);
auto tx_reg_hash = tx_reg.GetHash();
int nHeight = ::ChainActive().Height();
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
auto tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(tip_list.HasMN(tx_reg_hash));
auto pindex_create = ::ChainActive().Tip();
auto base_list = deterministicMNManager->GetListForBlock(pindex_create);
std::vector<CDeterministicMNListDiff> diffs;
// update
CBLSSecretKey operator_key_new;
operator_key_new.MakeNewKey();
auto tx_upreg = CreateProUpRegTx(*(setup.m_node.mempool), utxos, tx_reg_hash, owner_key, operator_key_new.GetPublicKey(), owner_key.GetPubKey().GetID(), collateralScript, setup.coinbaseKey);
block = std::make_shared<CBlock>(setup.CreateBlock({tx_upreg}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(tip_list.HasMN(tx_reg_hash));
diffs.push_back(base_list.BuildDiff(tip_list));
// spend
CMutableTransaction tx_spend;
COutPoint collateralOutpoint(tx_reg_hash, 0);
tx_spend.vin.emplace_back(collateralOutpoint);
tx_spend.vout.emplace_back(999.99 * COIN, collateralScript);
FillableSigningProvider signing_provider;
signing_provider.AddKeyPubKey(collateral_key, collateral_key.GetPubKey());
BOOST_ASSERT(SignSignature(signing_provider, CTransaction(tx_reg), tx_spend, 0, SIGHASH_ALL));
block = std::make_shared<CBlock>(setup.CreateBlock({tx_spend}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// mine another block so that it's not the last one before V19
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// this block should activate V19
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// check mn list/diff
CDeterministicMNListDiff dummy_diff = base_list.BuildDiff(tip_list);
CDeterministicMNList dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
// Lists should match
BOOST_ASSERT(dummmy_list == tip_list);
// mine 10 more blocks
for (int i = 0; i < 10; ++i)
{
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip()));
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight + 1 + i);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
}
// check mn list/diff
const CBlockIndex* v19_index = llmq::utils::V19ActivationIndex(::ChainActive().Tip());
auto v19_list = deterministicMNManager->GetListForBlock(v19_index);
dummy_diff = v19_list.BuildDiff(tip_list);
dummmy_list = v19_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
BOOST_ASSERT(dummmy_list == tip_list);
// NOTE: this fails on v19/v19.1 with errors like:
// "RemoveMN: Can't delete a masternode ... with a pubKeyOperator=..."
dummy_diff = base_list.BuildDiff(tip_list);
dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
BOOST_ASSERT(dummmy_list == tip_list);
dummmy_list = base_list;
for (const auto& diff : diffs) {
dummmy_list = dummmy_list.ApplyDiff(::ChainActive().Tip(), diff);
}
BOOST_ASSERT(dummmy_list == tip_list);
};
void FuncDIP3Protx(TestChainSetup& setup)
{
CKey sporkKey;
@ -481,7 +612,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup)
payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme);
payload.addr = LookupNumeric("1.1.1.1", 1);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -550,7 +681,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup)
CProRegTx payload;
payload.addr = LookupNumeric("1.1.1.1", 2);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -612,7 +743,7 @@ void FuncVerifyDB(TestChainSetup& setup)
payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme);
payload.addr = LookupNumeric("1.1.1.1", 1);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -667,6 +798,13 @@ BOOST_AUTO_TEST_CASE(dip3_activation_legacy)
FuncDIP3Activation(setup);
}
// V19 can only be activated with legacy scheme
BOOST_AUTO_TEST_CASE(v19_activation_legacy)
{
TestChainV19BeforeActivationSetup setup;
FuncV19Activation(setup);
}
BOOST_AUTO_TEST_CASE(dip3_protx_legacy)
{
TestChainDIP3Setup setup;

View File

@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(simplifiedmns_merkleroots)
std::vector<unsigned char> vecBytes{static_cast<unsigned char>(i)};
vecBytes.resize(CBLSSecretKey::SerSize);
smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey());
smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey(), bls::bls_legacy_scheme.load());
smle.keyIDVoting.SetHex(strprintf("%040x", i));
smle.isValid = true;

View File

@ -24,6 +24,7 @@
#include <llmq/signing_shares.h>
#include <llmq/signing.h>
#include <llmq/snapshot.h>
#include <llmq/utils.h>
#include <miner.h>
#include <net.h>
#include <net_processing.h>
@ -391,3 +392,9 @@ CBlock getBlock13b8a()
stream >> block;
return block;
}
TestChainV19BeforeActivationSetup::TestChainV19BeforeActivationSetup() : TestChainSetup(894)
{
bool v19_active = llmq::utils::IsV19Active(::ChainActive().Tip());
assert(!v19_active);
}

View File

@ -152,6 +152,11 @@ struct TestChainDIP3BeforeActivationSetup : public TestChainSetup
TestChainDIP3BeforeActivationSetup() : TestChainSetup(430) {}
};
struct TestChainV19BeforeActivationSetup : public TestChainSetup
{
TestChainV19BeforeActivationSetup();
};
class CTxMemPoolEntry;
struct TestMemPoolEntryHelper

View File

@ -19,7 +19,6 @@
#include <hash.h>
#include <validationinterface.h>
#include <bls/bls.h>
#include <evo/specialtx.h>
#include <evo/providertx.h>
#include <evo/deterministicmns.h>
@ -443,7 +442,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn);
newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator);
if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
newit->isKeyChangeProTx = true;
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
@ -838,7 +837,7 @@ void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID
}
}
void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey)
void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey)
{
if (mapProTxBlsPubKeyHashes.count(pubKey.GetHash())) {
uint256 conflictHash = mapProTxBlsPubKeyHashes[pubKey.GetHash()];
@ -1301,7 +1300,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
return true; // i.e. failed to find validated ProTx == conflict
}
// only allow one operator key change in the mempool
if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
if (hasKeyChangeInMempool(proTx.proTxHash)) {
return true;
}

View File

@ -17,6 +17,7 @@
#include <addressindex.h>
#include <spentindex.h>
#include <amount.h>
#include <bls/bls.h>
#include <coins.h>
#include <crypto/siphash.h>
#include <indirectmap.h>
@ -33,8 +34,6 @@
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
class CBLSPublicKey;
class CBlockIndex;
extern CCriticalSection cs_main;
@ -616,7 +615,7 @@ public:
void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxSpentCollateralConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxKeyChangedConflicts(const CTransaction &tx, const uint256& proTxHash, const uint256& newKeyHash) EXCLUSIVE_LOCKS_REQUIRED(cs);

View File

@ -4214,6 +4214,8 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
uint256 hash = block.GetHash();
if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
@ -4240,6 +4242,12 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand
return false;
assert(state.IsValid());
// we could switch to another scheme while testing, switch back to the original one
if (bls_legacy_scheme != bls::bls_legacy_scheme.load()) {
bls::bls_legacy_scheme.store(bls_legacy_scheme);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
return true;
}

View File

@ -66,9 +66,18 @@ class DIP3V19Test(DashTestFramework):
b_0 = self.nodes[0].getbestblockhash()
self.test_getmnlistdiff(null_hash, b_0, {}, [], expected_updated)
mn_list_before = self.nodes[0].masternodelist()
pubkeyoperator_list_before = set([mn_list_before[e]["pubkeyoperator"] for e in mn_list_before])
self.activate_v19(expected_activation_height=900)
self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount()))
mn_list_after = self.nodes[0].masternodelist()
pubkeyoperator_list_after = set([mn_list_after[e]["pubkeyoperator"] for e in mn_list_after])
self.log.info("pubkeyoperator should still be shown using legacy scheme")
assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after)
self.move_to_next_cycle()
self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount()))
self.move_to_next_cycle()