mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Classes, validation and update logic for CProUpRegTX
This commit is contained in:
parent
32951f795c
commit
1c68d11077
@ -400,8 +400,8 @@ 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)) {
|
||||
@ -433,8 +433,32 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
|
||||
newList.UpdateMN(proTx.proTxHash, newState);
|
||||
|
||||
LogPrintf("CDeterministicMNManager::%s -- MN %s updated with addr=%s, nProtocolVersion=%d. height=%d\n",
|
||||
__func__, proTx.proTxHash.ToString(), proTx.addr.ToString(false), proTx.nProtocolVersion, height);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,21 @@ public:
|
||||
READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
|
||||
}
|
||||
|
||||
void ResetOperatorFields()
|
||||
{
|
||||
keyIDOperator.SetNull();
|
||||
addr = CService();
|
||||
nProtocolVersion = 0;
|
||||
scriptOperatorPayout = CScript();
|
||||
revocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
|
||||
}
|
||||
void BanIfNotBanned(int height)
|
||||
{
|
||||
if (nPoSeBanHeight == -1) {
|
||||
nPoSeBanHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const CDeterministicMNState& rhs) const
|
||||
{
|
||||
return nRegisteredHeight == rhs.nRegisteredHeight &&
|
||||
|
@ -154,6 +154,69 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa
|
||||
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;
|
||||
}
|
||||
|
||||
std::string CProRegTx::ToString() const
|
||||
{
|
||||
CTxDestination dest;
|
||||
@ -190,8 +253,14 @@ void CProRegTx::ToJson(UniValue& obj) const
|
||||
|
||||
std::string CProUpServTx::ToString() const
|
||||
{
|
||||
return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s)",
|
||||
nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString());
|
||||
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
|
||||
@ -210,6 +279,34 @@ void CProUpServTx::ToJson(UniValue& obj) const
|
||||
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()));
|
||||
}
|
||||
|
||||
bool IsProTxCollateral(const CTransaction& tx, uint32_t n)
|
||||
{
|
||||
return GetProTxCollateralIndex(tx) == n;
|
||||
|
@ -93,8 +93,46 @@ public:
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
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 IsProTxCollateral(const CTransaction& tx, uint32_t n);
|
||||
uint32_t GetProTxCollateralIndex(const CTransaction& tx);
|
||||
|
@ -29,6 +29,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
|
||||
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);
|
||||
}
|
||||
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
|
||||
@ -42,6 +44,7 @@ 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:
|
||||
return true; // handled in batches per block
|
||||
}
|
||||
|
||||
@ -56,6 +59,7 @@ 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:
|
||||
return true; // handled in batches per block
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ enum {
|
||||
TRANSACTION_NORMAL = 0,
|
||||
TRANSACTION_PROVIDER_REGISTER = 1,
|
||||
TRANSACTION_PROVIDER_UPDATE_SERVICE = 2,
|
||||
TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3,
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
|
@ -144,6 +144,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hashBlock.IsNull()) {
|
||||
|
@ -455,6 +455,12 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
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;
|
||||
@ -646,6 +652,12 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
||||
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();
|
||||
@ -774,6 +786,16 @@ 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) {
|
||||
@ -788,18 +810,8 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
|
||||
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||
}
|
||||
}
|
||||
if (mapProTxPubKeyIDs.count(proTx.keyIDOwner)) {
|
||||
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOwner];
|
||||
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
|
||||
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||
}
|
||||
}
|
||||
if (mapProTxPubKeyIDs.count(proTx.keyIDOperator)) {
|
||||
uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOperator];
|
||||
if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) {
|
||||
removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||
}
|
||||
}
|
||||
removeProTxPubKeyConflicts(tx, proTx.keyIDOwner);
|
||||
removeProTxPubKeyConflicts(tx, proTx.keyIDOperator);
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
CProUpServTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
@ -812,6 +824,13 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1103,7 +1122,14 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
|
||||
CProUpServTx proTx;
|
||||
if (!GetTxPayload(tx, proTx))
|
||||
assert(false);
|
||||
return mapProTxAddresses.count(proTx.addr) && mapProTxAddresses[proTx.addr] != proTx.proTxHash;
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -574,7 +574,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
if (tx.nVersion >= 3) {
|
||||
if (tx.nType != TRANSACTION_NORMAL &&
|
||||
tx.nType != TRANSACTION_PROVIDER_REGISTER &&
|
||||
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE &&
|
||||
tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
|
||||
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