From d20100ecd4590d54b0be1b0012e269ea93ea83a4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Feb 2018 21:31:42 +0100 Subject: [PATCH 1/8] DIP0003 deployment --- src/chainparams.cpp | 10 ++++++++++ src/consensus/params.h | 1 + src/dsnotificationinterface.cpp | 1 + src/init.cpp | 5 +++++ src/rpc/blockchain.cpp | 1 + src/validation.cpp | 10 ++++++++++ src/validation.h | 2 ++ src/versionbits.cpp | 5 +++++ 8 files changed, 35 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c605d7d60d..b7edaa7bda 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -457,6 +457,13 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100 + // Deployment of DIP0003 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1535752800; // Sep 1st, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1567288800; // Sep 1st, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 50; // 50% of 100 + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -573,6 +580,9 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 999999999999ULL; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index ccba1d4996..524d094fd8 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -18,6 +18,7 @@ enum DeploymentPos DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. DEPLOYMENT_DIP0001, // Deployment of DIP0001 and lower transaction fees. DEPLOYMENT_BIP147, // Deployment of BIP147 (NULLDUMMY) + DEPLOYMENT_DIP0003, // Deployment of DIP0002 and DIP0003 (txv3 and deterministic MN lists) // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index d417c80c72..00be55b556 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -39,6 +39,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con // Update global DIP0001 activation status fDIP0001ActiveAtTip = pindexNew->nHeight >= Params().GetConsensus().DIP0001Height; + fDIP0003ActiveAtTip = (VersionBitsState(pindexNew->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE); if (fInitialDownload) return; diff --git a/src/init.cpp b/src/init.cpp index 26b80208dd..b35f6fa825 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1704,6 +1704,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + // Needs to be called after chain is initialized + if (chainActive.Tip() && chainActive.Tip()->pprev) { + fDIP0003ActiveAtTip = VersionBitsState(chainActive.Tip()->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + } + uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 481402b176..de5aee549d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1274,6 +1274,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); BIP9SoftForkDescPushBack(bip9_softforks, "dip0001", consensusParams, Consensus::DEPLOYMENT_DIP0001); + BIP9SoftForkDescPushBack(bip9_softforks, "dip0003", consensusParams, Consensus::DEPLOYMENT_DIP0003); BIP9SoftForkDescPushBack(bip9_softforks, "bip147", consensusParams, Consensus::DEPLOYMENT_BIP147); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); diff --git a/src/validation.cpp b/src/validation.cpp index a1a138416d..c616ce83b7 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -89,6 +89,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; std::atomic fDIP0001ActiveAtTip{false}; +std::atomic fDIP0003ActiveAtTip{false}; uint256 hashAssumeValid; @@ -1885,6 +1886,9 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s } } + if (pindex->pprev && pindex->pprev->pprev && VersionBitsState(pindex->pprev->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) != THRESHOLD_ACTIVE) { + fDIP0003ActiveAtTip = false; + } // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); @@ -2153,6 +2157,12 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd flags |= SCRIPT_VERIFY_NULLDUMMY; } + if (!fJustCheck && VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE) { + if (!fDIP0003ActiveAtTip) + LogPrintf("ConnectBlock -- DIP0003 got activated at height %d\n", pindex->nHeight); + fDIP0003ActiveAtTip = true; + } + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); diff --git a/src/validation.h b/src/validation.h index 18d7b8b888..8c45bbb4e2 100644 --- a/src/validation.h +++ b/src/validation.h @@ -189,6 +189,8 @@ extern bool fLargeWorkInvalidChainFound; extern std::map mapRejectedBlocks; extern std::atomic fDIP0001ActiveAtTip; +extern std::atomic fDIP0003ActiveAtTip; + /** Block hash whose ancestors we will assume to have valid scripts without checking them. */ extern uint256 hashAssumeValid; diff --git a/src/versionbits.cpp b/src/versionbits.cpp index af37b6350a..f7a5ce1d2b 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -26,6 +26,11 @@ const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION /*.name =*/ "bip147", /*.gbt_force =*/ true, /*.check_mn_protocol =*/ false, + }, + { + /*.name =*/ "dip0003", + /*.gbt_force =*/ true, + /*.check_mn_protocol =*/ false, } }; From a3c4ee3fda66710f64d593ff9026ff4ad8b5c096 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Feb 2018 14:44:32 +0100 Subject: [PATCH 2/8] DIP2 changes to CTransaction and CMutableTransaction --- src/consensus/consensus.h | 2 ++ src/primitives/transaction.cpp | 22 +++++++++++++--------- src/primitives/transaction.h | 30 +++++++++++++++++++++++++----- src/rpc/rawtransaction.cpp | 7 +++++++ src/script/interpreter.cpp | 5 ++++- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ee199b875c..0efe580c1e 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -18,6 +18,8 @@ inline unsigned int MaxBlockSigOps(bool fDIP0001Active /*= false */) { return MaxBlockSize(fDIP0001Active) / 50; } +/** The maximum allowed size of version 3 extra payload */ +static const unsigned int MAX_TX_EXTRA_PAYLOAD = 10000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index f2afae047c..892cec4f69 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -60,8 +60,8 @@ std::string CTxOut::ToString() const return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); } -CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nType(TRANSACTION_NORMAL), nLockTime(0) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), nType(tx.nType), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload) {} uint256 CMutableTransaction::GetHash() const { @@ -71,12 +71,14 @@ uint256 CMutableTransaction::GetHash() const std::string CMutableTransaction::ToString() const { std::string str; - str += strprintf("CMutableTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + str += strprintf("CMutableTransaction(hash=%s, ver=%d, type=%d, vin.size=%u, vout.size=%u, nLockTime=%u, vExtraPayload.size=%d)\n", GetHash().ToString().substr(0,10), nVersion, + nType, vin.size(), vout.size(), - nLockTime); + nLockTime, + vExtraPayload.size()); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) @@ -90,9 +92,9 @@ uint256 CTransaction::ComputeHash() const } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), nType(TRANSACTION_NORMAL), vin(), vout(), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), nType(tx.nType), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), nType(tx.nType), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { @@ -140,12 +142,14 @@ unsigned int CTransaction::GetTotalSize() const std::string CTransaction::ToString() const { std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + str += strprintf("CTransaction(hash=%s, ver=%d, type=%d, vin.size=%u, vout.size=%u, nLockTime=%u, vExtraPayload.size=%d)\n", GetHash().ToString().substr(0,10), nVersion, + nType, vin.size(), vout.size(), - nLockTime); + nLockTime, + vExtraPayload.size()); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index e29af2bcef..23ec4f698b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,11 @@ #include "serialize.h" #include "uint256.h" +/** Transaction types */ +enum { + TRANSACTION_NORMAL = 0, +}; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -215,17 +220,19 @@ public: // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and // MAX_STANDARD_VERSION will be equal. - static const int32_t MAX_STANDARD_VERSION=2; + static const int32_t MAX_STANDARD_VERSION=3; // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. - const int32_t nVersion; + const int16_t nVersion; + const int16_t nType; const std::vector vin; const std::vector vout; const uint32_t nLockTime; + const std::vector vExtraPayload; // only available for special transaction types private: /** Memory only. */ @@ -243,10 +250,13 @@ public: template inline void Serialize(Stream& s) const { - s << this->nVersion; + int32_t n32bitVersion = this->nVersion | (this->nType << 16); + s << n32bitVersion; s << vin; s << vout; s << nLockTime; + if (this->nVersion >= 3 && this->nType != TRANSACTION_NORMAL) + s << vExtraPayload; } /** This deserializing constructor is provided instead of an Unserialize method. @@ -301,10 +311,12 @@ public: /** A mutable version of CTransaction. */ struct CMutableTransaction { - int32_t nVersion; + int16_t nVersion; + int16_t nType; std::vector vin; std::vector vout; uint32_t nLockTime; + std::vector vExtraPayload; // only available for special transaction types CMutableTransaction(); CMutableTransaction(const CTransaction& tx); @@ -313,10 +325,18 @@ struct CMutableTransaction template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); + int32_t n32bitVersion = this->nVersion | (this->nType << 16); + READWRITE(n32bitVersion); + if (ser_action.ForRead()) { + this->nVersion = (int16_t) (n32bitVersion & 0xffff); + this->nType = (int16_t) ((n32bitVersion >> 16) & 0xffff); + } READWRITE(vin); READWRITE(vout); READWRITE(nLockTime); + if (this->nVersion >= 3 && this->nType != TRANSACTION_NORMAL) { + READWRITE(vExtraPayload); + } } template diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e773e6518c..0c02ce00ff 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -65,6 +65,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) entry.push_back(Pair("txid", txid.GetHex())); entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("type", tx.nType)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); UniValue vin(UniValue::VARR); BOOST_FOREACH(const CTxIn& txin, tx.vin) { @@ -121,6 +122,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) } entry.push_back(Pair("vout", vout)); + if (!tx.vExtraPayload.empty()) { + entry.push_back(Pair("extraPayloadSize", (int)tx.vExtraPayload.size())); + entry.push_back(Pair("extraPayload", HexStr(tx.vExtraPayload))); + } + if (!hashBlock.IsNull()) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); @@ -500,6 +506,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) " \"txid\" : \"id\", (string) The transaction id\n" " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" + " \"type\" : n, (numeric) The type\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 80c804b929..d86696cb8b 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1098,7 +1098,8 @@ public: template void Serialize(S &s) const { // Serialize nVersion - ::Serialize(s, txTo.nVersion); + int32_t n32bitVersion = txTo.nVersion | (txTo.nType << 16); + ::Serialize(s, n32bitVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); ::WriteCompactSize(s, nInputs); @@ -1111,6 +1112,8 @@ public: SerializeOutput(s, nOutput); // Serialize nLockTime ::Serialize(s, txTo.nLockTime); + if (txTo.nVersion >= 3 && txTo.nType != TRANSACTION_NORMAL) + ::Serialize(s, txTo.vExtraPayload); } }; From d6c5a72e2f725d81d365729a804b55c4834899df Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Feb 2018 13:33:23 +0100 Subject: [PATCH 3/8] Basic validation of version 3 TXs in CheckTransaction --- src/validation.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index c616ce83b7..998d800e89 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -518,6 +518,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_LEGACY_BLOCK_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); + if (tx.vExtraPayload.size() > MAX_TX_EXTRA_PAYLOAD) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-payload-oversize"); // Check for negative or overflow output values CAmount nValueOut = 0; @@ -561,6 +563,20 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, { int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; bool fDIP0001Active_context = nHeight >= consensusParams.DIP0001Height; + bool fDIP0003Active_context = VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + + if (fDIP0003Active_context) { + // check version 3 transaction types + if (tx.nVersion >= 3) { + if (tx.nType != TRANSACTION_NORMAL) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); + } + if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-cb-type"); + } else if (tx.nType != TRANSACTION_NORMAL) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); + } + } // Size limits if (fDIP0001Active_context && ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_STANDARD_TX_SIZE) From cebf71bbc3b5e4206a2c7046a1b84897d4618031 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Feb 2018 13:34:35 +0100 Subject: [PATCH 4/8] Stubs for special TX validation and processing --- src/Makefile.am | 2 ++ src/evo/specialtx.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++ src/evo/specialtx.h | 19 ++++++++++ src/validation.cpp | 12 +++++++ 4 files changed, 117 insertions(+) create mode 100644 src/evo/specialtx.cpp create mode 100644 src/evo/specialtx.h diff --git a/src/Makefile.am b/src/Makefile.am index b015c23880..9b4ef09fc5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -106,6 +106,7 @@ BITCOIN_CORE_H = \ core_io.h \ core_memusage.h \ cuckoocache.h \ + evo/specialtx.h \ privatesend.h \ privatesend-client.h \ privatesend-server.h \ @@ -216,6 +217,7 @@ libdash_server_a_SOURCES = \ chain.cpp \ checkpoints.cpp \ dsnotificationinterface.cpp \ + evo/specialtx.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp new file mode 100644 index 0000000000..f648f7f106 --- /dev/null +++ b/src/evo/specialtx.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2017 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 "primitives/transaction.h" +#include "primitives/block.h" +#include "consensus/validation.h" +#include "hash.h" +#include "clientversion.h" +#include "validation.h" +#include "chainparams.h" + +#include "specialtx.h" + +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + if (pindex && VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) != THRESHOLD_ACTIVE) { + return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); + } + + switch (tx.nType) { + } + + return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); +} + +bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state) +{ + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + switch (tx.nType) { + } + + return state.DoS(100, false, REJECT_INVALID, "bad-tx-type"); +} + +bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) +{ + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + switch (tx.nType) { + } + + return false; +} + +bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state) +{ + for (int i = 0; i < (int)block.vtx.size(); i++) { + const CTransaction& tx = *block.vtx[i]; + if (!CheckSpecialTx(tx, pindex, state)) + return false; + if (!ProcessSpecialTx(tx, pindex, state)) + return false; + } + + return true; +} + +bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex) +{ + for (int i = (int)block.vtx.size() - 1; i >= 0; --i) { + const CTransaction& tx = *block.vtx[i]; + if (!UndoSpecialTx(tx, pindex)) + return false; + } + return true; +} + +uint256 CalcTxInputsHash(const CTransaction& tx) +{ + CHashWriter hw(CLIENT_VERSION, SER_GETHASH); + for (const auto& in : tx.vin) { + hw << in.prevout; + } + return hw.GetHash(); +} diff --git a/src/evo/specialtx.h b/src/evo/specialtx.h new file mode 100644 index 0000000000..2eaf2c9cc5 --- /dev/null +++ b/src/evo/specialtx.h @@ -0,0 +1,19 @@ +// Copyright (c) 2017 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_SPECIALTX_H +#define DASH_SPECIALTX_H + +class CTransaction; +class CBlock; +class CBlockIndex; +class CValidationState; + +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state); +bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); +bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex); + +uint256 CalcTxInputsHash(const CTransaction& tx); + +#endif//DASH_SPECIALTX_H diff --git a/src/validation.cpp b/src/validation.cpp index 998d800e89..315ba19405 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -42,6 +42,8 @@ #include "masternodeman.h" #include "masternode-payments.h" +#include "evo/specialtx.h" + #include #include @@ -634,6 +636,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (!ContextualCheckTransaction(tx, state, Params().GetConsensus(), chainActive.Tip())) return error("%s: ContextualCheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + if (!CheckSpecialTx(tx, chainActive.Tip(), state)) + return false; + // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) return state.DoS(100, false, REJECT_INVALID, "coinbase"); @@ -1791,6 +1796,10 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s std::vector > addressUnspentIndex; std::vector > spentIndex; + if (!UndoSpecialTxsInBlock(block, pindex)) { + return DISCONNECT_FAILED; + } + // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = *(block.vtx[i]); @@ -2336,6 +2345,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); + if (!ProcessSpecialTxsInBlock(block, pindex, state)) + return false; + // DASH : MODIFIED TO CHECK MASTERNODE PAYMENTS AND SUPERBLOCKS // It's possible that we simply don't have enough data and this could fail From 61bbe54ab3b0f00712311749c21a8666d5a2dd08 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Feb 2018 12:05:25 +0100 Subject: [PATCH 5/8] Add Get-/SetTxPayload helpers --- src/evo/specialtx.h | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/evo/specialtx.h b/src/evo/specialtx.h index 2eaf2c9cc5..903380124b 100644 --- a/src/evo/specialtx.h +++ b/src/evo/specialtx.h @@ -5,7 +5,10 @@ #ifndef DASH_SPECIALTX_H #define DASH_SPECIALTX_H -class CTransaction; +#include "streams.h" +#include "version.h" +#include "primitives/transaction.h" + class CBlock; class CBlockIndex; class CValidationState; @@ -14,6 +17,36 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidati bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex); +template +inline bool GetTxPayload(const std::vector& payload, T& obj) +{ + CDataStream ds(payload, SER_NETWORK, PROTOCOL_VERSION); + try { + ds >> obj; + } catch (std::exception& e) { + return false; + } + return ds.empty(); +} +template +inline bool GetTxPayload(const CMutableTransaction& tx, T& obj) +{ + return GetTxPayload(tx.vExtraPayload, obj); +} +template +inline bool GetTxPayload(const CTransaction& tx, T& obj) +{ + return GetTxPayload(tx.vExtraPayload, obj); +} + +template +void SetTxPayload(CMutableTransaction& tx, const T& payload) +{ + CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); + ds << payload; + tx.vExtraPayload.assign(ds.begin(), ds.end()); +} + uint256 CalcTxInputsHash(const CTransaction& tx); #endif//DASH_SPECIALTX_H From b606bde9a99a26e8bf8b5eb33376043b00a973f1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 23 Feb 2018 11:37:14 +0100 Subject: [PATCH 6/8] Support version 3 transaction serialization in mininode.py --- qa/rpc-tests/test_framework/mininode.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 668326286f..d2fd22718d 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -357,33 +357,44 @@ class CTransaction(object): def __init__(self, tx=None): if tx is None: self.nVersion = 1 + self.nType = 0 self.vin = [] self.vout = [] self.nLockTime = 0 + self.vExtraPayload = None self.sha256 = None self.hash = None else: self.nVersion = tx.nVersion + self.nType = tx.nType self.vin = copy.deepcopy(tx.vin) self.vout = copy.deepcopy(tx.vout) self.nLockTime = tx.nLockTime + self.vExtraPayload = tx.vExtraPayload self.sha256 = tx.sha256 self.hash = tx.hash def deserialize(self, f): - self.nVersion = struct.unpack("> 16) & 0xffff self.vin = deser_vector(f, CTxIn) self.vout = deser_vector(f, CTxOut) self.nLockTime = struct.unpack(" Date: Wed, 28 Feb 2018 10:10:36 +0100 Subject: [PATCH 7/8] Account for extraPayload when calculating fees in FundTransaction --- src/wallet/wallet.cpp | 13 +++++++++++-- src/wallet/wallet.h | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6ae8b8b249..85c9ac1dd5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2962,9 +2962,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov BOOST_FOREACH(const CTxIn& txin, tx.vin) coinControl.Select(txin.prevout); + int nExtraPayloadSize = 0; + if (tx.nVersion >= 3 && tx.nType != TRANSACTION_NORMAL) + nExtraPayloadSize = (int)tx.vExtraPayload.size(); + CReserveKey reservekey(this); CWalletTx wtx; - if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false, ALL_COINS, false, nExtraPayloadSize)) return false; if (nChangePosInOut != -1) @@ -3403,7 +3407,7 @@ bool CWallet::ConvertList(std::vector vecTxIn, std::vector& vecA } bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, - int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend) + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend, int nExtraPayloadSize) { CAmount nFeePay = fUseInstantSend ? CTxLockRequest().GetMinFee() : 0; @@ -3705,6 +3709,11 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + if (nExtraPayloadSize != 0) { + // account for extra payload in fee calculation + nBytes += GetSizeOfCompactSize(nExtraPayloadSize) + nExtraPayloadSize; + } + if (nBytes > MAX_STANDARD_TX_SIZE) { // Do not create oversized transactions (bad-txns-oversize). strFailReason = _("Transaction too large"); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3f443332f6..2fe6be7314 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -936,7 +936,7 @@ public: * @note passing nChangePosInOut as -1 will result in setting a random position */ bool CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend=false); + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend=false, int nExtraPayloadSize = 0); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state, const std::string& strCommand="tx"); bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason); From fb4d301a2069b3d90c5138bc4d12e40bd250c91d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Aug 2018 15:45:29 +0200 Subject: [PATCH 8/8] Add extraPayloadSize/extraPayload fields to RPC help --- src/rpc/rawtransaction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0c02ce00ff..283afacf89 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -202,6 +202,8 @@ UniValue getrawtransaction(const JSONRPCRequest& request) " }\n" " ,...\n" " ],\n" + " \"extraPayloadSize\" : n (numeric) Size of DIP2 extra payload. Only present if it's a special TX\n" + " \"extraPayload\" : \"hex\" (string) Hex encoded DIP2 extra payload data. Only present if it's a special TX\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" @@ -537,6 +539,8 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) " }\n" " ,...\n" " ],\n" + " \"extraPayloadSize\" : n (numeric) Size of DIP2 extra payload. Only present if it's a special TX\n" + " \"extraPayload\" : \"hex\" (string) Hex encoded DIP2 extra payload data. Only present if it's a special TX\n" "}\n" "\nExamples:\n"