diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py index d1e0457a4..9d1543d96 100755 --- a/qa/rpc-tests/nulldummy.py +++ b/qa/rpc-tests/nulldummy.py @@ -113,7 +113,8 @@ class NULLDUMMYTest(BitcoinTestFramework): def block_submit(self, node, txs, accept = False): - block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1) + dip4_activated = self.lastblockheight + 1 >= 432 + block = create_block(self.tip, create_coinbase(self.lastblockheight + 1, dip4_activated=dip4_activated), self.lastblocktime + 1) block.nVersion = 4 for tx in txs: tx.rehash() diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 626631834..1db0e90de 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -40,7 +40,7 @@ def serialize_script_num(value): # Create a coinbase transaction, assuming no miner fees. # If pubkey is passed in, the coinbase output will be a P2PK output; # otherwise an anyone-can-spend output. -def create_coinbase(height, pubkey = None): +def create_coinbase(height, pubkey = None, dip4_activated=False): coinbase = CTransaction() coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), ser_string(serialize_script_num(height)), 0xffffffff)) @@ -53,6 +53,11 @@ def create_coinbase(height, pubkey = None): else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [ coinbaseoutput ] + if dip4_activated: + coinbase.nVersion = 3 + coinbase.nType = 5 + cbtx_payload = CCbTx(1, height, 0) + coinbase.vExtraPayload = cbtx_payload.serialize() coinbase.calc_sha256() return coinbase diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index d2fd22718..504fc77e5 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -793,6 +793,34 @@ class BlockTransactions(object): return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) +class CCbTx(object): + def __init__(self, version=None, height=None, merkleRootMNList=None): + self.set_null() + if version is not None: + self.version = version + if height is not None: + self.height = height + if merkleRootMNList is not None: + self.merkleRootMNList = merkleRootMNList + + def set_null(self): + self.version = 0 + self.height = 0 + self.merkleRootMNList = None + + def deserialize(self, f): + self.version = struct.unpack(" 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; +} + +// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list +bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (block.vtx[0]->nType != TRANSACTION_COINBASE) + return true; + + CCbTx cbTx; + if (!GetTxPayload(*block.vtx[0], cbTx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (pindex) { + uint256 calculatedMerkleRoot; + if (!CalcCbTxMerkleRootMNList(block, pindex->pprev, calculatedMerkleRoot, state)) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot"); + } + if (calculatedMerkleRoot != cbTx.merkleRootMNList) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot"); + } + } + + return true; +} + +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state) +{ + AssertLockHeld(cs_main); + LOCK(deterministicMNManager->cs); + + CDeterministicMNList tmpMNList; + if (!deterministicMNManager->BuildNewListFromBlock(block, pindexPrev, state, tmpMNList)) { + return false; + } + + CSimplifiedMNList sml(tmpMNList); + bool mutated = false; + merkleRootRet = sml.CalcMerkleRoot(&mutated); + return !mutated; +} + +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..0cc508bfa --- /dev/null +++ b/src/evo/cbtx.h @@ -0,0 +1,46 @@ +// 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 CBlock; +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); + +bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state); + +#endif//DASH_CBTX_H diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp new file mode 100644 index 000000000..0be8f689c --- /dev/null +++ b/src/evo/simplifiedmns.cpp @@ -0,0 +1,163 @@ +// 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 "simplifiedmns.h" +#include "specialtx.h" +#include "deterministicmns.h" +#include "cbtx.h" + +#include "validation.h" +#include "univalue.h" +#include "consensus/merkle.h" +#include "chainparams.h" + +CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) : + proRegTxHash(dmn.proTxHash), + service(dmn.pdmnState->addr), + keyIDOperator(dmn.pdmnState->keyIDOperator), + keyIDVoting(dmn.pdmnState->keyIDVoting), + isValid(dmn.pdmnState->nPoSeBanHeight == -1) +{ +} + +uint256 CSimplifiedMNListEntry::CalcHash() const +{ + CHashWriter hw(SER_GETHASH, CLIENT_VERSION); + hw << *this; + return hw.GetHash(); +} + +std::string CSimplifiedMNListEntry::ToString() const +{ + return strprintf("CSimplifiedMNListEntry(proRegTxHash=%s, service=%s, keyIDOperator=%s, keyIDVoting=%s, isValie=%d)", + proRegTxHash.ToString(), service.ToString(false), keyIDOperator.ToString(), keyIDVoting.ToString(), isValid); +} + +void CSimplifiedMNListEntry::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("proRegTxHash", proRegTxHash.ToString())); + obj.push_back(Pair("service", service.ToString(false))); + obj.push_back(Pair("keyIDOperator", keyIDOperator.ToString())); + obj.push_back(Pair("keyIDVoting", keyIDOperator.ToString())); + obj.push_back(Pair("isValid", isValid)); +} + +CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList) +{ + mnList.reserve(dmnList.all_count()); + + for (const auto& dmn : dmnList.all_range()) { + mnList.emplace_back(*dmn); + } + + std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) { + return a.proRegTxHash.Compare(b.proRegTxHash) < 0; + }); +} + +uint256 CSimplifiedMNList::CalcMerkleRoot(bool *pmutated) const +{ + std::vector leaves; + leaves.reserve(mnList.size()); + for (const auto& e : mnList) { + leaves.emplace_back(e.CalcHash()); + } + return ComputeMerkleRoot(leaves, pmutated); +} + +void CSimplifiedMNListDiff::ToJson(UniValue& obj) const +{ + obj.setObject(); + + obj.push_back(Pair("baseBlockHash", baseBlockHash.ToString())); + obj.push_back(Pair("blockHash", blockHash.ToString())); + + UniValue deletedMNsArr(UniValue::VARR); + for (const auto& h : deletedMNs) { + deletedMNsArr.push_back(h.ToString()); + } + obj.push_back(Pair("deletedMNs", deletedMNsArr)); + + UniValue mnListArr(UniValue::VARR); + for (const auto& e : mnList) { + UniValue eObj; + e.ToJson(eObj); + mnListArr.push_back(eObj); + } + obj.push_back(Pair("mnList", mnListArr)); + + CCbTx cbTxPayload; + if (GetTxPayload(*cbTx, cbTxPayload)) { + obj.push_back(Pair("merkleRootMNList", cbTxPayload.merkleRootMNList.ToString())); + } +} + +bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet) +{ + AssertLockHeld(cs_main); + mnListDiffRet = CSimplifiedMNListDiff(); + + BlockMap::iterator baseBlockIt = mapBlockIndex.begin(); + if (!baseBlockHash.IsNull()) { + baseBlockIt = mapBlockIndex.find(baseBlockHash); + } + auto blockIt = mapBlockIndex.find(blockHash); + if (baseBlockIt == mapBlockIndex.end()) { + errorRet = strprintf("block %s not found", baseBlockHash.ToString()); + return false; + } + if (blockIt == mapBlockIndex.end()) { + errorRet = strprintf("block %s not found", blockHash.ToString()); + return false; + } + + if (!chainActive.Contains(baseBlockIt->second) || !chainActive.Contains(blockIt->second)) { + errorRet = strprintf("block %s and %s are not in the same chain", baseBlockHash.ToString(), blockHash.ToString()); + return false; + } + if (baseBlockIt->second->nHeight > blockIt->second->nHeight) { + errorRet = strprintf("base block %s is higher then block %s", baseBlockHash.ToString(), blockHash.ToString()); + return false; + } + + LOCK(deterministicMNManager->cs); + + auto baseDmnList = deterministicMNManager->GetListForBlock(baseBlockHash); + auto dmnList = deterministicMNManager->GetListForBlock(blockHash); + auto dmnDiff = baseDmnList.BuildDiff(dmnList); + + // TODO store coinbase TX in CBlockIndex + CBlock block; + if (!ReadBlockFromDisk(block, blockIt->second, Params().GetConsensus())) { + errorRet = strprintf("failed to read block %s from disk", blockHash.ToString()); + return false; + } + + mnListDiffRet.baseBlockHash = baseBlockHash; + mnListDiffRet.blockHash = blockHash; + mnListDiffRet.cbTx = block.vtx[0]; + + std::vector vHashes; + std::vector vMatch(block.vtx.size(), false); + for (const auto& tx : block.vtx) { + vHashes.emplace_back(tx->GetHash()); + } + vMatch[0] = true; // only coinbase matches + mnListDiffRet.cbTxMerkleTree = CPartialMerkleTree(vHashes, vMatch); + mnListDiffRet.deletedMNs.assign(dmnDiff.removedMns.begin(), dmnDiff.removedMns.end()); + + for (const auto& p : dmnDiff.addedMNs) { + mnListDiffRet.mnList.emplace_back(*p.second); + } + for (const auto& p : dmnDiff.updatedMNs) { + const auto& dmn = dmnList.GetMN(p.first); + CDeterministicMN newDmn(*dmn); + newDmn.pdmnState = p.second; + mnListDiffRet.mnList.emplace_back(newDmn); + } + + return true; +} diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h new file mode 100644 index 000000000..00a701b65 --- /dev/null +++ b/src/evo/simplifiedmns.h @@ -0,0 +1,111 @@ +// 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_SIMPLIFIEDMNS_H +#define DASH_SIMPLIFIEDMNS_H + +#include "serialize.h" +#include "pubkey.h" +#include "netaddress.h" +#include "merkleblock.h" + +class UniValue; +class CDeterministicMNList; +class CDeterministicMN; + +class CSimplifiedMNListEntry +{ +public: + uint256 proRegTxHash; + CService service; + CKeyID keyIDOperator; + CKeyID keyIDVoting; + bool isValid; + +public: + CSimplifiedMNListEntry() {} + CSimplifiedMNListEntry(const CDeterministicMN& dmn); + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(proRegTxHash); + READWRITE(service); + READWRITE(keyIDOperator); + READWRITE(keyIDVoting); + READWRITE(isValid); + } + +public: + uint256 CalcHash() const; + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +class CSimplifiedMNList +{ +public: + std::vector mnList; + +public: + CSimplifiedMNList() {} + CSimplifiedMNList(const CDeterministicMNList& dmnList); + + uint256 CalcMerkleRoot(bool *pmutated = NULL) const; +}; + +/// P2P messages + +class CGetSimplifiedMNListDiff +{ +public: + uint256 baseBlockHash; + uint256 blockHash; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(baseBlockHash); + READWRITE(blockHash); + } +}; + +class CSimplifiedMNListDiff +{ +public: + uint256 baseBlockHash; + uint256 blockHash; + CPartialMerkleTree cbTxMerkleTree; + CTransactionRef cbTx; + std::vector deletedMNs; + std::vector mnList; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(baseBlockHash); + READWRITE(blockHash); + READWRITE(cbTxMerkleTree); + READWRITE(cbTx); + READWRITE(deletedMNs); + READWRITE(mnList); + } + +public: + void ToJson(UniValue& obj) const; +}; + +bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet); + +#endif//DASH_SIMPLIFIEDMNS_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index eb174bdb3..c6622a5a4 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; @@ -83,6 +90,9 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV if (!deterministicMNManager->ProcessBlock(block, pindex->pprev, state)) return false; + if (!CheckCbTxMerkleRootMNList(block, pindex, state)) + return false; + return true; } diff --git a/src/miner.cpp b/src/miner.cpp index 73a127125..413ba197a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -28,6 +28,11 @@ #include "masternode-sync.h" #include "validationinterface.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" +#include "evo/simplifiedmns.h" +#include "evo/deterministicmns.h" + #include #include #include @@ -126,6 +131,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 +174,25 @@ 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; + + CValidationState state; + if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state)) { + throw std::runtime_error(strprintf("%s: CalcSMLMerkleRootForNewBlock failed: %s", __func__, FormatStateMessage(state))); + } + + 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/net_processing.cpp b/src/net_processing.cpp index 01c5d0321..d53c38bfd 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -42,6 +42,8 @@ #endif // ENABLE_WALLET #include "privatesend-server.h" +#include "evo/simplifiedmns.h" + #include #if defined(NDEBUG) @@ -2818,6 +2820,31 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } + else if (strCommand == NetMsgType::GETMNLISTDIFF) { + CGetSimplifiedMNListDiff cmd; + vRecv >> cmd; + + LOCK(cs_main); + + CSimplifiedMNListDiff mnListDiff; + std::string strError; + if (BuildSimplifiedMNListDiff(cmd.baseBlockHash, cmd.blockHash, mnListDiff, strError)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNLISTDIFF, mnListDiff)); + } else { + LogPrint("net", "getmnlistdiff failed for baseBlockHash=%s, blockHash=%s. error=%s\n", cmd.baseBlockHash.ToString(), cmd.blockHash.ToString(), strError); + Misbehaving(pfrom->id, 1); + } + } + + + else if (strCommand == NetMsgType::MNLISTDIFF) { + // we have never requested this + LOCK(cs_main); + Misbehaving(pfrom->id, 100); + LogPrint("net", "received not-requested mnlistdiff. peer=%d\n", pfrom->id); + } + + else if (strCommand == NetMsgType::NOTFOUND) { // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. 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/protocol.cpp b/src/protocol.cpp index 4cd85e24d..fac2fab6e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -69,6 +69,8 @@ const char *MNGOVERNANCESYNC="govsync"; const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; const char *MNVERIFY="mnv"; +const char *GETMNLISTDIFF="getmnlistd"; +const char *MNLISTDIFF="mnlistdiff"; }; static const char* ppszTypeName[] = @@ -153,6 +155,8 @@ const static std::string allNetMessageTypes[] = { NetMsgType::MNGOVERNANCEOBJECT, NetMsgType::MNGOVERNANCEOBJECTVOTE, NetMsgType::MNVERIFY, + NetMsgType::GETMNLISTDIFF, + NetMsgType::MNLISTDIFF, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index dc7389dc8..f1dccca68 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -268,6 +268,8 @@ extern const char *MNGOVERNANCESYNC; extern const char *MNGOVERNANCEOBJECT; extern const char *MNGOVERNANCEOBJECTVOTE; extern const char *MNVERIFY; +extern const char *GETMNLISTDIFF; +extern const char *MNLISTDIFF; }; /* Get a vector of all valid message types (see above) */ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index de5aee549..582e47e35 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -23,6 +23,9 @@ #include "utilstrencodings.h" #include "hash.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" + #include #include @@ -131,6 +134,14 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx txs.push_back(tx->GetHash().GetHex()); } result.push_back(Pair("tx", txs)); + if (!block.vtx[0]->vExtraPayload.empty()) { + CCbTx cbTx; + if (GetTxPayload(block.vtx[0]->vExtraPayload, cbTx)) { + UniValue cbTxObj; + cbTx.ToJson(cbTxObj); + result.push_back(Pair("cbTx", cbTxObj)); + } + } result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("nonce", (uint64_t)block.nNonce)); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b0f83b225..7a3a4bfb3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -28,6 +28,9 @@ #include "masternode-payments.h" #include "masternode-sync.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" + #include #include @@ -736,6 +739,8 @@ UniValue getblocktemplate(const JSONRPCRequest& request) result.push_back(Pair("superblocks_started", pindexPrev->nHeight + 1 > consensusParams.nSuperblockStartBlock)); result.push_back(Pair("superblocks_enabled", sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED))); + result.push_back(Pair("coinbase_payload", HexStr(pblock->vtx[0]->vExtraPayload))); + return result; } 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/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 3e75fb933..177db49f0 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -20,6 +20,7 @@ #include "evo/specialtx.h" #include "evo/providertx.h" #include "evo/deterministicmns.h" +#include "evo/simplifiedmns.h" #ifdef ENABLE_WALLET extern UniValue signrawtransaction(const JSONRPCRequest& request); @@ -558,6 +559,60 @@ UniValue protx_list(const JSONRPCRequest& request) return ret; } +void protx_diff_help() +{ + throw std::runtime_error( + "protx diff \"baseBlock\" \"block\"\n" + "\nCalculates a diff between two deterministic masternode lists. The result also contains proof data.\n" + "specified, it defaults to \"wallet\". All types have the optional argument \"detailed\" which if set to\n" + "\"true\" will result in a detailed list to be returned. If set to \"false\", only the hashes of the ProTx\n" + "will be returned.\n" + "\nAvailable types:\n" + " wallet (detailed) - List only ProTx which are found in your wallet. This will also include ProTx which\n" + " failed PoSe verfication\n" + " valid (height) (detailed) - List only ProTx which are active/valid at the given chain height. If height is not\n" + " specified, it defaults to the current chain-tip\n" + " registered (height) (detaileD) - List all ProTx which are registered at the given chain height. If height is not\n" + " specified, it defaults to the current chain-tip. This will also include ProTx\n" + " which failed PoSe verification at that height\n" + ); +} + +static uint256 ParseBlock(const UniValue& v, std::string strName) +{ + AssertLockHeld(cs_main); + + try { + return ParseHashV(v, strName); + } catch (...) { + int h = ParseInt32V(v, strName); + if (h < 1 || h > chainActive.Height()) + throw std::runtime_error(strprintf("%s must be a block hash or chain height and not %s", strName, v.getValStr())); + return *chainActive[h]->phashBlock; + } +} + +UniValue protx_diff(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 3) + protx_diff_help(); + + LOCK(cs_main); + uint256 baseBlockHash = ParseBlock(request.params[1], "baseBlock"); + uint256 blockHash = ParseBlock(request.params[2], "block"); + + CSimplifiedMNListDiff mnListDiff; + std::string strError; + if (!BuildSimplifiedMNListDiff(baseBlockHash, blockHash, mnListDiff, strError)) { + throw std::runtime_error(strError); + } + + UniValue ret; + mnListDiff.ToJson(ret); + return ret; +} + + UniValue protx(const JSONRPCRequest& request) { if (request.params.empty()) { @@ -573,6 +628,7 @@ UniValue protx(const JSONRPCRequest& request) " update_service - Create and send ProUpServTx to network\n" " update_registrar - Create and send ProUpRegTx to network\n" " revoke - Create and send ProUpRevTx to network\n" + " diff - Calculate a diff and a proof between two masternode lists\n" ); } @@ -588,6 +644,8 @@ UniValue protx(const JSONRPCRequest& request) return protx_update_registrar(request); } else if (command == "revoke") { return protx_revoke(request); + } else if (command == "diff") { + return protx_diff(request); } else { throw std::runtime_error("invalid command: " + command); } 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; }