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();
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 &&
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user