Merge pull request #2251 from codablock/pr_dip4

DIP4 implementation
This commit is contained in:
UdjinM6 2018-09-03 13:16:52 +03:00 committed by GitHub
commit 2900760dac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 622 additions and 7 deletions

View File

@ -113,7 +113,8 @@ class NULLDUMMYTest(BitcoinTestFramework):
def block_submit(self, node, txs, accept = False): 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 block.nVersion = 4
for tx in txs: for tx in txs:
tx.rehash() tx.rehash()

View File

@ -40,7 +40,7 @@ def serialize_script_num(value):
# Create a coinbase transaction, assuming no miner fees. # Create a coinbase transaction, assuming no miner fees.
# If pubkey is passed in, the coinbase output will be a P2PK output; # If pubkey is passed in, the coinbase output will be a P2PK output;
# otherwise an anyone-can-spend 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 = CTransaction()
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
ser_string(serialize_script_num(height)), 0xffffffff)) ser_string(serialize_script_num(height)), 0xffffffff))
@ -53,6 +53,11 @@ def create_coinbase(height, pubkey = None):
else: else:
coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbaseoutput.scriptPubKey = CScript([OP_TRUE])
coinbase.vout = [ coinbaseoutput ] 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() coinbase.calc_sha256()
return coinbase return coinbase

View File

@ -793,6 +793,34 @@ class BlockTransactions(object):
return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) 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("<H", f.read(2))[0]
self.height = struct.unpack("<i", f.read(4))[0]
self.merkleRootMNList = deser_uint256(f)
def serialize(self):
r = b""
r += struct.pack("<H", self.version)
r += struct.pack("<i", self.height)
r += ser_uint256(self.merkleRootMNList)
return r
# Objects that correspond to messages on the wire # Objects that correspond to messages on the wire
class msg_version(object): class msg_version(object):
command = b"version" command = b"version"

View File

@ -110,6 +110,8 @@ BITCOIN_CORE_H = \
evo/specialtx.h \ evo/specialtx.h \
evo/providertx.h \ evo/providertx.h \
evo/deterministicmns.h \ evo/deterministicmns.h \
evo/cbtx.h \
evo/simplifiedmns.h \
privatesend.h \ privatesend.h \
privatesend-client.h \ privatesend-client.h \
privatesend-server.h \ privatesend-server.h \
@ -224,6 +226,8 @@ libdash_server_a_SOURCES = \
evo/specialtx.cpp \ evo/specialtx.cpp \
evo/providertx.cpp \ evo/providertx.cpp \
evo/deterministicmns.cpp \ evo/deterministicmns.cpp \
evo/cbtx.cpp \
evo/simplifiedmns.cpp \
httprpc.cpp \ httprpc.cpp \
httpserver.cpp \ httpserver.cpp \
init.cpp \ init.cpp \

89
src/evo/cbtx.cpp Normal file
View File

@ -0,0 +1,89 @@
// 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 "simplifiedmns.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;
}
// 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()));
}

46
src/evo/cbtx.h Normal file
View File

@ -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 <typename Stream, typename Operation>
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

163
src/evo/simplifiedmns.cpp Normal file
View File

@ -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<uint256> 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<uint256> vHashes;
std::vector<bool> 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;
}

111
src/evo/simplifiedmns.h Normal file
View File

@ -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 <typename Stream, typename Operation>
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<CSimplifiedMNListEntry> 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 <typename Stream, typename Operation>
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<uint256> deletedMNs;
std::vector<CSimplifiedMNListEntry> mnList;
public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
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

View File

@ -12,6 +12,7 @@
#include "specialtx.h" #include "specialtx.h"
#include "deterministicmns.h" #include "deterministicmns.h"
#include "cbtx.h"
bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) 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); return CheckProUpRegTx(tx, pindexPrev, state);
case TRANSACTION_PROVIDER_UPDATE_REVOKE: case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return CheckProUpRevTx(tx, pindexPrev, state); return CheckProUpRevTx(tx, pindexPrev, state);
case TRANSACTION_COINBASE:
return CheckCbTx(tx, pindexPrev, state);
} }
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); 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_REGISTRAR:
case TRANSACTION_PROVIDER_UPDATE_REVOKE: case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return true; // handled in batches per block 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"); 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_REGISTRAR:
case TRANSACTION_PROVIDER_UPDATE_REVOKE: case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return true; // handled in batches per block return true; // handled in batches per block
case TRANSACTION_COINBASE:
return true; // nothing to do
} }
return false; return false;
@ -83,6 +90,9 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV
if (!deterministicMNManager->ProcessBlock(block, pindex->pprev, state)) if (!deterministicMNManager->ProcessBlock(block, pindex->pprev, state))
return false; return false;
if (!CheckCbTxMerkleRootMNList(block, pindex, state))
return false;
return true; return true;
} }

View File

@ -28,6 +28,11 @@
#include "masternode-sync.h" #include "masternode-sync.h"
#include "validationinterface.h" #include "validationinterface.h"
#include "evo/specialtx.h"
#include "evo/cbtx.h"
#include "evo/simplifiedmns.h"
#include "evo/deterministicmns.h"
#include <algorithm> #include <algorithm>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
@ -126,6 +131,9 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOps.push_back(-1); // updated at end pblocktemplate->vTxSigOps.push_back(-1); // updated at end
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
bool fDIP0003Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE;
CBlockIndex* pindexPrev = chainActive.Tip(); CBlockIndex* pindexPrev = chainActive.Tip();
nHeight = pindexPrev->nHeight + 1; nHeight = pindexPrev->nHeight + 1;
@ -166,7 +174,25 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// Compute regular coinbase transaction. // Compute regular coinbase transaction.
coinbaseTx.vout[0].nValue = blockReward; 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, // Update coinbase transaction with additional info about masternode and governance payments,
// get some info back to pass to getblocktemplate // get some info back to pass to getblocktemplate

View File

@ -42,6 +42,8 @@
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
#include "privatesend-server.h" #include "privatesend-server.h"
#include "evo/simplifiedmns.h"
#include <boost/thread.hpp> #include <boost/thread.hpp>
#if defined(NDEBUG) #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) { else if (strCommand == NetMsgType::NOTFOUND) {
// We do not care about the NOTFOUND message, but logging an Unknown Command // We do not care about the NOTFOUND message, but logging an Unknown Command
// message would be undesirable as we transmit it ourselves. // message would be undesirable as we transmit it ourselves.

View File

@ -18,6 +18,7 @@ enum {
TRANSACTION_PROVIDER_UPDATE_SERVICE = 2, TRANSACTION_PROVIDER_UPDATE_SERVICE = 2,
TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3, TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3,
TRANSACTION_PROVIDER_UPDATE_REVOKE = 4, TRANSACTION_PROVIDER_UPDATE_REVOKE = 4,
TRANSACTION_COINBASE = 5,
}; };
/** An outpoint - a combination of a transaction hash and an index n into its vout */ /** An outpoint - a combination of a transaction hash and an index n into its vout */

View File

@ -69,6 +69,8 @@ const char *MNGOVERNANCESYNC="govsync";
const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECT="govobj";
const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote";
const char *MNVERIFY="mnv"; const char *MNVERIFY="mnv";
const char *GETMNLISTDIFF="getmnlistd";
const char *MNLISTDIFF="mnlistdiff";
}; };
static const char* ppszTypeName[] = static const char* ppszTypeName[] =
@ -153,6 +155,8 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::MNGOVERNANCEOBJECT, NetMsgType::MNGOVERNANCEOBJECT,
NetMsgType::MNGOVERNANCEOBJECTVOTE, NetMsgType::MNGOVERNANCEOBJECTVOTE,
NetMsgType::MNVERIFY, NetMsgType::MNVERIFY,
NetMsgType::GETMNLISTDIFF,
NetMsgType::MNLISTDIFF,
}; };
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));

View File

@ -268,6 +268,8 @@ extern const char *MNGOVERNANCESYNC;
extern const char *MNGOVERNANCEOBJECT; extern const char *MNGOVERNANCEOBJECT;
extern const char *MNGOVERNANCEOBJECTVOTE; extern const char *MNGOVERNANCEOBJECTVOTE;
extern const char *MNVERIFY; extern const char *MNVERIFY;
extern const char *GETMNLISTDIFF;
extern const char *MNLISTDIFF;
}; };
/* Get a vector of all valid message types (see above) */ /* Get a vector of all valid message types (see above) */

View File

@ -23,6 +23,9 @@
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include "hash.h" #include "hash.h"
#include "evo/specialtx.h"
#include "evo/cbtx.h"
#include <stdint.h> #include <stdint.h>
#include <univalue.h> #include <univalue.h>
@ -131,6 +134,14 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
txs.push_back(tx->GetHash().GetHex()); txs.push_back(tx->GetHash().GetHex());
} }
result.push_back(Pair("tx", txs)); 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("time", block.GetBlockTime()));
result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
result.push_back(Pair("nonce", (uint64_t)block.nNonce)); result.push_back(Pair("nonce", (uint64_t)block.nNonce));

View File

@ -28,6 +28,9 @@
#include "masternode-payments.h" #include "masternode-payments.h"
#include "masternode-sync.h" #include "masternode-sync.h"
#include "evo/specialtx.h"
#include "evo/cbtx.h"
#include <memory> #include <memory>
#include <stdint.h> #include <stdint.h>
@ -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_started", pindexPrev->nHeight + 1 > consensusParams.nSuperblockStartBlock));
result.push_back(Pair("superblocks_enabled", sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED))); 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; return result;
} }

View File

@ -31,6 +31,7 @@
#include "evo/specialtx.h" #include "evo/specialtx.h"
#include "evo/providertx.h" #include "evo/providertx.h"
#include "evo/cbtx.h"
#include <stdint.h> #include <stdint.h>
@ -158,6 +159,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
proTx.ToJson(proTxObj); proTx.ToJson(proTxObj);
entry.push_back(Pair("proUpRevTx", 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()) { if (!hashBlock.IsNull()) {

View File

@ -20,6 +20,7 @@
#include "evo/specialtx.h" #include "evo/specialtx.h"
#include "evo/providertx.h" #include "evo/providertx.h"
#include "evo/deterministicmns.h" #include "evo/deterministicmns.h"
#include "evo/simplifiedmns.h"
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
extern UniValue signrawtransaction(const JSONRPCRequest& request); extern UniValue signrawtransaction(const JSONRPCRequest& request);
@ -558,6 +559,60 @@ UniValue protx_list(const JSONRPCRequest& request)
return ret; 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) UniValue protx(const JSONRPCRequest& request)
{ {
if (request.params.empty()) { if (request.params.empty()) {
@ -573,6 +628,7 @@ UniValue protx(const JSONRPCRequest& request)
" update_service - Create and send ProUpServTx to network\n" " update_service - Create and send ProUpServTx to network\n"
" update_registrar - Create and send ProUpRegTx to network\n" " update_registrar - Create and send ProUpRegTx to network\n"
" revoke - Create and send ProUpRevTx 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); return protx_update_registrar(request);
} else if (command == "revoke") { } else if (command == "revoke") {
return protx_revoke(request); return protx_revoke(request);
} else if (command == "diff") {
return protx_diff(request);
} else { } else {
throw std::runtime_error("invalid command: " + command); throw std::runtime_error("invalid command: " + command);
} }

View File

@ -45,6 +45,7 @@
#include "evo/specialtx.h" #include "evo/specialtx.h"
#include "evo/providertx.h" #include "evo/providertx.h"
#include "evo/deterministicmns.h" #include "evo/deterministicmns.h"
#include "evo/cbtx.h"
#include <atomic> #include <atomic>
#include <sstream> #include <sstream>
@ -550,7 +551,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
if (tx.IsCoinBase()) 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"); return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
} }
else else
@ -576,10 +582,11 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
tx.nType != TRANSACTION_PROVIDER_REGISTER && tx.nType != TRANSACTION_PROVIDER_REGISTER &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE && tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE &&
tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR && 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"); 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"); return state.DoS(100, false, REJECT_INVALID, "bad-txns-cb-type");
} else if (tx.nType != TRANSACTION_NORMAL) { } else if (tx.nType != TRANSACTION_NORMAL) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); 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(); : block.GetBlockTime();
bool fDIP0001Active_context = nHeight >= Params().GetConsensus().DIP0001Height; bool fDIP0001Active_context = nHeight >= Params().GetConsensus().DIP0001Height;
bool fDIP0003Active_context = VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE;
// Size limits // Size limits
unsigned int nMaxBlockSize = MaxBlockSize(fDIP0001Active_context); 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"); 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 // 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; CScript expect = CScript() << nHeight;
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || 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; return true;
} }