Merge pull request #2250 from codablock/pr_dip3_othertxs
Implementation of remaining DIP3 special transactions
This commit is contained in:
commit
11df4f24de
@ -32,9 +32,9 @@ std::string CDeterministicMNState::ToString() const
|
||||
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)",
|
||||
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight,
|
||||
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
|
||||
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("PoSeRevivedHeight", nPoSeRevivedHeight));
|
||||
obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight));
|
||||
obj.push_back(Pair("revocationReason", nRevocationReason));
|
||||
obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString()));
|
||||
obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString()));
|
||||
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
|
||||
@ -400,8 +401,84 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
|
||||
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());
|
||||
LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n",
|
||||
__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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
int nPoSePenalty{0};
|
||||
int nPoSeRevivedHeight{-1};
|
||||
int nPoSeBanHeight{-1};
|
||||
uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
|
||||
|
||||
CKeyID keyIDOwner;
|
||||
CKeyID keyIDOperator;
|
||||
@ -63,6 +64,7 @@ public:
|
||||
READWRITE(nPoSePenalty);
|
||||
READWRITE(nPoSeRevivedHeight);
|
||||
READWRITE(nPoSeBanHeight);
|
||||
READWRITE(nRevocationReason);
|
||||
READWRITE(keyIDOwner);
|
||||
READWRITE(keyIDOperator);
|
||||
READWRITE(keyIDVoting);
|
||||
@ -72,6 +74,21 @@ public:
|
||||
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
|
||||
{
|
||||
return nRegisteredHeight == rhs.nRegisteredHeight &&
|
||||
@ -79,6 +96,7 @@ public:
|
||||
nPoSePenalty == rhs.nPoSePenalty &&
|
||||
nPoSeRevivedHeight == rhs.nPoSeRevivedHeight &&
|
||||
nPoSeBanHeight == rhs.nPoSeBanHeight &&
|
||||
nRevocationReason == rhs.nRevocationReason &&
|
||||
keyIDOwner == rhs.keyIDOwner &&
|
||||
keyIDOperator == rhs.keyIDOperator &&
|
||||
keyIDVoting == rhs.keyIDVoting &&
|
||||
|
@ -114,6 +114,132 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
|
||||
if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state))
|
||||
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;
|
||||
}
|
||||
@ -152,6 +278,78 @@ void CProRegTx::ToJson(UniValue& obj) const
|
||||
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)
|
||||
{
|
||||
return GetProTxCollateralIndex(tx) == n;
|
||||
|
@ -57,7 +57,124 @@ public:
|
||||
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 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);
|
||||
uint32_t GetProTxCollateralIndex(const CTransaction& tx);
|
||||
|
@ -27,6 +27,12 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
|
||||
switch (tx.nType) {
|
||||
case TRANSACTION_PROVIDER_REGISTER:
|
||||
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");
|
||||
@ -39,6 +45,9 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
|
||||
|
||||
switch (tx.nType) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -52,6 +61,9 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
|
||||
|
||||
switch (tx.nType) {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
enum {
|
||||
TRANSACTION_NORMAL = 0,
|
||||
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 */
|
||||
|
@ -29,6 +29,9 @@
|
||||
#include "wallet/wallet.h"
|
||||
#endif
|
||||
|
||||
#include "evo/specialtx.h"
|
||||
#include "evo/providertx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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)));
|
||||
}
|
||||
|
||||
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()) {
|
||||
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
|
||||
|
@ -240,6 +240,194 @@ UniValue protx_register(const JSONRPCRequest& request)
|
||||
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()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
@ -382,6 +570,9 @@ UniValue protx(const JSONRPCRequest& request)
|
||||
"\nAvailable commands:\n"
|
||||
" register - Create and send ProTx to network\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);
|
||||
} else if (command == "list") {
|
||||
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 {
|
||||
throw std::runtime_error("invalid command: " + command);
|
||||
}
|
||||
|
@ -446,9 +446,21 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
assert(false);
|
||||
}
|
||||
mapProTxRegisterAddresses.emplace(proTx.addr, tx.GetHash());
|
||||
mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
|
||||
mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, 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;
|
||||
@ -626,14 +638,26 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
||||
} else
|
||||
vTxHashes.clear();
|
||||
|
||||
if (it->GetTx().nVersion >= 3 && it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
if (it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
CProRegTx proTx;
|
||||
if (!GetTxPayload(it->GetTx(), proTx)) {
|
||||
assert(false);
|
||||
}
|
||||
mapProTxRegisterAddresses.erase(proTx.addr);
|
||||
mapProTxAddresses.erase(proTx.addr);
|
||||
mapProTxPubKeyIDs.erase(proTx.keyIDOwner);
|
||||
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();
|
||||
@ -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)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_PROVIDER_REGISTER)
|
||||
return;
|
||||
|
||||
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
CProRegTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (mapProTxRegisterAddresses.count(proTx.addr)) {
|
||||
uint256 conflictHash = mapProTxRegisterAddresses[proTx.addr];
|
||||
if (mapProTxAddresses.count(proTx.addr)) {
|
||||
uint256 conflictHash = mapProTxAddresses[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];
|
||||
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 (mapProTxAddresses.count(proTx.addr)) {
|
||||
uint256 conflictHash = mapProTxAddresses[proTx.addr];
|
||||
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);
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
|
||||
CProUpRegTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -830,7 +872,7 @@ void CTxMemPool::_clear()
|
||||
mapLinks.clear();
|
||||
mapTx.clear();
|
||||
mapNextTx.clear();
|
||||
mapProTxRegisterAddresses.clear();
|
||||
mapProTxAddresses.clear();
|
||||
mapProTxPubKeyIDs.clear();
|
||||
totalTxSize = 0;
|
||||
cachedInnerUsage = 0;
|
||||
@ -1071,12 +1113,25 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
|
||||
|
||||
bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
|
||||
LOCK(cs);
|
||||
if (tx.nVersion < 3 || tx.nType != TRANSACTION_PROVIDER_REGISTER)
|
||||
return false;
|
||||
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
CProRegTx proTx;
|
||||
if (!GetTxPayload(tx, proTx))
|
||||
assert(false);
|
||||
return mapProTxRegisterAddresses.count(proTx.addr) || mapProTxPubKeyIDs.count(proTx.keyIDOwner) || mapProTxPubKeyIDs.count(proTx.keyIDOperator);
|
||||
return mapProTxAddresses.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
|
||||
|
@ -535,7 +535,7 @@ private:
|
||||
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
|
||||
mapSpentIndexInserted mapSpentInserted;
|
||||
|
||||
std::map<CService, uint256> mapProTxRegisterAddresses;
|
||||
std::map<CService, uint256> mapProTxAddresses;
|
||||
std::map<CKeyID, uint256> mapProTxPubKeyIDs;
|
||||
|
||||
void UpdateParent(txiter entry, txiter parent, bool add);
|
||||
@ -580,6 +580,7 @@ public:
|
||||
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
|
||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||
void removeConflicts(const CTransaction &tx);
|
||||
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId);
|
||||
void removeProTxConflicts(const CTransaction &tx);
|
||||
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
|
||||
|
||||
|
@ -573,7 +573,10 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
// check version 3 transaction types
|
||||
if (tx.nVersion >= 3) {
|
||||
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");
|
||||
}
|
||||
if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL)
|
||||
|
Loading…
Reference in New Issue
Block a user