Skip to content

Commit

Permalink
feat: masternode payment reallocation from coin base to platform
Browse files Browse the repository at this point in the history
  • Loading branch information
ogabrielides committed Sep 4, 2023
1 parent ba68ea5 commit 485a367
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 33 deletions.
31 changes: 26 additions & 5 deletions src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
#include <evo/assetlocktx.h>
#include <evo/cbtx.h>

#include <llmq/utils.h>

#include <chain.h>
#include <llmq/utils.h>
#include <logging.h>
#include <validation.h>
#include <node/blockstorage.h>
Expand All @@ -19,6 +18,11 @@
#include <memory>
#include <stack>

// Forward declaration to prevent a new circular dependencies through masternode/payments.h
namespace MasternodePayments {
CAmount PlatformShare(const CAmount masternodeReward);
} // namespace MasternodePayments

static const std::string DB_CREDITPOOL_SNAPSHOT = "cpm_S";

std::unique_ptr<CCreditPoolManager> creditPoolManager;
Expand Down Expand Up @@ -213,21 +217,38 @@ CCreditPoolManager::CCreditPoolManager(CEvoDB& _evoDb)

CCreditPoolDiff::CCreditPoolDiff(CCreditPool starter, const CBlockIndex *pindex, const Consensus::Params& consensusParams) :
pool(std::move(starter)),
pindex(pindex)
pindex(pindex),
params(consensusParams)
{
assert(pindex);
}

void CCreditPoolDiff::AddRewardRealloced(const CAmount reward) {
assert(MoneyRange(reward));
platformReward += reward;
}

bool CCreditPoolDiff::SetTarget(const CTransaction& tx, TxValidationState& state)
{
CCbTx cbTx;
if (!GetTxPayload(tx, cbTx)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload");
}

if (cbTx.nVersion == 3) {
targetBalance = cbTx.creditPoolBalance;
if (cbTx.nVersion != 3) return true;

targetBalance = cbTx.creditPoolBalance;


if (!llmq::utils::IsMNRewardReallocationActive(pindex)) return true;

CAmount blockReward = 0;
for (const CTxOut& txout : tx.vout) {
blockReward += txout.nValue;
}
platformReward = MasternodePayments::PlatformShare(GetMasternodePayment(cbTx.nHeight, blockReward, params.BRRHeight));
LogPrintf("CreditPool: set target to %lld with MN reward %lld\n", *targetBalance, platformReward);

return true;
}

Expand Down
9 changes: 8 additions & 1 deletion src/evo/creditpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ class CCreditPoolDiff {

CAmount sessionLocked{0};
CAmount sessionUnlocked{0};
CAmount platformReward{0};

// target value is used to validate CbTx. If values mismatched, block is invalid
std::optional<CAmount> targetBalance;

const CBlockIndex *pindex{nullptr};
const Consensus::Params& params;
public:
explicit CCreditPoolDiff(CCreditPool starter, const CBlockIndex *pindex, const Consensus::Params& consensusParams);

Expand All @@ -82,8 +84,13 @@ class CCreditPoolDiff {
*/
bool ProcessTransaction(const CTransaction& tx, TxValidationState& state);

/**
* This function should be called by miner for initialization of MasterNode reward
*/
void AddRewardRealloced(const CAmount reward);

CAmount GetTotalLocked() const {
return pool.locked + sessionLocked - sessionUnlocked;
return pool.locked + sessionLocked - sessionUnlocked + platformReward;
}

const std::optional<CAmount>& GetTargetBalance() const {
Expand Down
38 changes: 31 additions & 7 deletions src/masternode/payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <governance/governance.h>
#include <key_io.h>
#include <logging.h>
#include <llmq/utils.h>
#include <masternode/sync.h>
#include <primitives/block.h>
#include <script/standard.h>
Expand All @@ -20,6 +21,7 @@
#include <util/system.h>
#include <validation.h>

#include <cassert>
#include <string>

/**
Expand All @@ -32,14 +34,27 @@ static bool GetBlockTxOuts(const int nBlockHeight, const CAmount blockReward, st
{
voutMasternodePaymentsRet.clear();

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

const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[nBlockHeight - 1]);
bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(pindex);

if (fMNRewardReallocated) {
const CAmount platformReward = MasternodePayments::PlatformShare(masternodeReward);
masternodeReward -= platformReward;

assert(MoneyRange(masternodeReward));

LogPrint(BCLog::MNPAYMENTS, "CMasternodePayments::%s -- MN reward %lld reallocated to credit pool\n", __func__, platformReward);
voutMasternodePaymentsRet.emplace_back(platformReward, CScript() << OP_RETURN);
}

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.
Expand All @@ -65,15 +80,15 @@ static bool GetMasternodeTxOuts(const int nBlockHeight, const CAmount blockRewar
voutMasternodePaymentsRet.clear();

if(!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) {
LogPrintf("CMasternodePayments::%s -- no payee (deterministic masternode list empty)\n", __func__);
LogPrintf("MasternodePayments::%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));
LogPrintf("MasternodePayments::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, EncodeDestination(dest));
}

return true;
Expand All @@ -88,7 +103,7 @@ static bool IsTransactionValid(const CTransaction& txNew, const int nBlockHeight

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);
LogPrintf("MasternodePayments::%s -- ERROR failed to get payees for block at height %s\n", __func__, nBlockHeight);
return true;
}

Expand All @@ -98,7 +113,7 @@ static bool IsTransactionValid(const CTransaction& txNew, const int nBlockHeight
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);
LogPrintf("MasternodePayments::%s -- ERROR failed to find expected payee %s in block at height %s\n", __func__, EncodeDestination(dest), nBlockHeight);
return false;
}
}
Expand Down Expand Up @@ -147,7 +162,7 @@ static bool IsOldBudgetBlockValueValid(const CMasternodeSync& mn_sync, const CBl
return isBlockRewardValueMet;
}

namespace CMasternodePayments {
namespace MasternodePayments {

/**
* IsBlockValueValid
Expand Down Expand Up @@ -329,4 +344,13 @@ void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& go
nBlockHeight, blockReward, voutMasternodeStr, txNew.ToString());
}

} // namespace CMasternodePayments
CAmount PlatformShare(const CAmount reward)
{
constexpr double platformShare = 0.375;
const CAmount platformReward = reward * platformShare;
assert(MoneyRange(platformReward));

return platformReward;
}

} // namespace MasternodePayments
19 changes: 13 additions & 6 deletions src/masternode/payments.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ class CSporkManager;
class CTxOut;
class CMasternodeSync;

//
// Masternode Payments Class
// Keeps track of who should get paid for which blocks
//
/**
* Masternode Payments Namespace
* Helpers to kees track of who should get paid for which blocks
*/

namespace CMasternodePayments
namespace MasternodePayments
{
bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet);
Expand All @@ -32,6 +32,13 @@ bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& go
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

/**
* this helper returns amount that should be reallocated to platform
* it is calculated based on total amount of Masternode rewards (not block reward)
*/
CAmount PlatformShare(const CAmount masternodeReward);

} // namespace MasternodePayments

#endif // BITCOIN_MASTERNODE_PAYMENTS_H
10 changes: 9 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
LogPrintf("CreateNewBlock() h[%d] CbTx failed to find best CL. Inserting null CL\n", nHeight);
}
assert(creditPoolDiff != std::nullopt);

bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(pindexPrev);
if (fMNRewardReallocated) {
const CAmount masternodeReward = GetMasternodePayment(nHeight, blockReward, Params().GetConsensus().BRRHeight);
const CAmount reallocedReward = MasternodePayments::PlatformShare(masternodeReward);
LogPrint(BCLog::MNPAYMENTS, "%s: add MN reward %lld (%lld) to credit pool\n", __func__, masternodeReward, reallocedReward);
creditPoolDiff->AddRewardRealloced(reallocedReward);
}
cbTx.creditPoolBalance = creditPoolDiff->GetTotalLocked();
}
}
Expand All @@ -240,7 +248,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
CMasternodePayments::FillBlockPayments(spork_manager, governance_manager, coinbaseTx, nHeight, blockReward, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments);
MasternodePayments::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 @@ -460,7 +460,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request, const Chainst
std::vector<CTxOut> voutMasternodePayments, voutDummy;
CMutableTransaction dummyTx;
CAmount blockReward = nBlockFees + GetBlockSubsidy(pindex, Params().GetConsensus());
CMasternodePayments::FillBlockPayments(*sporkManager, *governance, dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);
MasternodePayments::FillBlockPayments(*sporkManager, *governance, dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);

UniValue blockObj(UniValue::VOBJ);
CAmount payedPerBlock{0};
Expand Down
36 changes: 26 additions & 10 deletions src/test/block_reward_reallocation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <llmq/chainlocks.h>
#include <llmq/context.h>
#include <llmq/instantsend.h>
#include <llmq/utils.h>
#include <masternode/payments.h>
#include <util/enumerate.h>
#include <util/irange.h>

Expand Down Expand Up @@ -201,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
LOCK(cs_main);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
}
Expand All @@ -212,7 +214,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS

{
LOCK(cs_main);
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 13748571607);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
Expand All @@ -227,7 +229,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
CreateAndProcessBlock({}, coinbaseKey);
}
LOCK(cs_main);
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
}
Expand All @@ -236,7 +238,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
{
// Reward split should reach ~60/40 after reallocation is done
LOCK(cs_main);
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 10221599170);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
Expand All @@ -250,19 +252,33 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
CreateAndProcessBlock({}, coinbaseKey);
}
LOCK(cs_main);
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);

CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);

bool isMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip());
if (isMNRewardReallocated) {
const CAmount platform_payment = MasternodePayments::PlatformShare(masternode_payment);
masternode_payment -= platform_payment;
}
size_t payment_index = isMNRewardReallocated ? 1 : 0;

BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[payment_index].nValue, masternode_payment);
}

{
{ // At this moment Masternode reward should be reallocated to platform
// Reward split should reach ~60/40 after reallocation is done
LOCK(cs_main);
auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount platform_payment = MasternodePayments::PlatformShare(masternode_payment);
masternode_payment -= platform_payment;
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 9491484944);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 5694890966); // 0.6

BOOST_CHECK(llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip()));

BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[1].nValue, masternode_payment);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[1].nValue, 3559306854); // 0.6
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2413,7 +2413,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 (!CMasternodePayments::IsBlockValueValid(*sporkManager, *governance, *::masternodeSync, block, pindex->nHeight, blockReward, strError)) {
if (!MasternodePayments::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 @@ -2422,7 +2422,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 (!CMasternodePayments::IsBlockPayeeValid(*sporkManager, *governance, *block.vtx[0], pindex->nHeight, blockReward)) {
if (!MasternodePayments::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
Loading

0 comments on commit 485a367

Please sign in to comment.