From 6ec0d7aea52f9f458f02d8bc28c78dc50ed0b0c6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Mar 2018 12:14:11 +0100 Subject: [PATCH] Classes and basic validation of ProUpServTx --- src/evo/providertx.cpp | 58 ++++++++++++++++++++++++++++ src/evo/providertx.h | 37 ++++++++++++++++++ src/evo/specialtx.cpp | 4 ++ src/primitives/transaction.h | 1 + src/rpc/rawtransaction.cpp | 9 ++++- src/txmempool.cpp | 75 +++++++++++++++++++++++------------- src/txmempool.h | 2 +- src/validation.cpp | 3 +- 8 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index de2a3e3152005..afda06f2f9a37 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -114,6 +114,42 @@ 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; } @@ -152,6 +188,28 @@ void CProRegTx::ToJson(UniValue& obj) const obj.push_back(Pair("inputsHash", inputsHash.ToString())); } +std::string CProUpServTx::ToString() const +{ + return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s)", + nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString()); +} + +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())); +} + bool IsProTxCollateral(const CTransaction& tx, uint32_t n) { return GetProTxCollateralIndex(tx) == n; diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 7c37de3585164..1646a008c37ec 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -57,7 +57,44 @@ class CProRegTx 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 vchSig; + +public: + ADD_SERIALIZE_METHODS; + + template + 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; +}; + bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); bool IsProTxCollateral(const CTransaction& tx, uint32_t n); uint32_t GetProTxCollateralIndex(const CTransaction& tx); diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index 584321a160d51..dd4fa73748b53 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -27,6 +27,8 @@ 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); } return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); @@ -39,6 +41,7 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida switch (tx.nType) { case TRANSACTION_PROVIDER_REGISTER: + case TRANSACTION_PROVIDER_UPDATE_SERVICE: return true; // handled in batches per block } @@ -52,6 +55,7 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) switch (tx.nType) { case TRANSACTION_PROVIDER_REGISTER: + case TRANSACTION_PROVIDER_UPDATE_SERVICE: return true; // handled in batches per block } diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 129a0c473e3b9..fb023bb83df8a 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -15,6 +15,7 @@ enum { TRANSACTION_NORMAL = 0, TRANSACTION_PROVIDER_REGISTER = 1, + TRANSACTION_PROVIDER_UPDATE_SERVICE = 2, }; /** An outpoint - a combination of a transaction hash and an index n into its vout */ diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4a028ff399851..6f35f65367334 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -130,13 +130,20 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) entry.push_back(Pair("extraPayload", HexStr(tx.vExtraPayload))); } - if (tx.nVersion >= 3 && tx.nType == TRANSACTION_PROVIDER_REGISTER) { + 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)); + } } if (!hashBlock.IsNull()) { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 42b6246dd6008..52c10add874e1 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -446,9 +446,15 @@ 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()); } return true; @@ -626,14 +632,20 @@ 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); } totalTxSize -= it->GetTxSize(); @@ -764,30 +776,41 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) void CTxMemPool::removeProTxConflicts(const CTransaction &tx) { - if (tx.nType != TRANSACTION_PROVIDER_REGISTER) - return; - - CProRegTx proTx; - if (!GetTxPayload(tx, proTx)) { - assert(false); - } + if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { + CProRegTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); + } - if (mapProTxRegisterAddresses.count(proTx.addr)) { - uint256 conflictHash = mapProTxRegisterAddresses[proTx.addr]; - if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { - removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); + if (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]; - if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { - removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); + if (mapProTxPubKeyIDs.count(proTx.keyIDOwner)) { + uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOwner]; + if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { + removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); + } } - } - if (mapProTxPubKeyIDs.count(proTx.keyIDOperator)) { - uint256 conflictHash = mapProTxPubKeyIDs[proTx.keyIDOperator]; - if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { - removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); + 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_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); + } } } } @@ -830,7 +853,7 @@ void CTxMemPool::_clear() mapLinks.clear(); mapTx.clear(); mapNextTx.clear(); - mapProTxRegisterAddresses.clear(); + mapProTxAddresses.clear(); mapProTxPubKeyIDs.clear(); totalTxSize = 0; cachedInnerUsage = 0; @@ -1076,7 +1099,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { 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); } CFeeRate CTxMemPool::estimateFee(int nBlocks) const diff --git a/src/txmempool.h b/src/txmempool.h index b741dfd5c3734..6d1c7b76960ce 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -535,7 +535,7 @@ class CTxMemPool typedef std::map > mapSpentIndexInserted; mapSpentIndexInserted mapSpentInserted; - std::map mapProTxRegisterAddresses; + std::map mapProTxAddresses; std::map mapProTxPubKeyIDs; void UpdateParent(txiter entry, txiter parent, bool add); diff --git a/src/validation.cpp b/src/validation.cpp index 8d8f779573dbf..69b7fea778b6b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -573,7 +573,8 @@ 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) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); } if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL)