Merge pull request #2246 from codablock/pr_dip3
First part of DIP3 implementation
This commit is contained in:
commit
74629e453d
@ -106,7 +106,10 @@ BITCOIN_CORE_H = \
|
|||||||
core_io.h \
|
core_io.h \
|
||||||
core_memusage.h \
|
core_memusage.h \
|
||||||
cuckoocache.h \
|
cuckoocache.h \
|
||||||
|
evo/evodb.h \
|
||||||
evo/specialtx.h \
|
evo/specialtx.h \
|
||||||
|
evo/providertx.h \
|
||||||
|
evo/deterministicmns.h \
|
||||||
privatesend.h \
|
privatesend.h \
|
||||||
privatesend-client.h \
|
privatesend-client.h \
|
||||||
privatesend-server.h \
|
privatesend-server.h \
|
||||||
@ -217,7 +220,10 @@ libdash_server_a_SOURCES = \
|
|||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
dsnotificationinterface.cpp \
|
dsnotificationinterface.cpp \
|
||||||
|
evo/evodb.cpp \
|
||||||
evo/specialtx.cpp \
|
evo/specialtx.cpp \
|
||||||
|
evo/providertx.cpp \
|
||||||
|
evo/deterministicmns.cpp \
|
||||||
httprpc.cpp \
|
httprpc.cpp \
|
||||||
httpserver.cpp \
|
httpserver.cpp \
|
||||||
init.cpp \
|
init.cpp \
|
||||||
@ -254,6 +260,7 @@ libdash_server_a_SOURCES = \
|
|||||||
rpc/misc.cpp \
|
rpc/misc.cpp \
|
||||||
rpc/net.cpp \
|
rpc/net.cpp \
|
||||||
rpc/rawtransaction.cpp \
|
rpc/rawtransaction.cpp \
|
||||||
|
rpc/rpcevo.cpp \
|
||||||
rpc/server.cpp \
|
rpc/server.cpp \
|
||||||
script/sigcache.cpp \
|
script/sigcache.cpp \
|
||||||
script/ismine.cpp \
|
script/ismine.cpp \
|
||||||
|
215
src/dbwrapper.h
215
src/dbwrapper.h
@ -12,6 +12,8 @@
|
|||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
#include <typeindex>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <leveldb/db.h>
|
#include <leveldb/db.h>
|
||||||
@ -342,4 +344,217 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CDBTransaction {
|
||||||
|
private:
|
||||||
|
CDBWrapper &db;
|
||||||
|
|
||||||
|
struct KeyHolder {
|
||||||
|
virtual ~KeyHolder() = default;
|
||||||
|
virtual bool Less(const KeyHolder &b) const = 0;
|
||||||
|
virtual void Erase(CDBBatch &batch) = 0;
|
||||||
|
};
|
||||||
|
typedef std::unique_ptr<KeyHolder> KeyHolderPtr;
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
struct KeyHolderImpl : KeyHolder {
|
||||||
|
KeyHolderImpl(const K &_key)
|
||||||
|
: key(_key) {
|
||||||
|
}
|
||||||
|
virtual bool Less(const KeyHolder &b) const {
|
||||||
|
auto *b2 = dynamic_cast<const KeyHolderImpl<K>*>(&b);
|
||||||
|
return key < b2->key;
|
||||||
|
}
|
||||||
|
virtual void Erase(CDBBatch &batch) {
|
||||||
|
batch.Erase(key);
|
||||||
|
}
|
||||||
|
K key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeyValueHolder {
|
||||||
|
virtual void Write(CDBBatch &batch) = 0;
|
||||||
|
};
|
||||||
|
typedef std::unique_ptr<KeyValueHolder> KeyValueHolderPtr;
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
struct KeyValueHolderImpl : KeyValueHolder {
|
||||||
|
KeyValueHolderImpl(const KeyHolderImpl<K> &_key, const V &_value)
|
||||||
|
: key(_key),
|
||||||
|
value(_value) { }
|
||||||
|
virtual void Write(CDBBatch &batch) {
|
||||||
|
batch.Write(key.key, value);
|
||||||
|
}
|
||||||
|
const KeyHolderImpl<K> &key;
|
||||||
|
V value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct keyCmp {
|
||||||
|
bool operator()(const KeyHolderPtr &a, const KeyHolderPtr &b) const {
|
||||||
|
return a->Less(*b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::map<KeyHolderPtr, KeyValueHolderPtr, keyCmp> KeyValueMap;
|
||||||
|
typedef std::map<std::type_index, KeyValueMap> TypeKeyValueMap;
|
||||||
|
|
||||||
|
TypeKeyValueMap writes;
|
||||||
|
TypeKeyValueMap deletes;
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
KeyValueMap *getMapForType(TypeKeyValueMap &m, bool create) {
|
||||||
|
auto it = m.find(typeid(K));
|
||||||
|
if (it != m.end()) {
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
if (!create)
|
||||||
|
return nullptr;
|
||||||
|
auto it2 = m.emplace(typeid(K), KeyValueMap());
|
||||||
|
return &it2.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
KeyValueMap *getWritesMap(bool create) {
|
||||||
|
return getMapForType<K>(writes, create);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
KeyValueMap *getDeletesMap(bool create) {
|
||||||
|
return getMapForType<K>(deletes, create);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CDBTransaction(CDBWrapper &_db) : db(_db) {}
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
void Write(const K& key, const V& value) {
|
||||||
|
KeyHolderPtr k(new KeyHolderImpl<K>(key));
|
||||||
|
KeyHolderImpl<K>* k2 = dynamic_cast<KeyHolderImpl<K>*>(k.get());
|
||||||
|
KeyValueHolderPtr kv(new KeyValueHolderImpl<K,V>(*k2, value));
|
||||||
|
|
||||||
|
KeyValueMap *ds = getDeletesMap<K>(false);
|
||||||
|
if (ds)
|
||||||
|
ds->erase(k);
|
||||||
|
|
||||||
|
KeyValueMap *ws = getWritesMap<K>(true);
|
||||||
|
ws->erase(k);
|
||||||
|
ws->emplace(std::make_pair(std::move(k), std::move(kv)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
bool Read(const K& key, V& value) {
|
||||||
|
KeyHolderPtr k(new KeyHolderImpl<K>(key));
|
||||||
|
|
||||||
|
KeyValueMap *ds = getDeletesMap<K>(false);
|
||||||
|
if (ds && ds->count(k))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
KeyValueMap *ws = getWritesMap<K>(false);
|
||||||
|
if (ws) {
|
||||||
|
KeyValueMap::iterator it = ws->find(k);
|
||||||
|
if (it != ws->end()) {
|
||||||
|
auto *impl = dynamic_cast<KeyValueHolderImpl<K, V> *>(it->second.get());
|
||||||
|
if (!impl)
|
||||||
|
return false;
|
||||||
|
value = impl->value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.Read(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
bool Exists(const K& key) {
|
||||||
|
KeyHolderPtr k(new KeyHolderImpl<K>(key));
|
||||||
|
|
||||||
|
KeyValueMap *ds = getDeletesMap<K>(false);
|
||||||
|
if (ds && ds->count(k))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
KeyValueMap *ws = getWritesMap<K>(false);
|
||||||
|
if (ws && ws->count(k))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return db.Exists(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
void Erase(const K& key) {
|
||||||
|
KeyHolderPtr k(new KeyHolderImpl<K>(key));
|
||||||
|
|
||||||
|
KeyValueMap *ws = getWritesMap<K>(false);
|
||||||
|
if (ws)
|
||||||
|
ws->erase(k);
|
||||||
|
KeyValueMap *ds = getDeletesMap<K>(true);
|
||||||
|
ds->emplace(std::move(k), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
writes.clear();
|
||||||
|
deletes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Commit() {
|
||||||
|
CDBBatch batch(db);
|
||||||
|
for (auto &p : deletes) {
|
||||||
|
for (auto &p2 : p.second) {
|
||||||
|
p2.first->Erase(batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &p : writes) {
|
||||||
|
for (auto &p2 : p.second) {
|
||||||
|
p2.second->Write(batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ret = db.WriteBatch(batch, true);
|
||||||
|
Clear();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsClean() {
|
||||||
|
return writes.empty() && deletes.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CScopedDBTransaction {
|
||||||
|
private:
|
||||||
|
CDBTransaction &dbTransaction;
|
||||||
|
std::function<void ()> commitHandler;
|
||||||
|
std::function<void ()> rollbackHandler;
|
||||||
|
bool didCommitOrRollback{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CScopedDBTransaction(CDBTransaction &dbTx) : dbTransaction(dbTx) {}
|
||||||
|
~CScopedDBTransaction() {
|
||||||
|
if (!didCommitOrRollback)
|
||||||
|
Rollback();
|
||||||
|
}
|
||||||
|
bool Commit() {
|
||||||
|
assert(!didCommitOrRollback);
|
||||||
|
didCommitOrRollback = true;
|
||||||
|
bool result = dbTransaction.Commit();
|
||||||
|
if (commitHandler)
|
||||||
|
commitHandler();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
void Rollback() {
|
||||||
|
assert(!didCommitOrRollback);
|
||||||
|
didCommitOrRollback = true;
|
||||||
|
dbTransaction.Clear();
|
||||||
|
if (rollbackHandler)
|
||||||
|
rollbackHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<CScopedDBTransaction> Begin(CDBTransaction &dbTx) {
|
||||||
|
assert(dbTx.IsClean());
|
||||||
|
return std::unique_ptr<CScopedDBTransaction>(new CScopedDBTransaction(dbTx));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCommitHandler(const std::function<void ()> &h) {
|
||||||
|
commitHandler = h;
|
||||||
|
}
|
||||||
|
void SetRollbackHandler(const std::function<void ()> &h) {
|
||||||
|
rollbackHandler = h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_DBWRAPPER_H
|
#endif // BITCOIN_DBWRAPPER_H
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include "privatesend-client.h"
|
#include "privatesend-client.h"
|
||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
|
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
void CDSNotificationInterface::InitializeCurrentBlockTip()
|
void CDSNotificationInterface::InitializeCurrentBlockTip()
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
@ -35,6 +37,8 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
|
|||||||
if (pindexNew == pindexFork) // blocks were disconnected without any new ones
|
if (pindexNew == pindexFork) // blocks were disconnected without any new ones
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
deterministicMNManager->UpdatedBlockTip(pindexNew);
|
||||||
|
|
||||||
masternodeSync.UpdatedBlockTip(pindexNew, fInitialDownload, connman);
|
masternodeSync.UpdatedBlockTip(pindexNew, fInitialDownload, connman);
|
||||||
|
|
||||||
// Update global DIP0001 activation status
|
// Update global DIP0001 activation status
|
||||||
|
515
src/evo/deterministicmns.cpp
Normal file
515
src/evo/deterministicmns.cpp
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "deterministicmns.h"
|
||||||
|
#include "specialtx.h"
|
||||||
|
|
||||||
|
#include "validation.h"
|
||||||
|
#include "validationinterface.h"
|
||||||
|
#include "chainparams.h"
|
||||||
|
#include "script/standard.h"
|
||||||
|
#include "base58.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "spork.h"
|
||||||
|
|
||||||
|
#include <univalue.h>
|
||||||
|
|
||||||
|
static const std::string DB_LIST_SNAPSHOT = "dmn_S";
|
||||||
|
static const std::string DB_LIST_DIFF = "dmn_D";
|
||||||
|
|
||||||
|
CDeterministicMNManager* deterministicMNManager;
|
||||||
|
|
||||||
|
std::string CDeterministicMNState::ToString() const
|
||||||
|
{
|
||||||
|
CTxDestination dest;
|
||||||
|
std::string payoutAddress = "unknown";
|
||||||
|
std::string operatorRewardAddress = "none";
|
||||||
|
if (ExtractDestination(scriptPayout, dest)) {
|
||||||
|
payoutAddress = CBitcoinAddress(dest).ToString();
|
||||||
|
}
|
||||||
|
if (ExtractDestination(scriptOperatorPayout, dest)) {
|
||||||
|
operatorRewardAddress = CBitcoinAddress(dest).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenality=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, "
|
||||||
|
"keyIDOwner=%s, keyIDOperator=%s, keyIDVoting=%s, addr=%s, nProtocolVersion=%d, payoutAddress=%s, operatorRewardAddress=%s)",
|
||||||
|
nRegisteredHeight, nLastPaidHeight, nPoSePenality, nPoSeRevivedHeight, nPoSeBanHeight,
|
||||||
|
keyIDOwner.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), nProtocolVersion, payoutAddress, operatorRewardAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNState::ToJson(UniValue& obj) const
|
||||||
|
{
|
||||||
|
obj.clear();
|
||||||
|
obj.setObject();
|
||||||
|
obj.push_back(Pair("registeredHeight", nRegisteredHeight));
|
||||||
|
obj.push_back(Pair("lastPaidHeight", nLastPaidHeight));
|
||||||
|
obj.push_back(Pair("PoSePenality", nPoSePenality));
|
||||||
|
obj.push_back(Pair("PoSeRevivedHeight", nPoSeRevivedHeight));
|
||||||
|
obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight));
|
||||||
|
obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString()));
|
||||||
|
obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString()));
|
||||||
|
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
|
||||||
|
obj.push_back(Pair("addr", addr.ToStringIPPort(false)));
|
||||||
|
obj.push_back(Pair("protocolVersion", nProtocolVersion));
|
||||||
|
|
||||||
|
CTxDestination dest;
|
||||||
|
if (ExtractDestination(scriptPayout, dest)) {
|
||||||
|
CBitcoinAddress bitcoinAddress(dest);
|
||||||
|
obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString()));
|
||||||
|
}
|
||||||
|
if (ExtractDestination(scriptOperatorPayout, dest)) {
|
||||||
|
CBitcoinAddress bitcoinAddress(dest);
|
||||||
|
obj.push_back(Pair("operatorRewardAddress", bitcoinAddress.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CDeterministicMN::ToString() const
|
||||||
|
{
|
||||||
|
return strprintf("CDeterministicMN(proTxHash=%s, nCollateralIndex=%d, nOperatorReward=%f, state=%s", proTxHash.ToString(), nCollateralIndex, (double)nOperatorReward / 100, pdmnState->ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMN::ToJson(UniValue& obj) const
|
||||||
|
{
|
||||||
|
obj.clear();
|
||||||
|
obj.setObject();
|
||||||
|
|
||||||
|
UniValue stateObj;
|
||||||
|
pdmnState->ToJson(stateObj);
|
||||||
|
|
||||||
|
obj.push_back(Pair("proTxHash", proTxHash.ToString()));
|
||||||
|
obj.push_back(Pair("collateralIndex", (int)nCollateralIndex));
|
||||||
|
obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100));
|
||||||
|
obj.push_back(Pair("state", stateObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNList::IsMNValid(const uint256& proTxHash) const
|
||||||
|
{
|
||||||
|
auto p = mnMap.find(proTxHash);
|
||||||
|
if (p == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return IsMNValid(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNList::IsMNPoSeBanned(const uint256& proTxHash) const
|
||||||
|
{
|
||||||
|
auto p = mnMap.find(proTxHash);
|
||||||
|
if (p == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return IsMNPoSeBanned(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNList::IsMNValid(const CDeterministicMNCPtr& dmn) const
|
||||||
|
{
|
||||||
|
return !IsMNPoSeBanned(dmn);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNList::IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const
|
||||||
|
{
|
||||||
|
assert(dmn);
|
||||||
|
const CDeterministicMNState& state = *dmn->pdmnState;
|
||||||
|
return state.nPoSeBanHeight != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNCPtr CDeterministicMNList::GetMN(const uint256& proTxHash) const
|
||||||
|
{
|
||||||
|
auto p = mnMap.find(proTxHash);
|
||||||
|
if (p == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const
|
||||||
|
{
|
||||||
|
auto dmn = GetMN(proTxHash);
|
||||||
|
if (dmn && !IsMNValid(dmn)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return dmn;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CKeyID& keyID)
|
||||||
|
{
|
||||||
|
for (const auto& p : mnMap) {
|
||||||
|
if (p.second->pdmnState->keyIDOperator == keyID) {
|
||||||
|
return p.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CompareByLastPaid_GetHeight(const CDeterministicMN &dmn)
|
||||||
|
{
|
||||||
|
int h = dmn.pdmnState->nLastPaidHeight;
|
||||||
|
if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > h) {
|
||||||
|
h = dmn.pdmnState->nPoSeRevivedHeight;
|
||||||
|
} else if (h == 0) {
|
||||||
|
h = dmn.pdmnState->nRegisteredHeight;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CompareByLastPaid(const CDeterministicMN &_a, const CDeterministicMN &_b)
|
||||||
|
{
|
||||||
|
int ah = CompareByLastPaid_GetHeight(_a);
|
||||||
|
int bh = CompareByLastPaid_GetHeight(_b);
|
||||||
|
if (ah == bh) {
|
||||||
|
return _a.proTxHash < _b.proTxHash;
|
||||||
|
} else {
|
||||||
|
return ah < bh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static bool CompareByLastPaid(const CDeterministicMNCPtr &_a, const CDeterministicMNCPtr &_b)
|
||||||
|
{
|
||||||
|
return CompareByLastPaid(*_a, *_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const
|
||||||
|
{
|
||||||
|
if (mnMap.size() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
CDeterministicMNCPtr best;
|
||||||
|
for (const auto& dmn : valid_range()) {
|
||||||
|
if (!best || CompareByLastPaid(dmn, best))
|
||||||
|
best = dmn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(int nCount) const
|
||||||
|
{
|
||||||
|
std::vector<CDeterministicMNCPtr> result;
|
||||||
|
result.reserve(nCount);
|
||||||
|
|
||||||
|
CDeterministicMNList tmpMNList = *this;
|
||||||
|
for (int h = nHeight; h < nHeight + nCount; h++) {
|
||||||
|
tmpMNList.SetHeight(h);
|
||||||
|
|
||||||
|
CDeterministicMNCPtr payee = tmpMNList.GetMNPayee();
|
||||||
|
// push the original MN object instead of the one from the temporary list
|
||||||
|
result.push_back(GetMN(payee->proTxHash));
|
||||||
|
|
||||||
|
CDeterministicMNStatePtr newState = std::make_shared<CDeterministicMNState>(*payee->pdmnState);
|
||||||
|
newState->nLastPaidHeight = h;
|
||||||
|
tmpMNList.UpdateMN(payee->proTxHash, newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const
|
||||||
|
{
|
||||||
|
CDeterministicMNListDiff diffRet;
|
||||||
|
diffRet.prevBlockHash = blockHash;
|
||||||
|
diffRet.blockHash = to.blockHash;
|
||||||
|
diffRet.nHeight = to.nHeight;
|
||||||
|
|
||||||
|
for (const auto& p : to.mnMap) {
|
||||||
|
const auto fromPtr = mnMap.find(p.first);
|
||||||
|
if (fromPtr == nullptr) {
|
||||||
|
diffRet.addedMNs.emplace(p.first, p.second);
|
||||||
|
} else if (*p.second->pdmnState != *(*fromPtr)->pdmnState) {
|
||||||
|
diffRet.updatedMNs.emplace(p.first, p.second->pdmnState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& p : mnMap) {
|
||||||
|
const auto toPtr = to.mnMap.find(p.first);
|
||||||
|
if (toPtr == nullptr) {
|
||||||
|
diffRet.removedMns.insert(p.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diffRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNList CDeterministicMNList::ApplyDiff(const CDeterministicMNListDiff& diff) const
|
||||||
|
{
|
||||||
|
assert(diff.prevBlockHash == blockHash && diff.nHeight == nHeight + 1);
|
||||||
|
|
||||||
|
CDeterministicMNList result = *this;
|
||||||
|
result.blockHash = diff.blockHash;
|
||||||
|
result.nHeight = diff.nHeight;
|
||||||
|
|
||||||
|
for (const auto& hash : diff.removedMns) {
|
||||||
|
result.RemoveMN(hash);
|
||||||
|
}
|
||||||
|
for (const auto& p : diff.addedMNs) {
|
||||||
|
result.AddMN(p.second);
|
||||||
|
}
|
||||||
|
for (const auto& p : diff.updatedMNs) {
|
||||||
|
result.UpdateMN(p.first, p.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNList::AddMN(const CDeterministicMNCPtr &dmn)
|
||||||
|
{
|
||||||
|
assert(!mnMap.find(dmn->proTxHash));
|
||||||
|
mnMap = mnMap.set(dmn->proTxHash, dmn);
|
||||||
|
AddUniqueProperty(dmn, dmn->pdmnState->addr);
|
||||||
|
AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
|
||||||
|
AddUniqueProperty(dmn, dmn->pdmnState->keyIDOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNList::UpdateMN(const uint256 &proTxHash, const CDeterministicMNStateCPtr &pdmnState)
|
||||||
|
{
|
||||||
|
auto oldDmn = mnMap.find(proTxHash);
|
||||||
|
assert(oldDmn != nullptr);
|
||||||
|
auto dmn = std::make_shared<CDeterministicMN>(**oldDmn);
|
||||||
|
auto oldState = dmn->pdmnState;
|
||||||
|
dmn->pdmnState = pdmnState;
|
||||||
|
mnMap = mnMap.set(proTxHash, dmn);
|
||||||
|
|
||||||
|
UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr);
|
||||||
|
UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner);
|
||||||
|
UpdateUniqueProperty(dmn, oldState->keyIDOperator, pdmnState->keyIDOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
|
||||||
|
{
|
||||||
|
auto dmn = GetMN(proTxHash);
|
||||||
|
assert(dmn != nullptr);
|
||||||
|
DeleteUniqueProperty(dmn, dmn->pdmnState->addr);
|
||||||
|
DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
|
||||||
|
DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOperator);
|
||||||
|
mnMap = mnMap.erase(proTxHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNManager::CDeterministicMNManager(CEvoDB& _evoDb) :
|
||||||
|
evoDb(_evoDb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
int nHeight = pindexPrev->nHeight + 1;
|
||||||
|
|
||||||
|
CDeterministicMNList newList;
|
||||||
|
if (!BuildNewListFromBlock(block, pindexPrev, _state, newList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newList.GetHeight() == -1) {
|
||||||
|
newList.SetHeight(nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
newList.SetBlockHash(block.GetHash());
|
||||||
|
|
||||||
|
CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash());
|
||||||
|
CDeterministicMNListDiff diff = oldList.BuildDiff(newList);
|
||||||
|
|
||||||
|
evoDb.Write(std::make_pair(DB_LIST_DIFF, diff.blockHash), diff);
|
||||||
|
if ((nHeight % SNAPSHOT_LIST_PERIOD) == 0) {
|
||||||
|
evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, diff.blockHash), newList);
|
||||||
|
LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.size=%d\n",
|
||||||
|
__func__, nHeight, newList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nHeight == GetSpork15Value()) {
|
||||||
|
LogPrintf("CDeterministicMNManager::%s -- spork15 is active now. nHeight=%d\n", __func__, nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
CleanupCache(nHeight);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
int nHeight = pindex->nHeight;
|
||||||
|
|
||||||
|
evoDb.Erase(std::make_pair(DB_LIST_DIFF, block.GetHash()));
|
||||||
|
evoDb.Erase(std::make_pair(DB_LIST_SNAPSHOT, block.GetHash()));
|
||||||
|
|
||||||
|
if (nHeight == GetSpork15Value()) {
|
||||||
|
LogPrintf("CDeterministicMNManager::%s -- spork15 is not active anymore. nHeight=%d\n", __func__, nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNManager::UpdatedBlockTip(const CBlockIndex* pindex)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
tipHeight = pindex->nHeight;
|
||||||
|
tipBlockHash = pindex->GetBlockHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet)
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs);
|
||||||
|
|
||||||
|
int nHeight = pindexPrev->nHeight + 1;
|
||||||
|
|
||||||
|
CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash());
|
||||||
|
CDeterministicMNList newList = oldList;
|
||||||
|
newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash
|
||||||
|
newList.SetHeight(nHeight);
|
||||||
|
|
||||||
|
auto payee = oldList.GetMNPayee();
|
||||||
|
|
||||||
|
// we skip the coinbase
|
||||||
|
for (int i = 1; i < (int)block.vtx.size(); i++) {
|
||||||
|
const CTransaction& tx = *block.vtx[i];
|
||||||
|
|
||||||
|
// check if any existing MN collateral is spent by this transaction
|
||||||
|
for (const auto& in : tx.vin) {
|
||||||
|
const uint256& proTxHash = in.prevout.hash;
|
||||||
|
auto dmn = newList.GetMN(proTxHash);
|
||||||
|
if (dmn && dmn->nCollateralIndex == in.prevout.n) {
|
||||||
|
newList.RemoveMN(proTxHash);
|
||||||
|
|
||||||
|
LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. nHeight=%d, mapCurMNs.size=%d\n",
|
||||||
|
__func__, proTxHash.ToString(), nHeight, newList.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(tx, proTx)) {
|
||||||
|
assert(false); // this should have been handled already
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newList.HasUniqueProperty(proTx.addr))
|
||||||
|
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr");
|
||||||
|
if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.keyIDOperator))
|
||||||
|
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-key");
|
||||||
|
|
||||||
|
auto dmn = std::make_shared<CDeterministicMN>(tx.GetHash(), proTx);
|
||||||
|
|
||||||
|
CDeterministicMNState dmnState = *dmn->pdmnState;
|
||||||
|
dmnState.nRegisteredHeight = nHeight;
|
||||||
|
|
||||||
|
if (proTx.addr == CService() || proTx.nProtocolVersion == 0) {
|
||||||
|
// start in banned pdmnState as we need to wait for a ProUpServTx
|
||||||
|
dmnState.nPoSeBanHeight = nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmn->pdmnState = std::make_shared<CDeterministicMNState>(dmnState);
|
||||||
|
|
||||||
|
newList.AddMN(dmn);
|
||||||
|
|
||||||
|
LogPrintf("CDeterministicMNManager::%s -- MN %s added to MN list. nHeight=%d, mapCurMNs.size=%d\n",
|
||||||
|
__func__, tx.GetHash().ToString(), nHeight, newList.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The payee for the current block was determined by the previous block's list but it might have disappeared in the
|
||||||
|
// current block. We still pay that MN one last time however.
|
||||||
|
if (payee && newList.HasMN(payee->proTxHash)) {
|
||||||
|
auto newState = std::make_shared<CDeterministicMNState>(*newList.GetMN(payee->proTxHash)->pdmnState);
|
||||||
|
newState->nLastPaidHeight = nHeight;
|
||||||
|
newList.UpdateMN(payee->proTxHash, newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
mnListRet = std::move(newList);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNList CDeterministicMNManager::GetListForBlock(const uint256& blockHash)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
auto it = mnListsCache.find(blockHash);
|
||||||
|
if (it != mnListsCache.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 blockHashTmp = blockHash;
|
||||||
|
CDeterministicMNList snapshot;
|
||||||
|
std::vector<CDeterministicMNListDiff> vecDiff;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if (evoDb.Read(std::make_pair(DB_LIST_SNAPSHOT, blockHashTmp), snapshot)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNListDiff diff;
|
||||||
|
if (!evoDb.Read(std::make_pair(DB_LIST_DIFF, blockHashTmp), diff)) {
|
||||||
|
snapshot = CDeterministicMNList(blockHashTmp, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vecDiff.emplace(vecDiff.begin(), diff);
|
||||||
|
blockHashTmp = diff.prevBlockHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& diff : vecDiff) {
|
||||||
|
if (diff.HasChanges()) {
|
||||||
|
snapshot = snapshot.ApplyDiff(diff);
|
||||||
|
} else {
|
||||||
|
snapshot.SetBlockHash(diff.blockHash);
|
||||||
|
snapshot.SetHeight(diff.nHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mnListsCache.emplace(blockHash, snapshot);
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNList CDeterministicMNManager::GetListAtChainTip()
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
return GetListForBlock(tipBlockHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeterministicMNCPtr CDeterministicMNManager::GetMN(const uint256& blockHash, const uint256& proTxHash)
|
||||||
|
{
|
||||||
|
auto mnList = GetListForBlock(blockHash);
|
||||||
|
return mnList.GetMN(proTxHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::HasValidMNAtBlock(const uint256& blockHash, const uint256& proTxHash)
|
||||||
|
{
|
||||||
|
auto mnList = GetListForBlock(blockHash);
|
||||||
|
return mnList.IsMNValid(proTxHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::HasValidMNAtChainTip(const uint256& proTxHash)
|
||||||
|
{
|
||||||
|
return GetListAtChainTip().IsMNValid(proTxHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t CDeterministicMNManager::GetSpork15Value()
|
||||||
|
{
|
||||||
|
return sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeterministicMNManager::IsDeterministicMNsSporkActive(int nHeight)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
if (nHeight == -1) {
|
||||||
|
nHeight = tipHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t spork15Value = GetSpork15Value();
|
||||||
|
return nHeight >= spork15Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeterministicMNManager::CleanupCache(int nHeight)
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs);
|
||||||
|
|
||||||
|
std::vector<uint256> toDelete;
|
||||||
|
for (const auto& p : mnListsCache) {
|
||||||
|
if (p.second.GetHeight() + LISTS_CACHE_SIZE < nHeight) {
|
||||||
|
toDelete.emplace_back(p.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& h : toDelete) {
|
||||||
|
mnListsCache.erase(h);
|
||||||
|
}
|
||||||
|
}
|
414
src/evo/deterministicmns.h
Normal file
414
src/evo/deterministicmns.h
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef DASH_DETERMINISTICMNS_H
|
||||||
|
#define DASH_DETERMINISTICMNS_H
|
||||||
|
|
||||||
|
#include "evodb.h"
|
||||||
|
#include "providertx.h"
|
||||||
|
#include "dbwrapper.h"
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
|
#include "immer/map.hpp"
|
||||||
|
#include "immer/map_transient.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <boost/range/adaptors.hpp>
|
||||||
|
#include <boost/range/any_range.hpp>
|
||||||
|
|
||||||
|
class CBlock;
|
||||||
|
class CBlockIndex;
|
||||||
|
class CValidationState;
|
||||||
|
|
||||||
|
class CDeterministicMNState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int nRegisteredHeight{-1};
|
||||||
|
int nLastPaidHeight{0};
|
||||||
|
int nPoSePenality{0};
|
||||||
|
int nPoSeRevivedHeight{-1};
|
||||||
|
int nPoSeBanHeight{-1};
|
||||||
|
|
||||||
|
CKeyID keyIDOwner;
|
||||||
|
CKeyID keyIDOperator;
|
||||||
|
CKeyID keyIDVoting;
|
||||||
|
CService addr;
|
||||||
|
int32_t nProtocolVersion;
|
||||||
|
CScript scriptPayout;
|
||||||
|
CScript scriptOperatorPayout;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CDeterministicMNState() {}
|
||||||
|
CDeterministicMNState(const CProRegTx& proTx)
|
||||||
|
{
|
||||||
|
keyIDOwner = proTx.keyIDOwner;
|
||||||
|
keyIDOperator = proTx.keyIDOperator;
|
||||||
|
keyIDVoting = proTx.keyIDVoting;
|
||||||
|
addr = proTx.addr;
|
||||||
|
nProtocolVersion = proTx.nProtocolVersion;
|
||||||
|
scriptPayout = proTx.scriptPayout;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
CDeterministicMNState(deserialize_type, Stream& s) { s >> *this;}
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||||
|
{
|
||||||
|
READWRITE(nRegisteredHeight);
|
||||||
|
READWRITE(nLastPaidHeight);
|
||||||
|
READWRITE(nPoSePenality);
|
||||||
|
READWRITE(nPoSeRevivedHeight);
|
||||||
|
READWRITE(nPoSeBanHeight);
|
||||||
|
READWRITE(keyIDOwner);
|
||||||
|
READWRITE(keyIDOperator);
|
||||||
|
READWRITE(keyIDVoting);
|
||||||
|
READWRITE(addr);
|
||||||
|
READWRITE(nProtocolVersion);
|
||||||
|
READWRITE(*(CScriptBase*)(&scriptPayout));
|
||||||
|
READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const CDeterministicMNState& rhs) const
|
||||||
|
{
|
||||||
|
return nRegisteredHeight == rhs.nRegisteredHeight &&
|
||||||
|
nLastPaidHeight == rhs.nLastPaidHeight &&
|
||||||
|
nPoSePenality == rhs.nPoSePenality &&
|
||||||
|
nPoSeRevivedHeight == rhs.nPoSeRevivedHeight &&
|
||||||
|
nPoSeBanHeight == rhs.nPoSeBanHeight &&
|
||||||
|
keyIDOwner == rhs.keyIDOwner &&
|
||||||
|
keyIDOperator == rhs.keyIDOperator &&
|
||||||
|
keyIDVoting == rhs.keyIDVoting &&
|
||||||
|
addr == rhs.addr &&
|
||||||
|
nProtocolVersion == rhs.nProtocolVersion &&
|
||||||
|
scriptPayout == rhs.scriptPayout &&
|
||||||
|
scriptOperatorPayout == rhs.scriptOperatorPayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const CDeterministicMNState& rhs) const
|
||||||
|
{
|
||||||
|
return !(rhs == *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string ToString() const;
|
||||||
|
void ToJson(UniValue& obj) const;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<CDeterministicMNState> CDeterministicMNStatePtr;
|
||||||
|
typedef std::shared_ptr<const CDeterministicMNState> CDeterministicMNStateCPtr;
|
||||||
|
|
||||||
|
class CDeterministicMN
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDeterministicMN() {}
|
||||||
|
CDeterministicMN(const uint256& _proTxHash, const CProRegTx& _proTx)
|
||||||
|
{
|
||||||
|
proTxHash = _proTxHash;
|
||||||
|
nCollateralIndex = _proTx.nCollateralIndex;
|
||||||
|
nOperatorReward = _proTx.nOperatorReward;
|
||||||
|
pdmnState = std::make_shared<CDeterministicMNState>(_proTx);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
CDeterministicMN(deserialize_type, Stream& s) { s >> *this;}
|
||||||
|
|
||||||
|
uint256 proTxHash;
|
||||||
|
uint32_t nCollateralIndex;
|
||||||
|
uint16_t nOperatorReward;
|
||||||
|
CDeterministicMNStateCPtr pdmnState;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||||
|
{
|
||||||
|
READWRITE(proTxHash);
|
||||||
|
READWRITE(nCollateralIndex);
|
||||||
|
READWRITE(nOperatorReward);
|
||||||
|
READWRITE(pdmnState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string ToString() const;
|
||||||
|
void ToJson(UniValue& obj) const;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<CDeterministicMN> CDeterministicMNPtr;
|
||||||
|
typedef std::shared_ptr<const CDeterministicMN> CDeterministicMNCPtr;
|
||||||
|
|
||||||
|
class CDeterministicMNListDiff;
|
||||||
|
|
||||||
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
||||||
|
void SerializeImmerMap(Stream& os, const immer::map<K, T, Hash, Equal>& m)
|
||||||
|
{
|
||||||
|
WriteCompactSize(os, m.size());
|
||||||
|
for (typename immer::map<K, T, Hash, Equal>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
|
||||||
|
Serialize(os, (*mi));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
||||||
|
void UnserializeImmerMap(Stream& is, immer::map<K, T, Hash, Equal>& m)
|
||||||
|
{
|
||||||
|
m = immer::map<K, T, Hash, Equal>();
|
||||||
|
unsigned int nSize = ReadCompactSize(is);
|
||||||
|
for (unsigned int i = 0; i < nSize; i++)
|
||||||
|
{
|
||||||
|
std::pair<K, T> item;
|
||||||
|
Unserialize(is, item);
|
||||||
|
m = m.set(item.first, item.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CDeterministicMNList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef immer::map<uint256, CDeterministicMNCPtr> MnMap;
|
||||||
|
typedef immer::map<uint256, std::pair<uint256, uint32_t>> MnUniquePropertyMap;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint256 blockHash;
|
||||||
|
int nHeight{-1};
|
||||||
|
MnMap mnMap;
|
||||||
|
|
||||||
|
// map of unique properties like address and keys
|
||||||
|
// we keep track of this as checking for duplicates would otherwise be painfully slow
|
||||||
|
// the entries in the map are ref counted as some properties might appear multiple times per MN (e.g. operator/owner keys)
|
||||||
|
MnUniquePropertyMap mnUniquePropertyMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CDeterministicMNList() {}
|
||||||
|
explicit CDeterministicMNList(const uint256& _blockHash, int _height) :
|
||||||
|
blockHash(_blockHash),
|
||||||
|
nHeight(_height)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||||
|
{
|
||||||
|
READWRITE(blockHash);
|
||||||
|
READWRITE(nHeight);
|
||||||
|
if (ser_action.ForRead()) {
|
||||||
|
UnserializeImmerMap(s, mnMap);
|
||||||
|
UnserializeImmerMap(s, mnUniquePropertyMap);
|
||||||
|
} else {
|
||||||
|
SerializeImmerMap(s, mnMap);
|
||||||
|
SerializeImmerMap(s, mnUniquePropertyMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return mnMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef boost::any_range<const CDeterministicMNCPtr&, boost::forward_traversal_tag> range_type;
|
||||||
|
|
||||||
|
range_type all_range() const
|
||||||
|
{
|
||||||
|
return boost::adaptors::transform(mnMap, [] (const MnMap::value_type& p) -> const CDeterministicMNCPtr& {
|
||||||
|
return p.second;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
range_type valid_range() const
|
||||||
|
{
|
||||||
|
return boost::adaptors::filter(all_range(), [&] (const CDeterministicMNCPtr& dmn) -> bool {
|
||||||
|
return IsMNValid(dmn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t all_count() const
|
||||||
|
{
|
||||||
|
return mnMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t valid_count() const
|
||||||
|
{
|
||||||
|
size_t c = 0;
|
||||||
|
for (const auto& p : mnMap) {
|
||||||
|
if (IsMNValid(p.second)) {
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const uint256& GetBlockHash() const
|
||||||
|
{
|
||||||
|
return blockHash;
|
||||||
|
}
|
||||||
|
void SetBlockHash(const uint256& _blockHash)
|
||||||
|
{
|
||||||
|
blockHash = _blockHash;
|
||||||
|
}
|
||||||
|
int GetHeight() const
|
||||||
|
{
|
||||||
|
return nHeight;
|
||||||
|
}
|
||||||
|
void SetHeight(int _height)
|
||||||
|
{
|
||||||
|
nHeight = _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsMNValid(const uint256& proTxHash) const;
|
||||||
|
bool IsMNPoSeBanned(const uint256& proTxHash) const;
|
||||||
|
|
||||||
|
bool HasMN(const uint256& proTxHash) const {
|
||||||
|
return GetMN(proTxHash) != nullptr;
|
||||||
|
}
|
||||||
|
CDeterministicMNCPtr GetMN(const uint256& proTxHash) const;
|
||||||
|
CDeterministicMNCPtr GetValidMN(const uint256& proTxHash) const;
|
||||||
|
CDeterministicMNCPtr GetMNByOperatorKey(const CKeyID& keyID);
|
||||||
|
CDeterministicMNCPtr GetMNPayee() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct
|
||||||
|
* as PoSe banning might occur later
|
||||||
|
* @param count
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(int nCount) const;
|
||||||
|
|
||||||
|
CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const;
|
||||||
|
CDeterministicMNList ApplyDiff(const CDeterministicMNListDiff& diff) const;
|
||||||
|
|
||||||
|
void AddMN(const CDeterministicMNCPtr &dmn);
|
||||||
|
void UpdateMN(const uint256 &proTxHash, const CDeterministicMNStateCPtr &pdmnState);
|
||||||
|
void RemoveMN(const uint256& proTxHash);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool HasUniqueProperty(const T& v) const
|
||||||
|
{
|
||||||
|
return mnUniquePropertyMap.count(::SerializeHash(v)) != 0;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const
|
||||||
|
{
|
||||||
|
auto p = mnUniquePropertyMap.find(::SerializeHash(v));
|
||||||
|
if (!p) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return GetMN(p->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool IsMNValid(const CDeterministicMNCPtr& dmn) const;
|
||||||
|
bool IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void AddUniqueProperty(const CDeterministicMNCPtr& dmn, const T& v)
|
||||||
|
{
|
||||||
|
auto hash = ::SerializeHash(v);
|
||||||
|
auto oldEntry = mnUniquePropertyMap.find(hash);
|
||||||
|
assert(!oldEntry || oldEntry->first == dmn->proTxHash);
|
||||||
|
std::pair<uint256, uint32_t> newEntry(dmn->proTxHash, 1);
|
||||||
|
if (oldEntry) {
|
||||||
|
newEntry.second = oldEntry->second + 1;
|
||||||
|
}
|
||||||
|
mnUniquePropertyMap = mnUniquePropertyMap.set(hash, newEntry);
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
void DeleteUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue)
|
||||||
|
{
|
||||||
|
auto oldHash = ::SerializeHash(oldValue);
|
||||||
|
auto p = mnUniquePropertyMap.find(oldHash);
|
||||||
|
assert(p && p->first == dmn->proTxHash);
|
||||||
|
if (p->second == 1) {
|
||||||
|
mnUniquePropertyMap = mnUniquePropertyMap.erase(oldHash);
|
||||||
|
} else {
|
||||||
|
mnUniquePropertyMap = mnUniquePropertyMap.set(oldHash, std::make_pair(dmn->proTxHash, p->second - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
void UpdateUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue, const T& newValue)
|
||||||
|
{
|
||||||
|
if (oldValue == newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DeleteUniqueProperty(dmn, oldValue);
|
||||||
|
AddUniqueProperty(dmn, newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDeterministicMNListDiff
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint256 prevBlockHash;
|
||||||
|
uint256 blockHash;
|
||||||
|
int nHeight{-1};
|
||||||
|
std::map<uint256, CDeterministicMNCPtr> addedMNs;
|
||||||
|
std::map<uint256, CDeterministicMNStateCPtr> updatedMNs;
|
||||||
|
std::set<uint256> removedMns;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||||
|
{
|
||||||
|
READWRITE(prevBlockHash);
|
||||||
|
READWRITE(blockHash);
|
||||||
|
READWRITE(nHeight);
|
||||||
|
READWRITE(addedMNs);
|
||||||
|
READWRITE(updatedMNs);
|
||||||
|
READWRITE(removedMns);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool HasChanges() const
|
||||||
|
{
|
||||||
|
return !addedMNs.empty() || !updatedMNs.empty() || !removedMns.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDeterministicMNManager
|
||||||
|
{
|
||||||
|
static const int SNAPSHOT_LIST_PERIOD = 576; // once per day
|
||||||
|
static const int LISTS_CACHE_SIZE = 576;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CCriticalSection cs;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CEvoDB& evoDb;
|
||||||
|
|
||||||
|
std::map<uint256, CDeterministicMNList> mnListsCache;
|
||||||
|
int tipHeight{-1};
|
||||||
|
uint256 tipBlockHash;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CDeterministicMNManager(CEvoDB& _evoDb);
|
||||||
|
|
||||||
|
bool ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state);
|
||||||
|
bool UndoBlock(const CBlock& block, const CBlockIndex* pindex);
|
||||||
|
|
||||||
|
void UpdatedBlockTip(const CBlockIndex *pindex);
|
||||||
|
|
||||||
|
// the returned list will not contain the correct block hash (we can't know it yet as the coinbase TX is not updated yet)
|
||||||
|
bool BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state, CDeterministicMNList& mnListRet);
|
||||||
|
|
||||||
|
CDeterministicMNList GetListForBlock(const uint256& blockHash);
|
||||||
|
CDeterministicMNList GetListAtChainTip();
|
||||||
|
|
||||||
|
CDeterministicMNCPtr GetMN(const uint256& blockHash, const uint256& proTxHash);
|
||||||
|
bool HasValidMNAtBlock(const uint256& blockHash, const uint256& proTxHash);
|
||||||
|
bool HasValidMNAtChainTip(const uint256& proTxHash);
|
||||||
|
|
||||||
|
bool IsDeterministicMNsSporkActive(int nHeight = -1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateSpork15Value();
|
||||||
|
int64_t GetSpork15Value();
|
||||||
|
void CleanupCache(int nHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CDeterministicMNManager* deterministicMNManager;
|
||||||
|
|
||||||
|
#endif//DASH_DETERMINISTICMNS_H
|
13
src/evo/evodb.cpp
Normal file
13
src/evo/evodb.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "evodb.h"
|
||||||
|
|
||||||
|
CEvoDB* evoDb;
|
||||||
|
|
||||||
|
CEvoDB::CEvoDB(size_t nCacheSize, bool fMemory, bool fWipe) :
|
||||||
|
db(GetDataDir() / "evodb", nCacheSize, fMemory, fWipe),
|
||||||
|
dbTransaction(db)
|
||||||
|
{
|
||||||
|
}
|
64
src/evo/evodb.h
Normal file
64
src/evo/evodb.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef DASH_EVODB_H
|
||||||
|
#define DASH_EVODB_H
|
||||||
|
|
||||||
|
#include "dbwrapper.h"
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
|
class CEvoDB
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CCriticalSection cs;
|
||||||
|
CDBWrapper db;
|
||||||
|
CDBTransaction dbTransaction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CEvoDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||||
|
|
||||||
|
std::unique_ptr<CScopedDBTransaction> BeginTransaction()
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
auto t = CScopedDBTransaction::Begin(dbTransaction);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
bool Read(const K& key, V& value)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
return dbTransaction.Read(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
void Write(const K& key, const V& value)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
dbTransaction.Write(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
bool Exists(const K& key)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
return dbTransaction.Exists(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
void Erase(const K& key)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
dbTransaction.Erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDBWrapper& GetRawDB()
|
||||||
|
{
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CEvoDB* evoDb;
|
||||||
|
|
||||||
|
#endif//DASH_EVODB_H
|
158
src/evo/providertx.cpp
Normal file
158
src/evo/providertx.cpp
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "providertx.h"
|
||||||
|
#include "specialtx.h"
|
||||||
|
#include "deterministicmns.h"
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "clientversion.h"
|
||||||
|
#include "streams.h"
|
||||||
|
#include "messagesigner.h"
|
||||||
|
#include "chainparams.h"
|
||||||
|
#include "validation.h"
|
||||||
|
#include "univalue.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "script/standard.h"
|
||||||
|
#include "base58.h"
|
||||||
|
|
||||||
|
template <typename ProTx>
|
||||||
|
static bool CheckService(const uint256& proTxHash, const ProTx& proTx, const CBlockIndex* pindexPrev, CValidationState& state)
|
||||||
|
{
|
||||||
|
if (proTx.nProtocolVersion < MIN_PROTX_PROTO_VERSION || proTx.nProtocolVersion > MAX_PROTX_PROTO_VERSION)
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-proto-version");
|
||||||
|
|
||||||
|
if (!proTx.addr.IsValid())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
||||||
|
if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !proTx.addr.IsRoutable())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
||||||
|
|
||||||
|
if (!proTx.addr.IsIPv4())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr");
|
||||||
|
|
||||||
|
if (pindexPrev) {
|
||||||
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash());
|
||||||
|
if (mnList.HasUniqueProperty(proTx.addr) && mnList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTxHash) {
|
||||||
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ProTx>
|
||||||
|
static bool CheckInputsHashAndSig(const CTransaction &tx, const ProTx& proTx, const CKeyID &keyID, CValidationState& state)
|
||||||
|
{
|
||||||
|
uint256 inputsHash = CalcTxInputsHash(tx);
|
||||||
|
if (inputsHash != proTx.inputsHash)
|
||||||
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash");
|
||||||
|
|
||||||
|
std::string strError;
|
||||||
|
if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError))
|
||||||
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs_main);
|
||||||
|
|
||||||
|
CProRegTx ptx;
|
||||||
|
if (!GetTxPayload(tx, ptx))
|
||||||
|
return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload");
|
||||||
|
|
||||||
|
if (ptx.nVersion > CProRegTx::CURRENT_VERSION)
|
||||||
|
return state.DoS(100, false, REJECT_INVALID, "bad-protx-version");
|
||||||
|
|
||||||
|
if (ptx.nCollateralIndex >= tx.vout.size())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index");
|
||||||
|
if (tx.vout[ptx.nCollateralIndex].nValue != 1000 * COIN)
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
|
||||||
|
if (ptx.keyIDOwner.IsNull() || ptx.keyIDOperator.IsNull() || ptx.keyIDVoting.IsNull())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null");
|
||||||
|
// we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list)
|
||||||
|
if (!ptx.scriptPayout.IsPayToPublicKeyHash())
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee");
|
||||||
|
|
||||||
|
// This is a temporary restriction that will be lifted later
|
||||||
|
// It is required while we are transitioning from the old MN list to the deterministic list
|
||||||
|
if (tx.vout[ptx.nCollateralIndex].scriptPubKey != ptx.scriptPayout)
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral");
|
||||||
|
|
||||||
|
// It's allowed to set addr/protocolVersion to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later
|
||||||
|
// If any of both is set, it must be valid however
|
||||||
|
if ((ptx.addr != CService() || ptx.nProtocolVersion != 0) && !CheckService(tx.GetHash(), ptx, pindexPrev, state))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ptx.nOperatorReward > 10000)
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward");
|
||||||
|
|
||||||
|
if (pindexPrev) {
|
||||||
|
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash());
|
||||||
|
if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.keyIDOperator)) {
|
||||||
|
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexPrev->nHeight)) {
|
||||||
|
if (ptx.keyIDOwner != ptx.keyIDOperator || ptx.keyIDOwner != ptx.keyIDVoting) {
|
||||||
|
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CProRegTx::ToString() const
|
||||||
|
{
|
||||||
|
CTxDestination dest;
|
||||||
|
std::string payee = "unknown";
|
||||||
|
if (ExtractDestination(scriptPayout, dest)) {
|
||||||
|
payee = CBitcoinAddress(dest).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return strprintf("CProRegTx(nVersion=%d, nProtocolVersion=%d, nCollateralIndex=%d, addr=%s, nOperatorReward=%f, keyIDOwner=%s, keyIDOperator=%s, keyIDVoting=%s, scriptPayout=%s)",
|
||||||
|
nVersion, nProtocolVersion, nCollateralIndex, addr.ToString(), (double)nOperatorReward / 100, keyIDOwner.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), payee);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CProRegTx::ToJson(UniValue& obj) const
|
||||||
|
{
|
||||||
|
obj.clear();
|
||||||
|
obj.setObject();
|
||||||
|
obj.push_back(Pair("version", nVersion));
|
||||||
|
obj.push_back(Pair("protocolVersion", nProtocolVersion));
|
||||||
|
obj.push_back(Pair("collateralIndex", (int)nCollateralIndex));
|
||||||
|
obj.push_back(Pair("service", addr.ToString(false)));
|
||||||
|
obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString()));
|
||||||
|
obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString()));
|
||||||
|
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
|
||||||
|
|
||||||
|
CTxDestination dest;
|
||||||
|
if (ExtractDestination(scriptPayout, dest)) {
|
||||||
|
CBitcoinAddress bitcoinAddress(dest);
|
||||||
|
obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString()));
|
||||||
|
}
|
||||||
|
obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100));
|
||||||
|
|
||||||
|
obj.push_back(Pair("inputsHash", inputsHash.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsProTxCollateral(const CTransaction& tx, uint32_t n)
|
||||||
|
{
|
||||||
|
return GetProTxCollateralIndex(tx) == n;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetProTxCollateralIndex(const CTransaction& tx)
|
||||||
|
{
|
||||||
|
if (tx.nVersion < 3 || tx.nType != TRANSACTION_PROVIDER_REGISTER)
|
||||||
|
return (uint32_t) - 1;
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(tx, proTx))
|
||||||
|
assert(false);
|
||||||
|
return proTx.nCollateralIndex;
|
||||||
|
}
|
65
src/evo/providertx.h
Normal file
65
src/evo/providertx.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef DASH_PROVIDERTX_H
|
||||||
|
#define DASH_PROVIDERTX_H
|
||||||
|
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "consensus/validation.h"
|
||||||
|
|
||||||
|
#include "netaddress.h"
|
||||||
|
#include "pubkey.h"
|
||||||
|
|
||||||
|
class CBlockIndex;
|
||||||
|
class UniValue;
|
||||||
|
|
||||||
|
class CProRegTx
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint16_t CURRENT_VERSION = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint16_t nVersion{CURRENT_VERSION}; // message version
|
||||||
|
int32_t nProtocolVersion{0};
|
||||||
|
uint32_t nCollateralIndex{(uint32_t) - 1};
|
||||||
|
CService addr;
|
||||||
|
CKeyID keyIDOwner;
|
||||||
|
CKeyID keyIDOperator;
|
||||||
|
CKeyID keyIDVoting;
|
||||||
|
uint16_t nOperatorReward{0};
|
||||||
|
CScript scriptPayout;
|
||||||
|
uint256 inputsHash; // replay protection
|
||||||
|
std::vector<unsigned char> vchSig;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||||
|
{
|
||||||
|
READWRITE(nVersion);
|
||||||
|
READWRITE(nProtocolVersion);
|
||||||
|
READWRITE(nCollateralIndex);
|
||||||
|
READWRITE(addr);
|
||||||
|
READWRITE(keyIDOwner);
|
||||||
|
READWRITE(keyIDOperator);
|
||||||
|
READWRITE(keyIDVoting);
|
||||||
|
READWRITE(*(CScriptBase*)(&scriptPayout));
|
||||||
|
READWRITE(nOperatorReward);
|
||||||
|
READWRITE(inputsHash);
|
||||||
|
if (!(s.GetType() & SER_GETHASH)) {
|
||||||
|
READWRITE(vchSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString() const;
|
||||||
|
void ToJson(UniValue& obj) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
|
||||||
|
|
||||||
|
bool IsProTxCollateral(const CTransaction& tx, uint32_t n);
|
||||||
|
uint32_t GetProTxCollateralIndex(const CTransaction& tx);
|
||||||
|
|
||||||
|
#endif//DASH_PROVIDERTX_H
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2017 The Dash Core developers
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@ -11,6 +11,7 @@
|
|||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
|
|
||||||
#include "specialtx.h"
|
#include "specialtx.h"
|
||||||
|
#include "deterministicmns.h"
|
||||||
|
|
||||||
bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
||||||
{
|
{
|
||||||
@ -24,6 +25,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (tx.nType) {
|
switch (tx.nType) {
|
||||||
|
case TRANSACTION_PROVIDER_REGISTER:
|
||||||
|
return CheckProRegTx(tx, pindexPrev, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
|
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
|
||||||
@ -35,6 +38,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
switch (tx.nType) {
|
switch (tx.nType) {
|
||||||
|
case TRANSACTION_PROVIDER_REGISTER:
|
||||||
|
return true; // handled in batches per block
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-tx-type");
|
return state.DoS(100, false, REJECT_INVALID, "bad-tx-type");
|
||||||
@ -46,6 +51,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
switch (tx.nType) {
|
switch (tx.nType) {
|
||||||
|
case TRANSACTION_PROVIDER_REGISTER:
|
||||||
|
return true; // handled in batches per block
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -61,6 +68,9 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!deterministicMNManager->ProcessBlock(block, pindex->pprev, state))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +81,10 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
if (!UndoSpecialTx(tx, pindex))
|
if (!UndoSpecialTx(tx, pindex))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!deterministicMNManager->UndoBlock(block, pindex))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2017 The Dash Core developers
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
@ -493,6 +493,8 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingMast
|
|||||||
CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CKeyID());
|
CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CKeyID());
|
||||||
if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) {
|
if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) {
|
||||||
strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n";
|
strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n";
|
||||||
|
} else if (err == CMasternode::COLLATERAL_UTXO_NOT_PROTX) {
|
||||||
|
strError = "Masternode UTXO is not a ProTx, missing masternode=" + strOutpoint + "\n";
|
||||||
} else if (err == CMasternode::COLLATERAL_INVALID_AMOUNT) {
|
} else if (err == CMasternode::COLLATERAL_INVALID_AMOUNT) {
|
||||||
strError = "Masternode UTXO should have 1000 DASH, missing masternode=" + strOutpoint + "\n";
|
strError = "Masternode UTXO should have 1000 DASH, missing masternode=" + strOutpoint + "\n";
|
||||||
} else if (err == CMasternode::COLLATERAL_INVALID_PUBKEY) {
|
} else if (err == CMasternode::COLLATERAL_INVALID_PUBKEY) {
|
||||||
|
12
src/init.cpp
12
src/init.cpp
@ -66,6 +66,8 @@
|
|||||||
#include "spork.h"
|
#include "spork.h"
|
||||||
#include "warnings.h"
|
#include "warnings.h"
|
||||||
|
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -293,6 +295,10 @@ void PrepareShutdown()
|
|||||||
pcoinsdbview = NULL;
|
pcoinsdbview = NULL;
|
||||||
delete pblocktree;
|
delete pblocktree;
|
||||||
pblocktree = NULL;
|
pblocktree = NULL;
|
||||||
|
delete deterministicMNManager;
|
||||||
|
deterministicMNManager = NULL;
|
||||||
|
delete evoDb;
|
||||||
|
evoDb = NULL;
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
if (pwalletMain)
|
if (pwalletMain)
|
||||||
@ -1630,6 +1636,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
nTotalCache -= nCoinDBCache;
|
nTotalCache -= nCoinDBCache;
|
||||||
nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
|
nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
|
||||||
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
||||||
|
int64_t nEvoDbCache = 1024 * 1024 * 16; // TODO
|
||||||
LogPrintf("Cache configuration:\n");
|
LogPrintf("Cache configuration:\n");
|
||||||
LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
|
LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
|
||||||
LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
|
LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
|
||||||
@ -1652,11 +1659,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
delete pcoinsdbview;
|
delete pcoinsdbview;
|
||||||
delete pcoinscatcher;
|
delete pcoinscatcher;
|
||||||
delete pblocktree;
|
delete pblocktree;
|
||||||
|
delete deterministicMNManager;
|
||||||
|
delete evoDb;
|
||||||
|
|
||||||
|
evoDb = new CEvoDB(nEvoDbCache, false, fReindex || fReindexChainState);
|
||||||
|
|
||||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
|
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
|
||||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
|
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
|
||||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||||
|
deterministicMNManager = new CDeterministicMNManager(*evoDb);
|
||||||
|
|
||||||
if (fReindex) {
|
if (fReindex) {
|
||||||
pblocktree->WriteReindexing(true);
|
pblocktree->WriteReindexing(true);
|
||||||
|
@ -125,6 +125,22 @@ CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outp
|
|||||||
return COLLATERAL_INVALID_PUBKEY;
|
return COLLATERAL_INVALID_PUBKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CTransactionRef tx;
|
||||||
|
uint256 hashBlock;
|
||||||
|
if (!GetTransaction(outpoint.hash, tx, Params().GetConsensus(), hashBlock, true)) {
|
||||||
|
// should not happen
|
||||||
|
return COLLATERAL_UTXO_NOT_FOUND;
|
||||||
|
}
|
||||||
|
if (tx->nType != TRANSACTION_PROVIDER_REGISTER) {
|
||||||
|
assert(mapBlockIndex.count(hashBlock));
|
||||||
|
|
||||||
|
CBlockIndex *pindex = mapBlockIndex[hashBlock];
|
||||||
|
if (VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE) {
|
||||||
|
LogPrintf("CMasternode::CheckCollateral -- ERROR: Collateral of masternode %s was created after DIP3 activation and is not a ProTx\n", outpoint.ToStringShort());
|
||||||
|
return COLLATERAL_UTXO_NOT_PROTX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nHeightRet = coin.nHeight;
|
nHeightRet = coin.nHeight;
|
||||||
return COLLATERAL_OK;
|
return COLLATERAL_OK;
|
||||||
}
|
}
|
||||||
@ -554,6 +570,11 @@ bool CMasternodeBroadcast::CheckOutpoint(int& nDos)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err == COLLATERAL_UTXO_NOT_PROTX) {
|
||||||
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should be a ProTx, masternode=%s\n", outpoint.ToStringShort());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (err == COLLATERAL_INVALID_AMOUNT) {
|
if (err == COLLATERAL_INVALID_AMOUNT) {
|
||||||
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should have 1000 DASH, masternode=%s\n", outpoint.ToStringShort());
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should have 1000 DASH, masternode=%s\n", outpoint.ToStringShort());
|
||||||
nDos = 33;
|
nDos = 33;
|
||||||
|
@ -190,7 +190,8 @@ public:
|
|||||||
COLLATERAL_OK,
|
COLLATERAL_OK,
|
||||||
COLLATERAL_UTXO_NOT_FOUND,
|
COLLATERAL_UTXO_NOT_FOUND,
|
||||||
COLLATERAL_INVALID_AMOUNT,
|
COLLATERAL_INVALID_AMOUNT,
|
||||||
COLLATERAL_INVALID_PUBKEY
|
COLLATERAL_INVALID_PUBKEY,
|
||||||
|
COLLATERAL_UTXO_NOT_PROTX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
/** Transaction types */
|
/** Transaction types */
|
||||||
enum {
|
enum {
|
||||||
TRANSACTION_NORMAL = 0,
|
TRANSACTION_NORMAL = 0,
|
||||||
|
TRANSACTION_PROVIDER_REGISTER = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include "univalue.h"
|
#include "univalue.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
@ -47,8 +49,10 @@ void RPCNestedTests::rpcNestedTests()
|
|||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
ForceSetArg("-datadir", path);
|
ForceSetArg("-datadir", path);
|
||||||
//mempool.setSanityCheck(1.0);
|
//mempool.setSanityCheck(1.0);
|
||||||
|
evoDb = new CEvoDB(1 << 20, true, true);
|
||||||
pblocktree = new CBlockTreeDB(1 << 20, true);
|
pblocktree = new CBlockTreeDB(1 << 20, true);
|
||||||
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||||
|
deterministicMNManager = new CDeterministicMNManager(*evoDb);
|
||||||
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
||||||
InitBlockIndex(chainparams);
|
InitBlockIndex(chainparams);
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,8 @@ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC);
|
|||||||
void RegisterMasternodeRPCCommands(CRPCTable &tableRPC);
|
void RegisterMasternodeRPCCommands(CRPCTable &tableRPC);
|
||||||
/** Register governance RPC commands */
|
/** Register governance RPC commands */
|
||||||
void RegisterGovernanceRPCCommands(CRPCTable &tableRPC);
|
void RegisterGovernanceRPCCommands(CRPCTable &tableRPC);
|
||||||
|
/** Register Evo RPC commands */
|
||||||
|
void RegisterEvoRPCCommands(CRPCTable &tableRPC);
|
||||||
|
|
||||||
static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
|
static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
|
||||||
{
|
{
|
||||||
@ -33,6 +35,7 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
|
|||||||
RegisterRawTransactionRPCCommands(t);
|
RegisterRawTransactionRPCCommands(t);
|
||||||
RegisterMasternodeRPCCommands(t);
|
RegisterMasternodeRPCCommands(t);
|
||||||
RegisterGovernanceRPCCommands(t);
|
RegisterGovernanceRPCCommands(t);
|
||||||
|
RegisterEvoRPCCommands(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
413
src/rpc/rpcevo.cpp
Normal file
413
src/rpc/rpcevo.cpp
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
// Copyright (c) 2018 The Dash Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "base58.h"
|
||||||
|
#include "consensus/validation.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "messagesigner.h"
|
||||||
|
#include "rpc/server.h"
|
||||||
|
#include "utilmoneystr.h"
|
||||||
|
#include "validation.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
#include "wallet/wallet.h"
|
||||||
|
#endif//ENABLE_WALLET
|
||||||
|
|
||||||
|
#include "netbase.h"
|
||||||
|
|
||||||
|
#include "evo/specialtx.h"
|
||||||
|
#include "evo/providertx.h"
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
extern UniValue signrawtransaction(const JSONRPCRequest& request);
|
||||||
|
extern UniValue sendrawtransaction(const JSONRPCRequest& request);
|
||||||
|
#endif//ENABLE_WALLET
|
||||||
|
|
||||||
|
// Allows to specify Dash address or priv key. In case of Dash address, the priv key is taken from the wallet
|
||||||
|
static CKey ParsePrivKey(const std::string &strKeyOrAddress, bool allowAddresses = true) {
|
||||||
|
CBitcoinAddress address;
|
||||||
|
if (allowAddresses && address.SetString(strKeyOrAddress) && address.IsValid()) {
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
CKeyID keyId;
|
||||||
|
CKey key;
|
||||||
|
if (!address.GetKeyID(keyId) || !pwalletMain->GetKey(keyId, key))
|
||||||
|
throw std::runtime_error(strprintf("non-wallet or invalid address %s", strKeyOrAddress));
|
||||||
|
return key;
|
||||||
|
#else//ENABLE_WALLET
|
||||||
|
throw std::runtime_error("addresses not supported in no-wallet builds");
|
||||||
|
#endif//ENABLE_WALLET
|
||||||
|
}
|
||||||
|
|
||||||
|
CBitcoinSecret secret;
|
||||||
|
if (!secret.SetString(strKeyOrAddress) || !secret.IsValid())
|
||||||
|
throw std::runtime_error(strprintf("invalid priv-key/address %s", strKeyOrAddress));
|
||||||
|
return secret.GetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CKeyID ParsePubKeyIDFromAddress(const std::string& strAddress, const std::string& paramName)
|
||||||
|
{
|
||||||
|
CBitcoinAddress address(strAddress);
|
||||||
|
CKeyID keyID;
|
||||||
|
if (!address.IsValid() || !address.GetKeyID(keyID))
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be a valid P2PKH address, not %s", paramName, strAddress));
|
||||||
|
return keyID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
|
||||||
|
template<typename SpecialTxPayload>
|
||||||
|
static void FundSpecialTx(CMutableTransaction& tx, SpecialTxPayload payload)
|
||||||
|
{
|
||||||
|
// resize so that fee calculation is correct
|
||||||
|
payload.vchSig.resize(65);
|
||||||
|
|
||||||
|
CDataStream ds(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ds << payload;
|
||||||
|
tx.vExtraPayload.assign(ds.begin(), ds.end());
|
||||||
|
|
||||||
|
static CTxOut dummyTxOut(0, CScript() << OP_RETURN);
|
||||||
|
bool dummyTxOutAdded = false;
|
||||||
|
if (tx.vout.empty()) {
|
||||||
|
// add dummy txout as FundTransaction requires at least one output
|
||||||
|
tx.vout.emplace_back(dummyTxOut);
|
||||||
|
dummyTxOutAdded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAmount nFee;
|
||||||
|
CFeeRate feeRate = CFeeRate(0);
|
||||||
|
int nChangePos = -1;
|
||||||
|
std::string strFailReason;
|
||||||
|
std::set<int> setSubtractFeeFromOutputs;
|
||||||
|
if (!pwalletMain->FundTransaction(tx, nFee, false, feeRate, nChangePos, strFailReason, false, false, setSubtractFeeFromOutputs, true, CNoDestination()))
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
|
||||||
|
|
||||||
|
if (dummyTxOutAdded && tx.vout.size() > 1) {
|
||||||
|
// FundTransaction added a change output, so we don't need the dummy txout anymore
|
||||||
|
// Removing it results in slight overpayment of fees, but we ignore this for now (as it's a very low amount)
|
||||||
|
auto it = std::find(tx.vout.begin(), tx.vout.end(), dummyTxOut);
|
||||||
|
assert(it != tx.vout.end());
|
||||||
|
tx.vout.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SpecialTxPayload>
|
||||||
|
static void SignSpecialTxPayload(const CMutableTransaction& tx, SpecialTxPayload& payload, const CKey& key)
|
||||||
|
{
|
||||||
|
payload.inputsHash = CalcTxInputsHash(tx);
|
||||||
|
payload.vchSig.clear();
|
||||||
|
|
||||||
|
uint256 hash = ::SerializeHash(payload);
|
||||||
|
if (!CHashSigner::SignHash(hash, key, payload.vchSig)) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to sign special tx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SignAndSendSpecialTx(const CMutableTransaction& tx)
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CValidationState state;
|
||||||
|
if (!CheckSpecialTx(tx, NULL, state))
|
||||||
|
throw std::runtime_error(FormatStateMessage(state));
|
||||||
|
|
||||||
|
CDataStream ds(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ds << tx;
|
||||||
|
|
||||||
|
JSONRPCRequest signReqeust;
|
||||||
|
signReqeust.params.setArray();
|
||||||
|
signReqeust.params.push_back(HexStr(ds.begin(), ds.end()));
|
||||||
|
UniValue signResult = signrawtransaction(signReqeust);
|
||||||
|
|
||||||
|
JSONRPCRequest sendRequest;
|
||||||
|
sendRequest.params.setArray();
|
||||||
|
sendRequest.params.push_back(signResult["hex"].get_str());
|
||||||
|
return sendrawtransaction(sendRequest).get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void protx_register_help()
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"protx register \"collateralAddress\" collateralAmount \"ipAndPort\" protocolVersion \"ownerKeyAddr\" \"operatorKeyAddr\" \"votingKeyAddr\" operatorReward \"payoutAddress\"\n"
|
||||||
|
"\nCreates and sends a ProTx to the network. The resulting transaction will move the specified amount\n"
|
||||||
|
"to the address specified by collateralAddress and will then function as the collateral of your\n"
|
||||||
|
"masternode.\n"
|
||||||
|
"A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n"
|
||||||
|
"is fully deployed.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. \"collateralAddress\" (string, required) The dash address to send the collateral to.\n"
|
||||||
|
" Must be a P2PKH address.\n"
|
||||||
|
"2. \"collateralAmount\" (numeric or string, required) The collateral amount.\n"
|
||||||
|
" Must be exactly 1000 Dash.\n"
|
||||||
|
"3. \"ipAndPort\" (string, required) IP and port in the form \"IP:PORT\".\n"
|
||||||
|
" Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.\n"
|
||||||
|
"4. \"protocolVersion\" (numeric, required) The protocol version of your masternode.\n"
|
||||||
|
" Can be 0 to default to the clients protocol version.\n"
|
||||||
|
"5. \"ownerKeyAddr\" (string, required) The owner key used for payee updates and proposal voting.\n"
|
||||||
|
" The private key belonging to this address be known in your wallet. The address must\n"
|
||||||
|
" be unused and must differ from the collateralAddress\n"
|
||||||
|
"6. \"operatorKeyAddr\" (string, required) The operator key address. The private key does not have to be known by your wallet.\n"
|
||||||
|
" It has to match the private key which is later used when operating the masternode.\n"
|
||||||
|
" If set to \"0\" or an empty string, ownerAddr will be used.\n"
|
||||||
|
"7. \"votingKeyAddr\" (string, required) The voting key address. The private key does not have to be known by your wallet.\n"
|
||||||
|
" It has to match the private key which is later used when voting on proposals.\n"
|
||||||
|
" If set to \"0\" or an empty string, ownerAddr will be used.\n"
|
||||||
|
"8. \"operatorReward\" (numeric, required) The fraction in %% to share with the operator. If non-zero,\n"
|
||||||
|
" \"ipAndPort\" and \"protocolVersion\" must be zero as well. The value must be between 0 and 100.\n"
|
||||||
|
"9. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n"
|
||||||
|
" Must match \"collateralAddress\"."
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("protx", "register \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\" 1000 \"1.2.3.4:1234\" 0 \"93Fd7XY2zF4q9YKTZUSFxLgp4Xs7MuaMnvY9kpvH7V8oXWqsCC1\" XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue protx_register(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.fHelp || request.params.size() != 10)
|
||||||
|
protx_register_help();
|
||||||
|
|
||||||
|
CBitcoinAddress collateralAddress(request.params[1].get_str());
|
||||||
|
if (!collateralAddress.IsValid())
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[1].get_str()));
|
||||||
|
CScript collateralScript = GetScriptForDestination(collateralAddress.Get());
|
||||||
|
|
||||||
|
CAmount collateralAmount;
|
||||||
|
if (!ParseMoney(request.params[2].get_str(), collateralAmount))
|
||||||
|
throw std::runtime_error(strprintf("invalid collateral amount %s", request.params[2].get_str()));
|
||||||
|
if (collateralAmount != 1000 * COIN)
|
||||||
|
throw std::runtime_error(strprintf("invalid collateral amount %d. only 1000 DASH is supported at the moment", collateralAmount));
|
||||||
|
|
||||||
|
CTxOut collateralTxOut(collateralAmount, collateralScript);
|
||||||
|
|
||||||
|
CMutableTransaction tx;
|
||||||
|
tx.nVersion = 3;
|
||||||
|
tx.nType = TRANSACTION_PROVIDER_REGISTER;
|
||||||
|
tx.vout.emplace_back(collateralTxOut);
|
||||||
|
|
||||||
|
CProRegTx ptx;
|
||||||
|
ptx.nVersion = CProRegTx::CURRENT_VERSION;
|
||||||
|
|
||||||
|
if (request.params[3].get_str() != "0" && request.params[3].get_str() != "") {
|
||||||
|
if (!Lookup(request.params[3].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false))
|
||||||
|
throw std::runtime_error(strprintf("invalid network address %s", request.params[3].get_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptx.nProtocolVersion = ParseInt32V(request.params[4], "protocolVersion");
|
||||||
|
if (ptx.nProtocolVersion == 0 && ptx.addr != CService())
|
||||||
|
ptx.nProtocolVersion = PROTOCOL_VERSION;
|
||||||
|
|
||||||
|
CKey keyOwner = ParsePrivKey(request.params[5].get_str(), true);
|
||||||
|
CKeyID keyIDOperator = keyOwner.GetPubKey().GetID();
|
||||||
|
CKeyID keyIDVoting = keyOwner.GetPubKey().GetID();
|
||||||
|
if (request.params[6].get_str() != "0" && request.params[6].get_str() != "") {
|
||||||
|
keyIDOperator = ParsePubKeyIDFromAddress(request.params[6].get_str(), "operator address");
|
||||||
|
}
|
||||||
|
if (request.params[7].get_str() != "0" && request.params[7].get_str() != "") {
|
||||||
|
keyIDVoting = ParsePubKeyIDFromAddress(request.params[7].get_str(), "voting address");
|
||||||
|
}
|
||||||
|
|
||||||
|
double operatorReward = ParseDoubleV(request.params[8], "operatorReward");
|
||||||
|
if (operatorReward < 0 || operatorReward > 100)
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0 and 100");
|
||||||
|
ptx.nOperatorReward = (uint16_t)(operatorReward * 100);
|
||||||
|
|
||||||
|
CBitcoinAddress payoutAddress(request.params[9].get_str());
|
||||||
|
if (!payoutAddress.IsValid())
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[9].get_str()));
|
||||||
|
|
||||||
|
ptx.keyIDOwner = keyOwner.GetPubKey().GetID();
|
||||||
|
ptx.keyIDOperator = keyIDOperator;
|
||||||
|
ptx.keyIDVoting = keyIDVoting;
|
||||||
|
ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get());
|
||||||
|
|
||||||
|
FundSpecialTx(tx, ptx);
|
||||||
|
|
||||||
|
uint32_t collateralIndex = (uint32_t) - 1;
|
||||||
|
for (uint32_t i = 0; i < tx.vout.size(); i++) {
|
||||||
|
if (tx.vout[i] == collateralTxOut) {
|
||||||
|
collateralIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(collateralIndex != (uint32_t) - 1);
|
||||||
|
ptx.nCollateralIndex = collateralIndex;
|
||||||
|
|
||||||
|
SignSpecialTxPayload(tx, ptx, keyOwner);
|
||||||
|
SetTxPayload(tx, ptx);
|
||||||
|
|
||||||
|
return SignAndSendSpecialTx(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void protx_list_help()
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"protx list (\"type\")\n"
|
||||||
|
"\nLists all ProTxs in your wallet or on-chain, depending on the given type. If \"type\" is not\n"
|
||||||
|
"specified, it defaults to \"wallet\". All types have the optional argument \"detailed\" which if set to\n"
|
||||||
|
"\"true\" will result in a detailed list to be returned. If set to \"false\", only the hashes of the ProTx\n"
|
||||||
|
"will be returned.\n"
|
||||||
|
"\nAvailable types:\n"
|
||||||
|
" wallet (detailed) - List only ProTx which are found in your wallet. This will also include ProTx which\n"
|
||||||
|
" failed PoSe verfication\n"
|
||||||
|
" valid (height) (detailed) - List only ProTx which are active/valid at the given chain height. If height is not\n"
|
||||||
|
" specified, it defaults to the current chain-tip\n"
|
||||||
|
" registered (height) (detaileD) - List all ProTx which are registered at the given chain height. If height is not\n"
|
||||||
|
" specified, it defaults to the current chain-tip. This will also include ProTx\n"
|
||||||
|
" which failed PoSe verification at that height\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckWalletOwnsScript(const CScript& script) {
|
||||||
|
CTxDestination dest;
|
||||||
|
if (ExtractDestination(script, dest)) {
|
||||||
|
if ((boost::get<CKeyID>(&dest) && pwalletMain->HaveKey(*boost::get<CKeyID>(&dest))) || (boost::get<CScriptID>(&dest) && pwalletMain->HaveCScript(*boost::get<CScriptID>(&dest)))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue BuildDMNListEntry(const CDeterministicMNCPtr& dmn, bool detailed)
|
||||||
|
{
|
||||||
|
if (!detailed)
|
||||||
|
return dmn->proTxHash.ToString();
|
||||||
|
|
||||||
|
UniValue o(UniValue::VOBJ);
|
||||||
|
|
||||||
|
dmn->ToJson(o);
|
||||||
|
|
||||||
|
int confirmations = GetUTXOConfirmations(COutPoint(dmn->proTxHash, dmn->nCollateralIndex));
|
||||||
|
o.push_back(Pair("confirmations", confirmations));
|
||||||
|
|
||||||
|
bool hasOwnerKey = pwalletMain->HaveKey(dmn->pdmnState->keyIDOwner);
|
||||||
|
bool hasOperatorKey = pwalletMain->HaveKey(dmn->pdmnState->keyIDOperator);
|
||||||
|
bool hasVotingKey = pwalletMain->HaveKey(dmn->pdmnState->keyIDVoting);
|
||||||
|
|
||||||
|
bool ownsCollateral = false;
|
||||||
|
CTransactionRef collateralTx;
|
||||||
|
uint256 tmpHashBlock;
|
||||||
|
if (GetTransaction(dmn->proTxHash, collateralTx, Params().GetConsensus(), tmpHashBlock)) {
|
||||||
|
ownsCollateral = CheckWalletOwnsScript(collateralTx->vout[dmn->nCollateralIndex].scriptPubKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue walletObj(UniValue::VOBJ);
|
||||||
|
walletObj.push_back(Pair("hasOwnerKey", hasOwnerKey));
|
||||||
|
walletObj.push_back(Pair("hasOperatorKey", hasOperatorKey));
|
||||||
|
walletObj.push_back(Pair("hasVotingKey", hasVotingKey));
|
||||||
|
walletObj.push_back(Pair("ownsCollateral", ownsCollateral));
|
||||||
|
walletObj.push_back(Pair("ownsPayeeScript", CheckWalletOwnsScript(dmn->pdmnState->scriptPayout)));
|
||||||
|
walletObj.push_back(Pair("ownsOperatorRewardScript", CheckWalletOwnsScript(dmn->pdmnState->scriptOperatorPayout)));
|
||||||
|
o.push_back(Pair("wallet", walletObj));
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue protx_list(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.fHelp)
|
||||||
|
protx_list_help();
|
||||||
|
|
||||||
|
std::string type = "wallet";
|
||||||
|
if (request.params.size() > 1)
|
||||||
|
type = request.params[1].get_str();
|
||||||
|
|
||||||
|
UniValue ret(UniValue::VARR);
|
||||||
|
|
||||||
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||||
|
|
||||||
|
if (type == "wallet") {
|
||||||
|
if (request.params.size() > 3)
|
||||||
|
protx_list_help();
|
||||||
|
|
||||||
|
bool detailed = request.params.size() > 2 ? ParseBoolV(request.params[2], "detailed") : false;
|
||||||
|
|
||||||
|
std::vector<COutPoint> vOutpts;
|
||||||
|
pwalletMain->ListProTxCoins(vOutpts);
|
||||||
|
std::set<uint256> setOutpts;
|
||||||
|
for (const auto& outpt : vOutpts) {
|
||||||
|
setOutpts.emplace(outpt.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dmn : deterministicMNManager->GetListAtChainTip().all_range()) {
|
||||||
|
if (setOutpts.count(dmn->proTxHash) ||
|
||||||
|
pwalletMain->HaveKey(dmn->pdmnState->keyIDOwner) ||
|
||||||
|
pwalletMain->HaveKey(dmn->pdmnState->keyIDOperator) ||
|
||||||
|
pwalletMain->HaveKey(dmn->pdmnState->keyIDVoting) ||
|
||||||
|
CheckWalletOwnsScript(dmn->pdmnState->scriptPayout) ||
|
||||||
|
CheckWalletOwnsScript(dmn->pdmnState->scriptOperatorPayout)) {
|
||||||
|
ret.push_back(BuildDMNListEntry(dmn, detailed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == "valid" || type == "registered") {
|
||||||
|
if (request.params.size() > 4)
|
||||||
|
protx_list_help();
|
||||||
|
|
||||||
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
int height = request.params.size() > 2 ? ParseInt32V(request.params[2], "height") : chainActive.Height();
|
||||||
|
if (height < 1 || height > chainActive.Height())
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid height specified");
|
||||||
|
|
||||||
|
bool detailed = request.params.size() > 3 ? ParseBoolV(request.params[3], "detailed") : false;
|
||||||
|
|
||||||
|
CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(chainActive[height]->GetBlockHash());
|
||||||
|
CDeterministicMNList::range_type range;
|
||||||
|
|
||||||
|
if (type == "valid") {
|
||||||
|
range = mnList.valid_range();
|
||||||
|
} else if (type == "registered") {
|
||||||
|
range = mnList.all_range();
|
||||||
|
}
|
||||||
|
for (const auto& dmn : range) {
|
||||||
|
ret.push_back(BuildDMNListEntry(dmn, detailed));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid type specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue protx(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.params.empty()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"protx \"command\" ...\n"
|
||||||
|
"Set of commands to execute ProTx related actions.\n"
|
||||||
|
"To get help on individual commands, use \"help protx command\".\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. \"command\" (string, required) The command to execute\n"
|
||||||
|
"\nAvailable commands:\n"
|
||||||
|
" register - Create and send ProTx to network\n"
|
||||||
|
" list - List ProTxs\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command = request.params[0].get_str();
|
||||||
|
|
||||||
|
if (command == "register") {
|
||||||
|
return protx_register(request);
|
||||||
|
} else if (command == "list") {
|
||||||
|
return protx_list(request);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("invalid command: " + command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif//ENABLE_WALLET
|
||||||
|
|
||||||
|
static const CRPCCommand commands[] =
|
||||||
|
{ // category name actor (function) okSafeMode
|
||||||
|
// --------------------- ------------------------ ----------------------- ----------
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
// these require the wallet to be enabled to fund the transactions
|
||||||
|
{ "evo", "protx", &protx, false, {} },
|
||||||
|
#endif//ENABLE_WALLET
|
||||||
|
};
|
||||||
|
|
||||||
|
void RegisterEvoRPCCommands(CRPCTable &tableRPC)
|
||||||
|
{
|
||||||
|
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
|
||||||
|
tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
|
||||||
|
}
|
@ -25,6 +25,7 @@ std::map<int, int64_t> mapSporkDefaults = {
|
|||||||
{SPORK_10_MASTERNODE_PAY_UPDATED_NODES, 4070908800ULL}, // OFF
|
{SPORK_10_MASTERNODE_PAY_UPDATED_NODES, 4070908800ULL}, // OFF
|
||||||
{SPORK_12_RECONSIDER_BLOCKS, 0}, // 0 BLOCKS
|
{SPORK_12_RECONSIDER_BLOCKS, 0}, // 0 BLOCKS
|
||||||
{SPORK_14_REQUIRE_SENTINEL_FLAG, 4070908800ULL}, // OFF
|
{SPORK_14_REQUIRE_SENTINEL_FLAG, 4070908800ULL}, // OFF
|
||||||
|
{SPORK_15_DETERMINISTIC_MNS_ENABLED, 4070908800ULL}, // OFF
|
||||||
};
|
};
|
||||||
|
|
||||||
void CSporkManager::Clear()
|
void CSporkManager::Clear()
|
||||||
@ -182,6 +183,7 @@ int CSporkManager::GetSporkIDByName(const std::string& strName)
|
|||||||
if (strName == "SPORK_10_MASTERNODE_PAY_UPDATED_NODES") return SPORK_10_MASTERNODE_PAY_UPDATED_NODES;
|
if (strName == "SPORK_10_MASTERNODE_PAY_UPDATED_NODES") return SPORK_10_MASTERNODE_PAY_UPDATED_NODES;
|
||||||
if (strName == "SPORK_12_RECONSIDER_BLOCKS") return SPORK_12_RECONSIDER_BLOCKS;
|
if (strName == "SPORK_12_RECONSIDER_BLOCKS") return SPORK_12_RECONSIDER_BLOCKS;
|
||||||
if (strName == "SPORK_14_REQUIRE_SENTINEL_FLAG") return SPORK_14_REQUIRE_SENTINEL_FLAG;
|
if (strName == "SPORK_14_REQUIRE_SENTINEL_FLAG") return SPORK_14_REQUIRE_SENTINEL_FLAG;
|
||||||
|
if (strName == "SPORK_15_DETERMINISTIC_MNS_ENABLED") return SPORK_15_DETERMINISTIC_MNS_ENABLED;
|
||||||
|
|
||||||
LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
|
LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
|
||||||
return -1;
|
return -1;
|
||||||
@ -199,6 +201,7 @@ std::string CSporkManager::GetSporkNameByID(int nSporkID)
|
|||||||
case SPORK_10_MASTERNODE_PAY_UPDATED_NODES: return "SPORK_10_MASTERNODE_PAY_UPDATED_NODES";
|
case SPORK_10_MASTERNODE_PAY_UPDATED_NODES: return "SPORK_10_MASTERNODE_PAY_UPDATED_NODES";
|
||||||
case SPORK_12_RECONSIDER_BLOCKS: return "SPORK_12_RECONSIDER_BLOCKS";
|
case SPORK_12_RECONSIDER_BLOCKS: return "SPORK_12_RECONSIDER_BLOCKS";
|
||||||
case SPORK_14_REQUIRE_SENTINEL_FLAG: return "SPORK_14_REQUIRE_SENTINEL_FLAG";
|
case SPORK_14_REQUIRE_SENTINEL_FLAG: return "SPORK_14_REQUIRE_SENTINEL_FLAG";
|
||||||
|
case SPORK_15_DETERMINISTIC_MNS_ENABLED: return "SPORK_15_DETERMINISTIC_MNS_ENABLED";
|
||||||
default:
|
default:
|
||||||
LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
|
LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
|
@ -26,9 +26,10 @@ static const int SPORK_9_SUPERBLOCKS_ENABLED = 10008;
|
|||||||
static const int SPORK_10_MASTERNODE_PAY_UPDATED_NODES = 10009;
|
static const int SPORK_10_MASTERNODE_PAY_UPDATED_NODES = 10009;
|
||||||
static const int SPORK_12_RECONSIDER_BLOCKS = 10011;
|
static const int SPORK_12_RECONSIDER_BLOCKS = 10011;
|
||||||
static const int SPORK_14_REQUIRE_SENTINEL_FLAG = 10013;
|
static const int SPORK_14_REQUIRE_SENTINEL_FLAG = 10013;
|
||||||
|
static const int SPORK_15_DETERMINISTIC_MNS_ENABLED = 10014;
|
||||||
|
|
||||||
static const int SPORK_START = SPORK_2_INSTANTSEND_ENABLED;
|
static const int SPORK_START = SPORK_2_INSTANTSEND_ENABLED;
|
||||||
static const int SPORK_END = SPORK_14_REQUIRE_SENTINEL_FLAG;
|
static const int SPORK_END = SPORK_15_DETERMINISTIC_MNS_ENABLED;
|
||||||
|
|
||||||
extern std::map<int, int64_t> mapSporkDefaults;
|
extern std::map<int, int64_t> mapSporkDefaults;
|
||||||
extern CSporkManager sporkManager;
|
extern CSporkManager sporkManager;
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include "test/testutil.h"
|
#include "test/testutil.h"
|
||||||
|
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
@ -65,8 +67,10 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
|||||||
boost::filesystem::create_directories(pathTemp);
|
boost::filesystem::create_directories(pathTemp);
|
||||||
ForceSetArg("-datadir", pathTemp.string());
|
ForceSetArg("-datadir", pathTemp.string());
|
||||||
mempool.setSanityCheck(1.0);
|
mempool.setSanityCheck(1.0);
|
||||||
|
evoDb = new CEvoDB(1 << 20, true, true);
|
||||||
pblocktree = new CBlockTreeDB(1 << 20, true);
|
pblocktree = new CBlockTreeDB(1 << 20, true);
|
||||||
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||||
|
deterministicMNManager = new CDeterministicMNManager(*evoDb);
|
||||||
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
||||||
InitBlockIndex(chainparams);
|
InitBlockIndex(chainparams);
|
||||||
{
|
{
|
||||||
@ -89,8 +93,10 @@ TestingSetup::~TestingSetup()
|
|||||||
threadGroup.join_all();
|
threadGroup.join_all();
|
||||||
UnloadBlockIndex();
|
UnloadBlockIndex();
|
||||||
delete pcoinsTip;
|
delete pcoinsTip;
|
||||||
|
delete deterministicMNManager;
|
||||||
delete pcoinsdbview;
|
delete pcoinsdbview;
|
||||||
delete pblocktree;
|
delete pblocktree;
|
||||||
|
delete evoDb;
|
||||||
boost::filesystem::remove_all(pathTemp);
|
boost::filesystem::remove_all(pathTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
|
||||||
|
#include "evo/specialtx.h"
|
||||||
|
#include "evo/providertx.h"
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||||
CAmount _inChainInputValue,
|
CAmount _inChainInputValue,
|
||||||
@ -438,6 +441,16 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
vTxHashes.emplace_back(hash, newit);
|
vTxHashes.emplace_back(hash, newit);
|
||||||
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
||||||
|
|
||||||
|
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(tx, proTx)) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
mapProTxRegisterAddresses.emplace(proTx.addr, tx.GetHash());
|
||||||
|
mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash());
|
||||||
|
mapProTxPubKeyIDs.emplace(proTx.keyIDOperator, tx.GetHash());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,6 +626,16 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|||||||
} else
|
} else
|
||||||
vTxHashes.clear();
|
vTxHashes.clear();
|
||||||
|
|
||||||
|
if (it->GetTx().nVersion >= 3 && it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(it->GetTx(), proTx)) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
mapProTxRegisterAddresses.erase(proTx.addr);
|
||||||
|
mapProTxPubKeyIDs.erase(proTx.keyIDOwner);
|
||||||
|
mapProTxPubKeyIDs.erase(proTx.keyIDOperator);
|
||||||
|
}
|
||||||
|
|
||||||
totalTxSize -= it->GetTxSize();
|
totalTxSize -= it->GetTxSize();
|
||||||
cachedInnerUsage -= it->DynamicMemoryUsage();
|
cachedInnerUsage -= it->DynamicMemoryUsage();
|
||||||
cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children);
|
cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children);
|
||||||
@ -739,6 +762,36 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
|
||||||
|
{
|
||||||
|
if (tx.nType != TRANSACTION_PROVIDER_REGISTER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(tx, proTx)) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapProTxRegisterAddresses.count(proTx.addr)) {
|
||||||
|
uint256 conflictHash = mapProTxRegisterAddresses[proTx.addr];
|
||||||
|
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
|
||||||
|
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mapProTxPubKeyIDs.count(proTx.keyIDOwner)) {
|
||||||
|
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOwner];
|
||||||
|
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
|
||||||
|
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mapProTxPubKeyIDs.count(proTx.keyIDOperator)) {
|
||||||
|
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOperator];
|
||||||
|
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
|
||||||
|
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
|
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
|
||||||
*/
|
*/
|
||||||
@ -765,6 +818,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
|||||||
RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK);
|
RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK);
|
||||||
}
|
}
|
||||||
removeConflicts(*tx);
|
removeConflicts(*tx);
|
||||||
|
removeProTxConflicts(*tx);
|
||||||
ClearPrioritisation(tx->GetHash());
|
ClearPrioritisation(tx->GetHash());
|
||||||
}
|
}
|
||||||
lastRollingFeeUpdate = GetTime();
|
lastRollingFeeUpdate = GetTime();
|
||||||
@ -776,6 +830,8 @@ void CTxMemPool::_clear()
|
|||||||
mapLinks.clear();
|
mapLinks.clear();
|
||||||
mapTx.clear();
|
mapTx.clear();
|
||||||
mapNextTx.clear();
|
mapNextTx.clear();
|
||||||
|
mapProTxRegisterAddresses.clear();
|
||||||
|
mapProTxPubKeyIDs.clear();
|
||||||
totalTxSize = 0;
|
totalTxSize = 0;
|
||||||
cachedInnerUsage = 0;
|
cachedInnerUsage = 0;
|
||||||
lastRollingFeeUpdate = GetTime();
|
lastRollingFeeUpdate = GetTime();
|
||||||
@ -1013,6 +1069,16 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
|
|||||||
return GetInfo(i);
|
return GetInfo(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
|
||||||
|
LOCK(cs);
|
||||||
|
if (tx.nVersion < 3 || tx.nType != TRANSACTION_PROVIDER_REGISTER)
|
||||||
|
return false;
|
||||||
|
CProRegTx proTx;
|
||||||
|
if (!GetTxPayload(tx, proTx))
|
||||||
|
assert(false);
|
||||||
|
return mapProTxRegisterAddresses.count(proTx.addr) || mapProTxPubKeyIDs.count(proTx.keyIDOwner) || mapProTxPubKeyIDs.count(proTx.keyIDOperator);
|
||||||
|
}
|
||||||
|
|
||||||
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
|
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
|
#include "netaddress.h"
|
||||||
|
|
||||||
#undef foreach
|
#undef foreach
|
||||||
#include "boost/multi_index_container.hpp"
|
#include "boost/multi_index_container.hpp"
|
||||||
@ -534,6 +535,9 @@ private:
|
|||||||
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
|
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
|
||||||
mapSpentIndexInserted mapSpentInserted;
|
mapSpentIndexInserted mapSpentInserted;
|
||||||
|
|
||||||
|
std::map<CService, uint256> mapProTxRegisterAddresses;
|
||||||
|
std::map<CKeyID, uint256> mapProTxPubKeyIDs;
|
||||||
|
|
||||||
void UpdateParent(txiter entry, txiter parent, bool add);
|
void UpdateParent(txiter entry, txiter parent, bool add);
|
||||||
void UpdateChild(txiter entry, txiter child, bool add);
|
void UpdateChild(txiter entry, txiter child, bool add);
|
||||||
|
|
||||||
@ -576,6 +580,7 @@ public:
|
|||||||
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
|
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
|
||||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||||
void removeConflicts(const CTransaction &tx);
|
void removeConflicts(const CTransaction &tx);
|
||||||
|
void removeProTxConflicts(const CTransaction &tx);
|
||||||
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
|
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
@ -684,6 +689,8 @@ public:
|
|||||||
TxMempoolInfo info(const uint256& hash) const;
|
TxMempoolInfo info(const uint256& hash) const;
|
||||||
std::vector<TxMempoolInfo> infoAll() const;
|
std::vector<TxMempoolInfo> infoAll() const;
|
||||||
|
|
||||||
|
bool existsProviderTxConflict(const CTransaction &tx) const;
|
||||||
|
|
||||||
/** Estimate fee rate needed to get into the next nBlocks
|
/** Estimate fee rate needed to get into the next nBlocks
|
||||||
* If no answer can be given at nBlocks, return an estimate
|
* If no answer can be given at nBlocks, return an estimate
|
||||||
* at the lowest number of blocks where one can be given
|
* at the lowest number of blocks where one can be given
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
#include "masternode-payments.h"
|
#include "masternode-payments.h"
|
||||||
|
|
||||||
#include "evo/specialtx.h"
|
#include "evo/specialtx.h"
|
||||||
|
#include "evo/providertx.h"
|
||||||
|
#include "evo/deterministicmns.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -570,7 +572,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
|||||||
if (fDIP0003Active_context) {
|
if (fDIP0003Active_context) {
|
||||||
// check version 3 transaction types
|
// check version 3 transaction types
|
||||||
if (tx.nVersion >= 3) {
|
if (tx.nVersion >= 3) {
|
||||||
if (tx.nType != TRANSACTION_NORMAL) {
|
if (tx.nType != TRANSACTION_NORMAL &&
|
||||||
|
tx.nType != TRANSACTION_PROVIDER_REGISTER) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type");
|
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type");
|
||||||
}
|
}
|
||||||
if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL)
|
if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL)
|
||||||
@ -648,6 +651,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||||||
if (fRequireStandard && !IsStandardTx(tx, reason))
|
if (fRequireStandard && !IsStandardTx(tx, reason))
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
|
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
|
||||||
|
|
||||||
|
if (pool.existsProviderTxConflict(tx)) {
|
||||||
|
return state.DoS(0, false, REJECT_DUPLICATE, "protx-dup");
|
||||||
|
}
|
||||||
|
|
||||||
// Only accept nLockTime-using transactions that can be mined in the next
|
// Only accept nLockTime-using transactions that can be mined in the next
|
||||||
// block; we don't want our mempool filled up with transactions that can't
|
// block; we don't want our mempool filled up with transactions that can't
|
||||||
// be mined yet.
|
// be mined yet.
|
||||||
@ -2631,11 +2638,15 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
|
|||||||
// Apply the block atomically to the chain state.
|
// Apply the block atomically to the chain state.
|
||||||
int64_t nStart = GetTimeMicros();
|
int64_t nStart = GetTimeMicros();
|
||||||
{
|
{
|
||||||
|
auto dbTx = evoDb->BeginTransaction();
|
||||||
|
|
||||||
CCoinsViewCache view(pcoinsTip);
|
CCoinsViewCache view(pcoinsTip);
|
||||||
if (DisconnectBlock(block, state, pindexDelete, view) != DISCONNECT_OK)
|
if (DisconnectBlock(block, state, pindexDelete, view) != DISCONNECT_OK)
|
||||||
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
||||||
bool flushed = view.Flush();
|
bool flushed = view.Flush();
|
||||||
assert(flushed);
|
assert(flushed);
|
||||||
|
bool committed = dbTx->Commit();
|
||||||
|
assert(committed);
|
||||||
}
|
}
|
||||||
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
||||||
// Write the chain state to disk, if necessary.
|
// Write the chain state to disk, if necessary.
|
||||||
@ -2710,6 +2721,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
|
|||||||
int64_t nTime3;
|
int64_t nTime3;
|
||||||
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
|
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
|
||||||
{
|
{
|
||||||
|
auto dbTx = evoDb->BeginTransaction();
|
||||||
|
|
||||||
CCoinsViewCache view(pcoinsTip);
|
CCoinsViewCache view(pcoinsTip);
|
||||||
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
|
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
|
||||||
GetMainSignals().BlockChecked(blockConnecting, state);
|
GetMainSignals().BlockChecked(blockConnecting, state);
|
||||||
@ -2722,6 +2735,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
|
|||||||
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
|
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
|
||||||
bool flushed = view.Flush();
|
bool flushed = view.Flush();
|
||||||
assert(flushed);
|
assert(flushed);
|
||||||
|
bool committed = dbTx->Commit();
|
||||||
|
assert(committed);
|
||||||
}
|
}
|
||||||
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
|
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
|
||||||
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
|
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
|
||||||
@ -3729,6 +3744,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
|||||||
indexDummy.pprev = pindexPrev;
|
indexDummy.pprev = pindexPrev;
|
||||||
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
||||||
|
|
||||||
|
// begin tx and let it rollback
|
||||||
|
auto dbTx = evoDb->BeginTransaction();
|
||||||
|
|
||||||
// NOTE: CheckBlockHeader is called by CheckBlock
|
// NOTE: CheckBlockHeader is called by CheckBlock
|
||||||
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
|
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
|
||||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
|
||||||
@ -4082,6 +4100,9 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
|
|||||||
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
|
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// begin tx and let it rollback
|
||||||
|
auto dbTx = evoDb->BeginTransaction();
|
||||||
|
|
||||||
// Verify blocks in the best chain
|
// Verify blocks in the best chain
|
||||||
if (nCheckDepth <= 0)
|
if (nCheckDepth <= 0)
|
||||||
nCheckDepth = 1000000000; // suffices until the year 19000
|
nCheckDepth = 1000000000; // suffices until the year 19000
|
||||||
|
@ -44,4 +44,10 @@ static const int DIP0001_PROTOCOL_VERSION = 70208;
|
|||||||
//! short-id-based block download starts with this version
|
//! short-id-based block download starts with this version
|
||||||
static const int SHORT_IDS_BLOCKS_VERSION = 70209;
|
static const int SHORT_IDS_BLOCKS_VERSION = 70209;
|
||||||
|
|
||||||
|
//! minimum ProTx proto version
|
||||||
|
static const int MIN_PROTX_PROTO_VERSION = 70211;
|
||||||
|
|
||||||
|
//! maximum ProTx proto version (slightly higher then current PROTOCOL_VERSION to ensure masternodes can upgrade)
|
||||||
|
static const int MAX_PROTX_PROTO_VERSION = PROTOCOL_VERSION + 2;
|
||||||
|
|
||||||
#endif // BITCOIN_VERSION_H
|
#endif // BITCOIN_VERSION_H
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#include "privatesend-client.h"
|
#include "privatesend-client.h"
|
||||||
#include "spork.h"
|
#include "spork.h"
|
||||||
|
|
||||||
|
#include "evo/providertx.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
@ -1116,9 +1118,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
|||||||
wtxIn.hashBlock.ToString());
|
wtxIn.hashBlock.ToString());
|
||||||
}
|
}
|
||||||
AddToSpends(hash);
|
AddToSpends(hash);
|
||||||
|
|
||||||
|
uint32_t proTxCollateralIdx = GetProTxCollateralIndex(*wtx.tx);
|
||||||
for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||||
if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
|
if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
|
||||||
setWalletUTXO.insert(COutPoint(hash, i));
|
setWalletUTXO.insert(COutPoint(hash, i));
|
||||||
|
if (i == proTxCollateralIdx) {
|
||||||
|
LockCoin(COutPoint(hash, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3968,9 +3975,13 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
|
|||||||
{
|
{
|
||||||
LOCK2(cs_main, cs_wallet);
|
LOCK2(cs_main, cs_wallet);
|
||||||
for (auto& pair : mapWallet) {
|
for (auto& pair : mapWallet) {
|
||||||
|
uint32_t proTxCollateralIdx = GetProTxCollateralIndex(*pair.second.tx);
|
||||||
for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
|
for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
|
||||||
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {
|
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {
|
||||||
setWalletUTXO.insert(COutPoint(pair.first, i));
|
setWalletUTXO.insert(COutPoint(pair.first, i));
|
||||||
|
if (i == proTxCollateralIdx) {
|
||||||
|
LockCoin(COutPoint(pair.first, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4625,6 +4636,19 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CWallet::ListProTxCoins(std::vector<COutPoint>& vOutpts)
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs_wallet);
|
||||||
|
for (const auto &o : setWalletUTXO) {
|
||||||
|
if (mapWallet.count(o.hash)) {
|
||||||
|
const auto &p = mapWallet[o.hash];
|
||||||
|
if (IsProTxCollateral(*p.tx, o.n)) {
|
||||||
|
vOutpts.emplace_back(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @} */ // end of Actions
|
/** @} */ // end of Actions
|
||||||
|
|
||||||
class CAffectedKeysVisitor : public boost::static_visitor<void> {
|
class CAffectedKeysVisitor : public boost::static_visitor<void> {
|
||||||
|
@ -834,6 +834,7 @@ public:
|
|||||||
void UnlockCoin(const COutPoint& output);
|
void UnlockCoin(const COutPoint& output);
|
||||||
void UnlockAllCoins();
|
void UnlockAllCoins();
|
||||||
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
|
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
|
||||||
|
void ListProTxCoins(std::vector<COutPoint>& vOutpts);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keystore implementation
|
* keystore implementation
|
||||||
|
Loading…
Reference in New Issue
Block a user