Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 92 additions & 89 deletions src/masternode/payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,90 @@

#include <string>

CMasternodePayments mnpayments;
/**
* GetMasternodeTxOuts
*
* Get masternode payment tx outputs
*/

static bool GetBlockTxOuts(const int nBlockHeight, const CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
voutMasternodePaymentsRet.clear();

const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[nBlockHeight - 1]);
auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee(pindex);
if (!dmnPayee) {
return false;
}

CAmount operatorReward = 0;
CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward, Params().GetConsensus().BRRHeight);

if (dmnPayee->nOperatorReward != 0 && dmnPayee->pdmnState->scriptOperatorPayout != CScript()) {
// This calculation might eventually turn out to result in 0 even if an operator reward percentage is given.
// This will however only happen in a few years when the block rewards drops very low.
operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000;
masternodeReward -= operatorReward;
}

if (masternodeReward > 0) {
voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout);
}
if (operatorReward > 0) {
voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout);
}

return true;
}


static bool GetMasternodeTxOuts(const int nBlockHeight, const CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
// make sure it's not filled yet
voutMasternodePaymentsRet.clear();

if(!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) {
LogPrintf("CMasternodePayments::%s -- no payee (deterministic masternode list empty)\n", __func__);
return false;
}

for (const auto& txout : voutMasternodePaymentsRet) {
CTxDestination dest;
ExtractDestination(txout.scriptPubKey, dest);

LogPrintf("CMasternodePayments::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, EncodeDestination(dest));
}

return true;
}

static bool IsTransactionValid(const CTransaction& txNew, const int nBlockHeight, const CAmount blockReward)
{
if (!deterministicMNManager->IsDIP3Enforced(nBlockHeight)) {
// can't verify historical blocks here
return true;
}

bool IsOldBudgetBlockValueValid(const CMasternodeSync& mn_sync, const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet) {
std::vector<CTxOut> voutMasternodePayments;
if (!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePayments)) {
LogPrintf("CMasternodePayments::%s -- ERROR failed to get payees for block at height %s\n", __func__, nBlockHeight);
return true;
}

for (const auto& txout : voutMasternodePayments) {
bool found = ranges::any_of(txNew.vout, [&txout](const auto& txout2) {return txout == txout2;});
if (!found) {
CTxDestination dest;
if (!ExtractDestination(txout.scriptPubKey, dest))
assert(false);
LogPrintf("CMasternodePayments::%s -- ERROR failed to find expected payee %s in block at height %s\n", __func__, EncodeDestination(dest), nBlockHeight);
return false;
}
}
return true;
}

static bool IsOldBudgetBlockValueValid(const CMasternodeSync& mn_sync, const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet) {
const Consensus::Params& consensusParams = Params().GetConsensus();
bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);

Expand Down Expand Up @@ -66,6 +147,8 @@ bool IsOldBudgetBlockValueValid(const CMasternodeSync& mn_sync, const CBlock& bl
return isBlockRewardValueMet;
}

namespace CMasternodePayments {

/**
* IsBlockValueValid
*
Expand All @@ -76,9 +159,8 @@ bool IsOldBudgetBlockValueValid(const CMasternodeSync& mn_sync, const CBlock& bl
* - Other blocks are 10% lower in outgoing value, so in total, no extra coins are created
* - When non-superblocks are detected, the normal schedule should be maintained
*/

bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
const CMasternodeSync& mn_sync, const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet)
const CMasternodeSync& mn_sync, const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);
Expand Down Expand Up @@ -164,7 +246,7 @@ bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& go
}

bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
const CTransaction& txNew, int nBlockHeight, CAmount blockReward)
const CTransaction& txNew, const int nBlockHeight, const CAmount blockReward)
{
if(fDisableGovernance) {
//there is no budget data to use to check anything, let's just accept the longest chain
Expand Down Expand Up @@ -207,7 +289,7 @@ bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& go
}

// Check for correct masternode payment
if(CMasternodePayments::IsTransactionValid(txNew, nBlockHeight, blockReward)) {
if(IsTransactionValid(txNew, nBlockHeight, blockReward)) {
LogPrint(BCLog::MNPAYMENTS, "%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
return true;
}
Expand All @@ -217,7 +299,8 @@ bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& go
}

void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet)
CMutableTransaction& txNew, const int nBlockHeight, const CAmount blockReward,
std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet)
{
// only create superblocks if spork is enabled AND if superblock is actually triggered
// (height should be validated inside)
Expand All @@ -226,7 +309,7 @@ void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& go
CSuperblockManager::GetSuperblockPayments(governanceManager, nBlockHeight, voutSuperblockPaymentsRet);
}

if (!CMasternodePayments::GetMasternodeTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) {
if (!GetMasternodeTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) {
LogPrint(BCLog::MNPAYMENTS, "%s -- no masternode to pay (MN list probably empty)\n", __func__);
}

Expand All @@ -246,84 +329,4 @@ void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& go
nBlockHeight, blockReward, voutMasternodeStr, txNew.ToString());
}

/**
* GetMasternodeTxOuts
*
* Get masternode payment tx outputs
*/

bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
// make sure it's not filled yet
voutMasternodePaymentsRet.clear();

if(!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) {
LogPrintf("CMasternodePayments::%s -- no payee (deterministic masternode list empty)\n", __func__);
return false;
}

for (const auto& txout : voutMasternodePaymentsRet) {
CTxDestination dest;
ExtractDestination(txout.scriptPubKey, dest);

LogPrintf("CMasternodePayments::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, EncodeDestination(dest));
}

return true;
}

bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
voutMasternodePaymentsRet.clear();

const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[nBlockHeight - 1]);
auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee(pindex);
if (!dmnPayee) {
return false;
}

CAmount operatorReward = 0;
CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward, Params().GetConsensus().BRRHeight);

if (dmnPayee->nOperatorReward != 0 && dmnPayee->pdmnState->scriptOperatorPayout != CScript()) {
// This calculation might eventually turn out to result in 0 even if an operator reward percentage is given.
// This will however only happen in a few years when the block rewards drops very low.
operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000;
masternodeReward -= operatorReward;
}

if (masternodeReward > 0) {
voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout);
}
if (operatorReward > 0) {
voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout);
}

return true;
}

bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward)
{
if (!deterministicMNManager->IsDIP3Enforced(nBlockHeight)) {
// can't verify historical blocks here
return true;
}

std::vector<CTxOut> voutMasternodePayments;
if (!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePayments)) {
LogPrintf("CMasternodePayments::%s -- ERROR failed to get payees for block at height %s\n", __func__, nBlockHeight);
return true;
}

for (const auto& txout : voutMasternodePayments) {
bool found = ranges::any_of(txNew.vout, [&txout](const auto& txout2) {return txout == txout2;});
if (!found) {
CTxDestination dest;
if (!ExtractDestination(txout.scriptPubKey, dest))
assert(false);
LogPrintf("CMasternodePayments::%s -- ERROR failed to find expected payee %s in block at height %s\n", __func__, EncodeDestination(dest), nBlockHeight);
return false;
}
}
return true;
}
} // namespace CMasternodePayments
27 changes: 9 additions & 18 deletions src/masternode/payments.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,27 @@
#include <vector>

class CGovernanceManager;
class CMasternodePayments;
class CBlock;
class CTransaction;
struct CMutableTransaction;
class CSporkManager;
class CTxOut;
class CMasternodeSync;

/// TODO: all 4 functions do not belong here really, they should be refactored/moved somewhere (main.cpp ?)
bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet);
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
const CTransaction& txNew, int nBlockHeight, CAmount blockReward);
void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet);

extern CMasternodePayments mnpayments;

//
// Masternode Payments Class
// Keeps track of who should get paid for which blocks
//

class CMasternodePayments
namespace CMasternodePayments
{
public:
static bool GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet);
static bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward);

static bool GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector<CTxOut>& voutMasternodePaymentsRet);
};
bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet);
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
const CTransaction& txNew, const int nBlockHeight, const CAmount blockReward);
void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
CMutableTransaction& txNew, const int nBlockHeight, const CAmount blockReward,
std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet);
} // namespace CMasternodePayments

#endif // BITCOIN_MASTERNODE_PAYMENTS_H
2 changes: 1 addition & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc

// Update coinbase transaction with additional info about masternode and governance payments,
// get some info back to pass to getblocktemplate
FillBlockPayments(spork_manager, governance_manager, coinbaseTx, nHeight, blockReward, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments);
CMasternodePayments::FillBlockPayments(spork_manager, governance_manager, coinbaseTx, nHeight, blockReward, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments);

pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vTxFees[0] = -nFees;
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/masternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request)
std::vector<CTxOut> voutMasternodePayments, voutDummy;
CMutableTransaction dummyTx;
CAmount blockReward = nBlockFees + GetBlockSubsidy(pindex->pprev->nBits, pindex->pprev->nHeight, Params().GetConsensus());
FillBlockPayments(*sporkManager, *governance, dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);
CMasternodePayments::FillBlockPayments(*sporkManager, *governance, dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);

UniValue blockObj(UniValue::VOBJ);
CAmount payedPerBlock{0};
Expand Down
4 changes: 2 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2454,7 +2454,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5_2 = GetTimeMicros(); nTimeSubsidy += nTime5_2 - nTime5_1;
LogPrint(BCLog::BENCHMARK, " - GetBlockSubsidy: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5_2 - nTime5_1), nTimeSubsidy * MICRO, nTimeSubsidy * MILLI / nBlocksTotal);

if (!IsBlockValueValid(*sporkManager, *governance, *::masternodeSync, block, pindex->nHeight, blockReward, strError)) {
if (!CMasternodePayments::IsBlockValueValid(*sporkManager, *governance, *::masternodeSync, block, pindex->nHeight, blockReward, strError)) {
// NOTE: Do not punish, the node might be missing governance data
LogPrintf("ERROR: ConnectBlock(DASH): %s\n", strError);
return state.Invalid(BlockValidationResult::BLOCK_RESULT_UNSET, "bad-cb-amount");
Expand All @@ -2463,7 +2463,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5_3 = GetTimeMicros(); nTimeValueValid += nTime5_3 - nTime5_2;
LogPrint(BCLog::BENCHMARK, " - IsBlockValueValid: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5_3 - nTime5_2), nTimeValueValid * MICRO, nTimeValueValid * MILLI / nBlocksTotal);

if (!IsBlockPayeeValid(*sporkManager, *governance, *block.vtx[0], pindex->nHeight, blockReward)) {
if (!CMasternodePayments::IsBlockPayeeValid(*sporkManager, *governance, *block.vtx[0], pindex->nHeight, blockReward)) {
// NOTE: Do not punish, the node might be missing governance data
LogPrintf("ERROR: ConnectBlock(DASH): couldn't find masternode or superblock payments\n");
return state.Invalid(BlockValidationResult::BLOCK_RESULT_UNSET, "bad-cb-payee");
Expand Down