diff --git a/src/Makefile.am b/src/Makefile.am index 18ad67169..5bb89bf6c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,6 +110,7 @@ BITCOIN_CORE_H = \ evo/specialtx.h \ evo/providertx.h \ evo/deterministicmns.h \ + evo/cbtx.h \ privatesend.h \ privatesend-client.h \ privatesend-server.h \ @@ -224,6 +225,7 @@ libdash_server_a_SOURCES = \ evo/specialtx.cpp \ evo/providertx.cpp \ evo/deterministicmns.cpp \ + evo/cbtx.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp new file mode 100644 index 000000000..2d48ab747 --- /dev/null +++ b/src/evo/cbtx.cpp @@ -0,0 +1,47 @@ +// 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 "cbtx.h" +#include "specialtx.h" +#include "deterministicmns.h" + +#include "validation.h" +#include "univalue.h" + +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (!tx.IsCoinBase()) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-invalid"); + + CCbTx cbTx; + if (!GetTxPayload(tx, cbTx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (cbTx.nVersion > CCbTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version"); + + if (pindexPrev) { + if (pindexPrev->nHeight + 1 != cbTx.nHeight) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-height"); + } + + return true; +} + +std::string CCbTx::ToString() const +{ + return strprintf("CCbTx(nHeight=%d, nVersion=%d, merkleRootMNList=%s)", + nVersion, nHeight, merkleRootMNList.ToString()); +} + +void CCbTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", (int)nVersion)); + obj.push_back(Pair("height", (int)nHeight)); + obj.push_back(Pair("merkleRootMNList", merkleRootMNList.ToString())); +} diff --git a/src/evo/cbtx.h b/src/evo/cbtx.h new file mode 100644 index 000000000..5f780f927 --- /dev/null +++ b/src/evo/cbtx.h @@ -0,0 +1,42 @@ +// 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_CBTX_H +#define DASH_CBTX_H + +#include "primitives/transaction.h" +#include "consensus/validation.h" + +class CBlockIndex; +class UniValue; + +// coinbase transaction +class CCbTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; + int32_t nHeight{0}; + uint256 merkleRootMNList; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nHeight); + READWRITE(merkleRootMNList); + } + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); + +#endif//DASH_CBTX_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index eb174bdb3..f602e9b13 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -12,6 +12,7 @@ #include "specialtx.h" #include "deterministicmns.h" +#include "cbtx.h" bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { @@ -33,6 +34,8 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali return CheckProUpRegTx(tx, pindexPrev, state); case TRANSACTION_PROVIDER_UPDATE_REVOKE: return CheckProUpRevTx(tx, pindexPrev, state); + case TRANSACTION_COINBASE: + return CheckCbTx(tx, pindexPrev, state); } return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); @@ -49,6 +52,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: case TRANSACTION_PROVIDER_UPDATE_REVOKE: return true; // handled in batches per block + case TRANSACTION_COINBASE: + return true; // nothing to do } return state.DoS(100, false, REJECT_INVALID, "bad-tx-type"); @@ -65,6 +70,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: case TRANSACTION_PROVIDER_UPDATE_REVOKE: return true; // handled in batches per block + case TRANSACTION_COINBASE: + return true; // nothing to do } return false; diff --git a/src/miner.cpp b/src/miner.cpp index 73a127125..afb9cf641 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -28,6 +28,9 @@ #include "masternode-sync.h" #include "validationinterface.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" + #include #include #include @@ -126,6 +129,9 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps.push_back(-1); // updated at end LOCK2(cs_main, mempool.cs); + + bool fDIP0003Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + CBlockIndex* pindexPrev = chainActive.Tip(); nHeight = pindexPrev->nHeight + 1; @@ -166,7 +172,19 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // Compute regular coinbase transaction. coinbaseTx.vout[0].nValue = blockReward; - coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + + if (!fDIP0003Active_context) { + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + } else { + coinbaseTx.vin[0].scriptSig = CScript() << OP_RETURN; + + coinbaseTx.nVersion = 3; + coinbaseTx.nType = TRANSACTION_COINBASE; + + CCbTx cbTx; + cbTx.nHeight = nHeight; + SetTxPayload(coinbaseTx, cbTx); + } // Update coinbase transaction with additional info about masternode and governance payments, // get some info back to pass to getblocktemplate diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 71ad59880..e65795d19 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -18,6 +18,7 @@ enum { TRANSACTION_PROVIDER_UPDATE_SERVICE = 2, TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3, TRANSACTION_PROVIDER_UPDATE_REVOKE = 4, + TRANSACTION_COINBASE = 5, }; /** 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 4818338c5..a98a2a48c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -31,6 +31,7 @@ #include "evo/specialtx.h" #include "evo/providertx.h" +#include "evo/cbtx.h" #include @@ -158,6 +159,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) proTx.ToJson(proTxObj); entry.push_back(Pair("proUpRevTx", proTxObj)); } + } else if (tx.nType == TRANSACTION_COINBASE) { + CCbTx cbTx; + if (GetTxPayload(tx, cbTx)) { + UniValue proTxObj; + cbTx.ToJson(proTxObj); + entry.push_back(Pair("cbTx", proTxObj)); + } } if (!hashBlock.IsNull()) { diff --git a/src/validation.cpp b/src/validation.cpp index c38b84a6c..405379aa3 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -45,6 +45,7 @@ #include "evo/specialtx.h" #include "evo/providertx.h" #include "evo/deterministicmns.h" +#include "evo/cbtx.h" #include #include @@ -550,7 +551,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe if (tx.IsCoinBase()) { - if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + int minCbSize = 2; + if (tx.nType == TRANSACTION_COINBASE) { + // With the introduction of CbTx, coinbase scripts are not required anymore to hold a valid block height + minCbSize = 1; + } + if (tx.vin[0].scriptSig.size() < minCbSize || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); } else @@ -576,10 +582,11 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, tx.nType != TRANSACTION_PROVIDER_REGISTER && tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE && tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR && - tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) { + tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE && + tx.nType != TRANSACTION_COINBASE) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); } - if (tx.IsCoinBase() && tx.nType != TRANSACTION_NORMAL) + if (tx.IsCoinBase() && tx.nType != TRANSACTION_COINBASE) 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"); @@ -3513,6 +3520,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co : block.GetBlockTime(); bool fDIP0001Active_context = nHeight >= Params().GetConsensus().DIP0001Height; + bool fDIP0003Active_context = VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; // Size limits unsigned int nMaxBlockSize = MaxBlockSize(fDIP0001Active_context); @@ -3537,7 +3545,9 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co return state.DoS(10, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); // Enforce rule that the coinbase starts with serialized block height - if (nHeight >= consensusParams.BIP34Height) + // After DIP3/DIP4 activation, we don't enforce the height in the input script anymore. + // The CbTx special transaction payload will then contain the height, which is checked in CheckCbTx + if (nHeight >= consensusParams.BIP34Height && !fDIP0003Active_context) { CScript expect = CScript() << nHeight; if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || @@ -3546,6 +3556,12 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co } } + if (fDIP0003Active_context) { + if (block.vtx[0]->nType != TRANSACTION_COINBASE) { + return state.DoS(100, false, REJECT_INVALID, "bad-cb-type", false, "coinbase is not a CbTx"); + } + } + return true; }