Merge pull request #2246 from codablock/pr_dip3

First part of DIP3 implementation
This commit is contained in:
UdjinM6 2018-08-30 22:24:01 +03:00 committed by GitHub
commit 74629e453d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 2066 additions and 5 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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

View 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
View 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
View 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
View 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
View 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
View 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

View File

@ -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;
} }

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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,
}; };

View File

@ -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 */

View File

@ -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);
{ {

View File

@ -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
View 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]);
}

View File

@ -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";

View File

@ -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;

View File

@ -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);
} }

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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> {

View File

@ -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