From 958b84ace3ba7ef9ad191f63c769729ef59960eb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Feb 2018 13:36:36 +0100 Subject: [PATCH] Implementation of ProRegTx with basic validation (no processing) --- src/Makefile.am | 2 + src/evo/providertx.cpp | 122 +++++++++++++++++++++++++++++++++++ src/evo/providertx.h | 62 ++++++++++++++++++ src/evo/specialtx.cpp | 6 ++ src/primitives/transaction.h | 1 + src/validation.cpp | 4 +- src/version.h | 6 ++ 7 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/evo/providertx.cpp create mode 100644 src/evo/providertx.h diff --git a/src/Makefile.am b/src/Makefile.am index d4b7d31cb..e93c2a24d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -108,6 +108,7 @@ BITCOIN_CORE_H = \ cuckoocache.h \ evo/evodb.h \ evo/specialtx.h \ + evo/providertx.h \ privatesend.h \ privatesend-client.h \ privatesend-server.h \ @@ -220,6 +221,7 @@ libdash_server_a_SOURCES = \ dsnotificationinterface.cpp \ evo/evodb.cpp \ evo/specialtx.cpp \ + evo/providertx.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp new file mode 100644 index 000000000..6140906ba --- /dev/null +++ b/src/evo/providertx.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "providertx.h" +#include "specialtx.h" + +#include "hash.h" +#include "clientversion.h" +#include "streams.h" +#include "messagesigner.h" +#include "chainparams.h" +#include "validation.h" +#include "univalue.h" +#include "core_io.h" +#include "script/standard.h" +#include "base58.h" + +template +static bool CheckService(const uint256& proTxHash, const ProTx& proTx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (proTx.nProtocolVersion < MIN_PROTX_PROTO_VERSION || proTx.nProtocolVersion > MAX_PROTX_PROTO_VERSION) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-proto-version"); + + if (!proTx.addr.IsValid()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !proTx.addr.IsRoutable()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + + if (!proTx.addr.IsIPv4()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + + return true; +} + +template +static bool CheckInputsHashAndSig(const CTransaction &tx, const ProTx& proTx, const CKeyID &keyID, CValidationState& state) +{ + uint256 inputsHash = CalcTxInputsHash(tx); + if (inputsHash != proTx.inputsHash) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash"); + + std::string strError; + if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError)) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError); + + return true; +} + +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + CProRegTx 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.nCollateralIndex >= tx.vout.size()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index"); + if (tx.vout[ptx.nCollateralIndex].nValue != 1000 * COIN) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); + if (ptx.keyIDOwner.IsNull() || 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"); + + // 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 + if (tx.vout[ptx.nCollateralIndex].scriptPubKey != ptx.scriptPayout) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral"); + + // It's allowed to set addr/protocolVersion to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later + // If any of both is set, it must be valid however + if ((ptx.addr != CService() || ptx.nProtocolVersion != 0) && !CheckService(tx.GetHash(), ptx, pindexPrev, state)) + return false; + + if (ptx.nOperatorReward > 10000) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward"); + + if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state)) + return false; + + return true; +} + +std::string CProRegTx::ToString() const +{ + CTxDestination dest; + std::string payee = "unknown"; + if (ExtractDestination(scriptPayout, dest)) { + payee = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CProRegTx(nVersion=%d, nProtocolVersion=%d, nCollateralIndex=%d, addr=%s, nOperatorReward=%f, keyIDOwner=%s, keyIDOperator=%s, keyIDVoting=%s, scriptPayout=%s)", + nVersion, nProtocolVersion, nCollateralIndex, addr.ToString(), (double)nOperatorReward / 100, keyIDOwner.ToString(), keyIDOperator.ToString(), keyIDVoting.ToString(), payee); +} + +void CProRegTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", nVersion)); + obj.push_back(Pair("protocolVersion", nProtocolVersion)); + obj.push_back(Pair("collateralIndex", (int)nCollateralIndex)); + obj.push_back(Pair("service", addr.ToString(false))); + obj.push_back(Pair("keyIDOwner", keyIDOwner.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("operatorReward", (double)nOperatorReward / 100)); + + obj.push_back(Pair("inputsHash", inputsHash.ToString())); +} diff --git a/src/evo/providertx.h b/src/evo/providertx.h new file mode 100644 index 000000000..6c34f801f --- /dev/null +++ b/src/evo/providertx.h @@ -0,0 +1,62 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DASH_PROVIDERTX_H +#define DASH_PROVIDERTX_H + +#include "primitives/transaction.h" +#include "consensus/validation.h" + +#include "netaddress.h" +#include "pubkey.h" + +class CBlockIndex; +class UniValue; + +class CProRegTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; // message version + int32_t nProtocolVersion{0}; + uint32_t nCollateralIndex{(uint32_t) - 1}; + CService addr; + CKeyID keyIDOwner; + CKeyID keyIDOperator; + CKeyID keyIDVoting; + uint16_t nOperatorReward{0}; + CScript scriptPayout; + uint256 inputsHash; // replay protection + std::vector vchSig; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nProtocolVersion); + READWRITE(nCollateralIndex); + READWRITE(addr); + READWRITE(keyIDOwner); + READWRITE(keyIDOperator); + READWRITE(keyIDVoting); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(nOperatorReward); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(vchSig); + } + } + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); + +#endif//DASH_PROVIDERTX_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index a07a1e1d3..a5887b35a 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -24,6 +24,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali } switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + return CheckProRegTx(tx, pindexPrev, state); } return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); @@ -35,6 +37,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida return true; switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + return true; // handled in batches per block } return state.DoS(100, false, REJECT_INVALID, "bad-tx-type"); @@ -46,6 +50,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) return true; switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + return true; // handled in batches per block } return false; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 23ec4f698..129a0c473 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -14,6 +14,7 @@ /** Transaction types */ enum { TRANSACTION_NORMAL = 0, + TRANSACTION_PROVIDER_REGISTER = 1, }; /** An outpoint - a combination of a transaction hash and an index n into its vout */ diff --git a/src/validation.cpp b/src/validation.cpp index a709b9f1a..16ecbc53a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -43,6 +43,7 @@ #include "masternode-payments.h" #include "evo/specialtx.h" +#include "evo/providertx.h" #include #include @@ -570,7 +571,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, if (fDIP0003Active_context) { // check version 3 transaction types if (tx.nVersion >= 3) { - if (tx.nType != TRANSACTION_NORMAL) { + if (tx.nType != TRANSACTION_NORMAL && + tx.nType != TRANSACTION_PROVIDER_REGISTER) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); } if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL) diff --git a/src/version.h b/src/version.h index afcd8f315..dc4360bd3 100644 --- a/src/version.h +++ b/src/version.h @@ -44,4 +44,10 @@ static const int DIP0001_PROTOCOL_VERSION = 70208; //! short-id-based block download starts with this version static const int SHORT_IDS_BLOCKS_VERSION = 70209; +//! minimum ProTx proto version +static const int MIN_PROTX_PROTO_VERSION = 70211; + +//! maximum ProTx proto version (slightly higher then current PROTOCOL_VERSION to ensure masternodes can upgrade) +static const int MAX_PROTX_PROTO_VERSION = PROTOCOL_VERSION + 2; + #endif // BITCOIN_VERSION_H