Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Consensus] Single superblock #2858

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
45 changes: 45 additions & 0 deletions src/budget/budgetmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,31 @@ bool CBudgetManager::GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet
return GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet);
}

const CFinalizedBudget* CBudgetManager::GetBestFinalizedBudget(int chainHeight) const
{
if (!Params().GetConsensus().IsSuperBlock(chainHeight)) {
return nullptr;
}
int nFivePercent = mnodeman.CountEnabled() / 20;

const auto highest = GetBudgetWithHighestVoteCount(chainHeight);
if (highest.m_budget_fin == nullptr || highest.m_vote_count <= nFivePercent) {
// No finalization or not enough votes.
return nullptr;
}
return highest.m_budget_fin;
}

CAmount CBudgetManager::GetFinalizedBudgetTotalPayout(int chainHeight) const
{
const CFinalizedBudget* pfb = GetBestFinalizedBudget(chainHeight);
if (pfb == nullptr) {
// No finalization or not enough votes.
return 0;
}
return pfb->GetTotalPayout();
}

bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const
{
if (nHeight <= 0) return false;
Expand Down Expand Up @@ -553,6 +578,14 @@ bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTra
return true;
}

void CBudgetManager::FillBlockPayees(CMutableTransaction& tx, int height) const
{
const CFinalizedBudget* pfb = GetBestFinalizedBudget(height);
if (pfb != nullptr) {
pfb->PayAllBudgets(tx);
}
}

void CBudgetManager::VoteOnFinalizedBudgets()
{
// function called only from initialized masternodes
Expand Down Expand Up @@ -742,6 +775,18 @@ TrxValidationStatus CBudgetManager::IsTransactionValid(const CTransaction& txNew
return fThreshold ? TrxValidationStatus::InValid : TrxValidationStatus::VoteThreshold;
}

bool CBudgetManager::IsValidSuperBlockTx(const CTransaction& txNew, int nBlockHeight) const
{
assert(Params().GetConsensus().IsSuperBlock(nBlockHeight));

const CFinalizedBudget* pfb = GetBestFinalizedBudget(nBlockHeight);
if (pfb == nullptr) {
// No finalization or not enough votes. Nothing to check.
return true;
}
return pfb->AllBudgetsPaid(txNew);
}

std::vector<CBudgetProposal*> CBudgetManager::GetAllProposalsOrdered()
{
LOCK(cs_proposals);
Expand Down
13 changes: 9 additions & 4 deletions src/budget/budgetmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,23 @@ class CBudgetManager : public CValidationInterface
std::vector<CBudgetProposal*> GetAllProposalsOrdered();
std::vector<CFinalizedBudget*> GetFinalizedBudgets();
bool GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet) const;
bool IsBudgetPaymentBlock(int nBlockHeight) const;
bool IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const;
CAmount GetFinalizedBudgetTotalPayout(int chainHeight) const;
bool IsBudgetPaymentBlock(int nBlockHeight) const; // legacy (multiple SB)
bool IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const; // legacy (multiple SB)
bool AddProposal(CBudgetProposal& budgetProposal);
bool AddFinalizedBudget(CFinalizedBudget& finalizedBudget, CNode* pfrom = nullptr);
void ForceAddFinalizedBudget(const uint256& nHash, const uint256& feeTxId, const CFinalizedBudget& finalizedBudget);
uint256 SubmitFinalBudget();

bool UpdateProposal(const CBudgetVote& vote, CNode* pfrom, std::string& strError);
bool UpdateFinalizedBudget(const CFinalizedBudgetVote& vote, CNode* pfrom, std::string& strError);
TrxValidationStatus IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const;
TrxValidationStatus IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const; // legacy (multiple SB)
bool IsValidSuperBlockTx(const CTransaction& txNew, int nBlockHeight) const; // v6.0: single SB

std::string GetRequiredPaymentsString(int nBlockHeight);
bool FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const;
const CFinalizedBudget* GetBestFinalizedBudget(int chainHeight) const;
bool FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const; // legacy (multiple SB)
void FillBlockPayees(CMutableTransaction& tx, int height) const; // v6.0: single SB

// Only initialized masternodes: sign and submit votes on valid finalized budgets
void VoteOnFinalizedBudgets();
Expand Down
2 changes: 1 addition & 1 deletion src/budget/budgetproposal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ bool CBudgetProposal::CheckStartEnd()
{
// block start must be a superblock
if (nBlockStart < 0 ||
nBlockStart % Params().GetConsensus().nBudgetCycleBlocks != 0) {
!Params().GetConsensus().IsSuperBlock(nBlockStart)) {
strInvalid = "Invalid nBlockStart";
return false;
}
Expand Down
43 changes: 42 additions & 1 deletion src/budget/finalizedbudget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include "budget/finalizedbudget.h"

#include "chainparams.h"
#include "masternodeman.h"
#include "utilmoneystr.h"
#include "validation.h"

CFinalizedBudget::CFinalizedBudget() :
Expand Down Expand Up @@ -187,7 +189,7 @@ bool CFinalizedBudget::CheckStartEnd()
}

// Must be the correct block for payment to happen (once a month)
if (nBlockStart % Params().GetConsensus().nBudgetCycleBlocks != 0) {
if (!Params().GetConsensus().IsSuperBlock(nBlockStart)) {
strInvalid = "Invalid BlockStart";
return false;
}
Expand Down Expand Up @@ -389,6 +391,45 @@ bool CFinalizedBudget::GetPayeeAndAmount(int64_t nBlockHeight, CScript& payee, C
return true;
}

bool CFinalizedBudget::AllBudgetsPaid(const CTransaction& tx) const
{
// make a map for faster lookup and deal with duplicate payees
struct cmp {
bool operator()(const CTxOut& a, const CTxOut& b) const
{
return a.scriptPubKey < b.scriptPubKey ||
(a.scriptPubKey == b.scriptPubKey && a.nValue < b.nValue);
}
};
std::map<CTxOut, int, cmp> txouts;
for (const CTxOut& o : tx.vout) {
txouts[o]++;
}

for (const CTxBudgetPayment& payment : vecBudgetPayments) {
auto it = txouts.find(CTxOut(payment.nAmount, payment.payee));
if (it == txouts.end() || it->second == 0) {
// Payment not found
CTxDestination addr;
const std::string& payee = ExtractDestination(payment.payee, addr) ? EncodeDestination(addr) : HexStr(payment.payee);
LogPrint(BCLog::MNBUDGET, "Missing payment of %s for %s (proposal hash: %s)\n",
FormatMoney(payment.nAmount), payee, payment.nProposalHash.ToString());
return false;
}
it->second--;
}

// all budgets are paid by tx
return true;
}

void CFinalizedBudget::PayAllBudgets(CMutableTransaction& tx) const
{
for (const CTxBudgetPayment& payment : vecBudgetPayments) {
tx.vout.emplace_back(payment.nAmount, payment.payee);
}
}

// return broadcast serialization
CDataStream CFinalizedBudget::GetBroadcast() const
{
Expand Down
6 changes: 6 additions & 0 deletions src/budget/finalizedbudget.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ class CFinalizedBudget
bool GetBudgetPaymentByBlock(int64_t nBlockHeight, CTxBudgetPayment& payment) const;
bool GetPayeeAndAmount(int64_t nBlockHeight, CScript& payee, CAmount& nAmount) const;

// Check if ALL the budgets are paid by transaction tx
bool AllBudgetsPaid(const CTransaction& tx) const;

// Add payments for ALL budgets to tx outs
void PayAllBudgets(CMutableTransaction& tx) const;

// Check finalized budget proposals. Masternodes only (when voting on finalized budgets)
bool CheckProposals(const std::map<uint256, CBudgetProposal>& mapWinningProposals) const;
// Total amount paid out by this budget
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ struct Params {
return (contextHeight - utxoFromBlockHeight >= nStakeMinDepth);
}

bool IsSuperBlock(int height) const { return height % nBudgetCycleBlocks == 0; }

/*
* (Legacy) Zerocoin consensus params
Expand Down
118 changes: 88 additions & 30 deletions src/masternode-payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void DumpMasternodePayments()
LogPrint(BCLog::MASTERNODE,"Budget dump finished %dms\n", GetTimeMillis() - nStart);
}

bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
static bool IsBlockValueValid_legacy(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
{
const Consensus::Params& consensus = Params().GetConsensus();
if (!g_tiertwo_sync_state.IsSynced()) {
Expand Down Expand Up @@ -224,18 +224,39 @@ bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CA
return nMinted <= nExpectedValue;
}

bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
{
const Consensus::Params& consensus = Params().GetConsensus();
if (!consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0)) {
return IsBlockValueValid_legacy(nHeight, nExpectedValue, nMinted, nBudgetAmt);
}

if (consensus.IsSuperBlock(nHeight)) {
if (!g_tiertwo_sync_state.IsSynced()) {
// there is no budget data to use to check anything
nExpectedValue += g_budgetman.GetTotalBudget(nHeight);
} else if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) {
// we're synced and the superblock spork is enabled
nBudgetAmt = g_budgetman.GetFinalizedBudgetTotalPayout(nHeight);
nExpectedValue += nBudgetAmt;
}
}

return nMinted >= 0 && nMinted <= nExpectedValue;
}

bool IsBlockPayeeValid_legacy(const CBlock& block, const CBlockIndex* pindexPrev)
{
int nBlockHeight = pindexPrev->nHeight + 1;
assert(!Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0));
TrxValidationStatus transactionStatus = TrxValidationStatus::InValid;

if (!g_tiertwo_sync_state.IsSynced()) { //there is no budget data to use to check anything -- find the longest chain
LogPrint(BCLog::MASTERNODE, "Client not synced, skipping block payee checks\n");
return true;
}

const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS) &&
!Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0);
const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS);
const CTransaction& txNew = *(fPayCoinstake ? block.vtx[1] : block.vtx[0]);

//check if it's a budget block
Expand Down Expand Up @@ -272,8 +293,38 @@ bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
return true;
}

bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
{
const Consensus::Params& consensus = Params().GetConsensus();
int nBlockHeight = pindexPrev->nHeight + 1;
if (!consensus.NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0)) {
return IsBlockPayeeValid_legacy(block, pindexPrev);
}

void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
if (!g_tiertwo_sync_state.IsSynced()) { // there is no budget data to use to check anything -- find the longest chain
// !TODO: after transition to v6, restrict this to budget-checks only
LogPrint(BCLog::MASTERNODE, "Client not synced, skipping block payee checks\n");
return true;
}

const CTransaction& coinbase_tx = *block.vtx[0];

// Check masternode payment
if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT) &&
!masternodePayments.IsTransactionValid(coinbase_tx, pindexPrev)) {
LogPrint(BCLog::MASTERNODE, "Missing required masternode payment\n");
return false;
}

// Check budget payments during superblocks
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && consensus.IsSuperBlock(nBlockHeight)) {
return g_budgetman.IsValidSuperBlockTx(coinbase_tx, nBlockHeight);
}

return true;
}

static void FillBlockPayee_legacy(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
{
if (!sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) || // if superblocks are not enabled
// ... or this is not a superblock
Expand All @@ -283,6 +334,23 @@ void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoin
}
}

void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
{
int height = pindexPrev->nHeight + 1;
if (!Params().GetConsensus().NetworkUpgradeActive(height, Consensus::UPGRADE_V6_0)) {
// legacy - !TODO: remove after transition
return FillBlockPayee_legacy(txCoinbase, txCoinstake, pindexPrev, fProofOfStake);
}

// Add masternode payment
masternodePayments.FillBlockPayee(txCoinbase, txCoinstake, pindexPrev, fProofOfStake);

// Add budget payments (if superblock, and SPORK_13 is active)
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) {
g_budgetman.FillBlockPayees(txCoinbase, height);
}
}

std::string GetRequiredPaymentsString(int nBlockHeight)
{
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && g_budgetman.IsBudgetPaymentBlock(nBlockHeight)) {
Expand Down Expand Up @@ -821,35 +889,25 @@ void CMasternodePayments::RecordWinnerVote(const COutPoint& outMasternode, int n
mapMasternodesLastVote[outMasternode] = nBlockHeight;
}

bool IsCoinbaseValueValid(const CTransactionRef& tx, CAmount nBudgetAmt, CValidationState& _state)
bool IsCoinbaseValueValid(const int nHeight, const CTransactionRef& tx, CAmount nBudgetAmt, CValidationState& _state)
{
assert(tx->IsCoinBase());
if (g_tiertwo_sync_state.IsSynced()) {
const CAmount nCBaseOutAmt = tx->GetValueOut();
if (nBudgetAmt > 0) {
// Superblock
if (nCBaseOutAmt != nBudgetAmt) {
const std::string strError = strprintf("%s: invalid coinbase payment for budget (%s vs expected=%s)",
__func__, FormatMoney(nCBaseOutAmt), FormatMoney(nBudgetAmt));
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-superblock-cb-amt");
}
return true;
} else {
// regular block
int nHeight = mnodeman.GetBestHeight();
CAmount nMnAmt = GetMasternodePayment(nHeight);
// if enforcement is disabled, there could be no masternode payment
bool sporkEnforced = sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT);
const std::string strError = strprintf("%s: invalid coinbase payment for masternode (%s vs expected=%s)",
__func__, FormatMoney(nCBaseOutAmt), FormatMoney(nMnAmt));
if (sporkEnforced && nCBaseOutAmt != nMnAmt) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-cb-amt");
}
if (!sporkEnforced && nCBaseOutAmt > nMnAmt) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-cb-amt-spork8-disabled");
}
return true;
const CAmount paid = tx->GetValueOut();
const CAmount expected = GetMasternodePayment(nHeight) + nBudgetAmt;
// if enforcement is disabled, there could be no masternode payment
bool sporkEnforced = sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT);

const std::string strError = strprintf("%s: invalid coinbase payment (%s vs expected=%s)",
__func__, FormatMoney(paid), FormatMoney(expected));
std::string rej_reason = (nBudgetAmt > 0 ? "bad-superblock-cb-amt" : "bad-cb-amt");
if (sporkEnforced && paid != expected) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, rej_reason);
}
if (!sporkEnforced && paid > expected) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, rej_reason + "-spork8-disabled");
}
return true;
}
return true;
}
2 changes: 1 addition & 1 deletion src/masternode-payments.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoin
* Check coinbase output value for blocks after v6.0 enforcement.
* It must pay the masternode for regular blocks and a proposal during superblocks.
*/
bool IsCoinbaseValueValid(const CTransactionRef& tx, CAmount nBudgetAmt, CValidationState& _state);
bool IsCoinbaseValueValid(const int nHeight, const CTransactionRef& tx, CAmount nBudgetAmt, CValidationState& _state);

void DumpMasternodePayments();

Expand Down
Loading