Merge pull request #2250 from codablock/pr_dip3_othertxs

Implementation of remaining DIP3 special transactions
This commit is contained in:
UdjinM6 2018-08-31 18:20:26 +03:00 committed by GitHub
commit 11df4f24de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 753 additions and 39 deletions

View File

@ -32,9 +32,9 @@ std::string CDeterministicMNState::ToString() const
operatorRewardAddress = CBitcoinAddress(dest).ToString(); operatorRewardAddress = CBitcoinAddress(dest).ToString();
} }
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, " return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
"keyIDOwner=%s, keyIDOperator=%s, keyIDVoting=%s, addr=%s, nProtocolVersion=%d, payoutAddress=%s, operatorRewardAddress=%s)", "keyIDOwner=%s, keyIDOperator=%s, keyIDVoting=%s, addr=%s, nProtocolVersion=%d, payoutAddress=%s, operatorRewardAddress=%s)",
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
keyIDOwner.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), nProtocolVersion, payoutAddress, operatorRewardAddress); keyIDOwner.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), nProtocolVersion, payoutAddress, operatorRewardAddress);
} }
@ -47,6 +47,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const
obj.push_back(Pair("PoSePenalty", nPoSePenalty)); obj.push_back(Pair("PoSePenalty", nPoSePenalty));
obj.push_back(Pair("PoSeRevivedHeight", nPoSeRevivedHeight)); obj.push_back(Pair("PoSeRevivedHeight", nPoSeRevivedHeight));
obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight)); obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight));
obj.push_back(Pair("revocationReason", nRevocationReason));
obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString())); obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString()));
obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString())); obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString()));
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString())); obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
@ -400,8 +401,84 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
newList.AddMN(dmn); newList.AddMN(dmn);
LogPrintf("CDeterministicMNManager::%s -- MN %s added to MN list. nHeight=%d, mapCurMNs.size=%d\n", LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n",
__func__, tx.GetHash().ToString(), nHeight, newList.size()); __func__, tx.GetHash().ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash)
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr");
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->addr = proTx.addr;
newState->nProtocolVersion = proTx.nProtocolVersion;
newState->scriptOperatorPayout = proTx.scriptOperatorPayout;
if (newState->nPoSeBanHeight != -1) {
// only revive when all keys are set
if (!newState->keyIDOperator.IsNull() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
newState->nPoSeBanHeight = -1;
newState->nPoSeRevivedHeight = nHeight;
LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n",
__func__, proTx.proTxHash.ToString(), nHeight);
}
}
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->keyIDOperator != proTx.keyIDOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
}
newState->keyIDOperator = proTx.keyIDOperator;
newState->keyIDVoting = proTx.keyIDVoting;
newState->scriptPayout = proTx.scriptPayout;
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
CProUpRevTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
newState->nRevocationReason = proTx.nReason;
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} }
} }

View File

@ -30,6 +30,7 @@ public:
int nPoSePenalty{0}; int nPoSePenalty{0};
int nPoSeRevivedHeight{-1}; int nPoSeRevivedHeight{-1};
int nPoSeBanHeight{-1}; int nPoSeBanHeight{-1};
uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
CKeyID keyIDOwner; CKeyID keyIDOwner;
CKeyID keyIDOperator; CKeyID keyIDOperator;
@ -63,6 +64,7 @@ public:
READWRITE(nPoSePenalty); READWRITE(nPoSePenalty);
READWRITE(nPoSeRevivedHeight); READWRITE(nPoSeRevivedHeight);
READWRITE(nPoSeBanHeight); READWRITE(nPoSeBanHeight);
READWRITE(nRevocationReason);
READWRITE(keyIDOwner); READWRITE(keyIDOwner);
READWRITE(keyIDOperator); READWRITE(keyIDOperator);
READWRITE(keyIDVoting); READWRITE(keyIDVoting);
@ -72,6 +74,21 @@ public:
READWRITE(*(CScriptBase*)(&scriptOperatorPayout)); READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
} }
void ResetOperatorFields()
{
keyIDOperator.SetNull();
addr = CService();
nProtocolVersion = 0;
scriptOperatorPayout = CScript();
nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
}
void BanIfNotBanned(int height)
{
if (nPoSeBanHeight == -1) {
nPoSeBanHeight = height;
}
}
bool operator==(const CDeterministicMNState& rhs) const bool operator==(const CDeterministicMNState& rhs) const
{ {
return nRegisteredHeight == rhs.nRegisteredHeight && return nRegisteredHeight == rhs.nRegisteredHeight &&
@ -79,6 +96,7 @@ public:
nPoSePenalty == rhs.nPoSePenalty && nPoSePenalty == rhs.nPoSePenalty &&
nPoSeRevivedHeight == rhs.nPoSeRevivedHeight && nPoSeRevivedHeight == rhs.nPoSeRevivedHeight &&
nPoSeBanHeight == rhs.nPoSeBanHeight && nPoSeBanHeight == rhs.nPoSeBanHeight &&
nRevocationReason == rhs.nRevocationReason &&
keyIDOwner == rhs.keyIDOwner && keyIDOwner == rhs.keyIDOwner &&
keyIDOperator == rhs.keyIDOperator && keyIDOperator == rhs.keyIDOperator &&
keyIDVoting == rhs.keyIDVoting && keyIDVoting == rhs.keyIDVoting &&

View File

@ -114,6 +114,132 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state)) if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state))
return false; return false;
return true;
}
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
{
AssertLockHeld(cs_main);
CProUpServTx 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 (!CheckService(ptx.proTxHash, ptx, pindexPrev, state))
return false;
if (pindexPrev) {
auto mn = deterministicMNManager->GetMN(pindexPrev->GetBlockHash(), ptx.proTxHash);
if (!mn)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
if (ptx.scriptOperatorPayout != CScript()) {
if (mn->nOperatorReward == 0) {
// don't allow to set operator reward payee in case no operatorReward was set
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
}
// we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list)
if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash())
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
}
// we can only check the signature if pindexPrev != NULL and the MN is known
if (!CheckInputsHashAndSig(tx, ptx, mn->pdmnState->keyIDOperator, state))
return false;
}
return true;
}
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
{
AssertLockHeld(cs_main);
CProUpRegTx 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.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");
CTxDestination payoutDest;
if (!ExtractDestination(ptx.scriptPayout, payoutDest)) {
// should not happen as we checked script types before
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest");
}
if (pindexPrev) {
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash());
auto dmn = mnList.GetMN(ptx.proTxHash);
if (!dmn)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDOperator) || payoutDest == CTxDestination(ptx.keyIDVoting)) {
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
}
// 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
CTransactionRef proRegTx;
uint256 tmpHashBlock;
if (!GetTransaction(ptx.proTxHash, proRegTx, Params().GetConsensus(), tmpHashBlock))
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payee-collateral");
if (proRegTx->vout[dmn->nCollateralIndex].scriptPubKey != ptx.scriptPayout)
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral");
if (mnList.HasUniqueProperty(ptx.keyIDOperator)) {
auto otherDmn = mnList.GetUniquePropertyMN(ptx.keyIDOperator);
if (ptx.proTxHash != otherDmn->proTxHash) {
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
}
}
if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexPrev->nHeight)) {
if (dmn->pdmnState->keyIDOwner != ptx.keyIDOperator || dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) {
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
}
}
if (!CheckInputsHashAndSig(tx, ptx, dmn->pdmnState->keyIDOwner, state))
return false;
}
return true;
}
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
{
AssertLockHeld(cs_main);
CProUpRevTx 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.nReason < CProUpRevTx::REASON_NOT_SPECIFIED || ptx.nReason > CProUpRevTx::REASON_LAST)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-reason");
if (pindexPrev) {
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash());
auto dmn = mnList.GetMN(ptx.proTxHash);
if (!dmn)
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
if (!CheckInputsHashAndSig(tx, ptx, dmn->pdmnState->keyIDOperator, state))
return false;
}
return true; return true;
} }
@ -152,6 +278,78 @@ void CProRegTx::ToJson(UniValue& obj) const
obj.push_back(Pair("inputsHash", inputsHash.ToString())); obj.push_back(Pair("inputsHash", inputsHash.ToString()));
} }
std::string CProUpServTx::ToString() const
{
CTxDestination dest;
std::string payee = "unknown";
if (ExtractDestination(scriptOperatorPayout, dest)) {
payee = CBitcoinAddress(dest).ToString();
}
return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s, operatorPayoutAddress=%s)",
nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString(), payee);
}
void CProUpServTx::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
obj.push_back(Pair("version", nVersion));
obj.push_back(Pair("proTxHash", proTxHash.ToString()));
obj.push_back(Pair("protocolVersion", nProtocolVersion));
obj.push_back(Pair("service", addr.ToString(false)));
CTxDestination dest;
if (ExtractDestination(scriptOperatorPayout, dest)) {
CBitcoinAddress bitcoinAddress(dest);
obj.push_back(Pair("operatorPayoutAddress", bitcoinAddress.ToString()));
}
obj.push_back(Pair("inputsHash", inputsHash.ToString()));
}
std::string CProUpRegTx::ToString() const
{
CTxDestination dest;
std::string payee = "unknown";
if (ExtractDestination(scriptPayout, dest)) {
payee = CBitcoinAddress(dest).ToString();
}
return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, keyIDOperator=%s, keyIDVoting=%s, payoutAddress=%s)",
nVersion, proTxHash.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), payee);
}
void CProUpRegTx::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
obj.push_back(Pair("version", nVersion));
obj.push_back(Pair("proTxHash", proTxHash.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("inputsHash", inputsHash.ToString()));
}
std::string CProUpRevTx::ToString() const
{
return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)",
nVersion, proTxHash.ToString(), nReason);
}
void CProUpRevTx::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
obj.push_back(Pair("version", nVersion));
obj.push_back(Pair("proTxHash", proTxHash.ToString()));
obj.push_back(Pair("reason", (int)nReason));
obj.push_back(Pair("inputsHash", inputsHash.ToString()));
}
bool IsProTxCollateral(const CTransaction& tx, uint32_t n) bool IsProTxCollateral(const CTransaction& tx, uint32_t n)
{ {
return GetProTxCollateralIndex(tx) == n; return GetProTxCollateralIndex(tx) == n;

View File

@ -57,7 +57,124 @@ public:
void ToJson(UniValue& obj) const; void ToJson(UniValue& obj) const;
}; };
class CProUpServTx
{
public:
static const uint16_t CURRENT_VERSION = 1;
public:
uint16_t nVersion{CURRENT_VERSION}; // message version
uint256 proTxHash;
int32_t nProtocolVersion{0};
CService addr;
CScript scriptOperatorPayout;
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(proTxHash);
READWRITE(nProtocolVersion);
READWRITE(addr);
READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
READWRITE(inputsHash);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
}
public:
std::string ToString() const;
void ToJson(UniValue& obj) const;
};
class CProUpRegTx
{
public:
static const uint16_t CURRENT_VERSION = 1;
public:
uint16_t nVersion{CURRENT_VERSION}; // message version
uint256 proTxHash;
CKeyID keyIDOperator;
CKeyID keyIDVoting;
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(proTxHash);
READWRITE(keyIDOperator);
READWRITE(keyIDVoting);
READWRITE(*(CScriptBase*)(&scriptPayout));
READWRITE(inputsHash);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
}
public:
std::string ToString() const;
void ToJson(UniValue& obj) const;
};
class CProUpRevTx
{
public:
static const uint16_t CURRENT_VERSION = 1;
// these are just informational and do not have any effect on the revocation
enum {
REASON_NOT_SPECIFIED = 0,
REASON_TERMINATION_OF_SERVICE = 1,
REASON_COMPROMISED_KEYS = 2,
REASON_CHANGE_OF_KEYS = 3,
REASON_LAST = REASON_CHANGE_OF_KEYS
};
public:
uint16_t nVersion{CURRENT_VERSION}; // message version
uint256 proTxHash;
uint16_t nReason{REASON_NOT_SPECIFIED};
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(proTxHash);
READWRITE(nReason);
READWRITE(inputsHash);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
}
public:
std::string ToString() const;
void ToJson(UniValue& obj) const;
};
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state);
bool IsProTxCollateral(const CTransaction& tx, uint32_t n); bool IsProTxCollateral(const CTransaction& tx, uint32_t n);
uint32_t GetProTxCollateralIndex(const CTransaction& tx); uint32_t GetProTxCollateralIndex(const CTransaction& tx);

View File

@ -27,6 +27,12 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
switch (tx.nType) { switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER: case TRANSACTION_PROVIDER_REGISTER:
return CheckProRegTx(tx, pindexPrev, state); return CheckProRegTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
return CheckProUpServTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
return CheckProUpRegTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return CheckProUpRevTx(tx, pindexPrev, state);
} }
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
@ -39,6 +45,9 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
switch (tx.nType) { switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER: case TRANSACTION_PROVIDER_REGISTER:
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return true; // handled in batches per block return true; // handled in batches per block
} }
@ -52,6 +61,9 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
switch (tx.nType) { switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER: case TRANSACTION_PROVIDER_REGISTER:
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return true; // handled in batches per block return true; // handled in batches per block
} }

View File

@ -15,6 +15,9 @@
enum { enum {
TRANSACTION_NORMAL = 0, TRANSACTION_NORMAL = 0,
TRANSACTION_PROVIDER_REGISTER = 1, TRANSACTION_PROVIDER_REGISTER = 1,
TRANSACTION_PROVIDER_UPDATE_SERVICE = 2,
TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3,
TRANSACTION_PROVIDER_UPDATE_REVOKE = 4,
}; };
/** 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

@ -29,6 +29,9 @@
#include "wallet/wallet.h" #include "wallet/wallet.h"
#endif #endif
#include "evo/specialtx.h"
#include "evo/providertx.h"
#include <stdint.h> #include <stdint.h>
#include <boost/assign/list_of.hpp> #include <boost/assign/list_of.hpp>
@ -127,6 +130,36 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
entry.push_back(Pair("extraPayload", HexStr(tx.vExtraPayload))); entry.push_back(Pair("extraPayload", HexStr(tx.vExtraPayload)));
} }
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx;
if (GetTxPayload(tx, proTx)) {
UniValue proTxObj;
proTx.ToJson(proTxObj);
entry.push_back(Pair("proTx", proTxObj));
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (GetTxPayload(tx, proTx)) {
UniValue proTxObj;
proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpServTx", proTxObj));
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (GetTxPayload(tx, proTx)) {
UniValue proTxObj;
proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpRegTx", proTxObj));
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
CProUpRevTx proTx;
if (GetTxPayload(tx, proTx)) {
UniValue proTxObj;
proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpRevTx", proTxObj));
}
}
if (!hashBlock.IsNull()) { if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex())); entry.push_back(Pair("blockhash", hashBlock.GetHex()));
BlockMap::iterator mi = mapBlockIndex.find(hashBlock); BlockMap::iterator mi = mapBlockIndex.find(hashBlock);

View File

@ -240,6 +240,194 @@ UniValue protx_register(const JSONRPCRequest& request)
return SignAndSendSpecialTx(tx); return SignAndSendSpecialTx(tx);
} }
void protx_update_service_help()
{
throw std::runtime_error(
"protx update_service \"proTxHash\" \"ipAndPort\" protocolVersion (\"operatorPayoutAddress\")\n"
"\nCreates and sends a ProUpServTx to the network. This will update the address and protocol version\n"
"of a masternode. The operator key of the masternode must be known to your wallet.\n"
"If this is done for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.\n"
"\nArguments:\n"
"1. \"proTxHash\" (string, required) The hash of the initial ProRegTx.\n"
"2. \"ipAndPort\" (string, required) IP and port in the form \"IP:PORT\".\n"
" Must be unique on the network.\n"
"3. \"protocolVersion\" (numeric, required) The protocol version of your masternode.\n"
" Can be 0 to default to the clients protocol version\n"
"4. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n"
" Only allowed when the ProRegTx had a non-zero operatorReward value.\n"
"\nExamples:\n"
+ HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 0")
);
}
UniValue protx_update_service(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 4 && request.params.size() != 5))
protx_update_service_help();
CProUpServTx ptx;
ptx.nVersion = CProRegTx::CURRENT_VERSION;
ptx.proTxHash = ParseHashV(request.params[1], "proTxHash");
if (!Lookup(request.params[2].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[3], "protocolVersion");
if (ptx.nProtocolVersion == 0) {
ptx.nProtocolVersion = PROTOCOL_VERSION;
}
if (request.params.size() > 4) {
CBitcoinAddress payoutAddress(request.params[4].get_str());
if (!payoutAddress.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str()));
ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get());
}
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash);
if (!dmn) {
throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString()));
}
CKey keyOperator;
if (!pwalletMain->GetKey(dmn->pdmnState->keyIDOperator, keyOperator)) {
throw std::runtime_error(strprintf("operator key %s not found in your wallet", dmn->pdmnState->keyIDOperator.ToString()));
}
CMutableTransaction tx;
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE;
FundSpecialTx(tx, ptx);
SignSpecialTxPayload(tx, ptx, keyOperator);
SetTxPayload(tx, ptx);
return SignAndSendSpecialTx(tx);
}
void protx_update_registrar_help()
{
throw std::runtime_error(
"protx update_registrar \"proTxHash\" \"operatorKeyAddr\" \"votingKeyAddr\" operatorReward \"payoutAddress\"\n"
"\nCreates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout\n"
"address of the masternode specified by \"proTxHash\".\n"
"The owner key of the masternode must be known to your wallet.\n"
"\nArguments:\n"
"1. \"proTxHash\" (string, required) The hash of the initial ProRegTx.\n"
"2. \"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, the last on-chain operator key of the masternode will be used.\n"
"3. \"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, the last on-chain voting key of the masternode will be used.\n"
"5. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n"
" Must match \"collateralAddress\" of initial ProRegTx.\n"
" If set to \"0\" or an empty string, the last on-chain payout address of the masternode will be used.\n"
"\nExamples:\n"
+ HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"<operatorKeyAddr>\" \"0\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"")
);
}
UniValue protx_update_registrar(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 5)
protx_update_registrar_help();
CProUpRegTx ptx;
ptx.nVersion = CProRegTx::CURRENT_VERSION;
ptx.proTxHash = ParseHashV(request.params[1], "proTxHash");
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash);
if (!dmn) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString()));
}
ptx.keyIDOperator = dmn->pdmnState->keyIDOperator;
ptx.keyIDVoting = dmn->pdmnState->keyIDVoting;
ptx.scriptPayout = dmn->pdmnState->scriptPayout;
if (request.params[2].get_str() != "0" && request.params[2].get_str() != "") {
ptx.keyIDOperator = ParsePubKeyIDFromAddress(request.params[2].get_str(), "operator address");
}
if (request.params[3].get_str() != "0" && request.params[3].get_str() != "") {
ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[3].get_str(), "operator address");
}
CBitcoinAddress payoutAddress(request.params[4].get_str());
if (!payoutAddress.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[4].get_str()));
ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get());
CKey keyOwner;
if (!pwalletMain->GetKey(dmn->pdmnState->keyIDOwner, keyOwner)) {
throw std::runtime_error(strprintf("owner key %s not found in your wallet", dmn->pdmnState->keyIDOwner.ToString()));
}
CMutableTransaction tx;
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_UPDATE_REGISTRAR;
FundSpecialTx(tx, ptx);
SignSpecialTxPayload(tx, ptx, keyOwner);
SetTxPayload(tx, ptx);
return SignAndSendSpecialTx(tx);
}
void protx_revoke_help()
{
throw std::runtime_error(
"protx revoke \"proTxHash\"\n"
"\nCreates and sends a ProUpRevTx to the network. This will revoke the operator key of the masternode and\n"
"put it into the PoSe-banned state. It will also set the service and protocol version fields of the masternode\n"
"to zero. Use this in case your operator key got compromised or you want to stop providing your service\n"
"to the masternode owner.\n"
"The operator key of the masternode must be known to your wallet.\n"
"\nArguments:\n"
"1. \"proTxHash\" (string, required) The hash of the initial ProRegTx.\n"
"2. reason (numeric, optional) The reason for revocation.\n"
"\nExamples:\n"
+ HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"<operatorKeyAddr>\"")
);
}
UniValue protx_revoke(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 2 && request.params.size() != 3))
protx_revoke_help();
CProUpRevTx ptx;
ptx.nVersion = CProRegTx::CURRENT_VERSION;
ptx.proTxHash = ParseHashV(request.params[1], "proTxHash");
if (request.params.size() > 2) {
int32_t nReason = ParseInt32V(request.params[2], "reason");
if (nReason < 0 || nReason >= CProUpRevTx::REASON_LAST)
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid reason %d, must be between 0 and %d", nReason, CProUpRevTx::REASON_LAST));
ptx.nReason = (uint16_t)nReason;
}
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash);
if (!dmn) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString()));
}
CKey keyOperator;
if (!pwalletMain->GetKey(dmn->pdmnState->keyIDOperator, keyOperator)) {
throw std::runtime_error(strprintf("operator key %s not found in your wallet", dmn->pdmnState->keyIDOwner.ToString()));
}
CMutableTransaction tx;
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE;
FundSpecialTx(tx, ptx);
SignSpecialTxPayload(tx, ptx, keyOperator);
SetTxPayload(tx, ptx);
return SignAndSendSpecialTx(tx);
}
void protx_list_help() void protx_list_help()
{ {
throw std::runtime_error( throw std::runtime_error(
@ -380,8 +568,11 @@ UniValue protx(const JSONRPCRequest& request)
"\nArguments:\n" "\nArguments:\n"
"1. \"command\" (string, required) The command to execute\n" "1. \"command\" (string, required) The command to execute\n"
"\nAvailable commands:\n" "\nAvailable commands:\n"
" register - Create and send ProTx to network\n" " register - Create and send ProTx to network\n"
" list - List ProTxs\n" " list - List ProTxs\n"
" update_service - Create and send ProUpServTx to network\n"
" update_registrar - Create and send ProUpRegTx to network\n"
" revoke - Create and send ProUpRevTx to network\n"
); );
} }
@ -391,6 +582,12 @@ UniValue protx(const JSONRPCRequest& request)
return protx_register(request); return protx_register(request);
} else if (command == "list") { } else if (command == "list") {
return protx_list(request); return protx_list(request);
} else if (command == "update_service") {
return protx_update_service(request);
} else if (command == "update_registrar") {
return protx_update_registrar(request);
} else if (command == "revoke") {
return protx_revoke(request);
} else { } else {
throw std::runtime_error("invalid command: " + command); throw std::runtime_error("invalid command: " + command);
} }

View File

@ -446,9 +446,21 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
if (!GetTxPayload(tx, proTx)) { if (!GetTxPayload(tx, proTx)) {
assert(false); assert(false);
} }
mapProTxRegisterAddresses.emplace(proTx.addr, tx.GetHash()); mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash()); mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash());
mapProTxPubKeyIDs.emplace(proTx.keyIDOperator, tx.GetHash()); mapProTxPubKeyIDs.emplace(proTx.keyIDOperator, tx.GetHash());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}
mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}
mapProTxPubKeyIDs.emplace(proTx.keyIDOperator, tx.GetHash());
} }
return true; return true;
@ -626,14 +638,26 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
} else } else
vTxHashes.clear(); vTxHashes.clear();
if (it->GetTx().nVersion >= 3 && it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) { if (it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx; CProRegTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) { if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false); assert(false);
} }
mapProTxRegisterAddresses.erase(proTx.addr); mapProTxAddresses.erase(proTx.addr);
mapProTxPubKeyIDs.erase(proTx.keyIDOwner); mapProTxPubKeyIDs.erase(proTx.keyIDOwner);
mapProTxPubKeyIDs.erase(proTx.keyIDOperator); mapProTxPubKeyIDs.erase(proTx.keyIDOperator);
} else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false);
}
mapProTxAddresses.erase(proTx.addr);
} else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false);
}
mapProTxPubKeyIDs.erase(proTx.keyIDOperator);
} }
totalTxSize -= it->GetTxSize(); totalTxSize -= it->GetTxSize();
@ -762,33 +786,51 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
} }
} }
void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId)
{
if (mapProTxPubKeyIDs.count(keyId)) {
uint256 conflictHash = mapProTxPubKeyIDs[keyId];
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
}
void CTxMemPool::removeProTxConflicts(const CTransaction &tx) void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
{ {
if (tx.nType != TRANSACTION_PROVIDER_REGISTER) if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
return; CProRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}
CProRegTx proTx; if (mapProTxAddresses.count(proTx.addr)) {
if (!GetTxPayload(tx, proTx)) { uint256 conflictHash = mapProTxAddresses[proTx.addr];
assert(false); if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
} removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
}
removeProTxPubKeyConflicts(tx, proTx.keyIDOwner);
removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false);
}
if (mapProTxRegisterAddresses.count(proTx.addr)) { if (mapProTxAddresses.count(proTx.addr)) {
uint256 conflictHash = mapProTxRegisterAddresses[proTx.addr]; uint256 conflictHash = mapProTxAddresses[proTx.addr];
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
}
} }
} } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
if (mapProTxPubKeyIDs.count(proTx.keyIDOwner)) { CProUpRegTx proTx;
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOwner]; if (!GetTxPayload(tx, proTx)) {
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { assert(false);
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);
} }
removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
} }
} }
@ -830,7 +872,7 @@ void CTxMemPool::_clear()
mapLinks.clear(); mapLinks.clear();
mapTx.clear(); mapTx.clear();
mapNextTx.clear(); mapNextTx.clear();
mapProTxRegisterAddresses.clear(); mapProTxAddresses.clear();
mapProTxPubKeyIDs.clear(); mapProTxPubKeyIDs.clear();
totalTxSize = 0; totalTxSize = 0;
cachedInnerUsage = 0; cachedInnerUsage = 0;
@ -1071,12 +1113,25 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
LOCK(cs); LOCK(cs);
if (tx.nVersion < 3 || tx.nType != TRANSACTION_PROVIDER_REGISTER) if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
return false; CProRegTx proTx;
CProRegTx proTx; if (!GetTxPayload(tx, proTx))
if (!GetTxPayload(tx, proTx)) assert(false);
assert(false); return mapProTxAddresses.count(proTx.addr) || mapProTxPubKeyIDs.count(proTx.keyIDOwner) || mapProTxPubKeyIDs.count(proTx.keyIDOperator);
return mapProTxRegisterAddresses.count(proTx.addr) || mapProTxPubKeyIDs.count(proTx.keyIDOwner) || mapProTxPubKeyIDs.count(proTx.keyIDOperator); } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx))
assert(false);
auto it = mapProTxAddresses.find(proTx.addr);
return it != mapProTxAddresses.end() && it->second != proTx.proTxHash;
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx))
assert(false);
auto it = mapProTxPubKeyIDs.find(proTx.keyIDOperator);
return it != mapProTxPubKeyIDs.end() && it->second != proTx.proTxHash;
}
return false;
} }
CFeeRate CTxMemPool::estimateFee(int nBlocks) const CFeeRate CTxMemPool::estimateFee(int nBlocks) const

View File

@ -535,7 +535,7 @@ 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<CService, uint256> mapProTxAddresses;
std::map<CKeyID, uint256> mapProTxPubKeyIDs; std::map<CKeyID, uint256> mapProTxPubKeyIDs;
void UpdateParent(txiter entry, txiter parent, bool add); void UpdateParent(txiter entry, txiter parent, bool add);
@ -580,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 removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId);
void removeProTxConflicts(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);

View File

@ -573,7 +573,10 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
// 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) { tx.nType != TRANSACTION_PROVIDER_REGISTER &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) {
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)